Drag and Drop Events Example

dragdropevents.png

 

rem ' DragDropEvents.src

open (unt)"X0"
sysgui! = bbjapi().getSysGui()
window!= sysgui!.addWindow(100,100,750,800,"Drag/Drop Events",$00010083$)
window!.setCallback(sysgui!.ON_CLOSE,"Close")

rem ' Status Bar
id=100,statbar!=window!.addStatusBar(id)

rem ' BBjCEdit
id=id+1,rw_cedit!=window!.addCEdit(id,20,20,100,100,$$,$0102$)
id=id+1,ro_cedit!=window!.addCEdit(id,130,20,100,100,$$,$0302$)

rem ' BBjEditBox
id=id+1,rw_editbox!=window!.addEditBox(id,20,130,100,30,$$,$$)
id=id+1,ro_editbox!=window!.addEditBox(id,130,130,100,30,$$,$$)
ro_editbox!.setEditable(0)

rem ' Password BBjEditBox
id=id+1,rw_password!=window!.addEditBox(id,20,170,100,30,$$,$0400$)
id=id+1,ro_password!=window!.addEditBox(id,130,170,100,30,$$,$0400$)
ro_password!.setEditable(0)

rem ' BBjInputE
id=id+1,rw_inpute!=window!.addInputE(id,20,210,100,30,$$,255,"","")
id=id+1,ro_inpute!=window!.addInputE(id,130,210,100,30,$$,255,"","")
ro_inpute!.setEditable(0)

rem ' BBjInputN
id=id+1,rw_inputn!=window!.addInputN(id,20,250,100,30,$$,"-###,###,###.00",$$,0,0)
id=id+1,ro_inputn!=window!.addInputN(id,130,250,100,30,$$,"-###,###,###.00",$$,0,0)
ro_inputn!.setEditable(0)

rem ' BBjInputD
id=id+1,rw_inputd!=window!.addInputD(id,20,290,100,30,$$,"",$$,0,0)
id=id+1,ro_inputd!=window!.addInputD(id,130,290,100,30,$$,"",$$,0,0)
ro_inputd!.setEditable(0)

rem ' BBjListEdit
id=id+1,rw_listedit!=window!.addListEdit(id,20,330,100,100,"")
id=id+1,ro_listedit!=window!.addListEdit(id,130,330,100,100,"")
ro_listedit!.setEditable(0)

rem ' BBjListBox
id=id+1,rw_listbox!=window!.addListBox(id,20,370,100,100,"",$0000$)
rw_listbox!.setMultipleSelection(1)
id=id+1,ro_listbox!=window!.addListBox(id,130,370,100,100,"",$0400$)
ro_listbox!.setEditable(0)
ro_listbox!.setMultipleSelection(1)

rem ' BBjTree
id=id+1,tree!=window!.addTree(id,20,480,220,130)
parent_id=0,node_id=100
tree!.setRoot(parent_id,"Shakespeare's Plays")
restore shakespeare
dim play$[1:3,0:16]
dread play$[all]
for group=1 to 3
    node_id=node_id+1
    tree!.addExpandableNode(node_id,parent_id,play$[group,0])
    play_parent_id=node_id
    for play=1 to 16
        if play$[group,play]="" then break
        node_id=node_id+1
        tree!.addNode(node_id,play_parent_id,play$[group,play])
    next play
next group

rem ' BBjGrid
id=id+1,rows=20,cols=20
grid!=window!.addGrid(id,id+1,id+2,240,20,500,340,$91ce$,rows,cols),id=id+2
grid!.setRowHeaderWidth(40)
grid!.setDefaultColumnWidth(40)
grid!.setRowHeight(30)
grid!.setEditable(1)
grid!.setMultipleSelection(1)
for col=0 to cols-1
    for row=0 to rows-1
        cell$=str(row)+","+str(col)
        grid!.setCellText(row,col,cell$)
    next row
next col

rem ' BBjColorChooser
id=id+1,ColorChooser!=window!.addColorChooser(id,240,360,500,410)

rem ' Test area
id=id+1,scratch!=window!.addCEdit(id,20,620,220,110,$$,$0102$)

