#!/usr/bin/python2

#========================================================================
# Imports
#========================================================================
from Tkinter import *
import gettext
import tkFileDialog
import tkSimpleDialog
import tkMessageBox
import fileinput
import pickle
import string
import time
import tempfile
import re
import os
import codecs
import xml.dom
import xml.dom.minidom

with_cddb = 0
with_pycddb = 0

try:
  import PyCDDB
  with_pycddb = 1
except:
  pass

try:
  import DiscID
  import CDDB
  with_cddb = 1
except:
  pass

#========================================================================
# Globals
#========================================================================
gettext.install("cdcover")
cdcover_version = "0.7.4"
info = [ _("cdcover version %s") % cdcover_version,
         _("created 2001-2008 by Roland Schaeuble"),
         _("eMail: rschaeuble@gmx.ch"),
         _("the latest version of cdcover is always available at:"),
         _("http://cdcover.sourceforge.net") ]

def Error(msg):
  tkMessageBox.showerror(title="cdcover", message=msg)

#========================================================================
# CDDB Implementation for Python CDDB module by Ben Gertzfield
#========================================================================
class CddbCddb:
  def __init__(self):
    self.performer = None
    self.title = None
    self.tracks = []
    self.numtracks = 0

  def discid(self):
    try:
      device = DiscID.open()
      discid = DiscID.disc_id(device)
      self.numtracks = discid[1]
    except:
      discid = None
    return discid

  def query(self, discid):
    (status, info) = CDDB.query(discid)
    if status == 200:
      info = [info]
    return info

  def read(self, category, discid):
    item = {}
    (status, info) = CDDB.read(category, discid)
    if len(info['DTITLE']) > 0:
      (performer, title) = string.split(info['DTITLE'], " / ", 1)
      self.performer = performer.decode("iso8859-1")
      self.title = title.decode("iso8859-1")
      for track in range(0, self.numtracks):
        key = "TTITLE%d" % (track)
        tname = info[key].decode("iso8859-1")
        self.tracks.append(tname)

  def get_performer(self):
    return self.performer

  def get_title(self):
    return self.title

  def get_tracks(self):
    return self.tracks

#========================================================================
# CDDB Implementation for Python PyCDDB module
#========================================================================
class CddbPycddb:
  def __init__(self):
    self.db = PyCDDB.PyCDDB()
    self.performer = None
    self.title = None
    self.tracks = []

  def discid(self):
    discid = None
    try:
      f = os.popen(configuration.discid + " " + configuration.drive)
      discid = f.readline()
      f.close
    except:
      discid = None
    return discid

  def query(self, discid):
    return self.db.query(discid)

  def read(self, category, discid):
    item = {}
    item['category'] = category
    item['disc_id'] = discid
    info = self.db.read(item)
    if len(info['DTITLE']) > 0:
      (performer, title) = string.split(info['DTITLE'], " / ", 1)
      self.performer = performer.decode("iso8859-1")
      self.title = title.decode("iso8859-1")
      for track in range(len(info['TTITLE'])):
        tname = info['TTITLE'][track].decode("iso8859-1")
        self.tracks.append(tname)

  def get_performer(self):
    return self.performer

  def get_title(self):
    return self.title

  def get_tracks(self):
    return self.tracks

#========================================================================
# CDDB-Adapter
#========================================================================
class Cddb:
  def __init__(self):
    if with_pycddb:
      self.cddb = CddbPycddb()
    elif with_cddb:
      self.cddb = CddbCddb()
    else:
      self.cddb = None

  def discid(self):
    return self.cddb.discid()

  def query(self, discid):
    return self.cddb.query(discid)

  def read(self, category, discid):
    self.cddb.read(category, discid)

  def get_performer(self):
    return self.cddb.get_performer()

  def get_title(self):
    return self.cddb.get_title()

  def get_tracks(self):
    return self.cddb.get_tracks()


