#!/usr/bin/env python
#
# This program was made as part of the sk1project activity to improve UniConvertor
# See http://www.sk1project.org
#
# The code of vsview based on the results of the ALT Linux vsdump project.
# See www.altlinux.ru and http://freshmeat.net/projects/vsdump
#
# Copyright (C) 2007,	Valek Filippov (frob@df.ru)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of version 3 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 Lesser 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

import gobject
import gtk
import vsdoc
import vsdfuncs
import tree
import hexdump
import sys
import stencil
import cairo
import pango
import pangocairo
import draw
import struct
import mfpage
import infowin
import chunks

version = '0.0.13'

ui_info = \
'''<ui>
  <menubar name='MenuBar'>
    <menu action='FileMenu'>
      <menuitem action='Open'/>
      <menuitem action='Close'/>
      <separator/>
      <menuitem action='SaveAs'/>
      <separator/>
      <menuitem action='Quit'/>
    </menu>
    <menu action='ViewMenu'>
      <menuitem action='Tree'/>
      <menuitem action='Preview'/>
      <menuitem action='Slider'/>
    </menu>
    <menu action='HelpMenu'>
      <menuitem action='About'/>
    </menu>
  </menubar>
</ui>'''

def register_stock_icons():
    ''' This function registers our custom toolbar icons, so they  can be themed. '''
    # Add our custom icon factory to the list of defaults
    factory = gtk.IconFactory()
    factory.add_default()

