#! /usr/bin/env python
###############################################################################
#                                                                             #
#   Copyright 2005 University of Cambridge Computer Laboratory.               #
#                                                                             #
#   This file is part of Nprobe.                                              #
#                                                                             #
#   Nprobe is free software; you can redistribute it and/or modify            #
#   it under the terms of the GNU General Public License as published by      #
#   the Free Software Foundation; either version 2 of the License, or         #
#   (at your option) any later version.                                       #
#                                                                             #
#   Nprobe is distributed in the hope that it will be useful,                 #
#   but WITHOUT ANY WARRANTY; without even the implied warranty of            #
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             #
#   GNU General Public License for more details.                              #
#                                                                             #
#   You should have received a copy of the GNU General Public License         #
#   along with Nprobe; if not, write to the Free Software                     #
#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA #
#                                                                             #
###############################################################################


##############################################################################
## 
##
## 
## 
##
## 
############################################################################
############################################################################

from Tkinter import *
from copy import copy
#from np_obnode import *
#import np_obnode
import np_filerec
import np_WebHost
import np_TCPConn
import np_HTTPTrans

from np_lookups import *
from np_TCP_Window import *
#from np_tcp import TcpDisplay
#from np_TCPDisplay import *
import np_TCPDisplay
from np_widgets import *
#from np_Obtree import ObTree, G_AFTER, G_BEFORE
import np_Obtree
#from np_grabrec import grab
import np_grabrec
from bsearch import NotInRange

import gc

from np_bitmaps import drag, ccross


##############################################################################
##############################################################################

OS_WIDTH = 1500
OS_HEIGHT = 1000

UFRAME_WIDTH = 300
UFRAME_HEIGHT = OS_HEIGHT

TOOLBAR_HEIGHT = 25


DFRAME_WIDTH = OS_WIDTH - UFRAME_WIDTH
DFRAME_HEIGHT = OS_HEIGHT

SCALE_CANV_HEIGHT = 25
TREE_CANV_HEIGHT = DFRAME_HEIGHT - SCALE_CANV_HEIGHT

MAP_CANV_HEIGHT =  int(UFRAME_WIDTH*((TREE_CANV_HEIGHT*1.0)/DFRAME_WIDTH))
#MAP_CANV_HEIGHT =  50
INFO_CANV_HEIGHT = UFRAME_HEIGHT - MAP_CANV_HEIGHT - TOOLBAR_HEIGHT
INFO_CAN_SBAR_WIDTH = 15


##############################################################################
##############################################################################

class Tscreen(Frame):


    def __init__(self, clist, lookup=intoa_string, standalone='no', path='',
                 logfun=None, trace=0):

	if not len(clist):
	    print 'TScreen: empty connection list'
	    return

        if standalone == 'yes':
            self.root = Tk()
            self.quitfun = self.finish
        else:
            self.root = Toplevel()
            self.quitfun = self._destroy

        self.root.geometry('+0+0')

	Frame.__init__(self, self.root) 
	self.config(width=OS_WIDTH, height=OS_HEIGHT)
	self.pack(fill=BOTH, expand=1)

	self.clist = clist
	self.indx = -1
	self.ncanv = 0
	self.canvlist = []
	self.trees = []
	self.trees_pure = []
	self.started = 0
        
	self.path = path # where the rep file is
	self.logfun = logfun # to write any logged stuff
        self.trace = trace
        self.lookup = lookup

	self.mode = 'Time'

	self.make_screens()

	self.uframe_width = UFRAME_WIDTH
	self.map_height = MAP_CANV_HEIGHT
	self.dframe_width = DFRAME_WIDTH
	self.info_can_sbar_width = INFO_CAN_SBAR_WIDTH
	self.map_canv_ht = MAP_CANV_HEIGHT
	#print 'mch=%d' % (MAP_CANV_HEIGHT)

	self.dframe_ht = DFRAME_HEIGHT
	self.tscalecanv_ysz = SCALE_CANV_HEIGHT

	self.draw_next()
        
        if standalone == 'yes':
            self.root.mainloop()
        else:
            self.root.wait_window(self.root)
	    
##############################################################################

    def nullf(self, *args):
        return
	    
##############################################################################

    def make_screens(self):

	self.uframe = Frame(self, width=UFRAME_WIDTH, height=UFRAME_HEIGHT, 
			    bg='blue')
	self.uframe.pack(side=LEFT, fill=Y)

	self.dframe = Frame(self, width=DFRAME_WIDTH, height=DFRAME_HEIGHT, 
			    bg='lightblue')
	self.dframe.pack(side=RIGHT, fill=Y)

	self.make_toolbar()

        # These are 1-offs (other canvasses etc are one set per tree)
        self.pktinfo = np_TCPDisplay.ConnPktInfo(self.uframe, INFO_CANV_HEIGHT)
        self.conninfo = np_TCPDisplay.ConnTransInfo(self.uframe)

	    
