#!/usr/bin/env python
"""
 * dump-msole.py:
 *
 * Copyright (C) 2006 Danny Milosavljevic, Fabasoft (danny.milosavljevic@fabalabs.org)
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of version 2 or later of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
 * USA
"""

from __future__ import division

import struct
import sys
import exceptions
import os
from cStringIO import StringIO
#from msole import ElementType, Color, SectorNumber, Sid, Header, Fat, MiniFat, MiniStream, Stream, DirectoryEntry, determine_path, traverse_directory_tree_left_right

import msole
        
stream_1 = file(sys.argv[1], "rb")

header_1 = msole.Header()
header_1.read_from(stream_1)
header_1.verify()
#header_1.dump()


difat_1 = msole.Difat(header_1)
difat_1.read_from(stream_1)
#print difat_1.sector_numbers


fat_1 = msole.Fat(header_1, difat_1)
fat_1.read_from(stream_1)
#print fat_1.fat

mini_fat_1 = msole.MiniFat(header_1, fat_1)
mini_fat_1.read_from(stream_1)
#mini_fat_1.dump(0, "Mini Fat")

directory_1 = msole.Directory(header_1, fat_1)
directory_1.read_from(stream_1)

root_entry = directory_1.entries[0]
assert(root_entry.element_type == msole.ElementType.Root)

mini_stream_start = root_entry.start_sector
assert(mini_stream_start <= msole.SectorNumber.MaximumRegular or mini_stream_start == msole.SectorNumber.EndOfChain)

mini_stream_1 = None

if mini_stream_start != msole.SectorNumber.EndOfChain:
  mini_stream_1 = msole.Stream(header_1, fat_1, stream_1, mini_stream_start, root_entry.size)

# ____________________________________

import pygtk
pygtk.require("2.0")
import gtk
import gobject

class TreeNode:
	def __init__(self):
		self.center = (0, 0)
		self.radius = (0, 0)
		self.left_area = (0, 0)
		self.right_area = (0, 0)
		self.down_area = (0, 0)
		self.left = None
		self.right = None
		self.parent = None
		self.down = None
		self.title = "Bar"
		self.layout = None
		self.color = msole.Color()

line_height = 32