class ApplicationMainWindow(gtk.Window):
    def __init__(self, parent=None):
        register_stock_icons()
        # Create the toplevel window
        gtk.Window.__init__(self)
        try:
            self.set_screen(parent.get_screen())
        except AttributeError:
            self.connect('destroy', lambda *w: gtk.main_quit())

        self.set_title("vsdviewer")
        self.set_default_size(400, 600)

        merge = gtk.UIManager()
        self.set_data("ui-manager", merge)
        merge.insert_action_group(self.__create_action_group(), 0)
        self.add_accel_group(merge.get_accel_group())

        try:
            mergeid = merge.add_ui_from_string(ui_info)
        except gobject.GError, msg:
            print "building menus failed: %s" % msg
        bar = merge.get_widget("/MenuBar")
        bar.show()

        table = gtk.Table(1, 3, False)
        self.add(table)

        table.attach(bar,
            # X direction #          # Y direction
            0, 1,                      0, 1,
            gtk.EXPAND | gtk.FILL,     0,
            0,                         0);
        
        self.notebook =gtk.Notebook()
        self.notebook.set_tab_pos(gtk.POS_BOTTOM)
        table.attach(self.notebook,
            # X direction #          # Y direction
            0, 1,                      1, 2,
            gtk.EXPAND | gtk.FILL,     gtk.EXPAND | gtk.FILL,
            0,                         0);

        # Create statusbar
        self.statusbar = gtk.Statusbar()
        table.attach(self.statusbar,
            # X direction           Y direction
            0, 1,                   2, 3,
            gtk.EXPAND | gtk.FILL,  0,
            0,                      0)
        self.show_all()
        self.das = {}
        self.docs = {}
        self.fname = ''
        if len(sys.argv) > 1:
            for i in range(len(sys.argv)-1):
                self.fname = sys.argv[i+1]
                self.activate_open()

    def __create_action_group(self):
        # GtkActionEntry
        entries = (
          ( "FileMenu", None, "_File" ),               # name, stock id, label
          ( "ViewMenu", None, "_View" ),               # name, stock id, label
          ( "HelpMenu", None, "_Help" ),               # name, stock id, label
          ( "Open", gtk.STOCK_OPEN,                    # name, stock id
            "_Open","<control>O",                      # label, accelerator
            "Open a file",                             # tooltip
            self.activate_open),
          ( "Close", gtk.STOCK_CLOSE,                    # name, stock id
            "_Close","<control>C",                      # label, accelerator
            "Close the file",                             # tooltip
            self.activate_close),
          ( "SaveAs", gtk.STOCK_SAVE_AS,                    # name, stock id
            "_SaveAs...","<control>S",                      # label, accelerator
            "Save the file",                             # tooltip
            self.activate_save),
          ( "Tree", None,                    # name, stock id
            "_Tree","<control>T",                      # label, accelerator
            "View tree of file",                             # tooltip
            self.activate_tree),
          ( "Preview", None,                    # name, stock id
            "_Preview","<control>P",                      # label, accelerator
            "Preview of file",                             # tooltip
            self.activate_preview),
          ( "Slider", None,                    # name, stock id
            "S_lider","<control>L",                      # label, accelerator
            "Slider to play file shape by shape",                             # tooltip
            self.activate_slider),

          ( "Quit", gtk.STOCK_QUIT,                    # name, stock id
            "_Quit", "<control>Q",                     # label, accelerator
            "Quit",                                    # tooltip
            self.activate_quit ),
          ( "About", None,                             # name, stock id
            "_About", "<control>A",                    # label, accelerator
            "About",                                   # tooltip
            self.activate_about ),
        );

        # Create the menubar and toolbar
        action_group = gtk.ActionGroup("AppWindowActions")
        action_group.add_actions(entries)
        return action_group

    def activate_slider(self, action):
        pn = self.notebook.get_current_page()
        if pn != -1:
            if self.das[pn].hdv == 1:
                self.hscale.hide()
                self.das[pn].hdv = 0
                self.das[pn].hadj.value = len(self.das[pn].doc.pages[self.das[pn].pagenum].shapes[0].ShapeList)
            else:
                self.hscale.show()
                self.das[pn].hdv = 1
        
    def activate_about(self, action):
        dialog = gtk.AboutDialog()
        dialog.set_name("vsdviewer %s\nUtility to view VSD/VSS files"%version)
        dialog.set_copyright("\302\251 Copyright 2007 Valek Filippov")
        dialog.set_website("http://www.sk1project.org/")
        dialog.set_default_size(200,300)
        ## Close dialog on user response
        dialog.connect ("response", lambda d, r: d.destroy())
        dialog.show()

    def activate_quit(self, action):
         gtk.main_quit()
         return

    def activate_close(self, action):
        pn = self.notebook.get_current_page()
        if pn!=-1:
            del self.das[pn]
            self.notebook.remove_page(pn)
            if pn < len(self.das):  ## not the last page
                for i in range(pn,len(self.das)):
                        self.das[i] = self.das[i+1]
                del self.das[len(self.das)-1]
        return
 
    def update_statusbar(self, buffer):
        # clear any previous message, underflow is allowed
        self.statusbar.push(0,'%s' % (buffer))

    def update_resize_grip(self, widget, event):
        mask = gtk.gdk.WINDOW_STATE_MAXIMIZED | gtk.gdk.WINDOW_STATE_FULLSCREEN
        if (event.changed_mask & mask):
            self.statusbar.set_has_resize_grip(not (event.new_window_state & mask))

    def file_open(self,parent=None, dirname=None, fname=None):
        dlg = gtk.FileChooserDialog('Open', parent, buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK,gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL))
        dlg.set_local_only(True)
        resp = dlg.run()
        fname = dlg.get_filename()
        dlg.hide()
        if resp == gtk.RESPONSE_CANCEL:
            return None
        return fname
        
    def file_save(self,parent=None, dirname=None, fname=None):
        dlg = gtk.FileChooserDialog('SaveAs...', action=gtk.FILE_CHOOSER_ACTION_SAVE,\
                buttons=(gtk.STOCK_OK,gtk.RESPONSE_OK,gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL))
        dlg.set_local_only(True)
        resp = dlg.run()
        fname = dlg.get_filename()
        dlg.hide()
        if resp == gtk.RESPONSE_CANCEL:
            return None
        return fname
        
    def infowin_update(self,ptr):
        data = ptr.data
        str = ''
        if data:
            type = ord(data[0])