#========================================================================
# Configuration
#========================================================================
class Configuration:
  def __init__(self):
    self.share = ""
    self.template = ""
    self.ghostview = ""
    self.discid = ""
    self.drive = ""
    self.date_format = "%Y-%m-%d"
    self.index_format = "[%02d] "

  def get(self, key, attr):
    nodelist = self.document.getElementsByTagName(key)
    if nodelist.length > 0:
      val = nodelist[0].attributes.get(attr).value.encode("iso8859-1")
    else:
      val = ""
    return val

  def read(self, filename):
    try:
     self.document = xml.dom.minidom.parse(filename)
    except xml.parsers.expat.ExpatError, (e):
      Error(_("Error parsing configuration file: %s:%d:%d") % (filename, e.lineno, e.offset))
      sys.exit(-1)
    self.share = self.get("share", "path")
    self.template = self.get("template", "path")
    self.ghostview = self.get("ghostview", "path")
    self.discid = self.get("discid", "path")
    self.drive = self.get("discid", "drive")
    self.date_format = self.get("format", "date")
    self.index_format = self.get("format", "index")

  def check(self):
    if not os.access(self.share, os.R_OK):
      return  (0, "Share directory '%s' not readable" % self.share)
    if not os.access(self.template, os.R_OK):
      return (0, "Postscript Template '%s' not readable" % self.template)
    if not os.access(self.ghostview, os.X_OK):
      return (0, "Ghostview '%s' not executable" % self.ghostview)
    if self.discid != "" and not os.access(self.discid, os.X_OK):
      return (0, "Discid Program '%s' not executable" % self.discid)
    if self.drive != "" and not os.access(self.drive, os.R_OK):
      return (0, "Drive '%s' not readable" % self.drive)
    return (1, "")

  def write(self, filename):
    doc = xml.dom.minidom.Document()
    root = doc.createElement("cdcover-config")
    doc.appendChild(root)

    comment = doc.createComment("Created by cdcover on " + time.asctime())
    root.appendChild(comment)

    share = doc.createElement("share")
    share.setAttribute("path", self.share)
    root.appendChild(share)

    template = doc.createElement("template")
    template.setAttribute("path", self.template)
    root.appendChild(template)

    ghostview = doc.createElement("ghostview")
    ghostview.setAttribute("path", self.ghostview)
    root.appendChild(ghostview)

    discid = doc.createElement("discid")
    discid.setAttribute("path", self.discid)
    discid.setAttribute("drive", self.drive)
    root.appendChild(discid)

    format = doc.createElement("format")
    format.setAttribute("date", self.date_format)
    format.setAttribute("index", self.index_format)
    root.appendChild(format)

    f = open(filename, "w")
    f.write(doc.toprettyxml(indent="  "))
    f.close()

configuration = Configuration()

#========================================================================
# Data Model
#========================================================================
class Cover:
  def __init__(self):
    self.template = ""
    self.title = ""
    self.subtitle = ""
    self.foottext = ""
    self.volume = ""
    self.usedate = 0
    self.fronttext = ""
    self.backtext = ""

  def clear(self):
    self.title = ""
    self.subtitle = ""
    self.foottext = ""
    self.volume = ""
    self.usedate = 0
    self.fronttext = ""
    self.backtext = ""

  def save(self, filename):
    f = open(filename, "w")
    if f != None:
      pickle.dump(self.template, f);
      pickle.dump(self.title, f);
      pickle.dump(self.subtitle, f)
      pickle.dump(self.foottext, f)
      pickle.dump(self.volume, f)
      pickle.dump(self.usedate, f)
      pickle.dump(self.fronttext, f)
      pickle.dump(self.backtext, f)
      f.close()

  def restore(self, filename):
    f = open(filename, "r")
    if f != None:
      self.template = pickle.load(f)
      self.title = pickle.load(f)
      self.subtitle = pickle.load(f)
      self.foottext = pickle.load(f)
      self.volume = pickle.load(f)
      self.usedate = pickle.load(f)
      self.fronttext = pickle.load(f)
      self.backtext = pickle.load(f)
      f.close()

# The cover
cover = Cover()

#========================================================================
# PSString
#========================================================================
class PSString:
  def __init__(self, str):
    self.str = str;

  def asPS(self):
    str = self.str
    str = re.sub("\\\\", "\\\\\\\\", str)
    str = re.sub("\)", "\\\\)", str)
    str = re.sub("\(", "\\\\(", str)
    return "(" + str + ")"

#========================================================================
# Index
#========================================================================
class Index:
  def __init__(self, str):
    self.str = str[0:len(str)-1]

  def asPS(self):
    l = re.split("\n", self.str)
    psstr = "[ "
    for line in l:
      if len(line) > 0:
        ps = PSString(line)
        psstr = psstr + ps.asPS() + " "
    psstr = psstr + "]"
    return psstr

  def asString(self):
    return self.str

  def autonumber(self):
    l = re.split("\n", self.str)
    ns = ""
    index = 1
    for line in l:
      if len(line) > 0:
        s = configuration.index_format % index
        ns = ns + s + line + "\n"
        index = index + 1
      else:
        ns = ns + "\n"
    self.str = ns[0:len(ns)-1]

#========================================================================
# PSFile
#========================================================================
class PSFile:
  def __init__(self, template, ofilename):
    self.template = template
    self.ofilename = ofilename

  def generate(self, cover):
    status = 0
    f = open(self.ofilename, "w")
    if f != None:
      pstitle = PSString(cover.title)
      pssubtitle = PSString(cover.subtitle)
      psfoot = PSString(cover.foottext)
      if cover.usedate:
        date_format = configuration.date_format
        date = time.strftime(date_format, time.localtime(time.time()))
        psvolume = PSString(date)
      else:
        psvolume = PSString(cover.volume)
      psfront = Index(cover.fronttext)
      psback = Index(cover.backtext)

      version = "[ (" + info[0] + ") (" + info[1] + ") (" + \
                info[2] + ") (" + info[3] + ") (" + info[4] +") ]"

      slist = [ (re.compile("\%!cat"), "/cat " + pstitle.asPS() + " def"),
                (re.compile("\%!subcat"), "/subcat " + pssubtitle.asPS() + " def"),
                (re.compile("\%!foottext"), "/foottext " + psfoot.asPS() + " def"),
                (re.compile("\%!date"), "/date " + psvolume.asPS() + " def"),
                (re.compile("\%!fronttext"), "/fronttext " + psfront.asPS() + " def"),
                (re.compile("\%!backtext"), "/backtext " + psback.asPS() + " def"),
                (re.compile("\%!version"), "/version " + version + " def") ]

      if os.access(self.template, os.R_OK):
        for line in fileinput.input(self.template):
          for r in slist:
            if r[0].match(line):
              line = r[1]
          f.write(line.encode("latin-1", "replace"))
        f.close()
        status = 1
      else:
        Error(_("Can not open ") + self.template + "\n")

    return status