##############################################################################

    def set_titlebar(self, tree):

	tstr = 'Reference Tree for %s %s' % (tree.client.Class,
                                             tree.client.addr_str())
	self.winfo_toplevel().title(tstr)
	

############################################################################

    def make_canvasses(self):

	# Canvases for info/keys and map
  	info_canv = Can(self.uframe, UFRAME_WIDTH, INFO_CANV_HEIGHT, 0, 
			     0, YSCROLL)
 	info_canv.config(bg='white')
	info_canv.scrollY.config(width=15)
	info_canv.drawn = 0

  	map_canv = Can(self.uframe, UFRAME_WIDTH, MAP_CANV_HEIGHT, 0, 0, 
			    NOSCROLL)
 	map_canv.config(bg='white', highlightbackground='gray44', 
			highlightcolor='grey44')
 	map_canv.drawn = 0


	# Canvases for tree time scale - initially display for detail tree
  	scale_canv = Can(self.dframe, DFRAME_WIDTH, SCALE_CANV_HEIGHT, 
			      0, 0, NOSCROLL)
 	scale_canv.config(bg='white', borderwidth=0, 
			       highlightthickness=0)
  	scale_canv.drawn = 0

  	scale_vcanv = Can(self.dframe, DFRAME_WIDTH, SCALE_CANV_HEIGHT, 
			       0, 0, NOSCROLL)
 	scale_vcanv.config(bg='white', borderwidth=0, 
				highlightthickness=0)
	scale_vcanv.drawn = 0
	# Main canvases for tree - initially display detail tree
  	tree_canv = Can(self.dframe, DFRAME_WIDTH, TREE_CANV_HEIGHT, 0, 
			     0, NOSCROLL)
 	tree_canv.config(bg='white', borderwidth=0, highlightthickness=0)
  	tree_canv.drawn = 0
  	tree_vcanv = Can(self.dframe, DFRAME_WIDTH, TREE_CANV_HEIGHT, 0, 
			      0, NOSCROLL)
 	tree_vcanv.config(bg='white', borderwidth=0, highlightthickness=0)
 	tree_vcanv.drawn = 0

	return (info_canv, map_canv, (scale_canv, tree_canv), 
		(scale_vcanv, tree_vcanv))
	    
##############################################################################

    def make_toolbar(self):

	toolbar = self.toolbar = Menu(self.root)

        toolbar.config(background='grey')

	# quit entry
        toolbar.add_command(label="Quit", command=self.quitfun,
                            background='grey', foreground='red')

        toolbar.add_separator(background='red')
        toolbar.add_command(label=' ', command=None,
                            background='grey', state=DISABLED)

 	# draw previous tree entry
        toolbar.add_command(label="Prev", command=self.draw_prev,
                            background='grey', state=DISABLED)

 	# draw next tree entry
        toolbar.add_command(label="Next", command=self.draw_next,
                            background='grey', state=DISABLED)

 	# alternate view entry
 	if self.mode == 'Detail':
 	    txt = 'Time'
 	else:
 	    txt = 'Detail'
        toolbar.add_command(label=txt, command=self.change_view,
                            background='grey')

         # show connection details entry
        toolbar.add_command(label='Conns', command=self.display_conns,
                            background='grey')

         # grab entry
        toolbar.add_command(label='Grab', command=self.grab,
                            background='grey')

         # redo entry
        toolbar.add_command(label='Redo', command=self.redo,
                            background='grey')

        # reload entry
        rb = self.make_reloadmenu()
        toolbar.add_cascade(label='Reload', menu=rb)

        self.root.config(menu=toolbar) 

############################################################################

    def make_reloadmenu(self):

        #
        # Cause reload of contributing module
        #
        self.reload_what = StringVar()
        rb = self.rb =  Menu(self.toolbar)
        rb.configure(background='grey')
        
        for mod in ['FileRec', 'WebHost', 'TCPConn', 'HTTPTrans', 'Obtree']:
            rb.add_radiobutton(label=mod, background='grey', value=mod,
                               variable=self.reload_what, command=self.reload,
                               indicatoron=0)

        return rb

############################################################################

    def reload(self):

        what = self.reload_what.get()
        
        if what == 'FileRec':
            reload(np_filerec)
        elif what == 'WebHost':
            reload(np_WebHost)
        elif what == 'TCPConn':
            reload(np_TCPConn)
        elif what == 'HTTPTrans':
            reload(np_HTTPTrans)
        elif what == 'Obtree':
            reload(np_Obtree)
	    
