"""
Letter Composer for TeX-Letters
"""
#  Copyright (C) 2004  Henning Jacobs <henning@srcco.de>
#
#  This program 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.
#
#  $Id: LetterComposer.py 92 2004-11-28 15:34:44Z henning $

from Tkinter import *
import Pmw
import ToolTip
import os, sys, types
from InputWidgets import TextEdit, TextComboEdit, FilenameEdit, MemoEdit
import Preferences
import ConfigParser
import __version__

PADX = PADY = 2

class Letter:
    "Encapsulates Letter-Data"
    def __init__(self):
        self._metasect = "MetaInfo"
        self._datasect = "LetterData"
        self._dict = {
            'MySignature': "",
            'MyPlace': "",
            'MyAddress': "",
            'MyBackAddress': "",
            'MyBottomText': "",
            'Date': "",
            'Address': "",
            'YourMail': "",
            'Subject': "",
            'Opening': "",
            'Body': "",
            'Closing': "",
            'Enclosing': ""
            }
        
    def getDict(self):
        return self._dict

    def setDict(self, _dict):
        self._dict = _dict
        
    def saveToFile(self, filename):
        cp = ConfigParser.SafeConfigParser()
        # Make OptionNames case-sensitive:
        cp.optionxform = str
        # Add Meta-Info:
        sec = self._metasect
        if not cp.has_section(sec):
            cp.add_section(sec)
        import time
        cp.set(sec, "Creator", "PyCoCuMa-%s" % __version__.__version__)
        cp.set(sec, "CreationDate", time.strftime('%Y%m%dT%H%M%S'))
        # Add Data section:
        sec = self._datasect
        if not cp.has_section(sec):
            cp.add_section(sec)
        for key, val in zip(self._dict.keys(), self._dict.values()):
            cp.set(sec, key, val.encode('utf-8', 'replace'))
        cp.write(open(filename, 'wb'))
        
    def loadFromFile(self, filename):
        cp = ConfigParser.SafeConfigParser()
        # Make OptionNames case-sensitive:
        cp.optionxform = str
        cp.read([filename])
        for opt in cp.options(self._datasect):
            self._dict[opt] = unicode(cp.get(self._datasect, opt), 'utf-8', 'replace')
    

