#!/usr/bin/env python

############################################
# IMPORTATIONS DES BIBLIOTHEQUES
############################################
import os.path
from qt import *
import sys
from zipfile import ZipFile

# fabrique de poids mouche
factory = None
# pixmaps des icones employés pour l'arbre
openedDirPixmap = None
closedDirPixmap = None

# constantes pour les widgets de la pile de
# widgets
ID_WIDGET_TEXT_VIEW = 1
ID_WIDGET_PICT_VIEW = 2

# message de bienvenue
welcomeMessage = """<html><body>
<font size="+2" color="#F00000">Welcome To</font> <font size="+2" color="#0000F0">PyViewer</font><br />
<hr /><br /><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<b>PyViewer</b> is a simple yet very useful tool. It
allows to browse the hard drive on which it is executed in order to display the type and the contents
of many files.<br /><br />
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;You can this way check out the content of HTML files,
of plain text files or of picture files.
<br /><br /><br /><br />
<font align="right">(C)2002 Romain GUY - Licensed under GNU GPL</font>
</body></html>"""

############################################
# FABRIQUE POUR LES POIDS MOUCHE
############################################

class FileItemFactory:
  def __init__(self):
    self.files = { }
    self.generic = FileItem("")
    for file in os.listdir("images/"):
      extension = os.path.splitext(file)[0][9:]
      self.files[extension] = FileItem(extension)

  def getPainter(self, extension):
    if extension in self.files:
      return self.files[extension]
    else:
      return self.generic

############################################
# POIDS MOUCHE
############################################

class FileItem:
  # couleurs relatives aux extensions
  colors = { 'zip'   : QColor(0, 128, 64),    'htm' : QColor(128, 0, 128),   'c'   : QColor(128, 128, 255),
             'gz'    : QColor(128, 128, 128), 'tgz' : QColor(128, 128, 128), 'txt' : QColor(0, 0, 192),
             'class' : QColor(255, 128, 128), 'jar' : QColor(64, 255, 64),   'mp3' : QColor(255, 101, 127),
             'exe'   : QColor(100, 166, 255) }

  def __init__(self, extension):
    if os.path.exists("images/file-dot-" + extension + ".png"):
      mime = extension
    else:
      mime = "plain"
    self.thumbnail = QImage("images/file-dot-" + mime + ".png").smoothScale(16, 16)
  
    self.color = None
    if extension in self.colors:
      self.color = self.colors[extension]

  def paint(self, p, cg, width, height, text, selected):
    p.eraseRect(0, 0, width, height)

    if selected:
      p.fillRect(0, 0, width, height, QBrush(cg.color(QColorGroup.Highlight), QBrush.SolidPattern))
      p.setPen(cg.color(QColorGroup.HighlightedText))
    elif self.color:
      p.setPen(self.color)
    else:
      p.setPen(cg.color(QColorGroup.Text))

    p.drawImage(QPoint(2, 0), self.thumbnail)
    p.drawText(22, 13, text)

############################################
# CLASSE FILE
############################################
# Hérite de QListViewItem et affiche les
# fichiers dans diverses couleurs suivant
# l'extension.
############################################

class File(QListViewItem):
  # constructeur
  def __init__(self, parent, name = None):
    QListViewItem.__init__(self, parent)
    group = os.path.splitext(name)
    self.name = group[0]
    self.extension = group[1][1:].lower()
    self.parentDir = parent
    self.painter = factory.getPainter(self.extension)

  # renvoie le nom de l'élément
  def text(self, column):
    return self.name + '.' + self.extension

  # renvoie la clé employée pour le tri des éléments
  # nous avons ici l'extension (en minuscules) suivie
  # du nom du fichier en minuscules. les fichiers sont
  # triés par extension
  def key(self, column, ascending):
    return self.extension + self.name.lower()

  # redéfinit la méthode d'affichage pour utiliser des couleurs
  # spécifiques aux extensions de fichier
  # XXX : améliorer les performances en mettant en cache les thumbnails
  def paintCell(self, p, cg, column, width, align):
    self.painter.paint(p, cg, width, self.height(), self.text(0), self.isSelected())

  # renvoie le chemin complet du fichier
  def getFullName(self):
      return self.parentDir.getFullName() + self.text(0)

############################################
# CLASSE DIRECTORY
############################################
# Hérite de QListViewItem et affiche un
# répertoire
############################################