#========================================================================
# Toolbar
#========================================================================
class Toolbar(Frame):
  def __init__(self, master, buttons, status):
    Frame.__init__(self, master.root)
    self.status = status
    self.config(relief=GROOVE, borderwidth=2)
    self.buttons = []
    index = 0
    for b in buttons:
      self.buttons.append(Button(self, text=b[0], image=b[1], command=b[2]))
      self.buttons[index].pack(side=b[3])
      status.add(self.buttons[index], b[0])
      index = index + 1

#========================================================================
# Tabbar
#========================================================================
class Tabbar(Frame):
  def __init__(self, master, buttons, status):
    Frame.__init__(self, master.root)
    self.status = status
    self.buttons = []
    self.current = -1
    index = 0
    for b in buttons:
      self.buttons.append(Button(self, text=b[0], image=b[1], command=b[2]))
      self.buttons[index].pack(side="left")
      status.add(self.buttons[index], b[0])
      index = index + 1

  def setbutton(self, i):
    self.current = i
    index = 0
    for b in self.buttons:
      if i == index:
        b.configure(relief=SUNKEN)
      else:
        b.configure(relief=RAISED)
      index = index + 1

  def getbutton(self):
    return self.current

#========================================================================
# Statusbar
#========================================================================
class Statusbar(Label):
  def __init__(self, master):
    Label.__init__(self, master.root)
    self.config(relief=SUNKEN)
    self.w = []
    self.message = ""

  def add(self, widget, text):
    widget.bind("<Enter>", self.mouse_enter)
    widget.bind("<Leave>", self.mouse_leave)
    self.w.append( (widget, text) )

  def mouse_enter(self, event):
    for w in self.w:
      if w[0] == event.widget:
        self.config(text=w[1])

  def mouse_leave(self, event):
    self.config(text=self.message)

  def set_text(self, text):
    self.message = text
    self.config(text=self.message)
    self.update()

#========================================================================
# EntrySelection
#========================================================================
class EntrySelection(tkSimpleDialog.Dialog):
  def __init__(self, root, items):
    self.items = items
    self.selection = -1
    tkSimpleDialog.Dialog.__init__(self, root)

  def body(self, master):
    Label(master, text=_("CDDB:Multiple matches found, choose one:")).pack()

    scrollbar = Scrollbar(master, orient=VERTICAL)

    self.box = Listbox(master, width=80, selectmode=SINGLE, yscrollcommand=scrollbar.set)
    self.box.insert(END, _("none of these"))
    for i in self.items:
      self.box.insert(END, i)
    self.box.pack(side=LEFT, fill=BOTH)
    self.box.bind("<Double-Button-1>", self.ok)

    scrollbar.config(command=self.box.yview)
    scrollbar.pack(side=RIGHT, fill=Y)

  def apply(self):
    self.selection = int(self.box.curselection()[0]) - 1

#========================================================================
# AboutBox
#========================================================================
class AboutBox:
  def __init__(self, parent):
    self.top = Toplevel(parent, background="#909FCF")
    self.logo = PhotoImage(file=os.path.join(configuration.share, "images", "logo.gif"))
    self.top.bind("<Escape>", lambda event: self.top.withdraw())

    self.top.title(_("About CD Cover"))
    Button(self.top, text="CD Cover", image=self.logo, command=self.top.withdraw,
           relief=FLAT, borderwidth=0).pack()
    for self.label in info:
      Label(self.top, text=self.label, background="#909FCF").pack()

    self.top.focus_set()

#========================================================================
# Help Date Format
#========================================================================
class HelpDateFormat(Toplevel):
  def __init__(self, parent):
    Toplevel.__init__(self, parent)
    self.transient(parent)
    body = Frame(self)
    body.pack(side=TOP, padx=5, pady=5)

    text = Text(body)
    text.pack(side=TOP)
    text.insert(END, _("%a = Locale's abbreviated weekday name.\n"))
    text.insert(END, _("%A = Locale's full weekday name.\n"))
    text.insert(END, _("%b = Locale's abbreviated month name.\n"))
    text.insert(END, _("%B = Locale's full month name.\n"))
    text.insert(END, _("%c = Locale's appropriate date and time representation.\n"))
    text.insert(END, _("%d = Day of the month as a decimal number [01,31].\n"))
    text.insert(END, _("%H = Hour (24-hour clock) as a decimal number [00,23].\n"))
    text.insert(END, _("%I = Hour (12-hour clock) as a decimal number [01,12].\n"))
    text.insert(END, _("%j = Day of the year as a decimal number [001,366].\n"))
    text.insert(END, _("%m = Month as a decimal number [01,12].\n"))
    text.insert(END, _("%M = Minute as a decimal number [00,59].\n"))
    text.insert(END, _("%p = Locale's equivalent of either AM or PM.\n"))
    text.insert(END, _("%S = Second as a decimal number [00,61].\n"))
    text.insert(END, _("%U = Week number of the year.\n"))
    text.insert(END, _("%w = Weekday as a decimal number [0(Sunday),6].\n"))
    text.insert(END, _("%W = Week number of the year.\n"))
    text.insert(END, _("%x = Locale's appropriate date representation.\n"))
    text.insert(END, _("%X = Locale's appropriate time representation.\n"))
    text.insert(END, _("%y = Year without century as a decimal number [00,99].\n"))
    text.insert(END, _("%Y = Year with century as a decimal number.\n"))
    text.insert(END, _("%Z = Time zone name (no characters if no time zone exists).\n"))
    text.insert(END, _("%% = A literal \"%\" character.\n"))
    text.configure(state=DISABLED)
    self.wait_window(self)