##############################################################################

    def make_trees_entry(self, client):

        rootlist = client.rootlist

        if (not rootlist) or len(rootlist) == 1:
            self.tree_picked = 0
            return

        self.tree_picked = 1
        self.treemenu = treemenu = Menu(self.toolbar,
                            background='grey')
        self.toolbar.add_cascade(label="Trees", menu=treemenu)

        self.treevar = treevar = IntVar()
        treevar.set(-1)
        treemenu.add_radiobutton(label='All', variable=treevar, value=-1,
                                 command=self.picktree, indicatoron=0,
                                 selectcolor='blue')
        treemenu.add_separator(background='grey')
        
        i = 0
        for root in client.rootlist:
            treemenu.add_radiobutton(label='%d %s' % (root.order, root.absurl),
                                     variable=treevar,
                                     value=i, command=self.picktree,
                                     indicatoron=0,
                            background='grey',
                                 selectcolor='blue')
            i += 1
        
        treemenu.add_separator(background='grey')
        if client.unlinkedlist:
            treemenu.add_radiobutton(label='Unlinked', variable=treevar,
                                     value=-2,
                                     command=self.picktree, indicatoron=0,
                                     selectcolor='blue')

	    
##############################################################################


    def finish(self):
        
	sys.exit(0)

############################################################################

    def _destroy(self):

        self.root.destroy()
        del self.root
	self.master.destroy()

############################################################################

    def grab(self):

        #print self.clist[self.indx].FileRec
        #np_grabrec.grab(self.clist[self.indx].FileRec)
        np_grabrec.grab(self.clist[self.indx])
	    
##############################################################################

    def set_pnbuttons(self):

	if self.indx + 1 == len(self.clist):
	    self.toolbar.entryconfig("Next", state=DISABLED)
	else:
	    self.toolbar.entryconfig("Next", state=NORMAL)

	if self.indx == 0:
	    self.toolbar.entryconfig("Prev", state=DISABLED)
	else:
	    self.toolbar.entryconfig("Prev", state=NORMAL)
	    
##############################################################################

    def display_set(self, set):

	if self.mode == 'Detail':
	    mset = set[2]
	else:
	    mset = set[3]

        self.pktinfo.pack_forget()
        self.conninfo.pack_forget()
        self.conninfo.transdata=None
        
	set[0].pack(expand=1, fill=BOTH)
	set[1].pack(side=BOTTOM, fill=X)
	self.bind_map_buttons(set[1])

	self.display_mode(mset)
	    
##############################################################################

    def hide_set(self, set):

	if self.mode == 'Detail':
	    mset = set[2]
	else:
	    mset = set[3]

	set[0].pack_forget()
	set[1].pack_forget()
	self.unbind_map_buttons(set[1])
	    
	self.hide_mode(mset)

##############################################################################

    def display_mode(self, set):

	set[0].pack(side=TOP)
	set[1].pack(side=BOTTOM)
	set[1].draw_map_port(set[1])
	
	self.disp = set[1]
	self.bind_mode_buttons(set)
	    
##############################################################################

    def hide_mode(self, set):

	set[0].pack_forget()
	set[1].pack_forget()
	set[1].delete_map_port(set[1])

	self.unbind_mode_buttons(set)
	    
##############################################################################

    def set_drag_cursor(self, e):

        e.widget.config(cursor=(drag, 'orange'))
	    
##############################################################################

    def set_cross_cursor(self, e):

        e.widget.config(cursor=(ccross, 'orange'))
	    
##############################################################################

    def reset_cursor(self, e):

        e.widget.config(cursor=(''))
	    
##############################################################################

    def bind_mode_buttons(self, set):

        set[1].bind("<Enter>", self.set_cross_cursor)
        set[1].bind("<Leave>", self.reset_cursor)
	set[1].bind("<1>", self.tcanv_b1)
	set[1].bind("<B1-Motion>", self.tcanv_b1_drag)
	set[1].bind("<ButtonRelease-1>", self.tcanv_b1_up)

	set[1].bind("Control-<1>", self.tcanv_b1)
	set[1].bind("<Control-B1-Motion>", self.tcanv_b1_bigdrag)
	set[1].bind("<Control-ButtonRelease-1>", self.tcanv_b1_up)

	set[1].bind("<2>", self.tcanv_b2)
	set[1].bind("<B2-Motion>", self.tcanv_b2_drag)
	set[1].bind("<ButtonRelease-2>", self.tcanv_b2_up)

	set[1].bind("Shift-<2>", self.tcanv_shft_b2)
	set[1].bind("<Shift-B2-Motion>", self.tcanv_shft_b2_drag)
	set[1].bind("<Shift-ButtonRelease-2>", self.tcanv_shft_b2_up)

	set[1].bind("Control-<2>", self.tcanv_ctr_b2)
	set[1].bind("<Control-B2-Motion>", self.tcanv_ctr_b2_drag)
	set[1].bind("<Control-ButtonRelease-2>", self.tcanv_ctr_b2_up)

	set[1].bind("<3>", self.tcanv_b3)
	set[1].bind("<B3-Motion>", self.tcanv_b3_drag)
	set[1].bind("<ButtonRelease-3>", self.tcanv_b3_up)

        set[0].bind("<Enter>", self.set_drag_cursor)
        set[0].bind("<Leave>", self.reset_cursor)
	set[0].bind("<1>", self.scanv_b1)
	set[0].bind("<B1-Motion>", self.scanv_b1_drag)
	set[0].bind("<ButtonRelease-1>", self.scanv_b1_up)

	set[0].bind("Control-<1>", self.scanv_b1)
	set[0].bind("<Control-B1-Motion>", self.scanv_b1_bigdrag)
	set[0].bind("<Control-ButtonRelease-1>", self.scanv_b1_up)

	
	    