class TreeDisplayer(gtk.DrawingArea):
	__gtype_name__ = "TreeDisplayer"
	__gsignals__ = {
		"expose-event": "override",
	}

	def __init__(self):
		gtk.DrawingArea.__init__(self)

		self.root = None
		self.red = gtk.gdk.Color(red = 65535)
		self.black = gtk.gdk.Color()
		self.connect("realize", self.realize_cb)

	def realize_cb(self, widget):
		self.red = widget.window.get_colormap().alloc_color(self.red)
		self.black = widget.window.get_colormap().alloc_color(self.black)

	def _area(self, node):
		global line_height

		if node == None:
			return (0, 0)

		ink, logical = node.layout.get_pixel_extents()

		child_area_w = node.left_area[0] + node.down_area[0] + node.right_area[0]
		child_area_h = max(node.left_area[1], node.down_area[1], node.right_area[1])

		area_w = child_area_w #  + logical[2] included in down_area

		own_area = (area_w, child_area_h + line_height + logical[3])

		return own_area

	def calculate_allocation(self, node):
		global line_height

		if node == None:
			return

		#print "calc", node.title

		node.layout = self.create_pango_layout(node.title)
		ink, logical = node.layout.get_pixel_extents()
		node.radius = (logical[2] // 2, logical[3] // 2)

		self.calculate_allocation(node.left)
		self.calculate_allocation(node.down)
		self.calculate_allocation(node.right)

		node.left_area = self._area(node.left)
		node.down_area = self._area(node.down)
		if logical[2] > node.down_area[0]: # make sure at least the text fits
			node.down_area = (logical[2], node.down_area[1])

		node.right_area = self._area(node.right)

		area = self._area(node)
		#print "calc_resume", node.title, node.left_area, node.down_area, node.right_area, area

		return area

	def calculate_position(self, node):
		if node == None:
			return

		center_x, center_y = node.center

		if node.left != None:
			left_center_x = center_x + (-(node.left_area[0] + node.down_area[0]) // 2)
			left_center_y = center_y + line_height
			node.left.center = (left_center_x, left_center_y)

		if node.down != None:
			node.down.center = (center_x, center_y + line_height)

		if node.right != None:
			right_center_x = center_x + (+(node.right_area[0] + node.down_area[0]) // 2)
			right_center_y = center_y + line_height

			node.right.center = (right_center_x, right_center_y)

		self.calculate_position(node.left)
		self.calculate_position(node.down)
		self.calculate_position(node.right)

	def _from_global(self, index):
		global directory_1

		if index >= msole.Sid.MaximumRegular:
			return None

		entry = directory_1.entries[index]
		node_1 = TreeNode()
		node_1.title = entry.name
		node_1.color = entry.color

		node_1.left = self._from_global(entry.left_sid)
		node_1.right = self._from_global(entry.right_sid)
		node_1.down = self._from_global(entry.child_sid)

		return node_1

	def refresh(self):
		global root_entry

		self.root = self._from_global(0)
		area = self.calculate_allocation(self.root)
		self.calculate_position(self.root)
		self.set_size_request(15000, area[1])

	def _draw_node(self, node, offset_x):
		if node == None:
			return

		ink, logical = node.layout.get_pixel_extents()
		position_x = node.center[0] - logical[2] // 2 + offset_x
		position_y = node.center[1] - logical[3] // 2

		#print "position", node.title, position_x, position_y

		gc = gtk.gdk.GC(self.window)
		if node.color == msole.Color.Red:
			gc.set_foreground(self.red)
		else:
			gc.set_foreground(self.black)

		self.window.draw_layout(gc, position_x, position_y, node.layout)
		gc.set_foreground(self.black)

		self._draw_node(node.left, offset_x)
		self._draw_node(node.right, offset_x)
		self._draw_node(node.down, offset_x)

		if node.left != None:
			from_line = node.center[0] + offset_x, node.center[1]
			to_line = node.left.center[0] + offset_x, node.left.center[1]

			self.window.draw_line(gc, from_line[0], from_line[1], to_line[0], to_line[1])

		if node.right != None:
			from_line = node.center[0] + offset_x, node.center[1]
			to_line = node.right.center[0] + offset_x, node.right.center[1]

			self.window.draw_line(gc, from_line[0], from_line[1], to_line[0], to_line[1])

		if node.down != None:
			from_line = node.center[0] + offset_x, node.center[1]
			to_line = node.down.center[0] + offset_x, node.down.center[1]

			self.window.draw_line(gc, from_line[0], from_line[1], to_line[0], to_line[1])

	# most centers are negative
	def find_smallest_center_x(self, node):
		if node == None:
			return 0

		return min(node.center[0], self.find_smallest_center_x(node.left), self.find_smallest_center_x(node.right), self.find_smallest_center_x(node.down))

	def do_expose_event(self, event):
		if self.root == None:
			return

		node_1 = self.root
		area = self._area(self.root)

		offset_x = 100 - self.find_smallest_center_x(node_1)
		#area[0] // 2 # (node_1.left_area[0] + node_1.down_area[0] + node_1.right_area[0]) // 2

		#window_area = (self.window.allocation.width, self.window.allocation.height)

		#print "expose"
		self._draw_node(node_1, offset_x)

gobject.type_register(TreeDisplayer)

view_1 = TreeDisplayer()
view_1.refresh()
view_1.show()

scroller_1 = gtk.ScrolledWindow()
scroller_1.add_with_viewport(view_1)
scroller_1.show()

window_1 = gtk.Window()
window_1.set_size_request(1000,400)
window_1.set_title("%s - draw msole" % sys.argv[1])
window_1.add(scroller_1)
window_1.connect("destroy", lambda x: gtk.main_quit())
window_1.show()

gtk.main()
