#!/usr/bin/env python

from gtk import *
import sys
import tdb
import string
import re

#
# The gdbtool user interface.  The design here is to keep all the gtk stuff
# separate from the tdb stuff so all the user interface magic is stored
# here.
#

class gtdbtool:

    # Initialise the user interface.  A dictionary argument is passed
    # in which is the dictionary to display keys and values on the left
    # hand and right hand side of the user interface respectively."""

    def __init__(self, dict):        
        self.dict = dict
        self.value_display_fns = []
        self.filter_regex = ""

    # Create and configure user interface widgets.  A string argument is
    # used to set the window title.

    def build_ui(self, title):
        win = GtkWindow()
        win.set_title(title)

        win.connect("destroy", mainquit)

        hpaned = GtkHPaned()
        win.add(hpaned)
        hpaned.set_border_width(5)
        hpaned.show()

        vbox = GtkVBox()
        hpaned.add1(vbox)
        vbox.show()

        scrolled_win = GtkScrolledWindow()
        scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
        vbox.pack_start(scrolled_win)
        scrolled_win.show()

        hbox = GtkHBox()
        vbox.pack_end(hbox, expand = 0, padding = 5)
        hbox.show()

        label = GtkLabel("Filter:")
        hbox.pack_start(label, expand = 0, padding = 5)
        label.show()

        self.entry = GtkEntry()
        hbox.pack_end(self.entry, padding = 5)
        self.entry.show()

        self.entry.connect("activate", self.filter_activated)
        
        self.list = GtkList()
        self.list.set_selection_mode(SELECTION_MULTIPLE)
        self.list.set_selection_mode(SELECTION_BROWSE)
        scrolled_win.add_with_viewport(self.list)
        self.list.show()

        self.list.connect("select_child", self.key_selected)

        scrolled_win = GtkScrolledWindow()
        scrolled_win.set_policy(POLICY_AUTOMATIC, POLICY_AUTOMATIC)
        hpaned.add2(scrolled_win)
        scrolled_win.set_usize(500,400)
        scrolled_win.show()
        
        self.text = GtkText()
        self.text.set_editable(FALSE)
        scrolled_win.add_with_viewport(self.text)
        self.text.show()

        self.text.connect("event", self.event_handler)

        self.menu = GtkMenu()
        self.menu.show()

        self.font = load_font("fixed")

        self.update_keylist()

        win.show()

    # Add a key to the left hand side of the user interface

    def add_key(self, key):
        display_key = self.display_key(key)
        list_item = GtkListItem(display_key)
        list_item.set_data("raw_key", key) # Store raw key in item data
        self.list.add(list_item)
        list_item.show()

    # Event handler registered by build_ui()

    def event_handler(self, event, menu):
        return FALSE

    # Set the text to appear in the right hand side of the user interface 

    def set_value_text(self, text):
        self.text.delete_text(0, self.text.get_length())

        # The text widget has trouble inserting text containing NULL
        # characters.

        text = string.replace(text, "\x00", ".")

        self.text.insert(self.font, None, None, text)

    # This function is called when a key is selected in the left hand side
    # of the user interface.

    def key_selected(self, list, list_item):
        key = list_item.children()[0].get()

        # Look for a match in the value display function list

        text = t[list_item.get_data("raw_key")]

        for entry in self.value_display_fns:
            if re.match(entry[0], key):
                text = entry[1](text)
                break

        self.set_value_text(text)

    # Refresh the key list by removing all items and re-inserting them.
    # Items are only inserted if they pass through the filter regexp.

    def update_keylist(self):
        self.list.remove_items(self.list.children())
        self.set_value_text("")
        for k in self.dict.keys():
            if re.match(self.filter_regex, k):
                self.add_key(k)

    # Invoked when the user hits return in the filter text entry widget.

    def filter_activated(self, entry):
        self.filter_regex = entry.get_text()
        self.update_keylist()

    #
    # Public methods
    #

    # Set a function that translates between how keys look in the user
    # interface (displayed keys) versus how they are represented in the tdb
    # (raw keys).

    def set_display_key_fn(self, fn):
        self.display_key = fn

    # Register a value display function for a key.  The first argument is a
    # regex that matches key values, and the second argument is a function
    # to call to convert the raw value data to a string to display in the
    # right hand side of the UI.

    def register_display_value_fn(self, key_regexp, fn):
        self.value_display_fns.append((key_regexp, fn))

    def display_value_hex(self, value):
        return "foo"

def convert_to_hex(data):
    """Return a hex dump of a string as a string.

    The output produced is in the standard 16 characters per line hex +
    ascii format:

    00000000: 40 00 00 00 00 00 00 00  40 00 00 00 01 00 04 80  @....... @.......
    00000010: 01 01 00 00 00 00 00 01  00 00 00 00              ........ ....
    """
    
    pos = 0                             # Position in data
    line = 0                            # Line of data
    
    hex = ""                            # Hex display
    ascii = ""                          # ASCII display

    result = ""
    
    while pos < len(data):
        
	# Start with header
        
	if pos % 16 == 0:
            hex = "%08x: " % (line * 16)
            ascii = ""
            
        # Add character
            
	hex = hex + "%02x " % (ord(data[pos]))
        
        if ord(data[pos]) < 32 or ord(data[pos]) > 176:
            ascii = ascii + '.'
        else:
            ascii = ascii + data[pos]
                
        pos = pos + 1
            
        # Add separator if half way
            
	if pos % 16 == 8:
            hex = hex + " "
            ascii = ascii + " "

        # End of line

	if pos % 16 == 0:
            result = result + "%s %s\n" % (hex, ascii)
            line = line + 1
            
    # Leftover bits

    if pos % 16 != 0:

        # Pad hex string

        for i in range(0, (16 - (pos % 16))):
            hex = hex + "   "

        # Half way separator

        if (pos % 16) < 8:
            hex = hex + " "

        result = result + "%s %s\n" % (hex, ascii)

    return result

# Open handle on tdb

if len(sys.argv) != 2:
    print "Usage: gdbtool <tdbfile>"
    sys.exit(1)

t = tdb.open(sys.argv[1])

# Create user interface

w = gtdbtool(t)

# Set up a key display function.  A lot of keys have \x00 appended to the
# end which mucks up gtk.

def display_key_x00(key):
    return string.replace(key, "\x00", "")

w.set_display_key_fn(display_key_x00)
    
def display_value_hex(value):
    return value;

w.register_display_value_fn("DRIVERS/", convert_to_hex)
w.register_display_value_fn("SECDESC/", convert_to_hex)
w.register_display_value_fn("PRINTERS/", convert_to_hex)

# Show user interface

w.build_ui("gtdbtool: %s" % sys.argv[1])

mainloop()