class Directory(QListViewItem):
  # constructeur
  def __init__(self, parent, name = None):
    QListViewItem.__init__(self, parent)

    # si le parent est une liste, nous affichons
    # la racine du disque
    if isinstance(parent, QListView):
      self.parentDir = None
      self.fullName = '/'
    else:
      self.parentDir = parent
      self.fullName = name

  # renvoie l'icône adéquat suivant si le dossier
  # est ouvert ou fermé
  def pixmap(self, column):
    if self.isOpen():
      return openedDirPixmap
    else:
      return closedDirPixmap

  # ouvre un dossier : nous constituons la
  # liste de ses enfants
  def setOpen(self, o):
    if o and not self.childCount():
      thisDir = QDir(self.getFullName())
      files = thisDir.entryInfoList()
  
      if files:
        for f in files:
          fileName = f.fileName().__unicode__()
          if fileName == '.' or fileName == '..':
            continue
          if f.isDir():
            d = Directory(self, fileName)
          else:
            d = File(self, fileName)
  
    QListViewItem.setOpen(self, o)

  # utilisé pour trier les éléments
  # en ajoutant le zéro devant le nom nous nous
  # assurons que les répertoires apparaîtront
  # dans la liste avant les fichiers
  def key(self, column, ascending):
    return '0' + self.text(column)

  # appelé lors de la création
  def setup(self):
    self.setExpandable(1)
    QListViewItem.setup(self)

  # renvoie le chemin complet de ce répertoire
  def getFullName(self):
    if self.parentDir:
      return self.parentDir.getFullName() + self.fullName + '/'
    else:
      return '/'

  # renvoie le nom de l'élément
  def text(self, column):
    return self.fullName

##########################################################################################

# création de l'application
app = QApplication(sys.argv)

# génération des poids mouche
factory = FileItemFactory()

# chargement des images de l'arbre
openedDirPixmap = QPixmap("images/tree_open.png")
closedDirPixmap = QPixmap("images/tree_close.png")

# splitter principal
stacker = QSplitter(QSplitter.Vertical)
stacker.setRubberband(-1)

# splitter de l'arbre
splitter = QSplitter(stacker)

# zone de statut
status = QHBox(stacker)
status.setSpacing(4)
status.setMaximumSize(2000, 48)
status.setMinimumSize(48, 48)
stacker.setResizeMode(status, QSplitter.KeepSize)

# icône de type
fileType = QLabel(status)
fileType.setPixmap(QPixmap("images/file-dot-plain.png"))
fileType.setMinimumSize(48, 48)
fileType.setMaximumSize(48, 48)

# chemin d'accès
filePath = QLabel(status)
filePath.setText('/')

# l'explorateur
explorer = QListView(splitter)
explorer.addColumn('File')
explorer.header().hide()
explorer.setTreeStepSize(20)
root = Directory(explorer)
root.setOpen(1)

# panneau de visualisation
viewerStack = QWidgetStack(splitter)
textView = QTextView()
scroller = QScrollView()
pictView = QLabel(scroller)
scroller.addChild(pictView)
viewerStack.addWidget(textView, ID_WIDGET_TEXT_VIEW)
viewerStack.addWidget(scroller, ID_WIDGET_PICT_VIEW)
viewerStack.raiseWidget(ID_WIDGET_TEXT_VIEW)

# affiche le type du fichier et son chemin complet
def selectionChanged():
  item = explorer.selectedItem()
  path = item.getFullName()
  filePath.setText(path)
  mime = "plain"

  # s'il s'agit d'un fichier
  if isinstance(item, File):
    # on chercher l'icône correspondante
    if os.path.exists("images/file-dot-" + item.extension + ".png"):
      mime = item.extension

    # on veut afficher le contenu
    file = open(path, "rb")
    lines = file.read()

    # fichier binaire (donc une image ou un zip)
    if '\0' in lines:
      # contenu d'un ZIP
      if item.extension in ('zip', 'jar', ):
        textView.setTextFormat(QTextView.PlainText)
        z = ZipFile(path)
        textView.setText("\n".join(z.namelist()))
        z.close()
        viewerStack.raiseWidget(ID_WIDGET_TEXT_VIEW)
      # image
      else:
        pictView.setPixmap(QPixmap(path))
        viewerStack.raiseWidget(ID_WIDGET_PICT_VIEW)
    # page HTML ou texte simple
    else:
      if item.extension in ('htm', 'html'):
        textView.setTextFormat(QTextView.RichText)
        textView.mimeSourceFactory().setFilePath(QStringList(os.path.split(path)[0]))
      else:
        textView.setTextFormat(QTextView.PlainText)
      textView.setText(lines)
      viewerStack.raiseWidget(ID_WIDGET_TEXT_VIEW)
    file.close()
  fileType.setPixmap(QPixmap("images/file-dot-" + mime + ".png"))

app.connect(explorer, SIGNAL('selectionChanged()'), selectionChanged) 

# exécution de l'application
app.setMainWidget(stacker)
stacker.resize(640, 480)
stacker.show()
textView.setText(welcomeMessage)
app.exec_loop()

# fin de fileviewer.py