##############################################################################

    def unbind_mode_buttons(self, set):

        set[1].unbind("<Enter>")
        set[1].unbind("<Leave>")
	set[1].unbind("<1>")
	set[1].unbind("<B1-Motion>")
	set[1].unbind("<ButtonRelease-1>")

	set[1].unbind("Control-<1>")
	set[1].unbind("Control-<B1-Motion>")
	set[1].unbind("Control-<ButtonRelease-1>")

	set[1].unbind("<2>")
	set[1].unbind("<B2-Motion>")
	set[1].unbind("<ButtonRelease-2>")

	set[1].unbind("<3>")
	set[1].unbind("<B3-Motion>")
	set[1].unbind("<ButtonRelease-3>")

## 	set[0].bind("<1>", self.tcanv_b1)
## 	set[0].bind("<B1-Motion>", self.tcanv_b1_drag)
## 	set[0].bind("<ButtonRelease-1>", self.tcanv_b1_up)

## 	set[0].bind("Control-<1>", self.tcanv_b1)
## 	set[0].bind("<Control-B1-Motion>", self.tcanv_b1_bigdrag)
## 	set[0].bind("<Control-ButtonRelease-1>", self.tcanv_b1_up)

        set[0].unbind("<Enter>")
        set[0].unbind("<Leave>")
	set[0].unbind("<1>")
	set[0].unbind("<B1-Motion>")
	set[0].unbind("<ButtonRelease-1>")

	set[0].unbind("Control-<1>")
	set[0].unbind("<Control-B1-Motion>")
	set[0].unbind("<Control-ButtonRelease-1>")
	
	    
##############################################################################

    def bind_map_buttons(self, map):

        map.bind("<Enter>", self.set_drag_cursor)
        map.bind("<Leave>", self.reset_cursor)
	map.bind("<1>", self.map_b1)
	map.bind("Control-<1>", self.map_b1)
	map.bind("<B1-Motion>", self.map_b1_drag)
	map.bind("<Control-B1-Motion>", self.map_b1_alt_drag)
	map.bind("<ButtonRelease-1>", self.map_b1_up)
	map.bind("<Control-ButtonRelease-1>", self.map_b1_up)
	    
##############################################################################

    def unbind_map_buttons(self, map):

        map.unbind("<Enter>")
        map.unbind("<Leave>")
	map.unbind("<1>")
	map.unbind("Control-<1>")
	map.unbind("<B1-Motion>")
	map.unbind("Control-<B1-Motion>")
	map.unbind("<ButtonRelease-1>")
	map.unbind("Control-<ButtonRelease-1>")
    
##############################################################################

    def map_b1(self, ev):

	c = ev.widget
	x = ev.x
	y = ev.y

	c.xmark = x
	c.ymark = y
	c.plmark = c.pl

	c.mark_set = 1

        if self.disp.mode == 'time':
            # scan mark can be arbitrary - 0,0 allows just to use mouse movement for drag
            scanx = 0
            scany = 0
        else:
            #scanx = self.trees[self.indx].gaps[self.disp.mi][3]
            scanx = 0
            scany = 0
            self.disp.xmark = self.trees[self.indx].gaps[self.disp.mi][3]
            
	self.disp.scan_mark(scanx, scany)
	self.disp.scroll_with.scan_mark(scanx, 0)
	self.disp.mark_set = 1
	    
##############################################################################

    def map_b1_drag(self, ev):

        self.map_b1_drag_common(ev, 0)
	    
##############################################################################

    def map_b1_alt_drag(self, ev):

        self.map_b1_drag_common(ev, 1)
	    