#========================================================================
# Help Index Format
#========================================================================
class HelpIndexFormat(Toplevel):
  def __init__(self, parent):
    Toplevel.__init__(self, parent)
    self.transient(parent)
    body = Frame(self)
    body.pack(side=TOP, padx=5, pady=5)

    text = Text(body)
    text.pack(side=TOP)
    text.insert(END, _("%d = Track number\n\n"))
    text.insert(END, _("Examples:\n"))
    text.insert(END, _("\"Track %02 - \" will produce:\n"))
    text.insert(END, _("  Track 01 - First track.\n"))
    text.insert(END, _("  Track 02 - Second track.\n"))
    text.insert(END, _("  :\n"))
    text.insert(END, _("  and so on.\n")) 
    text.configure(state=DISABLED)
    self.wait_window(self)

#========================================================================
# ConfigDialog
#========================================================================
class ConfigDialog(Toplevel):
  def __init__(self, parent):
    Toplevel.__init__(self, parent)
    self.parent = parent
    self.transient(parent)
    body = Frame(self)
    body.pack(side=TOP, padx=5, pady=5)

    # Title
    Label(body, text=_("CD-Cover configuration")).pack(side=TOP)

    # Share directory
    f = Frame(body)
    f.pack(side=TOP, fill=X, expand=1)
    Label(f, text=_("Share directory:"), width=25, anchor=E).pack(side=LEFT)
    self.esh = Entry(f, width=40, validate="focusout", validatecommand=self.checkshare)
    self.esh.pack(side=LEFT, fill=X, expand=1)
    Button(f, text="...", command=self.getshare).pack(side=RIGHT)

    # Postscript Template
    f = Frame(body)
    f.pack(side=TOP, fill=X, expand=1)
    Label(f, text=_("Default Postscript Template:"), width=25, anchor=E).pack(side=LEFT)
    self.eps = Entry(f, width=40, validate="focusout", validatecommand=self.checktmpl)
    self.eps.pack(side=LEFT, fill=X, expand=1)
    Button(f, text="...", command=self.gettmpl).pack(side=RIGHT)

    # Ghostview
    f = Frame(body)
    f.pack(side=TOP, fill=X, expand=1)
    Label(f, text="Ghostview:", width=25, anchor=E).pack(side=LEFT)
    self.egv = Entry(f, width=40, validate="focusout", validatecommand=self.checkgv)
    self.egv.pack(side=LEFT, fill=X, expand=1)
    Button(f, text="...", command=self.getgv).pack(side=RIGHT)

    # Discid
    f = Frame(body)
    f.pack(side=TOP, fill=X, expand=1)
    Label(f, text=_("Discid program:"), width=25, anchor=E).pack(side=LEFT)
    self.edi = Entry(f, width=40, validate="focusout", validatecommand=self.checkdiscid)
    self.edi.pack(side=LEFT, fill=X, expand=1)

    self.bdiscid = Button(f, text="...", command=self.getdiscid)
    self.bdiscid.pack(side=LEFT)

    # Drive
    Label(f, text=_("Drive:"), width=10).pack(side=LEFT)
    self.edr = Entry(f, width=10)
    self.edr.pack(side=LEFT)

    # Date format
    f = Frame(body)
    f.pack(side=TOP, fill=X, expand=1)
    Label(f, text="Date Format:", width=25, anchor=E).pack(side=LEFT)
    self.edate = Entry(f, width=40, validate="focusout", validatecommand=self.checkdateformat)
    self.edate.pack(side=LEFT)

    # Help Button for date format
    Button(f, text="?", command=self.help_date).pack(side=LEFT)

    # Date format example
    self.example_date = Label(f, width=20)
    self.example_date.pack(side=LEFT)

    # Index format
    f = Frame(body)
    f.pack(side=TOP, fill=X, expand=1)
    Label(f, text="Index Format:", width=25, anchor=E).pack(side=LEFT)
    self.eindex = Entry(f, width=40, validate="focusout", validatecommand=self.checkindexformat)
    self.eindex.pack(side=LEFT)

    # Help Button for index format
    Button(f, text="?", command=self.help_index).pack(side=LEFT)

    # Index format example
    self.example_index = Label(f, width=20)
    self.example_index.pack(side=LEFT)

    # Buttons Frame
    buttons = Frame(self)
    buttons.pack(side=TOP, padx=5, pady=5)

    # Check Button
    check = Button(buttons, text=_("Check configuration"), command=self.check)
    check.pack(side=LEFT, padx=5)

    # OK Button
    ok = Button(buttons, text=_("Ok"), command=self.ok)
    ok.pack(side=LEFT, padx=5)

    # Cancel Button
    cancel = Button(buttons, text=_("Cancel"), command=self.cancel)
    cancel.pack(side=LEFT, padx=5)

    self.update_ui()
    self.grab_set()
    self.wait_window(self)

  def update_ui(self):
    # Share
    self.esh.delete(0, END)
    self.esh.insert(END, configuration.share)

    # Templates
    self.eps.delete(0, END)
    self.eps.insert(END, configuration.template)

    # Ghostview
    self.egv.delete(0, END)
    self.egv.insert(END, configuration.ghostview)

    # Discid
    if with_cddb or with_pycddb:
      stat = "normal"
    else:
      stat = "disabled"
    self.edi.config(state=stat)
    self.edi.delete(0, END)
    self.edi.insert(END, configuration.discid)

    self.bdiscid.configure(state=stat)

    # Drive
    self.edr.delete(0, END)
    self.edr.insert(END, configuration.drive)
    self.edr.configure(state=stat)

    # Date format
    self.edate.delete(0, END)
    self.edate.insert(END, configuration.date_format)

    # Date example
    self.update_date()

    # Index format
    self.eindex.delete(0, END)
    self.eindex.insert(END, configuration.index_format)

    # Index example
    self.update_index()

  def update_date(self):
    format = self.edate.get()
    self.example_date.configure(text=time.strftime(format, time.localtime(time.time())))

  def update_index(self):
    try:
      s = self.eindex.get() % 42 + _("Trackname")
      self.example_index.configure(text=s)
    except:
      self.example_index.configure(text=_("Error"))

  def help_date(self):
    dlg = HelpDateFormat(self)

  def help_index(self):
    dlg = HelpIndexFormat(self)

  def save(self):
    configuration.share =  self.esh.get()
    configuration.template = self.eps.get()
    configuration.ghostview = self.egv.get()
    configuration.discid = self.edi.get()
    configuration.drive = self.edr.get()
    configuration.date_format = self.edate.get()
    configuration.index_format = self.eindex.get()

    filename = os.path.join(os.environ["HOME"], ".cdcover")
    configuration.write(filename)

  def check(self):
    self.checkshare()
    self.checkgv()
    self.checktmpl()
    self.checkdiscid()
    self.checkdateformat()
    self.checkindexformat()

  def ok(self):
    self.save()
    self.destroy()

  def cancel(self):
    self.destroy()

  def checkshare(self):
    if len(self.esh.get()) > 0 and os.access(self.esh.get(), os.R_OK):
      self.esh.configure(background="green")
      return 1
    else:
      self.esh.configure(background="red")
      return 0

  def getshare(self):
    self.share = tkFileDialog.askdirectory()
    if len(self.share) > 0:
      self.esh.delete(0, END)
      self.esh.insert(END, self.share)
    self.checkshare()

  def checkgv(self):
    if len(self.egv.get()) > 0 and os.access(self.egv.get(), os.X_OK):
      self.egv.configure(background="green")
      return 1
    else:
      self.egv.configure(background="red")
      return 0

  def getgv(self):
    self.gv = tkFileDialog.askopenfilename(filetypes=[(_("All files"), "*"),
                                                      (_("Executable"), "*.exe")])
    if len(self.gv) > 0:
      self.egv.delete(0, END)
      self.egv.insert(END, self.gv)
    self.checkgv()

  def checktmpl(self):
    if len(self.eps.get()) > 0 and os.access(self.eps.get(), os.R_OK):
      self.eps.configure(background="green")
      return 1
    else:
      self.eps.configure(background="red")
      return 0

  def gettmpl(self):
    self.tmpl = tkFileDialog.askopenfilename(defaultextension=".pst",
                                             filetypes=[(_("PS Templatefiles"), "*.pst"),
                                                        (_("All files"), "*")])
    if len(self.tmpl) > 0:
      self.eps.delete(0, END)
      self.eps.insert(END, self.tmpl)
    self.checktmpl()

  def checkdiscid(self):
    if len(self.edi.get()) > 0:
      if os.access(self.edi.get(), os.X_OK):
        self.edi.configure(background="green")
        return 1
      else:
        self.edi.configure(background="red")
        return 0
    else:
      self.edi.configure(background="yellow")
      return 0

  def checkdateformat(self):
    self.edate.configure(background="green")
    self.update_date()
    return 1

  def checkindexformat(self):
    status = 0
    try:
      s = self.eindex.get() % 42
      self.eindex.configure(background="green")
      status = 1
    except:
      self.eindex.configure(background="red")
    self.update_index()
    return status

  def getdiscid(self):
    self.discid = tkFileDialog.askopenfilename(filetypes=[(_("All files"), "*"),
                                                          (_("Executable"), "*.exe")])
    if len(self.discid) > 0:
      self.edi.delete(0, END)
      self.edi.insert(END, self.discid)
    self.checkdiscid()