##        if chunks.chunktype.has_key[type]:
##            str_iwin = 'Type: ',chunks.chunktype[type]
##        else:
##            str_iwin = 'Type: %x'%type
            str = infowin.parse(data)
        return str

    def on_row_activated(self, view, path, column, hd,infowin):
        model = view.get_model()
        iter = model.get_iter(path)
        value = model.get_value(iter,0)
        pntr = value["pointer"]
        data = pntr.data
        str_addr = ''
        str_hex = ''
        str_asc = ''
        if data:
            for line in range(0, len(data), 16):
                str_addr+="%07x: "%line
                end = min(16, len(data) - line)
                for byte in range(0, 15):
                    if byte < end:
                        str_hex+="%02x " % ord(data[line + byte])
                        if ord(data[line + byte]) < 32 or 126<ord(data[line + byte]):
                            str_asc +='.'
                        else:
                            str_asc += data[line + byte]
                if end > 15:			
                    str_hex+="%02x" % ord(data[line + 15])
                    if ord(data[line + 15]) < 32 or 126<ord(data[line + 15]):
                        str_asc += '.'
                    else:
                        str_asc += data[line + 15]
                    str_hex+='\n'
                    str_asc+='\n'
                    str_addr+='\n'
        
        buffer_addr = hd.txtdump_addr.get_buffer()
        iter_addr = buffer_addr.get_iter_at_offset(0)
        iter_addr_end = buffer_addr.get_iter_at_offset(buffer_addr.get_char_count())
        buffer_addr.delete(iter_addr, iter_addr_end)
        buffer_addr.insert_with_tags_by_name(iter_addr, str_addr,"monospace")
        buffer_hex = hd.txtdump_hex.get_buffer()
        iter_hex = buffer_hex.get_iter_at_offset(0)
        iter_hex_end = buffer_hex.get_iter_at_offset(buffer_hex.get_char_count())
        buffer_hex.delete(iter_hex, iter_hex_end)
        buffer_hex.insert_with_tags_by_name(iter_hex, str_hex,"monospace")
        buffer_asc = hd.txtdump_asc.get_buffer()
        iter_asc = buffer_asc.get_iter_at_offset(0)
        iter_asc_end = buffer_asc.get_iter_at_offset(buffer_asc.get_char_count())
        buffer_asc.delete(iter_asc, iter_asc_end)
        buffer_asc.insert_with_tags_by_name(iter_asc, str_asc,"monospace")

        buffer_iwin = infowin.get_buffer()
        iter_iwin = buffer_iwin.get_iter_at_offset(0)
        iter_iwin_end = buffer_iwin.get_iter_at_offset(buffer_iwin.get_char_count())
        buffer_iwin.delete(iter_iwin, iter_iwin_end)
        str_iwin = self.infowin_update(pntr)
        buffer_iwin.insert_with_tags_by_name(iter_iwin, str_iwin,"monospace")

    def activate_open(self,parent=None):
        if self.fname !='':
           fname = self.fname
           self.fname = ''
        else:
           fname = self.file_open()
        if fname:
            doc = vsdoc.vsdoc()
            doc.fname = fname
            if vsdfuncs.vsdopen(fname,doc):