##############################################################################

    def map_b1_drag_common(self, ev, ctrl):
	c = ev.widget
	d = self.disp
	x = ev.x
	y = ev.y

	if c.mark_set:

	    xdelta = x-c.xmark
	    ydelta = y-c.ymark

	    if d.mode == 'time':
		# easy - linear x-scaling in main window
		movex = int(xdelta/d.mxfact)
	    else:
		# non-linear - assume dragging lhs of port
                gaps = self.trees[self.indx].gaps

                if ctrl:
                    li = d.mi
                    c.xmark = x

                    if xdelta != 0:
                        li = min(max(li+xdelta, 0), len(gaps)-1)
                    else:
                        return

                    movex = gaps[li][3] - d.xmark
                    d.mi = li
                    #print 'gap%d tm%d x%d movex%d' % (li, gaps[li][1]/1000, gaps[li][3], movex)
                else:

                    t = (x-d.mxoff)/d.mxfact
                    try:
                        li = d.find_gap_by_time(t, gaps, 0, len(gaps)-1,
                                                np_Obtree.G_BEFORE)
                    except NotInRange:
                        li = d.mi
                    movex = gaps[li][3] - d.xmark
                    d.mi = li

	    movey = int(ydelta/d.myfact)
		

	    # dragto is 10x scaled - want 1:1 movement
	    dmovex = -movex/10
	    dmovey = -movey/10
	    d.scan_dragto(dmovex, dmovey)
	    d.scroll_with.scan_dragto(dmovex, 0)

	    # set TLC by using canvas offsets
	    d.tlc[0] = d.canvasx(0)
	    d.tlc[1] = d.canvasy(0)

	    d.draw_map_port(d)
	    

##############################################################################
	    
    def map_b1_up(self, ev):

	ev.widget.mark_set = 0

	ev.widget.mark_set = 0
	self.disp.mark_set = 0
    
##############################################################################

    def scanv_b1(self, ev):

	ev.widget = ev.widget.scroll_with
	self.tcanv_b1(ev)
	
	    
##############################################################################

    def tcanv_b1(self, ev):
	#set drag anchor
	canv = ev.widget
        if canv.mode != 'scale':
            canv.config(cursor=(drag, 'orange'))
	tcanv = canv.scroll_with
	x = ev.x
	y = ev.y
	canv.scan_mark(x, y)
	tcanv.scan_mark(x, 0) #want x-pan only
	canv.mark_set = 1

	canv.xmark = x
	tcanv.xmark = x
	canv.ymark = y
	    
##############################################################################

    def scanv_b1_drag(self, ev):

	ev.widget = ev.widget.scroll_with
	self.tcanv_b1_drag(ev)
	    
##############################################################################

    def tcanv_b1_drag(self, ev):
	# pan following mouse
	#print self
	canv = ev.widget
	tcanv = canv.scroll_with
	x = ev.x
	y = ev.y

	if canv.mark_set:
	    movex = (x-canv.xmark)/10
	    movey = (y-canv.ymark)/10

	    #print 'movex %d movey %d' % (movex, movey)
	    canv.scan_dragto(canv.xmark+movex, 
			     canv.ymark+movey)
	    tcanv.scan_dragto(canv.xmark+movex, 0)


	    canv.tlc[0] = canv.canvasx(x)-x
	    canv.tlc[1] =  canv.canvasy(y)-y

	    #print 'tlc = (%d, %d)' % (canv.canvasx(x)-x, canv.canvasy(y)-y) 

	    canv.draw_map_port(canv)
	    
##############################################################################

    def scanv_b1_bigdrag(self, ev):

	ev.widget = ev.widget.scroll_with
	self.tcanv_b1_bigdrag(ev)
	    
##############################################################################

    def tcanv_b1_bigdrag(self, ev):
	# pan following mouse
	canv = ev.widget
	tcanv = canv.scroll_with
	x = ev.x
	y = ev.y

	if canv.mark_set:
	    canv.scan_dragto(x, y)
	    tcanv.scan_dragto(x, 0)


	    canv.tlc[0] = canv.canvasx(x)-x
	    canv.tlc[1] =  canv.canvasy(y)-y

	    #print 'tlc = (%d, %d)' % (canv.canvasx(x)-x, canv.canvasy(y)-y) 

	    canv.draw_map_port(canv)

##############################################################################
	    
    def scanv_b1_up(self, ev):

	#ev.widget.config(cursor=(''))
	ev.widget = ev.widget.scroll_with
	self.tcanv_b1_up(ev)

##############################################################################
	    
    def tcanv_b1_up(self, ev):

        c = ev.widget
        if c.mode != 'scale':
            c.config(cursor=(ccross, 'orange'))
	c.mark_set = 0
	    