#========================================================================
# Template Selector Listbox
#========================================================================
class TemplateSelector(tkSimpleDialog.Dialog):
  def __init__(self, root):
    self.selection = None
    self.values = []
    dir = os.path.dirname(cover.template)
    files = os.listdir(dir)
    pstfile = re.compile(".*\.pst$")
    for f in files:
      if pstfile.match(f):
        self.values.append(os.path.join(dir, f));
    tkSimpleDialog.Dialog.__init__(self, root)

  def body(self, master):
    scrollbar = Scrollbar(master, orient=VERTICAL)
    self.list = Listbox(master, width=40, selectmode=SINGLE, yscrollcommand=scrollbar.set)
    index = 0
    for f in self.values:
      self.list.insert(END, os.path.basename(f))
      if f == cover.template:
        self.list.select_set(index)
      index = index + 1
    self.list.bind("<Double-Button-1>", self.ok)
    scrollbar.config(command=self.list.yview)
    scrollbar.pack(side=RIGHT, fill=Y)
    self.list.pack(side=RIGHT, fill=BOTH)

  def apply(self):
    self.selection = self.values[int(self.list.curselection()[0])]

#========================================================================
# Appl
#========================================================================
class Appl:
  def __init__(self):
    if with_pycddb:
      modules = "[PyCDDB]"
    elif with_cddb:
      modules = "[CDDB]"
    else:
      modules = "[no CDDB module]"
    self.root = Tk()
    self.root.title(_("CD Cover %s %s") % (cdcover_version, modules))

    (status, msg) = self.check_config()
    if not status:
      Error("Configuration problem:\n%s\n" % msg)
      self.configure()
      (status, msg) = self.check_config()

    if status:
      cover.template = configuration.template
      self.create_key_bindings()

      self.aboutbox = None
      self.create_ui()
      self.update_ui()
      mainloop()
    else:
      Error(msg)

  def check_config(self):
    status = 0
    msg = ""
    configfilename = self.findconfig(".cdcover")
    if configfilename == "":
      msg = "No configuration found"
    else:
      if configfilename != "":
        configuration.read(configfilename)
        (status, msg) = configuration.check()
    return (status, msg)

  def create_key_bindings(self):
    self.root.bind("<Control-o>", lambda event: self.open())
    self.root.bind("<Control-s>", lambda event: self.save())
    self.root.bind("<Control-g>", lambda event: self.ghostview())
    self.root.bind("<Control-p>", lambda event: self.postscript())
    self.root.bind("<Control-q>", lambda event: self.root.quit())

    if with_pycddb or with_cddb:
      self.root.bind("<Control-d>", lambda event: self.cddb())

    self.root.bind("<Control-n>", lambda event: self.autonumber())

    self.root.bind("<F1>", lambda event: self.about())

  def create_ui(self):
    self.menu()
    self.statusbar()
    self.toolbar()

    # Button to select template
    Label(self.root, text=_("Template:"), height=1).grid(row=1, sticky=W)
    self.btemplate = Button(self.root, command=self.select_template)
    self.btemplate.grid(row=1, column=1, sticky=EW, columnspan=3)
    self.status.add(self.btemplate, _("Template selection"))

    # Title
    Label(self.root, text=_("Title:")).grid(row=2, sticky=W)
    self.etitle = Entry(self.root, width=40)
    self.etitle.grid(row=2, column=1, sticky=EW, columnspan=3)
    self.etitle.focus_set()
    self.status.add(self.etitle, _("Title/Artist"))

    # Subtitle
    Label(self.root, text=_("Subtitle:")).grid(row=3, sticky=W)
    self.esubtitle = Entry(self.root, width=40)
    self.esubtitle.grid(row=3, column=1, sticky=EW, columnspan=3)
    self.status.add(self.esubtitle, _("Subtitle"))

    # Foottext
    Label(self.root, text=_("Foottext:")).grid(row=4, sticky=W)
    self.efoot=Entry(self.root, width=40)
    self.efoot.grid(row=4, column=1, sticky=EW, columnspan=3)
    self.status.add(self.efoot, _("Foottext"))

    # Volume
    self.ldate = Label(self.root, text=_("Volume:"))
    self.ldate.grid(row=5, column=0, sticky=W)
    self.evolume = Entry(self.root, disabledbackground="lightgray")
    self.evolume.grid(row=5, column=1, columnspan=2, sticky=EW)
    self.status.add(self.evolume, _("Volume"))

    self.bdate = IntVar()
    self.cdate = Checkbutton(self.root, text=_("Use date"), variable=self.bdate,
                             command=self.check)
    self.cdate.grid(row=5, column=3, sticky=W)
    self.status.add(self.cdate, _("Use date as volume descriptor"))

    # Tab control
    bt1 = (_("Front"), None, self.switchfront)
    bt2 = (_("Back"), None, self.switchback)
    self.tab = Tabbar(self, [bt1, bt2], self.status)
    self.tab.grid(row=6, column=2, sticky=W)
    self.tab.setbutton(0)

    self.bfile = Button(self.root, text=_("File..."), command=self.getindex)
    self.bfile.grid(row=6, column=3, sticky=E)
    self.status.add(self.bfile, _("Read index from file"))

    # Index
    Label(self.root, text="Index:").grid(row=7, sticky=NW)

    self.tindex = Text(self.root, width=50, height=15)
    self.tindex.grid(row=7, column=1, columnspan=3, sticky=NSEW)
    self.status.add(self.tindex, _("Index/Contents of CD"))

    self.root.grid_rowconfigure(7, weight=1)
    self.root.grid_columnconfigure(2, weight=1)
    self.root.grid_columnconfigure(5, minsize=20)

  def select_template(self):
    d = TemplateSelector(self.root)
    if d.selection:
      cover.template = d.selection
      self.update_ui()

  def findconfig(self, filename):
    searchlist = [ ]
    if os.environ.has_key("HOME"):
      searchlist.append(os.path.join(os.environ["HOME"], filename))
    searchlist.append(os.path.join("/usr/local/share/cdcover/", filename))
    searchlist.append(os.path.join("/usr/share/cdcover/", filename))
    searchlist.append(filename)

    for f in searchlist:
      f = os.path.normpath(f)
      if os.access(f, os.O_RDONLY):
        return f
    return ""

  def update_ui(self):
    self.etitle.delete(0, END)
    self.etitle.insert(END, cover.title)

    self.esubtitle.delete(0, END)
    self.esubtitle.insert(END, cover.subtitle)

    self.efoot.delete(0, END)
    self.efoot.insert(END, cover.foottext)

    self.evolume.configure(state=NORMAL)
    self.evolume.delete(0, END)
    self.evolume.insert(END, cover.volume)
    if cover.usedate:
      self.evolume.configure(state=DISABLED)
      self.cdate.select()
    else:
      self.evolume.configure(state=NORMAL)
      self.cdate.deselect()

    if self.tab.getbutton() == 0:
      self.tindex.delete(1.0, END)
      self.tindex.insert(END, cover.fronttext[:-1])
    else:
      self.tindex.delete(1.0, END)
      self.tindex.insert(END, cover.backtext[:-1])

    self.btemplate.configure(text=os.path.basename(cover.template))

  def update_model(self):
    cover.title = self.etitle.get()
    cover.subtitle = self.esubtitle.get()
    cover.foottext = self.efoot.get()
    cover.volume = self.evolume.get()
    cover.usedate = self.bdate.get()
    if self.tab.getbutton() == 0:
      cover.fronttext = self.tindex.get(1.0, END)
    else:
      cover.backtext = self.tindex.get(1.0, END)

  def statusbar(self):
    Label(self.root, text=_("Status:")).grid(row=8, column=0)
    self.status = Statusbar(self)
    self.status.grid(row=8, column=1, columnspan=3, sticky=EW)
    self.status.add(self.status, _("Statusbar"))

  def mouse_enter(self, event):
    if event.widget == self.tindex:
      self.status.configure(text=_("Index of CD"))
    else:
      self.status.configure(text=event)

  def mouse_leave(self, event):
    self.status.configure(text="")

  def copy(self):
    self.root.event_generate('<Control-c>')

  def paste(self):
    self.root.event_generate('<Control-v>')

  def menu(self):
    menu = Menu(self.root)
    filemenu = Menu(menu, tearoff=1)
    filemenu.add_command(label=_("Open..."), command=self.open, accelerator="Ctlr-O")
    filemenu.add_command(label=_("Save..."), command=self.save, accelerator="Ctrl-S")
    filemenu.add_separator()
    filemenu.add_command(label=_("Ghostview..."), command=self.ghostview, accelerator="Ctrl-G")
    filemenu.add_command(label=_("Postscript..."), command=self.postscript, accelerator="Ctrl-P")
    filemenu.add_separator()
    filemenu.add_command(label=_("Exit"), command=self.root.quit, accelerator="Ctrl-Q")
    menu.add_cascade(label=_("File"), menu=filemenu)

    editmenu = Menu(menu, tearoff=1)
    if os.name == "nt":
      editmenu.add_command(label=_("Copy"), command=self.copy)
      editmenu.add_command(label=_("Paste"), command=self.paste)
    editmenu.add_command(label=_("Clear"), command=self.clear)
    menu.add_cascade(label=_("Edit"), menu=editmenu)

    indexmenu = Menu(menu, tearoff=1)
    if with_pycddb or with_cddb:
      indexmenu.add_command(label=_("Ask CDDB"), command=self.cddb, accelerator="Ctrl-D")
    indexmenu.add_command(label=_("Read Index from file..."),
                          command=self.getindex)
    indexmenu.add_command(label=_("Autonumber"), command=self.autonumber, accelerator="Ctrl-N")

    menu.add_cascade(label=_("Index"), menu=indexmenu)

    optmenu = Menu(menu, tearoff=1)
    optmenu.add_command(label=_("Configure..."), command=self.configure)
    menu.add_cascade(label=_("Options"), menu=optmenu)

    helpmenu = Menu(menu, tearoff=0)
    helpmenu.add_command(label=_("About CD-Cover"), command=self.about)
    menu.add_cascade(label=_("Help"), menu=helpmenu)

    self.root.config(menu=menu)

  def toolbar(self):
    imgdir = os.path.join(configuration.share, "images")
    self.img = [ PhotoImage(file=os.path.join(imgdir, "document-print-preview.gif")),
                 PhotoImage(file=os.path.join(imgdir, "document-save.gif")),
                 PhotoImage(file=os.path.join(imgdir, "renumber.gif")),
                 PhotoImage(file=os.path.join(imgdir, "application-exit.gif")) ]

    if with_pycddb or with_cddb:
      self.img.append(PhotoImage(file=os.path.join(imgdir, "cddb.gif")))
    else:
      self.img.append(PhotoImage(file=os.path.join(imgdir, "nocddb.gif")))

    b = []
    b.append((_("Ghostview"), self.img[0], self.ghostview, "left"))
    b.append((_("Postscript..."), self.img[1], self.postscript, "left"))
    b.append((_("Autonumber"), self.img[2], self.autonumber, "left"))
    b.append((_("Exit program"), self.img[3], self.root.quit, "left"))
    if with_pycddb or with_cddb:
      b.append((_("Ask CDDB"), self.img[4], self.cddb, "right"))
    else:
      b.append((_("Requires PyCDDB"), self.img[4], self.cddb, "right"))

    Toolbar(self, b, self.status).grid(row=0, columnspan=6, sticky=EW)

  def about(self):
    AboutBox(self.aboutbox)

  def clear(self):
    cover.clear()
    self.update_ui()

  def cddb(self):
    if with_pycddb or with_cddb:
      cddb = Cddb()

      self.ok = 0
      self.status.set_text(_("Calculating disc ID..."))
      discid = cddb.discid()
      if discid != None:
        self.status.set_text(_("Query CDDB..."))
        items = cddb.query(discid)
        if items != None and len(items) > 0: # Items found
          item = -1
          if len(items) > 1: # more than one item found
            e = EntrySelection(self.root, items)
            if e.selection >= 0:
              item = e.selection
          else:
            item = 0 # only one match

          if item >= 0:
            cddb.read(items[item]['category'], items[item]['disc_id'])
            title = cddb.get_title()
            if title != None: # info found
              cover.subtitle = title
              performer = cddb.get_performer()
              cover.title = performer
              text = ""
              track = 1
              tracks = cddb.get_tracks()
              for trackname in tracks:
                format = configuration.index_format + "%s\n"
                t = format % (track, trackname)
                text = text + t
                track = track + 1

              if self.tab.getbutton() == 0:
                cover.fronttext = text
              else:
                cover.backtext = text
              self.update_ui()
        else:
          self.status.set_text(_("No match found"))
      else:
        self.status.set_text(_("Could not get disc-id"))
    else:
        self.status.set_text(_("Need PyCDDB from http://pycddb.sourceforge.net"))


  def open(self):
    filename = tkFileDialog.askopenfilename(defaultextension=".cvr",
                                            filetypes=[(_("Coverfiles"),
                                                        "*.cvr"),
                                                       (_("All files"), "*")])
    if len(filename) > 0:
      cover.restore(filename)
      self.update_ui()

  def save(self):
    self.update_model()
    filename = tkFileDialog.asksaveasfilename(defaultextension=".cvr",
                                              filetypes=[(_("Coverfiles"),
                                                          "*.cvr"),
                                                         (_("All files"), "*")])
    if len(filename) > 0:
      cover.save(filename)

  def switchfront(self):
    self.update_model()
    self.tab.setbutton(0)
    self.update_ui()

  def switchback(self):
    self.update_model()
    self.tab.setbutton(1)
    self.update_ui()

  def getindex(self):
    ifilename = tkFileDialog.askopenfilename(defaultextension="txt",
                                             filetypes=[(_("Textfiles"),
                                                         "*.txt"),
                                                        (_("All files"), "*")])
    if len(ifilename) > 0:
      self.tindex.delete(1.0, END)
      for line in fileinput.input(ifilename):
        self.tindex.insert(END, line)

  def autonumber(self):
    i = Index(self.tindex.get(1.0, END))
    i.autonumber()
    self.tindex.delete(1.0, END)
    self.tindex.insert(1.0, i.asString())

  def check(self):
    cover.usedate = self.bdate.get()
    if self.bdate.get():
      format = configuration.date_format
      cover.volume = time.strftime(format, time.localtime(time.time()))
    else:
      cover.volume = self.evolume.get()
    self.update_model()
    self.update_ui()

  def configure(self):
    dlg = ConfigDialog(self.root)


  def write(self, fname):
    self.update_model()
    psfile = PSFile(cover.template, fname)
    return psfile.generate(cover)

  def postscript(self):
    filename = tkFileDialog.asksaveasfilename(defaultextension=".ps",
                                              filetypes=[(_("Postscript files"),
                                                          "*.ps"),
                                                         (_("All files"), "*")])
    if len(filename) > 0:
      self.write(filename)

  def ghostview(self):
    self.tempfile = tempfile.mktemp(".ps")
    if self.write(self.tempfile):
      self.gv = os.path.normpath(configuration.ghostview)
      if os.name == "posix":
        if os.fork() == 0:
          os.execv(self.gv, (self.gv, self.tempfile))
      else:
        os.spawnv(os.P_NOWAIT, self.gv, [ "\"" + self.gv + "\"", self.tempfile ])
#        os.unlink(self.tempfile)

appl = Appl()
