#!/usr/local/bin/wish4.0 -f # $Header: /home/cia/crosby/proj/advcad/tk/RCS/ifmap,v 2.0 1996/05/26 07:44:29 crosby Rel $ # IF Map Editor # # A visual tool for Interactive fiction playing # # Copyright 1996 Matthew Crosby, crosby@cs.colorado.edu # # This is free software that may be redistributed and/or modified # under the terms of the GNU General Public License as published by # the Free Software Foundation. You should have recieved a copy of # this license, if not, it may be obtained by ftp from prep.ai.mit.edu # # File box copyright Sven Delmas, garfield@cs.tu-berlin.de # # ###################################################################### # # Constants # set Version "0.1" set Debug 0 # Size of the room nodes set Xsize 60 set Ysize 30 set SQsize 4 set LoopSize 10 set directions {NW N NE W E SW S SE U D} ###################################################################### # # Variables # # The canvas canvas .map -scrollregion {0 0 2048 2048} \ -xscrollcommand ".horizontal set" -yscrollcommand ".vertical set" \ -bd 2 -relief sunken # The actual height, width of the map set MapXMax 0 set MapYMax 0 set MapXMin 2048 set MapYMin 2048 # # The title of the Dungeon # set DungeonTitle "" # Id: Maps canvas id to room id and or corridoor id # Room_id: In form of Room # Edge_id: In form of Edge # ReverseId: Maps room/edge id back to canvas ID # Nodelist: List of nodes # Node: Node information (pseudo structure) # Node(Room_Id.var) Var can be: # X: X pos # Y: Y pos # joins.{N,S,etc}: Room ID room joins to # joinsThru.{N,S,etc}: Edge ID they join through # joinsTo.{N,S,etc}: Exit in other room it joins # Name: room name # Desc: room desc # textId: ID of text (name) # Type: Room type # EdgeList: List of edges # Edge: Edge information (pseudo structure) # Edge(Edge_id.var) Var can be: # Node.1: Room_id of 1st node # Node.2: Room_id of 2nd node # NodePos.1,2 Pos (N,S, etc) # Secret: Is this a secret passage? # Oneway: Is this a oneway passage? 0-no 12-from 1 to 2, 21-from 2 to 1 # nodecount & edgecount: These keep a running count of nodes & edges set Nodecount 0 set Edgecount 0 # nextnode and next edge. These two are counters that are never decremented. # They are used to generate a new unique edge/node id. set NextNode 0 set NextEdge 0 # CPlist # contains the Direction for a particular ID # Selected # The currently selected room set selected 0 # moveedge # The edge being moved set movededge 0 # Changed # Flage to indicate that we changed something set changed 0 ###################################################################### # # Room handling procedures # # # Withcoord--converts coord from screen to canvas # proc withcoord {command oldx oldy} { set x [.map canvasx $oldx] set y [.map canvasy $oldy] $command $x $y } # # mkRoom: Creates a new room at (x,y) # proc mkRoom {x y {node_id ""} {name ""} {desc ""} {type "Normal"} {joins ""} {joinsto ""} {joinsthru ""}} { global Id Nodelist Node Nodecount directions ReverseId NextNode changed global MapXMax MapYMax MapXMin MapYMin # drawRoom: This procedure draws the room itself proc drawroom {x y node} { global Id Xsize Ysize Node ReverseId set new [.map create rectangle [expr $x-($Xsize/2)] \ [expr $y-($Ysize/2)] [expr $x+($Xsize/2)] \ [expr $y+($Ysize/2)] \ -outline black -fill white -tags $node ] set ReverseId($node) $new .map bind $new {withcoord roompopup %x %y} .map bind $new {withcoord selectroom %x %y} .map bind $new {withcoord moveroom %x %y} .map bind $new deleteobj set Id($new) $node # set new [ .map create text $x $y -tags $node -width [expr $Xsize-4] -text $Node($node.Name) -font "-*-helvetica-medium-r-normal--11-*-*-*-*-*-*-*"] set new [ .map create text $x $y -tags $node -width [expr $Xsize-4] -text $Node($node.Name) -justify center ] .map bind $new {withcoord roompopup %x %y} .map bind $new {withcoord moveroom %x %y} .map bind $new {withcoord selectroom %x %y} set Id($new) $node set Node($node.textId) $new } # drawconpoint: Draws the buttons for the edges proc drawconpoint {node dir} { global SQsize CPlist Id Node set pos [findConPos $node $dir] set x [ lindex $pos 0 ] set y [ lindex $pos 1 ] set new [.map create rectangle [expr $x-$SQsize/2] [expr $y-$SQsize/2] \ [expr $x+$SQsize/2] [expr $y+$SQsize/2] \ -outline black -fill black -tags "$node cpoint" ] .map bind $new { withcoord loopback %x %y} .map bind $new { withcoord drawline %x %y} .map bind $new {withcoord movedrawline %x %y} .map bind $new {withcoord finishdrawline %x %y} set Id($new) $node set CPlist($new) $dir .map bind $new set Node($node.joins.${dir}) 0 set Node($node.joinsThru.${dir}) 0 set Node($node.joinsTo.${dir}) 0 } # create an ID for this room if {$node_id==""} { set node_id "Room$NextNode" } set Nodelist($node_id) $node_id set Node($node_id.Name) $name set Node($node_id.Desc) $desc # set up the variables set Node(${node_id}.X) $x set Node(${node_id}.Y) $y set Node(${node_id}.Name) $name set Node(${node_id}.Type) $type #foreach i $joins { set $Node(${node_id}.Joins) $i } #foreach i $joinsto { set $Node(${node_id}.JoinsTo) $i } #foreach i $joinsthru { set $Node(${node_id}.JoinsThru) $i } # draw the room itself drawroom $x $y $node_id if {$x>$MapXMax} {set MapXMax $x} if {$y>$MapYMax} {set MapYMax $y} if {$x<$MapXMin} {set MapXMin $x} if {$y<$MapYMin} {set MapYMin $y} setroomtype $node_id $type foreach i $directions { drawconpoint $node_id $i } set Nodecount [ expr $Nodecount+1 ] set NextNode [ expr $NextNode+1 ] set changed 1 } # # findConPos: Finds the x,y position of a connection button # proc findConPos {node pos} { global Node Xsize Ysize SQsize set x $Node(${node}.X) set y $Node(${node}.Y) switch $pos { NW { set x [expr $x-($Xsize/2)-$SQsize/2] set y [expr $y-($Ysize/2)-$SQsize/2] } N { set x [expr $x] set y [expr $y-($Ysize/2)-$SQsize/2] } NE { set x [expr $x+($Xsize/2)+$SQsize/2] set y [expr $y-($Ysize/2)-$SQsize/2] } W { set x [expr $x-($Xsize/2)-$SQsize/2] set y [expr $y] } E { set x [expr $x+($Xsize/2)+$SQsize/2] set y [expr $y] } SW { set x [expr $x-($Xsize/2)-$SQsize/2] set y [expr $y+($Ysize/2)+$SQsize/2] } S { set x [expr $x] set y [expr $y+($Ysize/2)+$SQsize/2] } SE { set x [expr $x+($Xsize/2)+$SQsize/2] set y [expr $y+($Ysize/2)+$SQsize/2] } U { set x [expr $x-($Xsize/4)-$SQsize/2] set y [expr $y-($Ysize/2)-$SQsize/2] } D { set x [expr $x+($Xsize/4)+$SQsize/2] set y [expr $y+($Ysize/2)+$SQsize/2] } } return "$x $y" } # # findConPoint: Finds the x,y position of a connecting point for a node. # proc findConPoint {node pos} { global Node Xsize Ysize SQsize set x $Node(${node}.X) set y $Node(${node}.Y) switch $pos { NW { set x [expr $x-($Xsize/2)] set y [expr $y-($Ysize/2)] } N { set x [expr $x] set y [expr $y-($Ysize/2)] } NE { set x [expr $x+($Xsize/2)] set y [expr $y-($Ysize/2)] } W { set x [expr $x-($Xsize/2)] set y [expr $y] } E { set x [expr $x+($Xsize/2)] set y [expr $y] } SW { set x [expr $x-($Xsize/2)] set y [expr $y+($Ysize/2)] } S { set x [expr $x] set y [expr $y+($Ysize/2)] } SE { set x [expr $x+($Xsize/2)] set y [expr $y+($Ysize/2)] } U { set x [expr $x-($Xsize/4)] set y [expr $y-($Ysize/2)] } D { set x [expr $x+($Xsize/4)] set y [expr $y+($Ysize/2)] } } return "$x $y" } # # Moveroom. Moves rooms # proc moveroom {x y} { global Id Xsize Ysize Node directions ReverseId changed Edge global MapXMax MapYMax MapXMin MapYMin set tag $Id([.map find withtag current]) .map move $tag [expr $x-$Node($tag.X)] [expr $y-$Node(${tag}.Y)] if {$x>$MapXMax} {set MapXMax $x} if {$y>$MapYMax} {set MapYMax $y} if {$x<$MapXMin} {set MapXMin $x} if {$y<$MapYMin} {set MapYMin $y} set Node(${tag}.X) $x set Node(${tag}.Y) $y foreach i $directions { if { ($Node(${tag}.joins.${i}) != 0) && \ ($Node(${tag}.joins.${i}) != $tag) } { set edge $Node(${tag}.joinsThru.${i}) set tag1 $Edge($edge.Node.1) set tag2 $Edge($edge.Node.2) set pos [ findConPoint $tag1 $Edge($edge.NodePos.1) ] set pos2 [ findConPoint $tag2 $Edge($edge.NodePos.2) ] .map coords $ReverseId($edge) \ [ lindex $pos 0 ] [ lindex $pos 1] \ [ lindex $pos2 0 ] [ lindex $pos2 1] } } set changed 1 } # # Selectroom. Selects a room # proc selectroom {x y} { global selected Id ReverseId unselect set room $Id([.map find withtag current]) set id $ReverseId($room) set selected $room .map itemconfigure $id -outline red -width 2 } # # Selectedge. Selects an edge # proc selectedge {x y} { global selected Id ReverseId Node Edge unselect set selected $Id([.map find withtag current]) set id $ReverseId($selected) if { $Edge($selected.Node.1)==$Edge($selected.Node.2) } { .map itemconfigure $id -outline red -width 2 } else { .map itemconfigure $id -fill red -width 2 } } # # Unselect. Selects whatever is selected # proc unselect { } { global selected ReverseId Edge if [string match Edge* $selected] { if { $Edge($selected.Node.1)==$Edge($selected.Node.2) } { .map itemconfigure $ReverseId($selected) -outline black -width 1 } elseif {$Edge($selected.Secret) } { .map itemconfigure $ReverseId($selected) -fill grey -width 1 } else { .map itemconfigure $ReverseId($selected) -fill black -width 1 } } if [string match Room* $selected] { .map itemconfigure $ReverseId($selected) -outline black -width 1 } set selected 0 } # # drawline. Begins drawing an edge # proc drawline {x y} { global CPlist Node Id currentline cstartroom cstart changed xorig yorig unselect set idval [.map find withtag current] set node $Id($idval) if { $Node($node.joins.$CPlist($idval))==0 } { set pos [ findConPoint $node $CPlist($idval) ] set x [ lindex $pos 0 ] set y [ lindex $pos 1 ] set new [.map create line $x $y [expr $x+1] [expr $y+1 ]] set currentline $new set cstartroom $node set cstart $idval set changed 1 } else { set currentline 0 } set xorig x set yorig y } # # movedrawline. Continues drawing an edge # proc movedrawline {x2 y2} { global Id currentline CPlist cstartroom cstart if {$currentline!=0} { set pos [findConPoint $cstartroom $CPlist($cstart)] set x1 [ lindex $pos 0 ] set y1 [ lindex $pos 1 ] .map coords $currentline $x1 $y1 $x2 $y2 } } # # finishdrawline. Finishes off an edge. # proc finishdrawline {x y} { global Id currentline CPlist cstartroom cstart Edgecount Node ReverseId \ Edge EdgeList NextEdge xorig yorig set cend [ findbutton $x $y ] if {$cend != 0} { set cendroom [lindex $cend 0] set cendir [lindex $cend 1] set cstartdir $CPlist($cstart) if { ($Node($cendroom.joins.$cendir)==0) && (($cendroom!=$cstartroom) \ || ($cendir != $cstartdir)) } { set pos [findConPoint $cstartroom $cstartdir ] set x1 [ lindex $pos 0 ] set y1 [ lindex $pos 1 ] set pos [findConPoint $cendroom $cendir ] set x2 [ lindex $pos 0 ] set y2 [ lindex $pos 1 ] .map coords $currentline $x1 $y1 $x2 $y2 .map bind $currentline { withcoord selectedge %x %y } .map bind $currentline { withcoord edgepopup %x %y } .map bind $currentline { withcoord moveedge %x %y } .map bind $currentline {withcoord finishmoveedge %x %y} set edge_id "Edge$NextEdge" set EdgeList($edge_id) $edge_id set Id($currentline) $edge_id set ReverseId($edge_id) $currentline set Edge(${edge_id}.Node.1) $cstartroom set Edge(${edge_id}.NodePos.1) $cstartdir set Edge(${edge_id}.Node.2) $cendroom set Edge(${edge_id}.NodePos.2) $cendir set Edge(${edge_id}.Secret) 0 set Edge(${edge_id}.Oneway) 0 set Edgecount [expr $Edgecount+1] set NextEdge [expr $NextEdge+1] set Node(${cstartroom}.joins.${cstartdir}) $cendroom set Node(${cendroom}.joins.${cendir}) $cstartroom set Node(${cstartroom}.joinsThru.${cstartdir}) $edge_id set Node(${cendroom}.joinsThru.${cendir}) $edge_id set Node(${cstartroom}.joinsTo.${cstartdir}) $cendir set Node(${cendroom}.joinsTo.${cendir}) $cstartdir } else { .map delete $currentline } } else { .map delete $currentline } set currentline 0 } # # mkedge. Makes an edge between two different objects. # proc mkEdge {node1 pos1 node2 pos2 {edge_id ""} {secret 0} {oneway 0}} { global EdgeList Id ReverseId Edge Edgecount Node NextEdge changed set pos [findConPoint $node1 $pos1] set x1 [lindex $pos 0] set y1 [lindex $pos 1] if {$node1==$node2} { loopback $x1 $y1 $edge_id } else { set pos [findConPoint $node2 $pos2] set x2 [lindex $pos 0] set y2 [lindex $pos 1] set currentline [.map create line $x1 $y1 $x2 $y2] .map bind $currentline { withcoord selectedge %x %y } .map bind $currentline { withcoord edgepopup %x %y } .map bind $currentline { withcoord moveedge %x %y } .map bind $currentline {withcoord finishmoveedge %x %y} if {$edge_id==""} { set edge_id "Edge$NextEdge" } set EdgeList($edge_id) $edge_id set Id($currentline) $edge_id set ReverseId($edge_id) $currentline set Edge(${edge_id}.Node.1) $node1 set Edge(${edge_id}.NodePos.1) $pos1 set Edge(${edge_id}.Node.2) $node2 set Edge(${edge_id}.NodePos.2) $pos2 set Edge(${edge_id}.Secret) $secret set Edge(${edge_id}.Oneway) $oneway setedgetype $edge_id $secret $oneway set Edgecount [expr $Edgecount+1] set NextEdge [expr $NextEdge+1] set Node(${node1}.joins.${pos1}) $node2 set Node(${node2}.joins.${pos2}) $node1 set Node(${node1}.joinsThru.${pos1}) $edge_id set Node(${node2}.joinsThru.${pos2}) $edge_id set Node(${node1}.joinsTo.${pos1}) $pos2 set Node(${node2}.joinsTo.${pos2}) $pos1 } set changed 1 } # # moveedge Moves an edge # proc moveedge {x y} { global Edge Id selected movededge ReverseId notmoved changed if { $movededge==0 } { set pos [findConPoint $Edge($selected.Node.1) \ $Edge($selected.NodePos.1) ] set x1 [ lindex $pos 0 ] set y1 [ lindex $pos 1 ] set pos [findConPoint $Edge($selected.Node.2) \ $Edge($selected.NodePos.2) ] set x2 [ lindex $pos 0 ] set y2 [ lindex $pos 1 ] if { [ expr sqrt(($x-$x1)*($x-$x1)+($y-$y1)*($y-$y1))< \ sqrt(($x-$x2)*($x-$x2)+($y-$y2)*($y-$y2))] } { set x1 $x2 set y1 $y2 set notmoved 2 set movededge 1 } else { set movededge 2 set notmoved 1 } } else { set pos [findConPoint $Edge($selected.Node.$notmoved) \ $Edge($selected.NodePos.$notmoved) ] set x1 [ lindex $pos 0 ] set y1 [ lindex $pos 1 ] } if { $movededge==1 } { .map coords $ReverseId($selected) $x $y $x1 $y1 } else { .map coords $ReverseId($selected) $x1 $y1 $x $y } set changed 1 } # # finishmoveedge. Finished moving an edge. # proc finishmoveedge {x y} { global Edge ReverseId selected movededge Node notmoved set nogo 1 set cend [ findbutton $x $y ] if { $cend != 0 } { set room [lindex $cend 0] set dir [lindex $cend 1] if { $Node($room.joins.$dir) == 0 } { set nogo 0 set Node($Edge($selected.Node.$notmoved).joins.$Edge($selected.NodePos.$notmoved)) $room set Node($Edge($selected.Node.$notmoved).joinsTo.$Edge($selected.NodePos.$notmoved)) $dir set Node(${room}.joins.$dir) $Edge($selected.Node.$notmoved) set Node(${room}.joinsThru.$dir) $selected set Node(${room}.joinsTo.$dir) $Edge($selected.NodePos.$notmoved) set Node($Edge($selected.Node.$movededge).joins.$Edge($selected.NodePos.$movededge)) 0 set Node($Edge($selected.Node.$notmoved).joinsTo.$Edge($selected.NodePos.$movededge)) 0 set Node($Edge($selected.Node.$notmoved).joinsThru.$Edge($selected.NodePos.$movededge)) 0 set Edge(${selected}.Node.$movededge) $room set Edge(${selected}.NodePos.$movededge) $dir } } if { $nogo == 1 } { set pos [findConPoint $Edge($selected.Node.1) \ $Edge($selected.NodePos.1) ] set x1 [ lindex $pos 0 ] set y1 [ lindex $pos 1 ] set pos [findConPoint $Edge($selected.Node.2) \ $Edge($selected.NodePos.2) ] set x2 [ lindex $pos 0 ] set y2 [ lindex $pos 1 ] } set pos [findConPoint $Edge($selected.Node.1) \ $Edge($selected.NodePos.1) ] set x1 [ lindex $pos 0 ] set y1 [ lindex $pos 1 ] set pos [findConPoint $Edge($selected.Node.2) \ $Edge($selected.NodePos.2) ] set x2 [ lindex $pos 0 ] set y2 [ lindex $pos 1 ] .map coords $ReverseId($selected) $x1 $y1 $x2 $y2 set movededge 0 } # # loopback: Implements a loop corridoor # proc loopback {x y {edge_id ""}} { global Id Edgecount Node ReverseId LoopSize Edge EdgeList NextEdge changed unselect set button [ findbutton $x $y ] if {$button != 0} { set buttonroom [lindex $button 0] set buttondir [lindex $button 1] if { $Node($buttonroom.joins.$buttondir)==0 } { set pos [findConPoint $buttonroom $buttondir ] set x [ lindex $pos 0 ] set y [ lindex $pos 1 ] switch $buttondir { NW { set x1 [expr $x-$LoopSize ] set x2 $x set y1 [expr $y-$LoopSize ] set y2 $y } N { set x1 [expr $x-$LoopSize/2 ] set x2 [expr $x+$LoopSize/2 ] set y1 [expr $y-$LoopSize ] set y2 $y } NE { set x1 $x set x2 [expr $x+$LoopSize ] set y1 [expr $y-$LoopSize ] set y2 $y } W { set x1 [expr $x-$LoopSize ] set x2 $x set y1 [expr $y-$LoopSize/2 ] set y2 [expr $y+$LoopSize/2 ] } E { set x1 $x set x2 [expr $x+$LoopSize ] set y1 [expr $y-$LoopSize/2 ] set y2 [expr $y+$LoopSize/2 ] } SW { set x1 [expr $x-$LoopSize ] set x2 $x set y1 $y set y2 [expr $y+$LoopSize ] } S { set x1 [expr $x-$LoopSize/2 ] set x2 [expr $x+$LoopSize/2 ] set y1 $y set y2 [expr $y+$LoopSize ] } SE { set x1 $x set x2 [expr $x+$LoopSize ] set y1 $y set y2 [expr $y+$LoopSize ] } U { set x1 [expr $x-$LoopSize/2 ] set x2 [expr $x+$LoopSize/2 ] set y1 [expr $y-$LoopSize ] set y2 $y } D { set x1 [expr $x-$LoopSize/2 ] set x2 [expr $x+$LoopSize/2 ] set y1 $y set y2 [expr $y+$LoopSize ] } } debug $buttonroom set currentline [ .map create oval $x1 $y1 $x2 $y2 -tags $buttonroom ] .map bind $currentline { withcoord selectedge %x %y } if {$edge_id==""} { set edge_id "Edge$NextEdge" } set EdgeList($edge_id) $edge_id set Id($currentline) $edge_id set ReverseId($edge_id) $currentline set Edge(${edge_id}.Node.1) $buttonroom set Edge(${edge_id}.NodePos.1) $buttondir set Edge(${edge_id}.Node.2) $buttonroom set Edge(${edge_id}.NodePos.2) $buttondir set Edge(${edge_id}.Secret) 0 set Edge(${edge_id}.Oneway) 0 set Node(${buttonroom}.joins.${buttondir}) $buttonroom set Node(${buttonroom}.joinsThru.${buttondir}) $edge_id set Node(${buttonroom}.joinsTo.${buttondir}) $buttondir set Edgecount [expr $Edgecount+1] set NextEdge [expr $NextEdge+1] } set changed 1 } } # # findbutton. Finds the button associated with an X,Y pair # proc findbutton {x y} { global Node Xsize Ysize Nodelist SQsize directions foreach i [array names Nodelist] { foreach j $directions { if { [expr $x>[expr $Node(${i}.X)-($Xsize)] && \ $x<[expr $Node(${i}.X)+($Xsize)] && \ $y>[expr $Node(${i}.Y)-($Ysize)] && \ $y<[expr $Node(${i}.Y)+($Ysize)]] } { set pos [ findConPoint $i $j ] set x1 [ lindex $pos 0 ] set y1 [ lindex $pos 1 ] if { [expr $x>$x1-$SQsize] && [expr $x<$x1+$SQsize] && [expr $y>$y1-$SQsize] && [expr $y<$y1+$SQsize]} { return "$i $j" } } } } return 0 } # # inroom. Determines what room an x,y pair is in. # proc inroom {x y} { global Node Xsize Ysize Nodelist foreach i [array names Nodelist] { if { [expr $x>[expr $Node(${i}.X)-($Xsize)] && \ $x<[expr $Node(${i}.X)+($Xsize)] && \ $y>[expr $Node(${i}.Y)-($Ysize)] && \ $y<[expr $Node(${i}.Y)+($Ysize)]] } { return $i } } return 0 } # # InObject Boolean, determines whether or not this is in an object # proc inobject {x y} { global Node Xsize Ysize Nodelist if { [ .map find overlapping [expr $x-2] [expr $y-2] [expr $x+2] \ [expr $y+2] ] != "" } { return 1 } return 0 } proc setroomtype {room type} { global ReverseId switch $type { Normal { .map itemconfigure $ReverseId($room) -fill "white" -outline "black" .map dtag $ReverseId($room) conn } Maze { .map itemconfigure $ReverseId($room) -fill "wheat" -outline "black" .map dtag $ReverseId($room) conn } Joins { .map itemconfigure $ReverseId($room) -fill "light grey" -outline "" .map addtag conn withtag $ReverseId($room) } } } proc setedgetype {edge secret oneway} { global ReverseId if { $secret } { .map itemconfigure $ReverseId($edge) -fill lightgrey } else { .map itemconfigure $ReverseId($edge) -fill black } if { $oneway=="12"} { .map itemconfigure $ReverseId($edge) -arrow first } elseif { $oneway=="21"} { .map itemconfigure $ReverseId($edge) -arrow last } else { .map itemconfigure $ReverseId($edge) -arrow none } } # # roompoput. Pops up the room dialog box # proc roompopup {x y {room ""}} { global Node roomname type proc okbtn {room} { global .roomdialog Node roomname changed type set Node($room.Name) $roomname set Node($room.Type) $type set Node($room.Desc) [ .roomdialog.t.b.descript get 0.0 end ] setroomtype $room $type destroy .roomdialog .map itemconfigure $Node($room.textId) -text $roomname set changed 1 } proc cancelbtn {} { global .roomdialog destroy .roomdialog } if { [winfo exists .roomdialog]} { destroy .roomdialog } if {$room==""} { set room [inroom $x $y ] if { $room==0 } { return } } set roomname $Node($room.Name) set type $Node($room.Type) toplevel .roomdialog -class Dialog wm title .roomdialog "Room Information" frame .roomdialog.t -relief raised frame .roomdialog.t.t #frame .roomdialog.t.v frame .roomdialog.t.b frame .roomdialog.b -relief raised label .roomdialog.t.t.namelabel -text "Name: " entry .roomdialog.t.t.nameentry -width 20 -relief sunken \ -textvariable roomname label .roomdialog.t.t.typelabel -text "Type: " radiobutton .roomdialog.t.t.norm -text "Normal" -variable type -value "Normal" radiobutton .roomdialog.t.t.maze -text "Maze" -variable type -value "Maze" radiobutton .roomdialog.t.t.joins -text "Joins" -variable type -value "Joins" label .roomdialog.t.b.desclabel -text "Description: " text .roomdialog.t.b.descript -width 60 -height 15 button .roomdialog.b.ok -text "OK" -command "okbtn $room" button .roomdialog.b.cancel -text "Cancel" -command "cancelbtn" bind .roomdialog.t.t.nameentry ".roomdialog.b.ok flash;okbtn $room" bind .roomdialog ".roomdialog.b.cancel flash;cancelbtn" pack .roomdialog.t.t.namelabel .roomdialog.t.t.nameentry -anchor n pack .roomdialog.t.t.typelabel .roomdialog.t.t.norm .roomdialog.t.t.maze \ .roomdialog.t.t.joins pack .roomdialog.t.b.desclabel .roomdialog.t.b.descript pack .roomdialog.t.t .roomdialog.t.b -side left pack .roomdialog.b.ok .roomdialog.b.cancel -side left -padx 3m -pady 3m pack .roomdialog.t .roomdialog.b .roomdialog.t.b.descript insert 1.0 $Node($room.Desc) } # # Edgepopup. Pops up an edge roomdialog box # proc edgepopup {{x -1} {y -1}} { global Node Id Edge selected secret oneway proc okbtn {edge} { global Edge ReverseId secret changed oneway set Edge($edge.Secret) $secret set Edge($edge.Oneway) $oneway setedgetype $edge $secret $oneway set changed 1 destroy .edgedialog } proc cancelbtn {} { destroy .edgedialog } set edge $selected set secret $Edge($edge.Secret) set oneway $Edge($edge.Oneway) if { [winfo exists .edgedialog]} { destroy .edgedialog } toplevel .edgedialog -class Dialog wm title .edgedialog "Coridoor Information" frame .edgedialog.t -relief raised frame .edgedialog.b -relief raised pack .edgedialog.t .edgedialog.b checkbutton .edgedialog.t.secret -variable secret -text "Secret" radiobutton .edgedialog.t.twoway -text "Two Way" -variable oneway -value 0 radiobutton .edgedialog.t.oneway12 -text "One Way $Edge(${edge}.NodePos.1)-$Edge(${edge}.NodePos.2)" -variable oneway -value "12" radiobutton .edgedialog.t.oneway21 -text "One Way $Edge(${edge}.NodePos.2)-$Edge(${edge}.NodePos.1)" -variable oneway -value "21" pack .edgedialog.t.secret .edgedialog.t.twoway .edgedialog.t.oneway12 .edgedialog.t.oneway21 -anchor w button .edgedialog.b.ok -text "OK" -command "okbtn $edge" button .edgedialog.b.cancel -text "Cancel" -command "cancelbtn" pack .edgedialog.b.ok .edgedialog.b.cancel -side left -padx 3m -pady 3m bind .edgedialog ".edgedialog.b.ok flash;okbtn $edge" bind .edgedialog ".edgedialog.b.cancel flash;cancelbtn" } proc doedit {} { global selected if {[string first "Room" $selected]!=-1} { roompopup -1 -1 $selected } elseif {[string first "Edge" $selected]!=-1} { edgepopup } } # # deleteobj. Deletes an object (room or edge) # proc deleteobj { {object ""} } { # # deleteedge. deletes an edge. # proc deleteedge { selected } { global Node ReverseId Edge EdgeList Edgecount if { $selected !=0 } { .map delete $ReverseId($selected) set Node($Edge($selected.Node.1).joins.$Edge($selected.NodePos.1)) 0 set Node($Edge($selected.Node.1).joinsThru.$Edge($selected.NodePos.1)) 0 set Node($Edge($selected.Node.1).joinsTo.$Edge($selected.NodePos.1)) 0 set Node($Edge($selected.Node.2).joins.$Edge($selected.NodePos.2)) 0 set Node($Edge($selected.Node.2).joinsThru.$Edge($selected.NodePos.2)) 0 set Node($Edge($selected.Node.2).joinsTo.$Edge($selected.NodePos.2)) 0 unset Edge($selected.Node.1) unset Edge($selected.Node.2) unset Edge($selected.NodePos.1) unset Edge($selected.NodePos.2) unset EdgeList($selected) set Edgecount [expr $Edgecount-1] } } # # deleteroom. Deletes a room # proc deleteroom {selected} { global Node ReverseId directions Edge EdgeList Id Nodelist Nodecount if { $selected != 0 } { .map delete $selected foreach i $directions { if {$Node($selected.joinsThru.$i) != 0} { .map delete $ReverseId($Node($selected.joinsThru.$i)) set Node($Node($selected.joins.$i).joins.$Node($selected.joinsTo.$i)) 0 set Node($Node($selected.joins.$i).joinsThru.$Node($selected.joinsTo.$i)) 0 set Node($Node($selected.joins.$i).joinsTo.$Node($selected.joinsTo.$i)) 0 unset Edge($Node($selected.joinsThru.$i).Node.1) unset Edge($Node($selected.joinsThru.$i).Node.2) unset Edge($Node($selected.joinsThru.$i).NodePos.1) unset Edge($Node($selected.joinsThru.$i).NodePos.2) unset EdgeList($Node($selected.joinsThru.$i)) unset Id($ReverseId($Node($selected.joinsThru.$i))) unset ReverseId($Node($selected.joinsThru.$i)) } unset Node($selected.joins.$i) unset Node($selected.joinsThru.$i) unset Node($selected.joinsTo.$i) } unset Node($selected.Type) unset Node($selected.X) unset Node($selected.Y) unset Node($selected.Name) unset Node($selected.textId) unset Nodelist($selected) set Nodecount [expr $Nodecount-1] } } global selected changed if {$object==""} { set object $selected} if [string match Edge* $object] { deleteedge $object } if [string match Room* $object] { deleteroom $object } set selected 0 set changed 1 } ############################################################## # # Quit, save, etc commands # proc load {fn} { global Nodelist Edgelist Version Node Edge directions DungeonTitle global NextNode NextEdge changed Version if {[yousure]=="cancel"} { return } clear set node "" set nodedesc "" set edge "" set fd [open $fn "r"] while {![eof $fd]} { gets $fd line set i 0 foreach var [split $line " "] { set vars($i) $var set i [expr $i+1] } switch $vars(0) { "Version" { if { $vars(1) != $Version } { return } } "Dungeon" { set DungeonTitle [lindex [ split $line "\"" ] 1] } "\}" { if {$node!=""} { mkRoom $nodeX $nodeY $node $nodename $nodedesc $nodetype # $nodeJoins $nodeJoinsTo $nodeJoinsThru set node "" set nodedesc "" } if {$edge!=""} { mkEdge $edgeTo(1) $edgeExit(1) $edgeTo(2) $edgeExit(2) $edge $edgeSecret $edgeOneway set edge "" } } "NextNode" {set lnextnode $vars(1)} "NextEdge" {set lnextedge $vars(1)} "Name" { set nodename [lindex [ split $line "\"" ] 1] } "Desc" { set input [lindex [ split $line "\"" ] 1] if {$nodedesc!=""} { set nodedesc "$nodedesc\n$input" } else { set nodedesc $input } } "Oneway" {set edgeOneway $vars(1)} "Type" {set nodetype $vars(1)} "X" {set nodeX $vars(1)} "Y" {set nodeY $vars(1)} "joinsTo" {set nodeJoinsTo($vars(1)) $vars(2)} "joinsThru" {set nodeJoinsThru($vars(1)) $vars(2)} "joins" {set nodeJoins($vars(1)) $vars(2)} "ToNode" {set edgeTo($vars(1)) $vars(2)} "Exit" {set edgeExit($vars(1)) $vars(2)} "Secret" {set edgeSecret $vars(1)} "Edge" { set edge $vars(1) } "Node" { set node $vars(1) } } } # We put these at the end to ensure they are correct set NextNode $lnextnode set NextEdge $lnextedge set changed 0 } proc save {fn} { global Nodelist EdgeList Version Node Edge directions DungeonTitle Nodecount global Edgecount NextNode NextEdge set fd [open $fn "w"] puts $fd "IFMapper" puts $fd "Version $Version" puts $fd "Dungeon \"$DungeonTitle\"" puts $fd "Nodes\n\{" puts $fd "NumNodes $Nodecount" foreach i [array names Nodelist] { puts $fd "Node $i\n\{" puts $fd "Name \"$Node($i.Name)\"" set desc $Node($i.Desc) regsub -all "\n" $desc "\"\nDesc \"" desc puts $fd "Desc \"$desc\"" puts $fd "Type $Node($i.Type)" puts $fd "X $Node($i.X)" puts $fd "Y $Node($i.Y)" foreach j $directions { if {$Node($i.joins.$j) != 0 } { puts $fd "joins $j $Node($i.joins.$j)" puts $fd "joinsTo $j $Node($i.joinsTo.$j)" puts $fd "joinsThru $j $Node($i.joinsThru.$j)" } } puts $fd "\}" } puts $fd "NextNode $NextNode" puts $fd "\}\nEdges\n\{" puts $fd "NumEdges $Edgecount" foreach i [array names EdgeList] { puts $fd "Edge $i\n\{" foreach k {1 2} { puts $fd "ToNode $k $Edge($i.Node.$k)" puts $fd "Exit $k $Edge($i.NodePos.$k)" } puts $fd "Secret $Edge($i.Secret)" puts $fd "Oneway $Edge($i.Oneway)\n\}" } puts $fd "NextEdge $NextEdge" puts $fd "\}" close $fd } # # # # proc print {} { global printto printname filename proc okbtn {} { global printto printname filename printto printname global MapXMax MapYMax MapXMin MapYMin DungeonTitle filename destroy .printdialog if {$printto=="p"} {set filename "/tmp/map.ps"} .map itemconfigure cpoint -outline "" -fill "" .map itemconfigure conn -outline "" -fill "" .map create text [expr $MapXMin+($MapXMax-$MapXMin)/2] \ [expr $MapYMin-60] -tag title -text $DungeonTitle \ -font "-*-times-medium-r-normal-*-*-240-*-*-*-*-*-*" .map postscript -x [expr $MapXMin-60] -y [expr $MapYMin-120] \ -width [expr $MapXMax-$MapXMin+120] \ -height [expr $MapYMax-$MapYMin+180] \ -pagewidth "7.5i" -file $filename if {$printto=="p"} { exec lpr -P$printname $filename set filename "" } .map itemconfigure conn -outline "" -fill "lightgrey" .map itemconfigure cpoint -outline "black" -fill "black" .map delete title } proc cancelbtn {} { destroy .printdialog } set printto p toplevel .printdialog -class Dialog wm title .printdialog "Print" frame .printdialog.t -relief raised frame .printdialog.b -relief raised pack .printdialog.t .printdialog.b label .printdialog.t.title -text "Print To:" radiobutton .printdialog.t.prn -text "Printer: " -variable printto -value p entry .printdialog.t.printname -textvariable printname radiobutton .printdialog.t.fle -text "File: " -variable printto -value f entry .printdialog.t.filename -textvariable filename pack .printdialog.t.title .printdialog.t.prn .printdialog.t.printname .printdialog.t.fle .printdialog.t.filename button .printdialog.b.ok -text "OK" -command "okbtn" button .printdialog.b.cancel -text "Cancel" -command "cancelbtn" bind .printdialog ".printdialog.b.ok flash;okbtn" bind .printdialog ".printdialog.b.cancel flash;cancelbtn" pack .printdialog.b.ok .printdialog.b.cancel -side left -padx 3m } proc Help {} { } # # io: Handles io (The file box) # proc io {command} { if { $command == "load" } { set mesg "Select file to load" } else { set mesg "Select file to save" } FSBox "mesg" "" "$command \$fsBox(name)" } # # doquit: This handles quitting the program # proc doquit {} { if {[yousure]=="ok"} { exit } } proc doclear {} { if {[yousure]=="ok"} { clear } } proc clear {} { global Node Nodelist Edgelist Edge CPlist Room_id Edge_id Id ReverseId global Edgecount Nodecount selected movededge NextNode NextEdge .map changed .map delete all catch {unset Node unset Nodelist unset Edgelist unset Edge unset CPlist unset Room_id unset Edge_id unset Id unset ReverseId} set Edgecount 0 set Nodecount 0 set selected 0 set movededge 0 set NextNode 0 set NextEdge 0 set changed 0 } proc yousure {} { global retval changed if { [winfo exists .sure]} { return cancel } if {$changed!=0} { toplevel .sure -class Dialog wm iconname .sure Dialog wm title .sure "Are you sure?" frame .sure.t -relief raised -bd 1 label .sure.t.bitmap -bitmap questhead label .sure.t.text -text "Are you sure?" frame .sure.b -relief raised -bd 1 button .sure.b.ok -text "OK" -command "set retval ok" button .sure.b.cancel -text "Cancel" -command "set retval cancel" pack .sure.t.bitmap .sure.t.text -side left -padx 3m -expand 1 pack .sure.b.ok .sure.b.cancel -side left -padx 2m -expand 1 pack .sure.t .sure.b -expand 1 bind .sure ".sure.b.ok flash;set retval ok" bind .sure ".sure.b.cancel flash;set retval cancel" tkwait variable retval destroy .sure return $retval } else { return "ok" } } proc ShowHelp {text} { if { [winfo exists .help]} { return cancel } toplevel .help wm title .help "Help" frame .help.t text .help.t.text -wrap word -yscrollcommand ".help.t.scroll set" \ -font "-*-times-medium-r-normal-*-14-*-*-*-*-*-*-*" .help.t.text insert end $text scrollbar .help.t.scroll -command ".help.t.text yview" button .help.ok -text "OK" -command "destroy .help" pack .help.t.text .help.t.scroll -side left -fill y pack .help.t .help.ok } ##############################################################3 # # Misc Procedures # proc debug {outmsg} { global Debug if { $Debug==1} { puts $outmsg } } proc dump {} { global Version Xsize Ysize SQsize LoopSize DungeonTitle Nodecount Edgecount Selected Movededge debug "Version: $Version" debug "Xsize: $Xsize" debug "Ysize: $Ysize" debug "SQsize: $SQsize" debug "LoopSize: $LoopSize" debug "DungeonTitle: $DungeonTitle" debug "Nodecount: $Nodecount" debug "Edgecount: $Edgecount" debug "Selected: $selected" debug "Movededge: $movededge" } ##############################################################3 # # Bind the room creation # bind .map { set x [.map canvasx %x ] set y [.map canvasy %y ] if { [inobject $x $y]==0 } { mkRoom $x $y } } # # The Menu Bar # frame .mbar -relief raised -bd 2 menubutton .mbar.file -text File -underline 0 -menu .mbar.file.menu menubutton .mbar.edit -text Edit -underline 0 -menu .mbar.edit.menu menubutton .mbar.help -text Help -underline 0 -menu .mbar.help.menu pack .mbar.file .mbar.edit -side left pack .mbar.help -side right menu .mbar.file.menu .mbar.file.menu add command -label Load -command "io load" .mbar.file.menu add command -label Save -command "io save" .mbar.file.menu add command -label Print -command "print" .mbar.file.menu add separator .mbar.file.menu add command -label Clear -command doclear .mbar.file.menu add command -label Quit -command doquit menu .mbar.edit.menu .mbar.edit.menu add command -label Edit -command doedit .mbar.edit.menu add command -label Delete -command deleteobj menu .mbar.help.menu .mbar.help.menu add command -label Help -command "ShowHelp \$helptext" .mbar.help.menu add command -label "Release Notes" -command "ShowHelp \$releasenotes" tk_menuBar .mbar .mbar.file .mbar.edit .mbar.help # # # bind .map deleteobj bind .map deleteobj bind .map deleteobj bind .map deleteobj bind .map doedit bind .map doedit bind .map ShowHelp bind .map ShowHelp bind .map ShowHelp bind .map "io load" bind .map "io save" bind .map doquit # # The Scroll Bars # scrollbar .vertical -command ".map yview" scrollbar .horizontal -command ".map xview " -orient horizontal # # The Title # entry .title -textvariable DungeonTitle # Pack the widget pack .mbar -fill x pack .title -fill x pack .map -fill both -expand 1 pack .vertical -fill y -after .mbar -side right pack .horizontal -fill x -after .mbar -side bottom wm title . "Map editor version $Version" #---END--- ##############################################################3 ##############################################################3 ### Below isn't my code, its the file box ### The one I'm using was written by garfield@cs.tu-berlin.de, ### Availible from the tcl ftp sites. # XFNoParsing # Program: template # Description: file selector box # global fsBox set fsBox(activeBackground) "" set fsBox(activeForeground) "" set fsBox(background) "" set fsBox(font) "" set fsBox(foreground) "" set fsBox(scrollActiveForeground) "" set fsBox(scrollBackground) "" set fsBox(scrollForeground) "" set fsBox(scrollSide) left set fsBox(showPixmap) 0 set fsBox(name) "" set fsBox(path) [pwd] set fsBox(pattern) * set fsBox(all) 0 set fsBox(button) 0 set fsBox(extensions) 0 set fsBox(internalPath) [pwd] proc FSBox {{fsBoxMessage "Select file:"} {fsBoxFileName ""} {fsBoxActionOk ""} {fsBoxActionCancel ""}} {# xf ignore me 5 ########## # Procedure: FSBox # Description: show file selector box # Arguments: fsBoxMessage - the text to display # fsBoxFileName - a file name that should be selected # fsBoxActionOk - the action that should be performed on ok # fsBoxActionCancel - the action that should be performed on cancel # Returns: the filename that was selected, or nothing # Sideeffects: none ########## # # global fsBox(activeBackground) - active background color # global fsBox(activeForeground) - active foreground color # global fsBox(background) - background color # global fsBox(font) - text font # global fsBox(foreground) - foreground color # global fsBox(extensions) - scan directory for extensions # global fsBox(scrollActiveForeground) - scrollbar active background color # global fsBox(scrollBackground) - scrollbar background color # global fsBox(scrollForeground) - scrollbar foreground color # global fsBox(scrollSide) - side where scrollbar is located global fsBox set tmpButtonOpt "" set tmpFrameOpt "" set tmpMessageOpt "" set tmpScaleOpt "" set tmpScrollOpt "" if {"$fsBox(activeBackground)" != ""} { append tmpButtonOpt "-activebackground \"$fsBox(activeBackground)\" " } if {"$fsBox(activeForeground)" != ""} { append tmpButtonOpt "-activeforeground \"$fsBox(activeForeground)\" " } if {"$fsBox(background)" != ""} { append tmpButtonOpt "-background \"$fsBox(background)\" " append tmpFrameOpt "-background \"$fsBox(background)\" " append tmpMessageOpt "-background \"$fsBox(background)\" " } if {"$fsBox(font)" != ""} { append tmpButtonOpt "-font \"$fsBox(font)\" " append tmpMessageOpt "-font \"$fsBox(font)\" " } if {"$fsBox(foreground)" != ""} { append tmpButtonOpt "-foreground \"$fsBox(foreground)\" " append tmpMessageOpt "-foreground \"$fsBox(foreground)\" " } if {"$fsBox(scrollActiveForeground)" != ""} { append tmpScrollOpt "-activeforeground \"$fsBox(scrollActiveForeground)\" " } if {"$fsBox(scrollBackground)" != ""} { append tmpScrollOpt "-background \"$fsBox(scrollBackground)\" " } if {"$fsBox(scrollForeground)" != ""} { append tmpScrollOpt "-foreground \"$fsBox(scrollForeground)\" " } if {[file exists [file tail $fsBoxFileName]] && [IsAFile [file tail $fsBoxFileName]]} { set fsBox(name) [file tail $fsBoxFileName] } { set fsBox(name) "" } if {[file exists $fsBoxFileName] && [IsADir $fsBoxFileName]} { set fsBox(path) $fsBoxFileName } { if {"[file rootname $fsBoxFileName]" != "."} { set fsBox(path) [file rootname $fsBoxFileName] } } if {$fsBox(showPixmap)} { set fsBox(path) [string trimleft $fsBox(path) @] } if {"$fsBox(path)" != "" && [file exists $fsBox(path)] && [IsADir $fsBox(path)]} { set fsBox(internalPath) $fsBox(path) } { if {"$fsBox(internalPath)" == "" || ![file exists $fsBox(internalPath)]} { set fsBox(internalPath) [pwd] } } # build widget structure # start build of toplevel if {"[info commands XFDestroy]" != ""} { catch {XFDestroy .fsBox} } { catch {destroy .fsBox} } toplevel .fsBox \ -borderwidth 0 catch ".fsBox config $tmpFrameOpt" wm geometry .fsBox 350x300 wm title .fsBox {File select box} wm maxsize .fsBox 1000 1000 wm minsize .fsBox 100 100 # end build of toplevel label .fsBox.message1 \ -anchor c \ -relief raised \ -text "$fsBoxMessage" catch ".fsBox.message1 config $tmpMessageOpt" frame .fsBox.frame1 \ -borderwidth 0 \ -relief raised catch ".fsBox.frame1 config $tmpFrameOpt" button .fsBox.frame1.ok \ -text "OK" \ -command " global fsBox set fsBox(name) \[.fsBox.file.file get\] if {$fsBox(showPixmap)} { set fsBox(path) @\[.fsBox.path.path get\] } { set fsBox(path) \[.fsBox.path.path get\] } set fsBox(internalPath) \[.fsBox.path.path get\] $fsBoxActionOk if {\"\[info commands XFDestroy\]\" != \"\"} { catch {XFDestroy .fsBox} } { catch {destroy .fsBox} }" catch ".fsBox.frame1.ok config $tmpButtonOpt" button .fsBox.frame1.rescan \ -text "Rescan" \ -command { global fsBox FSBoxFSShow [.fsBox.path.path get] \ [.fsBox.pattern.pattern get] $fsBox(all)} catch ".fsBox.frame1.rescan config $tmpButtonOpt" button .fsBox.frame1.cancel \ -text "Cancel" \ -command " global fsBox set fsBox(name) {} set fsBox(path) {} $fsBoxActionCancel if {\"\[info commands XFDestroy\]\" != \"\"} { catch {XFDestroy .fsBox} } { catch {destroy .fsBox} }" catch ".fsBox.frame1.cancel config $tmpButtonOpt" if {$fsBox(showPixmap)} { frame .fsBox.frame2 \ -borderwidth 0 \ -relief raised catch ".fsBox.frame2 config $tmpFrameOpt" scrollbar .fsBox.frame2.scrollbar3 \ -command {.fsBox.frame2.canvas2 xview} \ -orient {horizontal} \ -relief {raised} catch ".fsBox.frame2.scrollbar3 config $tmpScrollOpt" scrollbar .fsBox.frame2.scrollbar1 \ -command {.fsBox.frame2.canvas2 yview} \ -relief {raised} catch ".fsBox.frame2.scrollbar1 config $tmpScrollOpt" canvas .fsBox.frame2.canvas2 \ -confine {true} \ -relief {raised} \ -scrollregion {0c 0c 20c 20c} \ -width {100} \ -xscrollcommand {.fsBox.frame2.scrollbar3 set} \ -yscrollcommand {.fsBox.frame2.scrollbar1 set} catch ".fsBox.frame2.canvas2 config $tmpFrameOpt" .fsBox.frame2.canvas2 addtag currentBitmap withtag [.fsBox.frame2.canvas2 create bitmap 5 5 -anchor nw] } frame .fsBox.path \ -borderwidth 0 \ -relief raised catch ".fsBox.path config $tmpFrameOpt" frame .fsBox.path.paths \ -borderwidth 2 \ -relief raised catch ".fsBox.path.paths config $tmpFrameOpt" menubutton .fsBox.path.paths.paths \ -borderwidth 0 \ -menu ".fsBox.path.paths.paths.menu" \ -relief flat \ -text "Pathname:" catch ".fsBox.path.paths.paths config $tmpButtonOpt" menu .fsBox.path.paths.paths.menu catch ".fsBox.path.paths.paths.menu config $tmpButtonOpt" .fsBox.path.paths.paths.menu add command \ -label "[string trimright $fsBox(internalPath) {/@}]" \ -command " global fsBox FSBoxFSShow \[.fsBox.path.path get\] \ \[.fsBox.pattern.pattern get\] \$fsBox(all) .fsBox.path.path delete 0 end .fsBox.path.path insert 0 [string trimright $fsBox(internalPath) {/@}]" entry .fsBox.path.path \ -relief raised catch ".fsBox.path.path config $tmpMessageOpt" if {![IsADir $fsBox(internalPath)]} { set $fsBox(internalPath) [pwd] } .fsBox.path.path insert 0 $fsBox(internalPath) frame .fsBox.pattern \ -borderwidth 0 \ -relief raised catch ".fsBox.pattern config $tmpFrameOpt" frame .fsBox.pattern.patterns \ -borderwidth 2 \ -relief raised catch ".fsBox.pattern.patterns config $tmpFrameOpt" menubutton .fsBox.pattern.patterns.patterns \ -borderwidth 0 \ -menu ".fsBox.pattern.patterns.patterns.menu" \ -relief flat \ -text "Selection pattern:" catch ".fsBox.pattern.patterns.patterns config $tmpButtonOpt" menu .fsBox.pattern.patterns.patterns.menu catch ".fsBox.pattern.patterns.patterns.menu config $tmpButtonOpt" .fsBox.pattern.patterns.patterns.menu add checkbutton \ -label "Scan extensions" \ -variable fsBoxExtensions \ -command { global fsBox FSBoxFSShow [.fsBox.path.path get] \ [.fsBox.pattern.pattern get] $fsBox(all)} entry .fsBox.pattern.pattern \ -relief raised catch ".fsBox.pattern.pattern config $tmpMessageOpt" .fsBox.pattern.pattern insert 0 $fsBox(pattern) frame .fsBox.files \ -borderwidth 0 \ -relief raised catch ".fsBox.files config $tmpFrameOpt" scrollbar .fsBox.files.vscroll \ -relief raised \ -command ".fsBox.files.files yview" catch ".fsBox.files.vscroll config $tmpScrollOpt" scrollbar .fsBox.files.hscroll \ -orient horiz \ -relief raised \ -command ".fsBox.files.files xview" catch ".fsBox.files.hscroll config $tmpScrollOpt" listbox .fsBox.files.files \ -exportselection false \ -relief raised \ -xscrollcommand ".fsBox.files.hscroll set" \ -yscrollcommand ".fsBox.files.vscroll set" catch ".fsBox.files.files config $tmpMessageOpt" frame .fsBox.file \ -borderwidth 0 \ -relief raised catch ".fsBox.file config $tmpFrameOpt" label .fsBox.file.labelfile \ -relief raised \ -text "Filename:" catch ".fsBox.file.labelfile config $tmpMessageOpt" entry .fsBox.file.file \ -relief raised catch ".fsBox.file.file config $tmpMessageOpt" .fsBox.file.file delete 0 end .fsBox.file.file insert 0 $fsBox(name) checkbutton .fsBox.pattern.all \ -offvalue 0 \ -onvalue 1 \ -text "Show all files" \ -variable fsBox(all) \ -command { global fsBox FSBoxFSShow [.fsBox.path.path get] \ [.fsBox.pattern.pattern get] $fsBox(all)} catch ".fsBox.pattern.all config $tmpButtonOpt" FSBoxFSShow $fsBox(internalPath) $fsBox(pattern) $fsBox(all) # bindings bind .fsBox.files.files " FSBoxFSFileSelectDouble %W $fsBox(showPixmap) \{$fsBoxActionOk\} %y" bind .fsBox.files.files " FSBoxFSFileSelect %W $fsBox(showPixmap) %y" bind .fsBox.files.files " FSBoxFSFileSelect %W $fsBox(showPixmap) %y" bind .fsBox.files.files " FSBoxFSFileSelect %W $fsBox(showPixmap) %y" bind .fsBox.files.files " FSBoxFSFileSelect %W $fsBox(showPixmap) %y" bind .fsBox.path.path { FSBoxFSNameComplete path} bind .fsBox.path.path { global tkVersion global fsBox FSBoxFSShow [.fsBox.path.path get] \ [.fsBox.pattern.pattern get] $fsBox(all) FSBoxFSInsertPath if {$tkVersion >= 3.0} { .fsBox.file.file icursor end } { .fsBox.file.file cursor end } focus .fsBox.file.file} catch "bind .fsBox.path.path {}" bind .fsBox.path.path { global tkVersion if {$tkVersion >= 3.0} { .fsBox.file.file icursor end } { .fsBox.file.file cursor end } focus .fsBox.file.file} bind .fsBox.file.file { FSBoxFSNameComplete file} bind .fsBox.file.file " global fsBox set fsBox(name) \[.fsBox.file.file get\] if {$fsBox(showPixmap)} { set fsBox(path) @\[.fsBox.path.path get\] } { set fsBox(path) \[.fsBox.path.path get\] } set fsBox(internalPath) \[.fsBox.path.path get\] $fsBoxActionOk if {\"\[info commands XFDestroy\]\" != \"\"} { catch {XFDestroy .fsBox} } { catch {destroy .fsBox} }" bind .fsBox.file.file { global tkVersion if {$tkVersion >= 3.0} { .fsBox.path.path icursor end } { .fsBox.path.path cursor end } focus .fsBox.path.path} bind .fsBox.file.file { global tkVersion if {$tkVersion >= 3.0} { .fsBox.pattern.pattern icursor end } { .fsBox.pattern.pattern cursor end } focus .fsBox.pattern.pattern} bind .fsBox.pattern.pattern { global fsBox FSBoxFSShow [.fsBox.path.path get] \ [.fsBox.pattern.pattern get] $fsBox(all)} bind .fsBox.pattern.pattern { global tkVersion if {$tkVersion >= 3.0} { .fsBox.file.file icursor end } { .fsBox.file.file cursor end } focus .fsBox.file.file} catch "bind .fsBox.pattern.pattern {}" # packing pack append .fsBox.files \ .fsBox.files.vscroll "$fsBox(scrollSide) filly" \ .fsBox.files.hscroll {bottom fillx} \ .fsBox.files.files {left fill expand} pack append .fsBox.file \ .fsBox.file.labelfile {left} \ .fsBox.file.file {left fill expand} pack append .fsBox.frame1 \ .fsBox.frame1.ok {left fill expand} \ .fsBox.frame1.rescan {left fill expand} \ .fsBox.frame1.cancel {left fill expand} pack append .fsBox.path.paths \ .fsBox.path.paths.paths {left} pack append .fsBox.pattern.patterns \ .fsBox.pattern.patterns.patterns {left} pack append .fsBox.path \ .fsBox.path.paths {left} \ .fsBox.path.path {left fill expand} pack append .fsBox.pattern \ .fsBox.pattern.patterns {left} \ .fsBox.pattern.all {right fill} \ .fsBox.pattern.pattern {left fill expand} if {$fsBox(showPixmap)} { pack append .fsBox.frame2 \ .fsBox.frame2.scrollbar1 {left filly} \ .fsBox.frame2.canvas2 {top expand fill} \ .fsBox.frame2.scrollbar3 {top fillx} pack append .fsBox \ .fsBox.message1 {top fill} \ .fsBox.frame1 {bottom fill} \ .fsBox.pattern {bottom fill} \ .fsBox.file {bottom fill} \ .fsBox.path {bottom fill} \ .fsBox.frame2 {right fill} \ .fsBox.files {left fill expand} } { pack append .fsBox \ .fsBox.message1 {top fill} \ .fsBox.frame1 {bottom fill} \ .fsBox.pattern {bottom fill} \ .fsBox.file {bottom fill} \ .fsBox.path {bottom fill} \ .fsBox.files {left fill expand} } if {"$fsBoxActionOk" == "" && "$fsBoxActionCancel" == ""} { # wait for the box to be destroyed update idletask grab .fsBox tkwait window .fsBox if {"[string trim $fsBox(path)]" != "" || "[string trim $fsBox(name)]" != ""} { if {"[string trimleft [string trim $fsBox(name)] /]" == ""} { return [string trimright [string trim $fsBox(path)] /] } { return [string trimright [string trim $fsBox(path)] /]/[string trimleft [string trim $fsBox(name)] /] } } } } ########## # Procedure: FSBoxFSFileSelect # Description: select file name # Arguments: fsBoxW - the widget # fsBoxShowPixmap - show pixmaps # fsBoxY - the y position in the listbox # Returns: none # Sideeffects: none ########## proc FSBoxFSFileSelect {fsBoxW fsBoxShowPixmap fsBoxY} {# xf ignore me 6 global fsBox FSBoxBindSelectOne $fsBoxW $fsBoxY set fsBoxNearest [$fsBoxW nearest $fsBoxY] if {$fsBoxNearest >= 0} { set fsBoxTmpEntry [$fsBoxW get $fsBoxNearest] if {"[string index $fsBoxTmpEntry \ [expr [string length $fsBoxTmpEntry]-1]]" == "/" || "[string index $fsBoxTmpEntry \ [expr [string length $fsBoxTmpEntry]-1]]" == "@"} { set fsBoxFileName [string range $fsBoxTmpEntry 0 \ [expr [string length $fsBoxTmpEntry]-2]] if {![IsADir [string trimright $fsBox(internalPath)/$fsBoxFileName @]] && ![IsASymlink [string trimright $fsBox(internalPath)/$fsBoxFileName @]]} { set fsBoxFileName $fsBoxTmpEntry } } { if {"[string index $fsBoxTmpEntry \ [expr [string length $fsBoxTmpEntry]-1]]" == "*"} { set fsBoxFileName [string range $fsBoxTmpEntry 0 \ [expr [string length $fsBoxTmpEntry]-2]] if {![file executable $fsBox(internalPath)/$fsBoxFileName]} { set fsBoxFileName $fsBoxTmpEntry } } { set fsBoxFileName $fsBoxTmpEntry } } if {![IsADir [string trimright $fsBox(internalPath)/$fsBoxFileName @]]} { set fsBox(name) $fsBoxFileName .fsBox.file.file delete 0 end .fsBox.file.file insert 0 $fsBox(name) if {$fsBoxShowPixmap} { catch ".fsBox.frame2.canvas2 itemconfigure currentBitmap -bitmap \"@$fsBox(internalPath)/$fsBox(name)\"" } } } } ########## # Procedure: FSBoxFSFileSelectDouble # Description: select file when double clicked # Arguments: fsBoxW - the widget # fsBoxShowPixmap - show pixmaps # fsBoxAction - the action bound to the ok button # fsBoxY - the y position in the listbox # Returns: none # Sideeffects: none ########## proc FSBoxFSFileSelectDouble {fsBoxW fsBoxShowPixmap fsBoxAction fsBoxY} {# xf ignore me 6 global fsBox FSBoxBindSelectOne $fsBoxW $fsBoxY set fsBoxNearest [$fsBoxW nearest $fsBoxY] if {$fsBoxNearest >= 0} { set fsBoxTmpEntry [$fsBoxW get $fsBoxNearest] if {"$fsBoxTmpEntry" == "../"} { set fsBoxTmpEntry [string trimright [string trim $fsBox(internalPath)] "@/"] if {"$fsBoxTmpEntry" == ""} { return } FSBoxFSShow [file dirname $fsBoxTmpEntry] \ [.fsBox.pattern.pattern get] $fsBox(all) .fsBox.path.path delete 0 end .fsBox.path.path insert 0 $fsBox(internalPath) } { if {"[string index $fsBoxTmpEntry \ [expr [string length $fsBoxTmpEntry]-1]]" == "/" || "[string index $fsBoxTmpEntry \ [expr [string length $fsBoxTmpEntry]-1]]" == "@"} { set fsBoxFileName [string range $fsBoxTmpEntry 0 \ [expr [string length $fsBoxTmpEntry]-2]] if {![IsADir [string trimright $fsBox(internalPath)/$fsBoxFileName @]] && ![IsASymlink [string trimright $fsBox(internalPath)/$fsBoxFileName @]]} { set fsBoxFileName $fsBoxTmpEntry } } { if {"[string index $fsBoxTmpEntry \ [expr [string length $fsBoxTmpEntry]-1]]" == "*"} { set fsBoxFileName [string range $fsBoxTmpEntry 0 \ [expr [string length $fsBoxTmpEntry]-2]] if {![file executable $fsBox(internalPath)/$fsBoxFileName]} { set fsBoxFileName $fsBoxTmpEntry } } { set fsBoxFileName $fsBoxTmpEntry } } if {[IsADir [string trimright $fsBox(internalPath)/$fsBoxFileName @]]} { set fsBox(internalPath) "[string trimright $fsBox(internalPath) {/@}]/$fsBoxFileName" FSBoxFSShow $fsBox(internalPath) \ [.fsBox.pattern.pattern get] $fsBox(all) .fsBox.path.path delete 0 end .fsBox.path.path insert 0 $fsBox(internalPath) } { set fsBox(name) $fsBoxFileName if {$fsBoxShowPixmap} { set fsBox(path) @$fsBox(internalPath) } { set fsBox(path) $fsBox(internalPath) } if {"$fsBoxAction" != ""} { eval "global fsBox; $fsBoxAction" } if {"[info commands XFDestroy]" != ""} { catch {XFDestroy .fsBox} } { catch {destroy .fsBox} } } } } } ########## # Procedure: FSBoxFSInsertPath # Description: insert current pathname into menu # Arguments: none # Returns: none # Sideeffects: none ########## proc FSBoxFSInsertPath {} {# xf ignore me 6 global fsBox set fsBoxLast [.fsBox.path.paths.paths.menu index last] set fsBoxNewEntry [string trimright [.fsBox.path.path get] "/@"] for {set fsBoxCounter 0} {$fsBoxCounter <= $fsBoxLast} {incr fsBoxCounter 1} { if {"$fsBoxNewEntry" == \ "[lindex [.fsBox.path.paths.paths.menu entryconfigure \ $fsBoxCounter -label] 4]"} { return } } if {$fsBoxLast < 9} { .fsBox.path.paths.paths.menu add command \ -label "$fsBoxNewEntry" \ -command " global fsBox FSBoxFSShow $fsBoxNewEntry \ \[.fsBox.pattern.pattern get\] \$fsBox(all) .fsBox.path.path delete 0 end .fsBox.path.path insert 0 $fsBoxNewEntry" } { for {set fsBoxCounter 0} {$fsBoxCounter < $fsBoxLast} {incr fsBoxCounter 1} { .fsBox.path.paths.paths.menu entryconfigure \ $fsBoxCounter -label \ [lindex [.fsBox.path.paths.paths.menu entryconfigure \ [expr $fsBoxCounter+1] -label] 4] .fsBox.path.paths.paths.menu entryconfigure $fsBoxCounter \ -command " global fsBox FSBoxFSShow [lindex [.fsBox.path.paths.paths.menu entryconfigure \ [expr $fsBoxCounter+1] -label] 4] \ \[.fsBox.pattern.pattern get\] \$fsBox(all) .fsBox.path.path delete 0 end .fsBox.path.path insert 0 [lindex \ [.fsBox.path.paths.paths.menu entryconfigure \ [expr $fsBoxCounter+1] -label] 4]" } .fsBox.path.paths.paths.menu entryconfigure $fsBoxLast \ -label "$fsBoxNewEntry" .fsBox.path.paths.paths.menu entryconfigure $fsBoxCounter \ -command " global fsBox FSBoxFSShow \[.fsBox.path.path get\] \ \[.fsBox.pattern.pattern get\] \$fsBox(all) .fsBox.path.path delete 0 end .fsBox.path.path insert 0 $fsBoxNewEntry" } } ########## # Procedure: FSBoxFSNameComplete # Description: perform name completion for fs box # Arguments: fsBoxType - the type we want to complete (path or file) # Returns: none # Sideeffects: none ########## proc FSBoxFSNameComplete {fsBoxType} {# xf ignore me 6 global tkVersion global fsBox set fsBoxNewFile "" if {"$fsBoxType" == "path"} { set fsBoxDirName [file dirname [.fsBox.path.path get]] set fsBoxFileName [file tail [.fsBox.path.path get]] } { set fsBoxDirName [file dirname [.fsBox.path.path get]/] set fsBoxFileName [file tail [.fsBox.file.file get]] } set fsBoxNewFile "" if {[IsADir [string trimright $fsBoxDirName @]]} { catch "glob -nocomplain $fsBoxDirName/${fsBoxFileName}*" fsBoxResult foreach fsBoxCounter $fsBoxResult { if {"$fsBoxNewFile" == ""} { set fsBoxNewFile [file tail $fsBoxCounter] } { if {"[string index [file tail $fsBoxCounter] 0]" != "[string index $fsBoxNewFile 0]"} { set fsBoxNewFile "" break } set fsBoxCounter1 0 set fsBoxTmpFile1 $fsBoxNewFile set fsBoxTmpFile2 [file tail $fsBoxCounter] set fsBoxLength1 [string length $fsBoxTmpFile1] set fsBoxLength2 [string length $fsBoxTmpFile2] set fsBoxNewFile "" if {$fsBoxLength1 > $fsBoxLength2} { set fsBoxLength1 $fsBoxLength2 } while {$fsBoxCounter1 < $fsBoxLength1} { if {"[string index $fsBoxTmpFile1 $fsBoxCounter1]" == \ "[string index $fsBoxTmpFile2 $fsBoxCounter1]"} { append fsBoxNewFile [string index $fsBoxTmpFile1 $fsBoxCounter1] } { break } incr fsBoxCounter1 1 } } } } if {"$fsBoxNewFile" != ""} { if {[IsADir [string trimright $fsBoxDirName/$fsBoxNewFile @]] || ![IsAFile [string trimright $fsBoxDirName/$fsBoxNewFile @]]} { if {[IsADir [string trimright $fsBoxDirName/$fsBoxNewFile @]]} { if {"$fsBoxDirName" == "/"} { .fsBox.path.path delete 0 end .fsBox.path.path insert 0 "/[string trimright [string trim $fsBoxNewFile /] @]/" } { .fsBox.path.path delete 0 end .fsBox.path.path insert 0 "[string trimright $fsBoxDirName /]/[string trimright [string trim $fsBoxNewFile /] @]/" } FSBoxFSShow [.fsBox.path.path get] \ [.fsBox.pattern.pattern get] $fsBox(all) FSBoxFSInsertPath } { .fsBox.path.path delete 0 end .fsBox.path.path insert 0 "[string trimright $fsBoxDirName /]/[string trimright [string trim $fsBoxNewFile /] @]" } } { .fsBox.path.path delete 0 end .fsBox.path.path insert 0 "[string trimright $fsBoxDirName {@/}]/" .fsBox.file.file delete 0 end .fsBox.file.file insert 0 $fsBoxNewFile if {$tkVersion >= 3.0} { .fsBox.file.file icursor end } { .fsBox.file.file cursor end } focus .fsBox.file.file } } } ########## # Procedure: FSBoxFSShow # Description: show the file list # Arguments: fsBoxPath - the path to show # fsBoxPattern - selection pattern # fsBoxAll - show all files # Returns: none # Sideeffects: none ########## proc FSBoxFSShow {fsBoxPath fsBoxPattern fsBoxAll} {# xf ignore me 6 global fsBox set tmpButtonOpt "" if {"$fsBox(activeBackground)" != ""} { append tmpButtonOpt "-activebackground \"$fsBox(activeBackground)\" " } if {"$fsBox(activeForeground)" != ""} { append tmpButtonOpt "-activeforeground \"$fsBox(activeForeground)\" " } if {"$fsBox(background)" != ""} { append tmpButtonOpt "-background \"$fsBox(background)\" " } if {"$fsBox(font)" != ""} { append tmpButtonOpt "-font \"$fsBox(font)\" " } if {"$fsBox(foreground)" != ""} { append tmpButtonOpt "-foreground \"$fsBox(foreground)\" " } set fsBox(pattern) $fsBoxPattern if {[file exists $fsBoxPath] && [file readable $fsBoxPath] && [IsADir $fsBoxPath]} { set fsBox(internalPath) $fsBoxPath } { if {[file exists $fsBoxPath] && [file readable $fsBoxPath] && [IsAFile $fsBoxPath]} { set fsBox(internalPath) [file dirname $fsBoxPath] .fsBox.file.file delete 0 end .fsBox.file.file insert 0 [file tail $fsBoxPath] set fsBoxPath $fsBox(internalPath) } { while {"$fsBoxPath" != "" && "$fsBoxPath" != "/" && ![file isdirectory $fsBoxPath]} { set fsBox(internalPath) [file dirname $fsBoxPath] set fsBoxPath $fsBox(internalPath) } } } if {"$fsBoxPath" == ""} { set fsBoxPath "/" set fsBox(internalPath) "/" } .fsBox.path.path delete 0 end .fsBox.path.path insert 0 $fsBox(internalPath) if {[.fsBox.files.files size] > 0} { .fsBox.files.files delete 0 end } if {$fsBoxAll} { if {[catch "exec ls -F -a $fsBoxPath" fsBoxResult]} { puts stderr "$fsBoxResult" } } { if {[catch "exec ls -F $fsBoxPath" fsBoxResult]} { puts stderr "$fsBoxResult" } } set fsBoxElementList [lsort $fsBoxResult] foreach fsBoxCounter [winfo children .fsBox.pattern.patterns.patterns] { if {[string length [info commands XFDestroy]] > 0} { catch {XFDestroy $fsBoxCounter} } { catch {destroy $fsBoxCounter} } } menu .fsBox.pattern.patterns.patterns.menu catch ".fsBox.pattern.patterns.patterns.menu config $tmpButtonOpt" if {$fsBox(extensions)} { .fsBox.pattern.patterns.patterns.menu add command \ -label "*" \ -command { global fsBox set fsBox(pattern) "*" .fsBox.pattern.pattern delete 0 end .fsBox.pattern.pattern insert 0 $fsBox(pattern) FSBoxFSShow [.fsBox.path.path get] $fsBox(pattern) \ $fsBox(all)} } if {"$fsBoxPath" != "/"} { .fsBox.files.files insert end "../" } foreach fsBoxCounter $fsBoxElementList { if {[string match $fsBoxPattern $fsBoxCounter] || [IsADir [string trimright $fsBoxPath/$fsBoxCounter "/@"]]} { if {"$fsBoxCounter" != "../" && "$fsBoxCounter" != "./"} { .fsBox.files.files insert end $fsBoxCounter } } if {$fsBox(extensions)} { catch "file rootname $fsBoxCounter" fsBoxRootName catch "file extension $fsBoxCounter" fsBoxExtension set fsBoxExtension [string trimright $fsBoxExtension "/*@"] if {"$fsBoxExtension" != "" && "$fsBoxRootName" != ""} { set fsBoxInsert 1 set fsBoxLast [.fsBox.pattern.patterns.patterns.menu index last] for {set fsBoxCounter1 0} {$fsBoxCounter1 <= $fsBoxLast} {incr fsBoxCounter1 1} { if {"*$fsBoxExtension" == \ "[lindex [.fsBox.pattern.patterns.patterns.menu entryconfigure \ $fsBoxCounter1 -label] 4]"} { set fsBoxInsert 0 } } if {$fsBoxInsert} { .fsBox.pattern.patterns.patterns.menu add command \ -label "*$fsBoxExtension" \ -command " global fsBox set fsBox(pattern) \"*$fsBoxExtension\" .fsBox.pattern.pattern delete 0 end .fsBox.pattern.pattern insert 0 \$fsBox(pattern) FSBoxFSShow \[.fsBox.path.path get\] \$fsBox(pattern) \ \$fsBox(all)" } } } } if {$fsBox(extensions)} { .fsBox.pattern.patterns.patterns.menu add separator } if {$fsBox(extensions) || "[.fsBox.pattern.patterns.patterns.menu index last]" == "none"} { .fsBox.pattern.patterns.patterns.menu add checkbutton \ -label "Scan extensions" \ -variable "fsBox(extensions)" \ -command { global fsBox FSBoxFSShow [.fsBox.path.path get] \ [.fsBox.pattern.pattern get] $fsBox(all)} } } ########## # Procedure: FSBoxBindSelectOne # Description: action to select the current list item # Arguments: fsBoxW - the widget # fsBoxY - the y position in the listbox # Returns: none # Sideeffects: none ########## proc FSBoxBindSelectOne {fsBoxW fsBoxY} {# xf ignore me 6 set fsBoxNearest [$fsBoxW nearest $fsBoxY] if {$fsBoxNearest >= 0} { $fsBoxW select set $fsBoxNearest $fsBoxW select set $fsBoxNearest } } proc IsADir {pathName} {# xf ignore me 5 ########## # Procedure: IsADir # Description: check if name is a directory (including symbolic links) # Arguments: pathName - the path to check # Returns: 1 if its a directory, otherwise 0 # Sideeffects: none ########## if {[file isdirectory $pathName]} { return 1 } { catch "file type $pathName" fileType if {"$fileType" == "link"} { if {[catch "file readlink $pathName" linkName]} { return 0 } catch "file type $linkName" fileType while {"$fileType" == "link"} { if {[catch "file readlink $linkName" linkName]} { return 0 } catch "file type $linkName" fileType } return [file isdirectory $linkName] } } return 0 } proc IsAFile {fileName} {# xf ignore me 5 ########## # Procedure: IsAFile # Description: check if filename is a file (including symbolic links) # Arguments: fileName - the filename to check # Returns: 1 if its a file, otherwise 0 # Sideeffects: none ########## if {[file isfile $fileName]} { return 1 } { catch "file type $fileName" fileType if {"$fileType" == "link"} { if {[catch "file readlink $fileName" linkName]} { return 0 } catch "file type $linkName" fileType while {"$fileType" == "link"} { if {[catch "file readlink $linkName" linkName]} { return 0 } catch "file type $linkName" fileType } return [file isfile $linkName] } } return 0 } proc IsASymlink {fileName} {# xf ignore me 5 ########## # Procedure: IsASymlink # Description: check if filename is a symbolic link # Arguments: fileName - the path/filename to check # Returns: none # Sideeffects: none ########## catch "file type $fileName" fileType if {"$fileType" == "link"} { return 1 } return 0 } # eof #------ # Help Text set helptext \ "Welcome to the IF Map Editor. This space under construction. The IF Map Editor is a tool to allow you to easily draw nice looking maps for IF games (text adventures). It creates maps conforming more or less to the standard used by Infocom in their maps. NOTE: This is Alpha software. It is still under development. There are certainly bugs. Please read the release notes. Also please send any feedback, bugreports or whatever to crosby@cs.colorado.edu For better help, please go to http://ugrad-www.cs.colorado.edu/~crosby/ifmap/Help.html How to use the map editor: - To create a room, click the mouse button in the location you desire it A room will be created with a box in the location of each of the compass points, plus one between NW and N for up and one between S and SE for down. - To move a room, select it with the mouse button and drag it to the new location - To create a passage between two rooms, select the location on the first room you desire, drag to the location on the second room and release the button. - To move a passage, select the end you wish to move and drag it to its new location - To create a passage looping back upon itself, double click upon the location you desire it. - To edit the parameters of a room or passage, double click on the room or passage. Parameters For Rooms: Name-The name of the room Description-Any notes on the room you wish to make, for instance objects found there Type-The type of room. May be normal (a normal room), joins (creates an annotation without a box) or maze (a greyed out box, indicating several rooms). Parameters For Passages: Secret-Indicates a secret passage. Greys out the passage. One way-A one way passage. Puts an arrow indicating the direction of the passag - To delete an item, select the item and select 'delete' from the edit menu. - To change the title of your map, select the box underneath the menu bar and type in the title. - To save your map, select 'Save' from the File menu, type in the name and select OK. - To load a map, select 'Load' from the File menu, type in the name and select ok. (Note that loading may take some time if the map is big). - To print, select 'Print' from the File menu. A dialog box will appear allowing you to print to a file or printer. You may choose between printing to a file or directly to a printer. In either case, you should select the destination in the appropriate box below each button: The printer name, or the file name. This option outputs Encapsulated postscript scaled to fit onto one page. This means that if you have a very large map, you may find it hard to read. There exist, however, various programs that will blow it up and print it over multiple pages. Also, it may be imported into other programs. The printed output is slightly different to the screen display, eliminating the direction buttons." set releasenotes \ " IF Map Editor v0.1 Copyright 1996 Matthew Crosby (crosby@cs.colorado.edu) This is free software that may be redistributed and/or modified under the terms of the GNU General Public License as published by the Free Software Foundation. You should have recieved a copy of this license, if not, it may be obtained by ftp from prep.ai.mit.edu. (see license.txt) This program is a beta. Please send bug reports, ideas for the future or any feedback whatsoever to crosby@cs.colorado.edu. More information about this program may be found on http://ugrad-www.cs.colorado.edu/~crosby/ifmap/IFMap.html This program is a map editor for IF games. I have tried to stick fairly close to Infocoms format. Some sample maps and postscript output may be found in the maps directory in the release package. This program is written in Tk/Tcl using Tk 4.0, however it _should_ be compatible with 3.6. Specifically, it requires wish. If you don't have wish, Tc/Tcl may be obtained from ftp://ftp.smli.com/pub/tcl To install this program, simply edit the line at the top, changing the location of wish to reflect its location on your system. You can then put the script wherever you desire--everything is self contained within the 'ifedit' script. You may also want to install the man page somewhere. This should be very portable, i'd be very interested in any problems anyone has running it. It was developed on an HP 735 running HP-UX, but I've tested it on a couple of other Unix platforms. It should run just fine on non Unix platforms, with the exception of printing (printing to a file should still work). Ideas for the future: Eventually, I want to expand this to create a graphical IF creation tool. A sort of 'Visual inform and tads'. As it stands, it is actually pretty far along; it should be relatively trivial to write a converter from the save format for this program to any language. (The format is pretty self explanatory if anyone wants to go for it). I also want to ditch Tcl. It was never designed for this level of project, and it shows. This started out as a prototype, however I ended up putting most of the functionality I wanted into it. But it does really need to be rewritten. I also need to clean the code up a _lot_. Some of the shorter term ideas for 0.2 include: -More entries for room info (so you can annotate things more) -Less sensativity (right now, it requires a lot of agility of the mouse) -optional grid (to make it easier to line things up) -selecting multiple objects (so you can move whole things) -Multiple maps (so you can, eg, have the zork1 forest, maze and dungeon maps all in one file) -Undo. obvious. -A nicer file box. I'm not all that thrilled with the one I have. -HTML help. I've actually done this, just didn't get around to interfacing it in. I'd definately be interested in any requests that anyone has. Finally, i'd be interested in recieving maps anyone creates for adventures. I'd like to build a large library of maps up. I envision a maps secition of ftp.gmd.de in the future, to go with the walkthroughs. This program really does make things a lot easier, I was able to create the map of Zork 1 in about 15 minutes, for instance, from my hand drawn maps. Enjoy. "