[brlcad-commits] SF.net SVN: brlcad:[47164] brlcad/trunk/src/other
Open Source Solid Modeling CAD
Brought to you by:
brlcad
From: <sta...@us...> - 2011-10-07 22:19:48
|
Revision: 47164 http://brlcad.svn.sourceforge.net/brlcad/?rev=47164&view=rev Author: starseeker Date: 2011-10-07 22:19:37 +0000 (Fri, 07 Oct 2011) Log Message: ----------- Add the full hv3 browser to src/other - preliminary for switching from special-purpose man and help viewers to customizations of the more powerful hv3 browser. Prompted by the css additions to the Docbook output, but may also provide a text searching capability. Modified Paths: -------------- brlcad/trunk/src/other/CMakeLists.txt Added Paths: ----------- brlcad/trunk/src/other/hv3/ brlcad/trunk/src/other/hv3/CMakeLists.txt brlcad/trunk/src/other/hv3/Makefile.am brlcad/trunk/src/other/hv3/README brlcad/trunk/src/other/hv3/bookmarks.html brlcad/trunk/src/other/hv3/bugreport.html brlcad/trunk/src/other/hv3/combobox.tcl brlcad/trunk/src/other/hv3/demo/ brlcad/trunk/src/other/hv3/demo/index.html brlcad/trunk/src/other/hv3/demo/small-eagleCAD.gif brlcad/trunk/src/other/hv3/demo/viewpage.tcl brlcad/trunk/src/other/hv3/hv.tcl brlcad/trunk/src/other/hv3/hv3.tcl brlcad/trunk/src/other/hv3/hv3_bookmarks.tcl brlcad/trunk/src/other/hv3/hv3_browser.tcl brlcad/trunk/src/other/hv3/hv3_bugreport.tcl brlcad/trunk/src/other/hv3/hv3_common.tcl brlcad/trunk/src/other/hv3/hv3_console.tcl brlcad/trunk/src/other/hv3/hv3_db.tcl brlcad/trunk/src/other/hv3/hv3_debug.tcl brlcad/trunk/src/other/hv3/hv3_doctype.tcl brlcad/trunk/src/other/hv3/hv3_dom.tcl brlcad/trunk/src/other/hv3/hv3_dom2.tcl brlcad/trunk/src/other/hv3/hv3_dom3.tcl brlcad/trunk/src/other/hv3/hv3_dom_compiler.tcl brlcad/trunk/src/other/hv3/hv3_dom_containers.tcl brlcad/trunk/src/other/hv3/hv3_dom_core.tcl brlcad/trunk/src/other/hv3/hv3_dom_events.tcl brlcad/trunk/src/other/hv3/hv3_dom_html.tcl brlcad/trunk/src/other/hv3/hv3_dom_ns.tcl brlcad/trunk/src/other/hv3/hv3_dom_style.tcl brlcad/trunk/src/other/hv3/hv3_dom_xmlhttp.tcl brlcad/trunk/src/other/hv3/hv3_download.tcl brlcad/trunk/src/other/hv3/hv3_encodings.tcl brlcad/trunk/src/other/hv3/hv3_file.tcl brlcad/trunk/src/other/hv3/hv3_form.tcl brlcad/trunk/src/other/hv3/hv3_frames.tcl brlcad/trunk/src/other/hv3/hv3_frameset.tcl brlcad/trunk/src/other/hv3/hv3_history.tcl brlcad/trunk/src/other/hv3/hv3_home.tcl brlcad/trunk/src/other/hv3/hv3_http.tcl brlcad/trunk/src/other/hv3/hv3_icons.tcl brlcad/trunk/src/other/hv3/hv3_image.tcl brlcad/trunk/src/other/hv3/hv3_log.tcl brlcad/trunk/src/other/hv3/hv3_main.tcl brlcad/trunk/src/other/hv3/hv3_nav.tcl brlcad/trunk/src/other/hv3/hv3_notebook.tcl brlcad/trunk/src/other/hv3/hv3_object.tcl brlcad/trunk/src/other/hv3/hv3_polipo.tcl brlcad/trunk/src/other/hv3/hv3_profile.tcl brlcad/trunk/src/other/hv3/hv3_prop.tcl brlcad/trunk/src/other/hv3/hv3_request.tcl brlcad/trunk/src/other/hv3/hv3_string.tcl brlcad/trunk/src/other/hv3/hv3_style.tcl brlcad/trunk/src/other/hv3/hv3_url.tcl brlcad/trunk/src/other/hv3/hv3_util.tcl brlcad/trunk/src/other/hv3/hv3_widgets.tcl brlcad/trunk/src/other/hv3/hv3bridge.c brlcad/trunk/src/other/hv3/hv3events.c brlcad/trunk/src/other/hv3/hv3format.c brlcad/trunk/src/other/hv3/hv3function.c brlcad/trunk/src/other/hv3/hv3see.c brlcad/trunk/src/other/hv3/hv3timeout.c brlcad/trunk/src/other/hv3/hvinit.c brlcad/trunk/src/other/hv3/index.html brlcad/trunk/src/other/hv3/license.txt brlcad/trunk/src/other/hv3/license_snit.txt brlcad/trunk/src/other/hv3/main.tcl brlcad/trunk/src/other/hv3/nogif.fig brlcad/trunk/src/other/hv3/nogif.gif brlcad/trunk/src/other/hv3/nogifsm.gif brlcad/trunk/src/other/hv3/snit.tcl brlcad/trunk/src/other/hv3/snit2.tcl brlcad/trunk/src/other/hv3/ss.tcl brlcad/trunk/src/other/hv3/ssinit.c brlcad/trunk/src/other/hv3/tst_main.tcl brlcad/trunk/src/other/sqlite3/ brlcad/trunk/src/other/sqlite3/CMake/ brlcad/trunk/src/other/sqlite3/CMake/ResolveCompilerPaths.cmake brlcad/trunk/src/other/sqlite3/CMakeLists.txt brlcad/trunk/src/other/sqlite3/shell.c brlcad/trunk/src/other/sqlite3/sqlite3.1 brlcad/trunk/src/other/sqlite3/sqlite3.c brlcad/trunk/src/other/sqlite3/sqlite3.h brlcad/trunk/src/other/sqlite3/sqlite3ext.h brlcad/trunk/src/other/sqlite3/tcl/ brlcad/trunk/src/other/sqlite3/tcl/CMake/ brlcad/trunk/src/other/sqlite3/tcl/CMake/CheckCFileRuns.cmake brlcad/trunk/src/other/sqlite3/tcl/CMake/CheckPrototypeExists.cmake brlcad/trunk/src/other/sqlite3/tcl/CMake/ResolveCompilerPaths.cmake brlcad/trunk/src/other/sqlite3/tcl/CMake/ac_std_funcs.cmake brlcad/trunk/src/other/sqlite3/tcl/CMake/tcl.cmake brlcad/trunk/src/other/sqlite3/tcl/CMakeLists.txt brlcad/trunk/src/other/sqlite3/tcl/tclsqlite3.c Modified: brlcad/trunk/src/other/CMakeLists.txt =================================================================== --- brlcad/trunk/src/other/CMakeLists.txt 2011-10-07 21:29:42 UTC (rev 47163) +++ brlcad/trunk/src/other/CMakeLists.txt 2011-10-07 22:19:37 UTC (rev 47164) @@ -375,6 +375,8 @@ MARK_AS_ADVANCED(IWIDGETS_VERSION) THIRD_PARTY_TCL_PACKAGE(Tkhtml tkhtml "${TCL_WISH_EXECUTABLE}" "tcl;tk") + ADD_SUBDIRECTORY(sqlite3) + ADD_SUBDIRECTORY(hv3) THIRD_PARTY_TCL_PACKAGE(tkpng tkpng "${TCL_WISH_EXECUTABLE}" "tcl;tk") Added: brlcad/trunk/src/other/hv3/CMakeLists.txt =================================================================== --- brlcad/trunk/src/other/hv3/CMakeLists.txt (rev 0) +++ brlcad/trunk/src/other/hv3/CMakeLists.txt 2011-10-07 22:19:37 UTC (rev 47164) @@ -0,0 +1,101 @@ +SET(hv3_TCLSCRIPTS + bookmarks.html + bugreport.html + combobox.tcl + hv3_bookmarks.tcl + hv3bridge.c + hv3_browser.tcl + hv3_bugreport.tcl + hv3_common.tcl + hv3_console.tcl + hv3_db.tcl + hv3_debug.tcl + hv3_doctype.tcl + hv3_dom2.tcl + hv3_dom3.tcl + hv3_dom_compiler.tcl + hv3_dom_containers.tcl + hv3_dom_core.tcl + hv3_dom_events.tcl + hv3_dom_html.tcl + hv3_dom_ns.tcl + hv3_dom_style.tcl + hv3_dom.tcl + hv3_dom_xmlhttp.tcl + hv3_download.tcl + hv3_encodings.tcl + hv3events.c + hv3_file.tcl + hv3format.c + hv3_form.tcl + hv3_frameset.tcl + hv3_frames.tcl + hv3function.c + hv3_history.tcl + hv3_home.tcl + hv3_http.tcl + hv3_icons.tcl + hv3_image.tcl + hv3_log.tcl + hv3_main.tcl + hv3_nav.tcl + hv3_notebook.tcl + hv3_object.tcl + hv3_polipo.tcl + hv3_profile.tcl + hv3_prop.tcl + hv3_request.tcl + hv3see.c + hv3_string.tcl + hv3_style.tcl + hv3.tcl + hv3timeout.c + hv3_url.tcl + hv3_util.tcl + hv3_widgets.tcl + hvinit.c + hv.tcl + index.html + license_snit.txt + license.txt + main.tcl + nogif.fig + nogif.gif + nogifsm.gif + snit2.tcl + snit.tcl + ssinit.c + ss.tcl + tst_main.tcl +) +file(COPY ${hv3_TCLSCRIPTS} DESTINATION ${CMAKE_BINARY_DIR}/lib/hv3) +install(FILES ${hv3_TCLSCRIPTS} DESTINATION lib/hv3) + +file(WRITE ${CMAKE_BINARY_DIR}/lib/hv3/pkgIndex.tcl "package ifneeded snit 1.0 [list source [file join $dir snit.tcl]]\n") +file(APPEND ${CMAKE_BINARY_DIR}/lib/hv3/pkgIndex.tcl "package ifneeded hv3 0.1 [list source [file join $dir hv3.tcl]]") +install(FILES ${CMAKE_BINARY_DIR}/lib/hv3/pkgIndex.tcl DESTINATION lib/hv3) + +SET(hv3_launch +"#!/bin/sh +#\\ +exec ${CMAKE_BINARY_DIR}/bin/wish $0 $\@ +source ${CMAKE_BINARY_DIR}/lib/hv3/hv3_main.tcl +") +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/hv3 "${hv3_launch}") +file(COPY ${CMAKE_CURRENT_BINARY_DIR}/hv3 + DESTINATION ${CMAKE_BINARY_DIR}/bin + FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ + GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) + +set(hv3_bat_launch +"\@ECHO OFF +SETLOCAL +SET WISH=%~dp0wish +SET HV3=%~dp0..\\lib\\hv3\\hv3_main.tcl +START /B \"\" \"%WISH%\" \"%HV3%\" %1 +") +file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/hv3.bat "${hv3_bat_launch}") +file(COPY ${CMAKE_CURRENT_BINARY_DIR}/hv3.bat + DESTINATION ${CMAKE_BINARY_DIR}/bin + FILE_PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ + GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) Property changes on: brlcad/trunk/src/other/hv3/CMakeLists.txt ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:eol-style + native Added: brlcad/trunk/src/other/hv3/Makefile.am =================================================================== --- brlcad/trunk/src/other/hv3/Makefile.am (rev 0) +++ brlcad/trunk/src/other/hv3/Makefile.am 2011-10-07 22:19:37 UTC (rev 47164) @@ -0,0 +1,55 @@ + +this_dir = tclscripts/hv3 +tcldir = $(BRLCAD_DATA)/$(this_dir) + +scripts = \ + combobox.tcl \ + hv3.tcl \ + hv3_encodings.tcl \ + hv3_form.tcl \ + hv3_request.tcl \ + hv3_util.tcl \ + snit.tcl + +BUILT_SOURCES = \ + pkgIndex.tcl \ + tclIndex + +tcl_SCRIPTS = \ + $(BUILT_SOURCES) \ + $(scripts) + +documentationdir = $(BRLCAD_DATA)/doc + +documentation_DATA = \ + hv3.man + +dist_man_MANS = \ + tkhtml.n + +EXTRA_DIST = \ + $(tcl_SCRIPTS) \ + $(documentation_DATA) \ + $(dist_man_MANS) \ + license.txt \ + license_snit.txt \ + demo/index.html \ + demo/small-eagleCAD.gif \ + demo/viewpage.tcl \ + CMakeLists.txt + +# CLEANFILES = $(BUILT_SOURCES) + +pkgIndex.tcl: + TCL_LIBRARY=$(top_srcdir)/src/other/tcl/library \ + LD_LIBRARY_PATH=$(top_builddir)/src/other/tcl/unix:$(top_builddir)/src/other/tk/unix:$$LD_LIBRARY_PATH \ + DYLD_LIBRARY_PATH=$(top_builddir)/src/other/tcl/unix:$(top_buildir)/src/other/tk/unix:$$DYLD_LIBRARY_PATH \ + $(top_builddir)/src/bwish/btclsh $(top_srcdir)/src/tclscripts/ampi.tcl $(top_srcdir)/src/$(this_dir) + +tclIndex: + TCL_LIBRARY=$(top_srcdir)/src/other/tcl/library \ + LD_LIBRARY_PATH=$(top_builddir)/src/other/tcl/unix:$(top_builddir)/src/other/tk/unix:$$LD_LIBRARY_PATH \ + DYLD_LIBRARY_PATH=$(top_builddir)/src/other/tcl/unix:$(top_buildir)/src/other/tk/unix:$$DYLD_LIBRARY_PATH \ + $(top_builddir)/src/bwish/btclsh $(top_srcdir)/src/tclscripts/ami.tcl $(top_srcdir)/src/$(this_dir) + +include $(top_srcdir)/misc/Makefile.defs Property changes on: brlcad/trunk/src/other/hv3/Makefile.am ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:eol-style + native Added: brlcad/trunk/src/other/hv3/README =================================================================== --- brlcad/trunk/src/other/hv3/README (rev 0) +++ brlcad/trunk/src/other/hv3/README 2011-10-07 22:19:37 UTC (rev 47164) @@ -0,0 +1 @@ + Property changes on: brlcad/trunk/src/other/hv3/README ___________________________________________________________________ Added: svn:mime-type + text/plain Added: svn:eol-style + native Added: brlcad/trunk/src/other/hv3/bookmarks.html =================================================================== --- brlcad/trunk/src/other/hv3/bookmarks.html (rev 0) +++ brlcad/trunk/src/other/hv3/bookmarks.html 2011-10-07 22:19:37 UTC (rev 47164) @@ -0,0 +1,553 @@ + +<HTML> + <HEAD> + <STYLE> + h1 { + font-size: 1.4em; + font-weight: normal; + } + h2 { + float: left; + width: 45%; + border: solid 1px purple; + border-right: none; + border-bottom: none; + color: purple; + margin: 2px; + background: #CCCCCC; + cursor: pointer; + font-size: 1.4em; + } + li { + float: left; + width: 50%; + min-width: 180px; + } + form { + margin: 0; + } + + .bookmark[active="true"]:hover { + background-color: #DDDDDD; + } + .bookmark[clickable="0"]:hover { + background-color: #EEEEEE; + } + .bookmark { + cursor:pointer; + margin: 1px; + padding: 2px 0 2px 15px; + background: #EEEEEE; + + border-top: solid 2px purple; + border-left: solid 2px purple; + + display: block; + position: relative; + + float: left; + width: 45%; + min-width: 180px; + } + .bookmark a { + text-decoration: none; + color: black; + display: block; + } + + ul { + padding: 0; + margin: auto auto auto 15px; + } + .folder { + padding: 0px 5px; + margin: 0 0 10px 0; + width: 100%; + display: table; + position: relative; + } + + #controls { + border-bottom: solid black 2px; + background: white; + position: fixed; + top: 0px; + left: 0px; + right: 0px; + z-index: 5; + } + .edit { + display: block; + float: right; + font-size: small; + color: darkblue; + text-decoration: underline; + font-weight: normal; + padding-right: 5px; + } + + body { + margin-top: 3em; + } + + #searchbox { + width:100%; + } + #searchbox[active="1"] { + border-color: red; + } + + #undelete { + display:block; + float:right; + font-size:medium; + } + </STYLE> + <SCRIPT> + +var drag = new Object() +drag.element = undefined +drag.interval = undefined +drag.x = undefined +drag.y = undefined +drag.original_x = undefined +drag.original_y = undefined +drag.isDelete = false + +var app = new Object() +app.zFilter = "" + +function mouseup_handler (event) { + drag.element.style.top = '0px' + drag.element.style.left = '0px' + drag.element.style.zIndex = 'auto' + drag.element.style.backgroundColor = "" + clearInterval(drag.interval) + document.onmouseup = undefined + document.onmousemove = undefined + + if (drag.isDelete) { + drag.element.parentNode.removeChild(drag.element) + app.version = hv3_bookmarks.remove(drag.element) + + var u = document.getElementById("undelete") + u.innerHTML = hv3_bookmarks.get_undelete() + + } else if (drag.element.onclick == ignore_click) { + if (drag.element.className == 'bookmark') { + app.version = hv3_bookmarks.bookmark_move(drag.element) + } + if (drag.element.className == 'folder') { + app.version = hv3_bookmarks.folder_move(drag.element) + } + } + drag.isDelete = false + drag.element = undefined + return 0 +} +function mousemove_handler (event) { + drag.x = event.clientX + drag.y = event.clientY + return 0 +} + +function ignore_click () { + this.onclick = undefined + this.setAttribute("clickable", "1") + return 0 +} + +function drag_cache_position(d) { + d.drag_x1 = 0 + d.drag_y1 = 0 + for (var p = d; p != null; p = p.offsetParent) { + d.drag_x1 += p.offsetLeft + d.drag_y1 += p.offsetTop + } + d.drag_x2 = d.drag_x1 + d.offsetWidth + d.drag_y2 = d.drag_y1 + d.offsetHeight +} + +function drag_makedropmap(elem) { + drag.drag_targets = new Array() + + var dlist = document.getElementsByTagName('div'); + for ( var i = 0; i < dlist.length; i++) { + var d = dlist[i] + if (d != elem && d.className == elem.className) { + if (d.className == "folder" && d.id == "0") { + continue + } + if (d.parentNode.style.display == "none") { + continue + } + drag_cache_position(d) + drag.drag_targets.push(d) + } + } + + if (elem.className == "bookmark") { + var hlist = document.getElementsByTagName('h2') + for ( var i = 0; i < hlist.length; i++) { + var h = hlist[i] + if (h.nextSibling.style.display != "none") { + drag_cache_position(h) + drag.drag_targets.push(h) + } + } + + hlist = document.getElementsByTagName('h1') + for ( var i = 0; i < hlist.length; i++) { + var h = hlist[i] + drag_cache_position(h) + h.drag_y2 += 15 + drag.drag_targets.push(h) + } + } + + drag_cache_position(drag.controls) +} + +// This function is called periodically while an object is being +// dragged (once every 20 ms or so). +// +function drag_update() { + if ( + Math.abs(drag.x - drag.original_x) > 10 || + Math.abs(drag.y - drag.original_y) > 10 + ) { + drag.element.onclick = ignore_click + drag.element.setAttribute("clickable", "0") + } + drag.element.style.left = (drag.x - drag.original_x) + 'px' + drag.element.style.top = (drag.y - drag.original_y) + 'px' + + if (!drag.drag_targets) { + drag_makedropmap(drag.element) + } + + drag_cache_position(drag.element) + var cx = (drag.element.drag_x1 + drag.element.drag_x2) / 2 + var cy = (drag.element.drag_y1 + drag.element.drag_y2) / 2 + + var isDelete = ((drag.element.drag_y1+5) < drag.controls.drag_y2) + if (isDelete && !drag.isDelete) { + drag.element.style.backgroundColor = "black" + drag.isDelete = isDelete + } else if (!isDelete && drag.isDelete) { + drag.element.style.backgroundColor = "" + drag.isDelete = isDelete + } + + for (var i = 0; i < drag.drag_targets.length; i++) { + var a = drag.drag_targets[i] + if (a.drag_x1 < cx && a.drag_x2 > cx && + a.drag_y1 < cy && a.drag_y2 > cy + ) { + + var x = drag.element.drag_x1 + var y = drag.element.drag_y1 + + var p = a.parentNode + if (a.nodeName == "H2") { + p = a.nextSibling + a = p.firstChild + } else if (a.nodeName == "H1") { + p = app.nofolder.childNodes[1] + a = p.firstChild + } + + if (drag.element.parentNode == p) { + for (var j = 0; j < p.childNodes.length; j++) { + var child = p.childNodes[j] + if (child == a) { + break + } else if (child == drag.element) { + a = a.nextSibling + break + } + } + } + + if (drag.element == a) return + p.insertBefore(drag.element, a) + + drag_cache_position(drag.element) + var sx = drag.element.drag_x1 - x + var sy = drag.element.drag_y1 - y + + drag.original_x += sx + drag.original_y += sy + + drag.element.style.left = (drag.x - drag.original_x) + 'px' + drag.element.style.top = (drag.y - drag.original_y) + 'px' + + drag_makedropmap(drag.element) + break + } + } +} + +function mousedown_handler (elem, event) { + clearInterval(drag.interval) + drag.isDelete = false + drag.element = elem + + drag.original_x = event.clientX + drag.original_y = event.clientY + drag.x = event.clientX + drag.y = event.clientY + drag.element.style.zIndex = 10 + drag.interval = setInterval(drag_update, 20) + document.onmouseup = mouseup_handler + document.onmousemove = mousemove_handler + + drag_makedropmap(drag.element) + return 0 +} + +// Toggle visibility of folder contents. +// +function folder_toggle (folder, event, toggle) { + var h2 = folder.childNodes[0] + var ul = folder.childNodes[1] + + if (folder.onclick == ignore_click) return + + var isHidden = (1 * folder.getAttribute('folder_hidden')) + if (toggle) { + isHidden = (isHidden ? 0 : 1) + folder.setAttribute('folder_hidden', isHidden) + app.version = hv3_bookmarks.folder_hidden(folder) + } + + if (isHidden) { + /* Hide the folder contents */ + ul.style.display = 'none' + ul.style.clear = 'none' + + h2.childNodes[1].innerHTML = '+ ' + h2.style.width = 'auto' + h2.style.cssFloat = 'none' + + folder.style.cssFloat = 'left' + folder.style.width = '45%' + folder.style.clear = 'none' + folder.style.marginBottom = '0' + } else { + /* Expand the folder contents */ + ul.style.display = 'table' + ul.style.clear = 'both' + + h2.childNodes[1].innerHTML = '- ' + h2.style.width = '45%' + h2.style.cssFloat = 'left' + + folder.style.clear = 'both' + folder.style.cssFloat = 'none' + folder.style.width = '100%' + folder.style.marginBottom = '10px' + } + + return 0 +} + +function bookmark_mousedown(elem, event) { + mousedown_handler(elem, event) + return 0 +} +function folder_mousedown(elem, event) { + mousedown_handler(elem.parentNode, event) + return 0 +} + +function bookmark_submit(form) { + var elem = form.parentNode.parentNode + + var new_name = form.n.value + var new_uri = form.u.value + var new_tags = form.t.value + + elem.setAttribute('bookmark_name', new_name) + elem.setAttribute('bookmark_uri', new_uri) + elem.setAttribute('bookmark_tags', new_tags) + + var a = elem.childNodes[1] + a.firstChild.data = new_name + a.href = new_uri + + app.version = hv3_bookmarks.bookmark_edit(elem) + + bookmark_edit(elem) + return 0 +} + +function bookmark_edit(elem) { + var div = elem.childNodes[2] + if (div.isExpanded) { + div.innerHTML = "" + elem.firstChild.firstChild.data = "(edit)" + elem.setAttribute("active", "true") + div.isExpanded = 0 + } else { + var str = "" + str += '<FORM onsubmit="return bookmark_submit(this)">' + str += '<TABLE width=100%>' + str += '<TR><TD>Name: <TD width=100%><INPUT width=90% name=n></INPUT>' + str += '<TR><TD>URI: <TD><INPUT width=90% name=u></INPUT>' + str += '<TR><TD>Tags: <TD><INPUT width=90% name=t></INPUT>' + str += '</TABLE></FORM>' + + div.innerHTML = str + var f = div.firstChild; + f.n.value = elem.getAttribute('bookmark_name') + f.u.value = elem.getAttribute('bookmark_uri') + f.t.value = elem.getAttribute('bookmark_tags') + f.n.select() + f.n.focus() + elem.firstChild.firstChild.data = "(cancel)" + elem.setAttribute("active", "false") + + div.isExpanded = 1 + } + return 0 +} + +function folder_submit(form) { + var elem = form.parentNode.parentNode.parentNode + var t = elem.firstChild.childNodes[2] + var new_name = form.n.value + elem.setAttribute('folder_name', new_name) + t.data = new_name + + app.version = hv3_bookmarks.folder_edit(elem) + folder_edit(elem) + return 0 +} + +function folder_edit(elem) { + var ed = elem.firstChild.firstChild + var div = elem.firstChild.childNodes[3] + + if (div.isExpanded) { + ed.firstChild.data = "(edit)" + div.innerHTML = "" + div.isExpanded = 0 + } else { + ed.firstChild.data = "(cancel)" + + var str = ""; + str += '<FORM onsubmit="return folder_submit(this)">' + str += '<TABLE width=100% style="color:black;margin-left:15px">' + str += '<TR><TD>Name: <TD width=100%><INPUT name=n></INPUT>' + str += '</TABLE></FORM>' + div.innerHTML = str + + f = div.firstChild + f.n.value = elem.getAttribute('folder_name') + f.n.select() + f.n.focus() + + div.isExpanded = 1 + } + return 0 +} + +// The following are "onclick" handlers for the "New Bookmark" +// and "New Folder" buttons respectively. +// +function bookmark_new() { + var id = hv3_bookmarks.bookmark_new(app.zFilter) + refresh_content() + bookmark_edit(document.getElementById(id)) +} +function folder_new() { + var id = hv3_bookmarks.folder_new() + refresh_content() + folder_edit(document.getElementById(id)) +} + +function refresh_content() { + drag.content.innerHTML = hv3_bookmarks.get_html_content(app.zFilter) + app.version = hv3_bookmarks.get_version() + app.nofolder = document.getElementById("0") + + var u = document.getElementById("undelete") + u.innerHTML = hv3_bookmarks.get_undelete() + + var dlist = document.getElementsByTagName('div'); + for ( var i = 0; i < dlist.length; i++) { + var d = dlist[i] + if (d.className == "folder") { + folder_toggle(d, 0, 0) + } + } +} +function check_refresh_content() { + if (app.version != hv3_bookmarks.get_version()) { + refresh_content() + } +} + +function filter_bookmarks () { + var s = document.getElementById("searchbox") + app.zFilter = s.value + refresh_content() + if (app.zFilter != "") { + s.setAttribute("active", "1") + } else { + s.setAttribute("active", "0") + } + return 0 +} + +function clear_filter () { + var s = document.getElementById("searchbox") + s.value = "" + return filter_bookmarks() +} + +function bookmark_undelete () { + hv3_bookmarks.undelete() + refresh_content() + return 0 +} + +window.onload = function () { + document.getElementById("searchbox").focus() + drag.controls = document.getElementById("controls") + drag.content = document.getElementById("content") + refresh_content() + setInterval(check_refresh_content, 2000) +} + + </SCRIPT> + </HEAD> + <BODY> + <TABLE id="controls"><TR> + <TD align="center"> + <INPUT type="button" value="New Folder" onclick="folder_new()"> + <TD align="center"> + <INPUT type="button" value="New Bookmark" onclick="bookmark_new()"> + </INPUT> + <TD align="left" style="padding-left:15px"> + Filter: + <TD align="left" width=100% style="padding-right:2px"> + <FORM onsubmit="return filter_bookmarks()"> + <INPUT type="text" id="searchbox"></INPUT> + </FORM> + <TD align="center"> + <INPUT type="button" value="View All" onclick="clear_filter()"></INPUT> + </TABLE> + + <H1>BOOKMARKS:<span id="undelete"></H1> + <DIV id=content></DIV> + <P style="clear:both;padding-top:1cm"> + <I>To delete individual bookmarks or entire folders, drag to the top + of the page (i.e. over the "Filter:" or "New Bookmark" controls). + A single level of undelete is available. + </I> + </P> + </BODY> +</HTML> + Property changes on: brlcad/trunk/src/other/hv3/bookmarks.html ___________________________________________________________________ Added: svn:mime-type + text/html Added: svn:eol-style + native Added: brlcad/trunk/src/other/hv3/bugreport.html =================================================================== --- brlcad/trunk/src/other/hv3/bugreport.html (rev 0) +++ brlcad/trunk/src/other/hv3/bugreport.html 2011-10-07 22:19:37 UTC (rev 47164) @@ -0,0 +1,153 @@ + + +<HTML> + <HEAD> + <STYLE> + body { + max-width: 750; + margin: auto; + background: #DDDDDD; + } + .block { + border: 2px solid purple; + background: white; + padding: 5px; + margin: 10px 5px; + } + h1 { text-align: center; } + a[href] { color: purple ; font-style: italic ; font-weight: bold } + a[href]:hover { background: purple ; color: white } + + </STYLE> + <SCRIPT> + function onload_handler (e) { + document.forms.quickreport.t.focus() + document.getElementById("formcontainer").style.display="block" + + var uri = decodeURIComponent(document.location.pathname) + document.getElementById('uritext').value = uri.substring(1, uri.length-1) + + document.getElementById("version").value = navigator.hv3_version + } + + function submit_handler(e) { + var caption = document.forms.quickreport.t.value + if (caption.length < 1 || caption.length > 60) { + alert("Caption field must be between 1 and 60 characters in length") + return false + } + + var uri = document.getElementById("uritext").value + var desc = document.forms.quickreport.d + desc.value = " URI: " + uri + "\n\n" + desc.value + + var email = document.forms.quickreport.c + if (email.value=="") { + email.value = "Anonymous Hv3 User" + } + + return true + } + </SCRIPT> + </HEAD> <BODY onload="onload_handler()"> + <DIV class=block> + <H1>Report a bug in Hv3</H1> + <CENTER><A href=#guidelines>View bug report guidelines</A></CENTER> + </DIV> + + <DIV class=block> + <H2>Quick Bug Report</H2> + <DIV id="formcontainer" style="display:none"> + <FORM + name=quickreport + action="http://tkhtml.tcl.tk/cvstrac/tktnew" + method=POST + onsubmit="return submit_handler()" + > + <TABLE> + <TR><TD>Caption: <TD><INPUT name=t size=60> + <TR><TD>Uri: <TD><INPUT id=uritext size=60> + <TR><TD>E-mail <B>(optional)</B>: <TD><INPUT size=60 name=c> + <TR><TD>Description:<TD><TEXTAREA name=d cols=70 rows=15></TEXTAREA> + </TABLE> + <CENTER> + <INPUT type=submit name=submit value="Submit Report!"> + <INPUT type=submit name=preview value="Online preview..."> + </CENTER> + <INPUT type=hidden name=f value=""> + <INPUT type=hidden name=s value=""> + <INPUT type=hidden name=w value="danielk1977"> + <INPUT type=hidden name=r value="1"> + <INPUT type=hidden name=p value="1"> + <INPUT type=hidden name=y value="Hv3"> + <INPUT type=hidden id=version name=v value=""> + </FORM> + </DIV> + <NOSCRIPT> + <P> + The Quick Bug Report form is unavailable because javascript is not + installed. Please submit the bug + <A href="http://tkhtml.tcl.tk/cvstrac/tktnew">via the web interface</A>. + Thanks. + </P> + </NOSCRIPT> + </DIV> + + <DIV class=block> + <A name=guidelines></A> + <H2>Bug Report Guidelines</H2> + <P> + Please report the bugs you observe in Hv3. If a web-page is rendered + incorrectly or some javascript features doesn't function correctly, + that's a bug. If an irritating error dialog pops up, that's a bug. + If Hv3 crashes, that's definitely a bug. + </P> + <P> + At this stage in the project, obtaining bug reports is very important. + Don't worry about submitting duplicates or accidentally reporting a + non-bug. Such reports take mere seconds to close and cause nobody any + inconveniance. Be as brief as you like - typing something like "the + page is busted because the menu is rendered wrong" is often all that + is required. + </P> + <P> + If you include a contact e-mail address, nobody but the Hv3 developers + will see it. And we'll (probably) send you an e-mail when the bug is + fixed. Or if the bug is complex we might mail you for clarification. + Whether or not you include an e-mail address, your help in bringing Hv3 + bugs to our attention will be greatly appreciated. + </P> + <P> + <CENTER><B><I>If in doubt, report it!</I></B></CENTER> + </P> + </DIV> + <DIV class=block> + <H2>Details</H2> + <P> + The Hv3/Tkhtml3 project uses cvstrac to manage source code, + bug-reports and user contributed wiki pages. Access the cvstrac + system here: + </P> + <P> + <A href="http://tkhtml.tcl.tk/cvstrac/"> + http://tkhtml.tcl.tk/cvstrac/ + </A> + </P> + + <P> + There are two ways to submit a bug into the system. The first is to + add the bug directly into cvstrac via the web-interface by visiting + the following link: + </P> + <P> + <A href="http://tkhtml.tcl.tk/cvstrac/tktnew"> + http://tkhtml.tcl.tk/cvstrac/tktnew + </A> + </P> + <P> + The second method is to submit the "Quick Bug Report" form above. + <P> + </DIV> + </BODY> +</HTML> + Property changes on: brlcad/trunk/src/other/hv3/bugreport.html ___________________________________________________________________ Added: svn:mime-type + text/html Added: svn:eol-style + native Added: brlcad/trunk/src/other/hv3/combobox.tcl =================================================================== --- brlcad/trunk/src/other/hv3/combobox.tcl (rev 0) +++ brlcad/trunk/src/other/hv3/combobox.tcl 2011-10-07 22:19:37 UTC (rev 47164) @@ -0,0 +1,2187 @@ +# Copyright (c) 1998-2003, Bryan Oakley +# All Rights Reservered +# +# Bryan Oakley +# oa...@ba... +# +# combobox v2.3 August 16, 2003 +# +# a combobox / dropdown listbox (pick your favorite name) widget +# written in pure tcl +# +# this code is freely distributable without restriction, but is +# provided as-is with no warranty expressed or implied. +# +# thanks to the following people who provided beta test support or +# patches to the code (in no particular order): +# +# Scott Beasley Alexandre Ferrieux Todd Helfter +# Matt Gushee Laurent Duperval John Jackson +# Fred Rapp Christopher Nelson +# Eric Galluzzo Jean-Francois Moine Oliver Bienert +# +# A special thanks to Martin M. Hunt who provided several good ideas, +# and always with a patch to implement them. Jean-Francois Moine, +# Todd Helfter and John Jackson were also kind enough to send in some +# code patches. +# +# ... and many others over the years. + +package require Tk 8.0 +package provide combobox 2.3 + +namespace eval ::combobox { + + # this is the public interface + namespace export combobox + + # these contain references to available options + variable widgetOptions + + # these contain references to available commands and subcommands + variable widgetCommands + variable scanCommands + variable listCommands +} + +# ::combobox::combobox -- +# +# This is the command that gets exported. It creates a new +# combobox widget. +# +# Arguments: +# +# w path of new widget to create +# args additional option/value pairs (eg: -background white, etc.) +# +# Results: +# +# It creates the widget and sets up all of the default bindings +# +# Returns: +# +# The name of the newly create widget + +proc ::combobox::combobox {w args} { + variable widgetOptions + variable widgetCommands + variable scanCommands + variable listCommands + + # perform a one time initialization + if {![info exists widgetOptions]} { + Init + } + + # build it... + eval Build $w $args + + # set some bindings... + SetBindings $w + + # and we are done! + return $w +} + + +# ::combobox::Init -- +# +# Initialize the namespace variables. This should only be called +# once, immediately prior to creating the first instance of the +# widget +# +# Arguments: +# +# none +# +# Results: +# +# All state variables are set to their default values; all of +# the option database entries will exist. +# +# Returns: +# +# empty string + +proc ::combobox::Init {} { + variable widgetOptions + variable widgetCommands + variable scanCommands + variable listCommands + variable defaultEntryCursor + + array set widgetOptions [list \ + -background {background Background} \ + -bd -borderwidth \ + -bg -background \ + -borderwidth {borderWidth BorderWidth} \ + -buttonbackground {buttonBackground Background} \ + -command {command Command} \ + -commandstate {commandState State} \ + -cursor {cursor Cursor} \ + -disabledbackground {disabledBackground DisabledBackground} \ + -disabledforeground {disabledForeground DisabledForeground} \ + -dropdownwidth {dropdownWidth DropdownWidth} \ + -editable {editable Editable} \ + -elementborderwidth {elementBorderWidth BorderWidth} \ + -fg -foreground \ + -font {font Font} \ + -foreground {foreground Foreground} \ + -height {height Height} \ + -highlightbackground {highlightBackground HighlightBackground} \ + -highlightcolor {highlightColor HighlightColor} \ + -highlightthickness {highlightThickness HighlightThickness} \ + -image {image Image} \ + -listvar {listVariable Variable} \ + -maxheight {maxHeight Height} \ + -opencommand {opencommand Command} \ + -relief {relief Relief} \ + -selectbackground {selectBackground Foreground} \ + -selectborderwidth {selectBorderWidth BorderWidth} \ + -selectforeground {selectForeground Background} \ + -state {state State} \ + -takefocus {takeFocus TakeFocus} \ + -textvariable {textVariable Variable} \ + -value {value Value} \ + -width {width Width} \ + -xscrollcommand {xScrollCommand ScrollCommand} \ + ] + + + set widgetCommands [list \ + bbox cget configure curselection \ + delete get icursor index \ + insert list scan selection \ + xview select toggle open \ + close subwidget \ + ] + + set listCommands [list \ + delete get \ + index insert size \ + ] + + set scanCommands [list mark dragto] + + # why check for the Tk package? This lets us be sourced into + # an interpreter that doesn't have Tk loaded, such as the slave + # interpreter used by pkg_mkIndex. In theory it should have no + # side effects when run + if {[lsearch -exact [package names] "Tk"] != -1} { + + ################################################################## + #- this initializes the option database. Kinda gross, but it works + #- (I think). + ################################################################## + + # the image used for the button... + if {$::tcl_platform(platform) == "windows"} { + image create bitmap ::combobox::bimage -data { + #define down_arrow_width 12 + #define down_arrow_height 12 + static char down_arrow_bits[] = { + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0xfc,0xf1,0xf8,0xf0,0x70,0xf0,0x20,0xf0, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00; + } + } + } else { + image create bitmap ::combobox::bimage -data { + #define down_arrow_width 15 + #define down_arrow_height 15 + static char down_arrow_bits[] = { + 0x00,0x80,0x00,0x80,0x00,0x80,0x00,0x80, + 0x00,0x80,0xf8,0x8f,0xf0,0x87,0xe0,0x83, + 0xc0,0x81,0x80,0x80,0x00,0x80,0x00,0x80, + 0x00,0x80,0x00,0x80,0x00,0x80 + } + } + } + + # compute a widget name we can use to create a temporary widget + set tmpWidget ".__tmp__" + set count 0 + while {[winfo exists $tmpWidget] == 1} { + set tmpWidget ".__tmp__$count" + incr count + } + + # get the scrollbar width. Because we try to be clever and draw our + # own button instead of using a tk widget, we need to know what size + # button to create. This little hack tells us the width of a scroll + # bar. + # + # NB: we need to be sure and pick a window that doesn't already + # exist... + scrollbar $tmpWidget + set sb_width [winfo reqwidth $tmpWidget] + set bbg [$tmpWidget cget -background] + destroy $tmpWidget + + # steal options from the entry widget + # we want darn near all options, so we'll go ahead and do + # them all. No harm done in adding the one or two that we + # don't use. + entry $tmpWidget + foreach foo [$tmpWidget configure] { + # the cursor option is special, so we'll save it in + # a special way + if {[lindex $foo 0] == "-cursor"} { + set defaultEntryCursor [lindex $foo 4] + } + if {[llength $foo] == 5} { + set option [lindex $foo 1] + set value [lindex $foo 4] + option add *Combobox.$option $value widgetDefault + + # these options also apply to the dropdown listbox + if {[string compare $option "foreground"] == 0 \ + || [string compare $option "background"] == 0 \ + || [string compare $option "font"] == 0} { + option add *Combobox*ComboboxListbox.$option $value \ + widgetDefault + } + } + } + destroy $tmpWidget + + # these are unique to us... + option add *Combobox.elementBorderWidth 1 widgetDefault + option add *Combobox.buttonBackground $bbg widgetDefault + option add *Combobox.dropdownWidth {} widgetDefault + option add *Combobox.openCommand {} widgetDefault + option add *Combobox.cursor {} widgetDefault + option add *Combobox.commandState normal widgetDefault + option add *Combobox.editable 1 widgetDefault + option add *Combobox.maxHeight 10 widgetDefault + option add *Combobox.height 0 + } + + # set class bindings + SetClassBindings +} + +# ::combobox::SetClassBindings -- +# +# Sets up the default bindings for the widget class +# +# this proc exists since it's The Right Thing To Do, but +# I haven't had the time to figure out how to do all the +# binding stuff on a class level. The main problem is that +# the entry widget must have focus for the insertion cursor +# to be visible. So, I either have to have the entry widget +# have the Combobox bindtag, or do some fancy juggling of +# events or some such. What a pain. +# +# Arguments: +# +# none +# +# Returns: +# +# empty string + +proc ::combobox::SetClassBindings {} { + + # make sure we clean up after ourselves... + bind Combobox <Destroy> [list ::combobox::DestroyHandler %W] + + # this will (hopefully) close (and lose the grab on) the + # listbox if the user clicks anywhere outside of it. Note + # that on Windows, you can click on some other app and + # the listbox will still be there, because tcl won't see + # that button click + set this {[::combobox::convert %W -W]} + bind Combobox <Any-ButtonPress> "$this close" + bind Combobox <Any-ButtonRelease> "$this close" + + # this helps (but doesn't fully solve) focus issues. The general + # idea is, whenever the frame gets focus it gets passed on to + # the entry widget + bind Combobox <FocusIn> {::combobox::tkTabToWindow \ + [::combobox::convert %W -W].entry} + + # this closes the listbox if we get hidden + bind Combobox <Unmap> {[::combobox::convert %W -W] close} + + return "" +} + +# ::combobox::SetBindings -- +# +# here's where we do most of the binding foo. I think there's probably +# a few bindings I ought to add that I just haven't thought +# about... +# +# I'm not convinced these are the proper bindings. Ideally all +# bindings should be on "Combobox", but because of my juggling of +# bindtags I'm not convinced thats what I want to do. But, it all +# seems to work, its just not as robust as it could be. +# +# Arguments: +# +# w widget pathname +# +# Returns: +# +# empty string + +proc ::combobox::SetBindings {w} { + upvar ::combobox::${w}::widgets widgets + upvar ::combobox::${w}::options options + + # juggle the bindtags. The basic idea here is to associate the + # widget name with the entry widget, so if a user does a bind + # on the combobox it will get handled properly since it is + # the entry widget that has keyboard focus. + bindtags $widgets(entry) \ + [concat $widgets(this) [bindtags $widgets(entry)]] + + bindtags $widgets(button) \ + [concat $widgets(this) [bindtags $widgets(button)]] + + # override the default bindings for tab and shift-tab. The + # focus procs take a widget as their only parameter and we + # want to make sure the right window gets used (for shift- + # tab we want it to appear as if the event was generated + # on the frame rather than the entry. + bind $widgets(entry) <Tab> \ + "::combobox::tkTabToWindow \[tk_focusNext $widgets(entry)\]; break" + bind $widgets(entry) <Shift-Tab> \ + "::combobox::tkTabToWindow \[tk_focusPrev $widgets(this)\]; break" + + # this makes our "button" (which is actually a label) + # do the right thing + bind $widgets(button) <ButtonPress-1> [list $widgets(this) toggle] + + # this lets the autoscan of the listbox work, even if they + # move the cursor over the entry widget. + bind $widgets(entry) <B1-Enter> "break" + + bind $widgets(listbox) <ButtonRelease-1> \ + "::combobox::Select [list $widgets(this)] \ + \[$widgets(listbox) nearest %y\]; break" + + bind $widgets(vsb) <ButtonPress-1> {continue} + bind $widgets(vsb) <ButtonRelease-1> {continue} + + bind $widgets(listbox) <Any-Motion> { + %W selection clear 0 end + %W activate @%x,%y + %W selection anchor @%x,%y + %W selection set @%x,%y @%x,%y + # need to do a yview if the cursor goes off the top + # or bottom of the window... (or do we?) + } + + # these events need to be passed from the entry widget + # to the listbox, or otherwise need some sort of special + # handling. + foreach event [list <Up> <Down> <Tab> <Return> <Escape> \ + <Next> <Prior> <Double-1> <1> <Any-KeyPress> \ + <FocusIn> <FocusOut>] { + bind $widgets(entry) $event \ + [list ::combobox::HandleEvent $widgets(this) $event] + } + + # like the other events, <MouseWheel> needs to be passed from + # the entry widget to the listbox. However, in this case we + # need to add an additional parameter + catch { + bind $widgets(entry) <MouseWheel> \ + [list ::combobox::HandleEvent $widgets(this) <MouseWheel> %D] + } +} + +# ::combobox::Build -- +# +# This does all of the work necessary to create the basic +# combobox. +# +# Arguments: +# +# w widget name +# args additional option/value pairs +# +# Results: +# +# Creates a new widget with the given name. Also creates a new +# namespace patterened after the widget name, as a child namespace +# to ::combobox +# +# Returns: +# +# the name of the widget + +proc ::combobox::Build {w args } { + variable widgetOptions + + if {[winfo exists $w]} { + error "window name \"$w\" already exists" + } + + # create the namespace for this instance, and define a few + # variables + namespace eval ::combobox::$w { + + variable ignoreTrace 0 + variable oldFocus {} + variable oldGrab {} + variable oldValue {} + variable options + variable this + variable widgets + + set widgets(foo) foo ;# coerce into an array + set options(foo) foo ;# coerce into an array + + unset widgets(foo) + unset options(foo) + } + + # import the widgets and options arrays into this proc so + # we don't have to use fully qualified names, which is a + # pain. + upvar ::combobox::${w}::widgets widgets + upvar ::combobox::${w}::options options + + # this is our widget -- a frame of class Combobox. Naturally, + # it will contain other widgets. We create it here because + # we need it in order to set some default options. + set widgets(this) [frame $w -class Combobox -takefocus 0] + set widgets(entry) [entry $w.entry -takefocus 1] + set widgets(button) [label $w.button -takefocus 0] + + # this defines all of the default options. We get the + # values from the option database. Note that if an array + # value is a list of length one it is an alias to another + # option, so we just ignore it + foreach name [array names widgetOptions] { + if {[llength $widgetOptions($name)] == 1} continue + + set optName [lindex $widgetOptions($name) 0] + set optClass [lindex $widgetOptions($name) 1] + + set value [option get $w $optName $optClass] + set options($name) $value + } + + # a couple options aren't available in earlier versions of + # tcl, so we'll set them to sane values. For that matter, if + # they exist but are empty, set them to sane values. + if {[string length $options(-disabledforeground)] == 0} { + set options(-disabledforeground) $options(-foreground) + } + if {[string length $options(-disabledbackground)] == 0} { + set options(-disabledbackground) $options(-background) + } + + # if -value is set to null, we'll remove it from our + # local array. The assumption is, if the user sets it from + # the option database, they will set it to something other + # than null (since it's impossible to determine the difference + # between a null value and no value at all). + if {[info exists options(-value)] \ + && [string length $options(-value)] == 0} { + unset options(-value) + } + + # we will later rename the frame's widget proc to be our + # own custom widget proc. We need to keep track of this + # new name, so we'll define and store it here... + set widgets(frame) ::combobox::${w}::$w + + # gotta do this sooner or later. Might as well do it now + pack $widgets(button) -side right -fill y -expand no + pack $widgets(entry) -side left -fill both -expand yes + + # I should probably do this in a catch, but for now it's + # good enough... What it does, obviously, is put all of + # the option/values pairs into an array. Make them easier + # to handle later on... + array set options $args + + # now, the dropdown list... the same renaming nonsense + # must go on here as well... + set widgets(dropdown) [toplevel $w.top] + set widgets(listbox) [listbox $w.top.list] + set widgets(vsb) [scrollbar $w.top.vsb] + + pack $widgets(listbox) -side left -fill both -expand y + + # fine tune the widgets based on the options (and a few + # arbitrary values...) + + # NB: we are going to use the frame to handle the relief + # of the widget as a whole, so the entry widget will be + # flat. This makes the button which drops down the list + # to appear "inside" the entry widget. + + $widgets(vsb) configure \ + -borderwidth 1 \ + -command "$widgets(listbox) yview" \ + -highlightthickness 0 + + $widgets(button) configure \ + -background $options(-buttonbackground) \ + -highlightthickness 0 \ + -borderwidth $options(-elementborderwidth) \ + -relief raised \ + -width [expr {[winfo reqwidth $widgets(vsb)] - 2}] + + $widgets(entry) configure \ + -borderwidth 0 \ + -relief flat \ + -highlightthickness 0 + + $widgets(dropdown) configure \ + -borderwidth $options(-elementborderwidth) \ + -relief sunken + + $widgets(listbox) configure \ + -selectmode browse \ + -background [$widgets(entry) cget -bg] \ + -yscrollcommand "$widgets(vsb) set" \ + -exportselection false \ + -borderwidth 0 + + +# trace variable ::combobox::${w}::entryTextVariable w \ +# [list ::combobox::EntryTrace $w] + + # do some window management foo on the dropdown window + wm overrideredirect $widgets(dropdown) 1 + wm transient $widgets(dropdown) [winfo toplevel $w] + wm group $widgets(dropdown) [winfo parent $w] + wm resizable $widgets(dropdown) 0 0 + wm withdraw $widgets(dropdown) + + # this moves the original frame widget proc into our + # namespace and gives it a handy name + rename ::$w $widgets(frame) + + # now, create our widget proc. Obviously (?) it goes in + # the global namespace. All combobox widgets will actually + # share the same widget proc to cut down on the amount of + # bloat. + proc ::$w {command args} \ + "eval ::combobox::WidgetProc $w \$command \$args" + + + # ok, the thing exists... let's do a bit more configuration. + if {[catch "::combobox::Configure [list $widgets(this)] [array get options]" error]} { + catch {destroy $w} + error "internal error: $error" + } + + return "" + +} + +# ::combobox::HandleEvent -- +# +# this proc handles events from the entry widget that we want +# handled specially (typically, to allow navigation of the list +# even though the focus is in the entry widget) +# +# Arguments: +# +# w widget pathname +# event a string representing the event (not necessarily an +# actual event) +# args additional arguments required by particular events + +proc ::combobox::HandleEvent {w event args} { + upvar ::combobox::${w}::widgets widgets + upvar ::combobox::${w}::options options + upvar ::combobox::${w}::oldValue oldValue + + # for all of these events, if we have a special action we'll + # do that and do a "return -code break" to keep additional + # bindings from firing. Otherwise we'll let the event fall + # on through. + switch $event { + + "<MouseWheel>" { + if {[winfo ismapped $widgets(dropdown)]} { + set D [lindex $args 0] + # the '120' number in the following expression has + # it's genesis in the tk bind manpage, which suggests + # that the smallest value of %D for mousewheel events + # will be 120. The intent is to scroll one line at a time. + $widgets(listbox) yview scroll [expr {-($D/120)}] units + } + } + + "<Any-KeyPress>" { + # if the widget is editable, clear the selection. + # this makes it more obvious what will happen if the + # user presses <Return> (and helps our code know what + # to do if the user presses return) + if {$options(-editable)} { + $widgets(listbox) see 0 + $widgets(listbox) selection clear 0 end + $widgets(listbox) selection anchor 0 + $widgets(listbox) activate 0 + } + } + + "<FocusIn>" { + set oldValue [$widgets(entry) get] + } + + "<FocusOut>" { + if {![winfo ismapped $widgets(dropdown)]} { + # did the value change? + set newValue [$widgets(entry) get] + if {$oldValue != $newValue} { + CallCommand $widgets(this) $newValue + } + } + } + + "<1>" { + set editable [::combobox::GetBoolean $options(-editable)] + if {!$editable} { + if {[winfo ismapped $widgets(dropdown)]} { + $widgets(this) close + return -code break; + + } else { + if {$options(-state) != "disabled"} { + $widgets(this) open + return -code break; + } + } + } + } + + "<Double-1>" { + if {$options(-state) != "disabled"} { + $widgets(this) toggle + return -code break; + } + } + + "<Tab>" { + if {[winfo ismapped $widgets(dropdown)]} { + ::combobox::Find $widgets(this) 0 + return -code break; + } else { + ::combobox::SetValue $widgets(this) [$widgets(this) get] + } + } + + "<Escape>" { +# $widgets(entry) delete 0 end +# $widgets(entry) insert 0 $oldValue + if {[winfo ismapped $widgets(dropdown)]} { + $widgets(this) close + return -code break; + } + } + + "<Return>" { + # did the value change? + set newValue [$widgets(entry) get] + if {$oldValue != $newValue} { + CallCommand $widgets(this) $newValue + } + + if {[winfo ismapped $widgets(dropdown)]} { + ::combobox::Select $widgets(this) \ + [$widgets(listbox) curselection] + return -code break; + } + + } + + "<Next>" { + $widgets(listbox) yview scroll 1 pages + set index [$widgets(listbox) index @0,0] + $widgets(listbox) see $index + $widgets(listbox) activate $index + $widgets(listbox) selection clear 0 end + $widgets(listbox) selection anchor $index + $widgets(listbox) selection set $index + + } + + "<Prior>" { + $widgets(listbox) yview scroll -1 pages + set index [$widgets(listbox) index @0,0] + $widgets(listbox) activate $index + $widgets(listbox) see $index + $widgets(listbox) selection clear 0 end + $widgets(listbox) selection anchor $index + $widgets(listbox) selection set $index + } + + "<Down>" { + if {[winfo ismapped $widgets(dropdown)]} { + ::combobox::tkListboxUpDown $widgets(listbox) 1 + return -code break; + + } else { + if {$options(-state) != "disabled"} { + $widgets(this) open + return -code break; + } + } + } + "<Up>" { + if {[winfo ismapped $widgets(dropdown)]} { + ::combobox::tkListboxUpDown $widgets(listbox) -1 + return -code break; + + } else { + if {$options(-state) != "disabled"} { + $widgets(this) open + return -code break; + } + } + } + } + + return "" +} + +# ::combobox::DestroyHandler {w} -- +# +# Cleans up after a combobox widget is destroyed +# +# Arguments: +# +# w widget pathname +# +# Results: +# +# The namespace that was created for the widget is deleted, +# and the widget proc is removed. + +proc ::combobox::DestroyHandler {w} { + + catch { + # if the widget actually being destroyed is of class Combobox, + # remove the namespace and associated proc. + if {[string compare [winfo class $w] "Combobox"] == 0} { + # delete the namespace and the proc which represents + # our widget + namespace delete ::combobox::$w + rename $w {} + } + } + return "" +} + +# ::combobox::Find +# +# finds something in the listbox that matches the pattern in the +# entry widget and selects it +# +# N.B. I'm not convinced this is working the way it ought to. It +# works, but is the behavior what is expected? I've also got a gut +# feeling that there's a better way to do this, but I'm too lazy to +# figure it out... +# +# Arguments: +# +# w widget pathname +# exact boolean; if true an exact match is desired +# +# Returns: +# +# Empty string + +proc ::combobox::Find {w {exact 0}} { + upvar ::combobox::${w}::widgets widgets + upvar ::combobox::${w}::options options + + ## *sigh* this logic is rather gross and convoluted. Surely + ## there is a more simple, straight-forward way to implement + ## all this. As the saying goes, I lack the time to make it + ## shorter... + + # use what is already in the entry widget as a pattern + set pattern [$widgets(entry) get] + + if {[string length $pattern] == 0} { + # clear the current selection + $widgets(listbox) see 0 + $widgets(listbox) selection clear 0 end + $widgets(listbox) selection anchor 0 + $widgets(listbox) activate 0 + return + } + + # we're going to be searching this list... + set list [$widgets(listbox) get 0 end] + + # if we are doing an exact match, try to find, + # well, an exact match + set exactMatch -1 + if {$exact} { + set exactMatch [lsearch -exact $list $pattern] + } + + # search for it. We'll try to be clever and not only + # search for a match for what they typed, but a match for + # something close to what they typed. We'll keep removing one + # character at a time from the pattern until we find a match + # of some sort. + set index -1 + while {$index == -1 && [string length $pattern]} { + set index [lsearch -glob $list "$pattern*"] + if {$index == -1} { + regsub {.$} $pattern {} pattern + } + } + + # this is the item that most closely matches... + set thisItem [lindex $list $index] + + # did we find a match? If so, do some additional munging... + if {$index != -1} { + + # we need to find the part of the first item that is + # unique WRT the second... I know there's probably a + # simpler way to do this... + + set nextIndex [expr {$index + 1}] + set nextItem [lindex $list $nextIndex] + + # we don't really need to do much if the next + # item doesn't match our pattern... + if {[string match $pattern* $nextItem]} { + # ok, the next item matches our pattern, too + # now the trick is to find the first character + # where they *don't* match... + set marker [string length $pattern] + while {$marker <= [string length $pattern]} { + set a [string index $thisItem $marker] + set b [string index $nextItem $marker] + if {[string compare $a $b] == 0} { + append pattern $a + incr marker + } else { + break + } + } + } else { + set marker [string length $pattern] + } + + } else { + set marker end + set index 0 + } + + # ok, we know the pattern and what part is unique; + # update the entry widget and listbox appropriately + if {$exact && $exactMatch == -1} { + # this means we didn't find an exact match + $widgets(listbox) selection clear 0 end + $widgets(listbox) see $index + + } elseif {!$exact} { + # this means we found something, but it isn't an exact + # match. If we find something that *is* an exact match we + # don't need to do the following, since it would merely + # be replacing the data in the entry widget with itself + set oldstate [$widgets(entry) cget -state] + $widgets(entry) configure -state normal + $widgets(entry) delete 0 end + $widgets(entry) insert end $thisItem + $widgets(entry) selection clear + $widgets(entry) selection range $marker end + $widgets(listbox) activate $index + $widgets(listbox) selection clear 0 end + $widgets(listbox) selection anchor $index + $widgets(listbox) selection set $index + $widgets(listbox) see $index + $widgets(entry) configure -state $oldstate + } +} + +# ::combobox::Select -- +# +# selects an item from the list and sets the value of the combobox +# to that value +# +# Arguments: +# +# w widget pathname +# index listbox index of item to be selected +# +# Returns: +# +# empty string + +proc ::combobox::Select {w index} { + upvar ::combobox::${w}::widgets widgets + upvar ::combobox::${w}::options options + + # the catch is because I'm sloppy -- presumably, the only... [truncated message content] |