##                for i in range(len(doc.pages)):
##                    print 'Page %x name: '%i,doc.names[doc.pages[i].nameidx]
                for i in range(len(doc.pages)):
                    dnum = len(self.das)
                    self.das[dnum] = vsdoc.VsdFace(doc,i)
                    self.das[dnum].set_size_request(int(doc.pages[i].width*doc.zoom*1.1),int(doc.pages[i].height*doc.zoom*1.1))
                    pos = fname.rfind('/')
                    if pos !=-1:
                        fname = fname[pos+1:]
                    try:
                        label = gtk.Label(fname+':'+doc.names[doc.pages[i].nameidx])
                    except:
                        label = gtk.Label(fname+': Page-%u'%i)
                    scrolled = gtk.ScrolledWindow()
                    scrolled.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
                    scrolled.add_with_viewport(self.das[dnum])
                    scrolled.connect("button_press_event",self.on_button_press)
                    scrolled2 = gtk.ScrolledWindow()
                    scrolled2.set_size_request(0,0)
                    scrolled2.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
                    if len(doc.stencils)>0 and doc.version>5:
                        print 'There are %u stencil items in this doc'%len(doc.stencils)
                        sb = stencil.sidebar(doc)
                        scrolled2.add_with_viewport(sb)
                        scrolled2.set_size_request(sb.hsize+24,200)
                    self.hpane = gtk.HPaned()
                    self.hpane.add1(scrolled2)
                    self.hpane.add2(scrolled)
                    self.vbox = gtk.VBox(homogeneous=False, spacing=0)
                    self.vbox.pack_start(self.hpane, expand=True, fill=True, padding=0)
                    try:
                        self.das[dnum].hadj = gtk.Adjustment(0.0, 0.0,  len(self.das[dnum].doc.pages[self.das[dnum].pagenum].shapes[0].ShapeList)+1, 0.1, 1.0, 1.0)
                        self.das[dnum].hadj.value = len(self.das[dnum].doc.pages[self.das[dnum].pagenum].shapes[0].ShapeList)
                    except:
                        self.das[dnum].hadj = gtk.Adjustment(0.0, 0.0, 10, 0.1, 1.0, 1.0)
                        self.das[dnum].hadj.value = 1

                    self.das[dnum].hadj.connect("value_changed", self.adj_changed)
                    self.hscale = gtk.HScale(self.das[dnum].hadj)
                    self.hscale.set_digits(0)
                    self.vbox.pack_end(self.hscale, expand=False, fill=True, padding=0)
                    self.notebook.append_page(self.vbox, label)
                self.notebook.show_tabs = True
                self.notebook.show_all()
                self.hscale.hide()
            return

    def adj_changed(self,widget):
        pn = self.notebook.get_current_page()
        if pn !=-1:
            self.das[pn].hide()
            self.das[pn].show()
            self.notebook.set_current_page(pn)

                        
    def activate_save(self,parent=None):
        fname = self.file_save()
        if fname:
            pn = self.notebook.get_current_page()
            if pn!=-1:
                sdp = self.das[pn]
                page = sdp.doc.pages[sdp.pagenum]
                surface = cairo.SVGSurface(fname,page.width,page.height)
                ct = cairo.Context(surface)
                cr = pangocairo.CairoContext(ct)
                cr.save()
                for i in range(len(page.shapes[0].ShapeList)):
                    draw.render3(sdp.doc,page,page.shapes[page.shapes[0].ShapeList[i]],cr,0)
                cr.restore()
                cr.show_page()
                surface.flush()
                surface.finish()
        
    def on_button_press(self,widget,event):
        pn = self.notebook.get_current_page()
        if pn!=-1:
            sdp = self.das[pn]
            w,h,zoom = sdp.w,sdp.h, sdp.doc.pages[sdp.pagenum].zoom
            if event.button == 1:
                zoom*=1.4
            if event.button == 3:
                zoom/=1.4
            sdp.doc.pages[sdp.pagenum].zoom = zoom
            self.das[pn].hide()
            self.das[pn].set_size_request(int(w*zoom*1.1),int(h*zoom*1.1))
            self.das[pn].show()
            self.notebook.set_current_page(pn)
        
    def activate_tree(self,parent=None):
        pn = self.notebook.get_current_page()
        if pn != -1:
            window = gtk.Window(gtk.WINDOW_TOPLEVEL)
            window.set_resizable(True)
            window.set_default_size(650, 700)
            window.set_border_width(0)
            view = tree.make_view(self.das[pn].doc)
            viewscroll = gtk.ScrolledWindow()
            viewscroll.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
            viewscroll.add(view)
            infowin = gtk.TextView()
            buffer_iwin = infowin.get_buffer()
            buffer_iwin.create_tag("monospace", family="monospace")
            hd = hexdump.hexdump()
            hd.vscroll1.add(infowin)
            hd.vscroll1.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
            view.connect("row-activated", self.on_row_activated, hd,infowin)
            hd.vscroll1.show()
            hd.vpaned.show_all()
            hpaned = gtk.HPaned()            
            hpaned.add1(viewscroll)
            hpaned.add2(hd.vpaned)
            hpaned.show_all()
            window.set_title("vsdviewer tree: "+self.das[pn].doc.fname)
            window.add(hpaned)
            window.show()

    def activate_preview(self, action):
        pn = self.notebook.get_current_page()
        if pn != -1:
            window = gtk.Window(gtk.WINDOW_TOPLEVEL)
            window.set_title("vsdviewer preview: "+self.das[pn].doc.fname)
            window.set_resizable(True)
            window.set_default_size(200, 300)
            window.set_border_width(0)
            buf = self.das[pn].doc.preview
##            try:
            page = mfpage.emfPage()
            page.file.loadmem(buf)
            page.parse()
            nums = len(page.cmds)
            page.hadj = 1
            pf = page.Face
            idx = 0
            for i in range(nums):
                spct = page.cmds[i].type
                if spct == 523:
                    page.DCs[0].x,page.DCs[0].y = page.cmds[i].args
                    idx+=1
                if spct == 524:
                    page.DCs[0].Wx,page.DCs[0].Wy = page.cmds[i].args
                    idx+=1
                if idx == 2:
                    page.width = abs(page.DCs[0].Wx) ##- self.page.DCs[0].x)
                    page.height = abs(page.DCs[0].Wy) ##- self.page.DCs[0].y)
                    break

            scrolled = gtk.ScrolledWindow()
##            pf.render(pf.ctx,pf.page)
            pf.set_size_request(page.width,page.height)
            scrolled.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
            scrolled.add_with_viewport(pf)
            scrolled.set_events(gtk.gdk.BUTTON_PRESS_MASK)
            window.add(scrolled)
            window.show_all()
##            except:
##                print 'Something wrong with the preview file.' ## FIXME! Bring up some dialog?
        return
    
    
def main():
    ApplicationMainWindow()
    gtk.main()

if __name__ == '__main__':
    main()