rem ' Reset
reset!=window!.addButton(1,20,740,220,30,"Reset")
CALLBACK(ON_BUTTON_PUSH,Init,sysgui!.getContext(),reset!.getID())
CALLBACK(ON_DROP_TARGET_DROP,DropTargetDrop,sysgui!.getContext(),reset!.getID())

rem ' Report drops to these controls
for i=101 to id
    window!.getControl(i).setCallback(window!.ON_DROP_TARGET_DROP,"DropTargetDrop",err=*next)
next i

rem ' Report completion of drops that start with these controls
for i=101 to id
    window!.getControl(i).setCallback(window!.ON_DRAG_SOURCE_DROP,"DragSourceDrop",err=*next)
next i
gosub init
process_events

DropTargetDrop:
    event!=sysgui!.getLastEvent()
    print 'lf',"ON_DROP_TARGET_DROP"
    dragSource!=event!.getDragSource()
    dropTarget!=event!.getControl()
    info$="Data was dragged from "+fnControlType$(dragSource!)+" to "+fnControlType$(dropTarget!)+$0a$
    if (dragSource!<>null()) then
        info$=info$+"Drag Source actions were: "+fnActions$(dragSource!.getDragActions())+$0a$
    endif
    info$=info$+"Drop Target actions were: "+fnActions$(dropTarget!.getDropActions())+$0a$
    selection$=fnSelection$(dragSource!,event!.getSelection())
    if (len(selection$)) then
        info$=info$+"Drag selection was "+fnTruncate$(selection$)+$0a$
    endif
    location$=fnDropLocation$(dropTarget!,event!.getDropLocation())
    if (len(location$)) then
        info$=info$+"Drop location was "+location$+$0a$
    endif
    info$=info$+"Action was "+fnAction$(event!.getAction())+$0a$
    if (event!.getText()="") then
        info$=info$+"Data is "+fnData$(event!.getData())
    else
        if (pos($0a$=event!.getText())) then
            info$=info$+"Text data is:"+$0a$+fnTruncate$(event!.getText())
        else
            info$=info$+"Text data is """+fnTruncate$(event!.getText())+""""
        endif
    endif
    i=msgbox(info$,1)
    if (i=1) then
        :    gosub AcceptDrop
        : else
        :    gosub RejectDrop
return

rem ' Accept the drop
AcceptDrop:
    rem ' If drop didn't include text, see if we recognize an object
    if event!.getText()=""
        data! = event!.getData()
        if data! = null() or data!.isEmpty() then return
        keys! = data!.keySet().iterator()
        while keys!.hasNext()
            key!=keys!.next()
            obj!=event!.getData(key!)
            if obj!.getClass().getName().equals("java.awt.Color")
                color!=sysgui!.makeColor(obj!.getRed(),obj!.getGreen(),obj!.getBlue())
                dropTarget!.setBackColor(color!)
            endif
            if obj!.getClass().getName().equals("java.awt.Font")
                font!=sysgui!.makeFont(obj!.getName(),obj!.getSize(),obj!.getStyle())
                dropTarget!.setFont(font!)
            endif
        wend
    endif
return

rem ' We have text; handle it based on the dropTarget
switch dropTarget!.getControlType()

    rem ' Drop to a text-based control
    case dropTarget!.CEDIT_CONTROL
    case dropTarget!.EDIT_CONTROL
    case dropTarget!.EDIT_SPINNER_CONTROL
    case dropTarget!.INPUTD_CONTROL
    case dropTarget!.INPUTD_SPINNER_CONTROL
    case dropTarget!.INPUTE_CONTROL
    case dropTarget!.INPUTE_SPINNER_CONTROL
    case dropTarget!.INPUTN_CONTROL
    case dropTarget!.INPUTN_SPINNER_CONTROL
    case dropTarget!.LISTEDIT_CONTROL
        dropLocation = event!.getDropLocation().get(0)
        dropText$ = event!.getText()
        if (dropTarget! = dragSource! and event!.getAction() = event!.ACTION_MOVE) then
            startOffset = event!.getSelection().get(0)
            endOffset = event!.getSelection().get(1)
            if (dropLocation>=startOffset and dropLocation<=endOffset) then
                i = startOffset
            endif
            if (dropLocation<startOffset) then
                i = dropLocation
            endif
            if (dropLocation>endOffset) then
                i = dropLocation-len(dropText$)
            endif
        else
            i = dropLocation
        endif
        text$=dropTarget!.getText()
        text$=text$(1,i)+dropText$+text$(i+1)
        dropTarget!.setText(text$)
        break

    rem ' Insert items to a listbox
    case dropTarget!.LISTBOX_CONTROL
        row = event!.getDropLocation().get(0)
        text$ = event!.getText()
        dropTarget!.insertItems(row,text$)
    break

    rem ' Insert nodes to a tree
    case dropTarget!.TREE_CONTROL
        text! = event!.getText().replaceAll($09$,$0a$)
        vector! = fnVector!(text!)
        node = event!.getDropLocation().get(0)
        if (tree!.isNodeExpandable(node)) then
            parent = node
            index = tree!.getNumChildren(parent)-1
        else
            parent = tree!.getParentNode(node)
            index = tree!.getIndexOfChild(node)
        endif
        for i=0 to vector!.size()-1
            node_id=node_id+1,index=index+1
            tree!.insertNode(node_id,parent,vector!.get(i),index)
        next i
    break

    rem ' Set grid cells
    case dropTarget!.GRID_CONTROL
        row = event!.getDropLocation().get(0)
        column = event!.getDropLocation().get(1)
        if dragSource!<>null() and
            :         dragSource!.getControlType()=dragSource!.GRID_CONTROL then
            text! = event!.getText().replaceAll($09$,$0a$)
            vector! = fnVector!(text!)
            cells! = event!.getSelection(), size=cells!.size()
            firstrow = cells!.get(0)
            firstcolumn = cells!.get(1)
            for i=0 to size-1 step 2
                droprow = row + cells!.get(i) - firstrow
                dropcolumn = column + cells!.get(i+1) - firstcolumn
                celltext$ = vector!.get(i/2)
                dropTarget!.setCellText(droprow,dropcolumn,celltext$)
            next i
        else
            text$ = event!.getText()
            vector! = fnVector!(text$)
            dropTarget!.setCellText(row,column,vector!)
        endif
        break

    rem ' Set button text
    case BBjControl!.PUSHBUTTON_CONTROL
        dropTarget!.setText(event!.getText())
        break
swend
return

rem ' If data was moved from a text control, we can reject
rem ' the drop by putting the text back where we found it.
RejectDrop:
    if dragSource!=null() then return
    if event!.getAction()<>event!.ACTION_MOVE then return
    switch dragSource!.getControlType()
        case dragSource!.CEDIT_CONTROL
        case dragSource!.EDIT_CONTROL
        case dragSource!.EDIT_SPINNER_CONTROL
        case dragSource!.INPUTD_CONTROL
        case dragSource!.INPUTD_SPINNER_CONTROL
        case dragSource!.INPUTE_CONTROL
        case dragSource!.INPUTE_SPINNER_CONTROL
        case dragSource!.INPUTN_CONTROL
        case dragSource!.INPUTN_SPINNER_CONTROL
        case dragSource!.LISTEDIT_CONTROL
            offset=event!.getSelection().get(0)
            text$=dragSource!.getText()
            text$=text$(1,offset)+event!.getText()+text$(offset+1)
            dragSource!.setText(text$)
            break
        swend
        return

def fnVector!(string$)
vector! = bbjapi().makeVector()
if (pos($0a$=string$,1,0)) then
    if string$(len(string$))<>$0a$ then
        string$=string$+$0a$
    endif
    count=pos($0a$=string$,1,0)
    dim rec$:"line["+str(count)+"]:c(1*=10)"
    let rec$=string$
    for i=1 to count
        vector!.add(rec.line$[i])
    next i
else
    vector!.add(string$)
endif
return vector!
fnend

def fnTruncate$(string$)
vector!=fnVector!(string$)
string$=$$,maxlines=16,maxwidth=128
for i=0 to min(vector!.size()-1,maxlines)
    line$=str(vector!.get(i))
    if len(line$) > maxwidth
        string$=string$+line$(1,maxwidth)+"..."
    else
        string$=string$+line$
    endif
    if (vector!.size()>1) then
        string$=string$+$0a$
    endif
next i
if (vector!.size()>maxlines) then
    string$=string$+"..."
endif
return string$
fnend

DragSourceDrop:
    event!=sysgui!.getLastEvent()
    print 'lf',"ON_DRAG_SOURCE_DROP"
    control!=event!.getControl()
    print "Data dragged from ",control!
    print "Drop action ",fnAction$(event!.getAction())," was ",
    if (event!.getSuccess()) then
        print "completed successfully."
    else
        print "not completed successfully."
    endif
return

def fnControlType$(BBjControl!)
if BBjControl!=null()
    return "Unknown"
endif
switch BBjControl!.getControlType()
    case BBjControl!.BARCHART_CONTROL
    return "BBjBarChart"
    case BBjControl!.CEDIT_CONTROL
    return "BBjCEdit"
    case BBjControl!.CHECKABLE_MENUITEM_CONTROL
    return "BBjCheckableMenuItem"
    case BBjControl!.CHECKBOX_CONTROL
    return "BBjCheckbox"
    case BBjControl!.CHILD_WINDOW
    return "BBjChildWindow"
    case BBjControl!.COLORCHOOSER_CONTROL
    return "BBjColorChooser"
    case BBjControl!.EDIT_CONTROL
    return "BBjEditBox"
    case BBjControl!.EDIT_SPINNER_CONTROL
    return "BBjEditBoxSpinner"
    case BBjControl!.FILECHOOSER_CONTROL
    return "BBjFileChooser"
    case BBjControl!.FONTCHOOSER_CONTROL
    return "BBjFontChooser"
    case BBjControl!.GRID_CONTROL
    return "BBjGrid"
    case BBjControl!.GROUPBOX_CONTROL
    return "BBjGroupBox"
    case BBjControl!.HSCROLL_CONTROL
    return "BBjScrollBar"
    case BBjControl!.HSLIDER_CONTROL
    return "BBjSlider"
    case BBjControl!.IMAGE_CONTROL
    return "BBjImage"
    case BBjControl!.INPUTD_CONTROL
    return "BBjInputD"
    case BBjControl!.INPUTD_SPINNER_CONTROL
    return "BBjInputDSpinner"
    case BBjControl!.INPUTE_CONTROL
    return "BBjInputE"
    case BBjControl!.INPUTE_SPINNER_CONTROL
    return "BBjInputESpinner"
    case BBjControl!.INPUTN_CONTROL
    return "BBjInputN"
    case BBjControl!.INPUTN_SPINNER_CONTROL
    return "BBjInputNSpinner"
    case BBjControl!.LINECHART_CONTROL
    return "BBjLineChart"
    case BBjControl!.LISTBOX_CONTROL
    return "BBjListBox"
    case BBjControl!.LISTBUTTON_CONTROL
    return "BBjListButton"
    case BBjControl!.LISTEDIT_CONTROL
    return "BBjListEdit"
    case BBjControl!.MAIN_WINDOW
    return "BBjWindow"
    case BBjControl!.MENUBUTTON_CONTROL
    return "BBjMenuButton"
    case BBjControl!.MENUITEM_CONTROL
    return "BBjMenuItem"
    case BBjControl!.NAVIGATOR_CONTROL
    return "BBjNavigator"
    case BBjControl!.PIECHART_CONTROL
    return "BBjPieChart"
    case BBjControl!.PROGRESS_CONTROL
    return "BBjProgressBar"
    case BBjControl!.PUSHBUTTON_CONTROL
    return "BBjButton"
    case BBjControl!.RADIOBUTTON_CONTROL
    return "BBjRadioButton"
    case BBjControl!.STATUSBAR_CONTROL
    return "BBjStatusBar"
    case BBjControl!.TAB_CONTROL
    return "BBjTabCtrl"
    case BBjControl!.TEXT_CONTROL
    return "BBjStaticText"
    case BBjControl!.TOOLBUTTON_CONTROL
    return "BBjToolButton"
    case BBjControl!.TREE_CONTROL
    return "BBjTree"
    case BBjControl!.VSCROLL_CONTROL
    return "BBjScrollBar"
    case BBjControl!.VSLIDER_CONTROL
    return "BBjSlider"
    case default
    return "Unknown control type "+str(BBjControl!.getControlType())
swend
fnend
def fnSelection$(BBjControl!,BBjVector!)
if BBjControl!=null()
    return ""
endif
switch BBjControl!.getControlType()
    case BBjControl!.CEDIT_CONTROL
        selection$="Offset "+str(BBjVector!.get(0))+
        :              " through "+str(BBjVector!.get(1))+" ("+
        :             "Paragraph "+str(BBjVector!.get(2))+
        :              ", offset "+str(BBjVector!.get(3))+" through "+
        :             "Paragraph "+str(BBjVector!.get(4))+
        :              ", offset "+str(BBjVector!.get(5))+")"
    return selection$
    case BBjControl!.GRID_CONTROL
        selection$=""
        for i=0 to BBjVector!.size()-1 step 2
            selection$=selection$+
            :          "[row="+str(BBjVector!.get(i))+
            :          ",col="+str(BBjVector!.get(i+1))+"],"
        next i
    return selection$(1,len(selection$)-1)
    case BBjControl!.LISTBOX_CONTROL
        selection$=""
        for i=0 to BBjVector!.size()-1
            selection$=selection$+"[row="+str(BBjVector!.get(i))+"],"
        next i
    return selection$(1,len(selection$)-1)
    case BBjControl!.TREE_CONTROL
        selection$=""
        for i=0 to BBjVector!.size()-1
            selection$=selection$+"[node="+str(BBjVector!.get(i))+"],"
        next i
    return selection$(1,len(selection$)-1)
    case BBjControl!.EDIT_CONTROL
    case BBjControl!.EDIT_SPINNER_CONTROL
    case BBjControl!.INPUTD_CONTROL
    case BBjControl!.INPUTD_SPINNER_CONTROL
    case BBjControl!.INPUTE_CONTROL
    case BBjControl!.INPUTE_SPINNER_CONTROL
    case BBjControl!.INPUTN_CONTROL
    case BBjControl!.INPUTN_SPINNER_CONTROL
    case BBjControl!.LISTEDIT_CONTROL
        selection$="Offset "+str(BBjVector!.get(0))+
        :              " through "+str(BBjVector!.get(1))
        return selection$
    case default
        return "N/A"
swend
fnend

def fnDropLocation$(BBjControl!,BBjVector!)
if BBjControl!=null()
    return ""
endif
switch BBjControl!.getControlType()
    case BBjControl!.CEDIT_CONTROL
        location$="Offset "+str(BBjVector!.get(0))+" ("+
        :            "Paragraph "+str(BBjVector!.get(1))+
        :             ", offset "+str(BBjVector!.get(2))+")"
        return location$
    case BBjControl!.GRID_CONTROL
        location$="[row="+str(BBjVector!.get(0))+
        :               ",col="+str(BBjVector!.get(1))+"]"
        return location$
    case BBjControl!.LISTBOX_CONTROL
        location$="[row="+str(BBjVector!.get(0))+"]"
        return location$
    case BBjControl!.TREE_CONTROL
        Node=BBjVector!.get(0)
        location$="[node="+str(node)+"]: """+BBjControl!.getNodeText(node)+""""
        return location$
    case BBjControl!.EDIT_CONTROL
    case BBjControl!.EDIT_SPINNER_CONTROL
    case BBjControl!.INPUTD_CONTROL
    case BBjControl!.INPUTD_SPINNER_CONTROL
    case BBjControl!.INPUTE_CONTROL
    case BBjControl!.INPUTE_SPINNER_CONTROL
    case BBjControl!.INPUTN_CONTROL
    case BBjControl!.INPUTN_SPINNER_CONTROL
    case BBjControl!.LISTEDIT_CONTROL
        location$="Offset "+str(BBjVector!.get(0))
        return location$
    case default
        return ""
swend
fnend

def fnAction$(action)
switch action
    case sysgui!.ACTION_NONE; return "NONE"
    case sysgui!.ACTION_COPY; return "COPY"
    case sysgui!.ACTION_MOVE; return "MOVE"
    case sysgui!.ACTION_COPY_OR_MOVE; return "COPY_OR_MOVE"
    case sysgui!.ACTION_LINK; return "LINK"
    case default; return str(action)
swend
fnend

def fnActions$(action)
actions$=$$
if (fnIsBitSet(action,sysgui!.ACTION_COPY)) then
    actions$=actions$+"COPY|"
endif
if (fnIsBitSet(action,sysgui!.ACTION_MOVE)) then
    actions$=actions$+"MOVE|"
endif
if (fnIsBitSet(action,sysgui!.ACTION_LINK)) then
    actions$=actions$+"LINK|"
endif
if (len(actions$)) then
    return actions$(1,len(actions$)-1)
else
    return "NONE"
endif
fnend

def fnIsBitSet(value,bit)
return sgn(dec(and(bin(value,4),bin(bit,4))))
fnend

def fnData$(data!)
if data!=null() or data!.isEmpty() then return "<Empty>"
data$=$$
key!=data!.keySet().iterator()
while key!.hasNext()
    data$=data$+str(data!.get(key!.next()))+$0a$
wend
return data$(1,len(data$)-1)
fnend

rem ' Initialize
init:
    restore 0
    dread text$
    rw_cedit!.setText(text$)
    ro_cedit!.setText(text$)
    dread text$
    rw_editbox!.setText(text$)
    ro_editbox!.setText(text$)
    dread text$
    rw_password!.setText(text$)
    ro_password!.setText(text$)
    dread text$
    rw_inpute!.setText(text$)
    ro_inpute!.setText(text$)
    dread text$
    rw_inputn!.setText(text$)
    ro_inputn!.setText(text$)
    dread text$
    rw_inputd!.setText(text$)
    ro_inputd!.setText(text$)
    rw_listedit!.removeAllItems()
    ro_listedit!.removeAllItems()
    rw_listbox!.removeAllItems()
    ro_listbox!.removeAllItems()
    dread state$,name$
    while len(state$)
        rw_listedit!.addItem(name$)
        ro_listedit!.addItem(name$)
        rw_listbox!.addItem(name$)
        ro_listbox!.addItem(name$)
        dread state$,name$
    wend
    rw_listedit!.selectIndex(0)
    ro_listedit!.selectIndex(0)
    rw_listbox!.selectIndex(0)
    ro_listbox!.selectIndex(0)
    parent_id=0,node_id=100
    tree!.removeNode(0)
    tree!.setRoot(parent_id,"Shakespeare's Plays")
    restore shakespeare
    dim play$[1:3,0:16]
    dread play$[all]
    for group=1 to 3
        node_id=node_id+1
        tree!.addExpandableNode(node_id,parent_id,play$[group,0])
        play_parent_id=node_id
        for play=1 to 16
            if play$[group,play]="" then break
            node_id=node_id+1
            tree!.addNode(node_id,play_parent_id,play$[group,play])
        next play
    next group
    scratch!.setText($$)
    reset!.setText("Reset")
    for col=0 to cols-1
        for row=0 to rows-1
            cell$=str(row)+","+str(col)
            grid!.setCellText(row,col,cell$)
        next row
    next col
    white! = sysgui!.makeColor(255,255,255)
    for i=101 to id
        control!=window!.getControl(i)
        editable=1,editable=control!.isEditable(err=*next)
        if editable then control!.setBackColor(white!)
    next i
return

rem ' BBjCEdit
data "A man stood upon a railroad bridge in northern Alabama, looking down into the swift water twenty feet below.  The man's hands were behind his back, the wrists bound with a cord.  A rope closely encircled his neck.  It was attached to a stout cross-timber above his head and the slack fell to the level of his knees.  Some loose boards laid upon the ties supporting the rails of the railway supplied a footing for him and his executioners--two private soldiers of the Federal army, directed by a sergeant who in civil life may have been a deputy sheriff.  At a short remove upon the same temporary platform was an officer in the uniform of his rank, armed.  He was a captain.  A sentinel at each end of the bridge stood with his rifle in the position known as 'support,' that is to say, vertical in front of the left shoulder, the hammer resting on the forearm thrown straight across the chest--a formal and unnatural position, enforcing an erect carriage of the body.  It did not appear to be the duty of these two men to know what was occurring at the center of the bridge; they merely blockaded the two ends of the foot planking that traversed it. (For the rest of the story, see: https://www.gutenberg.org/etext/375)."

rem ' BBjEditBox
data "The quick brown fox jumps over the lazy dog."

rem ' BBjEditBox Password field
data "xyzzy"

rem ' BBjInputE
data "The quick brown fox jumps over the lazy dog."

rem ' BBjInputN
data "1234567.89"

rem ' BBjInputD
data "7/7/7"

rem ' BBjListEdit, BBjListBox
DATA "AK","Alaska"
DATA "AL","Alabama"
DATA "AR","Arkansas"
DATA "AZ","Arizona"
DATA "CA","California"
DATA "CO","Colorado"
DATA "CT","Connecticut"
DATA "DC","District of Columbia"
DATA "DE","Delaware"
DATA "FL","Florida"
DATA "GA","Georgia"
DATA "GU","Guam"
DATA "HI","Hawaii"
DATA "IA","Iowa"
DATA "ID","Idaho"
DATA "IL","Illinois"
DATA "IN","Indiana"
DATA "KS","Kansas"
DATA "KY","Kentucky"
DATA "LA","Louisiana"
DATA "MA","Massachusetts"
DATA "MD","Maryland"
DATA "ME","Maine"
DATA "MI","Michigan"
DATA "MN","Minnesota"
DATA "MO","Missouri"
DATA "MS","Mississippi"
DATA "MT","Montana"
DATA "NC","North Carolina"
DATA "ND","North Dakota"
DATA "NE","Nebraska"
DATA "NH","New Hampshire"
DATA "NJ","New Jersey"
DATA "NM","New Mexico"
DATA "NV","Nevada"
DATA "NY","New York"
DATA "OH","Ohio"
DATA "OK","Oklahoma"
DATA "OR","Oregon"
DATA "PA","Pennsylvania"
DATA "PR","Puerto Rico"
DATA "RI","Rhode Island"
DATA "SC","South Carolina"
DATA "SD","South Dakota"
DATA "TN","Tennessee"
DATA "TX","Texas"
DATA "UT","Utah"
DATA "VA","Virginia"
DATA "VI","Virgin Islands"
DATA "VT","Vermont"
DATA "WA","Washington"
DATA "WI","Wisconsin"
DATA "WV","West Virginia"
DATA "WY","Wyoming"
DATA "",""

rem ' BBjTree
shakespeare:
    data "Comedies","The Tempest ","The Two Gentlemen of Verona","The Merry Wives of Windsor","Measure for Measure ","The Comedy of Errors","Much Ado About Nothing","Love's Labour's Lost","A Midsummer Night's Dream","The Merchant of Venice ","As You Like It","The Taming of the Shrew","All's Well That Ends Well","Twelfth Night or What You Will","The Winter's Tale ","Pericles, Prince of Tyre","The Two Noble Kinsmen"
    data "Histories","King John","Richard II","Henry IV, Part 1","Henry IV, Part 2","Henry V","Henry VI, Part 1","Henry VI, Part 2","Henry VI, Part 3","Richard III","Henry VIII","","","","","",""
    data "Tragedies","Troilus and Cressida ","Coriolanus","Titus Andronicus","Romeo and Juliet","Timon of Athens","Julius Caesar","Macbeth","Hamlet","King Lear","Othello","Antony and Cleopatra","Cymbeline","","","",""

Close:
release