##############################################################################

    def tcanv_b2(self, ev):

        self.tcanv_b2_common(ev)
        
    def tcanv_shft_b2(self, ev):

        self.tcanv_b2_common(ev)
        
    def tcanv_ctr_b2(self, ev):

        self.tcanv_b2_common(ev)
    
    def tcanv_b2_common(self, ev):
        
	canv = ev.widget
	x = canv.canvasx(ev.x)
	y = canv.canvasy(ev.y)

	#print 'B1 %d, %d' % (x, y)

	canv.startx=x
	canv.starty=y
	canv.rbox = None

    def tcanv_b2_drag(self, ev):

        self.tcanv_b2_drag_common(ev)

    def tcanv_shft_b2_drag(self, ev):

        self.tcanv_b2_drag_common(ev)

    def tcanv_ctr_b2_drag(self, ev):

        self.tcanv_b2_drag_common(ev)

    def tcanv_b2_drag_common(self, ev):
    
	# draw selection box
	canv = ev.widget
	x = canv.canvasx(ev.x)
	y = canv.canvasy(ev.y)

	#print 'B1D %d, %d Start %d %d' % (x, y, canv.startx, canv.starty)

	if (canv.startx != x)  and (canv.starty != y) : 
	    canv.delete(canv.rbox)
	canv.rbox = canv.create_rectangle(
	    canv.startx, canv.starty, x, y, outline='blue')
	    
    def tcanv_b2_up(self, ev):
        self.tcanv_b2_up_common(ev, '')
	    
    def tcanv_shft_b2_up(self, ev):
        self.tcanv_b2_up_common(ev, 'shift')
	    
    def tcanv_ctr_b2_up(self, ev):
        self.tcanv_b2_up_common(ev, 'ctrl')
        
    def tcanv_b2_up_common(self, ev, mod):
	# identify TCP connection(s) selected and fire up connection info
	canv = ev.widget
	canv.delete(canv.rbox)

        # allow for click without dragged box
	x = max(canv.canvasx(ev.x), canv.startx+canv.tree.connht/2)
	y = max(canv.canvasy(ev.y), canv.starty+canv.tree.connht/2)

 	conns = canv.tree.client.connlist
	
	items = list(canv.find_overlapping(canv.startx, canv.starty, x, y))
	#print 'items',
	#print items
	connlist = []
	for conn in conns:
	    if canv.mode == 'detailed':
		handle = conn.handle
	    elif canv.mode == 'time':
		handle = conn.vhandle
	    else:
		sys.stderr.write('Goof - unmatched canvas')
		sys.exit(0)
	    if items.count(handle):
		connlist.append(conn)

        if not connlist:
            # restore the tree info display
            self.pktinfo.pack_forget()
            self.conninfo.pack_forget()
            self.canvlist[self.indx][0].pack(side=TOP, fill=BOTH, expand=1)
            return
        
	if mod == 'ctrl':
            np_TCPDisplay.TcpDisplay(connlist, standalone = 'no',
                                     logfun=self.nullf, trace=1)
        elif mod == 'shift':
            self.canvlist[self.indx][0].pack_forget()
            self.conninfo.pack_forget()
            self.pktinfo.list_pkts(connlist[0], 1)
            self.pktinfo.pack(side=TOP, fill=BOTH, expand=1)
        else:
            self.canvlist[self.indx][0].pack_forget()
            self.pktinfo.pack_forget()
            self.conninfo.transdata = None
            self.conninfo.list_trans(connlist[0])
            self.conninfo.pack(side=TOP, fill=BOTH, expand=1)