class LetterComposer(Pmw.MegaToplevel):
    def __init__(self, master):
        Pmw.MegaToplevel.__init__(self, master=master, title='Compose Letter')
        self.userdeletefunc(self.withdraw)
        self.master = master    
        self.letter = Letter()
        self.createWidgets()
        self.fillDefaultValues()
        self.withdraw()

    def fillDefaultValues(self):
        "Get initial values from Preferences"
        myaddress = Preferences.get('lettercomposer.myaddress')
        self.edtMyAddress.clear()
        if myaddress: self.edtMyAddress.set(myaddress)
        templatefname = Preferences.get('lettercomposer.template_filename')
        if not templatefname: templatefname = os.path.dirname(sys.argv[0])+'/templates/DinBrief.tex'
        self.edtTemplateFilename.set(templatefname)
        self.edtSubject.clear()
        opening = Preferences.get('lettercomposer.opening', types.ListType)
        if opening is not None: 
            self.edtOpening.setlist(opening)
            if len(opening)>0:
                self.edtOpening.set(opening[0])
        self.edtBody.clear()
        closing = Preferences.get('lettercomposer.closing', types.ListType)
        if closing is not None: 
            self.edtClosing.setlist(closing)
            if len(closing)>0:
                self.edtClosing.set(closing[0])
        # Now fill the combobox lists:
        import time
        self.edtDate.setlist([
            time.strftime('%d. %B %Y'),
            time.strftime('%x'),
            time.strftime('%Y-%m-%d')])
        
    def createWidgets(self):
        hull = self.component('hull')
        hull.rowconfigure(0, weight=1)
        hull.columnconfigure(0, weight=1)
        top = Frame(hull, borderwidth=1, relief=RAISED)
        top.grid(sticky=W+E+S+N)
        sidepanel = Frame(hull, borderwidth=1, relief=RAISED)
        sidepanel.grid(row=0, column=1, sticky=N+S)
        self.edtTemplateFilename = FilenameEdit(sidepanel, showbasenameonly=True,
            filetypes=[('LaTeX Templates','*.tex'),('All Files','*')])
        self.edtTemplateFilename.grid(sticky=W+E, padx=PADX, pady=PADY)
        self.edtTeXFilename = FilenameEdit(sidepanel, type='saveas',
            filetypes=[('LaTeX Files','*.tex'),('All Files','*')])
        ToolTip.ToolTip(self.edtTeXFilename, 'TeX Filename to save to')
        self.edtTeXFilename.grid(sticky=W+E, padx=PADX, pady=PADY)    
        btn = Button(sidepanel, text='Save Letter', command=self.saveLetterToFile)
        btn.grid(sticky=W+E, padx=PADX, pady=PADY)
        btn = Button(sidepanel, text='Load Letter', command=self.loadLetterFromFile)
        btn.grid(sticky=W+E, padx=PADX, pady=PADY)
        btn = Button(sidepanel, text='Save TeX', command=self.saveTeXToFile)
        btn.grid(sticky=W+E, padx=PADX, pady=PADY)
        btn = Button(sidepanel, text='Preview PDF', command=self.previewPdfFile)
        btn.grid(sticky=W+E, padx=PADX, pady=PADY)
        btn = Button(sidepanel, text='Close', command=self.close)
        btn.grid(sticky=W+E, padx=PADX, pady=PADY)
        self.use_signature = IntVar()
        self.use_signature.set(1)
        chk = Checkbutton(sidepanel, text='Use Signature',
            variable=self.use_signature)
        chk.grid(sticky=W+E, padx=PADX, pady=PADY)
        top.columnconfigure(0, weight=1)
        # Expand Body:
        top.rowconfigure(4, weight=1)
        self.edtAddress = MemoEdit(top, labelpos=N, label_text='Address to:')
        ToolTip.ToolTip(self.edtAddress, "recipient's address: exactly four lines are used")
        self.edtAddress.grid(sticky=W+E+S+N, padx=PADX, pady=PADY)
        self.edtMyAddress = MemoEdit(top, labelpos=N, label_text='My Address:')
        ToolTip.ToolTip(self.edtMyAddress, "sender's address: first line is also signature\n"+
            'first three lines are used as backaddress\n'+
            'you can specify as many lines as you need (e.g. for phone, email)')
        self.edtMyAddress.set(' ')
        self.edtMyAddress.tag_add('default', '0.0', END)
        self.edtMyAddress.tag_config('default', justify=RIGHT)
        self.edtMyAddress.grid(row=0, column=1, sticky=W+E+S+N, padx=PADX, pady=PADY)
        self.edtYourMail = TextEdit(top, labelpos=W, label_text='Your Mail:')
        ToolTip.ToolTip(self.edtYourMail, "refer to recipient's mail (usually by date)")
        self.edtYourMail.grid(sticky=W+E, padx=PADX, pady=PADY)
        self.edtDate = TextComboEdit(top, labelpos=W, label_text='Date:')
        ToolTip.ToolTip(self.edtDate, "leave empty to use today's date")
        self.edtDate.grid(row=1, column=1, sticky=W+E, padx=PADX, pady=PADY)
        self.edtSubject = TextEdit(top, labelpos=W, label_text='Subject:')
        self.edtSubject.grid(columnspan=2, sticky=W+E, padx=PADX, pady=PADY)
        self.edtOpening = TextComboEdit(top, labelpos=W, label_text='Opening:')
        self.edtOpening.grid(columnspan=2, sticky=W+E, padx=PADX, pady=PADY)
        self.edtBody = MemoEdit(top, text_height=8, file_extension='.tex')
        ToolTip.ToolTip(self.edtBody, 'text body: you can use LaTeX commands here\n' +
            '(Press CTRL-E to open external editor)')
        self.edtBody.grid(columnspan=2, sticky=W+E+S+N, padx=PADX, pady=PADY)
        self.edtClosing = TextComboEdit(top, labelpos=W, label_text='Closing:')
        self.edtClosing.grid(columnspan=2, sticky=W+E, padx=PADX, pady=PADY)
        self.edtEnclosing = TextEdit(top, labelpos=W, label_text='Enclosing:')
        ToolTip.ToolTip(self.edtEnclosing, 'list of attachments, separate by LaTeX linebreak (\\\\)')
        self.edtEnclosing.grid(columnspan=2, sticky=W+E, padx=PADX, pady=PADY)

    def createLetterFromWidgets(self):
        address_lines = self.edtAddress.get().splitlines()
        # We want exactly four lines:
        for i in range(4-len(address_lines)):
            address_lines.append('')
        myaddress_lines = self.edtMyAddress.get().splitlines()
        if self.use_signature.get() and len(myaddress_lines)>0:
            signature = myaddress_lines[0]
        else: signature = ''    
        letterdict = {
            'MySignature':signature,
            'MyPlace':'',
            'MyAddress':"\n".join(myaddress_lines),
            'MyBackAddress':"\n".join(myaddress_lines[:3]),
            'MyBottomText':'',
            'Date':self.edtDate.get(),
            'Address':"\n".join(address_lines),
            'YourMail':self.edtYourMail.get(),
            'Subject':self.edtSubject.get(),
            'Opening':self.edtOpening.get(),
            'Body':self.edtBody.get(),
            'Closing':self.edtClosing.get(),
            'Enclosing':self.edtEnclosing.get()
            }
        self.letter.setDict(letterdict)

    def saveLetterToFile(self):
        self.createLetterFromWidgets()
        import tkFileDialog
        dlg = tkFileDialog.SaveAs(self.master, filetypes=[("PyCoCuMa Letter Files", "*.letter")])
        letter_dir = Preferences.get('lettercomposer.letter_dir')
        letter_fname, file_ext = os.path.splitext(os.path.basename(self.edtTeXFilename.get()))
        letter_fname += '.letter'
        fname = dlg.show(initialdir=letter_dir, initialfile=letter_fname)
        if fname:
            self.letter.saveToFile(fname)

    def loadLetterFromFile(self):
        import tkFileDialog
        dlg = tkFileDialog.Open(self.master, filetypes=[("PyCoCuMa Letter Files", "*.letter"),
            ("All Files", "*")])
        letter_dir = Preferences.get('lettercomposer.letter_dir')
        fname = dlg.show(initialdir=letter_dir)
        if fname:
            self.letter.loadFromFile(fname)
            d = self.letter.getDict()
            self.edtMyAddress.set(d["MyAddress"])
            self.edtDate.set(d["Date"])
            self.edtAddress.set(d["Address"])
            self.edtYourMail.set(d["YourMail"])
            self.edtSubject.set(d["Subject"])
            self.edtOpening.set(d["Opening"])
            self.edtBody.set(d["Body"])
            self.edtClosing.set(d["Closing"])
            self.edtEnclosing.set(d["Enclosing"])
    
    def saveTeXToFile(self):
        self.createLetterFromWidgets()
        def enclatin1(str):
            return str.encode('latin-1', 'replace')
        from TemplateProcessor import TemplateProcessor
        proc = TemplateProcessor(postproc=enclatin1)
        outfile = file(self.edtTeXFilename.get(), 'wb')
        d = self.letter.getDict().copy()
        d["MyAddress"] = d["MyAddress"].splitlines()
        d["MyBackAddress"] = d["MyBackAddress"].splitlines()
        d["Address"] = d["Address"].splitlines()
        proc.process(file(self.edtTemplateFilename.get()).readlines(),
            outfile.write, d)
        outfile.close()    
        self.savePreferences()

    def savePreferences(self):    
        Preferences.set('lettercomposer.myaddress', self.edtMyAddress.get())
        opening = self.edtOpening.get()
        openings = list(self.edtOpening.getlist())
        if opening and not opening in openings: openings.insert(0, opening)
        Preferences.set('lettercomposer.opening', openings[:10])
        closing = self.edtClosing.get()
        closings = list(self.edtClosing.getlist())
        if closing and not closing in closings: closings.insert(0, closing)
        Preferences.set('lettercomposer.closing', closings[:10])
        Preferences.set('lettercomposer.template_filename', self.edtTemplateFilename.get())
        Preferences.set('lettercomposer.letter_dir', os.path.dirname(self.edtTeXFilename.get()))
        
    def previewPdfFile(self):
        import texwrapper
        texfilename = self.edtTeXFilename.get()
        if not os.access(texfilename, os.F_OK):
            import tkMessageBox
            tkMessageBox.showerror("Error", "File not found: '%s'\n You must save the TeX-file first!" % texfilename)
            return
        texwrapper.run_pdflatex(texfilename)
        texwrapper.view_pdf(texfilename)

    def close(self):
        self.withdraw()

    def composeTo(self, addressee, adr):
        self.fillDefaultValues()
        adrstreet = adr.street.get()
        adrcity = '%s %s' % (adr.postcode.get(), adr.city.get())
        address = '\n%s\n%s\n%s' % (addressee.fn.get(), adrstreet, adrcity)
        self.edtAddress.clear()
        self.edtAddress.set(address)
        texfname = addressee.uid.get()
        if not texfname: texfname = addressee.fn.get().replace(' ', '_')
        texdir = Preferences.get('lettercomposer.letter_dir')
        if not texdir: texdir = os.path.dirname(sys.argv[0])
        import time
        texfname = '%s/%s-%s.tex' % (texdir, texfname, time.strftime('%Y%m%d'))
        self.edtTeXFilename.set(texfname)
        self.show()

    _firstshow = 1
    def show(self):
        if self._firstshow:
            self.centerWindow()
        else:
            self.deiconify()
        self.lift()    
        self._firstshow = 0
            
    def centerWindow(self, relx=0.5, rely=0.3):
        "Center the Window on Screen"
        widget = self
        master = self.master
        widget.update_idletasks() # Actualize geometry information
        if master.winfo_ismapped():
            m_width = master.winfo_width()
            m_height = master.winfo_height()
            m_x = master.winfo_rootx()
            m_y = master.winfo_rooty()
        else:
            m_width = master.winfo_screenwidth()
            m_height = master.winfo_screenheight()
            m_x = m_y = 0
        w_width = widget.winfo_reqwidth()
        w_height = widget.winfo_reqheight()
        x = m_x + (m_width - w_width) * relx
        y = m_y + (m_height - w_height) * rely
        if x+w_width > master.winfo_screenwidth():
            x = master.winfo_screenwidth() - w_width
        elif x < 0:
            x = 0
        if y+w_height > master.winfo_screenheight():
            y = master.winfo_screenheight() - w_height
        elif y < 0:
            y = 0
        widget.geometry("+%d+%d" % (x, y))
        widget.deiconify() # Become visible at the desired location



if __name__ == "__main__":
    import vcard
    import testvcard
    tk = Tk()
    dlg = LetterComposer(tk)
    card = vcard.vCard()
    testvcard.fillDemoCard(card)
    dlg.composeTo(card, card.adr[0])
    tk.mainloop()
