Gestion explicite de KeyboardInterrupt pour être sûr de quitter le jeu sur un ^C.
[minwii.git] / src / minwii / widgets / songfilebrowser.py
index 6117218..4c5b55c 100755 (executable)
@@ -7,6 +7,7 @@ $URL$
 """
 
 import pygame
 """
 
 import pygame
+from pygame.locals import K_RETURN
 from pgu.gui import FileDialog
 import pgu.gui.basic as basic
 import pgu.gui.input as input
 from pgu.gui import FileDialog
 import pgu.gui.basic as basic
 import pgu.gui.input as input
@@ -16,6 +17,9 @@ import pgu.gui.table as table
 import pgu.gui.area as area
 from pgu.gui.const import *
 from pgu.gui.dialog import Dialog
 import pgu.gui.area as area
 from pgu.gui.const import *
 from pgu.gui.dialog import Dialog
+from pgu.gui.app import Desktop
+import types
+from datetime import timedelta
 
 import os
 import tempfile
 
 import os
 import tempfile
@@ -23,6 +27,28 @@ from xml.etree import ElementTree
 from minwii.musicxml import musicXml2Song
 
 INDEX_TXT = 'index.txt'
 from minwii.musicxml import musicXml2Song
 
 INDEX_TXT = 'index.txt'
+PICTURE_ITEM_SIZE = 64
+
+def appEventFactory(app, dlg) :
+    # monkey patch de la méthode gestionnaire d'événements :
+    # l'ensemble du Desktop écoute les événements de la roulette de la souris
+    # et les redirige sur la liste déroulante.
+    def _appEvent(self, e) :
+        
+        if dlg.list.vscrollbar:
+            if not hasattr(dlg.list.vscrollbar,'value'): 
+                return False
+
+            if e.type == pygame.locals.MOUSEBUTTONDOWN:
+                if e.button == 4: #wheel up
+                    dlg.list.vscrollbar._click(-1)
+                    return True
+                elif e.button == 5: #wheel down
+                    dlg.list.vscrollbar._click(1)
+                    return True
+        return Desktop.event(self, e)
+
+    return types.MethodType(_appEvent, app)
 
 class FileOpenDialog(FileDialog):
     
 
 class FileOpenDialog(FileDialog):
     
@@ -34,13 +60,15 @@ class FileOpenDialog(FileDialog):
         else: self.curdir = path
         self.dir_img = basic.Image(
             pguglobals.app.theme.get(cls1+'.folder', '', 'image'))
         else: self.curdir = path
         self.dir_img = basic.Image(
             pguglobals.app.theme.get(cls1+'.folder', '', 'image'))
+        self.soundfile_img = basic.Image(
+            pguglobals.app.theme.get(cls1+'.soundfile', '', 'image'))
         td_style = {'padding_left': 4,
                     'padding_right': 4,
                     'padding_top': 2,
                     'padding_bottom': 2}
         td_style = {'padding_left': 4,
                     'padding_right': 4,
                     'padding_top': 2,
                     'padding_bottom': 2}
-        self.title = basic.Label("Ouvrir un chanson", cls="dialog.title.label")
+        self.title = basic.Label("Ouvrir une chanson", cls="dialog.title.label")
         self.body = table.Table()
         self.body = table.Table()
-        self.list = area.List(width=700, height=250)
+        self.list = area.List(width=880, height=375)
         self.input_dir = input.Input()
         self.input_file = input.Input()
         self._current_sort = 'alpha'
         self.input_dir = input.Input()
         self.input_file = input.Input()
         self._current_sort = 'alpha'
@@ -58,7 +86,7 @@ class FileOpenDialog(FileDialog):
         self.body.tr()
         self.body.td(self.list, colspan=4, style=td_style)
         self.list.connect(CHANGE, self._item_select_changed_, None)
         self.body.tr()
         self.body.td(self.list, colspan=4, style=td_style)
         self.list.connect(CHANGE, self._item_select_changed_, None)
-        self.list.connect(CLICK, self._check_dbl_click_, None)
+        #self.list.connect(CLICK, self._check_dbl_click_, None)
         self._last_time_click = pygame.time.get_ticks()
         self.button_ok.connect(CLICK, self._button_okay_clicked_, None)
         self.body.tr()
         self._last_time_click = pygame.time.get_ticks()
         self.button_ok.connect(CLICK, self._button_okay_clicked_, None)
         self.body.tr()
@@ -68,14 +96,18 @@ class FileOpenDialog(FileDialog):
         self.value = None
         Dialog.__init__(self, self.title, self.body)
         
         self.value = None
         Dialog.__init__(self, self.title, self.body)
         
-#        FileDialog.__init__(self, 
-#                            title_txt="Ouvrir une chanson",
-#                            button_txt="Ouvrir",
-#                            path=path,
-#                            )
-#        self.list.style.width = 700
-#        self.list.style.height = 250
+        # monkey patch
+        app = pguglobals.app
+        self.__regularEventMethod = app.event
+        app.event = appEventFactory(app, self)
     
     
+    def close(self, w=None) :
+        FileDialog.close(self, w)
+        # retrait du monkey patch
+        app = pguglobals.app
+        app.event = self.__regularEventMethod
+        
+            
     def _list_dir_(self):
         self.input_dir.value = self.curdir
         self.input_dir.pos = len(self.curdir)
     def _list_dir_(self):
         self.input_dir.value = self.curdir
         self.input_dir.pos = len(self.curdir)
@@ -103,12 +135,26 @@ class FileOpenDialog(FileDialog):
                 continue
             filepath = os.path.join(self.curdir, i)
             xmlFiles.append(filepath)
                 continue
             filepath = os.path.join(self.curdir, i)
             xmlFiles.append(filepath)