##############################################################################

    def tcanv_b3(self, ev):

        # time scaler
	canv = ev.widget
	x = int(canv.canvasx(ev.x))
	y = int(canv.canvasy(ev.y))

        # in case we have to scroll
	tcanv = canv.scroll_with
	canv.scan_mark(int(x), int(y))
	tcanv.scan_mark(int(x), 0) #want x-pan only
	canv.mark_set = 1

	canv.xmark = x
	tcanv.xmark = x
	canv.ymark = y
        canv.xsi = 1
        canv.last_dir = 0

	#print 'B1 %d, %d' % (x, y)

	canv.scaleline = None
	canv.scaletxt = None
        
        canv.startscalex=x
        canv.startscaley=y

        if canv.mode == 'detailed':
            # non -linear - find base time once and for all
            t = self.trees[self.indx]
            gaps = t.gaps
            try:
                i = t.find_gap(x, gaps, 0, len(gaps)-1,  np_Obtree.G_AFTER)
                post = 0
            except NotInRange:
                if x < gaps[0][3]:
                    i = 0
                    post = 0
                else:
                    i = len(gaps)-1
                    # after gap - add in duration 
                    post = gaps[i][2]
            canv.startscaletm = gaps[i][1] + post - (gaps[i][3]-x)/canv.scalef
            tm = canv.startscaletm
        else:
            tm = (x-canv.leftmargin)/canv.scalef
         
        # for button down show time
        if ev.x + 100 > self.dframe_width:
            anch = E
        else:
            anch = W   
        canv.scaletxt = canv.create_text(x+5, y-5, anchor=anch,
                                  fill='red', text='%+.3fms' % (tm/1000))
            

    def tcanv_b3_drag(self, ev):

        # for drag show delta

        def draw_scale(canv, x, y, delta, anch): 
	    canv.delete(canv.scaleline) 
	    canv.delete(canv.scaletxt)
            canv.scaleline = canv.create_line(canv.startscalex,
                                canv.startscaley, x, y, fill='red')
            canv.scaletxt = canv.create_text(x+5, y-5, anchor=anch,
                                  fill='red', text='%+.3fms' % (delta/1000))

        def get_tdelta(self, canv, x):

            if canv.mode == 'time':
                tdelta = (x-canv.startscalex)/canv.scalef
            else:
                tr = self.trees[self.indx]
                gaps = tr.gaps
                try:
                    i = tr.find_gap(x, gaps, 0, len(gaps)-1, np_Obtree.G_AFTER)
                    post = 0
                except NotInRange:
                    if x < gaps[0][3]:
                        i = 0
                        post = 0
                    else:
                        i = len(gaps)-1
                        # after gap - add in duration
                        post = gaps[i][2]
                t = gaps[i][1] + post - (gaps[i][3]-x)/canv.scalef
                tdelta = t - canv.startscaletm

            return tdelta
                
        if not 0 < ev.x < self.dframe_width:
            return
	# draw scale line
	canv = ev.widget

        if not 0 < ev.y < canv.ysize:
            return
        
	x = canv.canvasx(ev.x)
	y = canv.canvasy(ev.y)

	if (canv.startscalex != x) or (canv.startscaley != y): 

            if ev.x + 100 > self.dframe_width:
                anch = E
            else:
                anch = W

            tdelta = get_tdelta(self, canv, x)

            # scroll needed?
            dir = 0
            if ev.x >= self.dframe_width-10 and x + canv.xsi < canv.xsize:
                dir = 1
            if ev.x <= 10 and canv.tlc[0] > canv.xmin:
                dir = -1
                
            if dir and dir != canv.last_dir:
                canv.xsi = 1
                canv.last_dir = dir

            if dir:
                si = dir*canv.xsi
                canv.scan_dragto(canv.xmark-si, 
                                 canv.ymark+0)
                canv.scroll_with.scan_dragto(canv.xmark-si, 0)
                canv.tlc[0] = canv.canvasx(x)-x
                canv.draw_map_port(canv)
                draw_scale(canv, x, y, tdelta, anch)
                canv.xsi += 1
                # re-draw from within handler
                self.root.update_idletasks()
            draw_scale(canv, x, y, tdelta, anch)
	    
    def tcanv_b3_up(self, ev):
	# 
	canv = ev.widget
	canv.delete(canv.scaleline)
	canv.delete(canv.scaletxt)
	canv.mark_set = 0
        

##############################################################################
	

    def draw_next(self):

	#print self.indx
	if self.indx + 1 == len(self.clist):
	    return
        constructed = 1
	old_indx = self.indx
	self.indx += 1
	if self.indx == self.ncanv:
	    canvasses = self.make_canvasses()
	    self.canvlist.append(canvasses)
            client = self.clist[self.indx]
            try:
                if client.Class == 'Filerec':
                    #still have to build it from scratch
                    constructed = 0
                    try:
                        client = client.reconstruct(trace=self.trace)
                        self.clist[self.indx] = client
                        constructed = 1
                    except:
                        print 'Can\'t reconstruct client'
                        raise
                try:
                    tree = np_Obtree.ObTree(self.clist[self.indx],
                                        lookup=self.lookup, trace=self.trace)
                    self.trees.append(tree)
                    self.trees_pure.append(copy(tree))
                except:
                    print 'Can\'t construct tree'
                    self.trees.append(None)
                    self.trees_pure.append(None)
                    raise
                tree.draw(self)
            except:
                print 'Can\'t draw tree',
                if constructed:
                    print 'for client', client.addr_str()
                self.indx -= 1
                self.canvlist.pop()
                raise
	    self.ncanv += 1
	else:
	    canvasses = self.canvlist[self.indx]
            client = self.clist[self.indx]

	if self.started:
	    self.hide_set(self.canvlist[old_indx])
            if self.tree_picked:
                self.toolbar.delete('Trees')

	self.display_set(canvasses)

	self.set_titlebar(self.trees[self.indx])
        self.make_trees_entry(client)

	self.started = 1
	self.set_pnbuttons()
	    
