From e18731d328254b7e926369741b282fbffc840ea5 Mon Sep 17 00:00:00 2001 From: Keith Seitz Date: Mon, 15 Apr 2002 17:39:27 +0000 Subject: import of blt2.4y --- blt/INSTALL | 115 + blt/MANIFEST | 381 + blt/Makefile.cyg | 54 + blt/Makefile.in | 75 + blt/Makefile.vc | 59 + blt/NEWS | 609 ++ blt/PROBLEMS | 138 + blt/README | 182 + blt/acconfig.h | 39 + blt/aclocal.m4 | 38 + blt/blt.mak | 34 + blt/cf/config.guess | 1121 +++ blt/cf/config.sub | 1232 ++++ blt/cf/install-sh | 251 + blt/cf/install.sh | 238 + blt/cf/ldAix | 72 + blt/configure | 4566 ++++++++++++ blt/configure.in | 1385 ++++ blt/demos/Makefile.cyg | 78 + blt/demos/Makefile.in | 90 + blt/demos/Makefile.vc | 78 + blt/demos/barchart1.tcl | 185 + blt/demos/barchart2.tcl | 215 + blt/demos/barchart3.tcl | 173 + blt/demos/barchart4.tcl | 135 + blt/demos/barchart5.tcl | 112 + blt/demos/bgexec1.tcl | 198 + blt/demos/bgexec2.tcl | 46 + blt/demos/bgexec3.tcl | 224 + blt/demos/bgexec4.tcl | 180 + blt/demos/bgexec5.tcl | 47 + blt/demos/bitmap.tcl | 233 + blt/demos/bitmaps/face.xbm | 171 + blt/demos/bitmaps/fish/left.xbm | 8 + blt/demos/bitmaps/fish/left1.xbm | 8 + blt/demos/bitmaps/fish/left1m.xbm | 8 + blt/demos/bitmaps/fish/leftm.xbm | 8 + blt/demos/bitmaps/fish/mid.xbm | 8 + blt/demos/bitmaps/fish/midm.xbm | 8 + blt/demos/bitmaps/fish/right.xbm | 8 + blt/demos/bitmaps/fish/right1.xbm | 8 + blt/demos/bitmaps/fish/right1m.xbm | 8 + blt/demos/bitmaps/fish/rightm.xbm | 8 + blt/demos/bitmaps/greenback.xbm | 885 +++ blt/demos/bitmaps/hand/hand01.xbm | 8 + blt/demos/bitmaps/hand/hand01m.xbm | 8 + blt/demos/bitmaps/hand/hand02.xbm | 8 + blt/demos/bitmaps/hand/hand02m.xbm | 8 + blt/demos/bitmaps/hand/hand03.xbm | 8 + blt/demos/bitmaps/hand/hand03m.xbm | 8 + blt/demos/bitmaps/hand/hand04.xbm | 8 + blt/demos/bitmaps/hand/hand04m.xbm | 8 + blt/demos/bitmaps/hand/hand05.xbm | 8 + blt/demos/bitmaps/hand/hand05m.xbm | 8 + blt/demos/bitmaps/hand/hand06.xbm | 8 + blt/demos/bitmaps/hand/hand06m.xbm | 8 + blt/demos/bitmaps/hand/hand07.xbm | 8 + blt/demos/bitmaps/hand/hand07m.xbm | 8 + blt/demos/bitmaps/hand/hand08.xbm | 8 + blt/demos/bitmaps/hand/hand08m.xbm | 8 + blt/demos/bitmaps/hand/hand09.xbm | 8 + blt/demos/bitmaps/hand/hand09m.xbm | 8 + blt/demos/bitmaps/hand/hand10.xbm | 8 + blt/demos/bitmaps/hand/hand10m.xbm | 8 + blt/demos/bitmaps/hand/hand11.xbm | 8 + blt/demos/bitmaps/hand/hand11m.xbm | 8 + blt/demos/bitmaps/hand/hand12.xbm | 8 + blt/demos/bitmaps/hand/hand12m.xbm | 8 + blt/demos/bitmaps/hand/hand13.xbm | 8 + blt/demos/bitmaps/hand/hand13m.xbm | 8 + blt/demos/bitmaps/hand/hand14.xbm | 8 + blt/demos/bitmaps/hand/hand14m.xbm | 8 + blt/demos/bitmaps/hobbes.xbm | 16 + blt/demos/bitmaps/hobbes_mask.xbm | 14 + blt/demos/bitmaps/sharky.xbm | 129 + blt/demos/bitmaps/xbob.xbm | 47 + blt/demos/busy1.tcl | 254 + blt/demos/busy2.tcl | 259 + blt/demos/container.tcl | 14 + blt/demos/container3.tcl | 451 ++ blt/demos/dnd1.tcl | 212 + blt/demos/dnd2.tcl | 328 + blt/demos/dragdrop1.tcl | 131 + blt/demos/dragdrop2.tcl | 183 + blt/demos/eps.tcl | 256 + blt/demos/graph1.tcl | 132 + blt/demos/graph2.tcl | 142 + blt/demos/graph3.tcl | 105 + blt/demos/graph4.tcl | 2292 ++++++ blt/demos/graph5.tcl | 89 + blt/demos/graph6.tcl | 2343 +++++++ blt/demos/graph7.tcl | 95 + blt/demos/hierbox1.tcl | 133 + blt/demos/hierbox2.tcl | 100 + blt/demos/hierbox3.tcl | 80 + blt/demos/hierbox4.tcl | 78 + blt/demos/hiertable1.tcl | 236 + blt/demos/hiertable2.tcl | 221 + blt/demos/hiertable3.tcl | 201 + blt/demos/htext.txt | 615 ++ blt/demos/htext1.tcl | 186 + blt/demos/images/blt98.gif | Bin 0 -> 36719 bytes blt/demos/images/buckskin.gif | Bin 0 -> 7561 bytes blt/demos/images/chalk.gif | Bin 0 -> 4378 bytes blt/demos/images/close.gif | Bin 0 -> 142 bytes blt/demos/images/close2.gif | Bin 0 -> 142 bytes blt/demos/images/clouds.gif | Bin 0 -> 6414 bytes blt/demos/images/corrugated_metal.gif | Bin 0 -> 7708 bytes blt/demos/images/folder.gif | Bin 0 -> 88 bytes blt/demos/images/jan25_palm3x_L.jpg | Bin 0 -> 4048 bytes blt/demos/images/mini-book1.gif | Bin 0 -> 109 bytes blt/demos/images/mini-book2.gif | Bin 0 -> 93 bytes blt/demos/images/mini-display.gif | Bin 0 -> 109 bytes blt/demos/images/mini-doc.gif | Bin 0 -> 91 bytes blt/demos/images/mini-filemgr.gif | Bin 0 -> 106 bytes blt/demos/images/mini-ofolder.gif | Bin 0 -> 114 bytes blt/demos/images/mini-windows.gif | Bin 0 -> 86 bytes blt/demos/images/ofolder.gif | Bin 0 -> 110 bytes blt/demos/images/open.gif | Bin 0 -> 148 bytes blt/demos/images/open2.gif | Bin 0 -> 148 bytes blt/demos/images/out.ps | 11662 +++++++++++++++++++++++++++++++ blt/demos/images/qv100.t.gif | Bin 0 -> 2694 bytes blt/demos/images/rain.gif | Bin 0 -> 3785 bytes blt/demos/images/sample.gif | Bin 0 -> 186103 bytes blt/demos/images/smblue_rock.gif | Bin 0 -> 3820 bytes blt/demos/images/stopsign.gif | Bin 0 -> 259 bytes blt/demos/images/tan_paper.gif | Bin 0 -> 18904 bytes blt/demos/images/tan_paper2.gif | Bin 0 -> 18901 bytes blt/demos/images/txtrflag.gif | Bin 0 -> 17135 bytes blt/demos/scripts/barchart2.tcl | 125 + blt/demos/scripts/bgtest.tcl | 35 + blt/demos/scripts/clone.tcl | 88 + blt/demos/scripts/demo.tcl | 28 + blt/demos/scripts/globe.tcl | 509 ++ blt/demos/scripts/graph1.tcl | 72 + blt/demos/scripts/graph2.tcl | 138 + blt/demos/scripts/graph3.tcl | 78 + blt/demos/scripts/graph5.tcl | 65 + blt/demos/scripts/graph8.tcl | 85 + blt/demos/scripts/page.tcl | 131 + blt/demos/scripts/patterns.tcl | 16 + blt/demos/scripts/ps.tcl | 767 ++ blt/demos/scripts/send.tcl | 115 + blt/demos/scripts/stipples.tcl | 153 + blt/demos/scripts/xcolors.tcl | 271 + blt/demos/spline.tcl | 84 + blt/demos/stripchart1.tcl | 405 ++ blt/demos/tabnotebook1.tcl | 86 + blt/demos/tabnotebook2.tcl | 80 + blt/demos/tabnotebook3.tcl | 193 + blt/demos/tabset1.tcl | 57 + blt/demos/tabset2.tcl | 79 + blt/demos/tabset3.tcl | 199 + blt/demos/tabset4.tcl | 109 + blt/demos/tour.tcl | 159 + blt/demos/treeview1.tcl | 191 + blt/demos/winop1.tcl | 62 + blt/demos/winop2.tcl | 57 + blt/examples/calendar.tcl | 141 + blt/examples/form.tcl | 1060 +++ blt/examples/pareto.tcl | 139 + blt/html/BLT.html | 161 + blt/html/Makefile.cyg | 32 + blt/html/Makefile.vc | 33 + blt/html/barchart.html | 2240 ++++++ blt/html/beep.html | 41 + blt/html/bgexec.html | 271 + blt/html/bitmap.html | 208 + blt/html/bltdebug.html | 34 + blt/html/busy.html | 218 + blt/html/container.html | 272 + blt/html/cutbuffer.html | 57 + blt/html/dragdrop.html | 479 ++ blt/html/eps.html | 1231 ++++ blt/html/graph.html | 2311 ++++++ blt/html/hierbox.html | 2331 ++++++ blt/html/hiertable.html | 2331 ++++++ blt/html/htext.html | 397 ++ blt/html/spline.html | 160 + blt/html/stripchart.html | 2179 ++++++ blt/html/table.html | 721 ++ blt/html/tabset.html | 936 +++ blt/html/tile.html | 100 + blt/html/tree.html | 930 +++ blt/html/treeview.html | 2336 +++++++ blt/html/vector.html | 1124 +++ blt/html/watch.html | 140 + blt/html/winop.html | 124 + blt/library/Makefile.cyg | 70 + blt/library/Makefile.in | 74 + blt/library/Makefile.vc | 71 + blt/library/ZoomStack.itcl | 359 + blt/library/bltCanvEps.pro | 78 + blt/library/bltGraph.pro | 462 ++ blt/library/dd_protocols/dd-color.tcl | 51 + blt/library/dd_protocols/dd-file.tcl | 53 + blt/library/dd_protocols/dd-number.tcl | 51 + blt/library/dd_protocols/dd-text.tcl | 48 + blt/library/dd_protocols/tclIndex | 12 + blt/library/dnd.tcl | 102 + blt/library/dragdrop.tcl | 75 + blt/library/graph.tcl | 492 ++ blt/library/hierbox.tcl | 522 ++ blt/library/hiertable.tcl | 943 +++ blt/library/pkgIndex.tcl.in | 29 + blt/library/tabnotebook.tcl | 318 + blt/library/tabset.tcl | 325 + blt/library/tclIndex | 13 + blt/library/treeview.cur | Bin 0 -> 326 bytes blt/library/treeview.tcl | 972 +++ blt/library/treeview.xbm | 8 + blt/library/treeview_m.xbm | 8 + blt/man/BLT.mann | 153 + blt/man/Blt_Tree.man3 | 232 + blt/man/Blt_TreeCreate.man3 | 100 + blt/man/Blt_TreeCreateNode.man3 | 95 + blt/man/Blt_TreeDeleteNode.man3 | 75 + blt/man/Blt_TreeExists.man3 | 66 + blt/man/Blt_TreeGetNode.man3 | 69 + blt/man/Blt_TreeGetToken.man3 | 88 + blt/man/Blt_TreeName.man3 | 59 + blt/man/Blt_TreeNodeId.man3 | 58 + blt/man/Blt_TreeReleaseToken.man3 | 64 + blt/man/Makefile.in | 74 + blt/man/barchart.mann | 2236 ++++++ blt/man/beep.mann | 48 + blt/man/bgexec.mann | 309 + blt/man/bitmap.mann | 220 + blt/man/bltdebug.mann | 38 + blt/man/busy.mann | 241 + blt/man/container.mann | 303 + blt/man/cutbuffer.mann | 54 + blt/man/dragdrop.mann | 456 ++ blt/man/eps.mann | 163 + blt/man/graph.mann | 2329 ++++++ blt/man/hierbox.mann | 2261 ++++++ blt/man/hiertable.mann | 2261 ++++++ blt/man/htext.mann | 384 + blt/man/man.macros | 240 + blt/man/spline.mann | 181 + blt/man/stripchart.mann | 2168 ++++++ blt/man/table.mann | 757 ++ blt/man/tabset.mann | 920 +++ blt/man/tile.mann | 108 + blt/man/tree.mann | 897 +++ blt/man/treeview.mann | 2264 ++++++ blt/man/vector.mann | 1104 +++ blt/man/watch.mann | 137 + blt/man/winop.mann | 131 + blt/src/Makefile.cyg | 236 + blt/src/Makefile.in | 248 + blt/src/Makefile.vc | 330 + blt/src/TODO | 97 + blt/src/blt.h | 78 + blt/src/blt.mak | 304 + blt/src/bltAlloc.c | 98 + blt/src/bltArrayObj.c | 244 + blt/src/bltBeep.c | 92 + blt/src/bltBgexec.c | 2002 ++++++ blt/src/bltBind.c | 644 ++ blt/src/bltBind.h | 114 + blt/src/bltBitmap.c | 1504 ++++ blt/src/bltBusy.c | 1196 ++++ blt/src/bltCanvEps.c | 1742 +++++ blt/src/bltChain.c | 445 ++ blt/src/bltChain.h | 85 + blt/src/bltColor.c | 980 +++ blt/src/bltConfig.c | 1370 ++++ blt/src/bltConfig.h.in | 137 + blt/src/bltContainer.c | 1701 +++++ blt/src/bltCutbuffer.c | 265 + blt/src/bltDebug.c | 329 + blt/src/bltDragdrop.c | 2715 +++++++ blt/src/bltGrAxis.c | 4549 ++++++++++++ blt/src/bltGrAxis.h | 301 + blt/src/bltGrBar.c | 2270 ++++++ blt/src/bltGrElem.c | 2221 ++++++ blt/src/bltGrElem.h | 272 + blt/src/bltGrGrid.c | 517 ++ blt/src/bltGrHairs.c | 542 ++ blt/src/bltGrLegd.c | 1488 ++++ blt/src/bltGrLegd.h | 56 + blt/src/bltGrLine.c | 5091 ++++++++++++++ blt/src/bltGrMarker.c | 4954 +++++++++++++ blt/src/bltGrMisc.c | 1372 ++++ blt/src/bltGrPen.c | 700 ++ blt/src/bltGrPs.c | 1271 ++++ blt/src/bltGraph.c | 2362 +++++++ blt/src/bltGraph.h | 680 ++ blt/src/bltHash.c | 1349 ++++ blt/src/bltHash.h.in | 223 + blt/src/bltHierbox.c | 8672 +++++++++++++++++++++++ blt/src/bltHtext.c | 4496 ++++++++++++ blt/src/bltImage.c | 2700 +++++++ blt/src/bltImage.h | 287 + blt/src/bltInit.c | 680 ++ blt/src/bltInt.h | 1260 ++++ blt/src/bltInterp.h | 376 + blt/src/bltList.c | 588 ++ blt/src/bltList.h | 105 + blt/src/bltNsUtil.c | 728 ++ blt/src/bltNsUtil.h | 119 + blt/src/bltObjConfig.c | 2346 +++++++ blt/src/bltObjConfig.h | 238 + blt/src/bltParse.c | 541 ++ blt/src/bltPool.c | 458 ++ blt/src/bltPool.h | 36 + blt/src/bltPs.c | 1491 ++++ blt/src/bltPs.h | 154 + blt/src/bltSpline.c | 1451 ++++ blt/src/bltSwitch.c | 525 ++ blt/src/bltSwitch.h | 79 + blt/src/bltTable.c | 4956 +++++++++++++ blt/src/bltTable.h | 390 ++ blt/src/bltTabnotebook.c | 5712 +++++++++++++++ blt/src/bltTabset.c | 5882 ++++++++++++++++ blt/src/bltTed.c | 1868 +++++ blt/src/bltText.c | 1500 ++++ blt/src/bltText.h | 212 + blt/src/bltTile.c | 1274 ++++ blt/src/bltTile.h | 63 + blt/src/bltTkInt.h | 240 + blt/src/bltTree.c | 2511 +++++++ blt/src/bltTree.h | 435 ++ blt/src/bltTreeCmd.c | 5793 +++++++++++++++ blt/src/bltTreeView.c | 5245 ++++++++++++++ blt/src/bltTreeView.h | 1041 +++ blt/src/bltTreeViewCmd.c | 5206 ++++++++++++++ blt/src/bltTreeViewColumn.c | 1881 +++++ blt/src/bltTreeViewEdit.c | 1663 +++++ blt/src/bltTreeViewStyle.c | 423 ++ blt/src/bltUnixDnd.c | 5140 ++++++++++++++ blt/src/bltUnixImage.c | 885 +++ blt/src/bltUnixMain.c | 174 + blt/src/bltUnixPipe.c | 1071 +++ blt/src/bltUtil.c | 1178 ++++ blt/src/bltVecCmd.c | 1978 ++++++ blt/src/bltVecInt.h | 248 + blt/src/bltVecMath.c | 2041 ++++++ blt/src/bltVecObjCmd.c | 2068 ++++++ blt/src/bltVector.c | 2361 +++++++ blt/src/bltVector.h | 125 + blt/src/bltWait.h | 242 + blt/src/bltWatch.c | 855 +++ blt/src/bltWinConfig.h | 159 + blt/src/bltWinDde.c | 1312 ++++ blt/src/bltWinDraw.c | 2716 +++++++ blt/src/bltWinImage.c | 801 +++ blt/src/bltWinMain.c | 402 ++ blt/src/bltWinPipe.c | 2439 +++++++ blt/src/bltWinPrnt.c | 1548 ++++ blt/src/bltWinUtil.c | 77 + blt/src/bltWindow.c | 1666 +++++ blt/src/bltWinop.c | 1119 +++ blt/src/missing.h | 114 + blt/src/pure_api.c | 126 + blt/src/shared/Makefile.in | 325 + blt/src/tkButton.c | 2143 ++++++ blt/src/tkConsole.c | 641 ++ blt/src/tkFrame.c | 1094 +++ blt/src/tkMenubutton.c | 1239 ++++ blt/src/tkScrollbar.c | 1405 ++++ blt/win/README | 122 + blt/win/install.tcl | 739 ++ blt/win/makedefs | 52 + 365 files changed, 248004 insertions(+) create mode 100644 blt/INSTALL create mode 100644 blt/MANIFEST create mode 100644 blt/Makefile.cyg create mode 100644 blt/Makefile.in create mode 100644 blt/Makefile.vc create mode 100644 blt/NEWS create mode 100644 blt/PROBLEMS create mode 100644 blt/README create mode 100644 blt/acconfig.h create mode 100644 blt/aclocal.m4 create mode 100644 blt/blt.mak create mode 100755 blt/cf/config.guess create mode 100755 blt/cf/config.sub create mode 100755 blt/cf/install-sh create mode 100644 blt/cf/install.sh create mode 100644 blt/cf/ldAix create mode 100755 blt/configure create mode 100644 blt/configure.in create mode 100644 blt/demos/Makefile.cyg create mode 100644 blt/demos/Makefile.in create mode 100644 blt/demos/Makefile.vc create mode 100755 blt/demos/barchart1.tcl create mode 100755 blt/demos/barchart2.tcl create mode 100755 blt/demos/barchart3.tcl create mode 100755 blt/demos/barchart4.tcl create mode 100755 blt/demos/barchart5.tcl create mode 100755 blt/demos/bgexec1.tcl create mode 100755 blt/demos/bgexec2.tcl create mode 100755 blt/demos/bgexec3.tcl create mode 100755 blt/demos/bgexec4.tcl create mode 100755 blt/demos/bgexec5.tcl create mode 100755 blt/demos/bitmap.tcl create mode 100644 blt/demos/bitmaps/face.xbm create mode 100644 blt/demos/bitmaps/fish/left.xbm create mode 100644 blt/demos/bitmaps/fish/left1.xbm create mode 100644 blt/demos/bitmaps/fish/left1m.xbm create mode 100644 blt/demos/bitmaps/fish/leftm.xbm create mode 100644 blt/demos/bitmaps/fish/mid.xbm create mode 100644 blt/demos/bitmaps/fish/midm.xbm create mode 100644 blt/demos/bitmaps/fish/right.xbm create mode 100644 blt/demos/bitmaps/fish/right1.xbm create mode 100644 blt/demos/bitmaps/fish/right1m.xbm create mode 100644 blt/demos/bitmaps/fish/rightm.xbm create mode 100644 blt/demos/bitmaps/greenback.xbm create mode 100644 blt/demos/bitmaps/hand/hand01.xbm create mode 100644 blt/demos/bitmaps/hand/hand01m.xbm create mode 100644 blt/demos/bitmaps/hand/hand02.xbm create mode 100644 blt/demos/bitmaps/hand/hand02m.xbm create mode 100644 blt/demos/bitmaps/hand/hand03.xbm create mode 100644 blt/demos/bitmaps/hand/hand03m.xbm create mode 100644 blt/demos/bitmaps/hand/hand04.xbm create mode 100644 blt/demos/bitmaps/hand/hand04m.xbm create mode 100644 blt/demos/bitmaps/hand/hand05.xbm create mode 100644 blt/demos/bitmaps/hand/hand05m.xbm create mode 100644 blt/demos/bitmaps/hand/hand06.xbm create mode 100644 blt/demos/bitmaps/hand/hand06m.xbm create mode 100644 blt/demos/bitmaps/hand/hand07.xbm create mode 100644 blt/demos/bitmaps/hand/hand07m.xbm create mode 100644 blt/demos/bitmaps/hand/hand08.xbm create mode 100644 blt/demos/bitmaps/hand/hand08m.xbm create mode 100644 blt/demos/bitmaps/hand/hand09.xbm create mode 100644 blt/demos/bitmaps/hand/hand09m.xbm create mode 100644 blt/demos/bitmaps/hand/hand10.xbm create mode 100644 blt/demos/bitmaps/hand/hand10m.xbm create mode 100644 blt/demos/bitmaps/hand/hand11.xbm create mode 100644 blt/demos/bitmaps/hand/hand11m.xbm create mode 100644 blt/demos/bitmaps/hand/hand12.xbm create mode 100644 blt/demos/bitmaps/hand/hand12m.xbm create mode 100644 blt/demos/bitmaps/hand/hand13.xbm create mode 100644 blt/demos/bitmaps/hand/hand13m.xbm create mode 100644 blt/demos/bitmaps/hand/hand14.xbm create mode 100644 blt/demos/bitmaps/hand/hand14m.xbm create mode 100644 blt/demos/bitmaps/hobbes.xbm create mode 100644 blt/demos/bitmaps/hobbes_mask.xbm create mode 100644 blt/demos/bitmaps/sharky.xbm create mode 100644 blt/demos/bitmaps/xbob.xbm create mode 100755 blt/demos/busy1.tcl create mode 100755 blt/demos/busy2.tcl create mode 100755 blt/demos/container.tcl create mode 100755 blt/demos/container3.tcl create mode 100755 blt/demos/dnd1.tcl create mode 100755 blt/demos/dnd2.tcl create mode 100755 blt/demos/dragdrop1.tcl create mode 100755 blt/demos/dragdrop2.tcl create mode 100755 blt/demos/eps.tcl create mode 100755 blt/demos/graph1.tcl create mode 100755 blt/demos/graph2.tcl create mode 100755 blt/demos/graph3.tcl create mode 100755 blt/demos/graph4.tcl create mode 100755 blt/demos/graph5.tcl create mode 100755 blt/demos/graph6.tcl create mode 100755 blt/demos/graph7.tcl create mode 100755 blt/demos/hierbox1.tcl create mode 100755 blt/demos/hierbox2.tcl create mode 100755 blt/demos/hierbox3.tcl create mode 100755 blt/demos/hierbox4.tcl create mode 100755 blt/demos/hiertable1.tcl create mode 100755 blt/demos/hiertable2.tcl create mode 100755 blt/demos/hiertable3.tcl create mode 100644 blt/demos/htext.txt create mode 100755 blt/demos/htext1.tcl create mode 100644 blt/demos/images/blt98.gif create mode 100644 blt/demos/images/buckskin.gif create mode 100644 blt/demos/images/chalk.gif create mode 100644 blt/demos/images/close.gif create mode 100644 blt/demos/images/close2.gif create mode 100644 blt/demos/images/clouds.gif create mode 100644 blt/demos/images/corrugated_metal.gif create mode 100644 blt/demos/images/folder.gif create mode 100644 blt/demos/images/jan25_palm3x_L.jpg create mode 100644 blt/demos/images/mini-book1.gif create mode 100644 blt/demos/images/mini-book2.gif create mode 100644 blt/demos/images/mini-display.gif create mode 100644 blt/demos/images/mini-doc.gif create mode 100644 blt/demos/images/mini-filemgr.gif create mode 100644 blt/demos/images/mini-ofolder.gif create mode 100644 blt/demos/images/mini-windows.gif create mode 100644 blt/demos/images/ofolder.gif create mode 100644 blt/demos/images/open.gif create mode 100644 blt/demos/images/open2.gif create mode 100644 blt/demos/images/out.ps create mode 100644 blt/demos/images/qv100.t.gif create mode 100644 blt/demos/images/rain.gif create mode 100644 blt/demos/images/sample.gif create mode 100644 blt/demos/images/smblue_rock.gif create mode 100644 blt/demos/images/stopsign.gif create mode 100644 blt/demos/images/tan_paper.gif create mode 100644 blt/demos/images/tan_paper2.gif create mode 100644 blt/demos/images/txtrflag.gif create mode 100644 blt/demos/scripts/barchart2.tcl create mode 100644 blt/demos/scripts/bgtest.tcl create mode 100644 blt/demos/scripts/clone.tcl create mode 100644 blt/demos/scripts/demo.tcl create mode 100644 blt/demos/scripts/globe.tcl create mode 100644 blt/demos/scripts/graph1.tcl create mode 100644 blt/demos/scripts/graph2.tcl create mode 100644 blt/demos/scripts/graph3.tcl create mode 100644 blt/demos/scripts/graph5.tcl create mode 100644 blt/demos/scripts/graph8.tcl create mode 100755 blt/demos/scripts/page.tcl create mode 100644 blt/demos/scripts/patterns.tcl create mode 100644 blt/demos/scripts/ps.tcl create mode 100644 blt/demos/scripts/send.tcl create mode 100644 blt/demos/scripts/stipples.tcl create mode 100755 blt/demos/scripts/xcolors.tcl create mode 100755 blt/demos/spline.tcl create mode 100755 blt/demos/stripchart1.tcl create mode 100755 blt/demos/tabnotebook1.tcl create mode 100755 blt/demos/tabnotebook2.tcl create mode 100755 blt/demos/tabnotebook3.tcl create mode 100755 blt/demos/tabset1.tcl create mode 100755 blt/demos/tabset2.tcl create mode 100755 blt/demos/tabset3.tcl create mode 100755 blt/demos/tabset4.tcl create mode 100755 blt/demos/tour.tcl create mode 100755 blt/demos/treeview1.tcl create mode 100755 blt/demos/winop1.tcl create mode 100755 blt/demos/winop2.tcl create mode 100755 blt/examples/calendar.tcl create mode 100755 blt/examples/form.tcl create mode 100755 blt/examples/pareto.tcl create mode 100644 blt/html/BLT.html create mode 100644 blt/html/Makefile.cyg create mode 100644 blt/html/Makefile.vc create mode 100644 blt/html/barchart.html create mode 100644 blt/html/beep.html create mode 100644 blt/html/bgexec.html create mode 100644 blt/html/bitmap.html create mode 100644 blt/html/bltdebug.html create mode 100644 blt/html/busy.html create mode 100644 blt/html/container.html create mode 100644 blt/html/cutbuffer.html create mode 100644 blt/html/dragdrop.html create mode 100644 blt/html/eps.html create mode 100644 blt/html/graph.html create mode 100644 blt/html/hierbox.html create mode 100644 blt/html/hiertable.html create mode 100644 blt/html/htext.html create mode 100644 blt/html/spline.html create mode 100644 blt/html/stripchart.html create mode 100644 blt/html/table.html create mode 100644 blt/html/tabset.html create mode 100644 blt/html/tile.html create mode 100644 blt/html/tree.html create mode 100644 blt/html/treeview.html create mode 100644 blt/html/vector.html create mode 100644 blt/html/watch.html create mode 100644 blt/html/winop.html create mode 100644 blt/library/Makefile.cyg create mode 100644 blt/library/Makefile.in create mode 100644 blt/library/Makefile.vc create mode 100644 blt/library/ZoomStack.itcl create mode 100644 blt/library/bltCanvEps.pro create mode 100644 blt/library/bltGraph.pro create mode 100644 blt/library/dd_protocols/dd-color.tcl create mode 100644 blt/library/dd_protocols/dd-file.tcl create mode 100644 blt/library/dd_protocols/dd-number.tcl create mode 100644 blt/library/dd_protocols/dd-text.tcl create mode 100644 blt/library/dd_protocols/tclIndex create mode 100644 blt/library/dnd.tcl create mode 100644 blt/library/dragdrop.tcl create mode 100644 blt/library/graph.tcl create mode 100644 blt/library/hierbox.tcl create mode 100644 blt/library/hiertable.tcl create mode 100644 blt/library/pkgIndex.tcl.in create mode 100644 blt/library/tabnotebook.tcl create mode 100644 blt/library/tabset.tcl create mode 100644 blt/library/tclIndex create mode 100644 blt/library/treeview.cur create mode 100644 blt/library/treeview.tcl create mode 100644 blt/library/treeview.xbm create mode 100644 blt/library/treeview_m.xbm create mode 100644 blt/man/BLT.mann create mode 100644 blt/man/Blt_Tree.man3 create mode 100644 blt/man/Blt_TreeCreate.man3 create mode 100644 blt/man/Blt_TreeCreateNode.man3 create mode 100644 blt/man/Blt_TreeDeleteNode.man3 create mode 100644 blt/man/Blt_TreeExists.man3 create mode 100644 blt/man/Blt_TreeGetNode.man3 create mode 100644 blt/man/Blt_TreeGetToken.man3 create mode 100644 blt/man/Blt_TreeName.man3 create mode 100644 blt/man/Blt_TreeNodeId.man3 create mode 100644 blt/man/Blt_TreeReleaseToken.man3 create mode 100644 blt/man/Makefile.in create mode 100644 blt/man/barchart.mann create mode 100644 blt/man/beep.mann create mode 100644 blt/man/bgexec.mann create mode 100644 blt/man/bitmap.mann create mode 100644 blt/man/bltdebug.mann create mode 100644 blt/man/busy.mann create mode 100644 blt/man/container.mann create mode 100644 blt/man/cutbuffer.mann create mode 100644 blt/man/dragdrop.mann create mode 100644 blt/man/eps.mann create mode 100644 blt/man/graph.mann create mode 100644 blt/man/hierbox.mann create mode 100644 blt/man/hiertable.mann create mode 100644 blt/man/htext.mann create mode 100644 blt/man/man.macros create mode 100644 blt/man/spline.mann create mode 100644 blt/man/stripchart.mann create mode 100644 blt/man/table.mann create mode 100644 blt/man/tabset.mann create mode 100644 blt/man/tile.mann create mode 100644 blt/man/tree.mann create mode 100644 blt/man/treeview.mann create mode 100644 blt/man/vector.mann create mode 100644 blt/man/watch.mann create mode 100644 blt/man/winop.mann create mode 100644 blt/src/Makefile.cyg create mode 100644 blt/src/Makefile.in create mode 100644 blt/src/Makefile.vc create mode 100644 blt/src/TODO create mode 100644 blt/src/blt.h create mode 100644 blt/src/blt.mak create mode 100644 blt/src/bltAlloc.c create mode 100644 blt/src/bltArrayObj.c create mode 100644 blt/src/bltBeep.c create mode 100644 blt/src/bltBgexec.c create mode 100644 blt/src/bltBind.c create mode 100644 blt/src/bltBind.h create mode 100644 blt/src/bltBitmap.c create mode 100644 blt/src/bltBusy.c create mode 100644 blt/src/bltCanvEps.c create mode 100644 blt/src/bltChain.c create mode 100644 blt/src/bltChain.h create mode 100644 blt/src/bltColor.c create mode 100644 blt/src/bltConfig.c create mode 100644 blt/src/bltConfig.h.in create mode 100644 blt/src/bltContainer.c create mode 100644 blt/src/bltCutbuffer.c create mode 100644 blt/src/bltDebug.c create mode 100644 blt/src/bltDragdrop.c create mode 100644 blt/src/bltGrAxis.c create mode 100644 blt/src/bltGrAxis.h create mode 100644 blt/src/bltGrBar.c create mode 100644 blt/src/bltGrElem.c create mode 100644 blt/src/bltGrElem.h create mode 100644 blt/src/bltGrGrid.c create mode 100644 blt/src/bltGrHairs.c create mode 100644 blt/src/bltGrLegd.c create mode 100644 blt/src/bltGrLegd.h create mode 100644 blt/src/bltGrLine.c create mode 100644 blt/src/bltGrMarker.c create mode 100644 blt/src/bltGrMisc.c create mode 100644 blt/src/bltGrPen.c create mode 100644 blt/src/bltGrPs.c create mode 100644 blt/src/bltGraph.c create mode 100644 blt/src/bltGraph.h create mode 100644 blt/src/bltHash.c create mode 100644 blt/src/bltHash.h.in create mode 100644 blt/src/bltHierbox.c create mode 100644 blt/src/bltHtext.c create mode 100644 blt/src/bltImage.c create mode 100644 blt/src/bltImage.h create mode 100644 blt/src/bltInit.c create mode 100644 blt/src/bltInt.h create mode 100644 blt/src/bltInterp.h create mode 100644 blt/src/bltList.c create mode 100644 blt/src/bltList.h create mode 100644 blt/src/bltNsUtil.c create mode 100644 blt/src/bltNsUtil.h create mode 100644 blt/src/bltObjConfig.c create mode 100644 blt/src/bltObjConfig.h create mode 100644 blt/src/bltParse.c create mode 100644 blt/src/bltPool.c create mode 100644 blt/src/bltPool.h create mode 100644 blt/src/bltPs.c create mode 100644 blt/src/bltPs.h create mode 100644 blt/src/bltSpline.c create mode 100644 blt/src/bltSwitch.c create mode 100644 blt/src/bltSwitch.h create mode 100644 blt/src/bltTable.c create mode 100644 blt/src/bltTable.h create mode 100644 blt/src/bltTabnotebook.c create mode 100644 blt/src/bltTabset.c create mode 100644 blt/src/bltTed.c create mode 100644 blt/src/bltText.c create mode 100644 blt/src/bltText.h create mode 100644 blt/src/bltTile.c create mode 100644 blt/src/bltTile.h create mode 100644 blt/src/bltTkInt.h create mode 100644 blt/src/bltTree.c create mode 100644 blt/src/bltTree.h create mode 100644 blt/src/bltTreeCmd.c create mode 100644 blt/src/bltTreeView.c create mode 100644 blt/src/bltTreeView.h create mode 100644 blt/src/bltTreeViewCmd.c create mode 100644 blt/src/bltTreeViewColumn.c create mode 100644 blt/src/bltTreeViewEdit.c create mode 100644 blt/src/bltTreeViewStyle.c create mode 100644 blt/src/bltUnixDnd.c create mode 100644 blt/src/bltUnixImage.c create mode 100644 blt/src/bltUnixMain.c create mode 100644 blt/src/bltUnixPipe.c create mode 100644 blt/src/bltUtil.c create mode 100644 blt/src/bltVecCmd.c create mode 100644 blt/src/bltVecInt.h create mode 100644 blt/src/bltVecMath.c create mode 100644 blt/src/bltVecObjCmd.c create mode 100644 blt/src/bltVector.c create mode 100644 blt/src/bltVector.h create mode 100644 blt/src/bltWait.h create mode 100644 blt/src/bltWatch.c create mode 100644 blt/src/bltWinConfig.h create mode 100644 blt/src/bltWinDde.c create mode 100644 blt/src/bltWinDraw.c create mode 100644 blt/src/bltWinImage.c create mode 100644 blt/src/bltWinMain.c create mode 100644 blt/src/bltWinPipe.c create mode 100644 blt/src/bltWinPrnt.c create mode 100644 blt/src/bltWinUtil.c create mode 100644 blt/src/bltWindow.c create mode 100644 blt/src/bltWinop.c create mode 100644 blt/src/missing.h create mode 100644 blt/src/pure_api.c create mode 100644 blt/src/shared/Makefile.in create mode 100644 blt/src/tkButton.c create mode 100644 blt/src/tkConsole.c create mode 100644 blt/src/tkFrame.c create mode 100644 blt/src/tkMenubutton.c create mode 100644 blt/src/tkScrollbar.c create mode 100644 blt/win/README create mode 100644 blt/win/install.tcl create mode 100644 blt/win/makedefs 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 + for the bug report and example. + + 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 + 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 for the report. +vector + Vector "create" operation slows down using #auto as more vectors + are created. Thanks to Todd Copeland + 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 for the bug report. +treeview/hiertable + Giving the -path option to the "index" operation always fails. Thanks + to Paul Robins for the bug report. + + 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 + for the bug report. + + pkgIndex.tcl file not getting rebuilt. Thanks to Terri Fischer + for the bug report and fix. + + Send emulation script isn't needed for Tcl8.0 under Windows. Thanks to + Linh H Phan for the bug report. +graph/stripchart/barchart + Using pen styles results in a bus error. Thanks to Julian H J + Loaring for the bug report. +hiertable/treeview/tree + List of data values is reversed from 2.4v. Thanks to Jorge Suit + Prez Ronda for the bug report. + + Missing header file for varargs. Thanks to Terri Fischer + for the bug report and fix. + + TclpAlloc and TclpFree not found in Tcl 8.0. Again, thanks to Terri + Fischer for the bug report and fix. + + Updated manual pages graciously provided by Terri Fischer . + +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 + 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 + 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. + + Blt_MallocProcPtr and Blt_FreeProcPtr not declared extern in + bltInt.h + + Bogus test for mask in Blt_TilePolygon routine. + + Counter for transparent pixels wrong in Win32 version of Blt_PolygonMask. + + 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 + for the bug report. + + o Tcl command associated with a vector not destroyed when the vector + is deleted. Much thanks to Alexander Eisenhuth + 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 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 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 for + the bug report. + + o Scratch buffer to small for PostScript prolog. Thanks again to + Julian H J Loaring 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 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 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 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 . Thanks. + + o graph "bind" to use closest point instead of line segment when + element contains only 1 point. Thanks to Uwe Klein + 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 + 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 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 + for the new images. + + o Bgexec segfaults under Windows (NT/95) if file handler is + deleted inside of callback. Thanks to Chris Oliver + for the bug report. + + o graph segfaults if pen style range min/max are the same. + Thanks to Thomas Wu 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 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 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 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 + 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: + + 0xf44 /usr/local/lib/libtcl7.6.a(tclCmdIL.o) + 0xf3c /usr/local/lib/libtcl7.6.a(tclCmdIL.o) + 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 < ./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 <. +# The master version of this file is at the FSF in /home/gd/gnu/lib. +# Please send patches to . +# +# 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 <$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 + + 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 + #include + + 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 + 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 < +#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/^ //' <$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 </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 < +#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' /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 + echo i586-unisys-sysv4 + exit 0 ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # 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 < +# include +#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 + 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 <&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 < +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 < +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 < +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 <&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 <&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 <&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 <&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 <&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 <&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 <&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 < +#include +#include +#include +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 +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 +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 < +#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 < +#include +#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 < +#include +#include +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 +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 <&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 +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 <&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 +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 <&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 +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 <&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 +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 <&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 +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 <&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 +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 <&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 +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 <&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 +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 <&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 < +#if STDC_HEADERS +#include +#include +#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 < +#if STDC_HEADERS +#include +#include +#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 < +#include +int main() { + + /* + * Check whether 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 < +#include +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 < +#include +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 <&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 < +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 <&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 < +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 <&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 < +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 <&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 < +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 <&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 < +/* 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 <&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 < +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 < +#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 < ./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 <&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 < +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#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 < +#endif +#ifdef HAVE_MATH_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#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 < +#endif +#ifdef HAVE_MATH_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#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 < +#endif +#ifdef HAVE_MATH_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#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 +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 <&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 <&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 < +/* 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 <&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 <&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 <&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 <&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 <&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 <&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 +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 </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 < 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 <> $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 <> $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 <> $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 <> $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 +#include ], [ + /* + * Check whether 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 ], [ +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 +#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 +#endif +#ifdef HAVE_STRING_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#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 +#endif +#ifdef HAVE_MATH_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#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 +#endif +#ifdef HAVE_MATH_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#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 +#endif +#ifdef HAVE_MATH_H +#include +#endif +#ifdef HAVE_UNISTD_H +#include +#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 { + set axis [%W axis get current] + %W axis configure $axis -color blue3 -titlecolor blue3 +} +.bc axis bind x { + 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 { + set coords [%W invtransform %x %y] + catch { %W marker configure [%W marker get current] -coords $coords } +} + +.graph marker bind all { + set marker [%W marker get current] + catch { %W marker configure $marker -bg green} +} + +.graph marker bind all { + 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 { + set coords [%W invtransform %x %y] + catch { %W marker configure [%W marker get current] -coords $coords } +} + +.graph marker bind all { + set marker [%W marker get current] + catch { %W marker configure $marker -fill green -outline black} +} + +.graph marker bind all { + 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 { 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 { + StartAnimation [TranslateBusy %W] 0 + } + bind Busy { + 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 { 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 { + StartAnimation [TranslateBusy %W] 0 + } + bind Busy { + 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 { 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 { RedrawWorld %W } + +# ---------------------------------------------------------------------- +# USAGE: random ?? ?? +# +# Returns a random number in the range to . +# If 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 {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 \ + "$canvas configure -cursor {}" + $canvas bind $tagName-image \ + "SaveImageCoords $canvas %x %y" + $canvas bind $tagName-image \ + "MoveEpsItem $canvas $tagName %x %y" + + foreach grip { sw ne se nw } { + $canvas bind $tagName-$grip \ + "StartResize $canvas $tagName %x %y $grip" + $canvas bind $tagName-$grip \ + "ResizeEpsItem $canvas $grip $tagName %x %y" + $canvas bind $tagName-$grip \ + "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 { + %W legend activate [%W element get current] +} +.g element bind all { + %W legend deactivate [%W element get current] +} +.g axis bind all { + set axis [%W axis get current] + %W axis configure $axis -background lightblue2 +} +.g axis bind all { + 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 { MakeSnapshot } +bind $graph { + %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 { + 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 { + %W legend activate [%W element get current] +} + +$graph element bind all { + %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 { HighlightTrace %W } +$graph legend bind all { + %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 { + %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 { .h text get %x %y ; focus .h.edit } + +.h column bind all { + %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 { .h text get %x %y ; focus .h.edit } + +.h column bind all { + %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 {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 + 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 "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 { 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 { + %W select to @%x,%y +} +bind .htext <1> { + %W select from @%x,%y + %W select to @%x,%y +} + +bind .htext { + %W select word @%x,%y +} +bind .htext { + %W select line @%x,%y +} +bind .htext { + puts stderr [%W select index @%x,%y] +} + +bind .htext { + %W scan dragto @%x,%y +} +bind .htext <2> { + %W scan mark @%x,%y +} + +bind .htext <3> { + %W select adjust @%x,%y +} + +bind .htext { + set line [%W gotoline] + if { $line == 0 } { + bell + } else { + set line [expr $line-1] + %W gotoline $line.0 + } +} +bind .htext { + set line [%W gotoline] + incr line + if { [%W gotoline $line.0] != $line } { + bell + } +} + +bind .htext { + %W yview [expr [%W yview]+10] +} + +bind .htext { + %W yview [expr [%W yview]-10] +} + +bind .htext { + %W yview [expr [%W yview]-10] +} + +bind .htext { + exit 0 +} +bind .htext { + 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 Binary files /dev/null and b/blt/demos/images/blt98.gif differ diff --git a/blt/demos/images/buckskin.gif b/blt/demos/images/buckskin.gif new file mode 100644 index 00000000000..e2d7be9d628 Binary files /dev/null and b/blt/demos/images/buckskin.gif differ diff --git a/blt/demos/images/chalk.gif b/blt/demos/images/chalk.gif new file mode 100644 index 00000000000..30d29a72216 Binary files /dev/null and b/blt/demos/images/chalk.gif differ diff --git a/blt/demos/images/close.gif b/blt/demos/images/close.gif new file mode 100644 index 00000000000..02f83638cf9 Binary files /dev/null and b/blt/demos/images/close.gif differ diff --git a/blt/demos/images/close2.gif b/blt/demos/images/close2.gif new file mode 100644 index 00000000000..09cbdffc929 Binary files /dev/null and b/blt/demos/images/close2.gif differ diff --git a/blt/demos/images/clouds.gif b/blt/demos/images/clouds.gif new file mode 100644 index 00000000000..49d15c26195 Binary files /dev/null and b/blt/demos/images/clouds.gif differ diff --git a/blt/demos/images/corrugated_metal.gif b/blt/demos/images/corrugated_metal.gif new file mode 100644 index 00000000000..f212bd76741 Binary files /dev/null and b/blt/demos/images/corrugated_metal.gif differ diff --git a/blt/demos/images/folder.gif b/blt/demos/images/folder.gif new file mode 100644 index 00000000000..86eb798c7f4 Binary files /dev/null and b/blt/demos/images/folder.gif 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 Binary files /dev/null and b/blt/demos/images/jan25_palm3x_L.jpg differ diff --git a/blt/demos/images/mini-book1.gif b/blt/demos/images/mini-book1.gif new file mode 100644 index 00000000000..b8868dcd4da Binary files /dev/null and b/blt/demos/images/mini-book1.gif differ diff --git a/blt/demos/images/mini-book2.gif b/blt/demos/images/mini-book2.gif new file mode 100644 index 00000000000..266251a078e Binary files /dev/null and b/blt/demos/images/mini-book2.gif differ diff --git a/blt/demos/images/mini-display.gif b/blt/demos/images/mini-display.gif new file mode 100644 index 00000000000..a2416fbe31b Binary files /dev/null and b/blt/demos/images/mini-display.gif differ diff --git a/blt/demos/images/mini-doc.gif b/blt/demos/images/mini-doc.gif new file mode 100644 index 00000000000..30343205dc3 Binary files /dev/null and b/blt/demos/images/mini-doc.gif differ diff --git a/blt/demos/images/mini-filemgr.gif b/blt/demos/images/mini-filemgr.gif new file mode 100644 index 00000000000..cd16bc2624f Binary files /dev/null and b/blt/demos/images/mini-filemgr.gif differ diff --git a/blt/demos/images/mini-ofolder.gif b/blt/demos/images/mini-ofolder.gif new file mode 100644 index 00000000000..78d7436e342 Binary files /dev/null and b/blt/demos/images/mini-ofolder.gif differ diff --git a/blt/demos/images/mini-windows.gif b/blt/demos/images/mini-windows.gif new file mode 100644 index 00000000000..5a504fc4a1b Binary files /dev/null and b/blt/demos/images/mini-windows.gif differ diff --git a/blt/demos/images/ofolder.gif b/blt/demos/images/ofolder.gif new file mode 100644 index 00000000000..1238fa2b435 Binary files /dev/null and b/blt/demos/images/ofolder.gif differ diff --git a/blt/demos/images/open.gif b/blt/demos/images/open.gif new file mode 100644 index 00000000000..ea826a9aa2d Binary files /dev/null and b/blt/demos/images/open.gif differ diff --git a/blt/demos/images/open2.gif b/blt/demos/images/open2.gif new file mode 100644 index 00000000000..6db5fb74546 Binary files /dev/null and b/blt/demos/images/open2.gif 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 Binary files /dev/null and b/blt/demos/images/qv100.t.gif differ diff --git a/blt/demos/images/rain.gif b/blt/demos/images/rain.gif new file mode 100644 index 00000000000..d7bb417939d Binary files /dev/null and b/blt/demos/images/rain.gif differ diff --git a/blt/demos/images/sample.gif b/blt/demos/images/sample.gif new file mode 100644 index 00000000000..1d8a4010c38 Binary files /dev/null and b/blt/demos/images/sample.gif differ diff --git a/blt/demos/images/smblue_rock.gif b/blt/demos/images/smblue_rock.gif new file mode 100644 index 00000000000..df1c8f584f9 Binary files /dev/null and b/blt/demos/images/smblue_rock.gif differ diff --git a/blt/demos/images/stopsign.gif b/blt/demos/images/stopsign.gif new file mode 100644 index 00000000000..fe4d27ea5e5 Binary files /dev/null and b/blt/demos/images/stopsign.gif differ diff --git a/blt/demos/images/tan_paper.gif b/blt/demos/images/tan_paper.gif new file mode 100644 index 00000000000..aca947da509 Binary files /dev/null and b/blt/demos/images/tan_paper.gif differ diff --git a/blt/demos/images/tan_paper2.gif b/blt/demos/images/tan_paper2.gif new file mode 100644 index 00000000000..76d94ac3460 Binary files /dev/null and b/blt/demos/images/tan_paper2.gif differ diff --git a/blt/demos/images/txtrflag.gif b/blt/demos/images/txtrflag.gif new file mode 100644 index 00000000000..4017154c344 Binary files /dev/null and b/blt/demos/images/txtrflag.gif 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 { + set coords [%W invtransform %x %y] + catch { %W marker configure [%W marker get current] -coords $coords } +} + +$graph marker bind all { + set marker [%W marker get current] + catch { %W marker configure $marker -bg green} +} + +$graph marker bind all { + 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 { DoExit 0 } +bind all { 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 "StartMove $canvas %x %y" + $canvas bind image "MoveOutline $canvas %x %y" + $canvas bind image "EndMove $canvas" + + $canvas bind image "ConstrainMoveOutline $canvas %x %y" + $canvas bind image "EnterImage $canvas" + $canvas bind image "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 "StartResize %W $grip %x %y" + $canvas bind $grip "ResizeOutline %W %x %y" + $canvas bind $grip "EndResize %W $grip %x %y" + $canvas bind $grip "EnterGrip %W $grip %x %y" + $canvas bind $grip "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 { + MoveOutline %W [expr $pageInfo(lastX) - 1] $pageInfo(lastY) + } + bind $canvas { + MoveOutline %W [expr $pageInfo(lastX) + 1] $pageInfo(lastY) + } + bind $canvas { + MoveOutline %W $pageInfo(lastX) [expr $pageInfo(lastY) - 1] + } + bind $canvas { + 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 "" + bind $canvas "" + bind $canvas "" + bind $canvas "" + $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 { + ResizeOutline %W [expr $pageInfo(lastX) - 1] $pageInfo(lastY) + } + bind $canvas { + ResizeOutline %W [expr $pageInfo(lastX) + 1] $pageInfo(lastY) + } + bind $canvas { + ResizeOutline %W $pageInfo(lastX) [expr $pageInfo(lastY) - 1] + } + bind $canvas { + 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 "" + bind $canvas "" + bind $canvas "" + bind $canvas "" + $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 { StartChange %W 0.1} + bind $w.plus { EndChange %W } + bind $w.minus { StartChange %W -0.1} + bind $w.minus { 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 { + .status config -text \ + "Press button to write color name into primary selection" +} + +bind .rgb { + .status config -text \ + "Press button to write RGB value into primary selection" +} +bind .name { + .status config -text "" +} + +bind .rgb { + .status config -text "" +} + +bind .canvas { + .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 . { +# .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 . { + .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 ?? ?? +# +# Returns a random number in the range to . +# If 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 " + .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 { + 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 { + set axis [%W axis get current] + %W axis configure $axis -background "" +} + +.sc axis bind Y { + 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 { + 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 { + %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 { + FillFields %x %y + continue +} + +bind .text { + HighlightText %x %y + continue +} + +bind .text { + 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 { + if { [busy isbusy .] != "." } { + .text tag delete highlight + .status_label configure -text "" + } + continue +} + + +bind EntryFocus { + ChangeFocus next + break +} + +bind EntryFocus { + ChangeFocus last + break +} + +if { $pq_dict(visual) == "color" } { + bind EntryFocus { + ColorFocus %W on + } + bind EntryFocus { + ColorFocus %W off + } +} + +bind Entry 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 @@ + + + + + +graph(n) manual page + + +Table of Contents

+ +

Name

+BLT - Introduction to the BLT library +

Description

+BLT +is a library of extensions to the Tk library. It adds new commands and +variables to the application's interpreter.

+ +

Commands

+The following commands +are added to the interpreter from the BLT library: +
+ +
table
+
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.
+ +
graph
+
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.
+ +
barchart
+
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 barchart widget has of several components; +coordinate axes, crosshairs, a legend, and a collection of elements and +tags.
+ +
vector
+
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.
+ +
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.
+ +
bgexec
+
Like Tcl's exec command, bgexec runs +a pipeline of Unix commands in the background. Unlike exec, the output +of the last process is collected and a global Tcl variable is set upon +its completion. bgexec can be used with tkwait to wait for Unix commands +to finish while still handling expose events. Intermediate output is also +available while the pipeline is active.
+ +
busy
+
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).
+ +
bitmap
+
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.
+ +
drag&drop +
+
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. drag&drop acts as a coordinator, directing Tk +send commands between (or within) TCL/Tk applications.
+ +
htext
+
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. Htext can be also used to create scrolled +windows of Tk widgets.
+ +
winop
+
Raise, lower, map, or, unmap any window. The +raise and lower functions are useful for stacking windows above or below +"busy windows".
+ +
watch
+
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.
+ +
bltdebug
+
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.
+
+ +

Variables

+

+The following Tcl variables are either set or used by BLT +at various times in its execution: +

+ +
blt_library
+
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 drag&drop protocol +scripts and the PostScript prolog used by graph and barchart. 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 BLT library. +
+ +
blt_versions
+
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 major.minor. Major and minor 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.
+
+ +

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.
+if (Blt_Init(interp) != TCL_OK) {
+ return TCL_ERROR;
+}
+

Recompile and link with the BLT library (libBLT.a) and that's it.

+Alternately, +you can dynamically load BLT, simply by invoking the command
+package require BLT
+

from your Tcl script. +

Bugs

+Send bug reports, requests, suggestions, etc. to + gah@siliconmetrics.com or gah@myfirstlink.net +

Keywords

+BLT

+ +


+Table of Contents

+

+ 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 @@ + + + + + +barchat(n) manual page + + +Table of Contents

+ +

Name

+barchart - Bar chart for plotting X-Y coordinate +data. +

Synopsis

+barchart pathName ?option value?... +

Description

+The barchart 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. +

Introduction

+The barchart 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 plotting area. + 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.

+A barchart 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. +

+ +
axis +
+

+ 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.

+ +
crosshairs
+
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.
+ +
element
+
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.

+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.

+ +
grid
+
Extends the major and minor ticks of +the X-axis and/or Y-axis across the plotting area.
+ +
legend
+
The legend displays +the name and symbol of each data element. The legend can be drawn in any +margin or in the plotting area.
+ +
marker
+
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.
+ +
pen
+
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 -weight and -style options).
+ +
postscript
+
The widget can generate +encapsulated PostScript output. This component has several options to configure +how the PostScript is generated.
+
+ +

Syntax

+
+

+barchart pathName ?option value?...
+

The barchart command creates a new window pathName and makes it into a +barchart widget. At the time this command is invoked, there must not exist +a window named pathName, but pathName'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 configure operation +below for the exact details about what option and value pairs are valid. +

+If successful, barchart 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: +
+

+pathName operation ?arg?...
+

Both operation and its arguments determine the exact behavior of the command. + The operations available for the graph are described in the BARCHART +OPERATIONS + section.

+The command can also be used to access components of +the graph.
+

+pathName component operation ?arg?...
+

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. +

Example

+The barchart command creates a new bar chart.
+# Create a new bar chart. Plotting area is black.
+barchart .b -plotbackground black
+

A new Tcl command .b 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 configure operation.
+# Change the title.
+.b configure -title "My Plot"
+

To add data elements, you use the command and the element component.
+# 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 }
+

The element's X-Y coordinates are specified using lists of numbers. Alternately, +BLT vectors could be used to hold the X-Y coordinates.
+# 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
+

The advantage of using vectors is that when you modify one, the graph is +automatically redrawn to reflect the new values.
+# Change the y coordinate of the first point.
+set yVector(0) 25.18
+

An element named e1 is now created in .b. 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 show operation.
+# 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]
+

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 -barwidth option.
+# 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
+

The height of each bar is proportional to the ordinate (Y-coordinate) of +the data point.

+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 -barmode configuration option), the bars are stacked, +each bar above the previous.
+# Display the elements as stacked.
+.b configure -barmode stacked
+

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.
+# Display the elements side-by-side.
+.b configure -barmode aligned
+

By default, the element's label in the legend will be also e1. You can change +the label, or specify no legend entry, again using the element's configure +operation.
+# Don't display "e1" in the legend.
+.b element configure e1 -label ""
+

You can configure more than just the element's label. An element has many +attributes such as stipple, foreground and background colors, relief, etc. +
+.b element configure e1 -fg red -bg pink \
+    -stipple gray50
+

Four coordinate axes are automatically created: x, x2, y, and y2. And by +default, elements are mapped onto the axes x and y. This can be changed +with the -mapx and -mapy options.
+# Map "e1" on the alternate y axis "y2".
+.b element configure e1 -mapy y2
+

Axes can be configured in many ways too. For example, you change the scale +of the Y-axis from linear to log using the axis component.
+# Y-axis is log scale.
+.b axis configure y -logscale yes
+

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 -min and +-max configuration options.
+.b axis configure x -min 1.0 -max 1.5
+.b axis configure y -min 12.0 -max 55.15
+

To zoom interactively, you link theaxis configure operations with some +user interaction (such as pressing the mouse button), using the bind command. + To convert between screen and graph coordinates, use the invtransform +operation.
+# 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]
+}
+

By default, the limits of the axis are determined from data values. To reset +back to the default limits, set the -min and -max options to the empty value. +
+# Reset the axes to autoscale again.
+.b axis configure x -min {} -max {}
+.b axis configure y -min {} -max {}
+

By default, the legend is drawn in the right margin. You can change this +or any legend configuration options using the legend component.
+# Configure the legend font, color, and relief
+.b legend configure -position left -relief raised \
+    -font fixed -fg blue
+

To prevent the legend from being displayed, turn on the -hide option.
+# Don't display the legend.
+.b legend configure -hide yes
+

The barchart 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 marker operation.
+# 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
+

This creates a line marker named low_water. 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 -under option.
+# Draw the marker before elements are drawn.
+.b marker configure low_water -under yes
+

You can add cross hairs or grid lines using the crosshairs and grid components. +
+# Display both cross hairs and grid lines.
+.b crosshairs configure -hide no -color red
+.b grid configure -hide no -dashes { 2 2 }
+

Finally, to get hardcopy of the graph, use the postscript component.
+# Print the bar chart into file "file.ps"
+.b postscript output file.ps -maxpect yes -decorations no
+

This generates a file file.ps containing the encapsulated PostScript of +the graph. The option -maxpect says to scale the plot to the size of the +page. Turning off the -decorations 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). +

Syntax

+
+

+barchart pathName ?option value?...
+

The barchart command creates a new window pathName and makes it into a +barchart widget. At the time this command is invoked, there must not exist +a window named pathName, but pathName'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 configure +operation below for the exact details as to what option and value pairs +are valid.

+If successful, barchart returns pathName. It also creates a new +Tcl command pathName. This command may be used to invoke various operations +to query or modify the bar chart. It has the general form:
+

+pathName operation ?arg?...
+

Both operation and its arguments determine the exact behavior of the command. + The operations available for the bar chart are described in the following +section. +

Barchart Operations

+ +
+ +
pathName bar elemName ?option value?...
+
Creates +a new barchart element elemName. It's an error if an element elemName already +exists. See the manual for barchart for details about what option and +value pairs are valid.
+ +
pathName cget option
+
Returns the current value of +the configuration option given by option. Option may be any option described +below for the configure operation.
+ +
pathName configure ?option value?...
+
Queries +or modifies the configuration options of the graph. If option isn't specified, +a list describing the current options for pathName is returned. If option +is specified, but not value, then a list describing option is returned. +If one or more option and value pairs are specified, then for each pair, +the option option is set to value. The following options are valid.
+ +
-background +color
+
Sets the background color. This includes the margins and legend, but +not the plotting area.
+ +
-barmode mode
+
Indicates how related bar elements +will be drawn. Related elements have data points with the same abscissas +(X-coordinates). Mode indicates how those segments should be drawn. Mode can +be infront, aligned, overlap, or stacked. The default mode is infront.
+ +
infront +
+
Each successive segment is drawn in front of the previous.
+ +
stacked
+
Each +successive segment is stacked vertically on top of the previous.
+ +
aligned +
+
Segments is displayed aligned from right-to-left.
+ +
overlap
+
Like aligned but +segments slightly overlap each other.
+
+ + +
+ +
-barwidth value
+
Specifies the width +of the bars. This value can be overrided by the individual elements using +their -barwidth configuration option. Value is the width in terms of graph +coordinates. The default width is 1.0.
+ +
-borderwidth pixels
+
Sets the width +of the 3-D border around the outside edge of the widget. The -relief option +determines if the border is to be drawn. The default is 2.
+ +
-bottommargin +pixels
+
Specifies the size of the margin below the X-coordinate axis. If +pixels is 0, the size of the margin is selected automatically. The default +is 0.
+ +
-bufferelements boolean
+
Indicates whether an internal pixmap to buffer +the display of data elements should be used. If boolean 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 SPEED TIPS + section. The default +is 1.
+ +
-cursor cursor
+
Specifies the widget's cursor. The default cursor is +crosshair.
+ +
-font fontName
+
Specifies the font of the graph title. The default +is *-Helvetica-Bold-R-Normal-*-18-180-*.
+ +
-halo pixels
+
Specifies a maximum distance +to consider when searching for the closest data point (see the element's +closest operation below). Data points further than pixels away are ignored. + The default is 0.5i.
+ +
-height pixels
+
Specifies the requested height of widget. + The default is 4i.
+ +
-invertxy boolean
+
Indicates whether the placement X-axis +and Y-axis should be inverted. If boolean is true, the X and Y axes are +swapped. The default is 0.
+ +
-justify justify
+
Specifies how the title should +be justified. This matters only when the title contains more than one line +of text. Justify must be left, right, or center. The default is center.
+ +
-leftmargin +pixels
+
Sets the size of the margin from the left edge of the window to + the Y-coordinate axis. If pixels is 0, the size is calculated automatically. + The default is 0.
+ +
-plotbackground color
+
Specifies the background color of +the plotting area. The default is white.
+ +
-plotborderwidth pixels
+
Sets the +width of the 3-D border around the plotting area. The -plotrelief option +determines if a border is drawn. The default is 2.
+ +
-plotpadx pad
+
Sets the +amount of padding to be added to the left and right sides of the plotting +area. Pad can be a list of one or two screen distances. If pad 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 pad is just one distance, +both the left and right sides are padded evenly. The default is 8.
+ +
-plotpady +pad
+
Sets the amount of padding to be added to the top and bottom of the +plotting area. Pad can be a list of one or two screen distances. If pad +has two elements, the top of the plotting area is padded by the first distance +and the bottom by the second. If pad is just one distance, both the top +and bottom are padded evenly. The default is 8.
+ +
-plotrelief relief
+
Specifies +the 3-D effect for the plotting area. Relief specifies how the interior +of the plotting area should appear relative to rest of the graph; for example, +raised means the plot should appear to protrude from the graph, relative +to the surface of the graph. The default is sunken.
+ +
-relief relief
+
Specifies +the 3-D effect for the barchart widget. Relief specifies how the graph should +appear relative to widget it is packed into; for example, raised means +the graph should appear to protrude. The default is flat.
+ +
-rightmargin pixels +
+
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 pixels is than +1, the margin size is selected automatically.
+ +
-takefocus focus
+
Provides +information used when moving the focus from window to window via keyboard +traversal (e.g., Tab and Shift-Tab). If focus is 0, this means that this window +should be skipped entirely during keyboard traversal. 1 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 "".
+ +
-tile image
+
Specifies a tiled background for the widget. + If image isn't "", the background is tiled using image. Otherwise, the normal +background color is drawn (see the -background option). Image must be an +image created using the Tk image command. The default is "".
+ +
-title text +
+
Sets the title to text. If text is "", no title will be displayed.
+ +
-topmargin +pixels
+
Specifies the size of the margin above the x2 axis. If pixels is +0, the margin size is calculated automatically.
+ +
-width pixels
+
Specifies the +requested width of the widget. The default is 5i.
+
+ + +
+ +
pathName crosshairs operation +?arg?
+
See the CROSSHAIRS COMPONENT + section.
+ +
pathName element operation +?arg?...
+
See the ELEMENT COMPONENTS + section.
+ +
pathName extents item
+
Returns +the size of a particular item in the graph. Item must be either leftmargin, +rightmargin, topmargin, bottommargin, plotwidth, or plotheight.
+ +
pathName +grid operation ?arg?...
+
See the GRID COMPONENT + section.
+ +
pathName invtransform +winX winY
+
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.
+ +
pathName inside x +y
+
Returns 1 is the designated screen coordinate (x and y) is inside the +plotting area and 0 otherwise.
+ +
pathName legend operation ?arg?...
+
See the +LEGEND COMPONENT + section.
+ +
pathName line operation arg...
+
The operation is +the same as element.
+ +
pathName marker operation ?arg?...
+
See the MARKER COMPONENTS + + section.
+ +
pathName metafile ?fileName?
+
This operation is for Window platforms +only. Creates a Windows enhanced metafile of the barchart. If present, +fileName is the file name of the new metafile. Otherwise, the metafile is +automatically added to the clipboard.
+ +
pathName postscript operation ?arg?... +
+
See the POSTSCRIPT COMPONENT + section.
+ +
pathName snap photoName
+
Takes a +snapshot of the graph and stores the contents in the photo image photoName. + PhotoName is the name of a Tk photo image that must already exist.
+ +
pathName +transform x y
+
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.
+ +
pathName xaxis operation ?arg?...
+
+ +
pathName +x2axis operation ?arg?...
+
+ +
pathName yaxis operation ?arg?...
+
+ +
pathName y2axis +operation ?arg?...
+
See the AXIS COMPONENTS + section.
+
+ +

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. +

Axis Components

+Four +coordinate axes are automatically created: two X-coordinate axes (x and +x2) and two Y-coordinate axes (y, and y2). By default, the axis x is located +in the bottom margin, y in the left margin, x2 in the top margin, and y2 +in the right margin.

+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.

+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.

+You can create and use several axes. To create an axis, invoke the +axis component and its create operation.
+# Create a new axis called "temperature"
+.b axis create temperature
+

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.
+# Now map the temperature data to this axis.
+.b element create "temp" -xdata $x -ydata $tempData \
+ -mapy temperature
+

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.

+To display a different axis, you +invoke one of the following components: xaxis, yaxis, x2axis, and y2axis. +The use operation designates the axis to be drawn in the corresponding +margin: xaxis in the bottom, yaxis in the left, x2axis in the top, and +y2axis in the right.
+# Display the axis temperature in the left margin.
+.b yaxis use temperature
+

+

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.

+ +

+ +
pathName axis cget axisName +option
+
Returns the current value of the option given by option for axisName. + Option may be any option described below for the axis configure operation. +
+ +
pathName axis configure axisName ?axisName?... ?option value?...
+
Queries or modifies +the configuration options of axisName. Several axes can be changed. If option +isn't specified, a list describing all the current options for axisName +is returned. If option is specified, but not value, then a list describing +option is returned. If one or more option and value pairs are specified, +then for each pair, the axis option option is set to value. The following +options are valid for axes.
+ +
-autorange range
+
Sets the range of values for +the axis to range. The axis limits are automatically reset to display the +most recent data points in this range. If range is 0.0, the range is determined +from the limits of the data. If -min or -max are specified, they override +this option. The default is 0.0.
+ +
-color color
+
Sets the color of the axis and +tick labels. The default is black.
+ +
-command prefix
+
Specifies a Tcl command +to be invoked when formatting the axis tick labels. Prefix 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 "" is returned, no label will appear next to the tick. You can +get the standard tick labels again by setting prefix to "". The default +is "".

+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.

+ +
-descending boolean
+
Indicates +whether the values along the axis are monotonically increasing or decreasing. + If boolean is true, the axis values will be decreasing. The default is +0.
+ +
-hide boolean
+
Indicates whether the axis is displayed.
+ +
-justify justify +
+
Specifies how the axis title should be justified. This matters only when +the axis title contains more than one line of text. Justify must be left, +right, or center. The default is center.
+ +
-limits formatStr
+
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. + FormatStr 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 "" is given as either description, then the +that limit will not be displayed. The default is "".
+ +
-linewidth pixels
+
Sets +the width of the axis and tick lines. The default is 1 pixel.
+ +
-logscale boolean +
+
Indicates whether the scale of the axis is logarithmic or linear. If boolean +is true, the axis is logarithmic. The default scale is linear.
+ +
-loose boolean +
+
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 boolean +is true, the axis range is "loose". The default is 0.
+ +
-majorticks majorList +
+
Specifies where to display major axis ticks. You can use this option to +display ticks at non-uniform intervals. MajorList is a list of axis coordinates +designating the location of major ticks. No minor ticks are drawn. If majorList +is "", major ticks will be automatically computed. The default is "".
+ +
-max +value
+
Sets the maximum limit of axisName. Any data point greater than +value is not displayed. If value is "", the maximum limit is calculated +using the largest data value. The default is "".
+ +
-min value
+
Sets the minimum +limit of axisName. Any data point less than value is not displayed. If +value is "", the minimum limit is calculated using the smallest data value. +The default is "".
+ +
-minorticks minorList
+
Specifies where to display minor +axis ticks. You can use this option to display minor ticks at non-uniform +intervals. MinorList 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 -majortick +option is also set. If minorList is "", minor ticks will be automatically +computed. The default is "".
+ +
-rotate theta
+
Specifies the how many degrees +to rotate the axis tick labels. Theta is a real value representing the number +of degrees to rotate the tick labels. The default is 0.0 degrees.
+ +
-shiftby +value
+
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 value. You can use this option to prevent the axis limits +from being recomputed at each new time point. If value is 0.0, then no automatic +shifting is down. The default is 0.0.
+ +
-showticks boolean
+
Indicates whether +axis ticks should be drawn. If boolean is true, ticks are drawn. If false, +only the axis line is drawn. The default is 1.
+ +
-stepsize value
+
Specifies the +interval between major axis ticks. If value isn't a valid interval (must +be less than the axis range), the request is ignored and the step size +is automatically calculated.
+ +
-subdivisions number
+
Indicates how many minor +axis ticks are to be drawn. For example, if number is two, only one minor +tick is drawn. If number is one, no minor ticks are displayed. The default +is 2.
+ +
-tickfont fontName
+
Specifies the font for axis tick labels. The default +is *-Courier-Bold-R-Normal-*-100-*.
+ +
-ticklength pixels
+
Sets the length of major +and minor ticks (minor ticks are half the length of major ticks). If pixels +is less than zero, the axis will be inverted with ticks drawn pointing +towards the plot. The default is 0.1i.
+ +
-title text
+
Sets the title of the axis. +If text is "", no axis title will be displayed.
+ +
-titlecolor color
+
Sets +the color of the axis title. The default is black.
+ +
-titlefont fontName
+
Specifies +the font for axis title. The default is *-Helvetica-Bold-R-Normal-*-14-140-*.
+
+

+Axis +configuration options may be also be set by the option command. The resource +class is Axis. The resource names are the names of the axes (such as x +or x2).
+option add *Barchart.Axis.Color blue
+option add *Barchart.x.LogScale true
+option add *Barchart.x2.LogScale false
+ + +

+ +

pathName axis create axisName ?option value?...

+
Creates a new axis by the +name axisName. No axis by the same name can already exist. Option and value +are described in above in the axis configure operation.
+ +
pathName axis delete +?axisName?...
+
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.
+ +
pathName +axis invtransform axisName value
+
Performs the inverse transformation, changing +the screen coordinate value to a graph coordinate, mapping the value mapped +to axisName. Returns the graph coordinate.
+ +
pathName axis limits axisName +
+
Returns a list of the minimum and maximum limits for axisName. The order +of the list is min max.
+ +
pathName axis names ?pattern?...
+
Returns a list of +axes matching zero or more patterns. If no pattern argument is give, the +names of all axes are returned.
+ +
pathName axis transform axisName value
+
Transforms +the coordinate value to a screen coordinate by mapping the it to axisName. + Returns the transformed screen coordinate.
+
+

+Only four axes can be displayed +simultaneously. By default, they are x, y, x2, and y2. You can swap in +a different axis with use operation of the special axis components: xaxis, +x2axis, yaxis, and y2axis.
+.g create axis temp
+.g create axis time
+...
+.g xaxis use temp
+.g yaxis use time
+

Only the axes specified for use are displayed on the screen.

+The xaxis, +x2axis, yaxis, and y2axis components operate on an axis location rather +than a specific axis like the more general axis component does. The xaxis +component manages the X-axis located in the bottom margin (whatever axis +that happens to be). Likewise, yaxis uses the Y-axis in the left margin, +x2axis the top X-axis, and y2axis the right Y-axis.

+They implicitly control +the axis that is currently using to that location. By default, xaxis uses +the x axis, yaxis uses y, x2axis uses x2, and y2axis uses y2. These components +can be more convenient to use than always determining what axes are current +being displayed by the graph.

+The following operations are available for +axes. They mirror exactly the operations of the axis component. The axis +argument must be xaxis, x2axis, yaxis, or y2axis. +

+ +
pathName axis cget option +
+
+ +
pathName axis configure ?option value?...
+
+ +
pathName axis invtransform value +
+
+ +
pathName axis limits
+
+ +
pathName axis transform value
+
+ +
pathName axis use ?axisName? +
+
Designates the axis axisName is to be displayed at this location. AxisName +can not be already in use at another location. This command returns the +name of the axis currently using this location.
+
+ +

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.

+The following operations are available for cross hairs: +

+ +
pathName +crosshairs cget option
+
Returns the current value of the cross hairs configuration +option given by option. Option may be any option described below for the +cross hairs configure operation.
+ +
pathName crosshairs configure ?option value?... +
+
Queries or modifies the configuration options of the cross hairs. If +option isn't specified, a list describing all the current options for the +cross hairs is returned. If option is specified, but not value, then a +list describing option is returned. If one or more option and value pairs +are specified, then for each pair, the cross hairs option option is set +to value. The following options are available for cross hairs.
+ +
-color color +
+
Sets the color of the cross hairs. The default is black.
+ +
-dashes dashList +
+
Sets the dash style of the cross hairs. DashList 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 dashList is "", the +cross hairs will be solid lines.
+ +
-hide boolean
+
Indicates whether cross hairs +are drawn. If boolean is true, cross hairs are not drawn. The default is +yes.
+ +
-linewidth pixels
+
Set the width of the cross hair lines. The default +is 1.
+ +
-position pos
+
Specifies the screen position where the cross hairs +intersect. Pos must be in the form "@x,y", where x and y are the window +coordinates of the intersection.
+
+

+Cross hairs configuration options may be +also be set by the option command. The resource name and class are crosshairs +and Crosshairs respectively.
+option add *Barchart.Crosshairs.LineWidth 2
+option add *Barchart.Crosshairs.Color red
+ + +

+ +

pathName crosshairs off

+
Turns off the cross hairs.
+ +
pathName crosshairs +on
+
Turns on the display of the cross hairs.
+ +
pathName crosshairs toggle +
+
Toggles the current state of the cross hairs, alternately mapping and unmapping +the cross hairs.
+
+ +

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.

+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. +

+The following operations are available for elements. +

+ +
pathName element activate +elemName ?index?...
+
Specifies the data points of element elemName to be drawn +using active foreground and background colors. ElemName is the name of +the element and index is a number representing the index of the data point. +If no indices are present then all data points become active.
+ +
pathName element +bind tagName ?sequence? ?command?
+
Associates command with tagName such +that whenever the event sequence given by sequence occurs for an element +with this tag, command will be invoked. The syntax is similar to the bind +command except that it operates on graph elements, rather than widgets. +See the bind manual entry for complete details on sequence and the substitutions +performed on command before invoking it.

+If all arguments are specified +then a new binding is created, replacing any existing binding for the +same sequence and tagName. If the first character of command is + then command + augments an existing binding rather than replacing it. If no command argument +is provided then the command currently associated with tagName and sequence +(it's an error occurs if there's no such binding) is returned. If both command +and sequence are missing then a list of all the event sequences for which +bindings have been defined for tagName.

+ +
pathName element cget elemName +option
+
Returns the current value of the element configuration option given +by option. Option may be any of the options described below for the element +configure operation.
+ +
pathName element closest x y ?option value?... ?elemName?... +
+
Finds the data point representing the bar closest to the window coordinates +x and y in the element elemName. ElemName 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, "" is returned. + The following option-value pairs are available.
+ +
-halo pixels
+
Specifies a +threshold distance where selected data points are ignored. Pixels is a valid +screen distance, such as 2 or 1.2i. If this option isn't specified, then it +defaults to the value of the barchart's -halo option.
+
+ + +
+ +
pathName element configure +elemName ?elemName... ?option value?...
+
Queries or modifies the configuration +options for elements. Several elements can be modified at the same time. +If option isn't specified, a list describing all the current options for +elemName is returned. If option is specified, but not value, then a list +describing the option option is returned. If one or more option and value +pairs are specified, then for each pair, the element option option is set +to value. The following options are valid for elements.
+ +
-activepen penName +
+
Specifies pen to use to draw active element. If penName is "", no active +elements will be drawn. The default is activeLine.
+ +
-bindtags tagList
+
Specifies +the binding tags for the element. TagList 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 all.
+ +
-background color
+
Sets the the color +of the border around each bar. The default is white.
+ +
-barwidth value
+
Specifies +the width the bars drawn for the element. Value is the width in X-coordinates. + If this option isn't specified, the width of each bar is the value of the +widget's -barwidth option.
+ +
-baseline value
+
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 0.0.
+ +
-borderwidth +pixels
+
Sets the border width of the 3-D border drawn around the outside +of each bar. The -relief option determines if such a border is drawn. Pixels +must be a valid screen distance like 2 or 0.25i. The default is 2.
+ +
-data coordList +
+
Specifies the X-Y coordinates of the data. CoordList is a list of numeric +expressions representing the X-Y coordinate pairs of each data point.
+ +
-foreground +color
+
Sets the color of the interior of the bars.
+ +
-hide boolean
+
Indicates +whether the element is displayed. The default is no.
+ +
-label text
+
Sets the +element's label in the legend. If text is "", the element will have no entry +in the legend. The default label is the element's name.
+ +
-mapx xAxis
+
Selects +the X-axis to map the element's X-coordinates onto. XAxis must be the name +of an axis. The default is x.
+ +
-mapy yAxis
+
Selects the Y-axis to map the element's +Y-coordinates onto. YAxis must be the name of an axis. The default is y.
+ +
-relief +string
+
Specifies the 3-D effect desired for bars. Relief indicates how the +interior of the bar should appear relative to the surface of the chart; +for example, raised means the bar should appear to protrude from the surface +of the plotting area. The default is raised.
+ +
-stipple bitmap
+
Specifies a +stipple pattern with which to draw the bars. If bitmap is "", then the +bar is drawn in a solid fashion.
+ +
-xdata xVector
+
Specifies the x-coordinate +vector of the data. XVector is the name of a BLT vector or a list of numeric +expressions.
+ +
-ydata yVector
+
Specifies the y-coordinate vector of the data. +YVector is the name of a BLT vector or a list of numeric expressions. +
+
+

+Element configuration options may also be set by the option command. The +resource names in the option database are prefixed by elem.
+option add *Barchart.Element.background blue
+ + +

+ +

pathName element create elemName ?option value?...

+
Creates a new element elemName. + Element names must be unique, so an element elemName may not already exist. + If additional arguments are present, they specify any of the element options +valid for element configure operation.
+ +
pathName element deactivate pattern... +
+
Deactivates all the elements matching pattern for the graph. Elements +whose names match any of the patterns given are redrawn using their normal +colors.
+ +
pathName element delete ?pattern?...
+
Deletes all the elements matching +pattern for the graph. Elements whose names match any of the patterns +given are deleted. The graph will be redrawn without the deleted elements. +
+ +
pathName element exists elemName
+
Returns 1 if an element elemName currently +exists and 0 otherwise.
+ +
pathName element names ?pattern?...
+
Returns the elements +matching one or more pattern. If no pattern is given, the names of all +elements is returned.
+ +
pathName element show ?nameList?
+
Queries or modifies +the element display list. The element display list designates the elements +drawn and in what order. NameList is a list of elements to be displayed +in the order they are named. If there is no nameList argument, the current +display list is returned.
+ +
pathName element type elemName
+
Returns the type +of elemName. If the element is a bar element, the commands returns the +string "bar", otherwise it returns "line".
+
+ +

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. +
+ +
pathName grid cget option
+
Returns the current value of the grid line +configuration option given by option. Option may be any option described +below for the grid configure operation.
+ +
pathName grid configure ?option +value?...
+
Queries or modifies the configuration options for grid lines. If +option isn't specified, a list describing all the current grid options for +pathName is returned. If option is specified, but not value, then a list +describing option is returned. If one or more option and value pairs are +specified, then for each pair, the grid line option option is set to value. + The following options are valid for grid lines.
+ +
-color color
+
Sets the color +of the grid lines. The default is black.
+ +
-dashes dashList
+
Sets the dash style +of the grid lines. DashList 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 dashList is "", the grid will be solid lines. +
+ +
-hide boolean
+
Indicates whether the grid should be drawn. If boolean is true, +grid lines are not shown. The default is yes.
+ +
-linewidth pixels
+
Sets the width +of grid lines. The default width is 1.
+ +
-mapx xAxis
+
Specifies the X-axis to +display grid lines. XAxis must be the name of an axis or "" for no grid +lines. The default is "".
+ +
-mapy yAxis
+
Specifies the Y-axis to display grid +lines. YAxis must be the name of an axis or "" for no grid lines. The default +is y.
+ +
-minor boolean
+
Indicates whether the grid lines should be drawn for +minor ticks. If boolean is true, the lines will appear at minor tick intervals. + The default is 1.
+
+

+Grid configuration options may also be set by the option +command. The resource name and class are grid and Grid respectively.
+option add *Barchart.grid.LineWidth 2
+option add *Barchart.Grid.Color black
+ + +

+ +

pathName grid off

+
Turns off the display the grid lines.
+ +
pathName grid on +
+
Turns on the display the grid lines.
+ +
pathName grid toggle
+
Toggles the display +of the grid.
+
+ +

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.

+The following operations +are valid for the legend. +

+ +
pathName legend activate pattern...
+
Selects legend +entries to be drawn using the active legend colors and relief. All entries +whose element names match pattern are selected. To be selected, the element +name must match only one pattern.
+ +
pathName legend bind tagName ?sequence? + ?command?
+
Associates command with tagName such that whenever the event +sequence given by sequence occurs for a legend entry with this tag, command +will be invoked. Implicitly the element names in the entry are tags. The +syntax is similar to the bind command except that it operates on legend +entries, rather than widgets. See the bind manual entry for complete details +on sequence and the substitutions performed on command before invoking +it.

+If all arguments are specified then a new binding is created, replacing + any existing binding for the same sequence and tagName. If the first character +of command is + then command augments an existing binding rather than +replacing it. If no command argument is provided then the command currently +associated with tagName and sequence (it's an error occurs if there's no +such binding) is returned. If both command and sequence are missing then +a list of all the event sequences for which bindings have been defined +for tagName.

+ +
pathName legend cget option
+
Returns the current value of a +legend configuration option. Option may be any option described below in +the legend configure operation.
+ +
pathName legend configure ?option value?... +
+
Queries or modifies the configuration options for the legend. If option +isn't specified, a list describing the current legend options for pathName +is returned. If option is specified, but not value, then a list describing +option is returned. If one or more option and value pairs are specified, +then for each pair, the legend option option is set to value. The following +options are valid for the legend.
+ +
-activebackground color
+
Sets the background +color for active legend entries. All legend entries marked active (see +the legend activate operation) are drawn using this background color.
+ +
-activeborderwidth +pixels
+
Sets the width of the 3-D border around the outside edge of the active +legend entries. The default is 2.
+ +
-activeforeground color
+
Sets the foreground +color for active legend entries. All legend entries marked as active (see +the legend activate operation) are drawn using this foreground color.
+ +
-activerelief +relief
+
Specifies the 3-D effect desired for active legend entries. Relief +denotes how the interior of the entry should appear relative to the legend; +for example, raised means the entry should appear to protrude from the +legend, relative to the surface of the legend. The default is flat.
+ +
-anchor +anchor
+
Tells how to position the legend relative to the positioning point +for the legend. This is dependent on the value of the -position option. +The default is center.
+ +
left or right
+
The anchor describes how to position +the legend vertically.
+ +
top or bottom
+
The anchor describes how to position +the legend horizontally.
+ +
@x,y
+
The anchor specifies how to position the +legend relative to the positioning point. For example, if anchor is center +then the legend is centered on the point; if anchor is n 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.
+ +
plotarea
+
The anchor +specifies how to position the legend relative to the plotting area. For +example, if anchor is center then the legend is centered in the plotting +area; if anchor is ne then the legend will be drawn such that occupies +the upper right corner of the plotting area.
+
+ + +
+ +
-background color
+
Sets the background +color of the legend. If color is "", the legend background with be transparent. +
+ +
-bindtags tagList
+
Specifies the binding tags for legend entries. TagList +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 +all.
+ +
-borderwidth pixels
+
Sets the width of the 3-D border around the outside +edge of the legend (if such border is being drawn; the relief option determines +this). The default is 2 pixels.
+ +
-font fontName
+
FontName specifies a font +to use when drawing the labels of each element into the legend. The default +is *-Helvetica-Bold-R-Normal-*-12-120-*.
+ +
-foreground color
+
Sets the foreground color +of the text drawn for the element's label. The default is black.
+ +
-hide boolean +
+
Indicates whether the legend should be displayed. If boolean is true, the +legend will not be draw. The default is no.
+ +
-ipadx pad
+
Sets the amount of +internal padding to be added to the width of each legend entry. Pad can +be a list of one or two screen distances. If pad has two elements, the +left side of the legend entry is padded by the first distance and the right +side by the second. If pad is just one distance, both the left and right +sides are padded evenly. The default is 2.
+ +
-ipady pad
+
Sets an amount of internal +padding to be added to the height of each legend entry. Pad can be a list +of one or two screen distances. If pad has two elements, the top of the +entry is padded by the first distance and the bottom by the second. If +pad is just one distance, both the top and bottom of the entry are padded +evenly. The default is 2.
+ +
-padx pad
+
Sets the padding to the left and right +exteriors of the legend. Pad can be a list of one or two screen distances. + If pad has two elements, the left side of the legend is padded by the +first distance and the right side by the second. If pad has just one distance, +both the left and right sides are padded evenly. The default is 4.
+ +
-pady +pad
+
Sets the padding above and below the legend. Pad can be a list of one +or two screen distances. If pad has two elements, the area above the legend +is padded by the first distance and the area below by the second. If pad +is just one distance, both the top and bottom areas are padded evenly. +The default is 0.
+ +
-position pos
+
Specifies where the legend is drawn. The -anchor +option also affects where the legend is positioned. If pos is left, left, +top, or bottom, the legend is drawn in the specified margin. If pos is +plotarea, then the legend is drawn inside the plotting area at a particular +anchor. If pos is in the form "@x,y", where x and y are the window coordinates, +the legend is drawn in the plotting area at the specified coordinates. +The default is right.
+ +
-raised boolean
+
Indicates whether the legend is above +or below the data elements. This matters only if the legend is in the plotting +area. If boolean is true, the legend will be drawn on top of any elements +that may overlap it. The default is no.
+ +
-relief relief
+
Specifies the 3-D effect +for the border around the legend. Relief specifies how the interior of the +legend should appear relative to the bar chart; for example, raised means +the legend should appear to protrude from the bar chart, relative to the +surface of the bar chart. The default is sunken.
+
+

+Legend configuration options +may also be set by the option command. The resource name and class are +legend and Legend respectively.
+option add *Barchart.legend.Foreground blue
+option add *Barchart.Legend.Relief raised
+ + +

+ +

pathName legend deactivate pattern...

+
Selects legend entries to be drawn using +the normal legend colors and relief. All entries whose element names match +pattern are selected. To be selected, the element name must match only +one pattern.
+ +
pathName legend get pos
+
Returns the name of the element whose +entry is at the screen position pos in the legend. Pos must be in the form +"@x,y", where x and y are window coordinates. If the given coordinates +do not lie over a legend entry, "" is returned.
+
+ +

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 -weight and -style options). +

+One pen, called activeBar, 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.
+.g pen configure "activeBar" -fg green -bg green4
+

You can create and use several pens. To create a pen, invoke the pen component +and its create operation.
+.g pen create myPen
+

You map pens to a data element using either the element's -pen or -activepen +options.
+.g element create "e1" -xdata $x -ydata $tempData \
+ -pen myPen
+

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 -styles option).
+.g element configure "e1" -styles { myPen 2.0 3.0 }
+

This says that any data point with a weight between 2.0 and 3.0 is to be +drawn using the pen myPen. All other points are drawn with the element's +default attributes.

+The following operations are available for pen components. +

+ +

+ +
pathName pen cget penName option
+
Returns the current value of the option +given by option for penName. Option may be any option described below for +the pen configure operation.
+ +
pathName pen configure penName ?penName... ?option +value?...
+
Queries or modifies the configuration options of penName. Several +pens can be modified at once. If option isn't specified, a list describing +the current options for penName is returned. If option is specified, but +not value, then a list describing option is returned. If one or more option +and value pairs are specified, then for each pair, the pen option option +is set to value. The following options are valid for pens.
+ +
-background color +
+
Sets the the color of the border around each bar. The default is white. +
+ +
-borderwidth pixels
+
Sets the border width of the 3-D border drawn around +the outside of each bar. The -relief option determines if such a border +is drawn. Pixels must be a valid screen distance like 2 or 0.25i. The default +is 2.
+ +
-foreground color
+
Sets the color of the interior of the bars.
+ +
-relief +string
+
Specifies the 3-D effect desired for bars. Relief indicates how the +interior of the bar should appear relative to the surface of the chart; +for example, raised means the bar should appear to protrude from the bar +chart, relative to the surface of the plotting area. The default is raised. +
+ +
-stipple bitmap
+
Specifies a stipple pattern with which to draw the bars. + If bitmap is "", then the bar is drawn in a solid fashion.
+ +
-type elemType +
+
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".
+
+

+Pen configuration options may be also be set by the +option command. The resource class is Pen. The resource names are the names +of the pens.
+option add *Barchart.Pen.Foreground     blue
+option add *Barchart.activeBar.foreground green
+ + +

+ +

pathName pen create penName ?option value?...

+
Creates a new pen by the name +penName. No pen by the same name can already exist. Option and value are +described in above in the pen configure operation.
+ +
pathName pen delete +?penName?...
+
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.
+ +
pathName +pen names ?pattern?...
+
Returns a list of pens matching zero or more patterns. + If no pattern argument is give, the names of all pens are returned.
+
+ +

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. +

+The following postscript operations are available. +

+ +
pathName postscript cget +option
+
Returns the current value of the postscript option given by option. + Option may be any option described below for the postscript configure +operation.
+ +
pathName postscript configure ?option value?...
+
Queries or modifies +the configuration options for PostScript generation. If option isn't specified, +a list describing the current postscript options for pathName is returned. + If option is specified, but not value, then a list describing option is +returned. If one or more option and value pairs are specified, then for +each pair, the postscript option option is set to value. The following +postscript options are available.
+ +
-center boolean
+
Indicates whether the plot +should be centered on the PostScript page. If boolean is false, the plot +will be placed in the upper left corner of the page. The default is 1.
+ +
-colormap +varName
+
VarName must be the name of a global array variable that specifies +a color mapping from the X color name to PostScript. Each element of varName +must consist of PostScript code to set a particular color value (e.g. ``1.0 +1.0 0.0 setrgbcolor''). When generating color information in PostScript, the +array variable varName 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 varName +for a given color, then it uses the red, green, and blue intensities from +the X color.
+ +
-colormode mode
+
Specifies how to output color information. Mode +must be either color (for full color output), gray (convert all colors +to their gray-scale equivalents) or mono (convert foreground colors to black +and background colors to white). The default mode is color.
+ +
-fontmap varName +
+
VarName must be the name of a global array variable that specifies a font +mapping from the X font name to PostScript. Each element of varName 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 varName 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 Adobe (such as Times, +Helvetica, Courier, etc.). If all of this fails then the font defaults to +Helvetica-Bold.
+ +
-decorations boolean
+
Indicates whether PostScript commands +to generate color backgrounds and 3-D borders will be output. If boolean +is false, the graph will background will be white and no 3-D borders will +be generated. The default is 1.
+ +
-height pixels
+
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 pixels is 0, the height is the same as the widget's +height. The default is 0.
+ +
-landscape boolean
+
If boolean 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 0.
+ +
-maxpect boolean
+
Indicates +to scale the plot so that it fills the PostScript page. The aspect ratio +of the barchart is still retained. The default is 0.
+ +
-padx pad
+
Sets the horizontal +padding for the left and right page borders. The borders are exterior to +the plot. Pad can be a list of one or two screen distances. If pad has +two elements, the left border is padded by the first distance and the right +border by the second. If pad has just one distance, both the left and right +borders are padded evenly. The default is 1i.
+ +
-pady pad
+
Sets the vertical +padding for the top and bottom page borders. The borders are exterior to +the plot. Pad can be a list of one or two screen distances. If pad has +two elements, the top border is padded by the first distance and the bottom +border by the second. If pad has just one distance, both the top and bottom +borders are padded evenly. The default is 1i.
+ +
-paperheight pixels
+
Sets the +height of the postscript page. This can be used to select between different +page sizes (letter, A4, etc). The default height is 11.0i.
+ +
-paperwidth pixels +
+
Sets the width of the postscript page. This can be used to select between +different page sizes (letter, A4, etc). The default width is 8.5i.
+ +
-width +pixels
+
Sets the width of the plot. This lets you generate a plot of a width +different from that of the widget. If pixels is 0, the width is the same +as the widget's width. The default is 0.
+
+

+Postscript configuration options +may be also be set by the option command. The resource name and class are +postscript and Postscript respectively.
+option add *Barchart.postscript.Decorations false
+option add *Barchart.Postscript.Landscape true
+ + +

+ +

pathName postscript output ?fileName? ?option value?...

+
Outputs a file of +encapsulated PostScript. If a fileName argument isn't present, the command +returns the PostScript. If any option-value pairs are present, they set configuration +options controlling how the PostScript is generated. Option and value can +be anything accepted by the postscript configure operation above.
+
+ +

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 -under option.

+Markers, in contrast to elements, don't affect the scaling +of the coordinate axes. They can also have elastic coordinates (specified +by -Inf and Inf 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 +-Inf,-Inf.

+The following operations are available for markers. +

+ +
pathName marker +after markerId ?afterId?
+
Changes the order of the markers, drawing the +first marker after the second. If no second afterId 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.
+ +
pathName marker before markerId ?beforeId?
+
Changes +the order of the markers, drawing the first marker before the second. If +no second beforeId 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.
+ +
pathName +marker bind tagName ?sequence? ?command?
+
Associates command with tagName +such that whenever the event sequence given by sequence occurs for a marker +with this tag, command will be invoked. The syntax is similar to the bind +command except that it operates on graph markers, rather than widgets. +See the bind manual entry for complete details on sequence and the substitutions +performed on command before invoking it.

+If all arguments are specified +then a new binding is created, replacing any existing binding for the +same sequence and tagName. If the first character of command is + then command + augments an existing binding rather than replacing it. If no command argument +is provided then the command currently associated with tagName and sequence +(it's an error occurs if there's no such binding) is returned. If both command +and sequence are missing then a list of all the event sequences for which +bindings have been defined for tagName.

+ +
pathName marker cget option
+
Returns +the current value of the marker configuration option given by option. Option +may be any option described below in the configure operation.
+ +
pathName marker +configure markerId ?option value?...
+
Queries or modifies the configuration +options for markers. If option isn't specified, a list describing the current +options for markerId is returned. If option is specified, but not value, +then a list describing option is returned. If one or more option and value +pairs are specified, then for each pair, the marker option option is set +to value.

+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.

+ +
-bindtags tagList
+
Specifies the binding tags for the marker. TagList +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 all.
+ +
-coords coordList
+
Specifies the coordinates of the marker. +CoordList 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 coordList +is "", the marker will not be displayed. The default is "".
+ +
-element elemName +
+
Links the marker with the element elemName. The marker is drawn only if +the element is also currently displayed (see the element's show operation). + If elemName is "", the marker is always drawn. The default is "".
+ +
-hide +boolean
+
Indicates whether the marker is drawn. If boolean is true, the +marker is not drawn. The default is no.
+ +
-mapx xAxis
+
Specifies the X-axis +to map the marker's X-coordinates onto. XAxis must the name of an axis. The +default is x.
+ +
-mapy yAxis
+
Specifies the Y-axis to map the marker's Y-coordinates +onto. YAxis must the name of an axis. The default is y.
+ +
-name markerId
+
Changes +the identifier for the marker. The identifier markerId can not already +be used by another marker. If this option isn't specified, the marker's name +is uniquely generated.
+ +
-under boolean
+
Indicates whether the marker is drawn +below/above data elements. If boolean is true, the marker is be drawn underneath +the data elements. Otherwise, the marker is drawn on top of the element. + The default is 0.
+ +
-xoffset pixels
+
Specifies a screen distance to offset +the marker horizontally. Pixels is a valid screen distance, such as 2 or +1.2i. The default is 0.
+ +
-yoffset pixels
+
Specifies a screen distance to offset +the markers vertically. Pixels is a valid screen distance, such as 2 or +1.2i. The default is 0.
+
+

+Marker configuration options may also be set by the +option command. The resource class is either BitmapMarker, ImageMarker, + LineMarker, PolygonMarker, TextMarker, or WindowMarker, depending on the +type of marker. The resource name is the name of the marker.
+option add *Barchart.TextMarker.Foreground white
+option add *Barchart.BitmapMarker.Foreground white
+option add *Barchart.m1.Background blue
+ + +

+ +

pathName marker create type ?option value?...

+
Creates a marker of the selected +type. Type may be either text, line, bitmap, image, polygon, or window. +This command returns the marker identifier, used as the markerId argument +in the other marker-related commands. If the -name 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.
+ +
pathName marker delete +?name?...
+
Removes one of more markers. The graph will automatically be redrawn +without the marker..
+ +
pathName marker exists markerId
+
Returns 1 if the +marker markerId exists and 0 otherwise.
+ +
pathName marker names ?pattern? +
+
Returns the names of all the markers that currently exist. If pattern +is supplied, only those markers whose names match it will be returned.
+ +
pathName +marker type markerId
+
Returns the type of the marker given by markerId, +such as line or text. If markerId is not a valid a marker identifier, "" +is returned.
+
+ +

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.

+Bitmap markers are created with the marker's create operation +in the form:
+

+pathName marker create bitmap ?option value?...
+

There may be many option-value pairs, each sets a configuration options +for the marker. These same option-value pairs may be used with the marker's +configure operation.

+The following options are specific to bitmap markers: + +

+ +
-background color
+
Same as the -fill option.
+ +
-bitmap bitmap
+
Specifies the bitmap +to be displayed. If bitmap is "", the marker will not be displayed. The +default is "".
+ +
-fill color
+
Sets the background color of the bitmap. If color +is the empty string, no background will be transparent. The default background +color is "".
+ +
-foreground color
+
Same as the -outline option.
+ +
-mask mask
+
Specifies +a mask for the bitmap to be displayed. This mask is a bitmap itself, denoting +the pixels that are transparent. If mask is "", all pixels of the bitmap +will be drawn. The default is "".
+ +
-outline color
+
Sets the foreground color +of the bitmap. The default value is black.
+ +
-rotate theta
+
Sets the rotation +of the bitmap. Theta 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 0.0.
+
+ +

Image Markers

+A image marker +displays an image. Image markers are created with the marker's create operation +in the form:
+

+pathName marker create image ?option value?...
+

There may be many option-value pairs, each sets a configuration option for +the marker. These same option-value pairs may be used with the marker's configure +operation.

+The following options are specific to image markers: +

+ +
-anchor anchor +
+
Anchor tells how to position the image relative to the positioning point +for the image. For example, if anchor is center then the image is centered +on the point; if anchor is n 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 center.
+ +
-image image
+
Specifies +the image to be drawn. If image is "", the marker will not be drawn. The +default is "".
+
+ +

Line Markers

+A line marker displays one or more connected +line segments. Line markers are created with marker's create operation in +the form:
+

+pathName marker create line ?option value?...
+

There may be many option-value pairs, each sets a configuration option for +the marker. These same option-value pairs may be used with the marker's configure +operation.

+The following options are specific to line markers: +

+ +
-dashes dashList +
+
Sets the dash style of the line. DashList 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 dashList is "", the marker line +will be solid.
+ +
-fill color
+
Sets the background color of the line. This color +is used with striped lines (see the -fdashesR option). If color is the empty +string, no background color is drawn (the line will be dashed, not striped). + The default background color is "".
+ +
-linewidth pixels
+
Sets the width of +the lines. The default width is 0.
+ +
-outline color
+
Sets the foreground color +of the line. The default value is black.
+ +
-stipple bitmap
+
Specifies a stipple +pattern used to draw the line, rather than a solid line. Bitmap specifies +a bitmap to use as the stipple pattern. If bitmap is "", then the line +is drawn in a solid fashion. The default is "".
+
+ +

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 create operation in the form:
+

+pathName marker create polygon ?option value?...
+

There may be many option-value pairs, each sets a configuration option for +the marker. These same option-value pairs may be used with the marker configure +command to change the marker's configuration. The following options are supported +for polygon markers: +

+ +
-dashes dashList
+
Sets the dash style of the outline +of the polygon. DashList 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 dashList is "", the outline will be a solid +line.
+ +
-fill color
+
Sets the fill color of the polygon. If color is "", then +the interior of the polygon is transparent. The default is white.
+ +
-linewidth +pixels
+
Sets the width of the outline of the polygon. If pixels is zero, + no outline is drawn. The default is 0.
+ +
-outline color
+
Sets the color of the +outline of the polygon. If the polygon is stippled (see the -stipple option), +then this represents the foreground color of the stipple. The default is +black.
+ +
-stipple bitmap
+
Specifies that the polygon should be drawn with a +stippled pattern rather than a solid color. Bitmap specifies a bitmap to +use as the stipple pattern. If bitmap is "", then the polygon is filled +with a solid color (if the -fill option is set). The default is "".
+
+ +

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 create operation +in the form:
+

+pathName marker create text ?option value?...
+

There may be many option-value pairs, each sets a configuration option +for the text marker. These same option-value pairs may be used with the + marker's configure operation.

+The following options are specific to text +markers: +

+ +
-anchor anchor
+
Anchor tells how to position the text relative to +the positioning point for the text. For example, if anchor is center then +the text is centered on the point; if anchor is n 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 center.
+ +
-background +color
+
Same as the -fill option.
+ +
-font fontName
+
Specifies the font of the text. + The default is *-Helvetica-Bold-R-Normal-*-120-*.
+ +
-fill color
+
Sets the background +color of the text. If color is the empty string, no background will be +transparent. The default background color is "".
+ +
-foreground color
+
Same as +the -outline option.
+ +
-justify justify
+
Specifies how the text should be justified. + This matters only when the marker contains more than one line of text. +Justify must be left, right, or center. The default is center.
+ +
-outline color +
+
Sets the color of the text. The default value is black.
+ +
-padx pad
+
Sets the +padding to the left and right exteriors of the text. Pad can be a list of +one or two screen distances. If pad has two elements, the left side of +the text is padded by the first distance and the right side by the second. + If pad has just one distance, both the left and right sides are padded +evenly. The default is 4.
+ +
-pady pad
+
Sets the padding above and below the +text. Pad can be a list of one or two screen distances. If pad has two +elements, the area above the text is padded by the first distance and the +area below by the second. If pad is just one distance, both the top and +bottom areas are padded evenly. The default is 4.
+ +
-rotate theta
+
Specifies +the number of degrees to rotate the text. Theta 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 0.0.
+ +
-text text +
+
Specifies the text of the marker. The exact way the text is displayed may +be affected by other options such as -anchor or -rotate.
+
+ +

Window Markers

+A window +marker displays a widget at a given position. Window markers are created +with the marker's create operation in the form:
+

+pathName marker create window ?option value?...
+

There may be many option-value pairs, each sets a configuration option for +the marker. These same option-value pairs may be used with the marker's configure +command.

+The following options are specific to window markers: +

+ +
-anchor anchor +
+
Anchor tells how to position the widget relative to the positioning point +for the widget. For example, if anchor is center then the widget is centered +on the point; if anchor is n 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 center.
+ +
-height pixels +
+
Specifies the height to assign to the marker's window. If this option isn't +specified, or if it is specified as "", then the window is given whatever +height the widget requests internally.
+ +
-width pixels
+
Specifies the width +to assign to the marker's window. If this option isn't specified, or if it +is specified as "", then the window is given whatever width the widget +requests internally.
+ +
-window pathName
+
Specifies the widget to be managed +by the barchart. PathName must be a child of the barchart widget.
+
+ +

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 Enter, Leave, ButtonPress, Motion, and +KeyPress).

+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.

+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 -bindtags 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. +

+The -bindtags 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 -bindtags option doesn't change this. + +

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. +

+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.

+From Tcl, create the vectors and configure the element to use them. +
+vector X Y
+.g element configure line1 -xdata X -ydata Y
+

To set data points from C, you pass the values as arrays of doubles using +the Blt_ResetVector 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.
+#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;
+}
+

See the vector manual page for more details. +

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. +
  • Try to minimize the number of data points. The more data points + looked at, the more work the bar chart must do.
  • ·
  • 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.
  • ·
  • Don't stipple or dash the element. Solid +bars are much faster.
  • ·
  • If you update data elements frequently, try turning +off the widget's -bufferelements 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.
  • +
+ +

Limitations

+Auto-scale routines do not use requested min/max limits +as boundaries when the axis is logarithmically scaled.

+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. +

Keywords

+bar +chart, widget

+ +


+Table of Contents

+

+ 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 @@ + + + + + +beep(n) manual page + + +Table of Contents

+ +

Name

+beep - ring the bell +

Synopsis

+beep ?percent? +

Description

+The +beep command rings the keyboard bell. Percent is relative to the base volume +of the keyboard bell and can range from -100 to 100 inclusive.

+If percent +is nonnegative then the bell volume is:
+base - [(base * percent) / 100] + percent
+

If percent is negative then the bell volume is:
+base + [(base * percent) / 100]
+

The default percent is 50. +

Example

+
+beep
+ +

Keywords

+bell, beep

+ +


+Table of Contents

+

+ 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 @@ + + + + + +bgexec(n) manual page + + +Table of Contents

+ +

Name

+bgexec - Run programs in the background while +handling Tk events. +

Synopsis

+blt::bgexec varName ?option value?... program ?arg?... + +

Description

+

+The bgexec command executes programs in the background, allowing +Tk to handle events. A global Tcl variable varName is set when the program +has completed. +

Introduction

+Tcl's exec 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 du to get the summary.
+set out [exec du -s $dir]
+puts "Disk usage for $dir is $out"
+

While du is running, scrollbars won't respond. None of the Tk widgets will +be redrawn properly. The send command won't work. And the worst part is that +the application appears hung up or dead. The problem is that while exec +is waiting for du to finish, Tk is not able to handle X events.

+The bgexec +command performs the same functions as exec, 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. +

Example

+Here is the disk usage example again, this time using +bgexec. The syntax to invoke "du" is exactly the same as the previous example, +when we used exec.
+global myStatus myOutput
+blt::bgexec myStatus -output myOutput du -s $dir
+puts "Disk usage for $dir is $myOutput"
+

Two global variables, myStatus and myOutput, will be set by bgexec when +du has completed. MyStatus will contain the program's exit status. MyOutput, +specified by the -output option, will store the output of the program.

+You +can also terminate the program by setting the variable myStatus. If myStatus +is set before du 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 TerminateProcess. It makes no difference +what myStatus is set to.
+set myStatus {}
+

There are several bgexec options to collect different types of information. +
+global myStatus myOutput myErrs
+blt::bgexec myStatus -output myOutput -error myErrs du -s $dir
+

The -error option is similar to -output. It sets a global variable when the +program completes. The variable will contain any data written to stderr +by the program.

+The -output and -error 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 -onoutput 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.
+proc GetInfo { data } {
+ puts $data
+}
+blt::bgexec myStatus -onoutput GetInfo du -s $dir
+

When output is available, the procedure GetInfo is called. The -onerror option +performs a similar function for the stderr data stream.

+Like exec, bgexec +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 bgexec from within +a catch.
+catch { blt::bgexec myStatus -output myOutput du -s $dir }
+

By default, bgexec will wait for the program to finish. But you can detach +the program making ampersand (&) the last argument on the command line.
+global myStatus myOutput
+blt::bgexec myStatus -output myOutput du -s $dir &
+

Bgexec 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 tkwait. When the program finishes, the variable myStatus +will be written to, breaking out the tkwait command.
+global myStatus myOutput
+blt::bgexec myStatus -output myOutput du -s $dir &
+    ...
+tkwait variable myStatus
+ +

Syntax

+The bgexec command takes the following form:

+    blt::bgexec varName +?option value?... program ?arg?...

+VarName is the name of a global variable which +is set when program has finished executing. The exit status of will be +stored in varName. 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 varName. Under Unix, the program will +be sent a signal to terminate it (by default the signal is a SIGKILL; see +the -killsignal option).

+Program is the name of the program to be executed +and args are any extra arguments for program. The syntax of program and +args is the same as the exec command. So you can redirect I/O, execute pipelines, +etc. (see the exec manual for further information) just like exec. If the +last argument is an ampersand (&), the program will be run detached, and +bgexec will return immediately. VarName will still be set with the return +status when program completes. +

Options

+Option refers to the switch name always +beginning with a dash (-). Value 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 bgexec: +
+ +
-decodeerror encodingName
+

+Specifies the encoding of the stderr channel. This affects only data returned +to the Tcl interpreter. No translation is done on file redirection.
+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.
+ +
-decodeoutput encodingName
+

+Specifies the encoding of the stdout channels. This affects only data returned +to the Tcl interpreter. No translation is done on file redirection.
+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.
+ +
-error varName
+

+Specifies that a global variable varName is to be set with the contents +of stderr after the program has completed.
+ +
-keepnewline boolean
+
Specifies +that a trailing newline should be retained in the output. If boolean is +true, the trailing newline is truncated from the output of the -onoutput +and -output variables. The default value is true.
+ +
-killsignal signal
+
Specifies +the signal to be sent to the program when terminating. This is available +only under Unix. Signal can either be a number (typically 1-32) or a mnemonic +(such as SIGINT). If signal is the empty string, then no signal is sent. + The default signal is 9 (SIGKILL).
+ +
-lasterror varName
+
Specifies a variable +varName that is updated whenever data becomes available from standard error +of the program. VarName is a global variable. Unlike the -error option, data +is available as soon as it arrives.
+ +
-lastoutput varName
+
Specifies a variable +varName that is updated whenever data becomes available from standard output +of the program. VarName is a global variable. Unlike the -output option, data +is available as soon as it arrives.
+ +
-linebuffered boolean
+
Specifies that +updates should be made on a line-by-line basis. Normally when new data is +available bgexec will set the variable (-lastoutput and -lasterror options) +or invoke the command (-onoutput and -onerror options) delivering all the +new data currently available. If boolean 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 false.
+ +
-output varName
+

+Specifies that a global variable varName 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.
+ +
-onerror command
+
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. +
+ +
-onoutput command
+
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.
+ +
-update varName +
+
Deprecated. This option is replaced by -lasterror.
+ +
--
+
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 (-).
+
+ +

Preemption

+Because bgexec 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 du program +is running, the user accidently presses the button again. A second bgexec +program will preempt the first. What this means is that the first program +can not finish until the second program has completed.

+Care must be taken +to prevent an application from preempting itself by blocking further user-interactions +(such as button clicks). The BLT busy command is very useful for just these +situations. See the busy manual for details. +

Differences with Fileevent

+Since +Tk 4.0, a subset of bgexec can be also achieved using the fileevent command. + The steps for running a program in the background are:

+Execute the program +with the open command (using the "|" syntax) and save the file handle.
+global fileId
+set fileId [open "|du -s $dir" r]
+

Next register a Tcl code snippet with fileevent 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.
+fileevent fileId readable {
+ if { [gets $fileId line] < 0 } {
+    close $fileId
+    set output $temp
+    unset fileId temp
+ } else {
+    append temp $line
+ }
+}
+

+

The biggest advantage of bgexec is that, unlike fileevent, 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.

+Bgexec runs programs that fileevent can not. Fileevent assumes that +the when stdout is closed the program has completed. But some programs, +like the Unix compress program, reopen stdout, fooling fileevent 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 gets command reading a partial line.

+Bgexec +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, bgexec is faster. You get back to +the Tk event loop more quickly, making your application seem more responsive. + +

See Also

+busy, exec, tkwait +

Keywords

+exec, background, busy

+ +


+Table of Contents

+

+ 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 @@ + + + + + +bitmap(n) manual page + + +Table of Contents

+ +

Name

+bitmap - Define a new bitmap from a Tcl script + +

Synopsis

+bitmap define bitmapName data ?option value?...

+bitmap compose bitmapName +text ?option value?...

+bitmap exists bitmapName

+bitmap source bitmapName

+bitmap +data bitmapName

+bitmap height bitmapName

+bitmap width bitmapName +

Description

+The +bitmap 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. +

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 wish script, +since each bitmap must be its own file. It would be nicer if you could +create new bitmaps directly from your Tcl script.

+The bitmap 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 bitmap 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. +

Example

+<<<<<<< bitmap.mann You can define a new bitmap +with the define 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.
+label .l -bitmap @/usr/X11R6/include/X11/bitmaps/gray1
+

But you can simply cut and paste the contents of "gray1" into the bitmap +command.
+bitmap define gray1 {
+ #define gray1_width 2
+ #define gray1_height 2
+ static char gray1_bits[] = {
+ 0x01, 0x02};
+}
+label .l -bitmap gray1
+

Tk will recognize "gray1" as a bitmap which can now be used with any widget +that accepts bitmaps.
+

The bitmap data can be specified in a mulitude of forms. The following commands +are all equivalent.
+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 } }
+

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.

+You can scale or rotate the bitmap as you create it, +by using the -scale or-rotate options.
+bitmap define gray1 {
+ #define gray1_width 2
+ #define gray1_height 2
+ static char gray1_bits[] = {
+ 0x01, 0x02};
+} -scale 2.0 -rotate 90.0
+

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. +
+bitmap compose rot_text "This is rotated\ntext" \
+    -rotate 90.0 -font fixed
+

There are also a number of ways to query bitmaps. This isn't limited to +bitmaps that you create, but any bitmap.
+bitmap exists rot_text
+bitmap width rot_text
+bitmap height rot_text
+bitmap data rot_text
+bitmap source rot_text
+

The exists operation indicates if a bitmap by that name is defined. You +can query the dimensions of the bitmap using the width and height operations. +The data operation returns the list of the data used to create the bitmap. + For example, you could query the data of a bitmap and send it across +the network to another Tk application.
+set data [bitmap data @/usr/X11R6/include/X11/bitmaps/ghost.xbm]
+send {wish #2} bitmap define ghost $data
+ +

Operations

+The following operations are available for bitmap: +
+ +
bitmap compose +bitmapName text ?option value?...
+
Creates a bitmap bitmapName from the text +string text. A bitmap bitmapName can not already exist. The following options +are available.
+ +
-font fontName
+
Specifies a font to use when drawing text +into the bitmap. If this option isn't specified then fontName defaults to + *-Helvetica-Bold-R-Normal-*-140-*.
+ +
-rotate theta
+
Specifies the angle of rotation +of the text in the bitmap. Theta is a real number representing the angle +in degrees. It defaults to 0.0 degrees.
+ +
-scale value
+
Specifies the scale of +the bitmap. Value 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 1.0.
+
+ + +
+ +
bitmap data bitmapName
+
Returns +a list of both the dimensions of the bitmap bitmapName and its source data. +
+ +
bitmap define bitmapName data ?option value?...
+
Associates bitmapName with +in-memory bitmap data so that bitmapName can be used in later calls to Tk_GetBitmap. +The bitmapName argument is the name of the bitmap; it must not previously +have been defined in either a call to Tk_DefineBitmap or bitmap. The argument +data 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.
+ +
-rotate theta
+
Specifies how many degrees to rotate the bitmap. +Theta is a real number representing the angle. The default is 0.0 degrees. +
+ +
-scale value
+
Specifies how to scale the bitmap. Value 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 1.0.
+
+ + +
+ +
bitmap exists +bitmapName
+
Returns 1 if a bitmap bitmapName exists, otherwise 0.
+ +
bitmap +height bitmapName
+
Returns the height in pixels of the bitmap bitmapName. +
+ +
bitmap source bitmapName
+
Returns the source data of the bitmap bitmapName. +The source data is a list of the hexadecimal values.
+ +
bitmap width bitmapName +
+
Returns the width in pixels of the bitmap bitmapName.
+
+ +

Limitations

+Tk currently +offers no way of destroying bitmaps. Once a bitmap is created, it exists +until the application terminates. +

Keywords

+bitmap

+ +


+Table of Contents

+

+ 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 @@ + + + + + +bltdebug(n) manual page + + +Table of Contents

+ +

Name

+bltdebug - print Tcl commands before execution +

Synopsis

+bltdebug +?level? +

Description

+The bltdebug 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. Level indicates at what level to stop tracing commands. +If level is 0, no tracing is performed. This is the default. If no level +argument is given, the current level is printed. +

Keywords

+debug

+ +


+Table of Contents

+

+ 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 @@ + + + + + +busy(n) manual page + + +Table of Contents

+ +

Name

+busy - Make Tk widgets busy, temporarily blocking +user interactions. +

Synopsis

+busy hold window ?option value?...

+busy release +window ?window?...

+busy configure window ?option value?...

+busy forget window +?window?...

+busy isbusy ?pattern?

+busy names ?pattern?

+busy status window + +

Description

+

+The busy 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. +

Introduction

+

+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.

+The busy 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.

+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.

+Often, the busy command +can be used instead of Tk's grab command. Unlike grab 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). + +

Example

+You can make several widgets busy by simply making its ancestor +widget busy using the hold operation.
+frame .top
+button .top.button; canvas .top.canvas
+pack .top.button .top.canvas
+pack .top
+ . . .
+busy hold .top
+update
+

All the widgets within .top (including .top) are now busy. Using update +insures that busy command will take effect before any other user events +can occur.

+When the application is no longer busy processing, you can allow +user interactions again by the release operation.
+

    busy release .top 
+
The busy window has a configurable cursor. You can change the busy cursor +using the configure operation.
+
    busy configure .top -cursor "watch"
+
Finally, when you no longer need to the busy window, invoke the forget +operation to free any resources it allocated.
+
    busy forget .top 
+
Destroying the widget will also clean up any resources allocated by the +busy command.

+ +

Operations

+The following operations are available for the busy +command: +
+ +
busy hold window ?option value?...
+
Makes the widget window (and its +descendants in the Tk window hierarchy) busy. Window 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 update should be called immediately afterward to insure +that the hold operation is in effect before the application starts its +processing. The following configuration options are valid:
+ +
-cursor cursorName +
+
Specifies the cursor to be displayed when the widget is made busy. CursorName +can be in any form accepted by Tk_GetCursor. The default cursor is watch. +
+
+ + +
+ +
busy configure window ?option value?...
+
Queries or modifies the busy command +configuration options for window. Window must be the path name of a widget +previously made busy by the hold operation. If no options are specified, +a list describing all of the available options for window (see Tk_ConfigureInfo +for information on the format of this list) is returned. If option is specified +with no value, 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 option is specified). If one or more option-value 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. + Option may have any of the values accepted by the hold operation.

+Please +note that the option database is referenced through window. For example, +if the widget .frame is to be made busy, the busy cursor can be specified +for it by either option command:
+

    option add *frame.busyCursor gumby
+    option add *Frame.BusyCursor gumby
+
+ +
busy forget window ?window?...
+
Releases resources allocated by the busy command +for window, including the busy window. User events will again be received +again by window. Resources are also released when window is destroyed. +Window must be the name of a widget specified in the hold operation, otherwise +an error is reported.
+ +
busy isbusy ?pattern?
+
Returns the pathnames of all +widgets that are currently busy. If a pattern is given, the path names of +busy widgets matching pattern are returned.
+ +
busy names ?pattern?
+
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 pattern is given, the path +names of busy widgets matching pattern are returned.
+ +
busy release window +?window?...
+
Restores user interactions to the widget window again. This differs +from the forget operation in that the busy window is not destroyed, but +simply unmapped. Window must be the name of a widget specified in a hold +operation, otherwise an error is reported.
+ +
busy status window
+
Returns the +status of a widget window previously made busy. An error is reported if +window was never made busy, or the forget operation was invoked (i.e. does +not currently have a busy window associated with it). If window is presently +can not receive user interactions, 1 is returned, otherwise 0.

+

+
+ +

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 bind command, to handle +events in the busy window.
+busy hold .frame.canvas
+bind .frame.canvas_Busy <Enter> { ... }
+

+

Normally the busy window is a sibling of the widget. The name of the busy +window is "widget_Busy" where widget is the name of the widget to be made +busy. In the previous example, the pathname of the busy window is ".frame.canvas_Busy" +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 "widget._Busy" where widget is the name of the toplevel +widget. In the following example, the pathname of the busy window is "._Busy" +
+busy hold .
+bind ._Busy <Enter> { ... }
+ +

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. +

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.
+busy hold .frame
+label .dummy
+focus .dummy
+update
+

The above example moves the focus from .frame immediately after invoking +the hold so that no keyboard events will be sent to .frame or any of its +descendants. +

Keywords

+busy, keyboard events, pointer events, window, cursor +

+

+

+ +


+Table of Contents

+

+ 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 @@ + + + + + +container(n) manual page + + +Table of Contents

+ +

Name

+container - Widget to contain a foreign window. + +

Synopsis

+container pathName ?options? +

Description

+The container 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. +

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 frame because they are too large. The other alternative is +to pack the graphs into several toplevel 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.

+The container 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 container 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.

+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. +

Example

+You +create a container widget with the container command.
+# Create a new container
+container .c
+

A new Tcl command .c 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 configure operation.
+# Change the default font.
+.c configure -borderwidth 2
+

You can then add folders using the insert operation.
+# Create a new folder "f1"
+.c coinsert 0 "f1"
+

This inserts the new tab named "f1" into the container. The index 0 indicates +location to insert the new tab. You can also use the index end 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 -text option.
+# Change the label of "f1"
+.ts tab configure "f1" -label "Tab #1"
+

The insert operation lets you add one or more folders at a time.
+.ts insert end "f2" -label "Tab #2" "f3" "f4"
+

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 tab configure operation.
+# 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
+

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 +-window 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.
+graph .ts.graph
+.ts tab configure "f1" -window ".ts.graph" \
+ -fill both -padx 0.25i -pady 0.25i
+

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 -pagewidth +and -pageheight options override this.

+Other options control how the widget +appears in the folder. The -fill option says that you wish to have the widget +stretch to fill the available space in the folder.
+.ts tab configure "f1" -fill both -padx 0.25i -pady 0.25i
+

+

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.

+If you want to share a page between two different +folders, the -command option lets you specify a Tcl command to be invoked +whenever the folder is selected. You can reset the -window option for the +tab whenever it's clicked.
+.ts tab configure "f2" -command {
+ .ts tab configure "f2" -window ".ts.graph"
+}
+.ts tab configure "f1" -command {
+ .ts tab configure "f1" -window ".ts.graph"
+}
+

If you have many folders, you may wish to stack tabs in multiple tiers. + The container's -tiers option requests a maximum number of tiers. The default +is one tier.
+.ts configure -tiers 2
+

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.
+.ts configure -scrollcommand { .sbar set } -scrollincrement 20
+.sbar configure -orient horizontal -command { .ts view }
+

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 -side option. +
+# Arrange tabs along the right side of the container.
+.ts configure -side right -rotate 270
+ +

Syntax

+The container command creates a new window using the pathName argument +and makes it into a container widget.
+container pathName ?option value?...
+

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 container command returns its pathName argument. +At the time this command is invoked, there must not exist a window named +pathName, but pathName's parent must exist.

+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 -scrollcommand +option. They also support scanning (see the scan operation). Tabs may be +arranged along any side of the container window using the -side option.

+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.

+You can override either dimension with the container's +-width and -height options. +

Container Operations

+All container operations are +invoked by specifying the widget's pathname, the operation, and any arguments +that pertain to that operation. The general form is:

+
+    pathName operation ?arg arg ...?
+

+

Operation and the args determine the exact behavior of the command. The +following operations are available for container widgets: +

+ +
pathName cget +option
+
Returns the current value of the configuration option given by option. +Option may have any of the values accepted by the configure operation described +below.
+ +
pathName configure ?option? ?value option value ...?
+
Query or modify +the configuration options of the widget. If no option is specified, returns +a list describing all the available options for pathName (see Tk_ConfigureInfo +for information on the format of this list). If option is specified with +no value, 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 option is specified). If one or more option-value 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. Option +and value are described below:
+ +
-background color
+
Sets the border color of +the container.
+ +
-borderwidth pixels
+
Sets the width of the 3-D border around +the outside edge of the widget. The -relief option determines how the border +is to be drawn. The default is 2.
+ +
-command pattern
+
Specifies to search for +a window whose WM_COMMAND property matches the given pattern. If no windows, +or more than one window, matches the pattern, an error is generated. If +pattern is the empty string, then no command search is performed. The default +is "".
+ +
-cursor cursor
+
Specifies the widget's cursor. The default cursor is +"".
+ +
-height pixels
+
Specifies the requested height of widget. If pixels is +0, then the height is height the embedded window plus the specified borderwidth. +The default is 0.
+ +
-highlightbackground color
+
Sets the color to display in +the traversal highlight region when the container does not have the input +focus.
+ +
-highlightcolor color
+
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 black.
+ +
-highlightthickness pixels
+
Sets the width of the highlight +rectangle to draw around the outside of the widget when it has the input +focus. Pixels is a non-negative value and may have any of the forms acceptable +to Tk_GetPixels. If the value is zero, no focus highlight is drawn around +the widget. The default is 2.
+ +
-name pattern
+
Specifies to search for a window +whose WM_NAME property matches the given pattern. If no windows, or more +than one window, matches the pattern, an error is generated. If pattern +is the empty string, then no name search is performed. The default is "". +
+ +
-relief relief
+
Specifies the 3-D effect for the container widget. Relief +specifies how the container should appear relative to widget that it is +packed into; for example, raised means the container should appear to protrude. + The default is sunken.
+ +
-takefocus focus
+
Provides information used when +moving the focus from window to window via keyboard traversal (e.g., Tab +and Shift-Tab). If focus is 0, this means that this window should be skipped +entirely during keyboard traversal. 1 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 1.
+ +
-width pixels +
+
Specifies the requested width of the widget. If pixels is 0, then the +width is the width the embedded window and the specified borderwidth. The +default is 0.
+ +
-window id
+
Specifies the foreign embedded using its X window +id.
+
+ + +
+ +
pathName find -command|-name pattern
+
Searches for all windows that match +the given pattern. If the -command switch is given, all windows whose WWM_COMMAND +property match pattern are returned in a list. If the -name switch is given, +all windows whose WWM_NAME property match pattern are returned in a list. + The list returned will contains pairs of the window id and the matching +property.
+
+ +

Keywords

+container, widget

+ +


+Table of Contents

+

+ 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 @@ + + + + + +cutbuffer(n) manual page + + +Table of Contents

+ +

Name

+cutbuffer - Manipulate X cut buffer properties +

Synopsis

+cutbuffer +get ?number?
+cutbuffer rotate ?count?
+cutbuffer set value ?number? +

Description

+

+The cutbuffer command allows you +to read or modify the eight X cut buffer properties. You can also rotate +the buffers properties. +

Operations

+The following operations are available +for the cutbuffer command: +
+ +
cutbuffer get ?number?
+
Returns the value of +a cutbuffer number. Number 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 number does not exist, then "" is returned. +
+ +
cutbuffer rotate ?count?
+
Rotates the cut buffers by count. Count must be +a number between -7 and 7. The default is 1.
+ +
cutbuffer set value ?number? +
+
Sets the cutbuffer number to value. Number must be a number between 0 +and 7. The default is 0.
+
+ +

Keywords

+cut buffer, property

+ +


+Table of Contents

+

+ 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 @@ + + + + + +drag&drop(n) manual page + + +Table of Contents

+ +

Name

+drag&drop - facilities for handling drag&drop data +transfers +

Synopsis

+drag&drop source
+drag&drop source window ?options?
+drag&drop source window handler ?dataType? ?command arg arg...?

+drag&drop target +
+drag&drop target window handler ?dataType command arg arg...?

+drag&drop target +window handle dataType ?value?

+drag&drop token window

+drag&drop drag window +x y
+drag&drop drop window x y
+drag&drop active
+drag&drop errors ?proc?
+drag&drop location ?x y?

+ +

Description

+

+The drag&drop 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 source can export data to other +widgets registered as a drag&drop target. Note that a particular widget +can be registered as a source, as a target, or as both.

+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. +

+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.

+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").

+This introduction was presented as a brief +overview of the communication process; further details are presented below: + +

+ +
drag&drop source
+
Returns a list of path names for widgets registered as +drag&drop sources. Returns an empty string if no widgets have been registered. +
+ +
drag&drop source window ?options?
+
Registers a new drag&drop source window +with the given options, or modifies the options for an existing window: +
+
+

+
+

Name:    buttonBinding
+Class:    ButtonBinding
+Switch:    -button n
+
+
+ +
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:

+
+

bind win <ButtonPress-n> {drag&drop drag %W %X %Y}
+bind win <Bn-Motion> {drag&drop drag %W %X %Y}
+bind win <ButtonRelease-n> {drag&drop drop %W %X %Y}
+

+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.

+
+

Name:    packageCommand
+Class:    Command
+Switch:    -packagecmd command
+
+
+ +
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 drag&drop drag 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.
+
+

+The following substitutions +are made in the command string before it is executed:

+
+ +
%t
+
Replaced with +the window path name for the token which represents the data being dragged. +
+ +
%W
+
Replaced with the window path name for the drag&drop source.
+
+
+

+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.

+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 drag&drop location +command to determine the entry in the listbox that the user has selected +and it returns this as the data value:

+
+

proc 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
+}
+

+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.

+
+

Name:    rejectBackground
+Class:    Background
+Switch:    -rejectbg color
+
+
+ +
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.
+
+

+
+

Name:    rejectForeground
+Class:    Foreground
+Switch:    -rejectfg color
+
+
+ +
Specifies the color used to draw the foreground of the rejection symbol +
+
on the token window.
+
+

+
+

Name:    rejectStipple
+Class:    Stipple
+Switch:    -rejectstipple pattern
+
+
+ +
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.
+
+

+
+

Name:    selfTarget
+Class:    SelfTarget
+Switch:    -selftarget boolean
+
+
+ +
If the boolean 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.
+
+

+
+

Name:    send
+Class:    Send
+Switch:    -send list
+
+
+ +
Specifies a list of dataTypes enabled for communication. Only
+
dataTypes +defined by commands of the form "drag&drop source window handler ?dataType +?command arg arg...?" are allowed. This list also determines the priority +of the various dataTypes. When a token window is over a potential drag&drop +target, this list is searched from start to finish for a dataType that +is also recognized by the target. The first matching dataType found determines +the value that will be sent if the token is dropped. If no matching dataType +is found, then the target is incompatible, and is ignored. By default, +this option has the value "all", indicating that all dataTypes should be +considered in the order that they were defined for the source.
+
+

+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.

+
+

Name:    siteCommand
+Class:    Command
+Switch:    -sitecmd command
+
+
+ +
Specifies a Tcl command used to update the appearance of the token window. +
+
If specified, this command is automatically invoked by the drag&drop drag +command whenever the token window is over a compatible drag&drop target. +
+
+

+The following substitutions are made in the command string before it is +executed:

+
+ +
%s
+
Replaced with "1" if the token window is over a compatible +target, and "0" otherwise.
+ +
%t
+
Replaced with the window path name for the +token which represents the data being dragged.
+
+
+

+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.

+
+

Name:    tokenAnchor
+Class:    Anchor
+Switch:    -tokenanchor anchor
+
+
+ +
Specifies how the token window is positioned relative to the mouse
+
pointer +coordinates passed to the drag&drop drag 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".
+
+

+
+

Name:    tokenBackground
+Class:    Background
+Switch:    -tokenbg color
+
+
+ +
Specifies the color used to draw the background of the token window.
+
+
+

+
+

Name:    tokenBorderWidth
+Class:    BorderWidth
+Switch:    -tokenborderwidth size
+
+
+ +
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".
+
+

+
+

Name:    tokenCursor
+Class:    Cursor
+Switch:    -tokencursor cursor
+
+
+ +
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".
+
+ + +
+ +
drag&drop source window handler ?dataType? ?command arg arg...? +
+
With no extra arguments, this command returns a list of all dataType names +that have been registered for the source window. If only the dataType is +specified, then the dataType is created if necessary, and the command associated +with the dataType is returned. Otherwise, it concatenates the command and +any extra arg strings, and registers a new dataType with this command.
+
+

+The +following substitutions are made in the command string before it is executed: +

+
+ +
%i
+
Replaced with the name of the interpreter for the target application. +
+ +
%v
+
Replaced with the value returned from the "-packagecmd" command.
+ +
%w
+
Replaced +with the window path name for the target window.
+
+
+

+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 command handler is automatically substituted +into the "%v" fields of the target handler.

+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. +

+ +

+ +
drag&drop target
+
Returns +a list of path names for widgets registered as drag&drop targets. Returns +an empty string if no widgets have been registered.
+ +
drag&drop target window +handler ?dataType command arg arg...?
+
Registers a new drag&drop target window +with a given handler, or modifies the handlers for an existing window. +If no dataType is specified, this command returns the current list of recognized +dataType strings. Each dataType is a symbolic name representing a form +of data, and the corresponding command 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.
+
+

+The following +substitutions are made in the command string before it is executed:

+
+ +
%v +
+
In the simplest case, the source window does not have a handler command +for the selected dataType, 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.
+ +
%W
+
Replaced with the window path name for the target window. +
+
+
+ +
+ +
drag&drop target window handle dataType ?value?
+
Searches for the given dataType +name among the handlers registered for the target window, and invokes the +appropriate command. If a value is specified, it is substituted into any +"%v" fields in the handler command associated with the dataType. If the +dataType 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.
+ +
drag&drop token window
+
Returns the token window +associated with a drag&drop source window. 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,

+
+

drag&drop source .foo
+set win [drag&drop token .foo]
+label $win.label -text "Data"
+pack $win.label
+

+ +

+ +
drag&drop drag window x y
+
Marks the start of (or movement during) a drag&drop +operation. If the token window is unmapped when this command is invoked, +then the -packagecmd for the source window 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 x y location. + Unless the "-button 0" option is specified for the source, this command +is automatically bound to <ButtonPress-n> and <Bn-Motion> events for "-button +n" of the source widget.
+ +
drag&drop drop window x y
+
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 dataType 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 +"-button 0" option is specified for the source, this command is automatically +bound to the <ButtonRelease-n> event for "-button n" of the source widget.
+ +
drag&drop +active
+
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).
+ +
drag&drop errors ?proc?
+
Specifies a Tcl proc +used to handle errors encountered during drag&drop operations. If a proc +is not specified, this command returns the current error handler. By default, +all errors are sent to the usual tkerror 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 proc +name.
+ +
drag&drop location ?x y?
+
Used to set or query the pointer location +during a drag&drop operation. The x y 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 drag&drop drag and drag&drop drop commands, to keep track of pointer +movement.

+

+
+ +

Keywords

+drag&drop, send, bind, widget

+ +


+Table of Contents

+

+ 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 @@ + + + + + +graph(n) manual page + + +Table of Contents

+ +

Name

+eps - Encapsulated PostScript canvas item. +

Synopsis

+canvas +create eps x y ?option value?... +

Description

+The eps 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. +

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.

+Normally, you would have to resort to an external tool or write your +own PostScript program. The eps canvas item lets you do this through Tk's +canvas widget. An eps item displays an image (or rectangle) representing +the encapsulated PostScript file. It also scales and translates the EPS +file when the canvas is printed.

+ +

Syntax

+
+

+canvas create eps x y ?option value?...
+

The eps item creates a new canvas item. Canvas is the name of a canvas widget. + You must supply the X-Y coordinate of the new eps item. How the coordinate +is exactly interpretered is controlled by the -anchor option (see below). +

+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 option +and value pairs are valid. +

+ +
-anchor anchor
+
Tells how to position the EPS item +relative to its X-Y coordinate. The default is center.
+ +
-background color
+
Sets +the background color of the EPS rectangle.
+ +
-borderwidth pixels
+
Sets the width +of the 3-D border around the outside edge of the item. The -relief option +determines if the border is to be drawn. The default is 0.
+ +
-file fileName +
+
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 "".
+ +
-font fontName
+
Specifies the font of +the title. The default is *-Helvetica-Bold-R-Normal-*-18-180-*.
+ +
-foreground color +
+
Specifies the foreground color of the EPS rectangle. The option matters +only when the -stipple option is set. The default is white.
+ +
-height pixels +
+
Specifies the height EPS item. If pixels is 0, then the height is determined +from the PostScript "BoundingBox:" entry in the EPS file. The default is +0.
+ +
-image photo
+
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 "".
+ +
-justify justify
+
Specifies how the +title should be justified. This matters only when the title contains more +than one line of text. Justify must be left, right, or center. The default +is center.
+ +
-relief relief
+
Specifies the 3-D effect for the EPS item. Relief +specifies how the item should appear relative to canvas; for example, +raised means the item should appear to protrude. The default is flat.
+ +
-shadowcolor +color
+
Specifies the color of the drop shadow used for the title. The option +with the -shadowoffset option control how the title's drop shadow appears. +The default is grey.
+ +
-shadowoffset pixels
+
Specifies the offset of the drop +shadow from the title's text. If pixels is 0, no shadow will be seen. The +default is 0.
+ +
-showimage boolean
+
Indicates whether to display the image preview +(if one exists), or a simple rectangle. The default is yes.
+ +
-stipple bitmap +
+
Specifies a bitmap to used to stipple the rectangle representing the EPS +item. The default is "".
+ +
-title string
+
Sets the title of the EPS item. If +string is "", 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 "".
+ +
-titleanchor anchor
+
Tells how to position the title within +EPS item. The default is n.
+ +
-titlecolor color
+
Specifies the color of the title. + The default is white.
+ +
-titlerotate degrees
+
Sets the rotation of the title. + Degrees is a real number representing the angle of rotation. The title +is first rotated in space and then placed according to the -titleanchor +position. The default rotation is 0.0.
+ +
-width pixels
+
Specifies the width EPS +item. If pixels is 0, then the width is determined from the PostScript +"BoundingBox:" entry in the EPS file. The default is 0. 5i.
+
+ +

Example

+The graph +command creates a new graph.
+# Create a new graph. Plotting area is black.
+graph .g -plotbackground black
+

A new Tcl command .g 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 configure operation.
+# Change the title.
+.g configure -title "My Plot"
+

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 element component.
+# 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 }
+

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.
+# 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
+

The advantage of using vectors is that when you modify one, the graph is +automatically redrawn to display the new values.
+# Change the X-Y coordinates of the first point.
+set xVec(0) 0.18
+set yVec(0) 25.18
+

An element named line1 is now created in .g. By default, the element's label +in the legend will be also line1. You can change the label, or specify no +legend entry, again using the element's configure operation.
+# Don't display "line1" in the legend.
+.g element configure line1 -label ""
+

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.
+.g element configure line1 -symbol square -color red \
+    -dashes { 2 4 2 } -linewidth 2 -pixels 2c
+

Four coordinate axes are automatically created: x, x2, y, and y2. And by +default, elements are mapped onto the axes x and y. This can be changed +with the -mapx and -mapy options.
+# Map "line1" on the alternate Y-axis "y2".
+.g element configure line1 -mapy y2
+

Axes can be configured in many ways too. For example, you change the scale +of the Y-axis from linear to log using the axis component.
+# Y-axis is log scale.
+.g axis configure y -logscale yes
+

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 -min and +-max configuration options.
+.g axis configure x -min 1.0 -max 1.5
+.g axis configure y -min 12.0 -max 55.15
+

To zoom interactively, you link the axis configure operations with some +user interaction (such as pressing the mouse button), using the bind command. + To convert between screen and graph coordinates, use the invtransform +operation.
+# 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]
+}
+

By default, the limits of the axis are determined from data values. To reset +back to the default limits, set the -min and -max options to the empty value. +
+# Reset the axes to autoscale again.
+.g axis configure x -min {} -max {}
+.g axis configure y -min {} -max {}
+

By default, the legend is drawn in the right margin. You can change this +or any legend configuration options using the legend component.
+# Configure the legend font, color, and relief
+.g legend configure -position left -relief raised \
+    -font fixed -fg blue
+

To prevent the legend from being displayed, turn on the -hide option.
+# Don't display the legend.
+.g legend configure -hide yes
+

The graph 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 +marker component.
+# 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
+

This creates a text marker named first_marker. It will display the text +"start" near the coordinates of the first data point. The -anchor, -xoffset, +and -yoffset 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 -under option.
+# Draw the label before elements are drawn.
+.g marker configure first_marker -under yes
+

You can add cross hairs or grid lines using the crosshairs and grid components. +
+# Display both cross hairs and grid lines.
+.g crosshairs configure -hide no -color red
+.g grid configure -hide no -dashes { 2 2 }
+

Finally, to get hardcopy of the graph, use the postscript component.
+# Print the graph into file "file.ps"
+.g postscript output file.ps -maxpect yes -decorations no
+

This generates a file file.ps containing the encapsulated PostScript of +the graph. The option -maxpect says to scale the plot to the size of the +page. Turning off the -decorations 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). +

Graph Operations

+ +
+ +
pathName axis operation +?arg?...
+
See the AXIS COMPONENTS + section.
+ +
pathName bar elemName ?option value?... +
+
Creates a new barchart element elemName. It's an error if an element elemName +already exists. See the manual for barchart for details about what option +and value pairs are valid.
+ +
pathName cget option
+
Returns the current value +of the configuration option given by option. Option may be any option described +below for the configure operation.
+ +
pathName configure ?option value?...
+
Queries +or modifies the configuration options of the graph. If option isn't specified, +a list describing the current options for pathName is returned. If option +is specified, but not value, then a list describing option is returned. +If one or more option and value pairs are specified, then for each pair, +the option option is set to value. The following options are valid.
+ +
-background +color
+
Sets the background color. This includes the margins and legend, but +not the plotting area.
+ +
-borderwidth pixels
+
Sets the width of the 3-D border +around the outside edge of the widget. The -relief option determines if +the border is to be drawn. The default is 2.
+ +
-bottommargin pixels
+
Specifies +the size of the margin below the X-coordinate axis. If pixels is 0, the +size of the margin is selected automatically. The default is 0.
+ +
-bufferelements +boolean
+
Indicates whether an internal pixmap to buffer the display of data +elements should be used. If boolean 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 SPEED TIPS + section. The default is 1. +
+ +
-cursor cursor
+
Specifies the widget's cursor. The default cursor is crosshair. +
+ +
-font fontName
+
Specifies the font of the graph title. The default is *-Helvetica-Bold-R-Normal-*-18-180-*. +
+ +
-halo pixels
+
Specifies a maximum distance to consider when searching for +the closest data point (see the element's closest operation below). Data +points further than pixels away are ignored. The default is 0.5i.
+ +
-height +pixels
+
Specifies the requested height of widget. The default is 4i.
+ +
-invertxy +boolean
+
Indicates whether the placement X-axis and Y-axis should be inverted. + If boolean is true, the X and Y axes are swapped. The default is 0.
+ +
-justify +justify
+
Specifies how the title should be justified. This matters only +when the title contains more than one line of text. Justify must be left, +right, or center. The default is center.
+ +
-leftmargin pixels
+
Sets the size +of the margin from the left edge of the window to the Y-coordinate axis. + If pixels is 0, the size is calculated automatically. The default is 0. +
+ +
-plotbackground color
+
Specifies the background color of the plotting area. + The default is white.
+ +
-plotborderwidth pixels
+
Sets the width of the 3-D border +around the plotting area. The -plotrelief option determines if a border +is drawn. The default is 2.
+ +
-plotpadx pad
+
Sets the amount of padding to be +added to the left and right sides of the plotting area. Pad can be a list +of one or two screen distances. If pad 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 pad is just one distance, both the left and right +sides are padded evenly. The default is 8.
+ +
-plotpady pad
+
Sets the amount +of padding to be added to the top and bottom of the plotting area. Pad +can be a list of one or two screen distances. If pad has two elements, +the top of the plotting area is padded by the first distance and the bottom +by the second. If pad is just one distance, both the top and bottom are +padded evenly. The default is 8.
+ +
-plotrelief relief
+
Specifies the 3-D effect +for the plotting area. Relief specifies how the interior of the plotting +area should appear relative to rest of the graph; for example, raised means +the plot should appear to protrude from the graph, relative to the surface +of the graph. The default is sunken.
+ +
-relief relief
+
Specifies the 3-D effect +for the graph widget. Relief specifies how the graph should appear relative +to widget it is packed into; for example, raised means the graph should +appear to protrude. The default is flat.
+ +
-rightmargin pixels
+
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 pixels is than 1, the margin size +is selected automatically.
+ +
-takefocus focus
+
Provides information used when +moving the focus from window to window via keyboard traversal (e.g., Tab +and Shift-Tab). If focus is 0, this means that this window should be skipped +entirely during keyboard traversal. 1 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 +"".
+ +
-tile image
+
Specifies a tiled background for the widget. If image isn't +"", the background is tiled using image. Otherwise, the normal background +color is drawn (see the -background option). Image must be an image created +using the Tk image command. The default is "".
+ +
-title text
+
Sets the title +to text. If text is "", no title will be displayed.
+ +
-topmargin pixels
+
Specifies +the size of the margin above the x2 axis. If pixels is 0, the margin size +is calculated automatically.
+ +
-width pixels
+
Specifies the requested width +of the widget. The default is 5i.
+
+ + +
+ +
pathName crosshairs operation ?arg?
+
See +the CROSSHAIRS COMPONENT + section.
+ +
pathName element operation ?arg?...
+
See +the ELEMENT COMPONENTS + section.
+ +
pathName extents item
+
Reports the size +of a particular items in the graph. Item must be either leftmargin, rightmargin, +topmargin, bottommargin, plotwidth, or plotheight.
+ +
pathName grid operation +?arg?...
+
See the GRID COMPONENT + section.
+ +
pathName invtransform winX winY +
+
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.
+ +
pathName inside x y
+
Returns +1 is the designated screen coordinate (x and y) is inside the plotting +area and 0 otherwise.
+ +
pathName legend operation ?arg?...
+
See the LEGEND COMPONENT + + section.
+ +
pathName line operation arg...
+
The operation is the same as element. +
+ +
pathName marker operation ?arg?...
+
See the MARKER COMPONENTS + section.
+ +
pathName +postscript operation ?arg?...
+
See the POSTSCRIPT COMPONENT + section.
+ +
pathName +snap photoName
+
Takes a snapshot of the graph and stores the contents in +the photo image photoName. PhotoName is the name of a Tk photo image that +must already exist.
+ +
pathName transform x y
+
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.
+ +
pathName +xaxis operation ?arg?...
+
+ +
pathName x2axis operation ?arg?...
+
+ +
pathName yaxis operation +?arg?...
+
+ +
pathName y2axis operation ?arg?...
+
See the AXIS COMPONENTS + section. +
+
+ +

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. + +

Axis Components

+Four coordinate axes are automatically created: two X-coordinate +axes (x and x2) and two Y-coordinate axes (y, and y2). By default, the axis +x is located in the bottom margin, y in the left margin, x2 in the top +margin, and y2 in the right margin.

+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.

+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.

+You can create and use several axes. To create an axis, +invoke the axis component and its create operation.
+# Create a new axis called "tempAxis"
+.g axis create tempAxis
+

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.
+# Now map the tempAxis data to this axis.
+.g element create "e1" -xdata $x -ydata $y -mapy tempAxis
+

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 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.

+To display a different axis, you +invoke one of the following components: xaxis, yaxis, x2axis, and y2axis. +The use operation designates the axis to be drawn in the corresponding +margin: xaxis in the bottom, yaxis in the left, x2axis in the top, and +y2axis in the right.
+# Display the axis tempAxis in the left margin.
+.g yaxis use tempAxis
+

+

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.

+ +

+ +
pathName axis cget axisName +option
+
Returns the current value of the option given by option for axisName. + Option may be any option described below for the axis configure operation. +
+ +
pathName axis configure axisName ?axisName?... ?option value?...
+
Queries or modifies +the configuration options of axisName. Several axes can be changed. If option +isn't specified, a list describing all the current options for axisName +is returned. If option is specified, but not value, then a list describing +option is returned. If one or more option and value pairs are specified, +then for each pair, the axis option option is set to value. The following +options are valid for axes.
+ +
-color color
+
Sets the color of the axis and tick +labels. The default is black.
+ +
-command prefix
+
Specifies a Tcl command to be +invoked when formatting the axis tick labels. Prefix 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 "" is returned, no label will appear next to the tick. You can +get the standard tick labels again by setting prefix to "". The default +is "".

+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.

+ +
-descending boolean
+
Indicates whether the values +along the axis are monotonically increasing or decreasing. If boolean is +true, the axis values will be decreasing. The default is 0.
+ +
-hide boolean +
+
Indicates whether the axis is displayed.
+ +
-justify justify
+
Specifies how +the axis title should be justified. This matters only when the axis title +contains more than one line of text. Justify must be left, right, or center. + The default is center.
+ +
-limits formatStr
+
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. FormatStr 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 "" is given as either description, then the that limit will +not be displayed. The default is "".
+ +
-linewidth pixels
+
Sets the width of +the axis and tick lines. The default is 1 pixel.
+ +
-logscale boolean
+
Indicates +whether the scale of the axis is logarithmic or linear. If boolean is true, +the axis is logarithmic. The default scale is linear.
+ +
-loose boolean
+
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 boolean +is true, the axis range is "loose". The default is 0.
+ +
-majorticks majorList +
+
Specifies where to display major axis ticks. You can use this option to +display ticks at non-uniform intervals. MajorList is a list of axis coordinates +designating the location of major ticks. No minor ticks are drawn. If majorList +is "", major ticks will be automatically computed. The default is "".
+ +
-max +value
+
Sets the maximum limit of axisName. Any data point greater than +value is not displayed. If value is "", the maximum limit is calculated +using the largest data value. The default is "".
+ +
-min value
+
Sets the minimum +limit of axisName. Any data point less than value is not displayed. If +value is "", the minimum limit is calculated using the smallest data value. +The default is "".
+ +
-minorticks minorList
+
Specifies where to display minor +axis ticks. You can use this option to display minor ticks at non-uniform +intervals. MinorList 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 -majortick +option is also set. If minorList is "", minor ticks will be automatically +computed. The default is "".
+ +
-rotate theta
+
Specifies the how many degrees +to rotate the axis tick labels. Theta is a real value representing the number +of degrees to rotate the tick labels. The default is 0.0 degrees.
+ +
-showticks +boolean
+
Indicates whether axis ticks should be drawn. If boolean is true, +ticks are drawn. If false, only the axis line is drawn. The default is 1. +
+ +
-stepsize value
+
Specifies the interval between major axis ticks. If value +isn't a valid interval (must be less than the axis range), the request +is ignored and the step size is automatically calculated.
+ +
-subdivisions number +
+
Indicates how many minor axis ticks are to be drawn. For example, if number +is two, only one minor tick is drawn. If number is one, no minor ticks +are displayed. The default is 2.
+ +
-tickfont fontName
+
Specifies the font for +axis tick labels. The default is *-Courier-Bold-R-Normal-*-100-*.
+ +
-ticklength pixels +
+
Sets the length of major and minor ticks (minor ticks are half the length +of major ticks). If pixels is less than zero, the axis will be inverted +with ticks drawn pointing towards the plot. The default is 0.1i.
+ +
-title text +
+
Sets the title of the axis. If text is "", no axis title will be displayed. +
+ +
-titlecolor color
+
Sets the color of the axis title. The default is black. +
+ +
-titlefont fontName
+
Specifies the font for axis title. The default is *-Helvetica-Bold-R-Normal-*-14-140-*. +
+
+

+Axis configuration options may be also be set by the option command. The +resource class is Axis. The resource names are the names of the axes (such +as x or x2).
+option add *Graph.Axis.Color blue
+option add *Graph.x.LogScale true
+option add *Graph.x2.LogScale false
+ + +

+ +

pathName axis create axisName ?option value?...

+
Creates a new axis by the +name axisName. No axis by the same name can already exist. Option and value +are described in above in the axis configure operation.
+ +
pathName axis delete +?axisName?...
+
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.
+ +
pathName +axis invtransform axisName value
+
Performs the inverse transformation, changing +the screen coordinate value to a graph coordinate, mapping the value mapped +to axisName. Returns the graph coordinate.
+ +
pathName axis limits axisName +
+
Returns a list of the minimum and maximum limits for axisName. The order +of the list is min max.
+ +
pathName axis names ?pattern?...
+
Returns a list of +axes matching zero or more patterns. If no pattern argument is give, the +names of all axes are returned.
+ +
pathName axis transform axisName value
+
Transforms +the coordinate value to a screen coordinate by mapping the it to axisName. + Returns the transformed screen coordinate.
+
+

+Only four axes can be displayed +simultaneously. By default, they are x, y, x2, and y2. You can swap in +a different axis with use operation of the special axis components: xaxis, +x2axis, yaxis, and y2axis.
+.g create axis temp
+.g create axis time
+...
+.g xaxis use temp
+.g yaxis use time
+

Only the axes specified for use are displayed on the screen.

+The xaxis, +x2axis, yaxis, and y2axis components operate on an axis location rather +than a specific axis like the more general axis component does. The xaxis +component manages the X-axis located in the bottom margin (whatever axis +that happens to be). Likewise, yaxis uses the Y-axis in the left margin, +x2axis the top X-axis, and y2axis the right Y-axis.

+They implicitly control +the axis that is currently using to that location. By default, xaxis uses +the x axis, yaxis uses y, x2axis uses x2, and y2axis uses y2. These components +can be more convenient to use than always determining what axes are current +being displayed by the graph.

+The following operations are available for +axes. They mirror exactly the operations of the axis component. The axis +argument must be xaxis, x2axis, yaxis, or y2axis. +

+ +
pathName axis cget option +
+
+ +
pathName axis configure ?option value?...
+
+ +
pathName axis invtransform value +
+
+ +
pathName axis limits
+
+ +
pathName axis transform value
+
+ +
pathName axis use ?axisName? +
+
Designates the axis axisName is to be displayed at this location. AxisName +can not be already in use at another location. This command returns the +name of the axis currently using this location.
+
+ +

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.

+The following operations are available for cross hairs: +

+ +
pathName +crosshairs cget option
+
Returns the current value of the cross hairs configuration +option given by option. Option may be any option described below for the +cross hairs configure operation.
+ +
pathName crosshairs configure ?option value?... +
+
Queries or modifies the configuration options of the cross hairs. If +option isn't specified, a list describing all the current options for the +cross hairs is returned. If option is specified, but not value, then a +list describing option is returned. If one or more option and value pairs +are specified, then for each pair, the cross hairs option option is set +to value. The following options are available for cross hairs.
+ +
-color color +
+
Sets the color of the cross hairs. The default is black.
+ +
-dashes dashList +
+
Sets the dash style of the cross hairs. DashList 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 dashList is "", the +cross hairs will be solid lines.
+ +
-hide boolean
+
Indicates whether cross hairs +are drawn. If boolean is true, cross hairs are not drawn. The default is +yes.
+ +
-linewidth pixels
+
Set the width of the cross hair lines. The default +is 1.
+ +
-position pos
+
Specifies the screen position where the cross hairs +intersect. Pos must be in the form "@x,y", where x and y are the window +coordinates of the intersection.
+
+

+Cross hairs configuration options may be +also be set by the option command. The resource name and class are crosshairs +and Crosshairs respectively.
+option add *Graph.Crosshairs.LineWidth 2
+option add *Graph.Crosshairs.Color red
+ + +

+ +

pathName crosshairs off

+
Turns off the cross hairs.
+ +
pathName crosshairs +on
+
Turns on the display of the cross hairs.
+ +
pathName crosshairs toggle +
+
Toggles the current state of the cross hairs, alternately mapping and unmapping +the cross hairs.
+
+ +

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.

+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.

+The following +operations are available for elements. +

+ +
pathName element activate elemName +?index?...
+
Specifies the data points of element elemName to be drawn using +active foreground and background colors. ElemName is the name of the element +and index is a number representing the index of the data point. If no indices +are present then all data points become active.
+ +
pathName element cget elemName +option
+
Returns the current value of the element configuration option given +by option. Option may be any of the options described below for the element +configure operation.
+ +
pathName element closest x y varName ?option value?... +?elemName?...
+
Finds the data point closest to the window coordinates x and +y in the element elemName. ElemName 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 varName the name of the +closest element, the index of its closest point, and the graph coordinates +of the point. Returns 0, if no data point within the threshold distance +can be found, otherwise 1 is returned. The following option-value pairs +are available.
+ +
-halo pixels
+
Specifies a threshold distance where selected +data points are ignored. Pixels is a valid screen distance, such as 2 or +1.2i. If this option isn't specified, then it defaults to the value of the +graph's -halo option.
+ +
-interpolate boolean
+
Indicates that both the data points +and interpolated points along the line segment formed should be considered. + If boolean is true, the closest line segment will be selected instead +of the closest point. If this option isn't specified, boolean defaults to +0.
+
+ + +
+ +
pathName element configure elemName ?elemName... ?option value?...
+
Queries +or modifies the configuration options for elements. Several elements can +be modified at the same time. If option isn't specified, a list describing +all the current options for elemName is returned. If option is specified, +but not value, then a list describing the option option is returned. If +one or more option and value pairs are specified, then for each pair, the +element option option is set to value. The following options are valid +for elements.
+ +
-activepen penName
+
Specifies pen to use to draw active element. + If penName is "", no active elements will be drawn. The default is activeLine. +
+ +
-color color
+
Sets the color of the traces connecting the data points. +
+ +
-dashes dashList
+
Sets the dash style of element line. DashList 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 +dashList is "", the lines will be solid.
+ +
-data coordList
+
Specifies the X-Y +coordinates of the data. CoordList is a list of numeric expressions representing +the X-Y coordinate pairs of each data point.
+ +
-fill color
+
Sets the interior +color of symbols. If color is "", then the interior of the symbol is transparent. + If color is defcolor, then the color will be the same as the -color option. + The default is defcolor.
+ +
-hide boolean
+
Indicates whether the element is +displayed. The default is no.
+ +
-label text
+
Sets the element's label in the +legend. If text is "", the element will have no entry in the legend. The +default label is the element's name.
+ +
-linewidth pixels
+
Sets the width of +the connecting lines between data points. If pixels is 0, no connecting +lines will be drawn between symbols. The default is 0.
+ +
-mapx xAxis
+
Selects +the X-axis to map the element's X-coordinates onto. XAxis must be the name +of an axis. The default is x.
+ +
-mapy yAxis
+
Selects the Y-axis to map the element's +Y-coordinates onto. YAxis must be the name of an axis. The default is y.
+ +
-offdash +color
+
Sets the color of the stripes when traces are dashed (see the -dashes +option). If color is "", then the "off" pixels will represent gaps instead +of stripes. If color is defcolor, then the color will be the same as the +-color option. The default is defcolor.
+ +
-outline color
+
Sets the color or +the outline around each symbol. If color is "", then no outline is drawn. +If color is defcolor, then the color will be the same as the -color option. + The default is defcolor.
+ +
-outlinewidth pixels
+
Sets the width of the outline +bordering each symbol. If pixels is 0, no outline will be drawn. The default +is 1.
+ +
-pixels pixels
+
Sets the size of symbols. If pixels is 0, no symbols +will be drawn. The default is 0.125i.
+ +
-scalesymbols boolean
+
If boolean is +true, the size of the symbols drawn for elemName 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 -pixels 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 boolean is false, the element's +symbols are drawn at the designated size, regardless of axis scales. The +default is 0.
+ +
-smooth smooth
+
Specifies how connecting line segments are +drawn between data points. Smooth can be either linear, step, natural, or +quadratic. If smooth is linear, a single line segment is drawn, connecting +both data points. When smooth is step, 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 natural and quadratic +generate multiple segments between data points. If natural, the segments +are generated using a cubic spline. If quadratic, a quadratic spline is +used. The default is linear.
+ +
-styles styleList
+
Specifies what pen to use +based on the range of weights given. StyleList 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 -weight 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.
+ +
-symbol symbol
+
Specifies the symbol for +data points. Symbol can be either square, circle, diamond, plus, cross, +splus, scross, triangle, "" (where no symbol is drawn), or a bitmap. Bitmaps +are specified as "source ?mask?", where source is the name of the bitmap, +and mask is the bitmap's optional mask. The default is circle.
+ +
-trace direction +
+
Indicates whether connecting lines between data points (whose X-coordinate +values are either increasing or decreasing) are drawn. Direction must +be increasing, decreasing, or both. For example, if direction is increasing, +connecting lines will be drawn only between those data points where X-coordinate +values are monotonically increasing. If direction is both, connecting lines +will be draw between all data points. The default is both.
+ +
-weights wVec +
+
Specifies the weights of the individual data points. This, with the list +pen styles (see the -styles option), controls how data points are drawn. + WVec is the name of a BLT vector or a list of numeric expressions representing +the weights for each data point.
+ +
-xdata xVec
+
Specifies the X-coordinates +of the data. XVec is the name of a BLT vector or a list of numeric expressions. +
+ +
-ydata yVec
+
Specifies the Y-coordinates of the data. YVec is the name of +a BLT vector or a list of numeric expressions.
+
+

+Element configuration options +may also be set by the option command. The resource class is Element. The +resource name is the name of the element.
+option add *Graph.Element.symbol line
+option add *Graph.e1.symbol line
+ + +

+ +

pathName element create elemName ?option value?...

+
Creates a new element elemName. + It's an error is an element elemName already exists. If additional arguments +are present, they specify options valid for the element configure operation. +
+ +
pathName element deactivate elemName ?elemName?...
+
Deactivates all the elements +matching pattern. Elements whose names match any of the patterns given are +redrawn using their normal colors.
+ +
pathName element delete ?elemName?...
+
Deletes +all the named elements. The graph is automatically redrawn.
+ +
pathName element +exists elemName
+
Returns 1 if an element elemName currently exists and 0 +otherwise.
+ +
pathName element names ?pattern?...
+
Returns the elements matching +one or more pattern. If no pattern is given, the names of all elements +is returned.
+ +
pathName element show ?nameList?
+
Queries or modifies the +element display list. The element display list designates the elements +drawn and in what order. NameList is a list of elements to be displayed +in the order they are named. If there is no nameList argument, the current +display list is returned.
+ +
pathName element type elemName
+
Returns the type +of elemName. If the element is a bar element, the commands returns the +string "bar", otherwise it returns "line".
+
+ +

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. +
+ +
pathName grid cget option
+
Returns the current value of the grid line +configuration option given by option. Option may be any option described +below for the grid configure operation.
+ +
pathName grid configure ?option +value?...
+
Queries or modifies the configuration options for grid lines. If +option isn't specified, a list describing all the current grid options for +pathName is returned. If option is specified, but not value, then a list +describing option is returned. If one or more option and value pairs are +specified, then for each pair, the grid line option option is set to value. + The following options are valid for grid lines.
+ +
-color color
+
Sets the color +of the grid lines. The default is black.
+ +
-dashes dashList
+
Sets the dash style +of the grid lines. DashList 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 dashList is "", the grid will be solid lines. +
+ +
-hide boolean
+
Indicates whether the grid should be drawn. If boolean is true, +grid lines are not shown. The default is yes.
+ +
-linewidth pixels
+
Sets the width +of grid lines. The default width is 1.
+ +
-mapx xAxis
+
Specifies the X-axis to +display grid lines. XAxis must be the name of an axis. The default is x. +
+ +
-mapy yAxis
+
Specifies the Y-axis to display grid lines. YAxis must be the +name of an axis. The default is y.
+ +
-minor boolean
+
Indicates whether the grid +lines should be drawn for minor ticks. If boolean is true, the lines will +appear at minor tick intervals. The default is 1.
+
+

+ + +

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. +
  • Try to minimize the number of data points. The more data points +the looked at, the more work the graph must do.
  • ·
  • 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.
  • ·
  • Data elements without symbols are drawn +faster than with symbols. Set the data element's -symbol option to none. If +you need to draw symbols, try using the simple symbols such as splus and +scross.
  • ·
  • Don't stipple or dash the element. Solid lines are much faster.
  • ·
  • If +you update data elements frequently, try turning off the widget's -bufferelements +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.
  • +
+ +

Limitations

+Auto-scale +routines do not use requested min/max limits as boundaries when the axis +is logarithmically scaled.

+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. +

Future Incompatibility

+The -mapped options +are obsoleted and will be removed. You can achieve the same results using +the -hide option instead.
+# Works for now.
+.g legend configure -mapped no
+

+# Instead use this.
+.g legend configure -hide yes
+ +

Keywords

+graph, widget

+ +


+Table of Contents

+

+ 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 @@ + + + + + +graph(n) manual page + + +Table of Contents

+ +

Name

+graph - 2D graph for plotting X-Y coordinate data. + +

Synopsis

+graph pathName ?option value?... +

Description

+The graph 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. +

Introduction

+The +graph 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 plotting area. 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.

+The graph widget is composed of several components: coordinate axes, +data elements, legend, grid, cross hairs, pens, postscript, and annotation +markers. +

+ +
axis
+
The graph has four standard axes (x, x2, y, and y2), 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.
+ +
crosshairs
+
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.
+ +
element
+
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.
+ +
grid
+
Extends the +major and minor ticks of the X-axis and/or Y-axis across the plotting area. +
+ +
legend
+
The legend displays the name and symbol of each data element. +The legend can be drawn in any margin or in the plotting area.
+ +
marker
+
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. +
+ +
pen
+
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 -weight +and -style options).
+ +
postscript
+
The widget can generate encapsulated PostScript +output. This component has several options to configure how the PostScript +is generated.
+
+ +

Syntax

+
+

+graph pathName ?option value?...
+

The graph command creates a new window pathName and makes it into a graph +widget. At the time this command is invoked, there must not exist a window +named pathName, but pathName'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 configure operation +below for the exact details about what option and value pairs are valid. +

+If successful, graph 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: +
+

+pathName operation ?arg?...
+

Both operation and its arguments determine the exact behavior of the command. + The operations available for the graph are described in the GRAPH OPERATIONS + + section.

+The command can also be used to access components of the graph. +
+

+pathName component operation ?arg?...
+

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. +

Example

+The graph command creates a new graph.
+# Create a new graph. Plotting area is black.
+graph .g -plotbackground black
+

A new Tcl command .g 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 configure operation.
+# Change the title.
+.g configure -title "My Plot"
+

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 element component.
+# 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 }
+

The element's X-Y coordinates are specified using lists of numbers. Alternately, +BLT vectors could be used to hold the X-Y coordinates.
+# 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
+

The advantage of using vectors is that when you modify one, the graph is +automatically redrawn to reflect the new values.
+# Change the y coordinate of the first point.
+set yVector(0) 25.18
+

An element named e1 is now created in .b. 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 show operation.
+# 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]
+

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 -barwidth option.
+# Change the X-Y coordinates of the first point.
+set xVec(0) 0.18
+set yVec(0) 25.18
+

An element named line1 is now created in .g. By default, the element's label +in the legend will be also line1. You can change the label, or specify no +legend entry, again using the element's configure operation.
+# Don't display "line1" in the legend.
+.g element configure line1 -label ""
+

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.
+.g element configure line1 -symbol square -color red \
+    -dashes { 2 4 2 } -linewidth 2 -pixels 2c
+

Four coordinate axes are automatically created: x, x2, y, and y2. And by +default, elements are mapped onto the axes x and y. This can be changed +with the -mapx and -mapy options.
+# Map "line1" on the alternate Y-axis "y2".
+.g element configure line1 -mapy y2
+

Axes can be configured in many ways too. For example, you change the scale +of the Y-axis from linear to log using the axis component.
+# Y-axis is log scale.
+.g axis configure y -logscale yes
+

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 -min and +-max configuration options.
+.g axis configure x -min 1.0 -max 1.5
+.g axis configure y -min 12.0 -max 55.15
+

To zoom interactively, you link the axis configure operations with some +user interaction (such as pressing the mouse button), using the bind command. + To convert between screen and graph coordinates, use the invtransform +operation.
+# 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]
+}
+

By default, the limits of the axis are determined from data values. To reset +back to the default limits, set the -min and -max options to the empty value. +
+# Reset the axes to autoscale again.
+.g axis configure x -min {} -max {}
+.g axis configure y -min {} -max {}
+

By default, the legend is drawn in the right margin. You can change this +or any legend configuration options using the legend component.
+# Configure the legend font, color, and relief
+.g legend configure -position left -relief raised \
+    -font fixed -fg blue
+

To prevent the legend from being displayed, turn on the -hide option.
+# Don't display the legend.
+.g legend configure -hide yes
+

The graph 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 +marker component.
+# 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
+

This creates a text marker named first_marker. It will display the text +"start" near the coordinates of the first data point. The -anchor, -xoffset, +and -yoffset 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 -under option.
+# Draw the label before elements are drawn.
+.g marker configure first_marker -under yes
+

You can add cross hairs or grid lines using the crosshairs and grid components. +
+# 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
+}
+

The crosshairs are repositioned as the mouse pointer is moved in the graph. + The pointer X-Y coordinates define the center of the crosshairs.

+Finally, +to get hardcopy of the graph, use the postscript component.
+# Print the graph into file "file.ps"
+.g postscript output file.ps -maxpect yes -decorations no
+

This generates a file file.ps containing the encapsulated PostScript of +the graph. The option -maxpect says to scale the plot to the size of the +page. Turning off the -decorations 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). +

Graph Operations

+ +
+ +
pathName axis operation +?arg?...
+
See the AXIS COMPONENTS + section.
+ +
pathName bar elemName ?option value?... +
+
Creates a new barchart element elemName. It's an error if an element elemName +already exists. See the manual for barchart for details about what option +and value pairs are valid.
+ +
pathName cget option
+
Returns the current value +of the configuration option given by option. Option may be any option described +below for the configure operation.
+ +
pathName configure ?option value?...
+
Queries +or modifies the configuration options of the graph. If option isn't specified, +a list describing the current options for pathName is returned. If option +is specified, but not value, then a list describing option is returned. +If one or more option and value pairs are specified, then for each pair, +the option option is set to value. The following options are valid.
+ +
-background +color
+
Sets the background color. This includes the margins and legend, but +not the plotting area.
+ +
-borderwidth pixels
+
Sets the width of the 3-D border +around the outside edge of the widget. The -relief option determines if +the border is to be drawn. The default is 2.
+ +
-bottommargin pixels
+
If non-zero, +overrides the computed size of the margin extending below the X-coordinate +axis. If pixels is 0, the automatically computed size is used. The default +is 0.
+ +
-bufferelements boolean
+
Indicates whether an internal pixmap to buffer +the display of data elements should be used. If boolean 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 SPEED TIPS + section. The default +is 1.
+ +
-cursor cursor
+
Specifies the widget's cursor. The default cursor is +crosshair.
+ +
-font fontName
+
Specifies the font of the graph title. The default +is *-Helvetica-Bold-R-Normal-*-18-180-*.
+ +
-halo pixels
+
Specifies a maximum distance +to consider when searching for the closest data point (see the element's +closest operation below). Data points further than pixels away are ignored. + The default is 0.5i.
+ +
-height pixels
+
Specifies the requested height of widget. + The default is 4i.
+ +
-invertxy boolean
+
Indicates whether the placement X-axis +and Y-axis should be inverted. If boolean is true, the X and Y axes are +swapped. The default is 0.
+ +
-justify justify
+
Specifies how the title should +be justified. This matters only when the title contains more than one line +of text. Justify must be left, right, or center. The default is center.
+ +
-leftmargin +pixels
+
If non-zero, overrides the computed size of the margin extending + from the left edge of the window to the Y-coordinate axis. If pixels is +0, the automatically computed size is used. The default is 0.
+ +
-plotbackground +color
+
Specifies the background color of the plotting area. The default +is white.
+ +
-plotborderwidth pixels
+
Sets the width of the 3-D border around +the plotting area. The -plotrelief option determines if a border is drawn. + The default is 2.
+ +
-plotpadx pad
+
Sets the amount of padding to be added to +the left and right sides of the plotting area. Pad can be a list of one +or two screen distances. If pad 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 pad is just one distance, both the left and right sides +are padded evenly. The default is 8.
+ +
-plotpady pad
+
Sets the amount of padding +to be added to the top and bottom of the plotting area. Pad can be a list +of one or two screen distances. If pad has two elements, the top of the +plotting area is padded by the first distance and the bottom by the second. + If pad is just one distance, both the top and bottom are padded evenly. + The default is 8.
+ +
-plotrelief relief
+
Specifies the 3-D effect for the plotting +area. Relief specifies how the interior of the plotting area should appear +relative to rest of the graph; for example, raised means the plot should +appear to protrude from the graph, relative to the surface of the graph. + The default is sunken.
+ +
-relief relief
+
Specifies the 3-D effect for the graph +widget. Relief specifies how the graph should appear relative to widget +it is packed into; for example, raised means the graph should appear to +protrude. The default is flat.
+ +
-rightmargin pixels
+
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 pixels is 0, the automatically computed size is used. The default +is 0.
+ +
-takefocus focus
+
Provides information used when moving the focus from +window to window via keyboard traversal (e.g., Tab and Shift-Tab). If focus +is 0, this means that this window should be skipped entirely during keyboard +traversal. 1 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 "".
+ +
-tile image
+
Specifies +a tiled background for the widget. If image isn't "", the background is +tiled using image. Otherwise, the normal background color is drawn (see +the -background option). Image must be an image created using the Tk image +command. The default is "".
+ +
-title text
+
Sets the title to text. If text is +"", no title will be displayed.
+ +
-topmargin pixels
+
If non-zero, overrides +the computed size of the margin above the x2 axis. If pixels is 0, the +automatically computed size is used. The default is 0.
+ +
-width pixels
+
Specifies +the requested width of the widget. The default is 5i.
+
+ + +
+ +
pathName crosshairs +operation ?arg?
+
See the CROSSHAIRS COMPONENT + section.
+ +
pathName element +operation ?arg?...
+
See the ELEMENT COMPONENTS + section.
+ +
pathName extents item +
+
Returns the size of a particular item in the graph. Item must be either +leftmargin, rightmargin, topmargin, bottommargin, plotwidth, or plotheight. +
+ +
pathName grid operation ?arg?...
+
See the GRID COMPONENT + section.
+ +
pathName +invtransform winX winY
+
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.
+ +
pathName +inside x y
+
Returns 1 is the designated screen coordinate (x and y) is inside +the plotting area and 0 otherwise.
+ +
pathName legend operation ?arg?...
+
See the + LEGEND COMPONENT + section.
+ +
pathName line operation arg...
+
The operation is +the same as element.
+ +
pathName marker operation ?arg?...
+
See the MARKER COMPONENTS + + section.
+ +
pathName metafile ?fileName?
+
This operation is for Window platforms +only. Creates a Windows enhanced metafile of the graph. If present, fileName +is the file name of the new metafile. Otherwise, the metafile is automatically +added to the clipboard.
+ +
pathName postscript operation ?arg?...
+
See the POSTSCRIPT +COMPONENT + section.
+ +
pathName snap photoName
+
Takes a snapshot of the graph +and stores the contents in the photo image photoName. PhotoName is the +name of a Tk photo image that must already exist.
+ +
pathName transform x y +
+
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.
+ +
pathName xaxis operation ?arg?...
+
+ +
pathName x2axis +operation ?arg?...
+
+ +
pathName yaxis operation ?arg?...
+
+ +
pathName y2axis operation +?arg?...
+
See the AXIS COMPONENTS + section.
+
+ +

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. +

Axis Components

+Four coordinate axes are +automatically created: two X-coordinate axes (x and x2) and two Y-coordinate +axes (y, and y2). By default, the axis x is located in the bottom margin, +y in the left margin, x2 in the top margin, and y2 in the right margin. +

+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.

+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.

+You can have several +axes. To create an axis, invoke the axis component and its create operation. +
+# Create a new axis called "tempAxis"
+.g axis create tempAxis
+

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.
+# Now map the tempAxis data to this axis.
+.g element create "e1" -xdata $x -ydata $y -mapy tempAxis
+

Any number of axes can be displayed simultaneously. They are drawn in the +margins surrounding the plotting area. The default axes x and y are drawn +in the bottom and left margins. The axes x2 and y2 are drawn in top and +right margins. By default, only x and y are shown. Note that the axes can +have different scales.

+To display a different axis or more than one axis, +you invoke one of the following components: xaxis, yaxis, x2axis, and y2axis. + Each component has a use operation that designates the axis (or axes) +to be drawn in that corresponding margin: xaxis in the bottom, yaxis in +the left, x2axis in the top, and y2axis in the right.
+# Display the axis tempAxis in the left margin.
+.g yaxis use tempAxis
+

The use operation takes a list of axis names as its last argument. This +is the list of axes to be drawn in this margin.

+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.

+ +

+ +
pathName axis bind tagName ?sequence? ?command? +
+
Associates command with tagName such that whenever the event sequence +given by sequence occurs for an axis with this tag, command will be invoked. + The syntax is similar to the bind command except that it operates on +graph axes, rather than widgets. See the bind manual entry for complete +details on sequence and the substitutions performed on command before +invoking it.

+If all arguments are specified then a new binding is created, +replacing any existing binding for the same sequence and tagName. If the +first character of command is + then command augments an existing binding +rather than replacing it. If no command argument is provided then the command +currently associated with tagName and sequence (it's an error occurs if +there's no such binding) is returned. If both command and sequence are +missing then a list of all the event sequences for which bindings have +been defined for tagName.

+ +
pathName axis cget axisName option
+
Returns the +current value of the option given by option for axisName. Option may be +any option described below for the axis configure operation.
+ +
pathName axis +configure axisName ?axisName?... ?option value?...
+
Queries or modifies the configuration +options of axisName. Several axes can be changed. If option isn't specified, +a list describing all the current options for axisName is returned. If +option is specified, but not value, then a list describing option is returned. + If one or more option and value pairs are specified, then for each pair, +the axis option option is set to value. The following options are valid +for axes.
+ +
-bindtags tagList
+
Specifies the binding tags for the axis. TagList +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 +all.
+ +
-color color
+
Sets the color of the axis and tick labels. The default +is black.
+ +
-command prefix
+
Specifies a Tcl command to be invoked when formatting +the axis tick labels. Prefix 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 "" is returned, +no label will appear next to the tick. You can get the standard tick labels +again by setting prefix to "". The default is "".

+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.

+ +
-descending +boolean
+
Indicates whether the values along the axis are monotonically +increasing or decreasing. If boolean is true, the axis values will be decreasing. + The default is 0.
+ +
-hide string
+
Indicates if the axis and all the elements +mapped to it will be displayed. The valid values for string are shown +below. The default value is 0.
+ +
false
+
The axis and its data elements are +displayed.
+ +
true
+
The axis is hidden, but the data elements mapped to it are +displayed.
+ +
all
+
The axis and its data elements are hidden.
+
+ + +
+ +
-justify justify +
+
Specifies how the axis title should be justified. This matters only when +the axis title contains more than one line of text. Justify must be left, +right, or center. The default is center.
+ +
-limits formatStr
+
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. + FormatStr 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 "" is given as either description, then the +that limit will not be displayed. The default is "".
+ +
-linewidth pixels
+
Sets +the width of the axis and tick lines. The default is 1 pixel.
+ +
-logscale boolean +
+
Indicates whether the scale of the axis is logarithmic or linear. If boolean +is true, the axis is logarithmic. The default scale is linear.
+ +
-loose boolean +
+
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 boolean is true, the axis range is "loose". The default is 0. +
+ +
-majorticks majorList
+
Specifies where to display major axis ticks. You can +use this option to display ticks at non-uniform intervals. MajorList is +a list of axis coordinates designating the location of major ticks. No +minor ticks are drawn. If majorList is "", major ticks will be automatically +computed. The default is "".
+ +
-max value
+
Sets the maximum limit of axisName. + Any data point greater than value is not displayed. If value is "", +the maximum limit is calculated using the largest data value. The default +is "".
+ +
-min value
+
Sets the minimum limit of axisName. Any data point less +than value is not displayed. If value is "", the minimum limit is calculated +using the smallest data value. The default is "".
+ +
-minorticks minorList
+
Specifies +where to display minor axis ticks. You can use this option to display minor +ticks at non-uniform intervals. MinorList 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 -majortick option is also set. If minorList is "", minor +ticks will be automatically computed. The default is "".
+ +
-rotate theta
+
Specifies +the how many degrees to rotate the axis tick labels. Theta is a real value +representing the number of degrees to rotate the tick labels. The default +is 0.0 degrees.
+ +
-showticks boolean
+
Indicates whether axis ticks should be +drawn. If boolean is true, ticks are drawn. If false, only the axis line +is drawn. The default is 1.
+ +
-stepsize value
+
Specifies the interval between +major axis ticks. If value isn't a valid interval (must be less than the +axis range), the request is ignored and the step size is automatically +calculated.
+ +
-subdivisions number
+
Indicates how many minor axis ticks are +to be drawn. For example, if number is two, only one minor tick is drawn. + If number is one, no minor ticks are displayed. The default is 2.
+ +
-tickfont +fontName
+
Specifies the font for axis tick labels. The default is *-Courier-Bold-R-Normal-*-100-*. +
+ +
-ticklength pixels
+
Sets the length of major and minor ticks (minor ticks +are half the length of major ticks). If pixels is less than zero, the axis +will be inverted with ticks drawn pointing towards the plot. The default +is 0.1i.
+ +
-title text
+
Sets the title of the axis. If text is "", no axis title +will be displayed.
+ +
-titlecolor color
+
Sets the color of the axis title. The +default is black.
+ +
-titlefont fontName
+
Specifies the font for axis title. +The default is *-Helvetica-Bold-R-Normal-*-14-140-*.
+
+

+Axis configuration options +may be also be set by the option command. The resource class is Axis. The +resource names are the names of the axes (such as x or x2).
+option add *Graph.Axis.Color blue
+option add *Graph.x.LogScale true
+option add *Graph.x2.LogScale false
+ + +

+ +

pathName axis create axisName ?option value?...

+
Creates a new axis by the +name axisName. No axis by the same name can already exist. Option and value +are described in above in the axis configure operation.
+ +
pathName axis delete +?axisName?...
+
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.
+ +
pathName +axis invtransform axisName value
+
Performs the inverse transformation, changing +the screen coordinate value to a graph coordinate, mapping the value mapped +to axisName. Returns the graph coordinate.
+ +
pathName axis limits axisName +
+
Returns a list of the minimum and maximum limits for axisName. The order +of the list is min max.
+ +
pathName axis names ?pattern?...
+
Returns a list of +axes matching zero or more patterns. If no pattern argument is give, the +names of all axes are returned.
+ +
pathName axis transform axisName value
+
Transforms +the coordinate value to a screen coordinate by mapping the it to axisName. + Returns the transformed screen coordinate.
+
+

+The default axes are x, y, x2, +and y2. But you can display more than four axes simultaneously. You can +also swap in a different axis with use operation of the special axis components: +xaxis, x2axis, yaxis, and y2axis.
+.g create axis temp
+.g create axis time
+...
+.g xaxis use temp
+.g yaxis use time
+

Only the axes specified for use are displayed on the screen.

+The xaxis, +x2axis, yaxis, and y2axis components operate on an axis location rather +than a specific axis like the more general axis component does. They implicitly +control the axis that is currently using to that location. By default, +xaxis uses the x axis, yaxis uses y, x2axis uses x2, and y2axis uses y2. + When more than one axis is displayed in a margin, it represents the first +axis displayed.

+The following operations are available for axes. They mirror +exactly the operations of the axis component. The axis argument must be +xaxis, x2axis, yaxis, or y2axis. This feature is deprecated since more +than one axis can now be used a margin. You should only use the xaxis, +x2axis, yaxis, and y2axis components with the use operation. For all other +operations, use the general axis component instead. +

+ +
pathName axis cget option +
+
+ +
pathName axis configure ?option value?...
+
+ +
pathName axis invtransform value +
+
+ +
pathName axis limits
+
+ +
pathName axis transform value
+
+ +
pathName axis use ?axisName? +
+
Designates the axis axisName is to be displayed at this location. AxisName +can not be already in use at another location. This command returns the +name of the axis currently using this location.
+
+ +

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.

+The following operations are available for cross hairs: +

+ +
pathName +crosshairs cget option
+
Returns the current value of the cross hairs configuration +option given by option. Option may be any option described below for the +cross hairs configure operation.
+ +
pathName crosshairs configure ?option value?... +
+
Queries or modifies the configuration options of the cross hairs. If +option isn't specified, a list describing all the current options for the +cross hairs is returned. If option is specified, but not value, then a +list describing option is returned. If one or more option and value pairs +are specified, then for each pair, the cross hairs option option is set +to value. The following options are available for cross hairs.
+ +
-color color +
+
Sets the color of the cross hairs. The default is black.
+ +
-dashes dashList +
+
Sets the dash style of the cross hairs. DashList 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 dashList is "", the +cross hairs will be solid lines.
+ +
-hide boolean
+
Indicates whether cross hairs +are drawn. If boolean is true, cross hairs are not drawn. The default is +yes.
+ +
-linewidth pixels
+
Set the width of the cross hair lines. The default +is 1.
+ +
-position pos
+
Specifies the screen position where the cross hairs +intersect. Pos must be in the form "@x,y", where x and y are the window +coordinates of the intersection.
+
+

+Cross hairs configuration options may be +also be set by the option command. The resource name and class are crosshairs +and Crosshairs respectively.
+option add *Graph.Crosshairs.LineWidth 2
+option add *Graph.Crosshairs.Color red
+ + +

+ +

pathName crosshairs off

+
Turns off the cross hairs.
+ +
pathName crosshairs +on
+
Turns on the display of the cross hairs.
+ +
pathName crosshairs toggle +
+
Toggles the current state of the cross hairs, alternately mapping and unmapping +the cross hairs.
+
+ +

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.

+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.

+The following +operations are available for elements. +

+ +
pathName element activate elemName +?index?...
+
Specifies the data points of element elemName to be drawn using +active foreground and background colors. ElemName is the name of the element +and index is a number representing the index of the data point. If no indices +are present then all data points become active.
+ +
pathName element bind tagName +?sequence? ?command?
+
Associates command with tagName such that whenever +the event sequence given by sequence occurs for an element with this tag, +command will be invoked. The syntax is similar to the bind command except +that it operates on graph elements, rather than widgets. See the bind manual +entry for complete details on sequence and the substitutions performed +on command before invoking it.

+If all arguments are specified then a +new binding is created, replacing any existing binding for the same sequence +and tagName. If the first character of command is + then command augments +an existing binding rather than replacing it. If no command argument is +provided then the command currently associated with tagName and sequence +(it's an error occurs if there's no such binding) is returned. If both command +and sequence are missing then a list of all the event sequences for which +bindings have been defined for tagName.

+ +
pathName element cget elemName +option
+
Returns the current value of the element configuration option given +by option. Option may be any of the options described below for the element +configure operation.
+ +
pathName element closest x y varName ?option value?... +?elemName?...
+
Finds the data point closest to the window coordinates x and +y in the element elemName. ElemName 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 varName the name of the +closest element, the index of its closest point, and the graph coordinates +of the point. Returns 0, if no data point within the threshold distance +can be found, otherwise 1 is returned. The following option-value pairs +are available.
+ +
-halo pixels
+
Specifies a threshold distance where selected +data points are ignored. Pixels is a valid screen distance, such as 2 or +1.2i. If this option isn't specified, then it defaults to the value of the +graph's -halo option.
+ +
-interpolate string
+
Indicates whether to consider projections +that lie along the line segments connecting data points when searching +for the closest point. The default value is 0. The values for string are +described below.
+ +
no
+
Search only for the closest data point.
+ +
yes
+
Search includes +projections that lie along the line segments connecting the data points. +
+ +
x
+
Search includes vertical projections from the given X-coordinate.
+ +
y +
+
Search includes horizontal projections from the given Y-coordinate.
+
+ + + +
+ +
pathName +element configure elemName ?elemName... ?option value?...
+
Queries or modifies +the configuration options for elements. Several elements can be modified +at the same time. If option isn't specified, a list describing all the current +options for elemName is returned. If option is specified, but not value, +then a list describing the option option is returned. If one or more option +and value pairs are specified, then for each pair, the element option option +is set to value. The following options are valid for elements.
+ +
-activepen +penName
+
Specifies pen to use to draw active element. If penName is "", +no active elements will be drawn. The default is activeLine.
+ +
-bindtags tagList +
+
Specifies the binding tags for the element. TagList 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 all.
+ +
-color color +
+
Sets the color of the traces connecting the data points.
+ +
-dashes dashList +
+
Sets the dash style of element line. DashList 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 dashList is "", the lines +will be solid.
+ +
-data coordList
+
Specifies the X-Y coordinates of the data. +CoordList is a list of numeric expressions representing the X-Y coordinate +pairs of each data point.
+ +
-fill color
+
Sets the interior color of symbols. + If color is "", then the interior of the symbol is transparent. If color +is defcolor, then the color will be the same as the -color option. The default +is defcolor.
+ +
-hide boolean
+
Indicates whether the element is displayed. The +default is no.
+ +
-label text
+
Sets the element's label in the legend. If text +is "", the element will have no entry in the legend. The default label is +the element's name.
+ +
-linewidth pixels
+
Sets the width of the connecting lines +between data points. If pixels is 0, no connecting lines will be drawn +between symbols. The default is 0.
+ +
-mapx xAxis
+
Selects the X-axis to map the +element's X-coordinates onto. XAxis must be the name of an axis. The default +is x.
+ +
-mapy yAxis
+
Selects the Y-axis to map the element's Y-coordinates onto. +YAxis must be the name of an axis. The default is y.
+ +
-offdash color
+
Sets the +color of the stripes when traces are dashed (see the -dashes option). If +color is "", then the "off" pixels will represent gaps instead of stripes. + If color is defcolor, then the color will be the same as the -color option. + The default is defcolor.
+ +
-outline color
+
Sets the color or the outline around +each symbol. If color is "", then no outline is drawn. If color is defcolor, +then the color will be the same as the -color option. The default is defcolor. +
+ +
-outlinewidth pixels
+
Sets the width of the outline bordering each symbol. + If pixels is 0, no outline will be drawn. The default is 1.
+ +
-pixels pixels +
+
Sets the size of symbols. If pixels is 0, no symbols will be drawn. The +default is 0.125i.
+ +
-scalesymbols boolean
+
If boolean is true, the size of +the symbols drawn for elemName 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 -pixels 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 boolean is false, the element's symbols are +drawn at the designated size, regardless of axis scales. The default is +0.
+ +
-smooth smooth
+
Specifies how connecting line segments are drawn between +data points. Smooth can be either linear, step, natural, or quadratic. If +smooth is linear, a single line segment is drawn, connecting both data +points. When smooth is step, 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 natural and quadratic +generate multiple segments between data points. If natural, the segments +are generated using a cubic spline. If quadratic, a quadratic spline is +used. The default is linear.
+ +
-styles styleList
+
Specifies what pen to use +based on the range of weights given. StyleList 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 -weight 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.
+ +
-symbol symbol
+
Specifies the symbol for +data points. Symbol can be either square, circle, diamond, plus, cross, +splus, scross, triangle, "" (where no symbol is drawn), or a bitmap. Bitmaps +are specified as "source ?mask?", where source is the name of the bitmap, +and mask is the bitmap's optional mask. The default is circle.
+ +
-trace direction +
+
Indicates whether connecting lines between data points (whose X-coordinate +values are either increasing or decreasing) are drawn. Direction must +be increasing, decreasing, or both. For example, if direction is increasing, +connecting lines will be drawn only between those data points where X-coordinate +values are monotonically increasing. If direction is both, connecting lines +will be draw between all data points. The default is both.
+ +
-weights wVec +
+
Specifies the weights of the individual data points. This, with the list +pen styles (see the -styles option), controls how data points are drawn. + WVec is the name of a BLT vector or a list of numeric expressions representing +the weights for each data point.
+ +
-xdata xVec
+
Specifies the X-coordinates +of the data. XVec is the name of a BLT vector or a list of numeric expressions. +
+ +
-ydata yVec
+
Specifies the Y-coordinates of the data. YVec is the name of +a BLT vector or a list of numeric expressions.
+
+

+Element configuration options +may also be set by the option command. The resource class is Element. The +resource name is the name of the element.
+option add *Graph.Element.symbol line
+option add *Graph.e1.symbol line
+ + +

+ +

pathName element create elemName ?option value?...

+
Creates a new element elemName. + It's an error is an element elemName already exists. If additional arguments +are present, they specify options valid for the element configure operation. +
+ +
pathName element deactivate elemName ?elemName?...
+
Deactivates all the elements +matching pattern. Elements whose names match any of the patterns given are +redrawn using their normal colors.
+ +
pathName element delete ?elemName?...
+
Deletes +all the named elements. The graph is automatically redrawn.
+ +
pathName element +exists elemName
+
Returns 1 if an element elemName currently exists and 0 +otherwise.
+ +
pathName element names ?pattern?...
+
Returns the elements matching +one or more pattern. If no pattern is given, the names of all elements +is returned.
+ +
pathName element show ?nameList?
+
Queries or modifies the +element display list. The element display list designates the elements +drawn and in what order. NameList is a list of elements to be displayed +in the order they are named. If there is no nameList argument, the current +display list is returned.
+ +
pathName element type elemName
+
Returns the type +of elemName. If the element is a bar element, the commands returns the +string "bar", otherwise it returns "line".
+
+ +

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. +
+ +
pathName grid cget option
+
Returns the current value of the grid line +configuration option given by option. Option may be any option described +below for the grid configure operation.
+ +
pathName grid configure ?option +value?...
+
Queries or modifies the configuration options for grid lines. If +option isn't specified, a list describing all the current grid options for +pathName is returned. If option is specified, but not value, then a list +describing option is returned. If one or more option and value pairs are +specified, then for each pair, the grid line option option is set to value. + The following options are valid for grid lines.
+ +
-color color
+
Sets the color +of the grid lines. The default is black.
+ +
-dashes dashList
+
Sets the dash style +of the grid lines. DashList 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 dashList is "", the grid will be solid lines. +
+ +
-hide boolean
+
Indicates whether the grid should be drawn. If boolean is true, +grid lines are not shown. The default is yes.
+ +
-linewidth pixels
+
Sets the width +of grid lines. The default width is 1.
+ +
-mapx xAxis
+
Specifies the X-axis to +display grid lines. XAxis must be the name of an axis or "" for no grid +lines. The default is "".
+ +
-mapy yAxis
+
Specifies the Y-axis to display grid +lines. YAxis must be the name of an axis or "" for no grid lines. The default +is y.
+ +
-minor boolean
+
Indicates whether the grid lines should be drawn for +minor ticks. If boolean is true, the lines will appear at minor tick intervals. + The default is 1.
+
+

+Grid configuration options may also be set by the option +command. The resource name and class are grid and Grid respectively.
+option add *Graph.grid.LineWidth 2
+option add *Graph.Grid.Color black
+ + +

+ +

pathName grid off

+
Turns off the display the grid lines.
+ +
pathName grid on +
+
Turns on the display the grid lines.
+ +
pathName grid toggle
+
Toggles the display +of the grid.
+
+ +

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.

+The following operations +are valid for the legend. +

+ +
pathName legend activate pattern...
+
Selects legend +entries to be drawn using the active legend colors and relief. All entries +whose element names match pattern are selected. To be selected, the element +name must match only one pattern.
+ +
pathName legend bind tagName ?sequence? + ?command?
+
Associates command with tagName such that whenever the event +sequence given by sequence occurs for a legend entry with this tag, command +will be invoked. Implicitly the element names in the entry are tags. The +syntax is similar to the bind command except that it operates on legend +entries, rather than widgets. See the bind manual entry for complete details +on sequence and the substitutions performed on command before invoking +it.

+If all arguments are specified then a new binding is created, replacing + any existing binding for the same sequence and tagName. If the first character +of command is + then command augments an existing binding rather than +replacing it. If no command argument is provided then the command currently +associated with tagName and sequence (it's an error occurs if there's no +such binding) is returned. If both command and sequence are missing then +a list of all the event sequences for which bindings have been defined +for tagName.

+ +
pathName legend cget option
+
Returns the current value of a +legend configuration option. Option may be any option described below in +the legend configure operation.
+ +
pathName legend configure ?option value?... +
+
Queries or modifies the configuration options for the legend. If option +isn't specified, a list describing the current legend options for pathName +is returned. If option is specified, but not value, then a list describing +option is returned. If one or more option and value pairs are specified, +then for each pair, the legend option option is set to value. The following +options are valid for the legend.
+ +
-activebackground color
+
Sets the background +color for active legend entries. All legend entries marked active (see +the legend activate operation) are drawn using this background color.
+ +
-activeborderwidth +pixels
+
Sets the width of the 3-D border around the outside edge of the active +legend entries. The default is 2.
+ +
-activeforeground color
+
Sets the foreground +color for active legend entries. All legend entries marked as active (see +the legend activate operation) are drawn using this foreground color.
+ +
-activerelief +relief
+
Specifies the 3-D effect desired for active legend entries. Relief +denotes how the interior of the entry should appear relative to the legend; +for example, raised means the entry should appear to protrude from the +legend, relative to the surface of the legend. The default is flat.
+ +
-anchor +anchor
+
Tells how to position the legend relative to the positioning point +for the legend. This is dependent on the value of the -position option. +The default is center.
+ +
left or right
+
The anchor describes how to position +the legend vertically.
+ +
top or bottom
+
The anchor describes how to position +the legend horizontally.
+ +
@x,y
+
The anchor specifies how to position the +legend relative to the positioning point. For example, if anchor is center +then the legend is centered on the point; if anchor is n 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.
+ +
plotarea
+
The anchor +specifies how to position the legend relative to the plotting area. For +example, if anchor is center then the legend is centered in the plotting +area; if anchor is ne then the legend will be drawn such that occupies +the upper right corner of the plotting area.
+
+ + +
+ +
-background color
+
Sets the background +color of the legend. If color is "", the legend background with be transparent. +
+ +
-bindtags tagList
+
Specifies the binding tags for legend entries. TagList +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 all.
+ +
-borderwidth pixels
+
Sets the width of the 3-D border around +the outside edge of the legend (if such border is being drawn; the relief +option determines this). The default is 2 pixels.
+ +
-font fontName
+
FontName +specifies a font to use when drawing the labels of each element into the +legend. The default is *-Helvetica-Bold-R-Normal-*-12-120-*.
+ +
-foreground color
+
Sets +the foreground color of the text drawn for the element's label. The default +is black.
+ +
-hide boolean
+
Indicates whether the legend should be displayed. +If boolean is true, the legend will not be draw. The default is no.
+ +
-ipadx +pad
+
Sets the amount of internal padding to be added to the width of each +legend entry. Pad can be a list of one or two screen distances. If pad +has two elements, the left side of the legend entry is padded by the first +distance and the right side by the second. If pad is just one distance, +both the left and right sides are padded evenly. The default is 2.
+ +
-ipady +pad
+
Sets an amount of internal padding to be added to the height of each +legend entry. Pad can be a list of one or two screen distances. If pad +has two elements, the top of the entry is padded by the first distance +and the bottom by the second. If pad is just one distance, both the top +and bottom of the entry are padded evenly. The default is 2.
+ +
-padx pad
+
Sets +the padding to the left and right exteriors of the legend. Pad can be a +list of one or two screen distances. If pad has two elements, the left +side of the legend is padded by the first distance and the right side by +the second. If pad has just one distance, both the left and right sides +are padded evenly. The default is 4.
+ +
-pady pad
+
Sets the padding above and +below the legend. Pad can be a list of one or two screen distances. If +pad has two elements, the area above the legend is padded by the first +distance and the area below by the second. If pad is just one distance, +both the top and bottom areas are padded evenly. The default is 0.
+ +
-position +pos
+
Specifies where the legend is drawn. The -anchor option also affects +where the legend is positioned. If pos is left, left, top, or bottom, the +legend is drawn in the specified margin. If pos is plotarea, then the legend +is drawn inside the plotting area at a particular anchor. If pos is in +the form "@x,y", where x and y are the window coordinates, the legend is +drawn in the plotting area at the specified coordinates. The default is +right.
+ +
-raised boolean
+
Indicates whether the legend is above or below the +data elements. This matters only if the legend is in the plotting area. + If boolean is true, the legend will be drawn on top of any elements that +may overlap it. The default is no.
+ +
-relief relief
+
Specifies the 3-D effect +for the border around the legend. Relief specifies how the interior of the +legend should appear relative to the graph; for example, raised means the +legend should appear to protrude from the graph, relative to the surface +of the graph. The default is sunken.
+
+

+Legend configuration options may also +be set by the option command. The resource name and class are legend and +Legend respectively.
+option add *Graph.legend.Foreground blue
+option add *Graph.Legend.Relief raised
+ + +

+ +

pathName legend deactivate pattern...

+
Selects legend entries to be drawn using +the normal legend colors and relief. All entries whose element names match +pattern are selected. To be selected, the element name must match only +one pattern.
+ +
pathName legend get pos
+
Returns the name of the element whose +entry is at the screen position pos in the legend. Pos must be in the form +"@x,y", where x and y are window coordinates. If the given coordinates +do not lie over a legend entry, "" is returned.
+
+ +

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 +-weight and -style options).

+One pen, called activeLine, 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. +
+.g pen configure "activeLine" -color green
+

You can create and use several pens. To create a pen, invoke the pen component +and its create operation.
+.g pen create myPen
+

You map pens to a data element using either the element's -pen or -activepen +options.
+.g element create "line1" -xdata $x -ydata $tempData \
+ -pen myPen
+

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 -styles option).
+.g element configure "line1" -styles { myPen 2.0 3.0 }
+

This says that any data point with a weight between 2.0 and 3.0 is to be +drawn using the pen myPen. All other points are drawn with the element's +default attributes.

+The following operations are available for pen components. +

+ +

+ +
pathName pen cget penName option
+
Returns the current value of the option +given by option for penName. Option may be any option described below for +the pen configure operation.
+ +
pathName pen configure penName ?penName... ?option +value?...
+
Queries or modifies the configuration options of penName. Several +pens can be modified at once. If option isn't specified, a list describing +the current options for penName is returned. If option is specified, but +not value, then a list describing option is returned. If one or more option +and value pairs are specified, then for each pair, the pen option option +is set to value. The following options are valid for pens.
+ +
-color color +
+
Sets the color of the traces connecting the data points.
+ +
-dashes dashList +
+
Sets the dash style of element line. DashList 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 dashList is "", the lines +will be solid.
+ +
-fill color
+
Sets the interior color of symbols. If color +is "", then the interior of the symbol is transparent. If color is defcolor, +then the color will be the same as the -color option. The default is defcolor. +
+ +
-linewidth pixels
+
Sets the width of the connecting lines between data points. + If pixels is 0, no connecting lines will be drawn between symbols. The +default is 0.
+ +
-offdash color
+
Sets the color of the stripes when traces are +dashed (see the -dashes option). If color is "", then the "off" pixels will +represent gaps instead of stripes. If color is defcolor, then the color +will be the same as the -color option. The default is defcolor.
+ +
-outline color +
+
Sets the color or the outline around each symbol. If color is "", then +no outline is drawn. If color is defcolor, then the color will be the same +as the -color option. The default is defcolor.
+ +
-outlinewidth pixels
+
Sets +the width of the outline bordering each symbol. If pixels is 0, no outline +will be drawn. The default is 1.
+ +
-pixels pixels
+
Sets the size of symbols. +If pixels is 0, no symbols will be drawn. The default is 0.125i.
+ +
-symbol symbol +
+
Specifies the symbol for data points. Symbol can be either square, circle, +diamond, plus, cross, splus, scross, triangle, "" (where no symbol is drawn), +or a bitmap. Bitmaps are specified as "source ?mask?", where source is +the name of the bitmap, and mask is the bitmap's optional mask. The default +is circle.
+ +
-type elemType
+
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".
+
+

+Pen configuration options +may be also be set by the option command. The resource class is Pen. The +resource names are the names of the pens.
+option add *Graph.Pen.Color blue
+option add *Graph.activeLine.color green
+ + +

+ +

pathName pen create penName ?option value?...

+
Creates a new pen by the name +penName. No pen by the same name can already exist. Option and value are +described in above in the pen configure operation.
+ +
pathName pen delete +?penName?...
+
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.
+ +
pathName +pen names ?pattern?...
+
Returns a list of pens matching zero or more patterns. + If no pattern argument is give, the names of all pens are returned.
+
+ +

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. +

+The following postscript operations are available. +

+ +
pathName postscript cget +option
+
Returns the current value of the postscript option given by option. + Option may be any option described below for the postscript configure +operation.
+ +
pathName postscript configure ?option value?...
+
Queries or modifies +the configuration options for PostScript generation. If option isn't specified, +a list describing the current postscript options for pathName is returned. + If option is specified, but not value, then a list describing option is +returned. If one or more option and value pairs are specified, then for +each pair, the postscript option option is set to value. The following +postscript options are available.
+ +
-center boolean
+
Indicates whether the plot +should be centered on the PostScript page. If boolean is false, the plot +will be placed in the upper left corner of the page. The default is 1.
+ +
-colormap +varName
+
VarName must be the name of a global array variable that specifies +a color mapping from the X color name to PostScript. Each element of varName +must consist of PostScript code to set a particular color value (e.g. ``1.0 +1.0 0.0 setrgbcolor''). When generating color information in PostScript, the +array variable varName 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 varName +for a given color, then it uses the red, green, and blue intensities from +the X color.
+ +
-colormode mode
+
Specifies how to output color information. Mode +must be either color (for full color output), gray (convert all colors +to their gray-scale equivalents) or mono (convert foreground colors to black +and background colors to white). The default mode is color.
+ +
-fontmap varName +
+
VarName must be the name of a global array variable that specifies a font +mapping from the X font name to PostScript. Each element of varName 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 varName 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 Adobe (such as Times, +Helvetica, Courier, etc.). If all of this fails then the font defaults to +Helvetica-Bold.
+ +
-decorations boolean
+
Indicates whether PostScript commands +to generate color backgrounds and 3-D borders will be output. If boolean +is false, the background will be white and no 3-D borders will be generated. +The default is 1.
+ +
-height pixels
+
Sets the height of the plot. This lets you +print the graph with a height different from the one drawn on the screen. + If pixels is 0, the height is the same as the widget's height. The default +is 0.
+ +
-landscape boolean
+
If boolean 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 0.
+ +
-maxpect boolean
+
Indicates to scale the plot +so that it fills the PostScript page. The aspect ratio of the graph is still +retained. The default is 0.
+ +
-padx pad
+
Sets the horizontal padding for the +left and right page borders. The borders are exterior to the plot. Pad +can be a list of one or two screen distances. If pad has two elements, +the left border is padded by the first distance and the right border by +the second. If pad has just one distance, both the left and right borders +are padded evenly. The default is 1i.
+ +
-pady pad
+
Sets the vertical padding +for the top and bottom page borders. The borders are exterior to the plot. + Pad can be a list of one or two screen distances. If pad has two elements, +the top border is padded by the first distance and the bottom border by +the second. If pad has just one distance, both the top and bottom borders +are padded evenly. The default is 1i.
+ +
-paperheight pixels
+
Sets the height +of the postscript page. This can be used to select between different page +sizes (letter, A4, etc). The default height is 11.0i.
+ +
-paperwidth pixels
+
Sets +the width of the postscript page. This can be used to select between different +page sizes (letter, A4, etc). The default width is 8.5i.
+ +
-width pixels
+
Sets +the width of the plot. This lets you generate a plot of a width different +from that of the widget. If pixels is 0, the width is the same as the widget's +width. The default is 0.
+
+

+Postscript configuration options may be also be +set by the option command. The resource name and class are postscript and +Postscript respectively.
+option add *Graph.postscript.Decorations false
+option add *Graph.Postscript.Landscape true
+ + +

+ +

pathName postscript output ?fileName? ?option value?...

+
Outputs a file of +encapsulated PostScript. If a fileName argument isn't present, the command +returns the PostScript. If any option-value pairs are present, they set configuration +options controlling how the PostScript is generated. Option and value can +be anything accepted by the postscript configure operation above.
+
+ +

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 -under option.

+Markers, in contrast to elements, don't affect the scaling +of the coordinate axes. They can also have elastic coordinates (specified +by -Inf and Inf 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 +-Inf,-Inf.

+The following operations are available for markers. +

+ +
pathName marker +after markerId ?afterId?
+
Changes the order of the markers, drawing the +first marker after the second. If no second afterId 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.
+ +
pathName marker before markerId ?beforeId?
+
Changes +the order of the markers, drawing the first marker before the second. If +no second beforeId 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.
+ +
pathName +marker bind tagName ?sequence? ?command?
+
Associates command with tagName +such that whenever the event sequence given by sequence occurs for a marker +with this tag, command will be invoked. The syntax is similar to the bind +command except that it operates on graph markers, rather than widgets. +See the bind manual entry for complete details on sequence and the substitutions +performed on command before invoking it.

+If all arguments are specified +then a new binding is created, replacing any existing binding for the +same sequence and tagName. If the first character of command is + then command + augments an existing binding rather than replacing it. If no command argument +is provided then the command currently associated with tagName and sequence +(it's an error occurs if there's no such binding) is returned. If both command +and sequence are missing then a list of all the event sequences for which +bindings have been defined for tagName.

+ +
pathName marker cget option
+
Returns +the current value of the marker configuration option given by option. Option +may be any option described below in the configure operation.
+ +
pathName marker +configure markerId ?option value?...
+
Queries or modifies the configuration +options for markers. If option isn't specified, a list describing the current +options for markerId is returned. If option is specified, but not value, +then a list describing option is returned. If one or more option and value +pairs are specified, then for each pair, the marker option option is set +to value.

+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.

+ +
-bindtags tagList
+
Specifies the binding tags for the marker. TagList +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 all.
+ +
-coords coordList
+
Specifies the coordinates of the marker. +CoordList 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 coordList +is "", the marker will not be displayed. The default is "".
+ +
-element elemName +
+
Links the marker with the element elemName. The marker is drawn only if +the element is also currently displayed (see the element's show operation). + If elemName is "", the marker is always drawn. The default is "".
+ +
-hide +boolean
+
Indicates whether the marker is drawn. If boolean is true, the +marker is not drawn. The default is no.
+ +
-mapx xAxis
+
Specifies the X-axis +to map the marker's X-coordinates onto. XAxis must the name of an axis. The +default is x.
+ +
-mapy yAxis
+
Specifies the Y-axis to map the marker's Y-coordinates +onto. YAxis must the name of an axis. The default is y.
+ +
-name markerId
+
Changes +the identifier for the marker. The identifier markerId can not already +be used by another marker. If this option isn't specified, the marker's name +is uniquely generated.
+ +
-under boolean
+
Indicates whether the marker is drawn +below/above data elements. If boolean 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 0.
+ +
-xoffset pixels
+
Specifies a screen distance +to offset the marker horizontally. Pixels is a valid screen distance, such +as 2 or 1.2i. The default is 0.
+ +
-yoffset pixels
+
Specifies a screen distance +to offset the markers vertically. Pixels is a valid screen distance, such +as 2 or 1.2i. The default is 0.
+
+

+Marker configuration options may also be set +by the option command. The resource class is either BitmapMarker, ImageMarker, + LineMarker, PolygonMarker, TextMarker, or WindowMarker, depending on the +type of marker. The resource name is the name of the marker.
+option add *Graph.TextMarker.Foreground white
+option add *Graph.BitmapMarker.Foreground white
+option add *Graph.m1.Background blue
+ + +

+ +

pathName marker create type ?option value?...

+
Creates a marker of the selected +type. Type may be either text, line, bitmap, image, polygon, or window. +This command returns the marker identifier, used as the markerId argument +in the other marker-related commands. If the -name 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.
+ +
pathName marker delete +?name?...
+
Removes one of more markers. The graph will automatically be redrawn +without the marker..
+ +
pathName marker exists markerId
+
Returns 1 if the +marker markerId exists and 0 otherwise.
+ +
pathName marker names ?pattern? +
+
Returns the names of all the markers that currently exist. If pattern +is supplied, only those markers whose names match it will be returned.
+ +
pathName +marker type markerId
+
Returns the type of the marker given by markerId, +such as line or text. If markerId is not a valid a marker identifier, "" +is returned.
+
+ +

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.

+Bitmap markers are created with the marker's create operation +in the form:
+

+pathName marker create bitmap ?option value?...
+

There may be many option-value pairs, each sets a configuration options +for the marker. These same option-value pairs may be used with the marker's +configure operation.

+The following options are specific to bitmap markers: + +

+ +
-background color
+
Same as the -fill option.
+ +
-bitmap bitmap
+
Specifies the bitmap +to be displayed. If bitmap is "", the marker will not be displayed. The +default is "".
+ +
-fill color
+
Sets the background color of the bitmap. If color +is the empty string, no background will be transparent. The default background +color is "".
+ +
-foreground color
+
Same as the -outline option.
+ +
-mask mask
+
Specifies +a mask for the bitmap to be displayed. This mask is a bitmap itself, denoting +the pixels that are transparent. If mask is "", all pixels of the bitmap +will be drawn. The default is "".
+ +
-outline color
+
Sets the foreground color +of the bitmap. The default value is black.
+ +
-rotate theta
+
Sets the rotation +of the bitmap. Theta 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 0.0.
+
+ +

Image Markers

+A image marker +displays an image. Image markers are created with the marker's create operation +in the form:
+

+pathName marker create image ?option value?...
+

There may be many option-value pairs, each sets a configuration option for +the marker. These same option-value pairs may be used with the marker's configure +operation.

+The following options are specific to image markers: +

+ +
-anchor anchor +
+
Anchor tells how to position the image relative to the positioning point +for the image. For example, if anchor is center then the image is centered +on the point; if anchor is n 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 center.
+ +
-image image
+
Specifies +the image to be drawn. If image is "", the marker will not be drawn. The +default is "".
+
+ +

Line Markers

+A line marker displays one or more connected +line segments. Line markers are created with marker's create operation in +the form:
+

+pathName marker create line ?option value?...
+

There may be many option-value pairs, each sets a configuration option for +the marker. These same option-value pairs may be used with the marker's configure +operation.

+The following options are specific to line markers: +

+ +
-dashes dashList +
+
Sets the dash style of the line. DashList 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 dashList is "", the marker line +will be solid.
+ +
-fill color
+
Sets the background color of the line. This color +is used with striped lines (see the -fdashes option). If color is the empty +string, no background color is drawn (the line will be dashed, not striped). + The default background color is "".
+ +
-linewidth pixels
+
Sets the width of +the lines. The default width is 0.
+ +
-outline color
+
Sets the foreground color +of the line. The default value is black.
+ +
-stipple bitmap
+
Specifies a stipple +pattern used to draw the line, rather than a solid line. Bitmap specifies +a bitmap to use as the stipple pattern. If bitmap is "", then the line +is drawn in a solid fashion. The default is "".
+
+ +

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 create operation in the form:
+

+pathName marker create polygon ?option value?...
+

There may be many option-value pairs, each sets a configuration option for +the marker. These same option-value pairs may be used with the marker configure +command to change the marker's configuration. The following options are supported +for polygon markers: +

+ +
-dashes dashList
+
Sets the dash style of the outline +of the polygon. DashList 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 dashList is "", the outline will be a solid +line.
+ +
-fill color
+
Sets the fill color of the polygon. If color is "", then +the interior of the polygon is transparent. The default is white.
+ +
-linewidth +pixels
+
Sets the width of the outline of the polygon. If pixels is zero, + no outline is drawn. The default is 0.
+ +
-outline color
+
Sets the color of the +outline of the polygon. If the polygon is stippled (see the -stipple option), +then this represents the foreground color of the stipple. The default is +black.
+ +
-stipple bitmap
+
Specifies that the polygon should be drawn with a +stippled pattern rather than a solid color. Bitmap specifies a bitmap to +use as the stipple pattern. If bitmap is "", then the polygon is filled +with a solid color (if the -fill option is set). The default is "".
+
+ +

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 create operation +in the form:
+

+pathName marker create text ?option value?...
+

There may be many option-value pairs, each sets a configuration option +for the text marker. These same option-value pairs may be used with the + marker's configure operation.

+The following options are specific to text +markers: +

+ +
-anchor anchor
+
Anchor tells how to position the text relative to +the positioning point for the text. For example, if anchor is center then +the text is centered on the point; if anchor is n 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 center.
+ +
-background +color
+
Same as the -fill option.
+ +
-font fontName
+
Specifies the font of the text. + The default is *-Helvetica-Bold-R-Normal-*-120-*.
+ +
-fill color
+
Sets the background +color of the text. If color is the empty string, no background will be +transparent. The default background color is "".
+ +
-foreground color
+
Same as +the -outline option.
+ +
-justify justify
+
Specifies how the text should be justified. + This matters only when the marker contains more than one line of text. +Justify must be left, right, or center. The default is center.
+ +
-outline color +
+
Sets the color of the text. The default value is black.
+ +
-padx pad
+
Sets the +padding to the left and right exteriors of the text. Pad can be a list of +one or two screen distances. If pad has two elements, the left side of +the text is padded by the first distance and the right side by the second. + If pad has just one distance, both the left and right sides are padded +evenly. The default is 4.
+ +
-pady pad
+
Sets the padding above and below the +text. Pad can be a list of one or two screen distances. If pad has two +elements, the area above the text is padded by the first distance and the +area below by the second. If pad is just one distance, both the top and +bottom areas are padded evenly. The default is 4.
+ +
-rotate theta
+
Specifies +the number of degrees to rotate the text. Theta 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 0.0.
+ +
-text text +
+
Specifies the text of the marker. The exact way the text is displayed may +be affected by other options such as -anchor or -rotate.
+
+ +

Window Markers

+A window +marker displays a widget at a given position. Window markers are created +with the marker's create operation in the form:
+

+pathName marker create window ?option value?...
+

There may be many option-value pairs, each sets a configuration option for +the marker. These same option-value pairs may be used with the marker's configure +command.

+The following options are specific to window markers: +

+ +
-anchor anchor +
+
Anchor tells how to position the widget relative to the positioning point +for the widget. For example, if anchor is center then the widget is centered +on the point; if anchor is n 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 center.
+ +
-height pixels +
+
Specifies the height to assign to the marker's window. If this option isn't +specified, or if it is specified as "", then the window is given whatever +height the widget requests internally.
+ +
-width pixels
+
Specifies the width +to assign to the marker's window. If this option isn't specified, or if it +is specified as "", then the window is given whatever width the widget +requests internally.
+ +
-window pathName
+
Specifies the widget to be managed +by the graph. PathName must be a child of the graph widget.
+
+ +

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 Enter, Leave, ButtonPress, Motion, and KeyPress). +

+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.

+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 -bindtags 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.

+The -bindtags +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 -bindtags option doesn't change this. +

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.

+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.

+From Tcl, create +the vectors and configure the element to use them.
+vector X Y
+.g element configure line1 -xdata X -ydata Y
+

To set data points from C, you pass the values as arrays of doubles using +the Blt_ResetVector 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.
+#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;
+}
+

See the vector manual page for more details. +

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. + +
  • Try to minimize the number of data points. The more data points the looked +at, the more work the graph must do.
  • ·
  • 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.
  • ·
  • Data elements without symbols are drawn faster than with +symbols. Set the data element's -symbol option to none. If you need to draw +symbols, try using the simple symbols such as splus and scross.
  • ·
  • Don't stipple +or dash the element. Solid lines are much faster.
  • ·
  • If you update data elements +frequently, try turning off the widget's -bufferelements 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.
  • +
+ +

Limitations

+Auto-scale routines do +not use requested min/max limits as boundaries when the axis is logarithmically +scaled.

+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. +

Keywords

+graph, widget

+ +


+Table of Contents

+

+ 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 @@ + + + + + +treeview(n) manual page + + +Table of Contents

+ +

Name

+treeview - Create and manipulate hierarchical +table widgets +

Synopsis

+treeview pathName ?options? +

Description

+The treeview +widget displays a tree of data. It replaces both the hiertable and hierbox +widgets. The treeview is 100% syntax compatible with the hiertable widget. + The hiertable command is retained for sake of script-level compatibility. + This widget obsoletes the hierbox widget. It does everything the old hierbox +widget did, but also provides data sharing (via tree data objects) and +the ability to tag nodes. +

Introduction

+The treeview 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.

+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 open, its children are exposed. When it +is closed, the children and their descedants are hidden. The button is +normally a + or - symbol (ala Windows Explorer), but can be replaced with +a pair of Tk images (open and closed images).

+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. +

Tree Data Object

+The tree +is not stored inside the widget but in a tree data object (see the tree +command for a further explanation). Tree data objects can be shared among +different clients, such as a treeview widget or the tree command. You can +walk the tree and manage its data with the tree command tree, while displaying +it with the treeview widget. Whenever the tree is updated, the treeview +widget is automatically redrawn.

+By default, the treeview widget creates +its own tree object. The tree initially contains just a root node. But you +can also display trees created by the tree command using the -tree configuration +option. Treeview widgets can share the same tree object, possibly displaying +different views of the same data.

+A tree object has both a Tcl and C API. + You can insert or delete nodes using treeview widget or tree 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. +

Syntax

+
+

+treeview pathName ?option value?...
+

The treeview command creates a new window pathName and makes it into a +treeview widget. At the time this command is invoked, there must not exist +a window named pathName, but pathName'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 configure operation +below for the exact details about what option and value pairs are valid. +

+If successful, treeview 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: +
+

+pathName operation ?arg?...
+

Both operation and its arguments determine the exact behavior of the command. + The operations available are described in the TREEVIEW OPERATIONS + section. + +

IDs and Tags

+Nodes can be inserted into a tree using the treeview widget +
+blt::treeview .t
+set node [.t insert end root "one"]
+

or tree command.
+set tree [blt::tree create]
+set node [$tree insert root "one"]
+

In both cases, a number identifying the node is returned (the value of +$node). This serial number or id 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 0. 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 treeview widget or the tree command. Ids are recycled +when the node deleted.

+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, "x123" is valid, but "123" 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 treeview widget +take either node ids or tag names as arguments. Using a tag says to apply +the operation to all nodes with that tag.

+The tag all is implicitly associated +with every node in the tree. It may be used to invoke operations on all +the nodes in the tree.

+Tags may be shared, just like trees, between clients. + For example, you can use the tags created by the tree command with treeview +widgets. +

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 +treeview widget. +
+ +
active
+
The node where the mouse pointer is currently located. +When a node is active, it is drawn using its active icon (see the -activeicon +option). The active id is changed automatically by moving the mouse pointer +over another node or by using the entry activate operation. Note that there +can be only one active node at a time.
+ +
anchor
+
The node representing the +fixed end of the current selection. The anchor is set by the selection +anchor operation.
+ +
current
+
The node where the mouse pointer is currently +located. But unlike active, this id changes while the selection is dragged. + It is used to determine the current node during button drags.
+ +
down
+
The +next open node from the current focus. The down of the last open node is +the same.
+ +
end
+
The last open node (in depth-first order) on the tree.
+ +
focus +
+
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 focus operation.
+ +
last
+
The +last open node from the current focus. But unlike up, when the focus is +at root, last wraps around to the last open node in the tree.
+ +
mark
+
The node +representing the non-fixed end of the current selection. The mark is set +by the selection mark operation.
+ +
next
+
The next open node from the current +focus. But unlike down, when the focus is on last open node, next wraps +around to the root node.
+ +
nextsibling
+
The next sibling from the node with +the current focus. If the node is already the last sibling then it is the +nextsibling.
+ +
parent
+
The parent of the node with the current focus. The parent +of the root is also the root.
+ +
prevsibling
+
The previous sibling from the +node with the current focus. If the node is already the first sibling then +it is the prevsibling.
+ +
root
+
The root node. You can also use id 0 to indicate +the root.
+ +
up
+
The last open node (in depth-first order) from the current focus. +The up of the root node (i.e. the root has focus) is also the root.
+ +
view.top +
+
First node that's current visible in the widget.
+ +
view.bottom
+
Last node that's +current visible in the widget.
+ +
path
+
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 -separator configuration option), but component +names must be separated by the designated separator.
+ +
@x,y
+
Indicates the +node that covers the point in the treeview window specified by x and y +(in pixel coordinates). If no part of the entryd covers that point, then +the closest node to that point is used.
+
+

+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. +

Data Fields

+A node +in the tree can have data fields. 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 treeview 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. +

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 bindtags. Bindtags are simply names that associate a binding +with one or more nodes. There is a built-in tag all that all node entries +automatically have. +

Treeview Operations

+The treeview operations are the invoked +by specifying the widget's pathname, the operation, and any arguments that +pertain to that operation. The general form is:

+
+pathName operation ?arg arg ...?
+

+

Operation and the args determine the exact behavior of the command. The +following operation are available for treeview widgets: +

+ +
pathName bbox ?-screen? +tagOrId...
+
Returns a list of 4 numbers, representing a bounding box of around +the specified entries. The entries is given by one or more tagOrId arguments. + If the -screen flag is given, then the x-y coordinates of the bounding +box are returned as screen coordinates, not virtual coordinates. Virtual +coordinates start from 0 from the root node. The returned list contains +the following values.
+ +
x
+
X-coordinate of the upper-left corner of the bounding +box.
+ +
y
+
Y-coordinate of the upper-left corner of the bounding box.
+ +
width
+
Width +of the bounding box.
+ +
height
+
Height of the bounding box.
+
+ + +
+ +
pathName bind tagName +?sequence command?
+
Associates command with tagName such that whenever the +event sequence given by sequence occurs for a node with this tag, command +will be invoked. The syntax is similar to the bind command except that +it operates on treeview entries, rather than widgets. See the bind manual +entry for complete details on sequence and the substitutions performed +on command before invoking it.

+If all arguments are specified then a +new binding is created, replacing any existing binding for the same sequence +and tagName. If the first character of command is + then command augments +an existing binding rather than replacing it. If no command argument is +provided then the command currently associated with tagName and sequence +(it's an error occurs if there's no such binding) is returned. If both command +and sequence are missing then a list of all the event sequences for which +bindings have been defined for tagName.

+ +
pathName button operation ?args? +
+
This command is used to control the button selectors within a treeview +widget. It has several forms, depending on operation:
+ +
pathName button +activate tagOrId
+
Designates the node given by tagOrId as active. When +a node is active it's entry is drawn using its active icon (see the -activeicon +option). Note that there can be only one active entry at a time. The special +id active indicates the currently active node.
+ +
pathName button bind tagName +?sequence command?
+
Associates command with tagName such that whenever the +event sequence given by sequence occurs for an button of a node entry with +this tag, command will be invoked. The syntax is similar to the bind command +except that it operates on treeview buttons, rather than widgets. See the +bind manual entry for complete details on sequence and the substitutions +performed on command before invoking it.

+If all arguments are specified +then a new binding is created, replacing any existing binding for the +same sequence and tagName. If the first character of command is + then command + augments an existing binding rather than replacing it. If no command argument +is provided then the command currently associated with tagName and sequence +(it's an error occurs if there's no such binding) is returned. If both command +and sequence are missing then a list of all the event sequences for which +bindings have been defined for tagName.

+ +
pathName button cget option
+
Returns +the current value of the configuration option given by option. Option may +have any of the values accepted by the configure operation described below. +
+ +
pathName button configure ?option? ?value option value ...?
+
Query or modify +the configuration options of the widget. If no option is specified, returns +a list describing all of the available options for pathName (see Tk_ConfigureInfo +for information on the format of this list). If option is specified with +no value, 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 option is specified). If one or more option-value 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. Option +and value are described in the section BUTTON OPTIONS + below.
+
+ + +
+ +
pathName +cget option
+
Returns the current value of the configuration option given +by option. Option may have any of the values accepted by the configure operation +described below.
+ +
pathName close ?-recurse? tagOrId...
+
Closes the node specified +by tagOrId. In addition, if a Tcl script was specified by the -closecommand +option, it is invoked. If the node is already closed, this command has +no effect. If the -recurse flag is present, each child node is recursively +closed.
+ +
pathName column operation ?args?
+
The following operations are available +for treeview columns.
+ +
pathName column activate column
+
Sets the active column +to column. Column is the name of a column in the widget. When a column is +active, it's drawn using its -activetitlebackground and -activetitleforeground +options. If column is the "", then no column will be active. If no column +argument is provided, then the name of the currently active column is returned. +
+ +
pathName column cget name option
+
Returns the current value of the column +configuration option given by option for name. Name is the name of column +that corresponds to a data field. Option may have any of the values accepted +by the configure operation described below.
+ +
pathName column configure name +?option? ?value option value ...?
+
Query or modify the configuration options +of the column designated by name. Name is the name of the column corresponding +to a data field. If no option is specified, returns a list describing all +of the available options for pathName (see Tk_ConfigureInfo for information +on the format of this list). If option is specified with no value, 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 option +is specified). If one or more option-value 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. Option and value are +described in the section COLUMN OPTIONS + below.
+ +
pathName column delete field +?field...?
+
Deletes one of more columns designated by field. Note that this +does not delete the data fields themselves.
+ +
pathName column insert position +field ?options...?
+
Inserts one of more columns designated by field. 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. Position indicates where in the +list of columns to add the new column. It may be either a number or end. +
+ +
pathName column invoke field
+
Invokes the Tcl command associated with the +column field, if there is one (using the column's -command option). The +command is ignored if the column's -state option set to disabled.
+ +
pathName +column move name dest
+
Moves the column name to the destination position. + Dest is the name of another column or a screen position in the form @x,y. +
+ +
pathName column names
+
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.
+ +
pathName +column nearest x ?y?
+
Returns the name of the column closest to the given +X-Y screen coordinate. If you provide a y argument (it's optional), a name +is returned only when if the point is over a column's title.
+
+ + +
+ +
pathName configure +?option? ?value option value ...?
+
Query or modify the configuration options +of the widget. If no option is specified, returns a list describing all +of the available options for pathName (see Tk_ConfigureInfo for information +on the format of this list). If option is specified with no value, 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 option +is specified). If one or more option-value 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. Option and value are +described in the section TREEVIEW OPTIONS + below.
+ +
pathName curselection +
+
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. +
+ +
pathName delete tagOrId...
+
Deletes one or more entries given by tagOrId and +its children.
+ +
pathName entry operation ?args?
+
The following operations are +available for treeview entries.
+ +
pathName entry activate tagOrId
+
Sets the +active entry to the one specified by tagOrId. When an entry is active +it is drawn using its active icon (see the -activeicon option). Note that +there can be only one active node at a time. The special id of the currently +active node is active.
+ +
pathName entry cget option
+
Returns the current value +of the configuration option given by option. Option may have any of the +values accepted by the configure operation described below.
+ +
pathName entry +children tagOrId ?first? ?last?
+
Returns a list of ids for the given range +of children of tagOrId. TagOrId is the id or tag of the node to be examined. +If only a first argument is present, then the id of the that child at +that numeric position is returned. If both first and last arguments are +given, then the ids of all the children in that range are returned. Otherwise +the ids of all children are returned.
+ +
pathName entry configure ?option? +?value option value ...?
+
Query or modify the configuration options of the +widget. If no option is specified, returns a list describing all of the +available options for pathName (see Tk_ConfigureInfo for information on +the format of this list). If option is specified with no value, 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 option +is specified). If one or more option-value 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. Option and value are +described below:
+ +
pathName entry delete tagOrId ?first ?last?
+
Deletes the +one or more children nodes of the parent tagOrId. If first and last arguments +are present, they are positions designating a range of children nodes to +be deleted.
+ +
pathName entry isbefore tagOrId1 tagOrId2
+
Returns 1 if tagOrId1 +is before tagOrId2 and 0 otherwise.
+ +
pathName entry ishidden tagOrId
+
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.
+ +
pathName entry isopen +tagOrId
+
Returns 1 if the node is currently open and 0 otherwise.
+ +
pathName +entry size -recurse tagOrId
+
Returns the number of children for parent node +tagOrId. If the -recurse flag is set, the number of all its descendants +is returned. The node itself is not counted.
+
+ + +
+ +
pathName find ?flags? first +last
+
Finds for all entries matching the criteria given by flags. A list +of ids for all matching nodes is returned. First and last are ids designating +the range of the search in depth-first order. If last is before first, then +nodes are searched in reverse order. The valid flags are:
+ +
-name pattern +
+
Specifies pattern to match against node names.
+ +
-full pattern
+
Specifies pattern +to match against node pathnames.
+ +
-option pattern
+
Specifies pattern to match +against the node entry's configuration option.
+ +
-exact
+
Patterns must match +exactly. The is the default.
+ +
-glob
+
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:
+ +
*
+
Matches any sequence of + characters in string, including a null string.
+ +
?
+
Matches any single character +in string.
+ +
[chars]
+
Matches any character in the set given by chars. If a +sequence of the form x-y appears in chars, then any character between x +and y, inclusive, will match.
+ +
\x
+
Matches the single character x. This +provides a way of avoiding the special interpretation of the characters +*?[]\ in the pattern.
+
+ + +
+ +
-regexp
+
Use regular expression pattern matching (i.e. +the same as implemented by the regexp command).
+ +
-nonmatching
+
Pick entries +that don't match.
+ +
-exec string
+
Specifies a Tcl script to be invoked for +each matching node. Percent substitutions are performed on string before + it is executed. The following substitutions are valid:
+ +
%W
+
The pathname +of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The full pathname of the node. +
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single percent.
+
+ + +
+ +
-count number
+
Stop +searching after number matches.
+ +
--
+
Indicates the end of flags.
+
+ + +
+ +
pathName focus + tagOrId
+
Sets the focus to the node given by tagOrId. When a node has focus, +it can receive keyboard events. The special id focus designates the node +that currently has focus.
+ +
pathName get ?-full? tagOrId tagOrId...
+
Translates +one or more ids to their node entry names. It returns a list of names +for all the ids specified. If the -full flag is set, then the full pathnames +are returned.
+ +
pathName hide ?flags? tagOrId...
+
Hides all nodes matching the +criteria given by flags. The search is performed recursively for each node +given by tagOrId. The valid flags are described below:
+ +
-name pattern
+
Specifies +pattern to match against node names.
+ +
-full pattern
+
Specifies pattern to match +against node pathnames.
+ +
-option pattern
+
Specifies pattern to match against +the node entry's configuration option.
+ +
-exact
+
Match patterns exactly. The +is the default.
+ +
-glob
+
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:
+ +
*
+
Matches any sequence of characters in string, +including a null string.
+ +
?
+
Matches any single character in string.
+ +
[chars] +
+
Matches any character in the set given by chars. If a sequence of the form +x-y appears in chars, then any character between x and y, inclusive, will +match.
+ +
\x
+
Matches the single character x. This provides a way of avoiding + the special interpretation of the characters *?[]\ in the pattern.
+
+ + +
+ +
-regexp +
+
Use regular expression pattern matching (i.e. the same as implemented by +the regexp command).
+ +
-nonmatching
+
Hide nodes that don't match.
+ +
--
+
Indicates +the end of flags.
+
+ + +
+ +
pathName index ?-at tagOrId? string
+
Returns the id of +the node specified by string. String may be a tag or node id. Some special +ids are normally relative to the node that has focus. The -at flag lets +you select another node.
+ +
pathName insert ?-at tagOrId? position path ?options...? +?path? ?options...?
+
Inserts one or more nodes at position. Position is the +location (number or end) where the new nodes are added to the parent node. + Path 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 -separator option). Pathnames +are normally absolute, but the -at switch lets you select a relative starting +point. Its value is the id of the starting node.

+All ancestors of the +new node must already exist, unless the -autocreate option is set. It is +also an error if a node already exists, unless the -allowduplicates option +is set.

+Option and value may have any of the values accepted by the entry +configure operation described in the ENTRY OPERATIONS + section below. This +command returns a list of the ids of the new entries.

+ +
pathName move tagOrId +how destId
+
Moves the node given by tagOrId to the destination node. The +node can not be an ancestor of the destination. DestId is the id of the +destination node and can not be the root of the tree. In conjunction with +how, it describes how the move is performed.
+ +
before
+
Moves the node before +the destination node.
+ +
after
+
Moves the node after the destination node.
+ +
into +
+
Moves the node to the end of the destination's list of children.
+
+ + +
+ +
pathName +nearest x y ?varName?
+
Returns the id of the node entry closest to the given +X-Y screen coordinate. The optional argument varName is the name of variable +which is set to either button or select to indicate over what part of the +node the coordinate lies. If the coordinate is not directly over any node, +then varName will contain the empty string.
+ +
pathName open ?-recurse? tagOrId... +
+
Opens the one or more nodes specified by tagOrId. If a node is not already +open, the Tcl script specified by the -opencommand option is invoked. If +the -recurse flag is present, then each descendant is recursively opened. +
+ +
pathName range ?-open? first last
+
Returns the ids in depth-first order +of the nodes between the first and last ids. If the -open flag is present, +it indicates to consider only open nodes. If last is before first, then +the ids are returned in reverse order.
+ +
pathName scan option args
+
This command +implements scanning. It has two forms, depending on option:
+ +
pathName scan +mark x y
+
Records x and y and the current view in the treeview window; +used in conjunction with later scan dragto commands. Typically this command +is associated with a mouse button press in the widget. It returns an empty +string.
+ +
pathName scan dragto x y.
+
Computes the difference between its x and +y arguments and the x and y arguments to the last scan mark 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.
+
+ + +
+ +
pathName see ?-anchor anchor? tagOrId +
+
Adjusts the view of entries so that the node given by tagOrId is visible +in the widget window. It is an error if tagOrId 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 -anchor flag. Its value is a Tk +anchor position.
+ +
pathName selection option arg
+
This command is used to adjust +the selection within a treeview widget. It has several forms, depending +on option:
+ +
pathName selection anchor tagOrId
+
Sets the selection anchor +to the node given by tagOrId. If tagOrId 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 anchor may be used to refer to the anchor node.
+ +
pathName selection cancel +
+
Clears the temporary selection of entries back to the current anchor. Temporary +selections are created by the selection mark operation.
+ +
pathName selection +clear first ?last?
+
Removes the entries between first and last (inclusive) +from the selection. Both first and last are ids representing a range of +entries. If last isn't given, then only first is deselected. Entries outside +the selection are not affected.
+ +
pathName selection clearall
+
Clears the entire +selection.
+ +
pathName selection mark tagOrId
+
Sets the selection mark to +the node given by tagOrId. 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 mark may be used to refer to the current + mark node. If tagOrId 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.
+ +
pathName selection includes tagOrId
+
Returns 1 if the +node given by tagOrId is currently selected, 0 if it isn't.
+ +
pathName selection +present
+
Returns 1 if any nodes are currently selected and 0 otherwise.
+ +
pathName +selection set first ?last?
+
Selects all of the nodes in the range between +first and last, inclusive, without affecting the selection state of nodes +outside that range.
+ +
pathName selection toggle first ?last?
+
Selects/deselects +nodes in the range between first and last, inclusive, from the selection. +If a node is currently selected, it becomes deselected, and visa versa. +
+
+ + +
+ +
pathName show ?flags? tagOrId...
+
Exposes all nodes matching the criteria given +by flags. This is the inverse of the hide operation. The search is performed +recursively for each node given by tagOrId. The valid flags are described +below:
+ +
-name pattern
+
Specifies pattern to match against node names.
+ +
-full +pattern
+
Specifies pattern to match against node pathnames.
+ +
-option pattern +
+
Specifies pattern to match against the entry's configuration option.
+ +
-exact +
+
Match patterns exactly. The is the default.
+ +
-glob
+
-glob 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:
+ +
*
+
Matches + any sequence of characters in string, including a null string.
+ +
?
+
Matches +any single character in string.
+ +
[chars]
+
Matches any character in the set +given by chars. If a sequence of the form x-y appears in chars, then any +character between x and y, inclusive, will match.
+ +
\x
+
Matches the single + character x. This provides a way of avoiding the special interpretation +of the characters *?[]\ in the pattern.
+
+ + +
+ +
-regexp
+
Use regular expression pattern +matching (i.e. the same as implemented by the regexp command).
+ +
-nonmatching +
+
Expose nodes that don't match.
+ +
--
+
Indicates the end of flags.
+
+ + +
+ +
pathName sort +?operation? args...
+
+ +
pathName sort auto ?boolean
+
Turns on/off automatic sorting +of node entries. If boolean is true, entries will be automatically sorted +as they are opened, closed, inserted, or deleted. If no boolean argument +is provided, the current state is returned.
+ +
pathName sort cget option
+
Returns +the current value of the configuration option given by option. Option may +have any of the values accepted by the configure operation described below. +
+ +
pathName sort configure ?option? ?value option value ...?
+
Query or modify +the sorting configuration options of the widget. If no option is specified, +returns a list describing all of the available options for pathName (see +Tk_ConfigureInfo for information on the format of this list). If option +is specified with no value, 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 option is specified). If one or more +option-value 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. Option and value are described below:
+ +
-column string
+
Specifies +the column to sort. Entries in the widget are rearranged according to this +column. If column is "" then no sort is performed.
+ +
-command string
+
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.
+ +
-decreasing boolean +
+
Indicates to sort in ascending/descending order. If boolean is true, then +the entries as in descending order. The default is no.
+ +
-mode string
+
Specifies +how to compare entries when sorting. String may be one of the following: +
+ +
ascii
+
Use string comparison based upon the ASCII collation order.
+ +
dictionary +
+
Use dictionary-style comparison. This is the same as ascii 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".
+ +
integer
+
Compares fields as integers.
+ +
real
+
Compares fields +as floating point numbers.
+ +
command
+
Use the Tcl proc specified by the -command +option to compare entries when sorting. If no command is specified, the +sort reverts to ascii sorting.
+
+ + + +
+ +
pathName sort once ?flags? tagOrId...
+
Sorts +the children for each entries specified by tagOrId. By default, entries +are sorted by name, but you can specify a Tcl proc to do your own comparisons. +
+ +
-recurse
+
Recursively sort the entire branch, not just the children.
+
+ + + +
+ +
pathName +tag operation args
+
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.

+Both operation and its arguments determine the exact behavior of +the command. The operations available for tags are listed below.

+ +
pathName +tag add string id...
+
Adds the tag string to one of more entries.
+ +
pathName tag +delete string id...
+
Deletes the tag string from one or more entries.
+ +
pathName +tag forget string
+
Removes the tag string from all entries. It's not an error +if no entries are tagged as string.
+ +
pathName tag names ?id?
+
Returns a list +of tags used. If an id argument is present, only those tags used by the +node designated by id are returned.
+ +
pathName tag nodes string
+
Returns a +list of ids that have the tag string. If no node is tagged as string, then +an empty string is returned.
+
+ + +
+ +
pathName text operation ?args?
+
This operation +is used to provide text editing for cells (data fields in a column) or +entry labels. It has several forms, depending on operation:
+ +
pathName text +apply
+
Applies the edited buffer, replacing the entry label or data field. +The edit window is hidden.
+ +
pathName text cancel
+
Cancels the editing operation, +reverting the entry label or data value back to the previous value. The +edit window is hidden.
+ +
pathName text cget value
+
Returns the current value +of the configuration option given by option. Option may have any of the +values accepted by the configure operation described below.
+ +
pathName text +configure ?option value?
+
Query or modify the configuration options of the +edit window. If no option is specified, returns a list describing all of +the available options (see Tk_ConfigureInfo for information on the format +of this list). If option is specified with no value, 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 option is specified). + If one or more option-value 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. Option and value are described in the section + TEXT EDITING OPTIONS + below.
+
+ + +
+ +
pathName text delete first last
+
Deletes the +characters in the edit buffer between the two given character positions. +
+ +
pathName text get ?-root? x y
+
+ +
pathName text icursor index
+
+ +
pathName text +index index
+
Returns the text index of given index.
+ +
pathName text insert +index string
+
Insert the text string string into the edit buffer at the +index index. For example, the index 0 will prepend the buffer.
+ +
pathName +text selection args
+
This operation controls the selection of the editing +window. Note that this differs from the selection of entries. It has the +following forms:
+ +
pathName text selection adjust index
+
Adjusts either the +first or last index of the selection.
+ +
pathName text selection clear
+
Clears +the selection.
+ +
pathName text selection from index
+
Sets the anchor of the +selection.
+ +
pathName text selection present
+
Indicates if a selection is present. +
+ +
pathName text selection range start end
+
Sets both the anchor and mark of +the selection.
+ +
pathName text selection to index
+
Sets the unanchored end +(mark) of the selection.
+
+ + +
+ +
pathName toggle tagOrId
+
Opens or closes the node +given by tagOrId. If the corresponding -opencommand or -closecommand option +is set, then that command is also invoked.
+ +
pathName xview args
+
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:
+ +
pathName +xview
+
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 treeview 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 -xscrollcommand option. +
+ +
pathName xview tagOrId
+
Adjusts the view in the window so that the character +position given by tagOrId is displayed at the left edge of the window. Character +positions are defined by the width of the character 0.
+ +
pathName xview moveto +fraction
+
Adjusts the view in the window so that fraction of the total width +of the treeview widget's text is off-screen to the left. fraction must be +a fraction between 0 and 1.
+ +
pathName xview scroll number what
+
This command +shifts the view in the window left or right according to number and what. +Number must be an integer. What must be either units or pages or an abbreviation +of one of these. If what is units, the view adjusts left or right by number +character units (the width of the 0 character) on the display; if it is +pages then the view adjusts by number screenfuls. If number is negative +then characters farther to the left become visible; if it is positive +then characters farther to the right become visible.
+
+ + +
+ +
pathName yview ?args? +
+
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:
+ +
pathName +yview
+
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 -yscrollcommand option.
+ +
pathName yview tagOrId
+
Adjusts the view in the +window so that the node given by tagOrId is displayed at the top of the +window.
+ +
pathName yview moveto fraction
+
Adjusts the view in the window so +that the node given by fraction appears at the top of the window. Fraction +is a fraction between 0 and 1; 0 indicates the first node, 0.33 indicates +the node one-third the way through the treeview widget, and so on.
+ +
pathName +yview scroll number what
+
This command adjusts the view in the window up +or down according to number and what. Number must be an integer. What must +be either units or pages. If what is units, the view adjusts up or down +by number lines; if it is pages then the view adjusts by number screenfuls. +If number is negative then earlier nodes become visible; if it is positive +then later nodes become visible.
+
+ + +

Treeview Options

+In addition to the configure +operation, widget configuration options may also be set by the Tk option +command. The class resource name is TreeView.
+option add *TreeView.Foreground white
+option add *TreeView.Background blue
+

The following widget options are available: +

+ +
-activebackground color
+
Sets +the background color for active entries. A node is active when the mouse +passes over it's entry or using the activate operation.
+ +
-activeforeground +color
+
Sets the foreground color of the active node. A node is active when +the mouse passes over it's entry or using the activate operation.
+ +
-activeicons +images
+
Specifies images to be displayed for an entry's icon when it is active. +Images is a list of two Tk images: the first image is displayed when the +node is open, the second when it is closed.
+ +
-autocreate boolean
+
If boolean +is true, automatically create missing ancestor nodes when inserting new +nodes. Otherwise flag an error. The default is no.
+ +
-allowduplicates boolean +
+
If boolean is true, allow nodes with duplicate pathnames when inserting +new nodes. Otherwise flag an error. The default is no.
+ +
-background color
+
Sets +the background color of the widget. The default is white.
+ +
-borderwidth pixels +
+
Sets the width of the 3-D border around the outside edge of the widget. +The -relief option determines if the border is to be drawn. The default +is 2.
+ +
-closecommand string
+
Specifies a Tcl script to be invoked when a node +is closed. You can overrider this for individual entries using the entry's +-closecommand option. The default is "". Percent substitutions are performed +on string before it is executed. The following substitutions are valid: +
+ +
%W
+
The pathname of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The full pathname +of the node.
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single percent.
+
+ + +
+ +
-cursor +cursor
+
Specifies the widget's cursor. The default cursor is "".
+ +
-dashes number +
+
Sets the dash style of the horizontal and vertical lines drawn connecting + entries. Number is the length in pixels of the dashes and gaps in the line. +If number is 0, solid lines will be drawn. The default is 1 (dotted).
+ +
-exportselection +boolean
+
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 STRING; the value of the +selection will be the label of the selected nodes, separated by newlines. + The default is no.
+ +
-flat boolean
+
Indicates whether to display the tree as +a flattened list. If boolean is true, then the hierarchy will be a list +of full paths for the nodes. This option also has affect on sorting. See +the SORT OPERATIONS + section for more information. The default is no.
+ +
-focusdashes +dashList
+
Sets the dash style of the outline rectangle drawn around the +entry label of the node that current has focus. Number is the length in +pixels of the dashes and gaps in the line. If number is 0, a solid line +will be drawn. The default is 1.
+ +
-focusforeground color
+
Sets the color of +the focus rectangle. The default is black.
+ +
-font fontName
+
Specifies the +font for entry labels. You can override this for individual entries with +the entry's -font configuration option. The default is *-Helvetica-Bold-R-Normal-*-12-120-*. +
+ +
-foreground color
+
Sets the text color of entry labels. You can override +this for individual entries with the entry's -foreground configuration option. + The default is black.
+ +
-height pixels
+
Specifies the requested height of +widget. The default is 400.
+ +
-hideroot boolean
+
If boolean is true, it indicates +that no entry for the root node should be displayed. The default is no. +
+ +
-highlightbackground color
+
Specifies the normal color of the traversal +highlight region when the widget does not have the input focus.
+ +
-highlightcolor +color
+
Specifies the color of the traversal highlight rectangle when the +widget has the input focus. The default is black.
+ +
-highlightthickness pixels +
+
Specifies the width of the highlight rectangle indicating when the widget +has input focus. The value may have any of the forms acceptable to Tk_GetPixels. + If the value is zero, no focus highlight will be displayed. The default +is 2.
+ +
-icons images
+
Specifies images for the entry's icon. Images is a list +of two Tk images: the first image is displayed when the node is open, +the second when it is closed.
+ +
-linecolor color
+
Sets the color of the connecting +lines drawn between entries. The default is black.
+ +
-linespacing pixels
+
Sets +the number of pixels spacing between entries. The default is 0.
+ +
-linewidth +pixels
+
Set the width of the lines drawn connecting entries. If pixels is +0, no vertical or horizontal lines are drawn. The default is 1.
+ +
-opencommand +string
+
Specifies a Tcl script to be invoked when a node is open. You can +override this for individual entries with the entry's -opencommand configuration +option. The default is "". Percent substitutions are performed on string +before it is executed. The following substitutions are valid:
+ +
%W
+
The pathname +of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The full pathname of the node. +
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single percent.
+
+ + +
+ +
-relief relief
+
Specifies +the 3-D effect for the widget. Relief specifies how the treeview widget +should appear relative to widget it is packed into; for example, raised +means the treeview widget should appear to protrude. The default is sunken. +
+ +
-scrollmode mode
+
Specifies the style of scrolling to be used. The following +styles are valid. This is the default is hierbox.
+ +
listbox
+
Like the listbox +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.
+ +
hierbox
+
Like the hierbox widget, the last entry can only +be viewed at the bottom of the widget window. The scrollbar stays a constant +size.
+ +
canvas
+
Like the canvas widget, the entries are bound within the +scrolling area.
+
+ + +
+ +
-selectbackground color
+
Sets the background color selected +node entries. The default is #ffffea.
+ +
-selectborderwidth pixels
+
Sets the width +of the raised 3-D border drawn around the labels of selected entries. The +default is 0. -selectcommand string Specifies a Tcl script to invoked when +the set of selected nodes changes. The default is "".
+ +
-selectforeground color +
+
Sets the color of the labels of selected node entries. The default is black. +
+ +
-selectmode mode
+
Specifies the selection mode. If mode is single, only one +node can be selected at a time. If multiple more than one node can be selected. +The default is single.
+ +
-separator string
+
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 string is the empty string, the pathnames are Tcl lists. + Each element is a path component. The default is "".
+ +
-showtitles boolean +
+
If boolean is false, column titles are not be displayed. The default is +yes.
+ +
-sortselection boolean
+
If boolean 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 no.
+ +
-takefocus focus
+
Provides +information used when moving the focus from window to window via keyboard +traversal (e.g., Tab and Shift-Tab). If focus is 0, this means that this window +should be skipped entirely during keyboard traversal. 1 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 "1".
+ +
-trim string
+
Specifies a string leading characters to +trim from entry pathnames before parsing. This only makes sense if the +-separator is also set. The default is "".
+ +
-width pixels
+
Sets the requested +width of the widget. If pixels is 0, then the with is computed from the +contents of the treeview widget. The default is 200.
+ +
-xscrollcommand string +
+
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.
+ +
-xscrollincrement pixels
+
Sets the horizontal scrolling distance. +The default is 20 pixels.
+ +
-yscrollcommand string
+
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.
+ +
-yscrollincrement pixels +
+
Sets the vertical scrolling distance. The default is 20 pixels.
+
+ +

Entry Options

+Many +widget configuration options have counterparts in entries. For example, +there is a -closecommand 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.

+There is no resource class or +name for entries. +

+ +
-activeicons images
+
Specifies images to be displayed as +the entry's icon when it is active. This overrides the global -activeicons +configuration option for the specific entry. Images is a list of two Tk +images: the first image is displayed when the node is open, the second +when it is closed.
+ +
-bindtags tagList
+
Specifies the binding tags for nodes. + TagList 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 all.
+ +
-button string
+
Indicates whether a button should be displayed +on the left side of the node entry. String can be yes, no, or auto. If +auto, then a button is automatically displayed if the node has children. + This is the default.
+ +
-closecommand string
+
Specifies a Tcl script to be invoked +when the node is closed. This overrides the global -closecommand option +for this entry. The default is "". Percent substitutions are performed on +string before it is executed. The following substitutions are valid:
+ +
%W +
+
The pathname of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The full pathname +of the node.
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single percent.
+
+ + +
+ +
-data +string
+
Sets data fields for the node. String is a list of name-value pairs +to be set. The default is "".
+ +
-font fontName
+
Sets the font for entry labels. + This overrides the widget's -font option for this node. The default is *-Helvetica-Bold-R-Normal-*-12-120-*. +
+ +
-foreground color
+
Sets the text color of the entry label. This overrides +the widget's -foreground configuration option. The default is "".
+ +
-icons images +
+
Specifies images to be displayed for the entry's icon. This overrides the +global -icons configuration option. Images is a list of two Tk images: the +first image is displayed when the node is open, the second when it is closed. +
+ +
-label string
+
Sets the text for the entry's label. If not set, this defaults +to the name of the node. The default is "".
+ +
-opencommand string
+
Specifies +a Tcl script to be invoked when the entry is opened. This overrides the +widget's -opencommand option for this node. The default is "". Percent substitutions +are performed on string before it is executed. The following substitutions +are valid:
+ +
%W
+
The pathname of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The +full pathname of the node.
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single +percent.
+
+ + +

Button Options

+Button configuration options may also be set by the +option command. The resource subclass is Button. The resource name is always +button.
+option add *TreeView.Button.Foreground white
+option add *TreeView.button.Background blue
+

The following are the configuration options available for buttons. +

+ +
-activebackground +color
+
Sets the background color of active buttons. A button is made active +when the mouse passes over it or by the button activate operation.
+ +
-activeforeground +color
+
Sets the foreground color of active buttons. A button is made active +when the mouse passes over it or by the button activate operation.
+ +
-background +color
+
Sets the background of the button. The default is white.
+ +
-borderwidth +pixels
+
Sets the width of the 3-D border around the button. The -relief option +determines if a border is to be drawn. The default is 1.
+ +
-closerelief relief +
+
Specifies the 3-D effect for the closed button. Relief indicates how the +button should appear relative to the widget; for example, raised means +the button should appear to protrude. The default is solid.
+ +
-cursor cursor +
+
Sets the widget's cursor. The default cursor is "".
+ +
-foreground color
+
Sets +the foreground color of buttons. The default is black.
+ +
-images images
+
Specifies +images to be displayed for the button. Images 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 images is the empty string, then a plus/minus gadget +is drawn. The default is "".
+ +
-openrelief relief
+
Specifies the 3-D effect of +the open button. Relief indicates how the button should appear relative +to the widget; for example, raised means the button should appear to protrude. + The default is flat.
+ +
-size pixels
+
Sets the requested size of the button. + The default is 0.
+
+ + +

Column Options

+Column configuration options may also +be set by the option command. The resource subclass is Column. The resource +name is the name of the column.
+option add *TreeView.Column.Foreground white
+option add *TreeView.treeView.Background blue
+

The following configuration options are available for columns. +

+ +
-background +color
+
Sets the background color of the column. This overrides the widget's +-background option. The default is white.
+ +
-borderwidth pixels
+
Sets the width +of the 3-D border of the column. The -relief option determines if a border +is to be drawn. The default is 0.
+ +
-edit boolean
+
Indicates if the column's +data fields can be edited. If boolean is false, the data fields in the +column may not be edited. The default is yes.
+ +
-foreground color
+
Specifies +the foreground color of the column. You can override this for individual +entries with the entry's -foreground option. The default is black.
+ +
-font fontName +
+
Sets the font for a column. You can override this for individual entries +with the entry's -font option. The default is *-Helvetica-Bold-R-Normal-*-12-120-*. +
+ +
-hide boolean
+
If boolean is true, the column is not displayed. The default +is yes.
+ +
-justify justify
+
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. Justify must be left, right, or center. + The default is left.
+ +
-pad pad
+
Specifies how much padding for the left and +right sides of the column. Pad is a list of one or two screen distances. + If pad has two elements, the left side of the column is padded by the +first distance and the right side by the second. If pad has just one distance, +both the left and right sides are padded evenly. The default is 2.
+ +
-relief +relief
+
Specifies the 3-D effect of the column. Relief specifies how the +column should appear relative to the widget; for example, raised means +the column should appear to protrude. The default is flat.
+ +
-state state
+
Sets +the state of the column. If state is disable then the column title can not +be activated nor invoked. The default is normal.
+ +
-text string
+
Sets the title +for the column. The default is "".
+ +
-titleforeground color
+
Sets the foreground +color of the column title. The default is black.
+ +
-titleshadow color
+
Sets +the color of the drop shadow of the column title. The default is "".
+ +
-width +pixels
+
Sets the requested width of the column. This overrides the computed +with of the column. If pixels is 0, the width is computed as from the contents +of the column. The default is 0.
+
+ + +

Text Editing Options

+Text edit window configuration +options may also be set by the option command. The resource class is TreeViewEditor. +The resource name is always edit.
+option add *TreeViewEditor.Foreground white
+option add *edit.Background blue
+

The following are the configuration options available for the text editing +window. +

+ +
-background color
+
Sets the background of the text edit window. The +default is white.
+ +
-borderwidth pixels
+
Sets the width of the 3-D border around +the edit window. The -relief option determines if a border is to be drawn. + The default is 1.
+ +
-exportselection boolean
+
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 STRING. The default is no.
+ +
-relief relief
+
Specifies +the 3-D effect of the edit window. Relief indicates how the background should +appear relative to the edit window; for example, raised means the background +should appear to protrude. The default is solid.
+ +
-selectbackground color +
+
Sets the background of the selected text in the edit window. The default +is white.
+ +
-selectborderwidth pixels
+
Sets the width of the 3-D border around +the selected text in the edit window. The -selectrelief option determines +if a border is to be drawn. The default is 1.
+ +
-selectforeground color
+
Sets +the foreground of the selected text in the edit window. The default is +white.
+ +
-selectrelief relief
+
Specifies the 3-D effect of the selected text +in the edit window. Relief indicates how the text should appear relative +to the edit window; for example, raised means the text should appear to +protrude. The default is flat.
+
+ + +

Default Bindings

+Tk automatically creates +class bindings for treeviews that give them Motif-like behavior. Much of +the behavior of a treeview widget is determined by its -selectmode option, +which selects one of two ways of dealing with the selection.

+If the selection +mode is single, only one node can be selected at a time. Clicking button +1 on an node selects it and deselects any other selected item.

+If the selection +mode is multiple, 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. +

    +
  1. In extended 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.
  2. In extended 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.
  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.
  4. Mouse button 2 may +be used for scanning. If it is pressed and dragged over the treeview widget, +the contents of the hierarchy drag at high speed in the direction the mouse +moves.
  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 browse or extended +then the new active entry is also selected and all other entries are deselected. +In extended mode the new active entry becomes the selection anchor.
  6. In extended +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.
  7. The Left and Right keys scroll +the treeview widget view left and right by the width of the character 0. +Control-Left and Control-Right scroll the treeview 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.
  8. The Prior and Next keys scroll the +treeview widget view up and down by one page (the height of the window). +
  9. The Home and End keys scroll the treeview widget horizontally to the left +and right edges, respectively.
  10. Control-Home sets the location cursor to the +the first entry, selects that entry, and deselects everything else in +the widget.
  11. Control-End sets the location cursor to the the last entry, +selects that entry, and deselects everything else in the widget.
  12. In extended +mode, Control-Shift-Home extends the selection to the first entry and Control-Shift-End +extends the selection to the last entry.
  13. In multiple mode, Control-Shift-Home +moves the location cursor to the first entry and Control-Shift-End moves +the location cursor to the last entry.
  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.
  15. In extended 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.
  16. In extended mode, the Escape +key cancels the most recent selection and restores all the entries in the +selected range to their previous selection state.
  17. Control-slash selects everything +in the widget, except in single and browse modes, in which case it selects +the active entry and deselects everything else.
  18. Control-backslash deselects +everything in the widget, except in browse mode where it has no effect. +
  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.
  20. +
+

+The behavior +of treeview widgets can be changed by defining new bindings for individual +widgets or by redefining the class bindings. +

Widget Bindings

+In addition +to the above behavior, the following additional behavior is defined by +the default widget class (TreeView) bindings. +
+ +
<ButtonPress-2>
+
Starts scanning. +
+ +
<B2-Motion>
+
Adjusts the scan.
+ +
<ButtonRelease-2>
+
Stops scanning.
+ +
<B1-Leave>
+
Starts auto-scrolling. +
+ +
<B1-Enter>
+
Starts auto-scrolling
+ +
<KeyPress-Up>
+
Moves the focus to the previous +entry.
+ +
<KeyPress-Down>
+
Moves the focus to the next entry.
+ +
<Shift-KeyPress-Up>
+
Moves +the focus to the previous sibling.
+ +
<Shift-KeyPress-Down>
+
Moves the focus to the +next sibling.
+ +
<KeyPress-Prior>
+
Moves the focus to first entry. Closed or hidden +entries are ignored.
+ +
<KeyPress-Next>
+
Move the focus to the last entry. Closed +or hidden entries are ignored.
+ +
<KeyPress-Left>
+
Closes the entry. It is not an +error if the entry has no children.
+ +
<KeyPress-Right>
+
Opens the entry, displaying +its children. It is not an error if the entry has no children.
+ +
<KeyPress-space>
+
In +"single" select mode this selects the entry. In "multiple" mode, it toggles +the entry (if it was previous selected, it is not deselected).
+ +
<KeyRelease-space>
+
Turns +off select mode.
+ +
<KeyPress-Return>
+
Sets the focus to the current entry.
+ +
<KeyRelease-Return>
+
Turns +off select mode.
+ +
<KeyPress>
+
Moves to the next entry whose label starts with +the letter typed.
+ +
<KeyPress-Home>
+
Moves the focus to first entry. Closed or +hidden entries are ignored.
+ +
<KeyPress-End>
+
Move the focus to the last entry. +Closed or hidden entries are ignored.
+ +
<KeyPress-F1>
+
Opens all entries.
+ +
<KeyPress-F2>
+
Closes +all entries (except root).
+
+ +

Button Bindings

+Buttons have bindings. There are +associated with the "all" bindtag (see the entry's -bindtag option). You +can use the bind operation to change them. +
+ +
<Enter>
+
Highlights the button of +the current entry.
+ +
<Leave>
+
Returns the button back to its normal state.
+ +
<ButtonRelease-1>
+
Adjust +the view so that the current entry is visible.
+
+ +

Entry Bindings

+Entries have +default bindings. There are associated with the "all" bindtag (see the +entry's -bindtag option). You can use the bind operation to modify them. +
+ +
<Enter>
+
Highlights +the current entry.
+ +
<Leave>
+
Returns the entry back to its normal state.
+ +
<ButtonPress-1>
+
Sets +the selection anchor the current entry.
+ +
<Double-ButtonPress-1>
+
Toggles the selection +of the current entry.
+ +
<B1-Motion>
+
For "multiple" mode only. Saves the current +location of the pointer for auto-scrolling. Resets the selection mark. +
+ +
<ButtonRelease-1>
+
For "multiple" mode only. Sets the selection anchor to the + current entry.
+ +
<Shift-ButtonPress-1>
+
For "multiple" mode only. Extends the selection. +
+ +
<Shift-Double-ButtonPress-1>
+
Place holder. Does nothing.
+ +
<Shift-B1-Motion>
+
Place holder. +Does nothing.
+ +
<Shift-ButtonRelease-1>
+
Stop auto-scrolling.
+ +
<Control-ButtonPress-1>
+
For +"multiple" mode only. Toggles and extends the selection.
+ +
<Control-Double-ButtonPress-1>
+
Place +holder. Does nothing.
+ +
<Control-B1-Motion>
+
Place holder. Does nothing.
+ +
<Control-ButtonRelease-1>
+
Stops +auto-scrolling.
+ +
<Control-Shift-ButtonPress-1>
+
???
+ +
<Control-Shift-Double-ButtonPress-1>
+
Place +holder. Does nothing.
+ +
<Control-Shift-B1-Motion>
+
Place holder. Does nothing.
+
+ +

Column +Bindings

+Columns have bindings too. They are associated with the column's +"all" bindtag (see the column -bindtag option). You can use the column bind +operation to change them. +
+ +
<Enter>
+
Highlights the current column title.
+ +
<Leave>
+
Returns +the column back to its normal state.
+ +
<ButtonRelease-1>
+
Invokes the command (see +the column's -command option) if one if specified.
+
+ +

Column Rule Bindings

+ +
+ +
<Enter>
+
Highlights +the current and activates the ruler.
+ +
<Leave>
+
Returns the column back to its +normal state. Deactivates the ruler.
+ +
<ButtonPress-1>
+
Sets the resize anchor for +the column.
+ +
<B1-Motion>
+
Sets the resize mark for the column.
+ +
<ButtonRelease-1>
+
Adjust +the size of the column, based upon the resize anchor and mark positions. +
+
+ +

Example

+The treeview command creates a new widget.
+treeview .h -bg white
+

A new Tcl command .h is also created. This command can be used to query +and modify the treeview widget. For example, to change the background +color of the table to "green", you use the new command and the widget's +configure operation.
+# Change the background color.
+.h configure -background "green"
+

By default, the treeview 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 -tree option +to specify the name of another tree.
+# View the tree "myTree".
+.h configure -tree "myTree"
+

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 0 (you can use also use the special +id root). The insert operation lets you insert one or more new entries into +the tree. The last argument is the node's pathname.
+# Create a new entry named "myEntry"
+set id [.h insert end "myEntry"]
+

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.
+# Prepend "fred".
+set id [.h insert 0 "fred"]
+

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 -label option.
+# Create a new node named "fred"
+set id [.h insert end "fred" -label "Fred Flintstone"]
+

The insert operation returns the id of the new node. You can also use the +index operation to get this information.
+# Get the id of "fred"
+.h index "fred"
+

To insert a node somewhere other than root, use the -at switch. It takes +the id of the node where the new child will be added.
+# Create a new node "barney" in "fred".
+.h insert -at $id end "barney"
+

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.
+# Create a new sub-entry of "fred"
+.h insert end "fred barney"
+

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 -autocreate configuration option to force +the creation of ancestor nodes.
+# Force the creation of ancestors.
+.h configure -autocreate yes
+.h insert end "fred barney wilma betty"
+

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 -separator 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. +
+.h configure -separator /
+.h insert end "/usr/local/tcl/bin"
+

If the path is prefixed by extraneous characters, you can automatically +trim it off using the -trim option. It removed the string from the path +before it is parsed.
+.h configure -trim C:/windows -separator /
+.h insert end "C:/window/system"
+

You can insert more than one entry at a time with the insert operation. + This can be much faster than looping over a list of names.
+# The slow way
+foreach f [glob $dir/*] {
+ .h insert end $f
+}
+# The fast way
+eval .h insert end [glob $dir/*]
+

In this case, the insert operation will return a list of ids of the new +entries.

+You can delete entries with the delete operation. It takes one +or more tags of ids as its argument. It deletes the entry and all its children. +
+.h delete $id
+

Entries have several configuration options. They control the appearance +of the entry's icon and label. We have already seen the -label option that +sets the entry's text label. The entry configure operation lets you set +or modify an entry's configuration options.
+.h entry configure $id -color red -font fixed
+

You can hide an entry and its children using the -hide option.
+.h entry configure $id -hide yes
+

More that one entry can be configured at once. All entries specified are +configured with the same options.
+.h entry configure $i1 $i2 $i3 $i4 -color brown
+

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 -icons option. It takes +a list of two image names: one to represent the open entry, another when +it is closed.
+set im1 [image create photo -file openfolder.gif]
+set im2 [image create photo -file closefolder.gif]
+.h entry configure $id -icons "$im1 $im2"
+

If -icons is set to the empty string, no icons are display.

+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 + or - symbol, but can be configured in a variety of ways using the button +configure operation. For example, the + and - symbols can be replaced with +Tk images.
+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
+

Entries can contain an arbitrary number of data fields. Data fields are +name-value pairs. Both the value and name are strings. The entry's -data option +lets you set data fields.
+.h entry configure $id -data {mode 0666 group users}
+

The -data takes a list of name-value pairs.

+You can display these data fields +as columns in the treeview widget. You can create and configure columns +with the column operation. For example, to add a new column to the widget, +use the column insert operation. The last argument is the name of the data +field that you want to display.
+.h column insert end "mode"
+

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 -text option. +
+.h column insert end "mode" -text "File Permissions"
+

Columns have several configuration options. The column configure operation +lets you query or modify column options.
+.h column configure "mode" -justify left
+

The -justify option says how the data is justified within in the column. + The -hide option indicates whether the column is displayed.
+.h column configure "mode" -hide yes
+

Entries can be selected by clicking on the mouse. Selected entries are +drawn using the colors specified by the -selectforeground and -selectbackground +configuration options. The selection itself is managed by the selection +operation.
+# Clear all selections
+.h selection clear 0 end
+# Select the root node
+.h selection set 0
+

The curselection operation returns a list of ids of all the selected entries. +
+set ids [.h curselection]
+

You can use the get operation to convert the ids to their pathnames.
+set names [eval .h get -full $ids]
+

If a treeview is exporting its selection (using the -exportselection option), +then it will observe the standard X11 protocols for handling the selection. + Treeview selections are available as type STRING; the value of the selection +will be the pathnames of the selected entries, separated by newlines.

+The +treeview supports two modes of selection: single and multiple. 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 +-selectmode option.
+.h configure -selectmode "multiple"
+

You can be notified when the list of selected entries changes. The widget's +-selectcommand specifies a Tcl procedure that is called whenever the selection +changes.
+proc SelectNotify { widget } {
+ set ids [$widget curselection]
+}
+.h configure -selectcommand "SelectNotify .h"
+

The widget supports the standard Tk scrolling and scanning operations. The +treeview can be both horizontally and vertically. You can attach scrollbars +to the treeview the same way as the listbox or canvas widgets.
+scrollbar .xbar -orient horizontal -command ".h xview"
+scrollbar .ybar -orient vertical -command ".h yview"
+.h configure -xscrollcommand ".xbar set" \
+ -yscrollcommand ".ybar set"
+

There are three different modes of scrolling: listbox, canvas, and hierbox. + In listbox mode, the last entry can always be scrolled to the top of the +widget. In hierbox mode, the last entry is always drawn at the bottom of +the widget. The scroll mode is set by the widget's -selectmode option.
+.h configure -scrollmode "listbox"
+

Entries can be programmatically opened or closed using the open and close +operations respectively.
+.h open $id
+.h close $id
+

When an entry is opened, a Tcl procedure can be automatically invoked. The +-opencommand option specifies this procedure. This procedure can lazily +insert entries as needed.
+proc AddEntries { dir } {
+ eval .h insert end [glob -nocomplain $dir/*]
+}
+.h configure -opencommand "AddEntries %P"
+

Now when an entry is opened, the procedure AddEntries is called and adds +children to the entry. Before the command is invoked, special "%" substitutions +(like bind) are performed. Above, %P is translated to the pathname of the +entry.

+The same feature exists when an entry is closed. The -closecommand +option specifies the procedure.
+proc DeleteEntries { id } {
+ .h entry delete $id 0 end
+}
+.h configure -closecommand "DeleteEntries %#"
+

When an entry is closed, the procedure DeleteEntries is called and deletes +the entry's children using the entry delete operation (%# is the id of entry). + +

Keywords

+treeview, widget

+ +


+Table of Contents

+

+ 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 @@ + + + + + +treeview(n) manual page + + +Table of Contents

+ +

Name

+treeview - Create and manipulate hierarchical +table widgets +

Synopsis

+treeview pathName ?options? +

Description

+The treeview +widget displays a tree of data. It replaces both the hiertable and hierbox +widgets. The treeview is 100% syntax compatible with the hiertable widget. + The hiertable command is retained for sake of script-level compatibility. + This widget obsoletes the hierbox widget. It does everything the old hierbox +widget did, but also provides data sharing (via tree data objects) and +the ability to tag nodes. +

Introduction

+The treeview 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.

+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 open, its children are exposed. When it +is closed, the children and their descedants are hidden. The button is +normally a + or - symbol (ala Windows Explorer), but can be replaced with +a pair of Tk images (open and closed images).

+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. +

Tree Data Object

+The tree +is not stored inside the widget but in a tree data object (see the tree +command for a further explanation). Tree data objects can be shared among +different clients, such as a treeview widget or the tree command. You can +walk the tree and manage its data with the tree command tree, while displaying +it with the treeview widget. Whenever the tree is updated, the treeview +widget is automatically redrawn.

+By default, the treeview widget creates +its own tree object. The tree initially contains just a root node. But you +can also display trees created by the tree command using the -tree configuration +option. Treeview widgets can share the same tree object, possibly displaying +different views of the same data.

+A tree object has both a Tcl and C API. + You can insert or delete nodes using treeview widget or tree 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. +

Syntax

+
+

+treeview pathName ?option value?...
+

The treeview command creates a new window pathName and makes it into a +treeview widget. At the time this command is invoked, there must not exist +a window named pathName, but pathName'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 configure operation +below for the exact details about what option and value pairs are valid. +

+If successful, treeview 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: +
+

+pathName operation ?arg?...
+

Both operation and its arguments determine the exact behavior of the command. + The operations available are described in the TREEVIEW OPERATIONS + section. + +

IDs and Tags

+Nodes can be inserted into a tree using the treeview widget +
+blt::treeview .t
+set node [.t insert end root "one"]
+

or tree command.
+set tree [blt::tree create]
+set node [$tree insert root "one"]
+

In both cases, a number identifying the node is returned (the value of +$node). This serial number or id 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 0. 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 treeview widget or the tree command. Ids are recycled +when the node deleted.

+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, "x123" is valid, but "123" 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 treeview widget +take either node ids or tag names as arguments. Using a tag says to apply +the operation to all nodes with that tag.

+The tag all is implicitly associated +with every node in the tree. It may be used to invoke operations on all +the nodes in the tree.

+Tags may be shared, just like trees, between clients. + For example, you can use the tags created by the tree command with treeview +widgets. +

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 +treeview widget. +
+ +
active
+
The node where the mouse pointer is currently located. +When a node is active, it is drawn using its active icon (see the -activeicon +option). The active id is changed automatically by moving the mouse pointer +over another node or by using the entry activate operation. Note that there +can be only one active node at a time.
+ +
anchor
+
The node representing the +fixed end of the current selection. The anchor is set by the selection +anchor operation.
+ +
current
+
The node where the mouse pointer is currently +located. But unlike active, this id changes while the selection is dragged. + It is used to determine the current node during button drags.
+ +
down
+
The +next open node from the current focus. The down of the last open node is +the same.
+ +
end
+
The last open node (in depth-first order) on the tree.
+ +
focus +
+
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 focus operation.
+ +
last
+
The +last open node from the current focus. But unlike up, when the focus is +at root, last wraps around to the last open node in the tree.
+ +
mark
+
The node +representing the non-fixed end of the current selection. The mark is set +by the selection mark operation.
+ +
next
+
The next open node from the current +focus. But unlike down, when the focus is on last open node, next wraps +around to the root node.
+ +
nextsibling
+
The next sibling from the node with +the current focus. If the node is already the last sibling then it is the +nextsibling.
+ +
parent
+
The parent of the node with the current focus. The parent +of the root is also the root.
+ +
prevsibling
+
The previous sibling from the +node with the current focus. If the node is already the first sibling then +it is the prevsibling.
+ +
root
+
The root node. You can also use id 0 to indicate +the root.
+ +
up
+
The last open node (in depth-first order) from the current focus. +The up of the root node (i.e. the root has focus) is also the root.
+ +
view.top +
+
First node that's current visible in the widget.
+ +
view.bottom
+
Last node that's +current visible in the widget.
+ +
path
+
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 -separator configuration option), but component +names must be separated by the designated separator.
+ +
@x,y
+
Indicates the +node that covers the point in the treeview window specified by x and y +(in pixel coordinates). If no part of the entryd covers that point, then +the closest node to that point is used.
+
+

+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. +

Data Fields

+A node +in the tree can have data fields. 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 treeview 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. +

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 bindtags. Bindtags are simply names that associate a binding +with one or more nodes. There is a built-in tag all that all node entries +automatically have. +

Treeview Operations

+The treeview operations are the invoked +by specifying the widget's pathname, the operation, and any arguments that +pertain to that operation. The general form is:

+
+pathName operation ?arg arg ...?
+

+

Operation and the args determine the exact behavior of the command. The +following operation are available for treeview widgets: +

+ +
pathName bbox ?-screen? +tagOrId...
+
Returns a list of 4 numbers, representing a bounding box of around +the specified entries. The entries is given by one or more tagOrId arguments. + If the -screen flag is given, then the x-y coordinates of the bounding +box are returned as screen coordinates, not virtual coordinates. Virtual +coordinates start from 0 from the root node. The returned list contains +the following values.
+ +
x
+
X-coordinate of the upper-left corner of the bounding +box.
+ +
y
+
Y-coordinate of the upper-left corner of the bounding box.
+ +
width
+
Width +of the bounding box.
+ +
height
+
Height of the bounding box.
+
+ + +
+ +
pathName bind tagName +?sequence command?
+
Associates command with tagName such that whenever the +event sequence given by sequence occurs for a node with this tag, command +will be invoked. The syntax is similar to the bind command except that +it operates on treeview entries, rather than widgets. See the bind manual +entry for complete details on sequence and the substitutions performed +on command before invoking it.

+If all arguments are specified then a +new binding is created, replacing any existing binding for the same sequence +and tagName. If the first character of command is + then command augments +an existing binding rather than replacing it. If no command argument is +provided then the command currently associated with tagName and sequence +(it's an error occurs if there's no such binding) is returned. If both command +and sequence are missing then a list of all the event sequences for which +bindings have been defined for tagName.

+ +
pathName button operation ?args? +
+
This command is used to control the button selectors within a treeview +widget. It has several forms, depending on operation:
+ +
pathName button +activate tagOrId
+
Designates the node given by tagOrId as active. When +a node is active it's entry is drawn using its active icon (see the -activeicon +option). Note that there can be only one active entry at a time. The special +id active indicates the currently active node.
+ +
pathName button bind tagName +?sequence command?
+
Associates command with tagName such that whenever the +event sequence given by sequence occurs for an button of a node entry with +this tag, command will be invoked. The syntax is similar to the bind command +except that it operates on treeview buttons, rather than widgets. See the +bind manual entry for complete details on sequence and the substitutions +performed on command before invoking it.

+If all arguments are specified +then a new binding is created, replacing any existing binding for the +same sequence and tagName. If the first character of command is + then command + augments an existing binding rather than replacing it. If no command argument +is provided then the command currently associated with tagName and sequence +(it's an error occurs if there's no such binding) is returned. If both command +and sequence are missing then a list of all the event sequences for which +bindings have been defined for tagName.

+ +
pathName button cget option
+
Returns +the current value of the configuration option given by option. Option may +have any of the values accepted by the configure operation described below. +
+ +
pathName button configure ?option? ?value option value ...?
+
Query or modify +the configuration options of the widget. If no option is specified, returns +a list describing all of the available options for pathName (see Tk_ConfigureInfo +for information on the format of this list). If option is specified with +no value, 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 option is specified). If one or more option-value 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. Option +and value are described in the section BUTTON OPTIONS + below.
+
+ + +
+ +
pathName +cget option
+
Returns the current value of the configuration option given +by option. Option may have any of the values accepted by the configure operation +described below.
+ +
pathName close ?-recurse? tagOrId...
+
Closes the node specified +by tagOrId. In addition, if a Tcl script was specified by the -closecommand +option, it is invoked. If the node is already closed, this command has +no effect. If the -recurse flag is present, each child node is recursively +closed.
+ +
pathName column operation ?args?
+
The following operations are available +for treeview columns.
+ +
pathName column activate column
+
Sets the active column +to column. Column is the name of a column in the widget. When a column is +active, it's drawn using its -activetitlebackground and -activetitleforeground +options. If column is the "", then no column will be active. If no column +argument is provided, then the name of the currently active column is returned. +
+ +
pathName column cget name option
+
Returns the current value of the column +configuration option given by option for name. Name is the name of column +that corresponds to a data field. Option may have any of the values accepted +by the configure operation described below.
+ +
pathName column configure name +?option? ?value option value ...?
+
Query or modify the configuration options +of the column designated by name. Name is the name of the column corresponding +to a data field. If no option is specified, returns a list describing all +of the available options for pathName (see Tk_ConfigureInfo for information +on the format of this list). If option is specified with no value, 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 option +is specified). If one or more option-value 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. Option and value are +described in the section COLUMN OPTIONS + below.
+ +
pathName column delete field +?field...?
+
Deletes one of more columns designated by field. Note that this +does not delete the data fields themselves.
+ +
pathName column insert position +field ?options...?
+
Inserts one of more columns designated by field. 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. Position indicates where in the +list of columns to add the new column. It may be either a number or end. +
+ +
pathName column invoke field
+
Invokes the Tcl command associated with the +column field, if there is one (using the column's -command option). The +command is ignored if the column's -state option set to disabled.
+ +
pathName +column move name dest
+
Moves the column name to the destination position. + Dest is the name of another column or a screen position in the form @x,y. +
+ +
pathName column names
+
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.
+ +
pathName +column nearest x ?y?
+
Returns the name of the column closest to the given +X-Y screen coordinate. If you provide a y argument (it's optional), a name +is returned only when if the point is over a column's title.
+
+ + +
+ +
pathName configure +?option? ?value option value ...?
+
Query or modify the configuration options +of the widget. If no option is specified, returns a list describing all +of the available options for pathName (see Tk_ConfigureInfo for information +on the format of this list). If option is specified with no value, 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 option +is specified). If one or more option-value 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. Option and value are +described in the section TREEVIEW OPTIONS + below.
+ +
pathName curselection +
+
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. +
+ +
pathName delete tagOrId...
+
Deletes one or more entries given by tagOrId and +its children.
+ +
pathName entry operation ?args?
+
The following operations are +available for treeview entries.
+ +
pathName entry activate tagOrId
+
Sets the +active entry to the one specified by tagOrId. When an entry is active +it is drawn using its active icon (see the -activeicon option). Note that +there can be only one active node at a time. The special id of the currently +active node is active.
+ +
pathName entry cget option
+
Returns the current value +of the configuration option given by option. Option may have any of the +values accepted by the configure operation described below.
+ +
pathName entry +children tagOrId ?first? ?last?
+
Returns a list of ids for the given range +of children of tagOrId. TagOrId is the id or tag of the node to be examined. +If only a first argument is present, then the id of the that child at +that numeric position is returned. If both first and last arguments are +given, then the ids of all the children in that range are returned. Otherwise +the ids of all children are returned.
+ +
pathName entry configure ?option? +?value option value ...?
+
Query or modify the configuration options of the +widget. If no option is specified, returns a list describing all of the +available options for pathName (see Tk_ConfigureInfo for information on +the format of this list). If option is specified with no value, 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 option +is specified). If one or more option-value 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. Option and value are +described below:
+ +
pathName entry delete tagOrId ?first ?last?
+
Deletes the +one or more children nodes of the parent tagOrId. If first and last arguments +are present, they are positions designating a range of children nodes to +be deleted.
+ +
pathName entry isbefore tagOrId1 tagOrId2
+
Returns 1 if tagOrId1 +is before tagOrId2 and 0 otherwise.
+ +
pathName entry ishidden tagOrId
+
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.
+ +
pathName entry isopen +tagOrId
+
Returns 1 if the node is currently open and 0 otherwise.
+ +
pathName +entry size -recurse tagOrId
+
Returns the number of children for parent node +tagOrId. If the -recurse flag is set, the number of all its descendants +is returned. The node itself is not counted.
+
+ + +
+ +
pathName find ?flags? first +last
+
Finds for all entries matching the criteria given by flags. A list +of ids for all matching nodes is returned. First and last are ids designating +the range of the search in depth-first order. If last is before first, then +nodes are searched in reverse order. The valid flags are:
+ +
-name pattern +
+
Specifies pattern to match against node names.
+ +
-full pattern
+
Specifies pattern +to match against node pathnames.
+ +
-option pattern
+
Specifies pattern to match +against the node entry's configuration option.
+ +
-exact
+
Patterns must match +exactly. The is the default.
+ +
-glob
+
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:
+ +
*
+
Matches any sequence of + characters in string, including a null string.
+ +
?
+
Matches any single character +in string.
+ +
[chars]
+
Matches any character in the set given by chars. If a +sequence of the form x-y appears in chars, then any character between x +and y, inclusive, will match.
+ +
\x
+
Matches the single character x. This +provides a way of avoiding the special interpretation of the characters +*?[]\ in the pattern.
+
+ + +
+ +
-regexp
+
Use regular expression pattern matching (i.e. +the same as implemented by the regexp command).
+ +
-nonmatching
+
Pick entries +that don't match.
+ +
-exec string
+
Specifies a Tcl script to be invoked for +each matching node. Percent substitutions are performed on string before + it is executed. The following substitutions are valid:
+ +
%W
+
The pathname +of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The full pathname of the node. +
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single percent.
+
+ + +
+ +
-count number
+
Stop +searching after number matches.
+ +
--
+
Indicates the end of flags.
+
+ + +
+ +
pathName focus + tagOrId
+
Sets the focus to the node given by tagOrId. When a node has focus, +it can receive keyboard events. The special id focus designates the node +that currently has focus.
+ +
pathName get ?-full? tagOrId tagOrId...
+
Translates +one or more ids to their node entry names. It returns a list of names +for all the ids specified. If the -full flag is set, then the full pathnames +are returned.
+ +
pathName hide ?flags? tagOrId...
+
Hides all nodes matching the +criteria given by flags. The search is performed recursively for each node +given by tagOrId. The valid flags are described below:
+ +
-name pattern
+
Specifies +pattern to match against node names.
+ +
-full pattern
+
Specifies pattern to match +against node pathnames.
+ +
-option pattern
+
Specifies pattern to match against +the node entry's configuration option.
+ +
-exact
+
Match patterns exactly. The +is the default.
+ +
-glob
+
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:
+ +
*
+
Matches any sequence of characters in string, +including a null string.
+ +
?
+
Matches any single character in string.
+ +
[chars] +
+
Matches any character in the set given by chars. If a sequence of the form +x-y appears in chars, then any character between x and y, inclusive, will +match.
+ +
\x
+
Matches the single character x. This provides a way of avoiding + the special interpretation of the characters *?[]\ in the pattern.
+
+ + +
+ +
-regexp +
+
Use regular expression pattern matching (i.e. the same as implemented by +the regexp command).
+ +
-nonmatching
+
Hide nodes that don't match.
+ +
--
+
Indicates +the end of flags.
+
+ + +
+ +
pathName index ?-at tagOrId? string
+
Returns the id of +the node specified by string. String may be a tag or node id. Some special +ids are normally relative to the node that has focus. The -at flag lets +you select another node.
+ +
pathName insert ?-at tagOrId? position path ?options...? +?path? ?options...?
+
Inserts one or more nodes at position. Position is the +location (number or end) where the new nodes are added to the parent node. + Path 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 -separator option). Pathnames +are normally absolute, but the -at switch lets you select a relative starting +point. Its value is the id of the starting node.

+All ancestors of the +new node must already exist, unless the -autocreate option is set. It is +also an error if a node already exists, unless the -allowduplicates option +is set.

+Option and value may have any of the values accepted by the entry +configure operation described in the ENTRY OPERATIONS + section below. This +command returns a list of the ids of the new entries.

+ +
pathName move tagOrId +how destId
+
Moves the node given by tagOrId to the destination node. The +node can not be an ancestor of the destination. DestId is the id of the +destination node and can not be the root of the tree. In conjunction with +how, it describes how the move is performed.
+ +
before
+
Moves the node before +the destination node.
+ +
after
+
Moves the node after the destination node.
+ +
into +
+
Moves the node to the end of the destination's list of children.
+
+ + +
+ +
pathName +nearest x y ?varName?
+
Returns the id of the node entry closest to the given +X-Y screen coordinate. The optional argument varName is the name of variable +which is set to either button or select to indicate over what part of the +node the coordinate lies. If the coordinate is not directly over any node, +then varName will contain the empty string.
+ +
pathName open ?-recurse? tagOrId... +
+
Opens the one or more nodes specified by tagOrId. If a node is not already +open, the Tcl script specified by the -opencommand option is invoked. If +the -recurse flag is present, then each descendant is recursively opened. +
+ +
pathName range ?-open? first last
+
Returns the ids in depth-first order +of the nodes between the first and last ids. If the -open flag is present, +it indicates to consider only open nodes. If last is before first, then +the ids are returned in reverse order.
+ +
pathName scan option args
+
This command +implements scanning. It has two forms, depending on option:
+ +
pathName scan +mark x y
+
Records x and y and the current view in the treeview window; +used in conjunction with later scan dragto commands. Typically this command +is associated with a mouse button press in the widget. It returns an empty +string.
+ +
pathName scan dragto x y.
+
Computes the difference between its x and +y arguments and the x and y arguments to the last scan mark 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.
+
+ + +
+ +
pathName see ?-anchor anchor? tagOrId +
+
Adjusts the view of entries so that the node given by tagOrId is visible +in the widget window. It is an error if tagOrId 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 -anchor flag. Its value is a Tk +anchor position.
+ +
pathName selection option arg
+
This command is used to adjust +the selection within a treeview widget. It has several forms, depending +on option:
+ +
pathName selection anchor tagOrId
+
Sets the selection anchor +to the node given by tagOrId. If tagOrId 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 anchor may be used to refer to the anchor node.
+ +
pathName selection cancel +
+
Clears the temporary selection of entries back to the current anchor. Temporary +selections are created by the selection mark operation.
+ +
pathName selection +clear first ?last?
+
Removes the entries between first and last (inclusive) +from the selection. Both first and last are ids representing a range of +entries. If last isn't given, then only first is deselected. Entries outside +the selection are not affected.
+ +
pathName selection clearall
+
Clears the entire +selection.
+ +
pathName selection mark tagOrId
+
Sets the selection mark to +the node given by tagOrId. 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 mark may be used to refer to the current + mark node. If tagOrId 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.
+ +
pathName selection includes tagOrId
+
Returns 1 if the +node given by tagOrId is currently selected, 0 if it isn't.
+ +
pathName selection +present
+
Returns 1 if any nodes are currently selected and 0 otherwise.
+ +
pathName +selection set first ?last?
+
Selects all of the nodes in the range between +first and last, inclusive, without affecting the selection state of nodes +outside that range.
+ +
pathName selection toggle first ?last?
+
Selects/deselects +nodes in the range between first and last, inclusive, from the selection. +If a node is currently selected, it becomes deselected, and visa versa. +
+
+ + +
+ +
pathName show ?flags? tagOrId...
+
Exposes all nodes matching the criteria given +by flags. This is the inverse of the hide operation. The search is performed +recursively for each node given by tagOrId. The valid flags are described +below:
+ +
-name pattern
+
Specifies pattern to match against node names.
+ +
-full +pattern
+
Specifies pattern to match against node pathnames.
+ +
-option pattern +
+
Specifies pattern to match against the entry's configuration option.
+ +
-exact +
+
Match patterns exactly. The is the default.
+ +
-glob
+
-glob 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:
+ +
*
+
Matches + any sequence of characters in string, including a null string.
+ +
?
+
Matches +any single character in string.
+ +
[chars]
+
Matches any character in the set +given by chars. If a sequence of the form x-y appears in chars, then any +character between x and y, inclusive, will match.
+ +
\x
+
Matches the single + character x. This provides a way of avoiding the special interpretation +of the characters *?[]\ in the pattern.
+
+ + +
+ +
-regexp
+
Use regular expression pattern +matching (i.e. the same as implemented by the regexp command).
+ +
-nonmatching +
+
Expose nodes that don't match.
+ +
--
+
Indicates the end of flags.
+
+ + +
+ +
pathName sort +?operation? args...
+
+ +
pathName sort auto ?boolean
+
Turns on/off automatic sorting +of node entries. If boolean is true, entries will be automatically sorted +as they are opened, closed, inserted, or deleted. If no boolean argument +is provided, the current state is returned.
+ +
pathName sort cget option
+
Returns +the current value of the configuration option given by option. Option may +have any of the values accepted by the configure operation described below. +
+ +
pathName sort configure ?option? ?value option value ...?
+
Query or modify +the sorting configuration options of the widget. If no option is specified, +returns a list describing all of the available options for pathName (see +Tk_ConfigureInfo for information on the format of this list). If option +is specified with no value, 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 option is specified). If one or more +option-value 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. Option and value are described below:
+ +
-column string
+
Specifies +the column to sort. Entries in the widget are rearranged according to this +column. If column is "" then no sort is performed.
+ +
-command string
+
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.
+ +
-decreasing boolean +
+
Indicates to sort in ascending/descending order. If boolean is true, then +the entries as in descending order. The default is no.
+ +
-mode string
+
Specifies +how to compare entries when sorting. String may be one of the following: +
+ +
ascii
+
Use string comparison based upon the ASCII collation order.
+ +
dictionary +
+
Use dictionary-style comparison. This is the same as ascii 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".
+ +
integer
+
Compares fields as integers.
+ +
real
+
Compares fields +as floating point numbers.
+ +
command
+
Use the Tcl proc specified by the -command +option to compare entries when sorting. If no command is specified, the +sort reverts to ascii sorting.
+
+ + + +
+ +
pathName sort once ?flags? tagOrId...
+
Sorts +the children for each entries specified by tagOrId. By default, entries +are sorted by name, but you can specify a Tcl proc to do your own comparisons. +
+ +
-recurse
+
Recursively sort the entire branch, not just the children.
+
+ + + +
+ +
pathName +tag operation args
+
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.

+Both operation and its arguments determine the exact behavior of +the command. The operations available for tags are listed below.

+ +
pathName +tag add string id...
+
Adds the tag string to one of more entries.
+ +
pathName tag +delete string id...
+
Deletes the tag string from one or more entries.
+ +
pathName +tag forget string
+
Removes the tag string from all entries. It's not an error +if no entries are tagged as string.
+ +
pathName tag names ?id?
+
Returns a list +of tags used. If an id argument is present, only those tags used by the +node designated by id are returned.
+ +
pathName tag nodes string
+
Returns a +list of ids that have the tag string. If no node is tagged as string, then +an empty string is returned.
+
+ + +
+ +
pathName text operation ?args?
+
This operation +is used to provide text editing for cells (data fields in a column) or +entry labels. It has several forms, depending on operation:
+ +
pathName text +apply
+
Applies the edited buffer, replacing the entry label or data field. +The edit window is hidden.
+ +
pathName text cancel
+
Cancels the editing operation, +reverting the entry label or data value back to the previous value. The +edit window is hidden.
+ +
pathName text cget value
+
Returns the current value +of the configuration option given by option. Option may have any of the +values accepted by the configure operation described below.
+ +
pathName text +configure ?option value?
+
Query or modify the configuration options of the +edit window. If no option is specified, returns a list describing all of +the available options (see Tk_ConfigureInfo for information on the format +of this list). If option is specified with no value, 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 option is specified). + If one or more option-value 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. Option and value are described in the section + TEXT EDITING OPTIONS + below.
+
+ + +
+ +
pathName text delete first last
+
Deletes the +characters in the edit buffer between the two given character positions. +
+ +
pathName text get ?-root? x y
+
+ +
pathName text icursor index
+
+ +
pathName text +index index
+
Returns the text index of given index.
+ +
pathName text insert +index string
+
Insert the text string string into the edit buffer at the +index index. For example, the index 0 will prepend the buffer.
+ +
pathName +text selection args
+
This operation controls the selection of the editing +window. Note that this differs from the selection of entries. It has the +following forms:
+ +
pathName text selection adjust index
+
Adjusts either the +first or last index of the selection.
+ +
pathName text selection clear
+
Clears +the selection.
+ +
pathName text selection from index
+
Sets the anchor of the +selection.
+ +
pathName text selection present
+
Indicates if a selection is present. +
+ +
pathName text selection range start end
+
Sets both the anchor and mark of +the selection.
+ +
pathName text selection to index
+
Sets the unanchored end +(mark) of the selection.
+
+ + +
+ +
pathName toggle tagOrId
+
Opens or closes the node +given by tagOrId. If the corresponding -opencommand or -closecommand option +is set, then that command is also invoked.
+ +
pathName xview args
+
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:
+ +
pathName +xview
+
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 treeview 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 -xscrollcommand option. +
+ +
pathName xview tagOrId
+
Adjusts the view in the window so that the character +position given by tagOrId is displayed at the left edge of the window. Character +positions are defined by the width of the character 0.
+ +
pathName xview moveto +fraction
+
Adjusts the view in the window so that fraction of the total width +of the treeview widget's text is off-screen to the left. fraction must be +a fraction between 0 and 1.
+ +
pathName xview scroll number what
+
This command +shifts the view in the window left or right according to number and what. +Number must be an integer. What must be either units or pages or an abbreviation +of one of these. If what is units, the view adjusts left or right by number +character units (the width of the 0 character) on the display; if it is +pages then the view adjusts by number screenfuls. If number is negative +then characters farther to the left become visible; if it is positive +then characters farther to the right become visible.
+
+ + +
+ +
pathName yview ?args? +
+
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:
+ +
pathName +yview
+
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 -yscrollcommand option.
+ +
pathName yview tagOrId
+
Adjusts the view in the +window so that the node given by tagOrId is displayed at the top of the +window.
+ +
pathName yview moveto fraction
+
Adjusts the view in the window so +that the node given by fraction appears at the top of the window. Fraction +is a fraction between 0 and 1; 0 indicates the first node, 0.33 indicates +the node one-third the way through the treeview widget, and so on.
+ +
pathName +yview scroll number what
+
This command adjusts the view in the window up +or down according to number and what. Number must be an integer. What must +be either units or pages. If what is units, the view adjusts up or down +by number lines; if it is pages then the view adjusts by number screenfuls. +If number is negative then earlier nodes become visible; if it is positive +then later nodes become visible.
+
+ + +

Treeview Options

+In addition to the configure +operation, widget configuration options may also be set by the Tk option +command. The class resource name is TreeView.
+option add *TreeView.Foreground white
+option add *TreeView.Background blue
+

The following widget options are available: +

+ +
-activebackground color
+
Sets +the background color for active entries. A node is active when the mouse +passes over it's entry or using the activate operation.
+ +
-activeforeground +color
+
Sets the foreground color of the active node. A node is active when +the mouse passes over it's entry or using the activate operation.
+ +
-activeicons +images
+
Specifies images to be displayed for an entry's icon when it is active. +Images is a list of two Tk images: the first image is displayed when the +node is open, the second when it is closed.
+ +
-autocreate boolean
+
If boolean +is true, automatically create missing ancestor nodes when inserting new +nodes. Otherwise flag an error. The default is no.
+ +
-allowduplicates boolean +
+
If boolean is true, allow nodes with duplicate pathnames when inserting +new nodes. Otherwise flag an error. The default is no.
+ +
-background color
+
Sets +the background color of the widget. The default is white.
+ +
-borderwidth pixels +
+
Sets the width of the 3-D border around the outside edge of the widget. +The -relief option determines if the border is to be drawn. The default +is 2.
+ +
-closecommand string
+
Specifies a Tcl script to be invoked when a node +is closed. You can overrider this for individual entries using the entry's +-closecommand option. The default is "". Percent substitutions are performed +on string before it is executed. The following substitutions are valid: +
+ +
%W
+
The pathname of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The full pathname +of the node.
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single percent.
+
+ + +
+ +
-cursor +cursor
+
Specifies the widget's cursor. The default cursor is "".
+ +
-dashes number +
+
Sets the dash style of the horizontal and vertical lines drawn connecting + entries. Number is the length in pixels of the dashes and gaps in the line. +If number is 0, solid lines will be drawn. The default is 1 (dotted).
+ +
-exportselection +boolean
+
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 STRING; the value of the +selection will be the label of the selected nodes, separated by newlines. + The default is no.
+ +
-flat boolean
+
Indicates whether to display the tree as +a flattened list. If boolean is true, then the hierarchy will be a list +of full paths for the nodes. This option also has affect on sorting. See +the SORT OPERATIONS + section for more information. The default is no.
+ +
-focusdashes +dashList
+
Sets the dash style of the outline rectangle drawn around the +entry label of the node that current has focus. Number is the length in +pixels of the dashes and gaps in the line. If number is 0, a solid line +will be drawn. The default is 1.
+ +
-focusforeground color
+
Sets the color of +the focus rectangle. The default is black.
+ +
-font fontName
+
Specifies the +font for entry labels. You can override this for individual entries with +the entry's -font configuration option. The default is *-Helvetica-Bold-R-Normal-*-12-120-*. +
+ +
-foreground color
+
Sets the text color of entry labels. You can override +this for individual entries with the entry's -foreground configuration option. + The default is black.
+ +
-height pixels
+
Specifies the requested height of +widget. The default is 400.
+ +
-hideroot boolean
+
If boolean is true, it indicates +that no entry for the root node should be displayed. The default is no. +
+ +
-highlightbackground color
+
Specifies the normal color of the traversal +highlight region when the widget does not have the input focus.
+ +
-highlightcolor +color
+
Specifies the color of the traversal highlight rectangle when the +widget has the input focus. The default is black.
+ +
-highlightthickness pixels +
+
Specifies the width of the highlight rectangle indicating when the widget +has input focus. The value may have any of the forms acceptable to Tk_GetPixels. + If the value is zero, no focus highlight will be displayed. The default +is 2.
+ +
-icons images
+
Specifies images for the entry's icon. Images is a list +of two Tk images: the first image is displayed when the node is open, +the second when it is closed.
+ +
-linecolor color
+
Sets the color of the connecting +lines drawn between entries. The default is black.
+ +
-linespacing pixels
+
Sets +the number of pixels spacing between entries. The default is 0.
+ +
-linewidth +pixels
+
Set the width of the lines drawn connecting entries. If pixels is +0, no vertical or horizontal lines are drawn. The default is 1.
+ +
-opencommand +string
+
Specifies a Tcl script to be invoked when a node is open. You can +override this for individual entries with the entry's -opencommand configuration +option. The default is "". Percent substitutions are performed on string +before it is executed. The following substitutions are valid:
+ +
%W
+
The pathname +of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The full pathname of the node. +
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single percent.
+
+ + +
+ +
-relief relief
+
Specifies +the 3-D effect for the widget. Relief specifies how the treeview widget +should appear relative to widget it is packed into; for example, raised +means the treeview widget should appear to protrude. The default is sunken. +
+ +
-scrollmode mode
+
Specifies the style of scrolling to be used. The following +styles are valid. This is the default is hierbox.
+ +
listbox
+
Like the listbox +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.
+ +
hierbox
+
Like the hierbox widget, the last entry can only +be viewed at the bottom of the widget window. The scrollbar stays a constant +size.
+ +
canvas
+
Like the canvas widget, the entries are bound within the +scrolling area.
+
+ + +
+ +
-selectbackground color
+
Sets the background color selected +node entries. The default is #ffffea.
+ +
-selectborderwidth pixels
+
Sets the width +of the raised 3-D border drawn around the labels of selected entries. The +default is 0. -selectcommand string Specifies a Tcl script to invoked when +the set of selected nodes changes. The default is "".
+ +
-selectforeground color +
+
Sets the color of the labels of selected node entries. The default is black. +
+ +
-selectmode mode
+
Specifies the selection mode. If mode is single, only one +node can be selected at a time. If multiple more than one node can be selected. +The default is single.
+ +
-separator string
+
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 string is the empty string, the pathnames are Tcl lists. + Each element is a path component. The default is "".
+ +
-showtitles boolean +
+
If boolean is false, column titles are not be displayed. The default is +yes.
+ +
-sortselection boolean
+
If boolean 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 no.
+ +
-takefocus focus
+
Provides +information used when moving the focus from window to window via keyboard +traversal (e.g., Tab and Shift-Tab). If focus is 0, this means that this window +should be skipped entirely during keyboard traversal. 1 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 "1".
+ +
-trim string
+
Specifies a string leading characters to +trim from entry pathnames before parsing. This only makes sense if the +-separator is also set. The default is "".
+ +
-width pixels
+
Sets the requested +width of the widget. If pixels is 0, then the with is computed from the +contents of the treeview widget. The default is 200.
+ +
-xscrollcommand string +
+
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.
+ +
-xscrollincrement pixels
+
Sets the horizontal scrolling distance. +The default is 20 pixels.
+ +
-yscrollcommand string
+
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.
+ +
-yscrollincrement pixels +
+
Sets the vertical scrolling distance. The default is 20 pixels.
+
+ +

Entry Options

+Many +widget configuration options have counterparts in entries. For example, +there is a -closecommand 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.

+There is no resource class or +name for entries. +

+ +
-activeicons images
+
Specifies images to be displayed as +the entry's icon when it is active. This overrides the global -activeicons +configuration option for the specific entry. Images is a list of two Tk +images: the first image is displayed when the node is open, the second +when it is closed.
+ +
-bindtags tagList
+
Specifies the binding tags for nodes. + TagList 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 all.
+ +
-button string
+
Indicates whether a button should be displayed +on the left side of the node entry. String can be yes, no, or auto. If +auto, then a button is automatically displayed if the node has children. + This is the default.
+ +
-closecommand string
+
Specifies a Tcl script to be invoked +when the node is closed. This overrides the global -closecommand option +for this entry. The default is "". Percent substitutions are performed on +string before it is executed. The following substitutions are valid:
+ +
%W +
+
The pathname of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The full pathname +of the node.
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single percent.
+
+ + +
+ +
-data +string
+
Sets data fields for the node. String is a list of name-value pairs +to be set. The default is "".
+ +
-font fontName
+
Sets the font for entry labels. + This overrides the widget's -font option for this node. The default is *-Helvetica-Bold-R-Normal-*-12-120-*. +
+ +
-foreground color
+
Sets the text color of the entry label. This overrides +the widget's -foreground configuration option. The default is "".
+ +
-icons images +
+
Specifies images to be displayed for the entry's icon. This overrides the +global -icons configuration option. Images is a list of two Tk images: the +first image is displayed when the node is open, the second when it is closed. +
+ +
-label string
+
Sets the text for the entry's label. If not set, this defaults +to the name of the node. The default is "".
+ +
-opencommand string
+
Specifies +a Tcl script to be invoked when the entry is opened. This overrides the +widget's -opencommand option for this node. The default is "". Percent substitutions +are performed on string before it is executed. The following substitutions +are valid:
+ +
%W
+
The pathname of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The +full pathname of the node.
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single +percent.
+
+ + +

Button Options

+Button configuration options may also be set by the +option command. The resource subclass is Button. The resource name is always +button.
+option add *TreeView.Button.Foreground white
+option add *TreeView.button.Background blue
+

The following are the configuration options available for buttons. +

+ +
-activebackground +color
+
Sets the background color of active buttons. A button is made active +when the mouse passes over it or by the button activate operation.
+ +
-activeforeground +color
+
Sets the foreground color of active buttons. A button is made active +when the mouse passes over it or by the button activate operation.
+ +
-background +color
+
Sets the background of the button. The default is white.
+ +
-borderwidth +pixels
+
Sets the width of the 3-D border around the button. The -relief option +determines if a border is to be drawn. The default is 1.
+ +
-closerelief relief +
+
Specifies the 3-D effect for the closed button. Relief indicates how the +button should appear relative to the widget; for example, raised means +the button should appear to protrude. The default is solid.
+ +
-cursor cursor +
+
Sets the widget's cursor. The default cursor is "".
+ +
-foreground color
+
Sets +the foreground color of buttons. The default is black.
+ +
-images images
+
Specifies +images to be displayed for the button. Images 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 images is the empty string, then a plus/minus gadget +is drawn. The default is "".
+ +
-openrelief relief
+
Specifies the 3-D effect of +the open button. Relief indicates how the button should appear relative +to the widget; for example, raised means the button should appear to protrude. + The default is flat.
+ +
-size pixels
+
Sets the requested size of the button. + The default is 0.
+
+ + +

Column Options

+Column configuration options may also +be set by the option command. The resource subclass is Column. The resource +name is the name of the column.
+option add *TreeView.Column.Foreground white
+option add *TreeView.treeView.Background blue
+

The following configuration options are available for columns. +

+ +
-background +color
+
Sets the background color of the column. This overrides the widget's +-background option. The default is white.
+ +
-borderwidth pixels
+
Sets the width +of the 3-D border of the column. The -relief option determines if a border +is to be drawn. The default is 0.
+ +
-edit boolean
+
Indicates if the column's +data fields can be edited. If boolean is false, the data fields in the +column may not be edited. The default is yes.
+ +
-foreground color
+
Specifies +the foreground color of the column. You can override this for individual +entries with the entry's -foreground option. The default is black.
+ +
-font fontName +
+
Sets the font for a column. You can override this for individual entries +with the entry's -font option. The default is *-Helvetica-Bold-R-Normal-*-12-120-*. +
+ +
-hide boolean
+
If boolean is true, the column is not displayed. The default +is yes.
+ +
-justify justify
+
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. Justify must be left, right, or center. + The default is left.
+ +
-pad pad
+
Specifies how much padding for the left and +right sides of the column. Pad is a list of one or two screen distances. + If pad has two elements, the left side of the column is padded by the +first distance and the right side by the second. If pad has just one distance, +both the left and right sides are padded evenly. The default is 2.
+ +
-relief +relief
+
Specifies the 3-D effect of the column. Relief specifies how the +column should appear relative to the widget; for example, raised means +the column should appear to protrude. The default is flat.
+ +
-state state
+
Sets +the state of the column. If state is disable then the column title can not +be activated nor invoked. The default is normal.
+ +
-text string
+
Sets the title +for the column. The default is "".
+ +
-titleforeground color
+
Sets the foreground +color of the column title. The default is black.
+ +
-titleshadow color
+
Sets +the color of the drop shadow of the column title. The default is "".
+ +
-width +pixels
+
Sets the requested width of the column. This overrides the computed +with of the column. If pixels is 0, the width is computed as from the contents +of the column. The default is 0.
+
+ + +

Text Editing Options

+Text edit window configuration +options may also be set by the option command. The resource class is TreeViewEditor. +The resource name is always edit.
+option add *TreeViewEditor.Foreground white
+option add *edit.Background blue
+

The following are the configuration options available for the text editing +window. +

+ +
-background color
+
Sets the background of the text edit window. The +default is white.
+ +
-borderwidth pixels
+
Sets the width of the 3-D border around +the edit window. The -relief option determines if a border is to be drawn. + The default is 1.
+ +
-exportselection boolean
+
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 STRING. The default is no.
+ +
-relief relief
+
Specifies +the 3-D effect of the edit window. Relief indicates how the background should +appear relative to the edit window; for example, raised means the background +should appear to protrude. The default is solid.
+ +
-selectbackground color +
+
Sets the background of the selected text in the edit window. The default +is white.
+ +
-selectborderwidth pixels
+
Sets the width of the 3-D border around +the selected text in the edit window. The -selectrelief option determines +if a border is to be drawn. The default is 1.
+ +
-selectforeground color
+
Sets +the foreground of the selected text in the edit window. The default is +white.
+ +
-selectrelief relief
+
Specifies the 3-D effect of the selected text +in the edit window. Relief indicates how the text should appear relative +to the edit window; for example, raised means the text should appear to +protrude. The default is flat.
+
+ + +

Default Bindings

+Tk automatically creates +class bindings for treeviews that give them Motif-like behavior. Much of +the behavior of a treeview widget is determined by its -selectmode option, +which selects one of two ways of dealing with the selection.

+If the selection +mode is single, only one node can be selected at a time. Clicking button +1 on an node selects it and deselects any other selected item.

+If the selection +mode is multiple, 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. +

    +
  1. In extended 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.
  2. In extended 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.
  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.
  4. Mouse button 2 may +be used for scanning. If it is pressed and dragged over the treeview widget, +the contents of the hierarchy drag at high speed in the direction the mouse +moves.
  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 browse or extended +then the new active entry is also selected and all other entries are deselected. +In extended mode the new active entry becomes the selection anchor.
  6. In extended +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.
  7. The Left and Right keys scroll +the treeview widget view left and right by the width of the character 0. +Control-Left and Control-Right scroll the treeview 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.
  8. The Prior and Next keys scroll the +treeview widget view up and down by one page (the height of the window). +
  9. The Home and End keys scroll the treeview widget horizontally to the left +and right edges, respectively.
  10. Control-Home sets the location cursor to the +the first entry, selects that entry, and deselects everything else in +the widget.
  11. Control-End sets the location cursor to the the last entry, +selects that entry, and deselects everything else in the widget.
  12. In extended +mode, Control-Shift-Home extends the selection to the first entry and Control-Shift-End +extends the selection to the last entry.
  13. In multiple mode, Control-Shift-Home +moves the location cursor to the first entry and Control-Shift-End moves +the location cursor to the last entry.
  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.
  15. In extended 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.
  16. In extended mode, the Escape +key cancels the most recent selection and restores all the entries in the +selected range to their previous selection state.
  17. Control-slash selects everything +in the widget, except in single and browse modes, in which case it selects +the active entry and deselects everything else.
  18. Control-backslash deselects +everything in the widget, except in browse mode where it has no effect. +
  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.
  20. +
+

+The behavior +of treeview widgets can be changed by defining new bindings for individual +widgets or by redefining the class bindings. +

Widget Bindings

+In addition +to the above behavior, the following additional behavior is defined by +the default widget class (TreeView) bindings. +
+ +
<ButtonPress-2>
+
Starts scanning. +
+ +
<B2-Motion>
+
Adjusts the scan.
+ +
<ButtonRelease-2>
+
Stops scanning.
+ +
<B1-Leave>
+
Starts auto-scrolling. +
+ +
<B1-Enter>
+
Starts auto-scrolling
+ +
<KeyPress-Up>
+
Moves the focus to the previous +entry.
+ +
<KeyPress-Down>
+
Moves the focus to the next entry.
+ +
<Shift-KeyPress-Up>
+
Moves +the focus to the previous sibling.
+ +
<Shift-KeyPress-Down>
+
Moves the focus to the +next sibling.
+ +
<KeyPress-Prior>
+
Moves the focus to first entry. Closed or hidden +entries are ignored.
+ +
<KeyPress-Next>
+
Move the focus to the last entry. Closed +or hidden entries are ignored.
+ +
<KeyPress-Left>
+
Closes the entry. It is not an +error if the entry has no children.
+ +
<KeyPress-Right>
+
Opens the entry, displaying +its children. It is not an error if the entry has no children.
+ +
<KeyPress-space>
+
In +"single" select mode this selects the entry. In "multiple" mode, it toggles +the entry (if it was previous selected, it is not deselected).
+ +
<KeyRelease-space>
+
Turns +off select mode.
+ +
<KeyPress-Return>
+
Sets the focus to the current entry.
+ +
<KeyRelease-Return>
+
Turns +off select mode.
+ +
<KeyPress>
+
Moves to the next entry whose label starts with +the letter typed.
+ +
<KeyPress-Home>
+
Moves the focus to first entry. Closed or +hidden entries are ignored.
+ +
<KeyPress-End>
+
Move the focus to the last entry. +Closed or hidden entries are ignored.
+ +
<KeyPress-F1>
+
Opens all entries.
+ +
<KeyPress-F2>
+
Closes +all entries (except root).
+
+ +

Button Bindings

+Buttons have bindings. There are +associated with the "all" bindtag (see the entry's -bindtag option). You +can use the bind operation to change them. +
+ +
<Enter>
+
Highlights the button of +the current entry.
+ +
<Leave>
+
Returns the button back to its normal state.
+ +
<ButtonRelease-1>
+
Adjust +the view so that the current entry is visible.
+
+ +

Entry Bindings

+Entries have +default bindings. There are associated with the "all" bindtag (see the +entry's -bindtag option). You can use the bind operation to modify them. +
+ +
<Enter>
+
Highlights +the current entry.
+ +
<Leave>
+
Returns the entry back to its normal state.
+ +
<ButtonPress-1>
+
Sets +the selection anchor the current entry.
+ +
<Double-ButtonPress-1>
+
Toggles the selection +of the current entry.
+ +
<B1-Motion>
+
For "multiple" mode only. Saves the current +location of the pointer for auto-scrolling. Resets the selection mark. +
+ +
<ButtonRelease-1>
+
For "multiple" mode only. Sets the selection anchor to the + current entry.
+ +
<Shift-ButtonPress-1>
+
For "multiple" mode only. Extends the selection. +
+ +
<Shift-Double-ButtonPress-1>
+
Place holder. Does nothing.
+ +
<Shift-B1-Motion>
+
Place holder. +Does nothing.
+ +
<Shift-ButtonRelease-1>
+
Stop auto-scrolling.
+ +
<Control-ButtonPress-1>
+
For +"multiple" mode only. Toggles and extends the selection.
+ +
<Control-Double-ButtonPress-1>
+
Place +holder. Does nothing.
+ +
<Control-B1-Motion>
+
Place holder. Does nothing.
+ +
<Control-ButtonRelease-1>
+
Stops +auto-scrolling.
+ +
<Control-Shift-ButtonPress-1>
+
???
+ +
<Control-Shift-Double-ButtonPress-1>
+
Place +holder. Does nothing.
+ +
<Control-Shift-B1-Motion>
+
Place holder. Does nothing.
+
+ +

Column +Bindings

+Columns have bindings too. They are associated with the column's +"all" bindtag (see the column -bindtag option). You can use the column bind +operation to change them. +
+ +
<Enter>
+
Highlights the current column title.
+ +
<Leave>
+
Returns +the column back to its normal state.
+ +
<ButtonRelease-1>
+
Invokes the command (see +the column's -command option) if one if specified.
+
+ +

Column Rule Bindings

+ +
+ +
<Enter>
+
Highlights +the current and activates the ruler.
+ +
<Leave>
+
Returns the column back to its +normal state. Deactivates the ruler.
+ +
<ButtonPress-1>
+
Sets the resize anchor for +the column.
+ +
<B1-Motion>
+
Sets the resize mark for the column.
+ +
<ButtonRelease-1>
+
Adjust +the size of the column, based upon the resize anchor and mark positions. +
+
+ +

Example

+The treeview command creates a new widget.
+treeview .h -bg white
+

A new Tcl command .h is also created. This command can be used to query +and modify the treeview widget. For example, to change the background +color of the table to "green", you use the new command and the widget's +configure operation.
+# Change the background color.
+.h configure -background "green"
+

By default, the treeview 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 -tree option +to specify the name of another tree.
+# View the tree "myTree".
+.h configure -tree "myTree"
+

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 0 (you can use also use the special +id root). The insert operation lets you insert one or more new entries into +the tree. The last argument is the node's pathname.
+# Create a new entry named "myEntry"
+set id [.h insert end "myEntry"]
+

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.
+# Prepend "fred".
+set id [.h insert 0 "fred"]
+

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 -label option.
+# Create a new node named "fred"
+set id [.h insert end "fred" -label "Fred Flintstone"]
+

The insert operation returns the id of the new node. You can also use the +index operation to get this information.
+# Get the id of "fred"
+.h index "fred"
+

To insert a node somewhere other than root, use the -at switch. It takes +the id of the node where the new child will be added.
+# Create a new node "barney" in "fred".
+.h insert -at $id end "barney"
+

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.
+# Create a new sub-entry of "fred"
+.h insert end "fred barney"
+

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 -autocreate configuration option to force +the creation of ancestor nodes.
+# Force the creation of ancestors.
+.h configure -autocreate yes
+.h insert end "fred barney wilma betty"
+

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 -separator 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. +
+.h configure -separator /
+.h insert end "/usr/local/tcl/bin"
+

If the path is prefixed by extraneous characters, you can automatically +trim it off using the -trim option. It removed the string from the path +before it is parsed.
+.h configure -trim C:/windows -separator /
+.h insert end "C:/window/system"
+

You can insert more than one entry at a time with the insert operation. + This can be much faster than looping over a list of names.
+# The slow way
+foreach f [glob $dir/*] {
+ .h insert end $f
+}
+# The fast way
+eval .h insert end [glob $dir/*]
+

In this case, the insert operation will return a list of ids of the new +entries.

+You can delete entries with the delete operation. It takes one +or more tags of ids as its argument. It deletes the entry and all its children. +
+.h delete $id
+

Entries have several configuration options. They control the appearance +of the entry's icon and label. We have already seen the -label option that +sets the entry's text label. The entry configure operation lets you set +or modify an entry's configuration options.
+.h entry configure $id -color red -font fixed
+

You can hide an entry and its children using the -hide option.
+.h entry configure $id -hide yes
+

More that one entry can be configured at once. All entries specified are +configured with the same options.
+.h entry configure $i1 $i2 $i3 $i4 -color brown
+

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 -icons option. It takes +a list of two image names: one to represent the open entry, another when +it is closed.
+set im1 [image create photo -file openfolder.gif]
+set im2 [image create photo -file closefolder.gif]
+.h entry configure $id -icons "$im1 $im2"
+

If -icons is set to the empty string, no icons are display.

+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 + or - symbol, but can be configured in a variety of ways using the button +configure operation. For example, the + and - symbols can be replaced with +Tk images.
+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
+

Entries can contain an arbitrary number of data fields. Data fields are +name-value pairs. Both the value and name are strings. The entry's -data option +lets you set data fields.
+.h entry configure $id -data {mode 0666 group users}
+

The -data takes a list of name-value pairs.

+You can display these data fields +as columns in the treeview widget. You can create and configure columns +with the column operation. For example, to add a new column to the widget, +use the column insert operation. The last argument is the name of the data +field that you want to display.
+.h column insert end "mode"
+

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 -text option. +
+.h column insert end "mode" -text "File Permissions"
+

Columns have several configuration options. The column configure operation +lets you query or modify column options.
+.h column configure "mode" -justify left
+

The -justify option says how the data is justified within in the column. + The -hide option indicates whether the column is displayed.
+.h column configure "mode" -hide yes
+

Entries can be selected by clicking on the mouse. Selected entries are +drawn using the colors specified by the -selectforeground and -selectbackground +configuration options. The selection itself is managed by the selection +operation.
+# Clear all selections
+.h selection clear 0 end
+# Select the root node
+.h selection set 0
+

The curselection operation returns a list of ids of all the selected entries. +
+set ids [.h curselection]
+

You can use the get operation to convert the ids to their pathnames.
+set names [eval .h get -full $ids]
+

If a treeview is exporting its selection (using the -exportselection option), +then it will observe the standard X11 protocols for handling the selection. + Treeview selections are available as type STRING; the value of the selection +will be the pathnames of the selected entries, separated by newlines.

+The +treeview supports two modes of selection: single and multiple. 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 +-selectmode option.
+.h configure -selectmode "multiple"
+

You can be notified when the list of selected entries changes. The widget's +-selectcommand specifies a Tcl procedure that is called whenever the selection +changes.
+proc SelectNotify { widget } {
+ set ids [$widget curselection]
+}
+.h configure -selectcommand "SelectNotify .h"
+

The widget supports the standard Tk scrolling and scanning operations. The +treeview can be both horizontally and vertically. You can attach scrollbars +to the treeview the same way as the listbox or canvas widgets.
+scrollbar .xbar -orient horizontal -command ".h xview"
+scrollbar .ybar -orient vertical -command ".h yview"
+.h configure -xscrollcommand ".xbar set" \
+ -yscrollcommand ".ybar set"
+

There are three different modes of scrolling: listbox, canvas, and hierbox. + In listbox mode, the last entry can always be scrolled to the top of the +widget. In hierbox mode, the last entry is always drawn at the bottom of +the widget. The scroll mode is set by the widget's -selectmode option.
+.h configure -scrollmode "listbox"
+

Entries can be programmatically opened or closed using the open and close +operations respectively.
+.h open $id
+.h close $id
+

When an entry is opened, a Tcl procedure can be automatically invoked. The +-opencommand option specifies this procedure. This procedure can lazily +insert entries as needed.
+proc AddEntries { dir } {
+ eval .h insert end [glob -nocomplain $dir/*]
+}
+.h configure -opencommand "AddEntries %P"
+

Now when an entry is opened, the procedure AddEntries is called and adds +children to the entry. Before the command is invoked, special "%" substitutions +(like bind) are performed. Above, %P is translated to the pathname of the +entry.

+The same feature exists when an entry is closed. The -closecommand +option specifies the procedure.
+proc DeleteEntries { id } {
+ .h entry delete $id 0 end
+}
+.h configure -closecommand "DeleteEntries %#"
+

When an entry is closed, the procedure DeleteEntries is called and deletes +the entry's children using the entry delete operation (%# is the id of entry). + +

Keywords

+treeview, widget

+ +


+Table of Contents

+

+ 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 @@ + + + + + +htext(n) manual page + + +Table of Contents

+ +

Name

+htext - Create and manipulate hypertext widgets + +

Synopsis

+htext pathName ?option value?... +

Description

+

+The htext command creates +a new window (given by the pathName argument) and makes it into a htext +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 pathName, but pathName's parent must exist. The +htext command returns its pathName.

+The htext 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 htext 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.

+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 htext append command. All +widgets must be children of the htext 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.

+Before a file or text string is parsed +by the htext 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.

+Setting the either the -filename or -text 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 -filename option, +the value of the -text option is reset to the empty string. Likewise, when +the -text option is set, the string representing the -filename option is +cleared. +

File Format

+The format of htext 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 -specialchar option. In the following example of +a htext file, a button widget is appended to the text between the words +"a" and "which". The pathName of the htext widget is ".ht".
+This 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.
+

+ +

Indices

+

+Some of the widget operations (selection, gotoline, search, 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.

+An index must have one of the following forms: + +

+ +
line.char
+
Indicates char'th character on line line. Both lines and characters +are number from 0, so "0.0" is the first beginning of the text. Char may +be undesignated. In this case a character position of 0 is assumed.
+ +
@x,y +
+
Indicates the character that covers the pixel whose x and y coordinates +within the text's window are x and y.
+ +
end
+
Indicates the end of the text.
+ +
anchor +
+
Indicates the anchor point for the selection, which is set with the selection +operation.
+ +
sel.first
+
Indicates the first character in the selection. It is +an error to use this form if the selection isn't in the entry window.
+ +
sel.last +
+
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.
+
+ +

Variables

+

+The +following global Tcl variables are maintained when an htext file is parsed. + +

+ +
htext(widget)
+
is the pathname of the htext widget.
+ +
htext(file)
+
is the +name of the file the htext widget is currently parsing. It is the empty +string when the -text option is used.
+ +
htext(line) +
+
is the current line number +in the text.
+
+

+This information might be used to construct hyper links +between different files and/or lines.

+ +

Syntax

+The htext command creates a +new Tcl command whose name is pathName. This command may be used to invoke +various operations on the widget. It has the following general form:
+

+pathName oper ?args?
+

Oper and args determine the exact behavior of the command.

+ +

Operations

+The +following operations are available for htext widgets: +
+ +
pathName append window +?option value?...
+
Embeds the widget window into the htext widget. Window +is the pathname of the widget to be embedded which must be a child of pathName. + Window will be positioned in the htext widget at the current location +of the text. If option and value pairs are present, they configure various +aspects how window appears in pathName. The following options are available. +
+ +
-anchor anchorPos
+
Specifies how window will be arranged if there is any +extra space in the cavity surrounding the window. For example, if anchorPos +is center then the window is centered in the cavity; if anchorPos is w +then the window will be drawn such it touches the leftmost edge of the +cavity. The default is center.
+ +
-fill style
+
Specifies how the window should +be stretched to occupy the extra space in the cavity surrounding it (if +any exists). Style is none, x, y, both. If style is x, the width of window +is expanded to fill the cavity. If style is y, the height is expanded. +The default is none.
+ +
-height pixels
+
Sets the height of the cavity surrounding +window. If pixels is zero, the height of the cavity will be the same as +the requested height of window. If pixels is less than the requested height +of window, window will be reduced to fit the cavity. The default is 0.
+ +
-ipadx +pad
+
Sets the amount of internal padding to be added to the width window. + Pad can be a list of one or two numbers. If pad has two elements, the +left side of window is extended by the first value and the right side by +the second value. If pad is just one value, both the left and right sides +are padded by evenly by the value. The default is 0.
+ +
-ipady pad
+
Sets an amount +of internal padding to be added to the height of window. Pad can be a list +of one or two numbers. If pad has two elements, the top of window is padded +by the first value and the bottom by the second value. If pad is just one +number, both the top and bottom are padded evenly by the value. The default +is 0.
+ +
-justify justify
+
Justifies window vertically within the cavity containing +it in relation to the line of text. Justify is top, bottom, or center. + If justify is center the widget is centered along the baseline of the +line of text. The default is center.
+ +
-padx pad
+
Sets the padding on the left +and right sides of window. Pad can be a list of one or two numbers. If pad +has two elements, the left side of window is padded by the first value +and the right side by the second value. If pad has just one value, both +the left and right sides are padded evenly by the value. The default is +0.
+ +
-pady pad
+
Sets the padding above and below window. Pad can be a list of +one or two numbers. If pad has two elements, the area above window is padded +by the first value and the area below by the second value. If pad is just +one number, both the top and bottom are padded by the value. The default +is 0.
+ +
-relheight value
+
Specifies the height of the cavity containing window +relative to the height of pathName. Value is real number indicating the +ratio of the height of the cavity to the height of pathName. As the height +of pathName changes, so will the height of window. If value is 0.0 or less, +the height of the cavity is the requested height window. The default is +0.0.
+ +
-relwidth value
+
Specifies the width of the cavity containing window relative +to the width of pathName. Value is real number indicating the ratio of +the width of the cavity to the width of IpathName. As the height of pathName +changes, so will the height of window. If value is 0.0 or less, the width +of the cavity is the requested width of window. The default is 0.0.
+ +
-width +value
+
Species the width of the cavity containing the child window. Value +must be in a form accepted by Tk_GetPixels. If value 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.
+
+ + +
+ +
pathName +configure ?window? ?option? ?value option value ...?
+
Queries or modifies the +configuration options of the text widget or one of its embedded widgets. + If no window argument is present, the htext widget itself is configured. + Otherwise window is the pathname of a widget already embedded into the +htext widget. Then this command configure the options for the embedded widget. +
+
+

+If option isn't specified, a list describing all of the current options +for pathName or window is returned. If option is specified, but not value, +then a list describing the option option is returned. If one or more option +and value pairs are specified, then for each pair, the htext or embedded + window option option is set to value.

+The following options are valid for +the htext widget.

+
+ +
-background color
+
Sets the background of the htext widget +to color. This default is white.
+ +
-cursor cursor
+
Specifies the cursor for +the htext widget. The default cursor is pencil.
+ +
-filename fileName
+
Specifies +a htext file to be displayed in the window. If the value is the empty string, +the -text option is used instead. See the section FILE + for a description +of the htext file format.
+ +
-font fontName
+
Sets the font of the text in the +htext widget to fontName. The default is *-Helvetica-Bold-R-Normal-*-12-120-*.
+ +
-foreground +color
+
Sets the foreground of the htext widget to color. This is the color +of the text. This default is black.
+ +
-height pixels
+
Specifies the height of +the htext widget window.
+ +
-linespacing pixels
+
Specifies the spacing between +each line of text. The value must be in a form accepted by Tk_GetPixels. +The default value is 1 pixel.
+ +
-specialchar number
+
Specifies the ASCII value +of the special double character delimiters. In htext files, the text between +these special characters is evaluated as a block of Tcl commands. The default +special character is the 0x25 (percent sign).
+ +
-text text
+
Specifies the +text to be displayed in the htext widget. Text can be any valid string +of characters. See FILE FORMAT + for a description.
+ +
-xscrollcommand string +
+
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 +string 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.
+ +
-yscrollcommand string
+
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 string 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.
+ +
-width pixels
+
Specifies the desired width +of the viewport window. If the pixels is less than one, the window will +grow to accommodate the widest line of text.
+ +
-xscrollunits pixels
+
Specifies +the horizontal scrolling distance. The default is 10 pixels.
+ +
-yscrollunits +pixels
+
Specifies the vertical scrolling distance. The default is 10 pixels. +
+
+
+ +
+ +
pathName gotoline ?index?
+
Sets the top line of the text to index. Index +must be a valid text index (the character offset is ignored). If an index +isn't provided, the current line number is returned.
+ +
pathName scan mark +position
+
Records position and the current view in the text window; used +in conjunction with later scan dragto commands. Position must be in the +form "@x,y, where x and y are window coordinates. Typically this command +is associated with a mouse button press in the widget. It returns an empty +string.
+ +
pathName scan dragto position
+
Computes the difference between position +and the position registered in the last scan mark 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. Position +must be in the form "@x,y, where x and y are window coordinates. The command +returns an empty string.
+ +
pathName search pattern ?from? ?to?
+
Returns the +number of the next line matching pattern. Pattern is a string which obeys +the matching rules of Tcl_StringMatch. From and to are text line numbers +(inclusive) which bound the search. If no match for pattern can be found, +-1 is returned.
+ +
pathName xview ?position?
+
Moves the viewport horizontally +to the new text x-coordinate position. Position is the offset from the +left side of the text to the current position and must be in a form accepted +by Tk_GetPixels. If position is not present, the current text position is +returned.
+ +
pathName yview ?position?
+
Moves the viewport vertically to the +new text y-coordinate position. Position is the offset from the top of +the text to the current position and must be in a form accepted by Tk_GetPixels. +If position is not present, the current text position is returned.
+
+ +

Bugs

+Text +with embedded tabs can be obscured by child windows when scrolled horizontally. + +

Keywords

+hypertext, widget

+ +


+Table of Contents

+

+ 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 @@ + + + + + +spline(n) manual page + + +Table of Contents

+ +

Name

+spline - Fit curves with spline interpolation + +

Synopsis

+

+spline natural x y sx sy

+spline quadratic x y sx sy +

Description

+The +spline 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. +

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. +

+A spline is a device used in drafting to produce smoothed curves. The points +of the curve, known as knots, are fixed and the spline, 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.

+The spline 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. +

Example

+Before we can use the spline 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. +
+# 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)]
+}
+

We now have two vectors x and y representing the ten data points we're trying +to fit. The order of the values of x must be monotonically increasing. +We can use the vector's sort operation to sort the vectors.
+x sort y
+

The components of x are sorted in increasing order. The components of y +are rearranged so that the original x,y coordinate pairings are retained. +

+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.

+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 x. It will contain the same coordinates as x, but also +the abscissas of the new points we want interpolated. A simple way is to +use the vector's populate operation.
+x populate sx 10
+

This creates a new vector sx. It contains the abscissas of x, but in addition +sx will have ten evenly distributed values between each abscissa. You can +interpolate any points you wish, simply by setting the vector values.

+Finally, +we generate the ordinates (the images of the spline) using the spline command. + The ordinates are stored in a fourth vector.
+spline natural x y sx sy
+

This creates a new vector sy. It will have the same length as sx. The vectors +sx and sy represent the smoothed curve which we can now plot.
+graph .graph
+.graph element create original -x x -y x -color blue
+.graph element create spline -x sx -y sy -color red
+table . .graph
+

The natural operation employs a cubic interpolant when forming the spline. + In terms of the draftmen's spline, a natural spline 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.

+Alternatively, you can generate a spline +using the quadratic operation. Quadratic interpolation produces a spline +which follows the line segments of the data points much more closely. +
+spline quadratic x y sx sy
+ +

Operations

+ +
+ +
spline natural x y sx sy
+
Computes a cubic spline from the data +points represented by the vectors x and y and interpolates new points using +vector sx as the x-coordinates. The resulting y-coordinates are written to +a new vector sy. The vectors x and y must be the same length and contain +at least three components. The order of the components of x must be monotonically +increasing. Sx is the vector containing the x-coordinates of the points to +be interpolated. No component of sx can be less than first component of +x or greater than the last component. The order of the components of sx +must be monotonically increasing. Sy is the name of the vector where the +calculated y-coordinates will be stored. If sy does not already exist, a +new vector will be created.
+ +
spline quadratic x y sx sy
+
Computes a quadratic +spline from the data points represented by the vectors x and y and interpolates +new points using vector sx as the x-coordinates. The resulting y-coordinates +are written to a new vector sy. The vectors x and y must be the same length +and contain at least three components. The order of the components of x +must be monotonically increasing. Sx is the vector containing the x-coordinates +of the points to be interpolated. No component of sx can be less than first +component of x or greater than the last component. The order of the components +of sx must be monotonically increasing. Sy is the name of the vector where +the calculated y-coordinates are stored. If sy does not already exist, a +new vector will be created.
+
+ +

References

+
+
+Numerical Analysis
+by R. Burden, J. Faires and A. Reynolds.    
+Prindle, Weber & Schmidt, 1981, pp. 112
+
+Shape Preserving Quadratic Splines 
+by D.F.Mcallister & J.A.Roulier
+Coded by S.L.Dodd & M.Roulier N.C.State University.
+
+
The original code for the quadratric spline can be found in TOMS #574. +

Keywords

+spline, +vector, graph

+

+ +


+Table of Contents

+

+ 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 @@ + + + + + +stripchart(n) manual page + + +Table of Contents

+ +

Name

+stripchart - 2D strip chart for plotting x and +y coordinate data. +

Synopsis

+stripchart pathName ?option value?... +

Description

+The +stripchart 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.

+The stripchart is essentially the +same as the graph widget. It works almost exactly the very same way.

+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 -shiftby +and -autorange control this. You can specify different line styles for data +points (see the -styles option). +

Introduction

+The stripchart 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 plotting area. 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.

+A strip chart is composed of several +components: coordinate axes, data elements, legend, grid, cross hairs, +pens, postscript, and annotation markers. +

+ +
axis
+
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.
+ +
crosshairs
+
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.
+ +
element
+
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.
+ +
grid
+
Extends the +major and minor ticks of the X-axis and/or Y-axis across the plotting area. +
+ +
legend
+
The legend displays the name and symbol of each data element. +The legend can be drawn in any margin or in the plotting area.
+ +
marker
+
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. +
+ +
pen
+
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 -weight +and -style options).
+ +
postscript
+
The widget can generate encapsulated PostScript +output. This component has several options to configure how the PostScript +is generated.
+
+ +

Syntax

+
+

+stripchart pathName ?option value?...
+

The stripchart command creates a new window pathName and makes it into +a stripchart widget. At the time this command is invoked, there must not +exist a window named pathName, but pathName'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 configure operation below for the exact details as to what option and +value pairs are valid.

+If successful, stripchart 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:
+

+pathName operation ?arg?...
+

Both operation and its arguments determine the exact behavior of the command. + The operations available for the strip chart are described in the STRIPCHART +OPERATIONS + section.

+The command can also be used to access components of +the strip chart.
+

+pathName component operation ?arg?...
+

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. +

Example

+The stripchart command creates a new strip chart.
+# Create a new strip chart. Plotting area is black.
+stripchart .s -plotbackground black
+

A new Tcl command .s 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 configure operation. +
+# Change the title.
+.s configure -title "My Plot"
+

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 element component.
+# 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 }
+

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.
+# 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
+

The advantage of using vectors is that when you modify one, the graph is +automatically redrawn to display the new values.
+# Change the X-Y coordinates of the first point.
+set xVec(0) 0.18
+set yVec(0) 25.18
+

An element named line1 is now created in .s. By default, the element's label +in the legend will be also line1. You can change the label, or specify no +legend entry, again using the element's configure operation.
+# Don't display "line1" in the legend.
+.s element configure line1 -label ""
+

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.
+.s element configure line1 -symbol square -color red \
+    -dashes { 2 4 2 } -linewidth 2 -pixels 2c
+

Four coordinate axes are automatically created: x, x2, y, and y2. And by +default, elements are mapped onto the axes x and y. This can be changed +with the -mapx and -mapy options.
+# Map "line1" on the alternate Y-axis "y2".
+.s element configure line1 -mapy y2
+

Axes can be configured in many ways too. For example, you change the scale +of the Y-axis from linear to log using the axis operation.
+# Y-axis is log scale.
+.s axis configure y -logscale yes
+

Axis limits are reset by simply specifying new axis limits using the -min +and -max configuration options.
+.s axis configure x -min 1.0 -max 1.5
+.s axis configure y -min 12.0 -max 55.15
+

By default, the limits of the axis are determined from data values. To reset +back to the default limits, set the -min and -max options to the empty value. +
+# Reset the axes to autoscale again.
+.s axis configure x -min {} -max {}
+.s axis configure y -min {} -max {}
+

It's common with strip charts to automatically maintain a view of the most +recent time points. You can do this my setting the -autorange option.
+.s axis configure x -autorange 20.0
+

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.

+Sometimes the rate of data is so high that changing the axis limits +with each additional time point is prohibitive. You can use the -shiftby +option to define an increment to shift the view when needed.
+.s axis configure x -shiftby 15.0
+

When the view is shifted, it will allow a range of 15 new time points to +be added until the axis limits are recomputed.

+By default, the legend is +displayed in the right margin. You can change this or any other legend +configuration options using the legend component.
+# Configure the legend font, color, and relief
+.s legend configure -position left -relief raised \
+    -font fixed -fg blue
+

To prevent the legend from being displayed, turn on the -hide option.
+# Don't display the legend.
+.s legend configure -hide yes
+

The stripchart 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 marker +operation.
+# 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
+

This creates a text marker named first_marker. It will display the text +"start" near the coordinates of the first data point. The -anchor, -xoffset, +and -yoffset 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 -under option.
+# Draw the label before elements are drawn.
+.s marker configure first_marker -under yes
+

You can add cross hairs or grid lines using the crosshairs and grid operations. +
+# Display both cross hairs and grid lines.
+.s crosshairs configure -hide no -color red
+.s grid configure -hide no -dashes { 2 2 }
+

Finally, to get hardcopy of the strip chart, use the postscript operation. +
+# Print the strip chart into file "file.ps"
+.s postscript output file.ps -maxpect yes -decorations no
+

This generates a file file.ps containing the encapsulated PostScript of +the strip chart. The option -maxpect says to scale the plot to the size +of the page. Turning off the -decorations 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). +

Stripchart Operations

+ +
+ +
pathName +axis operation ?arg?...
+
See the AXIS COMPONENTS + section.
+ +
pathName bar elemName +?option value?...
+
Creates a new barchart element elemName. It's an error if +an element elemName already exists. See the manual for barchart for details +about what option and value pairs are valid.
+ +
pathName cget option
+
Returns +the current value of the stripchart configuration option given by option. + Option may be any option described below for the configure operation.
+ +
pathName +configure ?option value?...
+
Queries or modifies the configuration options +of the strip chart. If option isn't specified, a list describing all of +the current options for pathName is returned. If option is specified, but +not value, then a list describing option is returned. If one or more option +and value pairs are specified, then for each pair, the stripchart option +option is set to value. The following options are valid for the stripchart. +
+ +
-background color
+
Sets the background color. This includes the margins and +legend, but not the plotting area.
+ +
-borderwidth pixels
+
Sets the width of +the 3-D border around the outside edge of the widget. The -relief option +determines if the border is to be drawn. The default is 2.
+ +
-bottommargin +pixels
+
Specifies the size of the margin below the X-coordinate axis. If +pixels is 0, the size of the margin is selected automatically. The default +is 0.
+ +
-bufferelements boolean
+
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 boolean is true, data elements +are drawn to an internal pixmap. The option should be turned off if the +plot is updated frequently. See the SPEED TIPS + section. The default is 1. +
+ +
-buffergraph boolean
+
Indicates whether to draw the graph into a pixmap first. +If boolean 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 -bufferelements off. See the SPEED TIPS + section. The default +is 1.
+ +
-cursor cursor
+
Specifies the widget's cursor. The default cursor is +crosshair.
+ +
-font fontName
+
Specifies the title font. The default is *-Helvetica-Bold-R-Normal-*-18-180-*. +
+ +
-halo pixels
+
Specifies a maximum distance to consider when searching for +the closest data point (see the element's closest operation below). Data +points further than pixels away are ignored. The default is 0.5i.
+ +
-height +pixels
+
Specifies the requested height of widget. The default is 4i.
+ +
-invertxy +boolean
+
Indicates whether the placement X-axis and Y-axis should be inverted. + If boolean is true, the X and Y axes are swapped. The default is 0.
+ +
-justify +justify
+
Specifies how the title should be justified. This matters only +when the title contains more than one line of text. Justify must be left, +right, or center. The default is center.
+ +
-leftmargin pixels
+
Sets the size +of the margin from the left edge of the window to the Y-coordinate axis. + If pixels is 0, the size is calculated automatically. The default is 0. +
+ +
-plotbackground color
+
Specifies the background color of the plotting area. + The default is white.
+ +
-plotborderwidth pixels
+
Sets the width of the 3-D border +around the plotting area. The -plotrelief option determines if a border +is drawn. The default is 2.
+ +
-plotpadx pad
+
Sets the amount of padding to be +added to the left and right sides of the plotting area. Pad can be a list +of one or two screen distances. If pad 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 pad is just one distance, both the left and right +sides are padded evenly. The default is 8.
+ +
-plotpady pad
+
Sets the amount +of padding to be added to the top and bottom of the plotting area. Pad +can be a list of one or two screen distances. If pad has two elements, +the top of the plotting area is padded by the first distance and the bottom +by the second. If pad is just one distance, both the top and bottom are +padded evenly. The default is 8.
+ +
-plotrelief relief
+
Specifies the 3-D effect +for the plotting area. Relief indicates how the interior of the plotting +area should appear relative to rest of the strip chart; for example, raised +means the plot should appear to protrude from the strip chart, relative +to the surface of the strip chart. The default is sunken.
+ +
-relief relief +
+
Specifies the 3-D effect for the widget. Relief indicates how the strip +chart should appear relative to widget it is packed into; for example, +raised means the strip chart should appear to protrude. The default is +flat.
+ +
-rightmargin pixels
+
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 pixels is than 1, the margin size is selected automatically. +
+ +
-takefocus focus
+
Provides information used when moving the focus from window +to window via keyboard traversal (e.g., Tab and Shift-Tab). If focus is 0, +this means that this window should be skipped entirely during keyboard +traversal. 1 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 "".
+ +
-tile image
+
Specifies +a tiled background. If image isn't "", the background is tiled using image. +Otherwise, the normal background color is drawn (see the -background option). + Image must be an image created using the Tk image command. The default +is "".
+ +
-title text
+
Sets the title to text. If text is "", no title will be +displayed.
+ +
-topmargin pixels
+
Specifies the size of the margin above the x2 +axis. If pixels is 0, the margin size is calculated automatically.
+ +
-width +pixels
+
Specifies the requested width of the widget. The default is 5i.
+
+ + +
+ +
pathName +crosshairs operation ?arg?
+
See the CROSSHAIRS COMPONENT + section.
+ +
pathName +element operation ?arg?...
+
See the ELEMENT COMPONENTS + section.
+ +
pathName extents +item
+
Returns the size of a particular item in the strip chart. Item must +be either leftmargin, rightmargin, topmargin, bottommargin, plotwidth, +or plotheight.
+ +
pathName grid operation ?arg?...
+
See the GRID COMPONENT + section. +
+ +
pathName invtransform winX winY
+
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.
+ +
pathName +legend operation ?arg?...
+
See the LEGEND COMPONENT + section.
+ +
pathName line +elemName ?option value?...
+
The operation is the same as element.
+ +
pathName marker +operation ?arg?...
+
See the MARKER COMPONENTS + section.
+ +
pathName metafile ?fileName? +
+
This operation is for Window platforms only. Creates a Windows enhanced +metafile of the stripchart. If present, fileName is the file name of the +new metafile. Otherwise, the metafile is automatically added to the clipboard. +
+ +
pathName postscript operation ?arg?...
+
See the POSTSCRIPT COMPONENT + section. +
+ +
pathName snap photoName
+
Takes a snapshot of the strip chart and stores +the contents in the photo image photoName. PhotoName is the name of a Tk +photo image that must already exist.
+ +
pathName transform x y
+
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.
+ +
pathName xaxis operation ?arg?...
+
+ +
pathName x2axis operation ?arg?... +
+
+ +
pathName yaxis operation ?arg?...
+
+ +
pathName y2axis operation ?arg?...
+
See the + AXIS COMPONENTS + section.
+
+ +

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. +

Axis Components

+Four coordinate +axes are automatically created: two X-coordinate axes (x and x2) and two +Y-coordinate axes (y, and y2). By default, the axis x is located in the +bottom margin, y in the left margin, x2 in the top margin, and y2 in the +right margin.

+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.

+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.

+You +can create and use several axes. To create an axis, invoke the axis component +and its create operation.
+# Create a new axis called "temperature"
+.s axis create temperature
+

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.
+# Now map the temperature data to this axis.
+.s element create "temp" -xdata $x -ydata $tempData \
+ -mapy temperature
+

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.

+To display a different axis, you +invoke one of the following components: xaxis, yaxis, x2axis, and y2axis. +The use operation designates the axis to be drawn in the corresponding +margin: xaxis in the bottom, yaxis in the left, x2axis in the top, and +y2axis in the right.
+# Display the axis temperature in the left margin.
+.s yaxis use temperature
+

+

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.

+ +

+ +
pathName axis cget axisName +option
+
Returns the current value of the option given by option for axisName. + Option may be any option described below for the axis configure operation. +
+ +
pathName axis configure axisName ?option value?...
+
Queries or modifies the +configuration options of axisName. If option isn't specified, a list describing +all the current options for axisName is returned. If option is specified, +but not value, then a list describing option is returned. If one or more +option and value pairs are specified, then for each pair, the axis option +option is set to value. The following options are valid for axes.
+ +
-autorange +range
+
Sets the range of values for the axis to range. The axis limits +are automatically reset to display the most recent data points in this +range. If range is 0.0, the range is determined from the limits of the +data. If -min or -max are specified, they override this option. The default +is 0.0.
+ +
-color color
+
Sets the color of the axis and tick labels. The default +is black.
+ +
-command prefix
+
Specifies a Tcl command to be invoked when formatting +the axis tick labels. Prefix 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 "" is returned, +no label will appear next to the tick. You can get the standard tick labels +again by setting prefix to "". The default is "".

+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.

+ +
-descending boolean
+
Indicates whether the values along the axis +are monotonically increasing or decreasing. If boolean is true, the axis +values will be decreasing. The default is 0.
+ +
-hide boolean
+
Indicates whether +the axis is displayed.
+ +
-justify justify
+
Specifies how the axis title should +be justified. This matters only when the axis title contains more than +one line of text. Justify must be left, right, or center. The default is +center.
+ +
-limits formatStr
+
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. FormatStr 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 "" is given as either description, then the that limit will not be +displayed. The default is "".
+ +
-linewidth pixels
+
Sets the width of the axis +and tick lines. The default is 1 pixel.
+ +
-logscale boolean
+
Indicates whether +the scale of the axis is logarithmic or linear. If boolean is true, the +axis is logarithmic. The default scale is linear.
+ +
-loose boolean
+
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 boolean +is true, the axis range is "loose". The default is 0.
+ +
-majorticks majorList +
+
Specifies where to display major axis ticks. You can use this option to +display ticks at non-uniform intervals. MajorList is a list of axis coordinates +designating the location of major ticks. No minor ticks are drawn. If majorList +is "", major ticks will be automatically computed. The default is "".
+ +
-max +value
+
Sets the maximum limit of axisName. Any data point greater than +value is not displayed. If value is "", the maximum limit is calculated +using the largest data value. The default is "".
+ +
-min value
+
Sets the minimum +limit of axisName. Any data point less than value is not displayed. If +value is "", the minimum limit is calculated using the smallest data value. +The default is "".
+ +
-minorticks minorList
+
Specifies where to display minor +axis ticks. You can use this option to display minor ticks at non-uniform +intervals. MinorList 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 -majortick +option is also set. If minorList is "", minor ticks will be automatically +computed. The default is "".
+ +
-rotate theta
+
Specifies the how many degrees +to rotate the axis tick labels. Theta is a real value representing the number +of degrees to rotate the tick labels. The default is 0.0 degrees.
+ +
-shiftby +value
+
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 value. You can use this option to prevent the axis limits +from being recomputed at each new time point. If value is 0.0, then no automatic +shifting is done. The default is 0.0.
+ +
-showticks boolean
+
Indicates whether +axis ticks should be drawn. If boolean is true, ticks are drawn. If false, +only the axis line is drawn. The default is 1.
+ +
-stepsize value
+
Specifies the +interval between major axis ticks. If value isn't a valid interval (must +be less than the axis range), the request is ignored and the step size +is automatically calculated.
+ +
-subdivisions number
+
Indicates how many minor +axis ticks are to be drawn. For example, if number is two, only one minor +tick is drawn. If number is one, no minor ticks are displayed. The default +is 2.
+ +
-tickfont fontName
+
Specifies the font for axis tick labels. The default +is *-Courier-Bold-R-Normal-*-100-*.
+ +
-ticklength pixels
+
Sets the length of major +and minor ticks (minor ticks are half the length of major ticks). If pixels +is less than zero, the axis will be inverted with ticks drawn pointing +towards the plot. The default is 0.1i.
+ +
-title text
+
Sets the title of the axis. +If text is "", no axis title will be displayed.
+ +
-titlecolor color
+
Sets +the color of the axis title. The default is black.
+ +
-titlefont fontName
+
Specifies +the font for axis title. The default is *-Helvetica-Bold-R-Normal-*-14-140-*.
+
+

+Axis +configuration options may be also be set by the option command. The resource +class is Axis. The resource names are the names of the axes (such as x +or x2).
+option add *Stripchart.Axis.Color blue
+option add *Stripchart.x.LogScale true
+option add *Stripchart.x2.LogScale false
+ + +

+ +

pathName axis create axisName ?option value?...

+
Creates a new axis by the +name axisName. No axis by the same name can already exist. Option and value +are described in above in the axis configure operation.
+ +
pathName axis delete +?axisName?...
+
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.
+ +
pathName +axis invtransform axisName value
+
Performs the inverse transformation, changing +the screen coordinate value to a graph coordinate, mapping the value mapped +to axisName. Returns the graph coordinate.
+ +
pathName axis limits axisName +
+
Returns a list of the minimum and maximum limits for axisName. The order +of the list is min max.
+ +
pathName axis names ?pattern?...
+
Returns a list of +axes matching zero or more patterns. If no pattern argument is give, the +names of all axes are returned.
+ +
pathName axis transform axisName value
+
Transforms +the coordinate value to a screen coordinate by mapping the it to axisName. + Returns the transformed screen coordinate.
+
+

+Only four axes can be displayed +simultaneously. By default, they are x, y, x2, and y2. You can swap in +a different axis with use operation of the special axis components: xaxis, +x2axis, yaxis, and y2axis.
+.g create axis temp
+.g create axis time
+...
+.g xaxis use temp
+.g yaxis use time
+

Only the axes specified for use are displayed on the screen.

+The xaxis, +x2axis, yaxis, and y2axis components operate on an axis location rather +than a specific axis like the more general axis component does. The xaxis +component manages the X-axis located in the bottom margin (whatever axis +that happens to be). Likewise, yaxis uses the Y-axis in the left margin, +x2axis the top X-axis, and y2axis the right Y-axis.

+They implicitly control +the axis that is currently using to that location. By default, xaxis uses +the x axis, yaxis uses y, x2axis uses x2, and y2axis uses y2. These components +can be more convenient to use than always determining what axes are current +being displayed by the graph.

+The following operations are available for +axes. They mirror exactly the operations of the axis component. The axis +argument must be xaxis, x2axis, yaxis, or y2axis. +

+ +
pathName axis cget option +
+
+ +
pathName axis configure ?option value?...
+
+ +
pathName axis invtransform value +
+
+ +
pathName axis limits
+
+ +
pathName axis transform value
+
+ +
pathName axis use ?axisName? +
+
Designates the axis axisName is to be displayed at this location. AxisName +can not be already in use at another location. This command returns the +name of the axis currently using this location.
+
+ +

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.

+The following operations are available for cross hairs: +

+ +
pathName +crosshairs cget option
+
Returns the current value of the cross hairs configuration +option given by option. Option may be any option described below for the +cross hairs configure operation.
+ +
pathName crosshairs configure ?option value?... +
+
Queries or modifies the configuration options of the cross hairs. If +option isn't specified, a list describing all the current options for the +cross hairs is returned. If option is specified, but not value, then a +list describing option is returned. If one or more option and value pairs +are specified, then for each pair, the cross hairs option option is set +to value. The following options are available for cross hairs.
+ +
-color color +
+
Sets the color of the cross hairs. The default is black.
+ +
-dashes dashList +
+
Sets the dash style of the cross hairs. DashList 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 dashList is "", the +cross hairs will be solid lines.
+ +
-hide boolean
+
Indicates whether cross hairs +are drawn. If boolean is true, cross hairs are not drawn. The default is +yes.
+ +
-linewidth pixels
+
Set the width of the cross hair lines. The default +is 1.
+ +
-position pos
+
Specifies the screen position where the cross hairs +intersect. Pos must be in the form "@x,y", where x and y are the window +coordinates of the intersection.
+
+

+Cross hairs configuration options may be +also be set by the option command. The resource name and class are crosshairs +and Crosshairs respectively.
+option add *Stripchart.Crosshairs.LineWidth 2
+option add *Stripchart.Crosshairs.Color red
+ + +

+ +

pathName crosshairs off

+
Turns of the cross hairs.
+ +
pathName crosshairs on +
+
Turns on the display of the cross hairs.
+ +
pathName crosshairs toggle
+
Toggles +the current state of the cross hairs, alternately mapping and unmapping +the cross hairs.
+
+ +

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.

+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.

+The following +operations are available for elements. +

+ +
pathName element activate elemName +?index?...
+
Specifies the data points of element elemName to be drawn using +active foreground and background colors. ElemName is the name of the element +and index is a number representing the index of the data point. If no indices +are present then all data points become active.
+ +
pathName element cget elemName +option
+
Returns the current value of the element configuration option given +by option. Option may be any option described below for the element configure +operation.
+ +
pathName element closest x y varName ?option value?... ?elemName?... +
+
Finds the data point closest to the window coordinates x and y in the element +elemName. ElemName 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 varName the name of the closest element, +the index of its closest point, and the graph coordinates of the point. +Returns 0, if no data point within the threshold distance can be found, +otherwise 1 is returned. The following option-value pairs are available. +
+ +
-halo pixels
+
Specifies a threshold distance where selected data points are +ignored. Pixels is a valid screen distance, such as 2 or 1.2i. If this option +isn't specified, then it defaults to the value of the stripchart's -halo option. +
+ +
-interpolate boolean
+
Indicates that both the data points and interpolated +points along the line segment formed should be considered. If boolean +is true, the closest line segment will be selected instead of the closest +point. If this option isn't specified, boolean defaults to 0.
+
+ + +
+ +
pathName element +configure elemName ?option value?...
+
Queries or modifies the configuration +options for elements. If option isn't specified, a list describing all the +current options for elemName is returned. If option is specified, but not +value, then a list describing the option option is returned. If one or +more option and value pairs are specified, then for each pair, the element +option option is set to value. The following options are valid for elements. +
+ +
-activepen penName
+
Specifies pen to use to draw active element. If penName +is "", no active elements will be drawn. The default is activeLine.
+ +
-color +color
+
Sets the color of the traces connecting the data points.
+ +
-dashes +dashList
+
Sets the dash style of element line. DashList 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 dashList +is "", the lines will be solid.
+ +
-data coordList
+
Specifies the X-Y coordinates +of the data. CoordList is a list of numeric expressions representing the +X-Y coordinate pairs of each data point.
+ +
-fill color
+
Sets the interior color +of symbols. If color is "", then the interior of the symbol is transparent. + If color is defcolor, then the color will be the same as the -color option. + The default is defcolor.
+ +
-hide boolean
+
Indicates whether the element is +displayed. The default is no.
+ +
-label text
+
Sets the element's label in the +legend. If text is "", the element will have no entry in the legend. The +default label is the element's name.
+ +
-linewidth pixels
+
Sets the width of +the connecting lines between data points. If pixels is 0, no connecting +lines will be drawn between symbols. The default is 0.
+ +
-mapx xAxis
+
Selects +the X-axis to map the element's X-coordinates onto. XAxis must be the name +of an axis. The default is x.
+ +
-mapy yAxis
+
Selects the Y-axis to map the element's +Y-coordinates onto. YAxis must be the name of an axis. The default is y.
+ +
-offdash +color
+
Sets the color of the stripes when traces are dashed (see the -dashes +option). If color is "", then the "off" pixels will represent gaps instead +of stripes. If color is defcolor, then the color will be the same as the +-color option. The default is defcolor.
+ +
-outline color
+
Sets the color or +the outline around each symbol. If color is "", then no outline is drawn. +If color is defcolor, then the color will be the same as the -color option. + The default is defcolor.
+ +
-outlinewidth pixels
+
Sets the width of the outline +bordering each symbol. If pixels is 0, no outline will be drawn. The default +is 1.
+ +
-pixels pixels
+
Sets the size of symbols. If pixels is 0, no symbols +will be drawn. The default is 0.125i.
+ +
-scalesymbols boolean
+
If boolean is +true, the size of the symbols drawn for elemName 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 -pixels 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 boolean is false, the element's +symbols are drawn at the designated size, regardless of axis scales. The +default is 0.
+ +
-smooth smooth
+
Specifies how connecting line segments are +drawn between data points. Smooth can be either linear, step, natural, or +quadratic. If smooth is linear, a single line segment is drawn, connecting +both data points. When smooth is step, 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 natural and quadratic +generate multiple segments between data points. If natural, the segments +are generated using a cubic spline. If quadratic, a quadratic spline is +used. The default is linear.
+ +
-styles styleList
+
Specifies what pen to use +based upon the range of weights given. StyleList 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 -weight 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.
+ +
-symbol +symbol
+
Specifies the symbol for data points. Symbol can be either square, +circle, diamond, plus, cross, splus, scross, triangle, "" (where no symbol +is drawn), or a bitmap. Bitmaps are specified as "source ?mask?", where +source is the name of the bitmap, and mask is the bitmap's optional mask. + The default is circle.
+ +
-weights wVec
+
Specifies the weights of the individual +data points. This, in conjunction with the list pen styles (see the -styles +option) controls how data points are drawn. WVec is the name of a BLT vector +or a list of numeric expressions representing the weights for each data +point.
+ +
-xdata xVec
+
Specifies the x-coordinates of the data. XVec is the name +of a BLT vector or a list of numeric expressions.
+ +
-ydata yVec
+
Specifies +the y-coordinates of the data. YVec is the name of a BLT vector or a list +of numeric expressions.
+
+

+Element configuration options may also be set by +the option command. The resource class is Element. The resource name is +the name of the element.
+option add *Stripchart.Element.symbol line
+option add *Stripchart.e1.symbol line
+ + +

+ +

pathName element create elemName ?option value?...

+
Creates a new element elemName. + It's an error is an element elemName already exists. If additional arguments +are present, they specify options valid for element configure operation. +
+ +
pathName element deactivate elemName ?elemName?...
+
Deactivates all the elements +matching pattern. Elements whose names match any of the patterns given are +redrawn using their normal colors.
+ +
pathName element delete ?elemName?...
+
Deletes +all the named elements. The graph is automatically redrawn.
+ +
pathName element +exists elemName
+
Returns 1 if an element elemName currently exists and 0 +otherwise.
+ +
pathName element names ?pattern?...
+
Returns the elements matching +one or more pattern. If no pattern is given, the names of all elements +is returned.
+ +
pathName element show ?nameList?
+
Queries or modifies the +element display list. The element display list designates the elements +drawn and in what order. NameList is a list of elements to be displayed +in the order they are named. If there is no nameList argument, the current +display list is returned.
+ +
pathName element type elemName
+
Returns the type +of elemName. If the element is a bar element, the commands returns the +string "bar", otherwise it returns "line".
+
+ +

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. +
+ +
pathName grid cget option
+
Returns the current value of the grid line +configuration option given by option. Option may be any option described +below for the grid configure operation.
+ +
pathName grid configure ?option +value?...
+
Queries or modifies the configuration options for grid lines. If +option isn't specified, a list describing all the current grid options for +pathName is returned. If option is specified, but not value, then a list +describing option is returned. If one or more option and value pairs are +specified, then for each pair, the grid line option option is set to value. + The following options are valid for grid lines.
+ +
-color color
+
Sets the color +of the grid lines. The default is black.
+ +
-dashes dashList
+
Sets the dash style +of the grid lines. DashList 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 dashList is "", the grid will be solid lines. +
+ +
-hide boolean
+
Indicates whether the grid should be drawn. If boolean is true, +grid lines are not shown. The default is yes.
+ +
-linewidth pixels
+
Sets the width +of grid lines. The default width is 1.
+ +
-mapx xAxis
+
Specifies the X-axis to +display grid lines. XAxis must be the name of an axis. The default is x. +
+ +
-mapy yAxis
+
Specifies the Y-axis to display grid lines. YAxis must be the +name of an axis. The default is y.
+ +
-minor boolean
+
Indicates whether the grid +lines should be drawn for minor ticks. If boolean is true, the lines will +appear at minor tick intervals. The default is 1.
+
+

+Grid configuration options +may also be set by the option command. The resource name and class are +grid and Grid respectively.
+option add *Stripchart.grid.LineWidth 2
+option add *Stripchart.Grid.Color black
+ + +

+ +

pathName grid off

+
Turns off the display the grid lines.
+ +
pathName grid on +
+
Turns on the display the grid lines.
+ +
pathName grid toggle
+
Toggles the display +of the grid.
+
+ +

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.

+The following operations +are valid for the legend. +

+ +
pathName legend activate pattern...
+
Selects legend +entries to be drawn using the active legend colors and relief. All entries +whose element names match pattern are selected. To be selected, the element +name must match only one pattern.
+ +
pathName legend cget option
+
Returns the +current value of a legend configuration option. Option may be any option +described below in the legend configure operation.
+ +
pathName legend configure +?option value?...
+
Queries or modifies the configuration options for the legend. + If option isn't specified, a list describing the current legend options +for pathName is returned. If option is specified, but not value, then a +list describing option is returned. If one or more option and value pairs +are specified, then for each pair, the legend option option is set to value. + The following options are valid for the legend.
+ +
-activebackground color +
+
Sets the background color for active legend entries. All legend entries +marked active (see the legend activate operation) are drawn using this +background color.
+ +
-activeborderwidth pixels
+
Sets the width of the 3-D border +around the outside edge of the active legend entries. The default is 2. +
+ +
-activeforeground color
+
Sets the foreground color for active legend entries. + All legend entries marked as active (see the legend activate operation) +are drawn using this foreground color.
+ +
-activerelief relief
+
Specifies the +3-D effect desired for active legend entries. Relief denotes how the interior +of the entry should appear relative to the legend; for example, raised +means the entry should appear to protrude from the legend, relative to +the surface of the legend. The default is flat.
+ +
-anchor anchor
+
Tells how +to position the legend relative to the positioning point for the legend. + This is dependent on the value of the -position option. The default is +center.
+ +
left or right
+
The anchor describes how to position the legend vertically. +
+ +
top or bottom
+
The anchor describes how to position the legend horizontally. +
+ +
@x,y
+
The anchor specifies how to position the legend relative to the +positioning point. For example, if anchor is center then the legend is centered +on the point; if anchor is n 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.
+ +
plotarea
+
The anchor specifies how to position +the legend relative to the plotting area. For example, if anchor is center +then the legend is centered in the plotting area; if anchor is ne then +the legend will be drawn such that occupies the upper right corner of the +plotting area.
+
+ + +
+ +
-background color
+
Sets the background color of the legend. +If color is "", the legend background with be transparent.
+ +
-borderwidth pixels +
+
Sets the width of the 3-D border around the outside edge of the legend (if +such border is being drawn; the relief option determines this). The default +is 2 pixels.
+ +
-font fontName
+
FontName specifies a font to use when drawing +the labels of each element into the legend. The default is *-Helvetica-Bold-R-Normal-*-12-120-*. +
+ +
-foreground color
+
Sets the foreground color of the text drawn for the element's +label. The default is black.
+ +
-hide boolean
+
Indicates whether the legend should +be displayed. If boolean is true, the legend will not be draw. The default +is no.
+ +
-ipadx pad
+
Sets the amount of internal padding to be added to the +width of each legend entry. Pad can be a list of one or two screen distances. + If pad has two elements, the left side of the legend entry is padded by +the first distance and the right side by the second. If pad is just one +distance, both the left and right sides are padded evenly. The default +is 2.
+ +
-ipady pad
+
Sets an amount of internal padding to be added to the height +of each legend entry. Pad can be a list of one or two screen distances. + If pad has two elements, the top of the entry is padded by the first distance +and the bottom by the second. If pad is just one distance, both the top +and bottom of the entry are padded evenly. The default is 2.
+ +
-padx pad
+
Sets +the padding to the left and right exteriors of the legend. Pad can be a +list of one or two screen distances. If pad has two elements, the left +side of the legend is padded by the first distance and the right side by +the second. If pad has just one distance, both the left and right sides +are padded evenly. The default is 4.
+ +
-pady pad
+
Sets the padding above and +below the legend. Pad can be a list of one or two screen distances. If +pad has two elements, the area above the legend is padded by the first +distance and the area below by the second. If pad is just one distance, +both the top and bottom areas are padded evenly. The default is 0.
+ +
-position +pos
+
Specifies where the legend is drawn. The -anchor option also affects +where the legend is positioned. If pos is left, left, top, or bottom, the +legend is drawn in the specified margin. If pos is plotarea, then the legend +is drawn inside the plotting area at a particular anchor. If pos is in +the form "@x,y", where x and y are the window coordinates, the legend is +drawn in the plotting area at the specified coordinates. The default is +right.
+ +
-raised boolean
+
Indicates whether the legend is above or below the +data elements. This matters only if the legend is in the plotting area. + If boolean is true, the legend will be drawn on top of any elements that +may overlap it. The default is no.
+ +
-relief relief
+
Specifies the 3-D effect +for the border around the legend. Relief specifies how the interior of the +legend should appear relative to the strip chart; for example, raised means +the legend should appear to protrude from the strip chart, relative to +the surface of the strip chart. The default is sunken.
+
+

+Legend configuration +options may also be set by the option command. The resource name and class +are legend and Legend respectively.
+option add *Stripchart.legend.Foreground blue
+option add *Stripchart.Legend.Relief raised
+ + +

+ +

pathName legend deactivate pattern...

+
Selects legend entries to be drawn using +the normal legend colors and relief. All entries whose element names match +pattern are selected. To be selected, the element name must match only +one pattern.
+ +
pathName legend get pos
+
Returns the name of the element whose +entry is at the screen position pos in the legend. Pos must be in the form +"@x,y", where x and y are window coordinates. If the given coordinates +do not lie over a legend entry, "" is returned.
+
+ +

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 +-weight and -style options).

+One pen, called activeLine, 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. +
+.s pen configure "activeLine" -color green
+

You can create and use any number of pens. To create a pen, invoke the pen +component and its create operation.
+.s pen create myPen
+

You map pens to a data element using either the element's -pen or -activepen +options.
+.s element create "line1" -xdata $x -ydata $tempData \
+ -pen myPen
+

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 -styles option).
+.s element configure "line1" -styles { myPen 2.0 3.0 }
+

This says that any data point with a weight between 2.0 and 3.0 is to be +drawn using the pen myPen. All other points are drawn with the element's +default attributes.

+The following operations are available for pen components. +

+ +

+ +
pathName pen cget penName option
+
Returns the current value of the option +given by option for penName. Option may be any option described below for +the pen configure operation.
+ +
pathName pen configure penName ?option value?... +
+
Queries or modifies the configuration options of penName. If option isn't +specified, a list describing the current options for penName is returned. + If option is specified, but not value, then a list describing option is +returned. If one or more option and value pairs are specified, then for +each pair, the pen option option is set to value. The following options +are valid for pens.
+ +
-color color
+
Sets the color of the traces connecting +the data points.
+ +
-dashes dashList
+
Sets the dash style of element line. DashList +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 dashList is "", the lines will be solid.
+ +
-fill color
+
Sets the +interior color of symbols. If color is "", then the interior of the symbol +is transparent. If color is defcolor, then the color will be the same as +the -color option. The default is defcolor.
+ +
-linewidth pixels
+
Sets the width +of the connecting lines between data points. If pixels is 0, no connecting +lines will be drawn between symbols. The default is 0.
+ +
-offdash color
+
Sets +the color of the stripes when traces are dashed (see the -dashes option). + If color is "", then the "off" pixels will represent gaps instead of stripes. + If color is defcolor, then the color will be the same as the -color option. + The default is defcolor.
+ +
-outline color
+
Sets the color or the outline around +each symbol. If color is "", then no outline is drawn. If color is defcolor, +then the color will be the same as the -color option. The default is defcolor. +
+ +
-outlinewidth pixels
+
Sets the width of the outline bordering each symbol. + If pixels is 0, no outline will be drawn. The default is 1.
+ +
-pixels pixels +
+
Sets the size of symbols. If pixels is 0, no symbols will be drawn. The +default is 0.125i.
+ +
-symbol symbol
+
Specifies the symbol for data points. Symbol +can be either square, circle, diamond, plus, cross, splus, scross, triangle, +"" (where no symbol is drawn), or a bitmap. Bitmaps are specified as "source +?mask?", where source is the name of the bitmap, and mask is the bitmap's +optional mask. The default is circle.
+ +
-type elemType
+
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". +
+
+

+Pen configuration options may be also be set by the option command. The +resource class is Pen. The resource names are the names of the pens.
+option add *Stripchart.Pen.Color blue
+option add *Stripchart.activeLine.color green
+ + +

+ +

pathName pen create penName ?option value?...

+
Creates a new pen by the name +penName. No pen by the same name can already exist. Option and value are +described in above in the pen configure operation.
+ +
pathName pen delete +?penName?...
+
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.
+ +
pathName +pen names ?pattern?...
+
Returns a list of pens matching zero or more patterns. + If no pattern argument is give, the names of all pens are returned.
+
+ +

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. +

+The following postscript operations are available. +

+ +
pathName postscript cget +option
+
Returns the current value of the postscript option given by option. + Option may be any option described below for the postscript configure +operation.
+ +
pathName postscript configure ?option value?...
+
Queries or modifies +the configuration options for PostScript generation. If option isn't specified, +a list describing the current postscript options for pathName is returned. + If option is specified, but not value, then a list describing option is +returned. If one or more option and value pairs are specified, then for +each pair, the postscript option option is set to value. The following +postscript options are available.
+ +
-center boolean
+
Indicates whether the plot +should be centered on the PostScript page. If boolean is false, the plot +will be placed in the upper left corner of the page. The default is 1.
+ +
-colormap +varName
+
VarName must be the name of a global array variable that specifies +a color mapping from the X color name to PostScript. Each element of varName +must consist of PostScript code to set a particular color value (e.g. ``1.0 +1.0 0.0 setrgbcolor''). When outputting color information in PostScript, the +array variable varName 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 varName for a given color, then it uses the red, green, +and blue intensities from the X color.
+ +
-colormode mode
+
Specifies how to output +color information. Mode must be either color (for full color output), gray +(convert all colors to their gray-scale equivalents) or mono (convert foreground +colors to black and background colors to white). The default mode is color. +
+ +
-fontmap varName
+
VarName must be the name of a global array variable that +specifies a font mapping from the X font name to PostScript. Each element +of varName 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 varName 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 Adobe (such as Times, Helvetica, Courier, etc.). If all of this +fails then the font defaults to Helvetica-Bold.
+ +
-decorations boolean
+
Indicates +if PostScript commands to generate color backgrounds and 3-D borders should +be output. If boolean is false, the background will be white and no 3-D +borders will be generated. The default is 1.
+ +
-height pixels
+
Sets the height +of the plot. This lets you plot the stripchart with a height different +from the one displayed on the screen. If pixels is 0, the height is the +same as the displayed height. The default is 0.
+ +
-landscape boolean
+
If boolean +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 0.
+ +
-maxpect +boolean
+
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 0.
+ +
-padx pad
+
Sets the horizontal padding for the left and right page borders. + The borders are exterior to the plot. Pad can be a list of one or two +screen distances. If pad has two elements, the left border is padded by +the first distance and the right border by the second. If pad has just +one distance, both the left and right borders are padded evenly. The default +is 1i.
+ +
-pady pad
+
Sets the vertical padding for the top and bottom page borders. +The borders are exterior to the plot. Pad can be a list of one or two screen +distances. If pad has two elements, the top border is padded by the first +distance and the bottom border by the second. If pad has just one distance, +both the top and bottom borders are padded evenly. The default is 1i.
+ +
-paperheight +pixels
+
Sets the height of the postscript page. This can be used to select +between different page sizes (letter, A4, etc). The default height is 11.0i. +
+ +
-paperwidth pixels
+
Sets the width of the postscript page. This can be used +to select between different page sizes (letter, A4, etc). The default width +is 8.5i.
+ +
-width pixels
+
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 +pixels is 0, the width is the same as the widget's width. The default is +0.
+
+

+Postscript configuration options may be also be set by the option command. + The resource name and class are postscript and Postscript respectively. +
+option add *Stripchart.postscript.Decorations false
+option add *Stripchart.Postscript.Landscape true
+ + +

+ +

pathName postscript output ?fileName? ?option value?...

+
Outputs a file of +encapsulated PostScript. If a fileName argument isn't present, the command +returns the PostScript. If any option-value pairs are present, they set configuration +options controlling how the PostScript is generated. Option and value can +be anything accepted by the postscript configure operation above.
+
+ +

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 -under option.

+Markers, in contrast to elements, don't affect the scaling +of the coordinate axes. They can also have elastic coordinates (specified +by -Inf and Inf 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 +-Inf,-Inf.

+The following operations are available for markers. +

+ +
pathName marker +after markerId ?afterId?
+
Changes the order of the markers, drawing the +first marker after the second. If no second afterId 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.
+ +
pathName marker before markerId ?beforeId?
+
Changes +the order of the markers, drawing the first marker before the second. If +no second beforeId 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.
+ +
pathName +marker cget option
+
Returns the current value of the marker configuration +option given by option. Option may be any option described below in the +configure operation.
+ +
pathName marker configure markerId ?option value?...
+
Queries +or modifies the configuration options for markers. If option isn't specified, +a list describing the current options for markerId is returned. If option +is specified, but not value, then a list describing option is returned. +If one or more option and value pairs are specified, then for each pair, +the marker option option is set to value.

+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.

+ +
-coords coordList
+
Specifies the +coordinates of the marker. CoordList 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 coordList is "", the marker will not be displayed. +The default is "".
+ +
-element elemName
+
Links the marker with the element elemName. + The marker is drawn only if the element is also currently displayed (see +the element's show operation). If elemName is "", the marker is always drawn. + The default is "".
+ +
-hide boolean
+
Indicates whether the marker is drawn. +If boolean is true, the marker is not drawn. The default is no.
+ +
-mapx xAxis +
+
Specifies the X-axis to map the marker's X-coordinates onto. XAxis must the +name of an axis. The default is x.
+ +
-mapy yAxis
+
Specifies the Y-axis to map +the marker's Y-coordinates onto. YAxis must the name of an axis. The default +is y.
+ +
-name markerId
+
Changes the identifier for the marker. The identifier +markerId can not already be used by another marker. If this option isn't +specified, the marker's name is uniquely generated.
+ +
-under boolean
+
Indicates +whether the marker is drawn below/above data elements. If boolean 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 0.
+ +
-xoffset pixels +
+
Specifies a screen distance to offset the marker horizontally. Pixels is +a valid screen distance, such as 2 or 1.2i. The default is 0.
+ +
-yoffset pixels +
+
Specifies a screen distance to offset the markers vertically. Pixels is +a valid screen distance, such as 2 or 1.2i. The default is 0.
+
+

+Marker configuration +options may also be set by the option command. The resource class is either +BitmapMarker, ImageMarker, LineMarker, PolygonMarker, TextMarker, or +WindowMarker, depending on the type of marker. The resource name is the +name of the marker.
+option add *Stripchart.TextMarker.Foreground white
+option add *Stripchart.BitmapMarker.Foreground white
+option add *Stripchart.m1.Background blue
+ + +

+ +

pathName marker create type ?option value?...

+
Creates a marker of the selected +type. Type may be either text, line, bitmap, image, polygon, or window. +This command returns the marker identifier, used as the markerId argument +in the other marker-related commands. If the -name 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.
+ +
pathName marker delete +?name?...
+
Removes one of more markers. The graph will automatically be redrawn +without the marker..
+ +
pathName marker exists markerId
+
Returns 1 if the +marker markerId exists and 0 otherwise.
+ +
pathName marker names ?pattern? +
+
Returns the names of all the markers that currently exist. If pattern +is supplied, only those markers whose names match it will be returned.
+ +
pathName +marker type markerId
+
Returns the type of the marker given by markerId, +such as line or text. If markerId is not a valid a marker identifier, "" +is returned.
+
+ +

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.

+Bitmap markers are created with the marker's create operation +in the form:
+

+pathName marker create bitmap ?option value?...
+

There may be many option-value pairs, each sets a configuration options +for the marker. These same option-value pairs may be used with the marker's +configure operation.

+The following options are specific to bitmap markers: + +

+ +
-background color
+
Sets the background color of the bitmap. If color is "", +the background color will be transparent. The default background color +is white.
+ +
-bitmap bitmap
+
Specifies the bitmap to be displayed. If bitmap +is "", the marker will not be displayed. The default is "".
+ +
-foreground color +
+
Sets the foreground color of the bitmap. The default foreground color +is black.
+ +
-mask mask
+
Specifies a mask for the bitmap to be displayed. This +mask is a bitmap itself, denoting the pixels that are transparent. If mask +is "", all pixels of the bitmap will be drawn. The default is "".
+ +
-rotate +theta
+
Sets the rotation of the bitmap. Theta 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 0.0.
+
+ +

Image +Markers

+A image marker displays an image. Image markers are created with +the marker's create operation in the form:
+

+pathName marker create image ?option value?...
+

There may be many option-value pairs, each sets a configuration option for +the marker. These same option-value pairs may be used with the marker's configure +operation.

+The following options are specific to image markers: +

+ +
-anchor anchor +
+
Anchor tells how to position the image relative to the positioning point +for the image. For example, if anchor is center then the image is centered +on the point; if anchor is n 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 center.
+ +
-image image
+
Specifies +the image to be drawn. If image is "", the marker will not be drawn. The +default is "".
+
+ +

Line Markers

+A line marker displays one or more connected +line segments. Line markers are created with marker's create operation in +the form:
+

+pathName marker create line ?option value?...
+

There may be many option-value pairs, each sets a configuration option for +the marker. These same option-value pairs may be used with the marker's configure +operation.

+The following options are specific to line markers: +

+ +
-background +color
+
Sets the background color of the line. The option is affects the line +color only when the -stipple option is set. If this option isn't specified +then it defaults to white.
+ +
-dashes dashList
+
Sets the dash style of the line. +DashList 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 dashList is "", the marker line will be solid.
+ +
-foreground color
+
Sets +the foreground color. The default foreground color is black.
+ +
-linewidth pixels +
+
Sets the width of the lines. The default width is 0.
+ +
-stipple bitmap
+
Specifies +a stipple pattern used to draw the line, rather than a solid line. Bitmap +specifies a bitmap to use as the stipple pattern. If bitmap is "", then +the line is drawn in a solid fashion. The default is "".
+
+ +

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 create operation in the form:
+

+pathName marker create polygon ?option value?...
+

There may be many option-value pairs, each sets a configuration option for +the marker. These same option-value pairs may be used with the marker configure +command to change the marker's configuration. The following options are supported +for polygon markers: +

+ +
-dashes dashList
+
Sets the dash style of the outline +of the polygon. DashList 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 dashList is "", the outline will be a solid +line.
+ +
-fill color
+
Sets the fill color of the polygon. If color is "", then +the interior of the polygon is transparent. The default is white.
+ +
-linewidth +pixels
+
Sets the width of the outline of the polygon. If pixels is zero, + no outline is drawn. The default is 0.
+ +
-outline color
+
Sets the color of the +outline of the polygon. If the polygon is stippled (see the -stipple option), +then this represents the foreground color of the stipple. The default is +black.
+ +
-stipple bitmap
+
Specifies that the polygon should be drawn with a +stippled pattern rather than a solid color. Bitmap specifies a bitmap to +use as the stipple pattern. If bitmap is "", then the polygon is filled +with a solid color (if the -fill option is set). The default is "".
+
+ +

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 create operation +in the form:
+

+pathName marker create text ?option value?...
+

There may be many option-value pairs, each sets a configuration option +for the text marker. These same option-value pairs may be used with the + marker's configure operation.

+The following options are specific to text +markers: +

+ +
-anchor anchor
+
Anchor tells how to position the text relative to +the positioning point for the text. For example, if anchor is center then +the text is centered on the point; if anchor is n 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 center.
+ +
-background +color
+
Sets the background color of the text string. If color is "", the +background will be transparent. The default is white.
+ +
-font fontName
+
Specifies +the font of the text. The default is *-Helvetica-Bold-R-Normal-*-120-*.
+ +
-foreground +color
+
Sets the foreground color of the text. The default is black.
+ +
-justify +justify
+
Specifies how the text should be justified. This matters only when +the marker contains more than one line of text. Justify must be left, right, +or center. The default is center.
+ +
-padx pad
+
Sets the padding to the left +and right exteriors of the text. Pad can be a list of one or two screen +distances. If pad has two elements, the left side of the text is padded +by the first distance and the right side by the second. If pad has just +one distance, both the left and right sides are padded evenly. The default +is 4.
+ +
-pady pad
+
Sets the padding above and below the text. Pad can be a list +of one or two screen distances. If pad has two elements, the area above +the text is padded by the first distance and the area below by the second. +If pad is just one distance, both the top and bottom areas are padded evenly. + The default is 4.
+ +
-rotate theta
+
Specifies the number of degrees to rotate +the text. Theta 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 0.0.
+ +
-text text
+
Specifies the text of the +marker. The exact way the text is displayed may be affected by other options +such as -anchor or -rotate.
+
+ +

Window Markers

+A window marker displays a widget +at a given position. Window markers are created with the marker's create +operation in the form:
+

+pathName marker create window ?option value?...
+

There may be many option-value pairs, each sets a configuration option for +the marker. These same option-value pairs may be used with the marker's configure +command.

+The following options are specific to window markers: +

+ +
-anchor anchor +
+
Anchor tells how to position the widget relative to the positioning point +for the widget. For example, if anchor is center then the widget is centered +on the point; if anchor is n 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 center.
+ +
-height pixels +
+
Specifies the height to assign to the marker's window. If this option isn't +specified, or if it is specified as "", then the window is given whatever +height the widget requests internally.
+ +
-width pixels
+
Specifies the width +to assign to the marker's window. If this option isn't specified, or if it +is specified as "", then the window is given whatever width the widget +requests internally.
+ +
-window pathName
+
Specifies the widget to be managed. + PathName must be a child of the stripchart widget.
+
+ +

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 Enter, Leave, ButtonPress, Motion, and KeyPress).

+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.

+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 -bindtags +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.

+The -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 -bindtags option doesn't change this. +

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.

+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.

+From Tcl, create +the vectors and configure the element to use them.
+vector X Y
+.s element configure line1 -xdata X -ydata Y
+

To set data points from C, you pass the values as arrays of doubles using +the Blt_ResetVector 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.
+#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;
+}
+

See the vector manual page for more details. +

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. +
  • Try to minimize the number of data points. The more data points +the looked at, the more work the strip chart must do.
  • ·
  • 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.
  • ·
  • Data elements without symbols are drawn +faster than with symbols. Set the data element's -symbol option to none. If +you need to draw symbols, try using the simple symbols such as splus and +scross.
  • ·
  • Don't stipple or dash the element. Solid lines are much faster.
  • ·
  • If +you update data elements frequently, try turning off the widget's -bufferelements +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.
  • +
+ +

Limitations

+Auto-scale +routines do not use requested min/max limits as boundaries when the axis +is logarithmically scaled.

+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. +

Future Incompatibility

+The -mapped options +are obsoleted and will be removed. You can achieve the same results using +the -hide option instead.
+# Works for now.
+.s legend configure -mapped no
+

+# Instead use this.
+.s legend configure -hide yes
+ +

Keywords

+stripchart, graph, widget

+ +


+Table of Contents

+

+ 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 @@ + + + + + +table(n) manual page + + +Table of Contents

+ +

Name

+table - Arranges widgets in a table +

Synopsis

+table +container ?widget index option value?...

+table arrange container

+table cget +container ?item? option

+table configure container ?item?... ?option value?... +

+table extents container item

+table forget widget ?widget?...

+table info container +item

+table locate container x y

+table containers ?switch? ?arg?

+table save +container

+table search container ?switch arg?... +

Description

+The table 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. + +

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.

+The table 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 +slaves in the Tk parlance) are arranged inside a containing widget (called +the master). 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.

+The placement of widget windows determines both the size and arrangement +of the table. The table queries the requested size of each widget. The +requested size 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 normal size.

+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. +

Example

+The table geometry manager is created by +invoking the table command.
+# Create a table in the root window
+table .
+

The window . is now the container of the table. Widgets are packed into +the table and displayed within the confines of the container.

+You add widgets +to the table by row and column location. Row and column indices start from +zero.
+label .title -text "This is a title"
+

+# Add a label to the table
+table . .title 0,0
+

The label .title is added to the table. We can add more widgets in the same +way.
+button .ok -text "Ok"
+button .cancel -text "Cancel"
+

+# Add two buttons
+table . .ok 1,0
+table . .cancel 1,1
+

Two buttons .ok and .cancel 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.

+The first column contains two widgets, .title and +.ok. By default, the widest of the two widgets will define the width of +the column. However, we want .title to be centered horizontally along the +top of the table. We can make .title span two columns using the configure +operation.
+# Make the label span both columns
+table configure . .title -cspan 2
+

The label .title will now be centered along the top row of the table.

+In +the above example, we've create and arranged the layout for the table invoking +the table command several times. Alternately, we could have used a single +table command.
+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
+

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 configure operation again.
+table configure . -propagate no
+

You can also set the width of height of the table to a specific value. This +supersedes the calculated table size.
+# Make the container 4 inches wide, 3 inches high
+table configure . -reqwidth 4i -reqheight 3i
+

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 +-anchor configuration option.
+table configure . .ok -anchor w
+

The -fill option expands the widget to fill the extra space either vertically +or horizontally (or both).
+# 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
+

The width of .title will be the combined widths of both columns. Both .ok +and .cancel will become as tall as the second row.

+The -padx and -pady options +control the amount of padding around the widget. Both options take a list +of one or two values.
+# 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 }
+

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.

+Like the container, you can +also override the requested widths and heights of widgets using the -reqwidth +and -reqheight 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.
+# Make all buttons one inch wide
+table configure . .ok .cancel -reqwidth 1i
+

+

Each row and column of the table can be configured, again using the configure +operation. Rows are and columns are designated by Ri and Ci respectively, +where i is the index of the row or column.

+For example, you can set the +size of a row or column.
+# 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
+

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 -width and -height options to create empty spaces in the table.
+# Create an empty row and column
+table configure . r2 c2 -width 1i
+

The -pady option lets you add padding to the top and bottom sides of rows. + The -padx option adds padding to the left and right sides of columns. Both +options take a list of one or two values.
+# 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 }
+

+

Notice that you can configure all the rows and columns using either R* +or C*.

+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 -resize option indicates whether +the row or column can be shrunk or stretched. If the value is shrink, the +row or column can only be resized smaller. If expand, it can only be resized +larger. If none, the row or column is frozen at its requested size.
+# 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
+

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.
+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
+

Note that the value of the -width 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 .vscroll.

+Finally, the forget operation removes widgets +from the table.
+# Remove the windows from the table
+table forget .quit .frame
+

It's not necessary to specify the container. The table command determines +the container from the widget name. +

Operations

+The following operations are +available for the table: +
+ +
table container ?widget index option value?... +
+
Adds the widget widget to the table at index. Index is a row,column position +in the table. It must be in the form row,column where row and column are +the respective row and column numbers, starting from zero (0,0 is the upper +leftmost position). Row and column may also be numeric expressions that +are recursively evaluated. If a table doesn't exist for container, one is +created. Widget is the path name of the window, that must already exist, +to be arranged inside of container. Option and value are described in the + WIDGET + section.
+ +
table arrange container
+
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 normal sizes of rows and columns, that +are based upon the requested widget sizes.
+ +
table cget container ?item? option +
+
Returns the current value of the configuration option specific to item +given by option. Item is either a row or column index, or the path name +of a widget. Item can be in any form describe in the configure operation +below. If no item argument is provided, then the configuration option is +for the table itself. Option may be any one of the options described in +the appropiate section for item.
+ +
table configure container item... ?option +value?...
+
Queries or modifies the configuration options specific to item. +If no option is specified, this command returns a list describing all of +the available options for item If the argument item is omitted, then the +specified configuration options are for the table itself. Otherwise item +must be either a row or column specification, or the path name of a widget. + The following item types are available.
+ +
Ci
+
Specifies the column of container +to be configured. Item must be in the form Cn, where i is the index of + the column. See the COLUMN + section.
+ +
Ri
+
Specifies the row of container +to be configured. Item must be in the form Ri, where i is the index of the +row. See the ROW + section.
+ +
widget
+
Specifies a widget of container to be +queried. Widget is the path name of a widget packed in container. See the +WIDGET + section.
+ +
No argument
+
Specifies that the table itself is to be queried. + See the TABLE + section for a description of the option-value pairs for +the table.
+
+ +

+The option and value pairs are specific to item. If option is +specified with no value, 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 option is specified). If one or more option-value +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.

+ +
+ +
table +extents container index
+
Queries the location and dimensions of row and +columns in the table. Index 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.
+ +
table forget widget ?widget?... +
+
Requests that widget no longer have its geometry managed. Widget 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 widget is not +currently managed by any table, an error message is returned, otherwise +the empty string.
+ +
table info container item
+
Returns a list of the current +configuration options for item. The list returned is exactly in the form +that might be specified to the table command. It can be used to save and +reset table configurations. Item must be one of the following.
+ +
Ci
+
Specifies +the column of container to be queried. Item must be in the form Cn, where +n is the index of the column.
+ +
Ri
+
Specifies the row of container to be +queried. Item must be in the form Ri, where i is the index of the row. +
+ +
widget
+
Specifies a widget of container to be queried. Widget is the path +name of a widget packed in container.
+ +
No argument
+
Specifies that the table +itself is to be queried.
+
+ + +
+ +
table locate container x y
+
Returns the table index +(row,column) of the cell containing the given screen coordinates. The x +and y arguments represent the x and y coordinates of the sample point to +be tested.
+ +
table containers ?switch arg?
+
Returns a list of all container +windows matching a given criteria (using switch and arg). If no switch +and arg arguments are given, the names of all container windows (only those +using the table command) are returned. The following are valid switches: +
+ +
-pattern pattern
+
Returns a list of pathnames of all container windows matching +pattern.
+ +
-slave window
+
Returns the name of the container window of table +managing window. Window must be the path name of widget. If window is not +managed by any table, the empty string is returned.
+
+ + +
+ +
table search container +?switch arg?...
+
Returns the names of all the widgets in container matching +the criteria given by switch and arg. Container is name of the container +window associated with the table to be searched. The name of the widget +is returned if any one switch-arg criteria matches. If no switch-arg arguments +are given, the names of all widgets managed by container are returned. +The following are switches are available:
+ +
-pattern pattern
+
Returns the names +of any names of the widgets matching pattern.
+ +
-span index
+
Returns the +names of widgets that span index. A widget does not need to start at index +to be included. Index must be in the form row,column, where row and column +are valid row and column numbers.
+ +
-start index
+
Returns the names of widgets +that start at index. Index must be in the form row,column, where row and +column are valid row and column numbers.
+
+ + +

Table Options

+To configure the table +itself, you omit the item argument when invoking the configure operation. +
+table configure container ?option value?...
+

The following options are available for the table:

+
+ +
-padx pad
+
Sets how much +padding to add to the left and right exteriors of the table. Pad can be +a list of one or two numbers. If pad has two elements, the left side of +the table is padded by the first value and the right side by the second +value. If pad has just one value, both the left and right sides are padded +evenly by the value. The default is 0.
+ +
-pady pad
+
Sets how much padding to +add to the top and bottom exteriors of the table. Pad can be a list of one +or two numbers. If pad has two elements, the area above the table is padded +by the first value and the area below by the second value. If pad is just +one number, both the top and bottom areas are padded by the value. The +default is 0.
+ +
-propagate boolean
+
Indicates if the table should override +the requested width and height of the container window. If boolean is false, +container will not be resized. Container will be its requested size. The +default is 1.
+
+
+ +

Widget Options

+widgets are configured by specifying the name +of the widget when invoking the configure operation.
+

+table configure container widget ?option value?...
+

Widget must be the path name of a window already packed in the table associated +with container. The following options are available for widgets:

+
+ +
-anchor +anchor
+
Anchors widget to a particular edge of the cell(s) it resides. This +option has effect only if the space of the spans surrounding widget is +larger than widget. Anchor specifies how widget will be positioned in the +space. For example, if anchor is center then the window is centered in +the rows and columns it spans; if anchor is w then the window will be aligned +with the leftmost edge of the span. The default is center.
+ +
-columnspan number +
+
Sets the number of columns widget will span. The default is 1.
+ +
-columncontrol +control
+
Specifies how the width of widget should control the width of the +columns it spans. Control is either normal, none, or full. The default +is normal.
+ +
none
+
The width of widget is not considered.
+ +
full
+
Only the width +of widget will be considered when computing the widths of the columns. +
+ +
normal
+
Indicates that the widest widget spanning the column will determine + the width of the span.
+
+
+ +
+ +
-fill fill
+
Specifies if widget should be stretched +to fill any free space in the span surrounding widget. Fill is either none, +x, y, both. The default is none.
+ +
x
+
The widget can grow horizontally.
+ +
y +
+
The widget can grow vertically.
+ +
both
+
The widget can grow both vertically +and horizontally.
+ +
none
+
The widget does not grow along with the span. +
+
+ + +
+ +
-ipadx pixels
+
Sets how much horizontal padding to add internally on the +left and right sides of widget. Pixels must be a valid screen distance +like 2 or 0.3i. The default is 0.
+ +
-ipady pixels
+
Sets how much vertical padding +to add internally on the top and bottom of widget. Pixels must be a valid +screen distance like 2 or 0.3i. The default is 0.
+ +
-padx pad
+
Sets how much +padding to add to the left and right exteriors of widget. Pad can be a list +of one or two numbers. If pad has two elements, the left side of widget +is padded by the first value and the right side by the second value. If +pad has just one value, both the left and right sides are padded evenly +by the value. The default is 0.
+ +
-pady pad
+
Sets how much padding to add to +the top and bottom exteriors of widget. Pad can be a list of one or two +numbers. If pad has two elements, the area above widget is padded by the +first value and the area below by the second value. If pad is just one +number, both the top and bottom areas are padded by the value. The default +is 0.
+ +
-reqheight height
+
Specifies the limits of the requested height for +widget. Height is a list of bounding values. See the BOUNDING + section +for a description of this list. By default, the height of widget is its +requested height with its internal padding (see the -ipady option). The +bounds specified by height either override the height completely, or bound +the height between two sizes. The default is "".
+ +
-reqwidth width
+
Specifies +the limits of the requested width for widget. Width is a list of bounding +values. See the BOUNDING + section for a description of this list. By default, +the width of widget is its requested width with its internal padding (set +the -ipadx option). The bounds specified by width either override the width +completely, or bound the height between two sizes. The default is "".
+ +
-rowspan +number
+
Sets the number of rows widget will span. The default is 1.
+ +
-rowcontrol +control
+
Specifies how the height of widget should control the height of +the rows it spans. Control is either normal, none, or full. The default +is normal.
+ +
none
+
The height of widget is not considered.
+ +
full
+
Only the +height of widget will be considered when computing the heights of the rows. +
+ +
normal
+
Indicates that the tallest widget spanning the row will determine + the height of the span.
+
+ + + +

Column Options

+To configure a column in the table, +specify the column index as Ci, where i is the index of the column to be +configured.
+

+table configure container Ci ?option value?...
+

If the index is specified as C*, then all columns of the table will be +configured. The following options are available for table columns.

+
+ +
-padx +pad
+
Sets the padding to the left and right of the column. Pad can be a list +of one or two numbers. If pad has two elements, the left side of the column +is padded by the first value and the right side by the second value. If +pad has just one value, both the left and right sides are padded evenly +by the value. The default is 0.
+ +
-resize mode
+
Indicates that the column can +expand or shrink from its requested width when the table is resized. Mode +must be one of the following: none, expand, shrink, or both. If mode is + expand the width of the column is expanded if there is extra space in +the container window. If mode is shrink its width may be reduced beyond +its requested width if there is not enough space in the container. The default +is none.
+ +
-width width
+
Specifies the limits within that the width of the column +may expand or shrink. Width is a list of bounding values. See the section +BOUNDING + for a description of this list. By default there are no constraints. +
+
+
+ +

Row Options

+To configure a row in the table, specify the row index as Ri, +where i is the index of the row to be configured.
+

+table configure container Ri ?option value?...
+

If the index is specified as R*, then all rows of the table will be configured. + The following options are available for table rows.

+
+ +
-height height
+
Specifies +the limits of the height that the row may expand or shrink to. Height is +a list of bounding values. See the section BOUNDING + for a description +of this list. By default there are no constraints.
+ +
-pady pad
+
Sets the padding +above and below the row. Pad can be a list of one or two numbers. If pad +has two elements, the area above the row is padded by the first value and +the area below by the second value. If pad is just one number, both the +top and bottom areas are padded by the value. The default is 0.
+ +
-resize mode +
+
Indicates that the row can expand or shrink from its requested height +when the table is resized. Mode must be one of the following: none, expand, +shrink, or both. If mode is expand the height of the row is expanded if +there is extra space in the container. If mode is shrink its height may +be reduced beyond its requested height if there is not enough space in + the container. The default is none.
+
+
+ +

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 wm command +lets you specify a minsize and maxsize for a toplevel window, you can bound +the sizes the container, a widget, row, or column may take. The -width, -height, +-reqwidth, and -reqheight 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.
+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
+

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.

+How the elements of the list are interpreted +is dependent upon the number of elements in the list.

+
+ +
{}
+
Empty list. No +bounds are set. The default sizing is performed.
+ +
{ x }
+
Fixes the size to +x. The window or partition cannot grow or shrink.
+ +
{ min max }
+
Sets up minimum +and maximum limits for the size of the window or partition. The window +or partition can be reduced less than min, nor can it be stretched beyond +max.
+ +
{ min max nom }
+
Specifies minimum and maximum size limits, but also +specifies a nominal size nom. This overrides the calculated size of the +window or partition.
+
+
+ +

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.
+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
+ +

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. +
+ table .f1
+    ...
+ pack .f2
+    ...
+ grid .f3
+

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. +

Keywords

+frame, geometry manager, +location, table, size

+

+ +


+Table of Contents

+

+ 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 @@ + + + + + +tabset(n) manual page + + +Table of Contents

+ +

Name

+tabset - Create and manipulate tabset widgets + +

Synopsis

+tabset pathName ?options? +

Description

+The tabset 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.

+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". +

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 frame because they are too large. + The other alternative is to pack the graphs into several toplevel 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.

+The tabset +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 tabset 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.

+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. +

Example

+You +create a tabset widget with the tabset command.
+# Create a new tabset
+tabset .ts -relief sunken -borderwidth 2
+

A new Tcl command .ts 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 configure operation. +
+# Change the default font.
+.ts configure -font "fixed"
+

You can then add folders using the insert operation.
+# Create a new folder "f1"
+.ts insert 0 "f1"
+

This inserts the new tab named "f1" into the tabset. The index 0 indicates +location to insert the new tab. You can also use the index end 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 -text option.
+# Change the label of "f1"
+.ts tab configure "f1" -label "Tab #1"
+

The insert operation lets you add one or more folders at a time.
+.ts insert end "f2" -label "Tab #2" "f3" "f4"
+

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 tab configure operation.
+# 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
+

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 -window +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. +
+graph .ts.graph
+.ts tab configure "f1" -window ".ts.graph" \
+ -fill both -padx 0.25i -pady 0.25i
+

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 -pagewidth +and -pageheight options override this.

+Other options control how the widget +appears in the folder. The -fill option says that you wish to have the widget +stretch to fill the available space in the folder.
+.ts tab configure "f1" -fill both -padx 0.25i -pady 0.25i
+

+

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.

+If you want to share a page between two different +folders, the -command option lets you specify a Tcl command to be invoked +whenever the folder is selected. You can reset the -window option for the +tab whenever it's clicked.
+.ts tab configure "f2" -command {
+ .ts tab configure "f2" -window ".ts.graph"
+}
+.ts tab configure "f1" -command {
+ .ts tab configure "f1" -window ".ts.graph"
+}
+

If you have many folders, you may wish to stack tabs in multiple tiers. + The tabset's -tiers option requests a maximum number of tiers. The default +is one tier.
+.ts configure -tiers 2
+

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.
+.ts configure -scrollcommand { .sbar set } -scrollincrement 20
+.sbar configure -orient horizontal -command { .ts view }
+

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 -side option.
+# Arrange tabs along the right side of the tabset.
+.ts configure -side right -rotate 270
+ +

Syntax

+The tabset command creates a new window using the pathName argument +and makes it into a tabset widget.
+

+tabset pathName ?option value?...
+

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 tabset command returns its pathName argument. At the time +this command is invoked, there must not exist a window named pathName, +but pathName's parent must exist.

+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 -scrollcommand option. +They also support scanning (see the scan operation). Tabs may be arranged +along any side of the tabset window using the -side option.

+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.

+You can override either dimension with the tabset's -width and +-height options. +

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: +
+ +
number
+
Unique node +id of the tab.
+ +
@x,y
+
Tab that covers the point in the tabset window specified +by x and y (in screen coordinates). If no tab covers that point, then the +index is ignored.
+ +
select
+
The currently selected tab. The select index is + typically changed by either clicking on the tab with the left mouse button +or using the widget's invoke operation.
+ +
active
+
The tab where the mouse pointer +is currently located. The label is drawn using its active colors (see +the -activebackground and -activeforeground options). The active index is +typically changed by moving the mouse pointer over a tab or using the widget's +activate 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.
+ +
focus +
+
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 focus operation. If +no tab has focus, then the index is ignored.
+ +
down
+
Tab immediately below +the tab that currently has focus, if there is one. If there is no tab below, +the current tab is returned.
+ +
left
+
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.
+ +
right
+
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.
+ +
up
+
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.
+ +
end
+
Last tab in the tabset. If there are no tabs in the +tabset then the index is ignored.
+
+

+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. +

Tabset +Operations

+All tabset operations are invoked by specifying the widget's pathname, +the operation, and any arguments that pertain to that operation. The general +form is:

+
+

+    pathName operation ?arg arg ...?
+

+

Operation and the args determine the exact behavior of the command. The +following operations are available for tabset widgets: +

+ +
pathName activate +index
+
Sets the active tab to the one indicated by index. The active tab +is drawn with its active colors (see the -activebackground and -activeforeground +options) and may be retrieved with the index active. Only one tab may be +active at a time. If index is the empty string, then all tabs will be drawn +with their normal foreground and background colors.
+ +
pathName bind tagName +?sequence? ?command?
+
Associates command with tagName such that whenever +the event sequence given by sequence occurs for a tab with this tag, command +will be invoked. The syntax is similar to the bind command except that +it operates on tabs, rather than widgets. See the bind manual entry for +complete details on sequence and the substitutions performed on command. +

+If all arguments are specified then a new binding is created, replacing + any existing binding for the same sequence and tagName. If the first character +of command is + then command augments an existing binding rather than +replacing it. If no command argument is provided then the command currently +associated with tagName and sequence (it's an error occurs if there's no +such binding) is returned. If both command and sequence are missing then +a list of all the event sequences for which bindings have been defined +for tagName.

+ +
pathName cget option
+
Returns the current value of the configuration +option given by option. Option may have any of the values accepted by the +configure operation described below.
+ +
pathName configure ?option? ?value +option value ...?
+
Query or modify the configuration options of the widget. +If no option is specified, returns a list describing all the available +options for pathName (see Tk_ConfigureInfo for information on the format +of this list). If option is specified with no value, 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 option is specified). + If one or more option-value 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. Option and value are described below:
+ +
-activebackground +color
+
Sets the default active background color for tabs. A tab is active +when the mouse is positioned over it or set by the activate operation. +Individual tabs may override this option by setting the tab's -activebackground +option.
+ +
-activeforeground color
+
Sets the default active foreground color +for tabs. A tab is active when the mouse is positioned over it or set by +the activate operation. Individual tabs may override this option by setting +the tab's -activeforeground option.
+ +
-background color
+
Sets the background color +of the tabset.
+ +
-borderwidth pixels
+
Sets the width of the 3-D border around +the outside edge of the widget. The -relief option determines how the border +is to be drawn. The default is 2.
+ +
-cursor cursor
+
Specifies the widget's cursor. + The default cursor is "".
+ +
-dashes dashList
+
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. DashList 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 dashList is "", the outline will be +a solid line. The default value is 5 2.
+ +
-font fontName
+
Sets the default +font for the text in tab labels. Individual tabs may override this by setting +the tab's -font option. The default value is *-Helvetica-Bold-R-Normal-*-12-120-*. +
+ +
-foreground color
+
Sets the default color of tab labels. Individual tabs +may override this option by setting the tab's -foreground option. The default +value is black.
+ +
-gap size
+
Sets the gap (in pixels) between tabs. The default +value is 2.
+ +
-height pixels
+
Specifies the requested height of widget. If pixels +is 0, then the height of the widget will be calculated based on the size +the tabs and their pages. The default is 0.
+ +
-highlightbackground color
+
Sets +the color to display in the traversal highlight region when the tabset +does not have the input focus.
+ +
-highlightcolor color
+
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 black.
+ +
-highlightthickness pixels +
+
Sets the width of the highlight rectangle to draw around the outside of + the widget when it has the input focus. Pixels is a non-negative value +and may have any of the forms acceptable to Tk_GetPixels. If the value is +zero, no focus highlight is drawn around the widget. The default is 2.
+ +
-pageheight +pixels
+
Sets the requested height of the page. The page is the area under +the tab used to display the page contents. If pixels is 0, the maximum +height of all embedded tab windows is used. The default is 0.
+ +
-pagewidth +pixels
+
Sets the requested width of the page. The page is the area under +the tab used to display the page contents. If pixels is 0, the maximum +width of all embedded tab windows is used. The default is 0.
+ +
-relief relief +
+
Specifies the 3-D effect for the tabset widget. Relief specifies how the +tabset should appear relative to widget that it is packed into; for example, +raised means the tabset should appear to protrude. The default is sunken. +
+ +
-rotate theta
+
Specifies the degrees to rotate text in tab labels. Theta is +a real value representing the number of degrees to rotate the tick labels. + The default is 0.0 degrees.
+ +
-samewidth boolean
+
Indicates if each tab should +be the same width. If true, each tab will be as wide as the widest tab. + The default is no.
+ +
-scrollcommand string
+
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.
+ +
-scrollincrement pixels
+
Sets the smallest number +of pixels to scroll the tabs. If pixels 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).
+ +
-selectbackground color
+
Sets the +color to use when displaying background of the selected tab. Individual +tabs can override this option by setting the tab's -selectbackground option. +
+ +
-selectborderwidth pixels
+
Sets the width of the raised 3-D border to draw +around the label of the selected tab. Pixels must be a non-negative value. +The default value is 1.
+ +
-selectcommand string
+
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 -command option. The default value is "".
+ +
-selectforeground color +
+
Sets the default color of the selected tab's text label. Individual tabs +can override this option by setting the tab's -selectforeground option. The +default value is black.
+ +
-selectpad pixels
+
Specifies extra padding to be displayed +around the selected tab. The default value is 3.
+ +
-side side
+
Specifies the +side of the widget to place tabs. The following values are valid for side. +The default value is top.
+ +
top
+
Tabs are drawn along the top.
+ +
left
+
Tabs are +drawn along the left side.
+ +
right
+
Tabs are drawn along the right side.
+ +
both +
+
Tabs are drawn along the bottom side.
+
+ + +
+ +
-slant slant
+
Specifies if the tabs +should be slanted 45 degrees on the left and/or right sides. The following +values are valid for slant. The default is none.
+ +
none
+
Tabs are drawn as +a rectangle.
+ +
left
+
The left side of the tab is slanted.
+ +
right
+
The right +side of the tab is slanted.
+ +
both
+
Boths sides of the tab are slanted.
+
+ + +
+ +
-tabbackground +color
+
Sets the default background color of tabs. Individual tabs can override +this option by setting the tab's -background option.
+ +
-tabborderwidth pixels +
+
Sets the width of the 3-D border around the outside edge of the tab. The +-tabrelief option determines how the border is to be drawn. The default +is 2.
+ +
-tabforeground color
+
Specifies the color to use when displaying a tab's +label. Individual tabs can override this option by setting the tab's -foreground +option.
+ +
-tabrelief relief
+
Specifies the 3-D effect for both tabs and folders. + Relief specifies how the tabs should appear relative to background of +the widget; for example, raised means the tab should appear to protrude. + The default is raised.
+ +
-takefocus focus
+
Provides information used when +moving the focus from window to window via keyboard traversal (e.g., Tab +and Shift-Tab). If focus is 0, this means that this window should be skipped +entirely during keyboard traversal. 1 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 1.
+ +
-textside +side
+
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 +left, right, top, and bottom. The default value is left.
+ +
-tiers number
+
Specifies +the maximum number of tiers to use to display the tabs. The default value +is 1.
+ +
-tile image
+
Specifies a tiled background for the widget. If image +isn't "", the background is tiled using image. Otherwise, the normal background +color is drawn (see the -background option). Image must be an image created +using the Tk image command. The default is "".
+ +
-width pixels
+
Specifies the +requested width of the widget. If pixels is 0, then the width of the widget +will be calculated based on the size the tabs and their pages. The default +is 0.
+
+ + +
+ +
pathName delete first ?last?
+
Deletes one or more tabs from the tabset. + First and last are the first and last indices, defining a range of tabs +to be deleted. If last isn't specified, then only the tab at first is +deleted.
+ +
pathName focus index
+
Designates a tab to get the widget's focus. + This tab is displayed with a dashed line around its label.
+ +
pathName get +index
+
Returns the name of the tab. The value of index may be in any form +described in the section TABSET INDICES +
+ +
pathName index ?flag? string +
+
Returns the node id of the tab specified by string. If flag is -name, then +string is the name of a tab. If flag is -index, string is an index such as +"active" or "focus". If flag isn't specified, it defaults to -index.
+ +
pathName +insert position name ?option value?...
+
Inserts new tabs into the tabset. Tabs +are inserted just before the tab given by position. Position may be either +a number, indicating where in the list the new tab should be added, or +end, indicating that the new tab is to be added the end of the list. Name +is the symbolic name of the tab. Be careful not to use a number. Otherwise +the tabset will confuse it with tab indices. Returns a list of indices +for all the new tabs.
+ +
pathName invoke index
+
Selects the tab given by index, +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 -state +option) is disabled.
+ +
pathName move index before|after index
+
Moves the tab +index to a new position in the tabset.
+ +
pathName nearest x y
+
Returns the +name of the tab nearest to given X-Y screen coordinate.
+ +
pathName scan option +args
+
This command implements scanning on tabsets. It has two forms, depending +on option:
+ +
pathName scan mark x y
+
Records x and y and the current view +in the tabset window; used with later scan dragto commands. Typically this +command is associated with a mouse button press in the widget. It returns +an empty string.
+ +
pathName scan dragto x y.
+
This command computes the difference +between its x and y arguments and the x and y arguments to the last scan +mark 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.
+
+ + +
+ +
pathName +see index
+
Scrolls the tabset so that the tab index is visible in the widget's +window.
+ +
pathName size
+
Returns the number of tabs in the tabset.
+ +
pathName +tab operation ?args?
+
See the TAB OPERATIONS + section below.
+ +
pathName view +args
+
This command queries or changes the position of the tabset in the +widget's window. It can take any of the following forms:
+ +
pathName view +
+
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 view 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 -scrollcommand option. +
+ +
pathName view moveto fraction
+
Adjusts the view in the window so that fraction +of the total width of the tabset text is off-screen to the left. fraction +must be a number between 0.0 and 1.0.
+ +
pathName view scroll number what
+
This +command shifts the view in the window (left/top or right/bottom) according +to number and what. Number must be an integer. What must be either units +or pages or an abbreviation of these. If what is units, the view adjusts +left or right by number scroll units (see the -scrollincrement option). +; if it is pages then the view adjusts by number widget windows. If number +is negative then tabs farther to the left become visible; if it is positive +then tabs farther to the right become visible.
+
+ + +

Tab Operations

+ +
+ +
pathName tab +cget nameOrIndex option
+
Returns the current value of the configuration +option given by option. Option may have any of the values accepted by the +tab configure operation described below.
+ +
pathName tab configure nameOrIndex +?nameOrIndex...? option? ?value option value ...?
+
Query or modify the configuration +options of one or more tabs. If no option is specified, this operation returns +a list describing all the available options for nameOrIndex. NameOrIndex +can be either the name of a tab or its index. Names of tabs take precedence +over their indices. That means a tab named focus is picked over the "focus" +tab.
+
+

+If option is specified, but not value, then a list describing the one +named option is returned. If one or more option-value pairs are specified, +then each named tab (specified by nameOrIndex) will have its configurations +option(s) set the given value(s). In this last case, the empty string is +returned. Option and value are described below:

+
+ +
-activebackground color
+
Sets +the active background color for nameOrIndex. A tab is active when the mouse +is positioned over it or set by the activate operation. This overrides +the widget's -activebackground option.
+ +
-activeforeground color
+
Sets the default +active foreground color nameOrIndex. A tab is "active" when the mouse is +positioned over it or set by the activate operation. Individual tabs may +override this option by setting the tab's -activeforeground option.
+ +
-anchor +anchor
+
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. Anchor specifies how +the widget will be positioned in the extra space. For example, if anchor +is center then the window is centered in the folder ; if anchor is w then +the window will be aligned with the leftmost edge of the folder. The default +value is center.
+ +
-background color
+
Sets the background color for nameOrIndex. + Setting this option overides the widget's -tabbackground option.
+ +
-bindtags +tagList
+
Specifies the binding tags for this tab. TagList 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 all.
+ +
-command string +
+
Specifies a Tcl script to be associated with nameOrIndex. This command +is typically invoked when left mouse button is released over the tab. +Setting this option overrides the widget's -selectcommand option.
+ +
-data string +
+
Specifies a string to be associated with nameOrIndex. 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 "". +
+ +
-fill fill
+
If the space in the folder surrounding the tab's embedded widget +is larger than the widget, then fill indicates if the embedded widget + should be stretched to occupy the extra space. Fill is either none, x, +y, both. For example, if fill is x, then the widget is stretched horizontally. + If fill is y, the widget is stretched vertically. The default is none. +
+ +
-font fontName
+
Sets the font for the text in tab labels. If fontName is +not the empty string, this overrides the tabset's -font option. The default +value is "".
+ +
-foreground color
+
Sets the color of the label for nameOrIndex. + If color is not the empty string, this overrides the widget's -tabforeground + option. The default value is "".
+ +
-image imageName
+
Specifies the image to +be drawn in label for nameOrIndex. If image is "", no image will be drawn. + Both text and images may be displayed at the same time in tab labels. The +default value is "".
+ +
-ipadx pad
+
Sets the padding to the left and right of +the label. Pad can be a list of one or two screen distances. If pad has +two elements, the left side of the label is padded by the first distance +and the right side by the second. If pad has just one distance, both the +left and right sides are padded evenly. The default value is 0.
+ +
-ipady pad +
+
Sets the padding to the top and bottom of the label. Pad can be a list of +one or two screen distances. If pad has two elements, the top of the label +is padded by the first distance and the bottom by the second. If pad has +just one distance, both the top and bottom sides are padded evenly. The +default value is 0.
+ +
-padx pad
+
Sets the padding around the left and right +of the embedded widget, if one exists. Pad can be a list of one or two +screen distances. If pad has two elements, the left side of the widget +is padded by the first distance and the right side by the second. If pad +has just one distance, both the left and right sides are padded evenly. + The default value is 0.
+ +
-pady pad
+
Sets the padding around the top and bottom +of the embedded widget, if one exists. Pad can be a list of one or two screen +distances. If pad has two elements, the top of the widget is padded by +the first distance and the bottom by the second. If pad has just one distance, +both the top and bottom sides are padded evenly. The default value is 0. +
+ +
-selectbackground color
+
Sets the color to use when displaying background +of the selected tab. If color is not the empty string, this overrides the +widget's -selectbackground option. The default value is "".
+ +
-shadow color
+
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 color is the empty string, no shadow is drawn. The default value is "". +
+ +
-state state
+
Sets the state of the tab. If state is disable the text of the +tab is drawn as engraved and operations on the tab (such as invoke and +tab tearoff) are ignored. The default is normal.
+ +
-stipple bitmap
+
Specifies +a stipple pattern to use for the background of the folder when the window +is torn off. Bitmap specifies a bitmap to use as the stipple pattern. The +default is BLT.
+ +
-text text
+
Specifies the text of the tab's label. The exact +way the text is drawn may be affected by other options such as -state or +-rotate.
+ +
-window pathName
+
Specifies the widget to be embedded into the tab. + PathName must be a child of the tabset widget. The tabset will "pack" +and manage the size and placement of pathName. The default value is "". +
+ +
-windowheight pixels
+
Sets the requested height of the page. The page is +the area under the tab used to display the page contents. If pixels is +0, the maximum height of all embedded tab windows is used. The default +is 0.
+ +
-windowwidth pixels
+
Sets the requested width of the page. The page +is the area under the tab used to display the page contents. If pixels +is 0, the maximum width of all embedded tab windows is used. The default +is 0.
+
+
+ +
+ +
pathName tab names ?pattern?
+
Returns the names of all the tabs matching +the given pattern. If no pattern argument is provided, then all tab names +are returned.
+ +
pathName tab tearoff index ?newName?
+
Reparents the widget +embedded into index, placing it inside of newName. NewName is either the +name of an new widget that will contain the embedded widget or the name +of the tabset widget. It the last case, the embedded widget is put back +into the folder.

+If no newName argument is provided, then the name of the +current parent of the embedded widget is returned.

+
+ +

Default Bindings

+

+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 Tabset): +

+ +
<ButtonPress-2>
+
+ +
<B2-Motion>
+
+ +
<ButtonRelease-2>
+
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. +
+ +
<KeyPress-Up>
+
+ +
<KeyPress-Down>
+
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.
+ +
<KeyPress-Left>
+
+ +
<KeyPress-Right>
+
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.
+ +
<KeyPress-space>
+
+ +
<KeyPress-Return>
+
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. +
+
+

+Each tab, by default, also has a set of bindings (via the tag all). These +bindings may be reset using the tabset's bind operation. +

+ +
<Enter>
+
+ +
<Leave>
+
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. +
+ +
<ButtonRelease-1>
+
Clicking with the left mouse button on a tab causes the tab +to be selected and its Tcl script (see the -command or -selectcommand options) +to be invoked. The folder and any embedded widget (if one is specified) +is automatically mapped.
+ +
<ButtonRelease-3>
+
+ +
<Control-ButtonRelease-1>
+
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.
+
+ +

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 Enter, Leave, ButtonPress, Motion, +and KeyPress).

+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 -bindtags +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.

+The -bindtags 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 -bindtags option doesn't change this. +

Keywords

+tabset, +widget

+ +


+Table of Contents

+

+ 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 @@ + + + + + +tile(n) manual page + + +Table of Contents

+ +

Name

+tile - Tiling versions of Tk widgets +

Synopsis

+

+tile::button +pathName option value...

+tile::checkbutton pathName option value...

+tile::frame +pathName option value...

+tile::label pathName option value...

+tile::radiobutton +pathName option value...

+tile::scrollbar pathName option value...

+tile::toplevel +pathName option value...

+ +

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. +

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. +

+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. +

Example

+To add tiling +to a widget, you simply create an image using Tk's image command and use +the image name as the value for the -tile configuration option of the widget. +
+image create photo my_texture -file tan_paper.gif
+blt::tile::frame .f -tile my_texture
+

The image my_texture is added to the frame. If my_texture is updated, +so will the widget background.
+image create photo my_texture -file rain.gif
+

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. +
+image create photo my_texture -file tan_paper.gif
+namespace import -force blt::tile::*
+frame .f -tile my_texture
+

To use one image for all texturing, you can use the "Tile" option class +name to specify the same image for all tile widgets.
+image create photo my_texture -file tan_paper.gif
+option add *Tile my_texture
+ +

Options

+The following configurations options are added to the widgets. If +a -tile or -activetile option is specified, it overrides the background color +of the widget. +
+ +
-activetile image
+
Specifies a textured background to display +when the widget is active. This option is available for the tilebutton, +tilecheckbutton, tileradiobutton, and tilescrollbar widgets. Image is the +name an image created using Tk's image command. The background of the widget +is tiled with image. If image is "", then the active background color is +displayed. The default is "".
+ +
-tile image
+
Specifies a textured background +to display for the widget. Image is the name an image created using Tk's +image command. The background of the widget is tiled with image. If image +is "", then the normal background color is displayed. The default is "". +
+
+ +

Keywords

+tile, texture, button, label, radiobutton, checkbutton, scrollbar, +frame, toplevel

+ +


+Table of Contents

+

+ 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 @@ + + + + + +tree(n) manual page + + +Table of Contents

+ +

Name

+tree - Create and manage tree data objects. +

Synopsis

+blt::tree +create ?treeName?

+blt::tree destroy treeName...

+blt::tree names ?pattern? + +

Description

+The tree command creates tree data objects. A tree object 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. +

Introduction

+

+ +

Example

+

+ +

Syntax

+ +
+ +
tree create ?treeName? +
+
Creates a new tree object. The name of the new tree is returned. If no +treeName argument is present, then the name of the tree is automatically +generated in the form "tree0", "tree1", etc. If the substring "#auto" is +found in treeName, it is automatically substituted by a generated name. + For example, the name .foo.#auto.bar will be translated to .foo.tree0.bar.

+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 treeName. 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 treeName contains a namespace qualifier, +such as "fred::myTree".

+ +
tree destroy treeName...
+
Releases one of more trees. + The Tcl command associated with treeName is also removed. Trees are reference +counted. The internal tree data object isn't destroyed until no one else +is using the tree.
+ +
tree names ?pattern?
+
Returns the names of all tree objects. + if a pattern argument is given, then the only those trees whose name matches +pattern will be listed.
+
+ +

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.

+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, "x123" is valid, +but "123" isn't. The same tag may be associated with many different nodes. +This is commonly done to group nodes in various interesting ways.

+There +are two built-in tags: The tag all 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 root is managed automatically by the tree object. It applies +to the node current set as root.

+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 node 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 node is +specified in a way that names multiple items, then an error "refers to +more than one node" is generated. +

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, "root->firstchild" selects the first subtree of the root node. +

+The following modifiers are available:

+
+ +
firstchild
+
Selects the first child +of the node.
+ +
lastchild
+
Selects the last child of the node.
+ +
next
+
Selects +the next node in preorder to the node.
+ +
nextsibling
+
Selects the next sibling +of the node.
+ +
parent
+
Selects the parent of the node.
+ +
previous
+
Selects +the previous node in preorder to the node.
+ +
prevsibling
+
Selects the previous +sibling of the node.
+ +
"label"
+
Selects the node whose label is label. Enclosing +label in quotes indicates to always search for a node by its label (for +example, even if the node is labeled "parent").
+
+
+

+It's an error the node can't +be found. For example, lastchild and firstchild will generate errors if +the node has no children. The exception to this is the index operation. +You can use index to test if a modifier is valid. +

Tree Operations

+Once you +create a tree object, you can use its Tcl command to query or modify it. + The general form is
+

+treeName operation ?arg?...
+

Both operation and its arguments determine the exact behavior of the command. + The operations available for trees are listed below. +

+ +
treeName ancestor +node1 node2
+
Returns the mutual ancestor of the two nodes node1 and node2. + The ancestor can be one of the two nodes. For example, if node1 and node2 +are the same nodes, their ancestor is node1.
+ +
treeName apply node ?switches? +
+
Runs commands for all nodes matching the criteria given by switches for +the subtree designated by node. By default all nodes match, but you can +set switches to narrow the match. This operation differs from find 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.

+The -exact, -glob, + and -regexp 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 -path 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 -key switch is used, it designates the data field to be matched.

+The +valid switches are listed below:

+ +
-depth number
+
Descend at most number (a +non-negative integer) levels If number is 1 this means only apply the tests +to the children of node.
+ +
-exact string
+
Matches each node using string. The +node must match string exactly.
+ +
-glob string
+
Test each node to string using +global pattern matching. Matching is done in a fashion similar to that +used by the C-shell.
+ +
-invert
+
Select non-matching nodes. Any node that doesn't +match the given criteria will be selected.
+ +
-key key
+
If pattern matching is +selected (using the -exact, -glob, or -regexp switches), compare the values +of the data field keyed by key instead of the node's label. If no pattern +matching switches are set, then any node with this data key will match. +
+ +
-leafonly
+
Only test nodes with no children.
+ +
-nocase
+
Ignore case when matching +patterns.
+ +
-path
+
Use the node's full path when comparing nodes.
+ +
-precommand command +
+
Invoke command for each matching node. Before command is invoked, the id +of the node is appended. You can control processing by the return value +of command. If command generates an error, processing stops and the find +operation returns an error. But if command returns break, then processing +stops, no error is generated. If command returns continue, then processing +stops on that subtree and continues on the next.
+ +
-postcommand command
+
Invoke +command for each matching node. Before command is invoked, the id of the +node is appended. You can control processing by the return value of command. + If command generates an error, processing stops and the find operation + returns an error. But if command returns break, then processing stops, +no error is generated. If command returns continue, then processing stops +on that subtree and continues on the next.
+ +
-regexp string
+
Test each node +using string as a regular expression pattern.
+ +
-tag string
+
Only test nodes +that have the tag string.
+
+ + +
+ +
treeName attach treeObject
+
Attaches to an existing +tree object treeObject. This is for cases where the tree object was previously +created via the C API. The current tree associated with treeName is discarded. + In addition, the current set of tags, notifier events, and traces are +removed.
+ +
treeName children node
+
Returns a list of children for node. If +node is a leaf, then an empty string is returned.
+ +
treeName copy srcNode +?destTree? destNode ?switches?
+
Copies srcNode into destNode. Both nodes +srcNode and destNode must already exist. If destTree argument is present, +it indicates the name of the destination tree. By default both the source +and destination trees are the same. The valid switches are listed below: +
+ +
-overwrite
+
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.
+ +
-recurse
+
Recursively +copy all the subtrees of srcNode as well. In this case, srcNode can't be +an ancestor of destNode as it would result in a cyclic copy.
+ +
-tags
+
Copy tag +inforation. Normally the following node is copied: its label and data +fields. This indicates to copy tags as well.
+
+ + +
+ +
treeName degree node
+
Returns +the number of children of node.
+ +
treeName delete node...
+
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.
+ +
treeName depth node
+
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 0.
+ +
treeName dump node
+
Returns a list of the paths and +respective data for node and its descendants. The subtree designated by +node is traversed returning the following information for each node: 1) +the node's path relative to node, 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 restore operation.
+ +
treeName +dumpfile node fileName
+
Writes a list of the paths and respective data for +node and its descendants to the given file fileName. The subtree designated +by node is traversed returning the following information for each node: +1) the node's path relative to node, 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 restore operation.
+ +
treeName +exists node ?key?
+
Indicates if node exists in the tree. If a key argument +is present then the command also indicates if the named data field exists. +
+ +
treeName find node ?switches?
+
Finds for all nodes matching the criteria +given by switches for the subtree designated by node. A list of the selected + nodes is returned. By default all nodes match, but you can set switches +to narrow the match.

+The -exact, -glob, and -regexp 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 -path switch is +present, it will match the full path of the node. If the -key switch is +used, it designates the data field to be matched.

+The order in which +the nodes are traversed is controlled by the -order switch. The possible +orderings are preorder, postorder, inorder, and breadthfirst. The default +is postorder.

+The valid switches are listed below:

+ +
-addtag string
+
Add the +tag string to each selected node.
+ +
-count number
+
Stop processing after number +(a positive integer) matches.
+ +
-depth number
+
Descend at most number (a non-negative +integer) levels If number is 1 this means only apply the tests to the children +of node.
+ +
-exact string
+
Matches each node using string. The node must match +string exactly.
+ +
-exec command
+
Invoke command for each matching node. Before +command is invoked, the id of the node is appended. You can control processing +by the return value of command. If command generates an error, processing +stops and the find operation returns an error. But if command returns +break, then processing stops, no error is generated. If command returns + continue, then processing stops on that subtree and continues on the next. +
+ +
-glob string
+
Test each node to string using global pattern matching. Matching +is done in a fashion similar to that used by the C-shell.
+ +
-invert
+
Select non-matching +nodes. Any node that doesn't match the given criteria will be selected.
+ +
-key +key
+
If pattern matching is selected (using the -exact, -glob, or -regexp switches), +compare the values of the data field keyed by key instead of the node's +label. If no pattern matching switches are set, then any node with this +data key will match.
+ +
-leafonly
+
Only test nodes with no children.
+ +
-nocase
+
Ignore +case when matching patterns.
+ +
-order string
+
Traverse the tree and process +nodes according to string. String can be one of the following:
+ +
breadthfirst +
+
Process the node and the subtrees at each sucessive level. Each node on +a level is processed before going to the next level.
+ +
inorder
+
Recursively +process the nodes of the first subtree, the node itself, and any the remaining +subtrees.
+ +
postorder
+
Recursively process all subtrees before the node.
+ +
preorder +
+
Recursively process the node first, then any subtrees.
+
+ + +
+ +
-path
+
Use the node's +full path when comparing nodes.
+ +
-regexp string
+
Test each node using string +as a regular expression pattern.
+ +
-tag string
+
Only test nodes that have the +tag string.
+
+ + +
+ +
treeName findchild node label
+
Searches for a child node Ilabel +in node. The id of the child node is returned if found. Otherwise -1 is +returned.
+ +
treeName firstchild node
+
Returns the id of the first child in +the node's list of subtrees. If node is a leaf (has no children), then +-1 is returned.
+ +
treeName get node ?key? ?defaultValue?
+
Returns a list of +key-value pairs of data for the node. If key is present, then onlyx the +value for that particular data field is returned. It's normally an error +if node does not contain the data field key. But if you provide a defaultValue +argument, this value is returned instead (node will still not contain key). + This feature can be used to access a data field of node without first +testing if it exists. This operation may trigger read data traces.
+ +
treeName +index node
+
Returns the id of node. If node is a tag, it can only specify +one node. If node does not represent a valid node id or tag, or has modifiers +that are invalid, then -1 is returned.
+ +
treeName insert parent ?switches? +
+
Inserts a new node into parent node parent. The id of the new node is +returned. The following switches are available:
+ +
-at number
+
Inserts the +node into parent's list of children at position number. The default is +to append node.
+ +
-data dataList
+
Sets the value for each data field in dataList +for the new node. DataList is a list of key-value pairs.
+ +
-label string
+
Designates +the labels of the node as string. By default, nodes are labeled as node0, +node1, etc.
+ +
-tags tagList
+
Adds each tag in tagList to the new node. TagList +is a list of tags, so be careful if a tag has embedded space.
+
+ + +
+ +
treeName is +property args
+
Indicates the property of a node. Both property and args +determine the property being tested. Returns 1 if true and 0 otherwise. + The following property and args are valid:
+ +
ancestor node1 node2
+
Indicates +if node1 is an ancestor of node2.
+ +
before node1 node2
+
Indicates if node1 +is before node2 in depth first traversal.
+ +
leaf node
+
Indicates if node is +a leaf (it has no subtrees).
+ +
root node
+
Indicates if node is the designated +root. This can be changed by the root operation.
+
+ + +
+ +
treeName label node ?newLabel? +
+
Returns the label of the node designated by node. If newLabel is present, +the node is relabeled using it as the new label.
+ +
treeName lastchild node +
+
Returns the id of the last child in the node's list of subtrees. If node +is a leaf (has no children), then -1 is returned.
+ +
treeName move node newParent +?switches?
+
Moves node into newParent. Node is appended to the list children +of newParent. Node can not be an ancestor of newParent. The valid flags +for switches are described below.
+ +
-after child
+
Position node after child. + The node child must be a child of newParent.
+ +
-at number
+
Inserts node into +parent's list of children at position number. The default is to append the +node.
+ +
-before child
+
Position node before child. The node child must be a + child of newParent.
+
+ + +
+ +
treeName next node
+
Returns the next node from node +in a preorder traversal. If node is the last node in the tree, then -1 is +returned.
+ +
treeName nextsibling node
+
Returns the node representing the next +subtree from node in its parent's list of children. If node is the last +child, then -1 is returned.
+ +
treeName notify args
+
Manages notification events +that indicate that the tree structure has been changed. See the NOTIFY +OPERATIONS + section below.
+ +
treeName parent node
+
Returns the parent node +of node. If node is the root of the tree, then -1 is returned.
+ +
treeName +path node
+
Returns the full path (from root) of node.
+ +
treeName position node +
+
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.
+ +
treeName +previous node
+
Returns the previous node from node in a preorder traversal. +If node is the root of the tree, then -1 is returned.
+ +
treeName prevsibling +node
+
Returns the node representing the previous subtree from node in its +parent's list of children. If node is the first child, then -1 is returned. +
+ +
treeName restore node dataString switches
+
Performs the inverse function +of the dump operation, restoring nodes to the tree. The format of dataString +is exactly what is returned by the dump 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 node. 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 switches are listed below:
+ +
-overwrite
+
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.
+
+ + +
+ +
treeName restorefile node fileName switches
+
Performs +the inverse function of the dumpfile operation, restoring nodes to the +tree from the file fileName. The format of fileName is exactly what is +returned by the dumpfile 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 node. 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 switches are listed below:
+ +
-overwrite
+
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.
+
+ + +
+ +
treeName root ?node?
+
Returns the id of the root node. + Normally this is node 0. If a node 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 next, path, previous, +etc.
+ +
treeName set node key value ?key value...?
+
Sets one or more data fields +in node. Node may be a tag that represents several nodes. Key is the name +of the data field to be set and value is its respective value. This operation +may trigger write and create data traces.
+ +
treeName size node
+
Returns the +number of nodes in the subtree. This includes the node and all its descendants. + The size of a leaf node is 1.
+ +
treeName sort node ?switches?
+
+ +
-ascii
+
Compare +strings using the ASCII collation order.
+ +
-command string
+
Use command string +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.
+ +
-decreasing
+
Sort in decreasing order (largest +items come first).
+ +
-dictionary
+
Compare strings using a dictionary-style comparison. + This is the same as -ascii 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 -dictionary mode, bigBoy sorts +between bigbang and bigboy, and x10y sorts between x9y and x11y.
+ +
-integer +
+
Compare the nodes as integers.
+ +
-key string
+
Sort based upon the node's data +field keyed by string. Normally nodes are sorted according to their label. +
+ +
-path
+
Compare the full path of each node. The default is to compare only +its label.
+ +
-real
+
Compare the nodes as real numbers.
+ +
-recurse
+
Recursively sort +the entire subtree rooted at node.
+ +
-reorder
+
Recursively sort subtrees for +each node. Warning. Unlike the normal flat sort, where a list of nodes +is returned, this will reorder the tree.
+
+ + +
+ +
treeName tag args
+
Manages tags +for the tree object. See the TAG OPERATIONS + section below.
+ +
treeName trace +args
+
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 TRACE OPERATIONS + section below.
+ +
treeName unset +node key...
+
Removes one or more data fields from node. Node may be a tag that +represents several nodes. Key is the name of the data field to be removed. + It's not an error is node does not contain key. This operation may trigger +unset data traces.
+
+ + +

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.

+There are two built-in tags: The tag all 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 root is managed automatically +by the tree object. It specifies the node that is currently set as the +root of the tree.

+Most tree operations use tags. And several operations +let you operate on multiple nodes at once. For example, you can use the +set operation with the tag all to set a data field in for all nodes in +the tree.

+Tags are invoked by the tag operation. The general form is
+

+treeName tag operation ?arg?...
+

Both operation and its arguments determine the exact behavior of the command. + The operations available for tags are listed below. +

+ +
treeName tag add string +node...
+
Adds the tag string to one of more nodes.
+ +
treeName tag delete string +node...
+
Deletes the tag string from one or more nodes.
+ +
treeName tag forget +string
+
Removes the tag string from all nodes. It's not an error if no nodes +are tagged as string.
+ +
treeName tag names ?node?
+
Returns a list of tags used +by the tree. If a node argument is present, only those tags used by node +are returned.
+ +
treeName tag nodes string
+
Returns a list of nodes that have +the tag string. If no node is tagged as string, then an empty string is +returned.
+
+ +

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.

+The tree's get, +set, and unset operations can trigger various traces. The get operation +can cause a read trace to fire. The set operation causes a write trace +to fire. And if the data field is written for the first time, you will +also get a create trace. The unset operation triggers unset traces.

+Data +traces are invoked by the trace operation. The general form is
+

+treeName trace operation ?arg?...
+

Both operation and its arguments determine the exact behavior of the command. + The operations available for traces are listed below. +

+ +
treeName trace create +node key ops command
+
Creates a trace for node on data field key. Node can +refer to more than one node (for example, the tag all). If node is a tag, +any node with that tag can possibly trigger a trace, invoking command. + Command is command prefix, typically a procedure name. Whenever a trace +is triggered, four arguments are appended to command before it is invoked: +treeName, id of the node, key and, ops. Note that no nodes need have the +field key. A trace identifier in the form "trace0", "trace1", etc. is returned. +

+Ops indicates which operations are of interest, and consists of one or +more of the following letters:

+ +
r
+
Invoke command whenever key is read. Both +read and write traces are temporarily disabled when command is executed. +
+ +
w
+
Invoke command whenever key is written. Both read and write traces are +temporarily disabled when command is executed.
+ +
c
+
Invoke command whenever +key is created.
+ +
u
+
Invoke command whenever key is unset. Data fields are +typically unset with the unset command. Data fields are also unset when +the tree is released, but all traces are disabled prior to that.

+

+
+ + +
+ +
treeName +trace delete traceId...
+
Deletes one of more traces. TraceId is the trace identifier +returned by the trace create operation.
+ +
treeName trace info traceId
+
Returns +information about the trace traceId. TraceId is a trace identifier previously +returned by the trace create operation. It's the same information specified +for the trace create 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 ops) and, the command prefix.
+ +
treeName trace names +
+
Returns a list of identifers for all the current traces.
+
+ +

Notify Operations

+Tree +objects can be shared among many clients, such as a hiertable 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.

+Notifications are +handled by the notify operation. The general form is
+

+treeName notify operation ?arg?...
+

Both operation and its arguments determine the exact behavior of the command. + The operations available for events are listed below. +

+ +
treeName notify create +?switches? command ?args?...
+
Creates a notifier for the tree. A notify identifier +in the form "notify0", "notify1", etc. is returned.

+Command and args are +saved and invoked whenever the tree structure is changed (according to +switches). Two arguments are appended to command and args 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:

+ +
-create
+
Invoke command whenever a new +node has been added.
+ +
-delete
+
Invoke command whenever a node has been deleted. +
+ +
-move
+
Invoke command whenever a node has been moved.
+ +
-sort
+
Invoke command +whenever the tree has been sorted and reordered.
+ +
-relabel
+
Invoke command +whenever a node has been relabeled.
+ +
-allevents
+
Invoke command whenever any +of the above events occur.
+ +
-whenidle
+
When an event occurs don't invoke command +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, command will still be invoked only once.
+
+ + +
+ +
treeName +notify delete notifyId
+
Deletes one or more notifiers from the tree. NotifyId +is the notifier identifier returned by the notify create operation.
+ +
treeName +notify info notifyId
+
Returns information about the notify event notifyId. + NotifyId is a notify identifier previously returned by the notify create +operation. It's the same information specified for the notify create operation. +It consists of the notify id, a sublist of event flags (it's in the same +form as flags) and, the command prefix.
+ +
treeName notify names
+
Returns a +list of identifers for all the current notifiers.
+
+ +

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. +

Keywords

+tree, hiertable, widget +

+ +


+Table of Contents

+

+ 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 @@ + + + + + +treeview(n) manual page + + +Table of Contents

+ +

Name

+treeview - Create and manipulate hierarchical +table widgets +

Synopsis

+treeview pathName ?options? +

Description

+The treeview +widget displays a tree of data. It replaces both the hiertable and hierbox +widgets. The treeview is 100% syntax compatible with the hiertable widget. + The hiertable command is retained for sake of script-level compatibility. + This widget obsoletes the hierbox widget. It does everything the old hierbox +widget did, but also provides data sharing (via tree data objects) and +the ability to tag nodes. +

Introduction

+The treeview 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.

+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 open, its children are exposed. When it +is closed, the children and their descedants are hidden. The button is +normally a + or - symbol (ala Windows Explorer), but can be replaced with +a pair of Tk images (open and closed images).

+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. +

Tree Data Object

+The tree +is not stored inside the widget but in a tree data object (see the tree +command for a further explanation). Tree data objects can be shared among +different clients, such as a treeview widget or the tree command. You can +walk the tree and manage its data with the tree command tree, while displaying +it with the treeview widget. Whenever the tree is updated, the treeview +widget is automatically redrawn.

+By default, the treeview widget creates +its own tree object. The tree initially contains just a root node. But you +can also display trees created by the tree command using the -tree configuration +option. Treeview widgets can share the same tree object, possibly displaying +different views of the same data.

+A tree object has both a Tcl and C API. + You can insert or delete nodes using treeview widget or tree 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. +

Syntax

+
+

+treeview pathName ?option value?...
+

The treeview command creates a new window pathName and makes it into a +treeview widget. At the time this command is invoked, there must not exist +a window named pathName, but pathName'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 configure operation +below for the exact details about what option and value pairs are valid. +

+If successful, treeview 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: +
+

+pathName operation ?arg?...
+

Both operation and its arguments determine the exact behavior of the command. + The operations available are described in the TREEVIEW OPERATIONS + section. + +

IDs and Tags

+Nodes can be inserted into a tree using the treeview widget +
+blt::treeview .t
+set node [.t insert end root "one"]
+

or tree command.
+set tree [blt::tree create]
+set node [$tree insert root "one"]
+

In both cases, a number identifying the node is returned (the value of +$node). This serial number or id 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 0. 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 treeview widget or the tree command. Ids are recycled +when the node deleted.

+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, "x123" is valid, but "123" 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 treeview widget +take either node ids or tag names as arguments. Using a tag says to apply +the operation to all nodes with that tag.

+The tag all is implicitly associated +with every node in the tree. It may be used to invoke operations on all +the nodes in the tree.

+Tags may be shared, just like trees, between clients. + For example, you can use the tags created by the tree command with treeview +widgets. +

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 +treeview widget. +
+ +
active
+
The node where the mouse pointer is currently located. +When a node is active, it is drawn using its active icon (see the -activeicon +option). The active id is changed automatically by moving the mouse pointer +over another node or by using the entry activate operation. Note that there +can be only one active node at a time.
+ +
anchor
+
The node representing the +fixed end of the current selection. The anchor is set by the selection +anchor operation.
+ +
current
+
The node where the mouse pointer is currently +located. But unlike active, this id changes while the selection is dragged. + It is used to determine the current node during button drags.
+ +
down
+
The +next open node from the current focus. The down of the last open node is +the same.
+ +
end
+
The last open node (in depth-first order) on the tree.
+ +
focus +
+
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 focus operation.
+ +
last
+
The +last open node from the current focus. But unlike up, when the focus is +at root, last wraps around to the last open node in the tree.
+ +
mark
+
The node +representing the non-fixed end of the current selection. The mark is set +by the selection mark operation.
+ +
next
+
The next open node from the current +focus. But unlike down, when the focus is on last open node, next wraps +around to the root node.
+ +
nextsibling
+
The next sibling from the node with +the current focus. If the node is already the last sibling then it is the +nextsibling.
+ +
parent
+
The parent of the node with the current focus. The parent +of the root is also the root.
+ +
prevsibling
+
The previous sibling from the +node with the current focus. If the node is already the first sibling then +it is the prevsibling.
+ +
root
+
The root node. You can also use id 0 to indicate +the root.
+ +
up
+
The last open node (in depth-first order) from the current focus. +The up of the root node (i.e. the root has focus) is also the root.
+ +
view.top +
+
First node that's current visible in the widget.
+ +
view.bottom
+
Last node that's +current visible in the widget.
+ +
path
+
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 -separator configuration option), but component +names must be separated by the designated separator.
+ +
@x,y
+
Indicates the +node that covers the point in the treeview window specified by x and y +(in pixel coordinates). If no part of the entryd covers that point, then +the closest node to that point is used.
+
+

+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. +

Data Fields

+A node +in the tree can have data fields. 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 treeview 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. +

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 bindtags. Bindtags are simply names that associate a binding +with one or more nodes. There is a built-in tag all that all node entries +automatically have. +

Treeview Operations

+The treeview operations are the invoked +by specifying the widget's pathname, the operation, and any arguments that +pertain to that operation. The general form is:

+
+pathName operation ?arg arg ...?
+

+

Operation and the args determine the exact behavior of the command. The +following operation are available for treeview widgets: +

+ +
pathName bbox ?-screen? +tagOrId...
+
Returns a list of 4 numbers, representing a bounding box of around +the specified entries. The entries is given by one or more tagOrId arguments. + If the -screen flag is given, then the x-y coordinates of the bounding +box are returned as screen coordinates, not virtual coordinates. Virtual +coordinates start from 0 from the root node. The returned list contains +the following values.
+ +
x
+
X-coordinate of the upper-left corner of the bounding +box.
+ +
y
+
Y-coordinate of the upper-left corner of the bounding box.
+ +
width
+
Width +of the bounding box.
+ +
height
+
Height of the bounding box.
+
+ + +
+ +
pathName bind tagName +?sequence command?
+
Associates command with tagName such that whenever the +event sequence given by sequence occurs for a node with this tag, command +will be invoked. The syntax is similar to the bind command except that +it operates on treeview entries, rather than widgets. See the bind manual +entry for complete details on sequence and the substitutions performed +on command before invoking it.

+If all arguments are specified then a +new binding is created, replacing any existing binding for the same sequence +and tagName. If the first character of command is + then command augments +an existing binding rather than replacing it. If no command argument is +provided then the command currently associated with tagName and sequence +(it's an error occurs if there's no such binding) is returned. If both command +and sequence are missing then a list of all the event sequences for which +bindings have been defined for tagName.

+ +
pathName button operation ?args? +
+
This command is used to control the button selectors within a treeview +widget. It has several forms, depending on operation:
+ +
pathName button +activate tagOrId
+
Designates the node given by tagOrId as active. When +a node is active it's entry is drawn using its active icon (see the -activeicon +option). Note that there can be only one active entry at a time. The special +id active indicates the currently active node.
+ +
pathName button bind tagName +?sequence command?
+
Associates command with tagName such that whenever the +event sequence given by sequence occurs for an button of a node entry with +this tag, command will be invoked. The syntax is similar to the bind command +except that it operates on treeview buttons, rather than widgets. See the +bind manual entry for complete details on sequence and the substitutions +performed on command before invoking it.

+If all arguments are specified +then a new binding is created, replacing any existing binding for the +same sequence and tagName. If the first character of command is + then command + augments an existing binding rather than replacing it. If no command argument +is provided then the command currently associated with tagName and sequence +(it's an error occurs if there's no such binding) is returned. If both command +and sequence are missing then a list of all the event sequences for which +bindings have been defined for tagName.

+ +
pathName button cget option
+
Returns +the current value of the configuration option given by option. Option may +have any of the values accepted by the configure operation described below. +
+ +
pathName button configure ?option? ?value option value ...?
+
Query or modify +the configuration options of the widget. If no option is specified, returns +a list describing all of the available options for pathName (see Tk_ConfigureInfo +for information on the format of this list). If option is specified with +no value, 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 option is specified). If one or more option-value 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. Option +and value are described in the section BUTTON OPTIONS + below.
+
+ + +
+ +
pathName +cget option
+
Returns the current value of the configuration option given +by option. Option may have any of the values accepted by the configure operation +described below.
+ +
pathName close ?-recurse? tagOrId...
+
Closes the node specified +by tagOrId. In addition, if a Tcl script was specified by the -closecommand +option, it is invoked. If the node is already closed, this command has +no effect. If the -recurse flag is present, each child node is recursively +closed.
+ +
pathName column operation ?args?
+
The following operations are available +for treeview columns.
+ +
pathName column activate column
+
Sets the active column +to column. Column is the name of a column in the widget. When a column is +active, it's drawn using its -activetitlebackground and -activetitleforeground +options. If column is the "", then no column will be active. If no column +argument is provided, then the name of the currently active column is returned. +
+ +
pathName column cget name option
+
Returns the current value of the column +configuration option given by option for name. Name is the name of column +that corresponds to a data field. Option may have any of the values accepted +by the configure operation described below.
+ +
pathName column configure name +?option? ?value option value ...?
+
Query or modify the configuration options +of the column designated by name. Name is the name of the column corresponding +to a data field. If no option is specified, returns a list describing all +of the available options for pathName (see Tk_ConfigureInfo for information +on the format of this list). If option is specified with no value, 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 option +is specified). If one or more option-value 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. Option and value are +described in the section COLUMN OPTIONS + below.
+ +
pathName column delete field +?field...?
+
Deletes one of more columns designated by field. Note that this +does not delete the data fields themselves.
+ +
pathName column insert position +field ?options...?
+
Inserts one of more columns designated by field. 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. Position indicates where in the +list of columns to add the new column. It may be either a number or end. +
+ +
pathName column invoke field
+
Invokes the Tcl command associated with the +column field, if there is one (using the column's -command option). The +command is ignored if the column's -state option set to disabled.
+ +
pathName +column move name dest
+
Moves the column name to the destination position. + Dest is the name of another column or a screen position in the form @x,y. +
+ +
pathName column names
+
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.
+ +
pathName +column nearest x ?y?
+
Returns the name of the column closest to the given +X-Y screen coordinate. If you provide a y argument (it's optional), a name +is returned only when if the point is over a column's title.
+
+ + +
+ +
pathName configure +?option? ?value option value ...?
+
Query or modify the configuration options +of the widget. If no option is specified, returns a list describing all +of the available options for pathName (see Tk_ConfigureInfo for information +on the format of this list). If option is specified with no value, 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 option +is specified). If one or more option-value 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. Option and value are +described in the section TREEVIEW OPTIONS + below.
+ +
pathName curselection +
+
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. +
+ +
pathName delete tagOrId...
+
Deletes one or more entries given by tagOrId and +its children.
+ +
pathName entry operation ?args?
+
The following operations are +available for treeview entries.
+ +
pathName entry activate tagOrId
+
Sets the +active entry to the one specified by tagOrId. When an entry is active +it is drawn using its active icon (see the -activeicon option). Note that +there can be only one active node at a time. The special id of the currently +active node is active.
+ +
pathName entry cget option
+
Returns the current value +of the configuration option given by option. Option may have any of the +values accepted by the configure operation described below.
+ +
pathName entry +children tagOrId ?first? ?last?
+
Returns a list of ids for the given range +of children of tagOrId. TagOrId is the id or tag of the node to be examined. +If only a first argument is present, then the id of the that child at +that numeric position is returned. If both first and last arguments are +given, then the ids of all the children in that range are returned. Otherwise +the ids of all children are returned.
+ +
pathName entry configure ?option? +?value option value ...?
+
Query or modify the configuration options of the +widget. If no option is specified, returns a list describing all of the +available options for pathName (see Tk_ConfigureInfo for information on +the format of this list). If option is specified with no value, 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 option +is specified). If one or more option-value 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. Option and value are +described below:
+ +
pathName entry delete tagOrId ?first ?last?
+
Deletes the +one or more children nodes of the parent tagOrId. If first and last arguments +are present, they are positions designating a range of children nodes to +be deleted.
+ +
pathName entry isbefore tagOrId1 tagOrId2
+
Returns 1 if tagOrId1 +is before tagOrId2 and 0 otherwise.
+ +
pathName entry ishidden tagOrId
+
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.
+ +
pathName entry isopen +tagOrId
+
Returns 1 if the node is currently open and 0 otherwise.
+ +
pathName +entry size -recurse tagOrId
+
Returns the number of children for parent node +tagOrId. If the -recurse flag is set, the number of all its descendants +is returned. The node itself is not counted.
+
+ + +
+ +
pathName find ?flags? first +last
+
Finds for all entries matching the criteria given by flags. A list +of ids for all matching nodes is returned. First and last are ids designating +the range of the search in depth-first order. If last is before first, then +nodes are searched in reverse order. The valid flags are:
+ +
-name pattern +
+
Specifies pattern to match against node names.
+ +
-full pattern
+
Specifies pattern +to match against node pathnames.
+ +
-option pattern
+
Specifies pattern to match +against the node entry's configuration option.
+ +
-exact
+
Patterns must match +exactly. The is the default.
+ +
-glob
+
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:
+ +
*
+
Matches any sequence of + characters in string, including a null string.
+ +
?
+
Matches any single character +in string.
+ +
[chars]
+
Matches any character in the set given by chars. If a +sequence of the form x-y appears in chars, then any character between x +and y, inclusive, will match.
+ +
\x
+
Matches the single character x. This +provides a way of avoiding the special interpretation of the characters +*?[]\ in the pattern.
+
+ + +
+ +
-regexp
+
Use regular expression pattern matching (i.e. +the same as implemented by the regexp command).
+ +
-nonmatching
+
Pick entries +that don't match.
+ +
-exec string
+
Specifies a Tcl script to be invoked for +each matching node. Percent substitutions are performed on string before + it is executed. The following substitutions are valid:
+ +
%W
+
The pathname +of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The full pathname of the node. +
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single percent.
+
+ + +
+ +
-count number
+
Stop +searching after number matches.
+ +
--
+
Indicates the end of flags.
+
+ + +
+ +
pathName focus + tagOrId
+
Sets the focus to the node given by tagOrId. When a node has focus, +it can receive keyboard events. The special id focus designates the node +that currently has focus.
+ +
pathName get ?-full? tagOrId tagOrId...
+
Translates +one or more ids to their node entry names. It returns a list of names +for all the ids specified. If the -full flag is set, then the full pathnames +are returned.
+ +
pathName hide ?flags? tagOrId...
+
Hides all nodes matching the +criteria given by flags. The search is performed recursively for each node +given by tagOrId. The valid flags are described below:
+ +
-name pattern
+
Specifies +pattern to match against node names.
+ +
-full pattern
+
Specifies pattern to match +against node pathnames.
+ +
-option pattern
+
Specifies pattern to match against +the node entry's configuration option.
+ +
-exact
+
Match patterns exactly. The +is the default.
+ +
-glob
+
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:
+ +
*
+
Matches any sequence of characters in string, +including a null string.
+ +
?
+
Matches any single character in string.
+ +
[chars] +
+
Matches any character in the set given by chars. If a sequence of the form +x-y appears in chars, then any character between x and y, inclusive, will +match.
+ +
\x
+
Matches the single character x. This provides a way of avoiding + the special interpretation of the characters *?[]\ in the pattern.
+
+ + +
+ +
-regexp +
+
Use regular expression pattern matching (i.e. the same as implemented by +the regexp command).
+ +
-nonmatching
+
Hide nodes that don't match.
+ +
--
+
Indicates +the end of flags.
+
+ + +
+ +
pathName index ?-at tagOrId? string
+
Returns the id of +the node specified by string. String may be a tag or node id. Some special +ids are normally relative to the node that has focus. The -at flag lets +you select another node.
+ +
pathName insert ?-at tagOrId? position path ?options...? +?path? ?options...?
+
Inserts one or more nodes at position. Position is the +location (number or end) where the new nodes are added to the parent node. + Path 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 -separator option). Pathnames +are normally absolute, but the -at switch lets you select a relative starting +point. Its value is the id of the starting node.

+All ancestors of the +new node must already exist, unless the -autocreate option is set. It is +also an error if a node already exists, unless the -allowduplicates option +is set.

+Option and value may have any of the values accepted by the entry +configure operation described in the ENTRY OPERATIONS + section below. This +command returns a list of the ids of the new entries.

+ +
pathName move tagOrId +how destId
+
Moves the node given by tagOrId to the destination node. The +node can not be an ancestor of the destination. DestId is the id of the +destination node and can not be the root of the tree. In conjunction with +how, it describes how the move is performed.
+ +
before
+
Moves the node before +the destination node.
+ +
after
+
Moves the node after the destination node.
+ +
into +
+
Moves the node to the end of the destination's list of children.
+
+ + +
+ +
pathName +nearest x y ?varName?
+
Returns the id of the node entry closest to the given +X-Y screen coordinate. The optional argument varName is the name of variable +which is set to either button or select to indicate over what part of the +node the coordinate lies. If the coordinate is not directly over any node, +then varName will contain the empty string.
+ +
pathName open ?-recurse? tagOrId... +
+
Opens the one or more nodes specified by tagOrId. If a node is not already +open, the Tcl script specified by the -opencommand option is invoked. If +the -recurse flag is present, then each descendant is recursively opened. +
+ +
pathName range ?-open? first last
+
Returns the ids in depth-first order +of the nodes between the first and last ids. If the -open flag is present, +it indicates to consider only open nodes. If last is before first, then +the ids are returned in reverse order.
+ +
pathName scan option args
+
This command +implements scanning. It has two forms, depending on option:
+ +
pathName scan +mark x y
+
Records x and y and the current view in the treeview window; +used in conjunction with later scan dragto commands. Typically this command +is associated with a mouse button press in the widget. It returns an empty +string.
+ +
pathName scan dragto x y.
+
Computes the difference between its x and +y arguments and the x and y arguments to the last scan mark 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.
+
+ + +
+ +
pathName see ?-anchor anchor? tagOrId +
+
Adjusts the view of entries so that the node given by tagOrId is visible +in the widget window. It is an error if tagOrId 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 -anchor flag. Its value is a Tk +anchor position.
+ +
pathName selection option arg
+
This command is used to adjust +the selection within a treeview widget. It has several forms, depending +on option:
+ +
pathName selection anchor tagOrId
+
Sets the selection anchor +to the node given by tagOrId. If tagOrId 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 anchor may be used to refer to the anchor node.
+ +
pathName selection cancel +
+
Clears the temporary selection of entries back to the current anchor. Temporary +selections are created by the selection mark operation.
+ +
pathName selection +clear first ?last?
+
Removes the entries between first and last (inclusive) +from the selection. Both first and last are ids representing a range of +entries. If last isn't given, then only first is deselected. Entries outside +the selection are not affected.
+ +
pathName selection clearall
+
Clears the entire +selection.
+ +
pathName selection mark tagOrId
+
Sets the selection mark to +the node given by tagOrId. 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 mark may be used to refer to the current + mark node. If tagOrId 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.
+ +
pathName selection includes tagOrId
+
Returns 1 if the +node given by tagOrId is currently selected, 0 if it isn't.
+ +
pathName selection +present
+
Returns 1 if any nodes are currently selected and 0 otherwise.
+ +
pathName +selection set first ?last?
+
Selects all of the nodes in the range between +first and last, inclusive, without affecting the selection state of nodes +outside that range.
+ +
pathName selection toggle first ?last?
+
Selects/deselects +nodes in the range between first and last, inclusive, from the selection. +If a node is currently selected, it becomes deselected, and visa versa. +
+
+ + +
+ +
pathName show ?flags? tagOrId...
+
Exposes all nodes matching the criteria given +by flags. This is the inverse of the hide operation. The search is performed +recursively for each node given by tagOrId. The valid flags are described +below:
+ +
-name pattern
+
Specifies pattern to match against node names.
+ +
-full +pattern
+
Specifies pattern to match against node pathnames.
+ +
-option pattern +
+
Specifies pattern to match against the entry's configuration option.
+ +
-exact +
+
Match patterns exactly. The is the default.
+ +
-glob
+
-glob 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:
+ +
*
+
Matches + any sequence of characters in string, including a null string.
+ +
?
+
Matches +any single character in string.
+ +
[chars]
+
Matches any character in the set +given by chars. If a sequence of the form x-y appears in chars, then any +character between x and y, inclusive, will match.
+ +
\x
+
Matches the single + character x. This provides a way of avoiding the special interpretation +of the characters *?[]\ in the pattern.
+
+ + +
+ +
-regexp
+
Use regular expression pattern +matching (i.e. the same as implemented by the regexp command).
+ +
-nonmatching +
+
Expose nodes that don't match.
+ +
--
+
Indicates the end of flags.
+
+ + +
+ +
pathName sort +?operation? args...
+
+ +
pathName sort auto ?boolean
+
Turns on/off automatic sorting +of node entries. If boolean is true, entries will be automatically sorted +as they are opened, closed, inserted, or deleted. If no boolean argument +is provided, the current state is returned.
+ +
pathName sort cget option
+
Returns +the current value of the configuration option given by option. Option may +have any of the values accepted by the configure operation described below. +
+ +
pathName sort configure ?option? ?value option value ...?
+
Query or modify +the sorting configuration options of the widget. If no option is specified, +returns a list describing all of the available options for pathName (see +Tk_ConfigureInfo for information on the format of this list). If option +is specified with no value, 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 option is specified). If one or more +option-value 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. Option and value are described below:
+ +
-column string
+
Specifies +the column to sort. Entries in the widget are rearranged according to this +column. If column is "" then no sort is performed.
+ +
-command string
+
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.
+ +
-decreasing boolean +
+
Indicates to sort in ascending/descending order. If boolean is true, then +the entries as in descending order. The default is no.
+ +
-mode string
+
Specifies +how to compare entries when sorting. String may be one of the following: +
+ +
ascii
+
Use string comparison based upon the ASCII collation order.
+ +
dictionary +
+
Use dictionary-style comparison. This is the same as ascii 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".
+ +
integer
+
Compares fields as integers.
+ +
real
+
Compares fields +as floating point numbers.
+ +
command
+
Use the Tcl proc specified by the -command +option to compare entries when sorting. If no command is specified, the +sort reverts to ascii sorting.
+
+ + + +
+ +
pathName sort once ?flags? tagOrId...
+
Sorts +the children for each entries specified by tagOrId. By default, entries +are sorted by name, but you can specify a Tcl proc to do your own comparisons. +
+ +
-recurse
+
Recursively sort the entire branch, not just the children.
+
+ + + +
+ +
pathName +tag operation args
+
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.

+Both operation and its arguments determine the exact behavior of +the command. The operations available for tags are listed below.

+ +
pathName +tag add string id...
+
Adds the tag string to one of more entries.
+ +
pathName tag +delete string id...
+
Deletes the tag string from one or more entries.
+ +
pathName +tag forget string
+
Removes the tag string from all entries. It's not an error +if no entries are tagged as string.
+ +
pathName tag names ?id?
+
Returns a list +of tags used. If an id argument is present, only those tags used by the +node designated by id are returned.
+ +
pathName tag nodes string
+
Returns a +list of ids that have the tag string. If no node is tagged as string, then +an empty string is returned.
+
+ + +
+ +
pathName text operation ?args?
+
This operation +is used to provide text editing for cells (data fields in a column) or +entry labels. It has several forms, depending on operation:
+ +
pathName text +apply
+
Applies the edited buffer, replacing the entry label or data field. +The edit window is hidden.
+ +
pathName text cancel
+
Cancels the editing operation, +reverting the entry label or data value back to the previous value. The +edit window is hidden.
+ +
pathName text cget value
+
Returns the current value +of the configuration option given by option. Option may have any of the +values accepted by the configure operation described below.
+ +
pathName text +configure ?option value?
+
Query or modify the configuration options of the +edit window. If no option is specified, returns a list describing all of +the available options (see Tk_ConfigureInfo for information on the format +of this list). If option is specified with no value, 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 option is specified). + If one or more option-value 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. Option and value are described in the section + TEXT EDITING OPTIONS + below.
+
+ + +
+ +
pathName text delete first last
+
Deletes the +characters in the edit buffer between the two given character positions. +
+ +
pathName text get ?-root? x y
+
+ +
pathName text icursor index
+
+ +
pathName text +index index
+
Returns the text index of given index.
+ +
pathName text insert +index string
+
Insert the text string string into the edit buffer at the +index index. For example, the index 0 will prepend the buffer.
+ +
pathName +text selection args
+
This operation controls the selection of the editing +window. Note that this differs from the selection of entries. It has the +following forms:
+ +
pathName text selection adjust index
+
Adjusts either the +first or last index of the selection.
+ +
pathName text selection clear
+
Clears +the selection.
+ +
pathName text selection from index
+
Sets the anchor of the +selection.
+ +
pathName text selection present
+
Indicates if a selection is present. +
+ +
pathName text selection range start end
+
Sets both the anchor and mark of +the selection.
+ +
pathName text selection to index
+
Sets the unanchored end +(mark) of the selection.
+
+ + +
+ +
pathName toggle tagOrId
+
Opens or closes the node +given by tagOrId. If the corresponding -opencommand or -closecommand option +is set, then that command is also invoked.
+ +
pathName xview args
+
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:
+ +
pathName +xview
+
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 treeview 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 -xscrollcommand option. +
+ +
pathName xview tagOrId
+
Adjusts the view in the window so that the character +position given by tagOrId is displayed at the left edge of the window. Character +positions are defined by the width of the character 0.
+ +
pathName xview moveto +fraction
+
Adjusts the view in the window so that fraction of the total width +of the treeview widget's text is off-screen to the left. fraction must be +a fraction between 0 and 1.
+ +
pathName xview scroll number what
+
This command +shifts the view in the window left or right according to number and what. +Number must be an integer. What must be either units or pages or an abbreviation +of one of these. If what is units, the view adjusts left or right by number +character units (the width of the 0 character) on the display; if it is +pages then the view adjusts by number screenfuls. If number is negative +then characters farther to the left become visible; if it is positive +then characters farther to the right become visible.
+
+ + +
+ +
pathName yview ?args? +
+
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:
+ +
pathName +yview
+
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 -yscrollcommand option.
+ +
pathName yview tagOrId
+
Adjusts the view in the +window so that the node given by tagOrId is displayed at the top of the +window.
+ +
pathName yview moveto fraction
+
Adjusts the view in the window so +that the node given by fraction appears at the top of the window. Fraction +is a fraction between 0 and 1; 0 indicates the first node, 0.33 indicates +the node one-third the way through the treeview widget, and so on.
+ +
pathName +yview scroll number what
+
This command adjusts the view in the window up +or down according to number and what. Number must be an integer. What must +be either units or pages. If what is units, the view adjusts up or down +by number lines; if it is pages then the view adjusts by number screenfuls. +If number is negative then earlier nodes become visible; if it is positive +then later nodes become visible.
+
+ + +

Treeview Options

+In addition to the configure +operation, widget configuration options may also be set by the Tk option +command. The class resource name is TreeView.
+option add *TreeView.Foreground white
+option add *TreeView.Background blue
+

The following widget options are available: +

+ +
-activebackground color
+
Sets +the background color for active entries. A node is active when the mouse +passes over it's entry or using the activate operation.
+ +
-activeforeground +color
+
Sets the foreground color of the active node. A node is active when +the mouse passes over it's entry or using the activate operation.
+ +
-activeicons +images
+
Specifies images to be displayed for an entry's icon when it is active. +Images is a list of two Tk images: the first image is displayed when the +node is open, the second when it is closed.
+ +
-autocreate boolean
+
If boolean +is true, automatically create missing ancestor nodes when inserting new +nodes. Otherwise flag an error. The default is no.
+ +
-allowduplicates boolean +
+
If boolean is true, allow nodes with duplicate pathnames when inserting +new nodes. Otherwise flag an error. The default is no.
+ +
-background color
+
Sets +the background color of the widget. The default is white.
+ +
-borderwidth pixels +
+
Sets the width of the 3-D border around the outside edge of the widget. +The -relief option determines if the border is to be drawn. The default +is 2.
+ +
-closecommand string
+
Specifies a Tcl script to be invoked when a node +is closed. You can overrider this for individual entries using the entry's +-closecommand option. The default is "". Percent substitutions are performed +on string before it is executed. The following substitutions are valid: +
+ +
%W
+
The pathname of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The full pathname +of the node.
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single percent.
+
+ + +
+ +
-cursor +cursor
+
Specifies the widget's cursor. The default cursor is "".
+ +
-dashes number +
+
Sets the dash style of the horizontal and vertical lines drawn connecting + entries. Number is the length in pixels of the dashes and gaps in the line. +If number is 0, solid lines will be drawn. The default is 1 (dotted).
+ +
-exportselection +boolean
+
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 STRING; the value of the +selection will be the label of the selected nodes, separated by newlines. + The default is no.
+ +
-flat boolean
+
Indicates whether to display the tree as +a flattened list. If boolean is true, then the hierarchy will be a list +of full paths for the nodes. This option also has affect on sorting. See +the SORT OPERATIONS + section for more information. The default is no.
+ +
-focusdashes +dashList
+
Sets the dash style of the outline rectangle drawn around the +entry label of the node that current has focus. Number is the length in +pixels of the dashes and gaps in the line. If number is 0, a solid line +will be drawn. The default is 1.
+ +
-focusforeground color
+
Sets the color of +the focus rectangle. The default is black.
+ +
-font fontName
+
Specifies the +font for entry labels. You can override this for individual entries with +the entry's -font configuration option. The default is *-Helvetica-Bold-R-Normal-*-12-120-*. +
+ +
-foreground color
+
Sets the text color of entry labels. You can override +this for individual entries with the entry's -foreground configuration option. + The default is black.
+ +
-height pixels
+
Specifies the requested height of +widget. The default is 400.
+ +
-hideroot boolean
+
If boolean is true, it indicates +that no entry for the root node should be displayed. The default is no. +
+ +
-highlightbackground color
+
Specifies the normal color of the traversal +highlight region when the widget does not have the input focus.
+ +
-highlightcolor +color
+
Specifies the color of the traversal highlight rectangle when the +widget has the input focus. The default is black.
+ +
-highlightthickness pixels +
+
Specifies the width of the highlight rectangle indicating when the widget +has input focus. The value may have any of the forms acceptable to Tk_GetPixels. + If the value is zero, no focus highlight will be displayed. The default +is 2.
+ +
-icons images
+
Specifies images for the entry's icon. Images is a list +of two Tk images: the first image is displayed when the node is open, +the second when it is closed.
+ +
-linecolor color
+
Sets the color of the connecting +lines drawn between entries. The default is black.
+ +
-linespacing pixels
+
Sets +the number of pixels spacing between entries. The default is 0.
+ +
-linewidth +pixels
+
Set the width of the lines drawn connecting entries. If pixels is +0, no vertical or horizontal lines are drawn. The default is 1.
+ +
-newtags +boolean
+
If boolean is true, when sharing a tree object (see the -tree option), +don't share its tags too. The default is 0.
+ +
-opencommand string
+
Specifies +a Tcl script to be invoked when a node is open. You can override this +for individual entries with the entry's -opencommand configuration option. + The default is "". Percent substitutions are performed on string before + it is executed. The following substitutions are valid:
+ +
%W
+
The pathname +of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The full pathname of the node. +
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single percent.
+
+ + +
+ +
-relief relief
+
Specifies +the 3-D effect for the widget. Relief specifies how the treeview widget +should appear relative to widget it is packed into; for example, raised +means the treeview widget should appear to protrude. The default is sunken. +
+ +
-scrollmode mode
+
Specifies the style of scrolling to be used. The following +styles are valid. This is the default is hierbox.
+ +
listbox
+
Like the listbox +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.
+ +
hierbox
+
Like the hierbox widget, the last entry can only +be viewed at the bottom of the widget window. The scrollbar stays a constant +size.
+ +
canvas
+
Like the canvas widget, the entries are bound within the +scrolling area.
+
+ + +
+ +
-selectbackground color
+
Sets the background color selected +node entries. The default is #ffffea.
+ +
-selectborderwidth pixels
+
Sets the width +of the raised 3-D border drawn around the labels of selected entries. The +default is 0. -selectcommand string Specifies a Tcl script to invoked when +the set of selected nodes changes. The default is "".
+ +
-selectforeground color +
+
Sets the color of the labels of selected node entries. The default is black. +
+ +
-selectmode mode
+
Specifies the selection mode. If mode is single, only one +node can be selected at a time. If multiple more than one node can be selected. +The default is single.
+ +
-separator string
+
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 string is the empty string, the pathnames are Tcl lists. + Each element is a path component. The default is "".
+ +
-showtitles boolean +
+
If boolean is false, column titles are not be displayed. The default is +yes.
+ +
-sortselection boolean
+
If boolean 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 no.
+ +
-takefocus focus
+
Provides +information used when moving the focus from window to window via keyboard +traversal (e.g., Tab and Shift-Tab). If focus is 0, this means that this window +should be skipped entirely during keyboard traversal. 1 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 "1".
+ +
-trim string
+
Specifies a string leading characters to +trim from entry pathnames before parsing. This only makes sense if the +-separator is also set. The default is "".
+ +
-width pixels
+
Sets the requested +width of the widget. If pixels is 0, then the with is computed from the +contents of the treeview widget. The default is 200.
+ +
-xscrollcommand string +
+
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.
+ +
-xscrollincrement pixels
+
Sets the horizontal scrolling distance. +The default is 20 pixels.
+ +
-yscrollcommand string
+
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.
+ +
-yscrollincrement pixels +
+
Sets the vertical scrolling distance. The default is 20 pixels.
+
+ +

Entry Options

+Many +widget configuration options have counterparts in entries. For example, +there is a -closecommand 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.

+There is no resource class or +name for entries. +

+ +
-activeicons images
+
Specifies images to be displayed as +the entry's icon when it is active. This overrides the global -activeicons +configuration option for the specific entry. Images is a list of two Tk +images: the first image is displayed when the node is open, the second +when it is closed.
+ +
-bindtags tagList
+
Specifies the binding tags for nodes. + TagList 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 all.
+ +
-button string
+
Indicates whether a button should be displayed +on the left side of the node entry. String can be yes, no, or auto. If +auto, then a button is automatically displayed if the node has children. + This is the default.
+ +
-closecommand string
+
Specifies a Tcl script to be invoked +when the node is closed. This overrides the global -closecommand option +for this entry. The default is "". Percent substitutions are performed on +string before it is executed. The following substitutions are valid:
+ +
%W +
+
The pathname of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The full pathname +of the node.
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single percent.
+
+ + +
+ +
-data +string
+
Sets data fields for the node. String is a list of name-value pairs +to be set. The default is "".
+ +
-font fontName
+
Sets the font for entry labels. + This overrides the widget's -font option for this node. The default is *-Helvetica-Bold-R-Normal-*-12-120-*. +
+ +
-foreground color
+
Sets the text color of the entry label. This overrides +the widget's -foreground configuration option. The default is "".
+ +
-icons images +
+
Specifies images to be displayed for the entry's icon. This overrides the +global -icons configuration option. Images is a list of two Tk images: the +first image is displayed when the node is open, the second when it is closed. +
+ +
-label string
+
Sets the text for the entry's label. If not set, this defaults +to the name of the node. The default is "".
+ +
-opencommand string
+
Specifies +a Tcl script to be invoked when the entry is opened. This overrides the +widget's -opencommand option for this node. The default is "". Percent substitutions +are performed on string before it is executed. The following substitutions +are valid:
+ +
%W
+
The pathname of the widget.
+ +
%p
+
The name of the node.
+ +
%P
+
The +full pathname of the node.
+ +
%#
+
The id of the node.
+ +
%%
+
Translates to a single +percent.
+
+ + +

Button Options

+Button configuration options may also be set by the +option command. The resource subclass is Button. The resource name is always +button.
+option add *TreeView.Button.Foreground white
+option add *TreeView.button.Background blue
+

The following are the configuration options available for buttons. +

+ +
-activebackground +color
+
Sets the background color of active buttons. A button is made active +when the mouse passes over it or by the button activate operation.
+ +
-activeforeground +color
+
Sets the foreground color of active buttons. A button is made active +when the mouse passes over it or by the button activate operation.
+ +
-background +color
+
Sets the background of the button. The default is white.
+ +
-borderwidth +pixels
+
Sets the width of the 3-D border around the button. The -relief option +determines if a border is to be drawn. The default is 1.
+ +
-closerelief relief +
+
Specifies the 3-D effect for the closed button. Relief indicates how the +button should appear relative to the widget; for example, raised means +the button should appear to protrude. The default is solid.
+ +
-cursor cursor +
+
Sets the widget's cursor. The default cursor is "".
+ +
-foreground color
+
Sets +the foreground color of buttons. The default is black.
+ +
-images images
+
Specifies +images to be displayed for the button. Images 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 images is the empty string, then a plus/minus gadget +is drawn. The default is "".
+ +
-openrelief relief
+
Specifies the 3-D effect of +the open button. Relief indicates how the button should appear relative +to the widget; for example, raised means the button should appear to protrude. + The default is flat.
+ +
-size pixels
+
Sets the requested size of the button. + The default is 0.
+
+ + +

Column Options

+Column configuration options may also +be set by the option command. The resource subclass is Column. The resource +name is the name of the column.
+option add *TreeView.Column.Foreground white
+option add *TreeView.treeView.Background blue
+

The following configuration options are available for columns. +

+ +
-background +color
+
Sets the background color of the column. This overrides the widget's +-background option. The default is white.
+ +
-borderwidth pixels
+
Sets the width +of the 3-D border of the column. The -relief option determines if a border +is to be drawn. The default is 0.
+ +
-edit boolean
+
Indicates if the column's +data fields can be edited. If boolean is false, the data fields in the +column may not be edited. The default is yes.
+ +
-foreground color
+
Specifies +the foreground color of the column. You can override this for individual +entries with the entry's -foreground option. The default is black.
+ +
-font fontName +
+
Sets the font for a column. You can override this for individual entries +with the entry's -font option. The default is *-Helvetica-Bold-R-Normal-*-12-120-*. +
+ +
-hide boolean
+
If boolean is true, the column is not displayed. The default +is yes.
+ +
-justify justify
+
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. Justify must be left, right, or center. + The default is left.
+ +
-pad pad
+
Specifies how much padding for the left and +right sides of the column. Pad is a list of one or two screen distances. + If pad has two elements, the left side of the column is padded by the +first distance and the right side by the second. If pad has just one distance, +both the left and right sides are padded evenly. The default is 2.
+ +
-relief +relief
+
Specifies the 3-D effect of the column. Relief specifies how the +column should appear relative to the widget; for example, raised means +the column should appear to protrude. The default is flat.
+ +
-state state
+
Sets +the state of the column. If state is disable then the column title can not +be activated nor invoked. The default is normal.
+ +
-text string
+
Sets the title +for the column. The default is "".
+ +
-titleforeground color
+
Sets the foreground +color of the column title. The default is black.
+ +
-titleshadow color
+
Sets +the color of the drop shadow of the column title. The default is "".
+ +
-width +pixels
+
Sets the requested width of the column. This overrides the computed +with of the column. If pixels is 0, the width is computed as from the contents +of the column. The default is 0.
+
+ + +

Text Editing Options

+Text edit window configuration +options may also be set by the option command. The resource class is TreeViewEditor. +The resource name is always edit.
+option add *TreeViewEditor.Foreground white
+option add *edit.Background blue
+

The following are the configuration options available for the text editing +window. +

+ +
-background color
+
Sets the background of the text edit window. The +default is white.
+ +
-borderwidth pixels
+
Sets the width of the 3-D border around +the edit window. The -relief option determines if a border is to be drawn. + The default is 1.
+ +
-exportselection boolean
+
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 STRING. The default is no.
+ +
-relief relief
+
Specifies +the 3-D effect of the edit window. Relief indicates how the background should +appear relative to the edit window; for example, raised means the background +should appear to protrude. The default is solid.
+ +
-selectbackground color +
+
Sets the background of the selected text in the edit window. The default +is white.
+ +
-selectborderwidth pixels
+
Sets the width of the 3-D border around +the selected text in the edit window. The -selectrelief option determines +if a border is to be drawn. The default is 1.
+ +
-selectforeground color
+
Sets +the foreground of the selected text in the edit window. The default is +white.
+ +
-selectrelief relief
+
Specifies the 3-D effect of the selected text +in the edit window. Relief indicates how the text should appear relative +to the edit window; for example, raised means the text should appear to +protrude. The default is flat.
+
+ + +

Default Bindings

+Tk automatically creates +class bindings for treeviews that give them Motif-like behavior. Much of +the behavior of a treeview widget is determined by its -selectmode option, +which selects one of two ways of dealing with the selection.

+If the selection +mode is single, only one node can be selected at a time. Clicking button +1 on an node selects it and deselects any other selected item.

+If the selection +mode is multiple, 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. +

    +
  1. In extended 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.
  2. In extended 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.
  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.
  4. Mouse button 2 may +be used for scanning. If it is pressed and dragged over the treeview widget, +the contents of the hierarchy drag at high speed in the direction the mouse +moves.
  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 browse or extended +then the new active entry is also selected and all other entries are deselected. +In extended mode the new active entry becomes the selection anchor.
  6. In extended +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.
  7. The Left and Right keys scroll +the treeview widget view left and right by the width of the character 0. +Control-Left and Control-Right scroll the treeview 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.
  8. The Prior and Next keys scroll the +treeview widget view up and down by one page (the height of the window). +
  9. The Home and End keys scroll the treeview widget horizontally to the left +and right edges, respectively.
  10. Control-Home sets the location cursor to the +the first entry, selects that entry, and deselects everything else in +the widget.
  11. Control-End sets the location cursor to the the last entry, +selects that entry, and deselects everything else in the widget.
  12. In extended +mode, Control-Shift-Home extends the selection to the first entry and Control-Shift-End +extends the selection to the last entry.
  13. In multiple mode, Control-Shift-Home +moves the location cursor to the first entry and Control-Shift-End moves +the location cursor to the last entry.
  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.
  15. In extended 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.
  16. In extended mode, the Escape +key cancels the most recent selection and restores all the entries in the +selected range to their previous selection state.
  17. Control-slash selects everything +in the widget, except in single and browse modes, in which case it selects +the active entry and deselects everything else.
  18. Control-backslash deselects +everything in the widget, except in browse mode where it has no effect. +
  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.
  20. +
+

+The behavior +of treeview widgets can be changed by defining new bindings for individual +widgets or by redefining the class bindings. +

Widget Bindings

+In addition +to the above behavior, the following additional behavior is defined by +the default widget class (TreeView) bindings. +
+ +
<ButtonPress-2>
+
Starts scanning. +
+ +
<B2-Motion>
+
Adjusts the scan.
+ +
<ButtonRelease-2>
+
Stops scanning.
+ +
<B1-Leave>
+
Starts auto-scrolling. +
+ +
<B1-Enter>
+
Starts auto-scrolling
+ +
<KeyPress-Up>
+
Moves the focus to the previous +entry.
+ +
<KeyPress-Down>
+
Moves the focus to the next entry.
+ +
<Shift-KeyPress-Up>
+
Moves +the focus to the previous sibling.
+ +
<Shift-KeyPress-Down>
+
Moves the focus to the +next sibling.
+ +
<KeyPress-Prior>
+
Moves the focus to first entry. Closed or hidden +entries are ignored.
+ +
<KeyPress-Next>
+
Move the focus to the last entry. Closed +or hidden entries are ignored.
+ +
<KeyPress-Left>
+
Closes the entry. It is not an +error if the entry has no children.
+ +
<KeyPress-Right>
+
Opens the entry, displaying +its children. It is not an error if the entry has no children.
+ +
<KeyPress-space>
+
In +"single" select mode this selects the entry. In "multiple" mode, it toggles +the entry (if it was previous selected, it is not deselected).
+ +
<KeyRelease-space>
+
Turns +off select mode.
+ +
<KeyPress-Return>
+
Sets the focus to the current entry.
+ +
<KeyRelease-Return>
+
Turns +off select mode.
+ +
<KeyPress>
+
Moves to the next entry whose label starts with +the letter typed.
+ +
<KeyPress-Home>
+
Moves the focus to first entry. Closed or +hidden entries are ignored.
+ +
<KeyPress-End>
+
Move the focus to the last entry. +Closed or hidden entries are ignored.
+ +
<KeyPress-F1>
+
Opens all entries.
+ +
<KeyPress-F2>
+
Closes +all entries (except root).
+
+ +

Button Bindings

+Buttons have bindings. There are +associated with the "all" bindtag (see the entry's -bindtag option). You +can use the bind operation to change them. +
+ +
<Enter>
+
Highlights the button of +the current entry.
+ +
<Leave>
+
Returns the button back to its normal state.
+ +
<ButtonRelease-1>
+
Adjust +the view so that the current entry is visible.
+
+ +

Entry Bindings

+Entries have +default bindings. There are associated with the "all" bindtag (see the +entry's -bindtag option). You can use the bind operation to modify them. +
+ +
<Enter>
+
Highlights +the current entry.
+ +
<Leave>
+
Returns the entry back to its normal state.
+ +
<ButtonPress-1>
+
Sets +the selection anchor the current entry.
+ +
<Double-ButtonPress-1>
+
Toggles the selection +of the current entry.
+ +
<B1-Motion>
+
For "multiple" mode only. Saves the current +location of the pointer for auto-scrolling. Resets the selection mark. +
+ +
<ButtonRelease-1>
+
For "multiple" mode only. Sets the selection anchor to the + current entry.
+ +
<Shift-ButtonPress-1>
+
For "multiple" mode only. Extends the selection. +
+ +
<Shift-Double-ButtonPress-1>
+
Place holder. Does nothing.
+ +
<Shift-B1-Motion>
+
Place holder. +Does nothing.
+ +
<Shift-ButtonRelease-1>
+
Stop auto-scrolling.
+ +
<Control-ButtonPress-1>
+
For +"multiple" mode only. Toggles and extends the selection.
+ +
<Control-Double-ButtonPress-1>
+
Place +holder. Does nothing.
+ +
<Control-B1-Motion>
+
Place holder. Does nothing.
+ +
<Control-ButtonRelease-1>
+
Stops +auto-scrolling.
+ +
<Control-Shift-ButtonPress-1>
+
???
+ +
<Control-Shift-Double-ButtonPress-1>
+
Place +holder. Does nothing.
+ +
<Control-Shift-B1-Motion>
+
Place holder. Does nothing.
+
+ +

Column +Bindings

+Columns have bindings too. They are associated with the column's +"all" bindtag (see the column -bindtag option). You can use the column bind +operation to change them. +
+ +
<Enter>
+
Highlights the current column title.
+ +
<Leave>
+
Returns +the column back to its normal state.
+ +
<ButtonRelease-1>
+
Invokes the command (see +the column's -command option) if one if specified.
+
+ +

Column Rule Bindings

+ +
+ +
<Enter>
+
Highlights +the current and activates the ruler.
+ +
<Leave>
+
Returns the column back to its +normal state. Deactivates the ruler.
+ +
<ButtonPress-1>
+
Sets the resize anchor for +the column.
+ +
<B1-Motion>
+
Sets the resize mark for the column.
+ +
<ButtonRelease-1>
+
Adjust +the size of the column, based upon the resize anchor and mark positions. +
+
+ +

Example

+The treeview command creates a new widget.
+treeview .h -bg white
+

A new Tcl command .h is also created. This command can be used to query +and modify the treeview widget. For example, to change the background +color of the table to "green", you use the new command and the widget's +configure operation.
+# Change the background color.
+.h configure -background "green"
+

By default, the treeview 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 -tree option +to specify the name of another tree.
+# View the tree "myTree".
+.h configure -tree "myTree"
+

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 0 (you can use also use the special +id root). The insert operation lets you insert one or more new entries into +the tree. The last argument is the node's pathname.
+# Create a new entry named "myEntry"
+set id [.h insert end "myEntry"]
+

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.
+# Prepend "fred".
+set id [.h insert 0 "fred"]
+

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 -label option.
+# Create a new node named "fred"
+set id [.h insert end "fred" -label "Fred Flintstone"]
+

The insert operation returns the id of the new node. You can also use the +index operation to get this information.
+# Get the id of "fred"
+.h index "fred"
+

To insert a node somewhere other than root, use the -at switch. It takes +the id of the node where the new child will be added.
+# Create a new node "barney" in "fred".
+.h insert -at $id end "barney"
+

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.
+# Create a new sub-entry of "fred"
+.h insert end "fred barney"
+

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 -autocreate configuration option to force +the creation of ancestor nodes.
+# Force the creation of ancestors.
+.h configure -autocreate yes
+.h insert end "fred barney wilma betty"
+

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 -separator 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. +
+.h configure -separator /
+.h insert end "/usr/local/tcl/bin"
+

If the path is prefixed by extraneous characters, you can automatically +trim it off using the -trim option. It removed the string from the path +before it is parsed.
+.h configure -trim C:/windows -separator /
+.h insert end "C:/window/system"
+

You can insert more than one entry at a time with the insert operation. + This can be much faster than looping over a list of names.
+# The slow way
+foreach f [glob $dir/*] {
+ .h insert end $f
+}
+# The fast way
+eval .h insert end [glob $dir/*]
+

In this case, the insert operation will return a list of ids of the new +entries.

+You can delete entries with the delete operation. It takes one +or more tags of ids as its argument. It deletes the entry and all its children. +
+.h delete $id
+

Entries have several configuration options. They control the appearance +of the entry's icon and label. We have already seen the -label option that +sets the entry's text label. The entry configure operation lets you set +or modify an entry's configuration options.
+.h entry configure $id -color red -font fixed
+

You can hide an entry and its children using the -hide option.
+.h entry configure $id -hide yes
+

More that one entry can be configured at once. All entries specified are +configured with the same options.
+.h entry configure $i1 $i2 $i3 $i4 -color brown
+

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 -icons option. It takes +a list of two image names: one to represent the open entry, another when +it is closed.
+set im1 [image create photo -file openfolder.gif]
+set im2 [image create photo -file closefolder.gif]
+.h entry configure $id -icons "$im1 $im2"
+

If -icons is set to the empty string, no icons are display.

+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 + or - symbol, but can be configured in a variety of ways using the button +configure operation. For example, the + and - symbols can be replaced with +Tk images.
+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
+

Entries can contain an arbitrary number of data fields. Data fields are +name-value pairs. Both the value and name are strings. The entry's -data option +lets you set data fields.
+.h entry configure $id -data {mode 0666 group users}
+

The -data takes a list of name-value pairs.

+You can display these data fields +as columns in the treeview widget. You can create and configure columns +with the column operation. For example, to add a new column to the widget, +use the column insert operation. The last argument is the name of the data +field that you want to display.
+.h column insert end "mode"
+

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 -text option. +
+.h column insert end "mode" -text "File Permissions"
+

Columns have several configuration options. The column configure operation +lets you query or modify column options.
+.h column configure "mode" -justify left
+

The -justify option says how the data is justified within in the column. + The -hide option indicates whether the column is displayed.
+.h column configure "mode" -hide yes
+

Entries can be selected by clicking on the mouse. Selected entries are +drawn using the colors specified by the -selectforeground and -selectbackground +configuration options. The selection itself is managed by the selection +operation.
+# Clear all selections
+.h selection clear 0 end
+# Select the root node
+.h selection set 0
+

The curselection operation returns a list of ids of all the selected entries. +
+set ids [.h curselection]
+

You can use the get operation to convert the ids to their pathnames.
+set names [eval .h get -full $ids]
+

If a treeview is exporting its selection (using the -exportselection option), +then it will observe the standard X11 protocols for handling the selection. + Treeview selections are available as type STRING; the value of the selection +will be the pathnames of the selected entries, separated by newlines.

+The +treeview supports two modes of selection: single and multiple. 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 +-selectmode option.
+.h configure -selectmode "multiple"
+

You can be notified when the list of selected entries changes. The widget's +-selectcommand specifies a Tcl procedure that is called whenever the selection +changes.
+proc SelectNotify { widget } {
+ set ids [$widget curselection]
+}
+.h configure -selectcommand "SelectNotify .h"
+

The widget supports the standard Tk scrolling and scanning operations. The +treeview can be both horizontally and vertically. You can attach scrollbars +to the treeview the same way as the listbox or canvas widgets.
+scrollbar .xbar -orient horizontal -command ".h xview"
+scrollbar .ybar -orient vertical -command ".h yview"
+.h configure -xscrollcommand ".xbar set" \
+ -yscrollcommand ".ybar set"
+

There are three different modes of scrolling: listbox, canvas, and hierbox. + In listbox mode, the last entry can always be scrolled to the top of the +widget. In hierbox mode, the last entry is always drawn at the bottom of +the widget. The scroll mode is set by the widget's -selectmode option.
+.h configure -scrollmode "listbox"
+

Entries can be programmatically opened or closed using the open and close +operations respectively.
+.h open $id
+.h close $id
+

When an entry is opened, a Tcl procedure can be automatically invoked. The +-opencommand option specifies this procedure. This procedure can lazily +insert entries as needed.
+proc AddEntries { dir } {
+ eval .h insert end [glob -nocomplain $dir/*]
+}
+.h configure -opencommand "AddEntries %P"
+

Now when an entry is opened, the procedure AddEntries is called and adds +children to the entry. Before the command is invoked, special "%" substitutions +(like bind) are performed. Above, %P is translated to the pathname of the +entry.

+The same feature exists when an entry is closed. The -closecommand +option specifies the procedure.
+proc DeleteEntries { id } {
+ .h entry delete $id 0 end
+}
+.h configure -closecommand "DeleteEntries %#"
+

When an entry is closed, the procedure DeleteEntries is called and deletes +the entry's children using the entry delete operation (%# is the id of entry). + +

Keywords

+treeview, widget

+ +


+Table of Contents

+

+ 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 @@ + + + + + +vector(n) manual page + + +Table of Contents

+ +

Name

+vector - Vector data type for Tcl +

Synopsis

+vector +create vecName ?vecName...? ?switches?

+vector destroy vecName ?vecName...?

+vector +expr expression

+vector names ?pattern...? +

Description

+The vector 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. + +

Introduction

+A vector is simply an ordered set of numbers. The components +of a vector are real numbers, indexed by counting numbers.

+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.

+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. +

  • 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.
  • ·
  • 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.
  • ·
  • 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.
  • +
+

+The vector +command tries to overcome these disadvantages while still retaining the +ease of use of Tcl arrays. The vector 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 +parray 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. +

Example

+You create +vectors using the vector command and its create operation.
+# Create a new vector.
+vector create y(50)
+

This creates a new vector named y. It has fifty components, by default, +initialized to 0.0. In addition, both a Tcl command and array variable, +both named y, are created. You can use either the command or variable to +query or modify components of the vector.
+# Set the first value.
+set y(0) 9.25
+puts "y has [y length] components"
+

The array y 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 y.
+# This is an error. The vector only has 50 components.
+set y(50) 0.02
+

You can also specify a range of indices using a colon (:) to separate the +first and last indices of the range.
+# Set the first six components of y
+set y(0:5) 25.2
+

If you don't include an index, then it will default to the first and/or +last component of the vector.
+# Print out all the components of y
+puts "y = $y(:)"
+

There are special non-numeric indices. The index end, 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 ++end 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.
+# Extend the vector by one component.
+set y(++end) 0.02
+

The other special indices are min and max. They return the current smallest +and largest components of the vector.
+# Print the bounds of the vector
+puts "min=$y(min) max=$y(max)"
+

To delete components from a vector, simply unset the corresponding array +element. In the following example, the first component of y is deleted. +All the remaining components of y will be moved down by one index as the +length of the vector is reduced by one.
+# Delete the first component
+unset y(0)
+puts "new first element is $y(0)"
+

The vector's Tcl command can also be used to query or set the vector.
+# 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 }
+

Here we've created a vector x without a initial length specification. In +this case, the length is zero. The set operation resets the vector, extending +it and setting values for each new component.

+There are several operations +for vectors. The range operation lists the components of a vector between +two indices.
+# List the components
+puts "x = [x range 0 end]"
+

You can search for a particular value using the search operation. It returns +a list of indices of the components with the same value. If no component +has the same value, it returns "".
+# Find the index of the biggest component
+set indices [x search $x(max)]
+

Other operations copy, append, or sort vectors. You can append vectors +or new values onto an existing vector with the append operation.
+# Append assorted vectors and values to x
+x append x2 x3 { 2.3 4.5 } x4
+

The sort 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.
+# Sort the data points
+x sort y
+

The vector x is sorted while the components of y are rearranged so that +the original x,y coordinate pairs are retained.

+The expr operation lets +you perform arithmetic on vectors. The result is stored in the vector. +
+# Add the two vectors and a scalar
+x expr { x + y }
+x expr { x * 2 }
+

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 graph 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 notify operation.
+# Make vector x notify after every change
+x notify always
+    ...
+# Never notify
+x notify never
+    ...
+# Force notification now
+x notify now
+

To delete a vector, use the vector delete command. Both the vector and +its corresponding Tcl command are destroyed.
+# Remove vector x
+vector destroy x
+ +

Syntax

+Vectors are created using the vector create operation. Th create +operation can be invoked in one of three forms: +
+ +
vector create vecName
+
This +creates a new vector vecName which initially has no components.
+ +
vector create +vecName(size)
+
This second form creates a new vector which will contain +size number of components. The components will be indexed starting from +zero (0). The default value for the components is 0.0.
+ +
vector create vecName(first:last) +
+
The last form creates a new vector of indexed first through last. First +and last can be any integer value so long as first is less than last.
+
+

+Vector +names must start with a letter and consist of letters, digits, or underscores. +
+# Error: must start with letter
+vector create 1abc
+

You can automatically generate vector names using the "#auto" vector name. + The create operation will generate a unique vector name.
+set vec [vector create #auto]
+puts "$vec has [$vec length] components"
+ +

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.

+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 offset operation to change a vector's indices +on-the-fly.
+puts $vecName(0)
+vecName offset -5
+puts $vecName(-5)
+

You can also use numeric expressions as indices. The result of the expression +must be an integer value.
+set n 21
+set vecName($n+3) 50.2
+

The following special non-numeric indices are available: min, max, end, +and ++end.
+puts "min = $vecName($min)"
+set vecName(end) -1.2
+

The indices min and max will return the minimum and maximum values of the +vector. The index end returns the value of the last component in the vector. + The index ++end is used to append new value onto the vector. It automatically +extends the vector by one component and sets its value.
+# Append an new component to the end
+set vecName(++end) 3.2
+

A range of indices can be indicated by a colon (:).
+# Set the first six components to 1.0
+set vecName(0:5) 1.0
+

If no index is supplied the first or last component is assumed.
+# Print the values of all the components
+puts $vecName(:)
+ +

Vector Operations

+ +
+ +
vector create vecName?(size)?... ?switches?
+
The create operation +creates a new vector vecName. Both a Tcl command and array variable vecName +are also created. The name vecName 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 vecName is unset, the vector and its Tcl command +are also destroyed.

+The vector has optional switches that affect how the +vector is created. They are as follows:

+ +
-variable varName
+
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 varName is the empty string, then +no variable will be mapped. You can always map a variable back to the vector +using the vector's variable operation.
+ +
-command cmdName
+
Maps a Tcl command +to the vector. The vector can be accessed using cmdName and one of the +vector instance operations. A Tcl command by that name cannot already +exist. If cmdName is the empty string, no command mapping will be made.
+ +
-watchunset +boolean
+
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 boolean +to "true" to get the old behavior.
+
+ + +
+ +
vector destroy vecName ?vecName...?
+
+ +
vector +expr expression
+
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.

+The valid operators are listed +below, grouped in decreasing order of precedence:

+ +
- !
+
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.
+ +
^
+
Exponentiation.
+ +
* + / %
+
Multiply, divide, remainder.
+ +
+ -
+
Add and subtract.
+ +
<< >>
+
Left and +right shift. Circularly shifts the values of the vector (not implemented +yet).
+ +
< > <= >=
+
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.
+ +
== !=
+
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.
+ +
|
+
Bit-wise OR. + (Not implemented).
+ +
&&
+
Logical AND. Produces a 1 result if both operands are +non-zero, 0 otherwise.
+ +
||
+
Logical OR. Produces a 0 result if both operands +are zero, 1 otherwise.
+ +
x?y:z
+
If-then-else, as in C. (Not implemented yet). +
+
+

+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.

+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.
+

+acos    cos    hypot    sinh
+asin    cosh    log    sqrt
+atan    exp    log10    tan
+ceil    floor    sin    tanh
+

Additional functions are: +

+ +
abs
+
Returns the absolute value of each component. +
+ +
random
+
Returns a vector of non-negative values uniformly distributed between +[0.0, 1.0) using drand48. The seed comes from the internal clock of the machine +or may be set manual with the srandom function.
+ +
round
+
Rounds each component +of the vector.
+ +
srandom
+
Initializes the random number generator using srand48. +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.
+
+

+The following functions return a single +value. +

+ +
adev
+
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).
+ +
kurtosis
+
Returns the degree of peakedness (fourth +moment) of the vector.
+ +
length
+
Returns the number of components in the vector. +
+ +
max
+
Returns the vector's maximum value.
+ +
mean
+
Returns the mean value of the +vector.
+ +
median
+
Returns the median of the vector.
+ +
min
+
Returns the vector's +minimum value.
+ +
q1
+
Returns the first quartile of the vector.
+ +
q3
+
Returns the +third quartile of the vector.
+ +
prod
+
Returns the product of the components. +
+ +
sdev
+
Returns the standard deviation (defined as the square root of the +variance) of the vector.
+ +
skew
+
Returns the skewness (or third moment) of +the vector. This characterizes the degree of asymmetry of the vector about +the mean.
+ +
sum
+
Returns the sum of the components.
+ +
var
+
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.
+
+

+The last set returns a vector of the same length +as the argument. +

+ +
norm
+
Scales the values of the vector to lie in the range +[0.0..1.0].
+ +
sort
+
Returns the vector components sorted in ascending order.
+
+ + +
+ +
vector +names ?pattern?
+
+
+ +

Instance Operations

+You can also use the vector's Tcl command +to query or modify it. The general form is
+

+vecName operation ?arg?...
+

Both operation and its arguments determine the exact behavior of the command. + The operations available for vectors are listed below. +

+ +
vecName append item +?item?...
+
Appends the component values from item to vecName. Item can be either +the name of a vector or a list of numeric values.
+ +
vecName clear
+
Clears +the element indices from the array variable associated with vecName. 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.
+ +
vecName delete index ?index?...
+
Deletes the indexth component from +the vector vecName. Index is the index of the element to be deleted. This +is the same as unsetting the array variable element index. The vector is +compacted after all the indices have been deleted.
+ +
vecName dup destName +
+
Copies vecName to destName. DestName is the name of a destination vector. + If a vector destName already exists, it is overwritten with the components +of vecName. Otherwise a new vector is created.
+ +
vecName expr expression +
+
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.
+ +
vecName length ?newSize?
+
Queries or resets the number +of components in vecName. NewSize is a number specifying the new size of +the vector. If newSize is smaller than the current size of vecName, vecName +is truncated. If newSize is greater, the vector is extended and the new +components are initialized to 0.0. If no newSize argument is present, the +current length of the vector is returned.
+ +
vecName merge srcName ?srcName?... +
+
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.
+ +
vecName notify keyword
+
Controls how vector clients are notified of +changes to the vector. The exact behavior is determined by keyword.
+ +
always +
+
Indicates that clients are to be notified immediately whenever the vector +is updated.
+ +
never
+
Indicates that no clients are to be notified.
+ +
whenidle +
+
Indicates that clients are to be notified at the next idle point whenever +the vector is updated.
+ +
now
+
If any client notifications is currently pending, +they are notified immediately.
+ +
cancel
+
Cancels pending notifications of clients +using the vector.
+ +
pending
+
Returns 1 if a client notification is pending, +and 0 otherwise.
+
+ + +
+ +
vecName offset ?value?
+
Shifts the indices of the vector +by the amount specified by value. Value is an integer number. If no value +argument is given, the current offset is returned.
+ +
vecName populate destName +?density?
+
Creates a vector destName which is a superset of vecName. DestName +will include all the components of vecName, in addition the interval between +each of the original components will contain a density 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. +
+ +
vecName range firstIndex ?lastIndex?...
+
Returns a list of numeric values representing +the vector components between two indices. Both firstIndex and lastIndex +are indices representing the range of components to be returned. If lastIndex +is less than firstIndex, the components are listed in reverse order.
+ +
vecName +search value ?value?
+
Searches for a value or range of values among the +components of vecName. If one value argument is given, a list of indices +of the components which equal value is returned. If a second value 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 "" is returned. +
+ +
vecName set item
+
Resets the components of the vector to item. Item can be +either a list of numeric expressions or another vector.
+ +
vecName seq start +?finish? ?step?
+
Generates a sequence of values starting with the value +start. Finish indicates the terminating value of the sequence. The vector +is automatically resized to contain just the sequence. If three arguments +are present, step designates the interval.

+With only two arguments (no +finish argument), the sequence will continue until the vector is filled. + With one argument, the interval defaults to 1.0.

+ +
vecName sort ?-reverse? +?argName?...
+
Sorts the vector vecName in increasing order. If the -reverse +flag is present, the vector is sorted in decreasing order. If other arguments +argName are present, they are the names of vectors which will be rearranged +in the same manner as vecName. Each vector must be the same length as vecName. +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.
+ +
vecName variable varName
+
Maps +a Tcl variable to the vector, creating another means for accessing the +vector. The variable varName can't already exist. This overrides any current +variable mapping the vector may have.
+
+ + +

C Language API

+You can create, modify, +and destroy vectors from C code, using library routines. You need to +include the header file blt.h. It contains the definition of the structure +Blt_Vector, which represents the vector. It appears below.
+typedef struct {
+ double *valueArr;
+ int numValues;
+ int arraySize;
+ double min, max;
+} Blt_Vector;
+

The field valueArr points to memory holding the vector components. The +components are stored in a double precision array, whose size size is represented +by arraySize. NumValues is the length of vector. The size of the array +is always equal to or larger than the length of the vector. Min and max +are minimum and maximum component values. +

Library Routines

+The following +routines are available from C to manage vectors. Vectors are identified +by the vector name.

+Blt_CreateVector

+
+ +
Synopsis:
+

+int Blt_CreateVector (interp, vecName, length, vecPtrPtr)
+
Tcl_Interp *interp;
+char *vecName;
+int length;
+Blt_Vector **vecPtrPtr;
+
+
+
+ +
+ +

Description:

+
Creates a new vector vecName with a length of length. Blt_CreateVector +creates both a new Tcl command and array variable vecName. Neither a command +nor variable named vecName can already exist. A pointer to the vector +is placed into vecPtrPtr.
+ +
Results:
+
Returns TCL_OK if the vector is successfully +created. If length is negative, a Tcl variable or command vecName already +exists, or memory cannot be allocated for the vector, then TCL_ERROR is +returned and interp->result will contain an error message.
+
+ +

+

+Blt_DeleteVectorByName +

+
+ +
Synopsis:
+

+int Blt_DeleteVectorByName (interp, vecName)
+
Tcl_Interp *interp;
+char *vecName;
+
+
+
+ +
+ +

Description:

+
Removes the vector vecName. VecName is the name of a vector +which must already exist. Both the Tcl command and array variable vecName +are destroyed. All clients of the vector will be notified immediately that +the vector has been destroyed.
+ +
Results:
+
Returns TCL_OK if the vector is +successfully deleted. If vecName is not the name a vector, then TCL_ERROR +is returned and interp->result will contain an error message.
+
+ +

+

+Blt_DeleteVector +

+
+ +
Synopsis:
+

+int Blt_DeleteVector (vecPtr)
+
Blt_Vector *vecPtr;
+
+
+
+ +
+ +

Description:

+
Removes the vector pointed to by vecPtr. VecPtr is a pointer +to a vector, typically set by Blt_GetVector or Blt_CreateVector. 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. +
+ +
Results:
+
Returns TCL_OK if the vector is successfully deleted. If vecName +is not the name a vector, then TCL_ERROR is returned and interp->result will +contain an error message.
+
+ +

+

+Blt_GetVector

+
+ +
Synopsis:
+

+int Blt_GetVector (interp, vecName, vecPtrPtr)
+
Tcl_Interp *interp;
+char *vecName;
+Blt_Vector **vecPtrPtr;
+
+
+
+ +
+ +

Description:

+
Retrieves the vector vecName. VecName is the name of a vector +which must already exist. VecPtrPtr will point be set to the address of +the vector.
+ +
Results:
+
Returns TCL_OK if the vector is successfully retrieved. + If vecName is not the name of a vector, then TCL_ERROR is returned and +interp->result will contain an error message.
+
+ +

+

+Blt_ResetVector

+

+
+ +
Synopsis:
+

+int Blt_ResetVector (vecPtr, dataArr,
+    numValues, arraySize, freeProc)
+
Blt_Vector *vecPtr;
+double *dataArr;
+int *numValues;
+int *arraySize;
+Tcl_FreeProc *freeProc;
+
+
+
+ +
+ +

Description:

+
Resets the components of the vector pointed to by vecPtr. +Calling Blt_ResetVector will trigger the vector to dispatch notifications +to its clients. DataArr is the array of doubles which represents the vector +data. NumValues is the number of elements in the array. ArraySize is the +actual size of the array (the array may be bigger than the number of values +stored in it). FreeProc indicates how the storage for the vector component +array (dataArr) was allocated. It is used to determine how to reallocate +memory when the vector is resized or destroyed. It must be TCL_DYNAMIC, +TCL_STATIC, TCL_VOLATILE, or a pointer to a function to free the memory +allocated for the vector array. If freeProc is TCL_VOLATILE, it indicates +that dataArr must be copied and saved. If freeProc is TCL_DYNAMIC, it indicates +that dataArr was dynamically allocated and that Tcl should free dataArr +if necessary. Static indicates that nothing should be done to release storage +for dataArr.
+ +
Results:
+
Returns TCL_OK if the vector is successfully resized. + If newSize is negative, a vector vecName does not exist, or memory cannot +be allocated for the vector, then TCL_ERROR is returned and interp->result +will contain an error message.
+
+ +

+

+Blt_ResizeVector

+
+ +
Synopsis:
+

+int Blt_ResizeVector (vecPtr, newSize)
+
Blt_Vector *vecPtr;
+int newSize;
+
+
+
+ +
+ +

Description:

+
Resets the length of the vector pointed to by vecPtr to newSize. + If newSize is smaller than the current size of the vector, it is truncated. + If newSize is greater, the vector is extended and the new components are +initialized to 0.0. Calling Blt_ResetVector will trigger the vector to dispatch +notifications.
+ +
Results:
+
Returns TCL_OK if the vector is successfully resized. + If newSize is negative or memory can not be allocated for the vector, + then TCL_ERROR is returned and interp->result will contain an error message. +

+

+
+

+Blt_VectorExists

+
+ +
Synopsis:
+

+int Blt_VectorExists (interp, vecName)
+
Tcl_Interp *interp;
+char *vecName;
+
+
+
+ +
+ +

Description:

+
Indicates if a vector named vecName exists in interp.
+ +
Results: +
+
Returns 1 if a vector vecName exists and 0 otherwise.
+
+ +

+

+If your application +needs to be notified when a vector changes, it can allocate a unique client +identifier 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.

+The call-back routine must of +the following type.
+


+typedef void (Blt_VectorChangedProc) (Tcl_Interp *interp,
+
ClientData clientData, Blt_VectorNotify notify);
+
+
+
+

ClientData is passed to this routine whenever it is called. You can use +this to pass information to the call-back. The notify argument indicates +whether the vector has been updated of destroyed. It is an enumerated type. +
+


+typedef enum {
+ BLT_VECTOR_NOTIFY_UPDATE=1,
+ BLT_VECTOR_NOTIFY_DESTROY=2
+} Blt_VectorNotify;
+
+
+

+

Blt_AllocVectorId

+
+ +
Synopsis:
+

+Blt_VectorId Blt_AllocVectorId (interp, vecName)
+
Tcl_Interp *interp;
+char *vecName;
+
+
+
+ +
+ +

Description:

+
Allocates an client identifier for with the vector vecName. +This identifier can be used to specify a call-back which is triggered when +the vector is updated or destroyed.
+ +
Results:
+
Returns a client identifier +if successful. If vecName is not the name of a vector, then NULL is returned +and interp->result will contain an error message.
+
+ +

+

+Blt_GetVectorById

+
+ +
Synopsis: +
+

+int Blt_GetVector (interp, clientId, vecPtrPtr)
+
Tcl_Interp *interp;
+Blt_VectorId clientId;
+Blt_Vector **vecPtrPtr;
+
+
+
+ +
+ +

Description:

+
Retrieves the vector used by clientId. ClientId is a valid +vector client identifier allocated by Blt_AllocVectorId. VecPtrPtr will +point be set to the address of the vector.
+ +
Results:
+
Returns TCL_OK if the +vector is successfully retrieved.
+
+ +

+

+Blt_SetVectorChangedProc

+
+ +
Synopsis:
+

+void Blt_SetVectorChangedProc (clientId, proc, clientData);
+
Blt_VectorId clientId;
+Blt_VectorChangedProc *proc;
+ClientData *clientData;
+
+
+
+ +
+ +

Description:

+
Specifies a call-back routine to be called whenever the vector +associated with clientId is updated or deleted. Proc is a pointer to call-back +routine and must be of the type Blt_VectorChangedProc. ClientData is a +one-word value to be passed to the routine when it is invoked. If proc is +NULL, then the client is not notified.
+ +
Results:
+
The designated call-back +procedure will be invoked when the vector is updated or destroyed.
+
+ +

+

+Blt_FreeVectorId +

+
+ +
Synopsis:
+

+void Blt_FreeVectorId (clientId);
+
Blt_VectorId clientId;
+
+
+
+ +
+ +

Description:

+
Frees the client identifier. Memory allocated for the identifier + is released. The client will no longer be notified when the vector is +modified.
+ +
Results:
+
The designated call-back procedure will be no longer be +invoked when the vector is updated or destroyed.
+
+ +

+

+Blt_NameOfVectorId

+
+ +
Synopsis: +
+

+char *Blt_NameOfVectorId (clientId);
+
Blt_VectorId clientId;
+
+
+
+ +
+ +

Description:

+
Retrieves the name of the vector associated with the client +identifier clientId.
+ +
Results:
+
Returns the name of the vector associated +with clientId. If clientId is not an identifier or the vector has been +destroyed, NULL is returned.
+
+ +

+

+Blt_InstallIndexProc

+
+ +
Synopsis:
+

+void Blt_InstallIndexProc (indexName, procPtr)
+
char *indexName;
+Blt_VectorIndexProc *procPtr;
+
+
+
+ +
+ +

Description:

+
Registers a function to be called to retrieved the index +indexName from the vector's array variable.

+typedef double Blt_VectorIndexProc(Vector +*vecPtr);

+The function will be passed a pointer to the vector. The function +must return a double representing the value at the index.

+ +
Results:
+
The new +index is installed into the vector.
+
+ + + +

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 +Blt_VectorExists, Blt_GetVector is called to get the pointer to the vector. +Otherwise the routine Blt_CreateVector 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 lt_ResetVector is called. + Blt_ResetVector makes the changes visible to the Tcl interface and other +vector clients (such as a graph widget).

+
+#include <tcl.h>
+#include <blt.h>                
+ + + +

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.
+proc doit {} {
+ # Temporary vector x
+ vector x(10)
+ set x(9) + 2.0
+ ...
+}
+

+

This has changed. Variables are not automatically destroyed when their +variable is unset. You can restore the old behavior by setting the "-watchunset" +switch. +

Keywords

+vector, graph, widget

+ +


+Table of Contents

+

+ 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 @@ + + + + + +watch(n) manual page + + +Table of Contents

+ +

Name

+watch - call Tcl procedures before and after each +command +

Synopsis

+watch create watchName ?options?

+watch activate watchName +

+watch deactivate watchName

+watch delete watchName

+watch configure watchName +?options

+watch info watchName

+watch names +

Description

+The watch command +arranges for Tcl procedures to be invoked before and after the execution +of each Tcl command. +

Introduction

+When an error occurs in Tcl, the global +variable errorInfo 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. +

+The watch 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 +if and set), not just Tcl procedures. +

Example

+The following example use watch +to trace Tcl commands (printing to standard error) both before and after +they are executed.
+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 => $argv0= ($retcode) $results"
+}
+watch create trace \
+    -postcmd postCmd -precmd preCmd
+ +

Operations

+The following operations are available for the watch command: + +
+ +
watch activate watchName
+
Activates the watch, causing Tcl commands the +be traced to the maximum depth selected.
+ +
watch create watchName ?options?... +
+
Creates a new watch watchName. It's an error if another watch watchName +already exists and an error message will be returned. Options may have any +of the values accepted by the watch configure command. This command returns +the empty string.
+ +
watch configure watchName ?options...?
+
Queries or modifies +the configuration options of the watch watchName. WatchName is the name +of a watch. Options may have any of the following values:
+ +
-active boolean +
+
Specifies if the watch is active. By default, watches are active when created. +
+ +
-postcmd string
+
Specifies a Tcl procedure to be called immediately after +each Tcl command. String is name of a Tcl procedure and any extra arguments +to be passed to it. Before string 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.
+ +
-precmd string
+
Specifies a Tcl procedure +to be called immediately before each Tcl command. String is name of a Tcl +procedure and any extra arguments to be passed to it. Before string 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 -precmd procedure is always +ignored.
+ +
-maxlevel number
+
Specifies the maximum evaluation depth to watch +Tcl commands. The default maximum level is 10000.
+
+ + +
+ +
watch deactivate watchName +
+
Deactivates the watch. The -precmd and -postcmd procedures will no longer +be invoked.
+ +
watch info watchName
+
Returns the configuration information +associated with the watch watchName. WatchName is the name of a watch. +
+ +
watch names ?state?
+
Lists the names of the watches for a given state. State +may be one of the following: active, idle, or ignore. If a state argument +isn't specified, all watches are
+ listed.
+
+ + +

Keywords

+debug, profile

+ +


+Table of Contents

+

+ 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 @@ + + + + + +winop(n) manual page + + +Table of Contents

+ +

Name

+winop - Perform assorted window operations +

Synopsis

+winop +lower ?window?...

+winop map ?window?...

+winop move window x y

+winop raise ?window?... +

+winop snap window photoName

+winop unmap ?window?...

+winop warpto ?window? + +

Description

+The winop command performs various window operations on Tk windows +using low-level Xlib function calls to work around window manager pecularities. + +

Introduction

+Tk has several commands for manipulating its windows: raise, +lower, wm, 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.

+For example, if you positioned a toplevel window +using wm geometry, 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.

+In situations like these, the winop command +can be used to workaround these difficulties. Instead, it makes low-level +Xlib (such XRaiseWindow and XMapWindow) calls to perform these operations. +
+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
+ +

Operations

+The following operations are available for the winop command: + +
+ +
winop lower ?window?...
+
Lowers window to the bottom of the X window stack. + Window is the path name of a Tk window.
+ +
winop map ?window?...
+
Maps window +on the screen. Window is the path name of a Tk window. If window is already +mapped, this command has no effect.
+ +
winop move window x y
+
Move window +to the screen location specified by x and y. Window is the path name of +a Tk window, while x and y are screen coordinates. This command returns + the empty string.
+ +
winop raise ?window?...
+
Raises window to the top of the +X window stack. Window must be a valid path name of a Tk window. This command +returns the empty string.
+ +
winop snap window photoName
+
Takes a snapshot of +the window and stores the contents in the photo image photoName. Window +is the valid path name of a Tk window which must be totally visible (unobscured). + PhotoName 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.
+ +
winop unmap ?window?...
+
Unmaps window from the screen. Window is +the path name of a Tk window.
+ +
winop warpto ?window?
+
Warps the pointer to +window. Window is the path name of a Tk window which must be mapped. If window +is in the form @x,y, where x and y are root screen coordinates, the pointer +is warped to that location on the screen.

+[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.]

+If no window argument is present the current +location of the pointer is returned. The location is returned as a list +in the form "x y", where x and y are the current coordinates of the pointer. +

+
+ +

Keywords

+window, map, raise, lower, pointer, warp

+ +


+Table of Contents

+

+ 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 binding. + set corner "first" + bind $graph {} + } +} + +# ---------------------------------------------------------------------- +# +# 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 [code $this UpdateOutline %x %y] + + # Indicate the next corner is the last + set corner last + } else { + + # Stop watching motion events + bind $graph {} + + # 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 +# +# INPUTS +# = interpreter for target application +# = pathname for target drag&drop window +# = 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 +# +# INPUTS +# = interpreter for target application +# = pathname for target drag&drop window +# = 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 +# +# INPUTS +# = interpreter for target application +# = pathname for target drag&drop window +# = 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 +# +# INPUTS +# = interpreter for target application +# = pathname for target drag&drop window +# = 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 [list $cmd select %W %X %Y %t] + bind BltDndButton$i [list $cmd drag %W %X %Y] + bind BltDndButton$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. +# +# Starts the drag operation. +# Updates the drag. +# 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 [list $cmd drag %W %X %Y] + bind BltDrag&DropButton$i [list $cmd drag %W %X %Y] + bind BltDrag&DropButton$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. +# +# Starts the drag operation. +# Updates the drag. +# 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 [list blt::ActivateLegend $graph ] + $graph legend bind all [list blt::DeactivateLegend $graph] + $graph legend bind all [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 { + %W crosshairs off + } + bind crosshairs-$graph { + %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 { + set bltHierbox(cursor) [%W cget -cursor] + %W configure -cursor hand1 + %W scan mark %x %y +} + +bind Hierbox { + %W scan dragto %x %y +} + +bind Hierbox { + %W configure -cursor $bltHierbox(cursor) +} + +bind Hierbox { + if { $bltHierbox(scroll) } { + blt::Hierbox::AutoScroll %W + } +} + +bind Hierbox { + 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 { + blt::Hierbox::MoveFocus %W up + if { $bltHierbox(space) } { + %W selection toggle focus + } +} + +bind Hierbox { + blt::Hierbox::MoveFocus %W down + if { $bltHierbox(space) } { + %W selection toggle focus + } +} + +bind Hierbox { + blt::Hierbox::MoveFocus %W prevsibling +} + +bind Hierbox { + blt::Hierbox::MoveFocus %W nextsibling +} + +bind Hierbox { + blt::Hierbox::MovePage %W top +} + +bind Hierbox { + blt::Hierbox::MovePage %W bottom +} + +bind Hierbox { + %W close focus +} +bind Hierbox { + %W open focus + %W see focus -anchor w +} + +bind Hierbox { + blt::HierboxToggle %W focus + set bltHierbox(space) on +} + +bind Hierbox { + set bltHierbox(space) off +} + +bind Hierbox { + blt::HierboxToggle %W focus + set bltHierbox(space) on +} + +bind Hierbox { + set bltHierbox(space) off +} + +bind Hierbox { + blt::Hierbox::NextMatchingEntry %W %A +} + +bind Hierbox { + blt::Hierbox::MoveFocus %W root +} + +bind Hierbox { + blt::Hierbox::MoveFocus %W end +} + +bind Hierbox { + %W open -r root +} + +bind Hierbox { + eval %W close -r [%W entry children root 0 end] +} + +# ---------------------------------------------------------------------- +# USAGE: blt::HierboxToggle +# Arguments: hierbox hierarchy widget +# +# Invoked when the user presses the space bar. Toggles the selection +# for the entry at . +# ---------------------------------------------------------------------- +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 +# Arguments: hierbox hierarchy widget +# +# Invoked by KeyPress bindings. Pages the current view up or down. +# The 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 { + 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 { + %W entry highlight current + } + $widget bind Entry { + %W entry highlight "" + } + + # + # Button bindings + # + $widget button bind all { + %W see -anchor nw current + %W toggle current + } + $widget button bind all { + %W button highlight current + } + $widget button bind all { + %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 { + blt::Hierbox::SetSelectionAnchor %W current + set bltHierbox(scroll) 1 + } + + $widget bin Entry { + %W toggle current + } + + # + # B1-Motion + # + # For "multiple" mode only. Saves the current location of the + # pointer for auto-scrolling. + # + $widget bind Entry { + 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 { + 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 { + 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 { + # do nothing + } + $widget bind Entry { + after cancel $bltHierbox(afterId) + set bltHierbox(scroll) 0 + } + + # + # Control-ButtonPress-1 + # + # For "multiple" mode only. + # + $widget bind Entry { + 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 { + # do nothing + } + $widget bind Entry { + after cancel $bltHierbox(afterId) + set bltHierbox(scroll) 0 + } + # + # Control-Shift-ButtonPress-1 + # + # For "multiple" mode only. + # + $widget bind Entry { + 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 { + # do nothing + } +} + + +# ---------------------------------------------------------------------- +# USAGE: blt::Hierbox::AutoScroll +# +# 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 +# Arguments: hierbox hierarchy widget +# +# Invoked by KeyPress bindings. Searches for an entry that starts +# with the letter 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 +# +# Invoked by KeyPress bindings. Moves the active selection to the +# entry , 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 { + set blt::Hiertable::cursor [%W cget -cursor] + %W configure -cursor hand1 + %W scan mark %x %y +} + +bind Hiertable { + %W scan dragto %x %y +} + +bind Hiertable { + %W configure -cursor $blt::Hiertable::cursor +} + +bind Hiertable { + if { $blt::Hiertable::scroll } { + blt::Hiertable::AutoScroll %W + } +} + +bind Hiertable { + 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 { + blt::Hiertable::MoveFocus %W up + if { $blt::Hiertable::space } { + %W selection toggle focus + } +} + +bind Hiertable { + blt::Hiertable::MoveFocus %W down + if { $blt::Hiertable::space } { + %W selection toggle focus + } +} + +bind Hiertable { + blt::Hiertable::MoveFocus %W prevsibling +} + +bind Hiertable { + blt::Hiertable::MoveFocus %W nextsibling +} + +bind Hiertable { + blt::Hiertable::MovePage %W top +} + +bind Hiertable { + blt::Hiertable::MovePage %W bottom +} + +bind Hiertable { + %W close focus +} +bind Hiertable { + %W open focus + %W see focus -anchor w +} + +bind Hiertable { + 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 { + set blt::Hiertable::space off +} + +bind Hiertable { + blt::Hiertable::MoveFocus %W focus + set blt::Hiertable::space on +} + +bind Hiertable { + set blt::Hiertable::space off +} + +bind Hiertable { + blt::Hiertable::NextMatchingEntry %W %A +} + +bind Hiertable { + blt::Hiertable::MoveFocus %W top +} + +bind Hiertable { + blt::Hiertable::MoveFocus %W bottom +} + +bind Hiertable { + %W open -r root +} + +bind Hiertable { + 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 +# +# 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 { + %W entry highlight current + } + $widget bind Entry { + %W entry highlight "" + } + + # + # Button bindings + # + $widget button bind all { + %W see -anchor nw current + %W toggle current + } + $widget button bind all { + %W button highlight current + } + $widget button bind all { + %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 { + blt::Hiertable::SetSelectionAnchor %W current + set blt::Hiertable::scroll 1 + } + + $widget bind Entry { + %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 { + 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 { + 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 { + 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 { + puts + # do nothing + } + $widget bind Entry { + # do nothing + } + $widget bind Entry { + after cancel $blt::Hiertable::afterId + set blt::Hiertable::scroll 0 + } + + # + # Control-ButtonPress-1 + # + # For "multiple" mode only. + # + $widget bind Entry { + 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 { + puts + # do nothing + } + $widget bind Entry { + # do nothing + } + $widget bind Entry { + after cancel $blt::Hiertable::afterId + set blt::Hiertable::scroll 0 + } + + $widget bind Entry { + 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 { + puts + # do nothing + } + $widget bind Entry { + # do nothing + } + $widget column bind all { + %W column highlight [%W column current] + } + $widget column bind all { + %W column highlight "" + } + $widget column bind Rule { + %W column highlight [%W column current] + %W column resize activate [%W column current] + } + $widget column bind Rule { + %W column highlight "" + %W column resize activate "" + } + $widget column bind Rule { + %W column resize anchor %x + } + $widget column bind Rule { + %W column resize mark %x + } + $widget column bind Rule { + %W column configure [%W column current] -width [%W column resize set] + } + $widget column bind all { + set column [%W column nearest %x %y] + if { $column != "" } { + %W column invoke $column + } + } +} + +# ---------------------------------------------------------------------- +# USAGE: blt::Hiertable::AutoScroll +# +# 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 +# +# Invoked by KeyPress bindings. Moves the active selection to the +# entry , 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 +# Arguments: hiertable hierarchy widget +# +# Invoked by KeyPress bindings. Pages the current view up or down. +# The 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 +# Arguments: hiertable hierarchy widget +# +# Invoked by KeyPress bindings. Searches for an entry that starts +# with the letter 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 { + 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 { + %W text icursor @%x,%y +} + +bind HiertableEditor { + %W text icursor last +} +bind HiertableEditor { + %W text icursor next +} +bind HiertableEditor { + 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 { + 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 { + %W text icursor 0 +} +bind HiertableEditor { + 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 { + %W text icursor end +} +bind HiertableEditor { + 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 { + if { [%W text selection present]} { + %W text delete sel.first sel.last + } else { + %W text delete insert + } +} + +bind HiertableEditor { + 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 { + %W text selection from insert +} + +bind HiertableEditor { + %W text selection from insert +} + +bind ${className}Editor { + %W text selection adjust insert +} + +bind ${className}Editor { + %W text selection adjust insert +} + +bind ${className}Editor { + %W text selection range 0 end +} + +bind ${className}Editor { + %W text selection clear +} + +bind ${className}Editor { + 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 +# class binding will also fire and insert the character, +# which is wrong. Ditto for Escape, Return, and Tab. + +bind ${className}Editor { + # nothing +} + +bind ${className}Editor { + # nothing +} + +bind ${className}Editor { + # nothing +} + +bind ${className}Editor { + %W text cancel + grab release %W +} + +bind ${className}Editor { + %W text apply + grab release %W +} + +bind ${className}Editor { + blt::tv::InsertText %W "\n" +} + +bind ${className}Editor { + # nothing +} + +bind ${className}Editor { + # nothing +} + +if {![string compare $tcl_platform(platform) "macintosh"]} { + bind ${className}Editor { + # nothing + } +} + +# On Windows, paste is done using Shift-Insert. Shift-Insert already +# generates the <> event, so we don't need to do anything here. +if { [string compare $tcl_platform(platform) "windows"] != 0 } { + bind ${className}Editor { + catch {blt::tv::InsertText %W [selection get -displayof %W]} + } +} + +# Additional emacs-like bindings: +bind ${className}Editor { + 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 { + %W text icursor 0 + %W text selection clear +} + +bind ${className}Editor { + %W text icursor [expr {[%W index insert] - 1}] + %W text selection clear +} + +bind ${className}Editor { + %W text delete insert +} + +bind ${className}Editor { + %W text icursor end + %W text selection clear +} + +bind ${className}Editor { + %W text icursor [expr {[%W index insert] + 1}] + %W text selection clear +} + +bind ${className}Editor { + 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 { + %W text delete insert end +} + +if 0 { + bind ${className}Editor { + blt::tv::Transpose %W + } + bind ${className}Editor { + %W text icursor [blt::tv::PreviousWord %W insert] + %W text selection clear + } + bind ${className}Editor { + %W delete insert [blt::tv::NextWord %W insert] + } + bind ${className}Editor { + %W text icursor [blt::tv::NextWord %W insert] + %W text selection clear + } + bind ${className}Editor { + %W delete [blt::tv::PreviousWord %W insert] insert + } + bind ${className}Editor { + %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 +.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 \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 +.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 +.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 \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 \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 \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 \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 \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 \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 { + %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 +#include + +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 { ... } +.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 { ... } +.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 {drag&drop drag %W %X %Y} +\f(CWbind \fIwin\fP {drag&drop drag %W %X %Y} +\f(CWbind \fIwin\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 +and 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 + 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 { + %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 { + .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 +#include + +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\fR +Starts scanning. +.IP \f(CW\fR +Adjusts the scan. +.IP \f(CW\fR +Stops scanning. +.IP \f(CW\fR +Starts auto-scrolling. +.IP \f(CW\fR +Starts auto-scrolling +.IP \f(CW\fR +Moves the focus to the previous entry. +.IP \f(CW\fR +Moves the focus to the next entry. +.IP \f(CW\fR +Moves the focus to the previous sibling. +.IP \f(CW\fR +Moves the focus to the next sibling. +.IP \f(CW\fR +Moves the focus to first entry. Closed or hidden entries +are ignored. +.IP \f(CW\fR +Move the focus to the last entry. Closed or hidden entries +are ignored. +.IP \f(CW\fR +Closes the entry. It is not an error if the entry has no children. +.IP \f(CW\fR +Opens the entry, displaying its children. It is not an +error if the entry has no children. +.IP \f(CW\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\fR +Turns off select mode. +.IP \f(CW\fR +Sets the focus to the current entry. +.IP \f(CW\fR +Turns off select mode. +.IP \f(CW\fR +Moves to the next entry whose label starts with the letter typed. +.IP \f(CW\fR +Moves the focus to first entry. Closed or hidden entries +are ignored. +.IP \f(CW\fR +Move the focus to the last entry. Closed or hidden entries +are ignored. +.IP \f(CW\fR +Opens all entries. +.IP \f(CW\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\fR +Highlights the button of the current entry. +.IP \f(CW\fR +Returns the button back to its normal state. +.IP \f(CW\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\fR +Highlights the current entry. +.IP \f(CW\fR +Returns the entry back to its normal state. +.IP \f(CW\fR +Sets the selection anchor the current entry. +.IP \f(CW\fR +Toggles the selection of the current entry. +.IP \f(CW\fR +For "multiple" mode only. Saves the current location of the +pointer for auto-scrolling. Resets the selection mark. +.IP \f(CW\fR +For "multiple" mode only. Sets the selection anchor to the +current entry. +.IP \f(CW\fR +For "multiple" mode only. Extends the selection. +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\fR +Stop auto-scrolling. +.IP \f(CW\fR +For "multiple" mode only. Toggles and extends the selection. +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\fR +Stops auto-scrolling. +.IP \f(CW\fR +??? +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\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\fR +Highlights the current column title. +.IP \f(CW\fR +Returns the column back to its normal state. +.IP \f(CW\fR +Invokes the command (see the column's -command option) if one +if specified. +.SS COLUMN RULE BINDINGS +.IP \f(CW\fR +Highlights the current and activates the ruler. +.IP \f(CW\fR +Returns the column back to its normal state. Deactivates the +ruler. +.IP \f(CW\fR +Sets the resize anchor for the column. +.IP \f(CW\fR +Sets the resize mark for the column. +.IP \f(CW\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\fR +Starts scanning. +.IP \f(CW\fR +Adjusts the scan. +.IP \f(CW\fR +Stops scanning. +.IP \f(CW\fR +Starts auto-scrolling. +.IP \f(CW\fR +Starts auto-scrolling +.IP \f(CW\fR +Moves the focus to the previous entry. +.IP \f(CW\fR +Moves the focus to the next entry. +.IP \f(CW\fR +Moves the focus to the previous sibling. +.IP \f(CW\fR +Moves the focus to the next sibling. +.IP \f(CW\fR +Moves the focus to first entry. Closed or hidden entries +are ignored. +.IP \f(CW\fR +Move the focus to the last entry. Closed or hidden entries +are ignored. +.IP \f(CW\fR +Closes the entry. It is not an error if the entry has no children. +.IP \f(CW\fR +Opens the entry, displaying its children. It is not an +error if the entry has no children. +.IP \f(CW\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\fR +Turns off select mode. +.IP \f(CW\fR +Sets the focus to the current entry. +.IP \f(CW\fR +Turns off select mode. +.IP \f(CW\fR +Moves to the next entry whose label starts with the letter typed. +.IP \f(CW\fR +Moves the focus to first entry. Closed or hidden entries +are ignored. +.IP \f(CW\fR +Move the focus to the last entry. Closed or hidden entries +are ignored. +.IP \f(CW\fR +Opens all entries. +.IP \f(CW\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\fR +Highlights the button of the current entry. +.IP \f(CW\fR +Returns the button back to its normal state. +.IP \f(CW\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\fR +Highlights the current entry. +.IP \f(CW\fR +Returns the entry back to its normal state. +.IP \f(CW\fR +Sets the selection anchor the current entry. +.IP \f(CW\fR +Toggles the selection of the current entry. +.IP \f(CW\fR +For "multiple" mode only. Saves the current location of the +pointer for auto-scrolling. Resets the selection mark. +.IP \f(CW\fR +For "multiple" mode only. Sets the selection anchor to the +current entry. +.IP \f(CW\fR +For "multiple" mode only. Extends the selection. +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\fR +Stop auto-scrolling. +.IP \f(CW\fR +For "multiple" mode only. Toggles and extends the selection. +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\fR +Stops auto-scrolling. +.IP \f(CW\fR +??? +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\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\fR +Highlights the current column title. +.IP \f(CW\fR +Returns the column back to its normal state. +.IP \f(CW\fR +Invokes the command (see the column's -command option) if one +if specified. +.SS COLUMN RULE BINDINGS +.IP \f(CW\fR +Highlights the current and activates the ruler. +.IP \f(CW\fR +Returns the column back to its normal state. Deactivates the +ruler. +.IP \f(CW\fR +Sets the resize anchor for the column. +.IP \f(CW\fR +Sets the resize mark for the column. +.IP \f(CW\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 +#include + +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\fR +.IP \fB\fR +.IP \fB\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\fR +.IP \fB\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\fR +.IP \fB\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\fR +.IP \fB\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\fR +.IP \fB\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\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\fR +.IP \fB\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\fR +Starts scanning. +.IP \f(CW\fR +Adjusts the scan. +.IP \f(CW\fR +Stops scanning. +.IP \f(CW\fR +Starts auto-scrolling. +.IP \f(CW\fR +Starts auto-scrolling +.IP \f(CW\fR +Moves the focus to the previous entry. +.IP \f(CW\fR +Moves the focus to the next entry. +.IP \f(CW\fR +Moves the focus to the previous sibling. +.IP \f(CW\fR +Moves the focus to the next sibling. +.IP \f(CW\fR +Moves the focus to first entry. Closed or hidden entries +are ignored. +.IP \f(CW\fR +Move the focus to the last entry. Closed or hidden entries +are ignored. +.IP \f(CW\fR +Closes the entry. It is not an error if the entry has no children. +.IP \f(CW\fR +Opens the entry, displaying its children. It is not an +error if the entry has no children. +.IP \f(CW\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\fR +Turns off select mode. +.IP \f(CW\fR +Sets the focus to the current entry. +.IP \f(CW\fR +Turns off select mode. +.IP \f(CW\fR +Moves to the next entry whose label starts with the letter typed. +.IP \f(CW\fR +Moves the focus to first entry. Closed or hidden entries +are ignored. +.IP \f(CW\fR +Move the focus to the last entry. Closed or hidden entries +are ignored. +.IP \f(CW\fR +Opens all entries. +.IP \f(CW\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\fR +Highlights the button of the current entry. +.IP \f(CW\fR +Returns the button back to its normal state. +.IP \f(CW\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\fR +Highlights the current entry. +.IP \f(CW\fR +Returns the entry back to its normal state. +.IP \f(CW\fR +Sets the selection anchor the current entry. +.IP \f(CW\fR +Toggles the selection of the current entry. +.IP \f(CW\fR +For "multiple" mode only. Saves the current location of the +pointer for auto-scrolling. Resets the selection mark. +.IP \f(CW\fR +For "multiple" mode only. Sets the selection anchor to the +current entry. +.IP \f(CW\fR +For "multiple" mode only. Extends the selection. +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\fR +Stop auto-scrolling. +.IP \f(CW\fR +For "multiple" mode only. Toggles and extends the selection. +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\fR +Stops auto-scrolling. +.IP \f(CW\fR +??? +.IP \f(CW\fR +Place holder. Does nothing. +.IP \f(CW\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\fR +Highlights the current column title. +.IP \f(CW\fR +Returns the column back to its normal state. +.IP \f(CW\fR +Invokes the command (see the column's -command option) if one +if specified. +.SS COLUMN RULE BINDINGS +.IP \f(CW\fR +Highlights the current and activates the ruler. +.IP \f(CW\fR +Returns the column back to its normal state. Deactivates the +ruler. +.IP \f(CW\fR +Sets the resize anchor for the column. +.IP \f(CW\fR +Sets the resize mark for the column. +.IP \f(CW\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 +#include +... +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 + +#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 +#include +#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 +#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 +#include +#ifdef HAVE_SYS_PARAM_H +#include +#endif +#include + +#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 events within + * button grab on an object. They're marked NotifyVirtual instead + * of NotifyAncestor. A better solution: generate new-style + * virtual <> <> <> 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 and 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 + +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 + +#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 + +#ifdef WIN32 +#include +#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 +#else +#include +#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 that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to `int' if doesn't define. */ +#undef pid_t + +/* Define to `unsigned' if 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 and . */ +#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 header file. */ +#undef HAVE_CTYPE_H + +/* Define if you have the header file. */ +#undef HAVE_ERRNO_H + +/* Define if you have the header file. */ +#undef HAVE_FLOAT_H + +/* Define if you have the header file. */ +#undef HAVE_IEEEFP_H + +/* Define if you have the header file. */ +#undef HAVE_INTTYPES_H + +/* Define if you have the header file. */ +#undef HAVE_JPEGLIB_H + +/* Define if you have the header file. */ +#undef HAVE_LIMITS_H + +/* Define if you have the header file. */ +#undef HAVE_MALLOC_H + +/* Define if you have the header file. */ +#undef HAVE_MATH_H + +/* Define if you have the header file. */ +#undef HAVE_MEMORY_H + +/* Define if you have the header file. */ +#undef HAVE_SETJMP_H + +/* Define if you have the header file. */ +#undef HAVE_STDLIB_H + +/* Define if you have the header file. */ +#undef HAVE_STRING_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_WAIT_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the 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 +#include +#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 +#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 +#include +#else +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#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 + +#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 +#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 + */ + 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 + */ +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 ?? + */ +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 ? ? + */ +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 + */ +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 handler \ + * ?? ?...? + */ + 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 handler + * + * Create the new 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 handler \ + * ?...? + * + * Create the new 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 ?options...? + * drag&drop source handler ?? ? ...? + */ +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 ?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 ?? ?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 handler + * drag&drop target handler ? ...? + */ + 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 handle ?? + */ + 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 ", + (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 ?options...? + * drag&drop source handler ?? ? ...? + * + * drag&drop target + * drag&drop target handler ? ...? + * drag&drop target handle ?? + * + * drag&drop token + * drag&drop drag + * drag&drop drop + * + * drag&drop errors ?? + * drag&drop active + * drag&drop location ? ? + * + * ------------------------------------------------------------------------ + */ +/*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 + +#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 + +#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 + + +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 + +#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 + +#if defined(__STDC__) +#include +#else +#include +#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 + +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 +#if defined(__STDC__) +#include +#else +#include +#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 +#include +#else +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#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 + +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 + +#include +#include +/* The following header is required for LP64 compilation */ +#include + +#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 +#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 +#include + +#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 +#include +#include "bltTile.h" + +#include +#include + +#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 +#ifndef WIN32 +#include +#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 +#ifndef WIN32 +#include +#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 + +#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 +#undef STRICT +#undef WIN32_LEAN_AND_MEAN +#include +#endif /* WIN32 */ + +#include +#include + +#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 +#include +#include + +#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 +#endif /* HAVE_STDLIB_H */ + +#ifdef HAVE_STRING_H +#include +#endif /* HAVE_STRING_H */ + +#ifdef HAVE_ERRNO_H +#include +#endif /* HAVE_ERRNO_H */ + +#ifdef HAVE_CTYPE_H +#include +#endif /* HAVE_CTYPE_H */ + +#ifdef HAVE_MEMORY_H +#include +#endif /* HAVE_MEMORY_H */ + +#ifdef HAVE_UNISTD_H +#include +#endif /* HAVE_UNISTD_H */ + +#ifdef HAVE_FLOAT_H +#include +#endif + +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifdef HAVE_IEEEFP_H +#include +#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 +#include +#include +#include +#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 +#else +#include +#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 + +#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 +#include +#if defined(__STDC__) +#include +#else +#include +#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 - + */ + 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 +#else +#include +#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 +#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, ®ion); + return Blt_ScaleBitmapRegion(tkwin, srcBitmap, srcWidth, srcHeight, + scaledWidth, scaledHeight, ®ion); +} + +/*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 + +#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, ®ion, + 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 +#include +#include + +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 + +#ifndef NO_TREE + +#include +#include +#include "bltSwitch.h" +#include + +#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 ::t1 + * n1::t1 ::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 ::t1 + * n1::t1 ::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 +#include + +#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 +#include + +#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 + +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 +#include + +#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 +#include + +#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 +#include + + +#include +#include + +#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], ×tamp) != 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 +#include + +#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 + +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 +#ifndef TCL_ONLY +#include +#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 +#include + +#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 +#else +#include +#endif +#include + +#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 +#include + +#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 +#include +#else +#ifdef HAVE_SYS_TIME_H +#include +#else +#include +#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 +#endif +#ifdef HAVE_SYS_WAIT_H +# include +#endif +#ifdef HAVE_ERRNO_H +# include +#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 +#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 that is POSIX.1 compatible. */ +#undef HAVE_SYS_WAIT_H + +/* Define to `int' if doesn't define. */ +#ifdef _MSC_VER +#define pid_t int +#endif + +/* Define to `unsigned' if 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 and . */ +#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 header file. */ +#define HAVE_CTYPE_H 1 + +/* Define if you have the header file. */ +#define HAVE_ERRNO_H 1 + +/* Define if you have the header file. */ +#define HAVE_FLOAT_H 1 + +/* Define if you have the header file. */ +#undef HAVE_IEEEFP_H + +/* Define if you have the header file. */ +/* Defined in Makefile */ +/* #undef HAVE_JPEGLIB_H */ +#define HAVE_JPEGLIB_H 1 + +/* Define if you have the header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the header file. */ +#define HAVE_MALLOC_H 1 + +/* Define if you have the header file. */ +#define HAVE_MATH_H 1 + +/* Define if you have the header file. */ +#define HAVE_MEMORY_H 1 + +/* Define if you have the header file. */ +#define HAVE_SETJMP_H 1 + +/* Define if you have the header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the header file. */ +#undef HAVE_SYS_PARAM_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_TIME_H + +/* Define if you have the header file. */ +#undef HAVE_SYS_WAIT_H + +/* Define if you have the header file. */ +#undef HAVE_UNISTD_H + +/* Define if you have the 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 + +/* + * 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 +#include +#include + +#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 + +#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), ®ion); + } + 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 + +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 + +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 + +/* + * 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 + +#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 +#include +#ifndef NO_PRINTER +#include +#undef Status +#ifdef _MSC_VER +#include +#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 + +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 +#ifndef WIN32 +#include +#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, + ¬Used); + 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 +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 +#ifndef WIN32 +#include +#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, ®ion); + 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 + +#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 +#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 \ + "$command; $widget tag configure $tag -foreground blue" + $widget tag bind $tag \ + [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 { + .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 + -- cgit v1.2.1