-            # self.list.add(FileOpenDialog.getSongTitle(filepath), value=i)
         
         if xmlFiles :
             printableLines = self.getPrintableLines(xmlFiles)
             for l in printableLines :
         
         if xmlFiles :
             printableLines = self.getPrintableLines(xmlFiles)
             for l in printableLines :
-                self.list.add(l[0], value = l[1])
+                imgpath = os.path.splitext(os.path.join(self.curdir, l[1]))[0] + '.jpg'
+                if os.path.exists(imgpath) :
+                    img = pygame.image.load(imgpath)
+                    iw, ih = img.get_width(), img.get_height()
+                    style = {}
+                    if iw > ih :
+                        style['width'] = PICTURE_ITEM_SIZE
+                        style['height'] = int(round(PICTURE_ITEM_SIZE * float(ih) / iw))
+                    else :
+                        style['heigth'] = PICTURE_ITEM_SIZE
+                        style['width'] = int(round(PICTURE_ITEM_SIZE * float(iw) / ih))
+                        
+                    img = basic.Image(img, style=style)
+                else :
+                    img = self.soundfile_img
+                self.list.add(l[0], value = l[1], image = img)
  
         self.list.set_vertical_scroll(0)
     
  
         self.list.set_vertical_scroll(0)
     
@@ -150,14 +196,29 @@ class FileOpenDialog(FileDialog):
         song = musicXml2Song(file)
         metadata['distinctNotes'] = len(song.distinctNotes)
         
         song = musicXml2Song(file)
         metadata['distinctNotes'] = len(song.distinctNotes)
         
-        histo = song.intervalsHistogram
-        coeffInter = reduce(lambda a, b : a + b,
-                            [abs(k) * v for k, v in histo.items()])
+        duration = song.duration / 1000.
+        duration = int(round(duration, 0))
+        duration = timedelta(seconds=duration)
+        try :
+            duration = str(duration) # p.ex. 0:03:05
+            duration = duration.split(':')
+            h, m, s = [int(n) for n in duration]
+            if h : raise ValueError(h)
+            duration = ':'.join([str(n).zfill(2) for n in (m, s)])
+        except :
+            raise
+            duration = srt(duration)
 
 
-        totInter = reduce(lambda a, b: a+b, histo.values())
-        totInter = totInter - histo.get(0, 0)
-        difficulty = int(round(float(coeffInter) / totInter, 0))
-        metadata['difficulty'] = difficulty
+        metadata['duration'] = duration
+        
+        # histo = song.intervalsHistogram
+        # coeffInter = reduce(lambda a, b : a + b,
+        #                     [abs(k) * v for k, v in histo.items()])
+        # 
+        # totInter = reduce(lambda a, b: a+b, histo.values())
+        # totInter = totInter - histo.get(0, 0)
+        # difficulty = int(round(float(coeffInter) / totInter, 0))
+        # metadata['difficulty'] = difficulty
 
         return metadata
     
 
         return metadata
     
@@ -177,7 +238,7 @@ class FileOpenDialog(FileDialog):
                     if e.args and e.args[0] == 'not a musicxml file' :
                         continue
                 
                     if e.args and e.args[0] == 'not a musicxml file' :
                         continue
                 
-                line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
+                line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(duration)s\n' % metadata
                 index.append(line)
                 tmp.write(line)
             
                 index.append(line)
                 tmp.write(line)
             
@@ -214,7 +275,7 @@ class FileOpenDialog(FileDialog):
                             continue
                     
                     metadata = FileOpenDialog.getSongMetadata(filePath)
                             continue
                     
                     metadata = FileOpenDialog.getSongMetadata(filePath)
-                    line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
+                    line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(duration)s\n' % metadata
                     indexedFiles[fileBaseName] = line
             
             # check for new files.
                     indexedFiles[fileBaseName] = line
             
             # check for new files.
@@ -230,7 +291,7 @@ class FileOpenDialog(FileDialog):
                             continue
                 
                     metadata = FileOpenDialog.getSongMetadata(file)
                             continue
                 
                     metadata = FileOpenDialog.getSongMetadata(file)
-                    line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(difficulty)d\n' % metadata
+                    line = '%(file)s\t%(mtime)s\t%(title)s\t%(distinctNotes)d\t%(duration)s\n' % metadata
                     indexedFiles[fileBaseName] = line
             
             # ok, the index is up to date !
                     indexedFiles[fileBaseName] = line
             
             # ok, the index is up to date !
@@ -265,6 +326,17 @@ class FileOpenDialog(FileDialog):
             self._button_okay_clicked_(None)
         else :
             self._last_time_click = pygame.time.get_ticks()
             self._button_okay_clicked_(None)
         else :
             self._last_time_click = pygame.time.get_ticks()
+    
+    def event(self, e) :
+        FileDialog.event(self, e)
+        
+        if e.type == CLICK and \
+           e.button == 1 and \
+           self.list.rect.collidepoint(e.pos) :
+            self._check_dbl_click_(e)
+        
+        if e.type == KEYDOWN and e.key == K_RETURN :
+            self._button_okay_clicked_(None)
             
 
 # utils
             
 
 # utils
@@ -294,8 +366,8 @@ def _recurseDecomposition(uc):
     fullDeco = u''.join(filter(lambda c : isPrintable(c), fullDeco))
     return fullDeco
 
     fullDeco = u''.join(filter(lambda c : isPrintable(c), fullDeco))
     return fullDeco
 
-def desacc(s) :
-    us = s.decode('utf-8', 'ignore')
+def desacc(s, encoding='iso-8859-1') :
+    us = s.decode(encoding, 'ignore')
     ret = []
     for uc in us :
         ret.append(_recurseDecomposition(uc))
     ret = []
     for uc in us :
         ret.append(_recurseDecomposition(uc))