##############################################################################

    def draw_prev(self):

	if self.indx == 0:
	    return
	old_indx = self.indx
	self.indx -= 1
	canvasses = self.canvlist[self.indx]
	self.hide_set(self.canvlist[old_indx])
        if self.tree_picked:
            self.toolbar.delete('Trees')
	self.display_set(canvasses)
        self.make_trees_entry(self.clist[self.indx])

	self.set_pnbuttons()
	    
##############################################################################

    def redo(self, rooturl=None, dist=None):

        canvasses = self.canvlist[self.indx]
        for c in canvasses[0:2]:
            c.delete(ALL)
            c.drawn = 0
        for set in canvasses[2:]:
            for c in set:
                c.delete(ALL)
                c.drawn = 0

        self.pktinfo.pack_forget()
        self.conninfo.pack_forget()
        self.conninfo.transdata=None
	canvasses[0].pack(expand=1, fill=BOTH)
        
        if self.tree_picked:
            self.toolbar.delete('Trees')

        reload(np_WebHost)
        reload(np_TCPConn)
        reload(np_HTTPTrans)
        reload(np_Obtree)
        reload (np_filerec)

        client = self.clist[self.indx].FileRec.reconstruct()
        self.clist[self.indx] = client
        client.rooturl = rooturl
        tree = np_Obtree.ObTree(client, lookup=self.lookup,
                                trace=self.trace)
        if dist:
            tree.rooturl = (None, None)
            tree.tree_trans = [t for t in client.translist if t.order in dist[0]]
            tree.tree_conns = [c for c in client.connlist if c.id in dist[1]]
        self.trees[self.indx] = tree
        self.trees_pure[self.indx] = copy(tree)
        tree.draw(self)
        self.make_trees_entry(self.clist[self.indx])
	    
##############################################################################

    def redraw(self, rooturl=None, dist=None):

        canvasses = self.canvlist[self.indx]
        for c in canvasses[0:2]:
            c.delete(ALL)
            c.drawn = 0
        for set in canvasses[2:]:
            for c in set:
                c.delete(ALL)
                c.drawn = 0

        self.pktinfo.pack_forget()
        self.conninfo.pack_forget()
        self.conninfo.transdata=None
	canvasses[0].pack(expand=1, fill=BOTH)
        
        if self.tree_picked:
            self.toolbar.delete('Trees')

        client = self.clist[self.indx]
        client.rooturl = rooturl

        for conn in client.connlist:
            try:
                del conn.reqlabelstart
                del conn.replabelstart
                del conn.vreqlabelstart
                del conn.vreplabelstart
            except AttributeError:
                pass

        tree = self.trees_pure[self.indx]
        self.trees_pure[self.indx] = copy(tree)

        tree.LinkDict = np_obnode.LinkDict().dict

        if dist:
            tree.rooturl = (None, None)
            tree.tree_trans = [t for t in client.translist if t.order in dist[0]]
            tree.tree_conns = [c for c in client.connlist if c.id in dist[1]]
        
        #print tree
        tree.draw(self)
        self.make_trees_entry(self.clist[self.indx])
	    
##############################################################################

    def picktree(self):

        tree = self.treevar.get()
        if tree == -1:
            rooturl = None
            dist = None
            entry = 'All'
        elif tree == -2:
            rooturl = None
            unlinked = self.clist[self.indx].unlinkedlist
            dist = [[t.order for t in unlinked], [t.connid for t in unlinked]]
            entry = 'Unlinked'
        else:
            client = self.clist[self.indx]
            trans = client.rootlist[tree]
            rooturl = (trans.absurl, trans.connid)
            dist = None
            entry = tree+3
            #entry = '%d' % (tree)

        self.redraw(rooturl=rooturl, dist=dist)
        self.treemenu.entryconfig(entry, background='lightblue')
	    
##############################################################################

    def change_view(self):

	#self.toolbar.viewb.config(text=self.mode)

	if self.mode == 'Detail':
	    show = 3
	    hide = 2
	    newmode = 'Time'
	else:
	    show = 2
	    hide = 3
	    newmode = 'Detail'
            
	self.toolbar.entryconfig(newmode, label=self.mode)

        self.mode = newmode

	canvasses = self.canvlist[self.indx]
	self.hide_mode(canvasses[hide])
	self.display_mode(canvasses[show])
	    
##############################################################################

    def display_conns(self):

        np_TCPDisplay.TcpDisplay(self.clist[self.indx].connlist,
                                 standalone='no', logfun = self.nullf, trace=1)

##############################################################################
##############################################################################

def main():

    Tscreen([1, 2, 3, 4])
	    
##############################################################################


# Call main when run as script
if __name__ == '__main__':
        main()

