Guest User

Untitled

a guest
Jan 21st, 2018
92
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 42.96 KB | None | 0 0
  1. #####################################################################
  2. # -*- coding: iso-8859-1 -*-                                        #
  3. #                                                                   #
  4. # Frets on Fire                                                     #
  5. # Copyright (C) 2006 Sami Kyöstilä                                  #
  6. #                                                                   #
  7. # This program is free software; you can redistribute it and/or     #
  8. # modify it under the terms of the GNU General Public License       #
  9. # as published by the Free Software Foundation; either version 2    #
  10. # of the License, or (at your option) any later version.            #
  11. #                                                                   #
  12. # This program is distributed in the hope that it will be useful,   #
  13. # but WITHOUT ANY WARRANTY; without even the implied warranty of    #
  14. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the     #
  15. # GNU General Public License for more details.                      #
  16. #                                                                   #
  17. # You should have received a copy of the GNU General Public License #
  18. # along with this program; if not, write to the Free Software       #
  19. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,        #
  20. # MA  02110-1301, USA.                                              #
  21. #####################################################################
  22.  
  23. """A bunch of dialog functions for interacting with the user."""
  24.  
  25. import pygame
  26. from OpenGL.GL import *
  27. from OpenGL.GLU import *
  28. import math
  29. import os
  30. import fnmatch
  31.  
  32. from View import Layer, BackgroundLayer
  33. from Input import KeyListener
  34. from Camera import Camera
  35. from Mesh import Mesh
  36. from Menu import Menu
  37. from Language import _
  38. from Texture import Texture
  39. import Theme
  40. import Log
  41. import Song
  42. import Data
  43. import Player
  44. import Guitar
  45.  
  46. def wrapText(font, pos, text, rightMargin = 0.9, scale = 0.002, visibility = 0.0, hide = 0, hidestring = ""):
  47.   """
  48.  Wrap a piece of text inside given margins.
  49.  
  50.  @param pos:         (x, y) tuple, x defines the left margin
  51.  @param text:        Text to wrap
  52.  @param rightMargin: Right margin
  53.  @param scale:       Text scale
  54.  @param visibility:  Visibility factor [0..1], 0 is fully visible
  55.  @param hide:        Hide text instead of line wrap
  56.  """
  57.   x, y = pos
  58.   space = font.getStringSize(" ", scale = scale)[0]
  59.   hidew, hideh = font.getStringSize(hidestring, scale = scale)
  60.   rightMargin = rightMargin - hidew
  61.   for n, word in enumerate(text.split(" ")):
  62.     w, h = font.getStringSize(word, scale = scale)
  63.     if x + w > rightMargin and hide:
  64.       word = hidestring
  65.     if (x + w > rightMargin and not hide) or word == "\n":
  66.       x = pos[0]
  67.       y += h
  68.     if word == "\n":
  69.       continue
  70.     glPushMatrix()
  71.     glRotate(visibility * (n + 1) * -45, 0, 0, 1)
  72.     font.render(word, (x, y + visibility * n), scale = scale)
  73.     glPopMatrix()
  74.     if x + w > rightMargin and hide:
  75.       x += hidew + space
  76.       break
  77.     x += w + space
  78.   return (x - space, y)
  79.  
  80. def fadeScreen(v):
  81.   """
  82.  Fade the screen to a dark color to make whatever is on top easier to read.
  83.  
  84.  @param v: Visibility factor [0..1], 0 is fully visible
  85.  """
  86.   glEnable(GL_BLEND)
  87.   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  88.   glEnable(GL_COLOR_MATERIAL)
  89.  
  90.   glBegin(GL_TRIANGLE_STRIP)
  91.   glColor4f(0, 0, 0, .3 - v * .3)
  92.   glVertex2f(0, 0)
  93.   glColor4f(0, 0, 0, .3 - v * .3)
  94.   glVertex2f(1, 0)
  95.   glColor4f(0, 0, 0, .9 - v * .9)
  96.   glVertex2f(0, 1)
  97.   glColor4f(0, 0, 0, .9 - v * .9)
  98.   glVertex2f(1, 1)
  99.   glEnd()
  100.  
  101.  
  102. class GetText(Layer, KeyListener):
  103.   """Text input layer."""
  104.   def __init__(self, engine, prompt = "", text = ""):
  105.     self.text = text
  106.     self.prompt = prompt
  107.     self.engine = engine
  108.     self.time = 0
  109.     self.accepted = False
  110.    
  111.   def shown(self):
  112.     self.engine.input.addKeyListener(self, priority = True)
  113.     self.engine.input.enableKeyRepeat()
  114.    
  115.   def hidden(self):
  116.     self.engine.input.removeKeyListener(self)
  117.     self.engine.input.disableKeyRepeat()
  118.    
  119.   def keyPressed(self, key, unicode):
  120.     self.time = 0
  121.     c = self.engine.input.controls.getMapping(key)
  122.     if (c in [Player.KEY1] or key == pygame.K_RETURN) and not self.accepted:
  123.       self.engine.view.popLayer(self)
  124.       self.accepted = True
  125.     elif c in [Player.CANCEL, Player.KEY2] and not self.accepted:
  126.       self.text = None
  127.       self.engine.view.popLayer(self)
  128.       self.accepted = True
  129.     elif key == pygame.K_BACKSPACE and not self.accepted:
  130.       self.text = self.text[:-1]
  131.     elif unicode and ord(unicode) > 31 and not self.accepted:
  132.       self.text += str(unicode)
  133.     return True
  134.    
  135.   def run(self, ticks):
  136.     self.time += ticks / 50.0
  137.  
  138.   def render(self, visibility, topMost):
  139.     self.engine.view.setOrthogonalProjection(normalize = True)
  140.     font = self.engine.data.font
  141.    
  142.     try:
  143.       v = (1 - visibility) ** 2
  144.      
  145.       fadeScreen(v)
  146.       Theme.setBaseColor(1 - v)
  147.  
  148.       if (self.time % 10) < 5 and visibility > .9:
  149.         cursor = "|"
  150.       else:
  151.         cursor = ""
  152.  
  153.       pos = wrapText(font, (.1, .33 - v), self.prompt)
  154.  
  155.       Theme.setSelectedColor(1 - v)
  156.      
  157.       if self.text is not None:
  158.         pos = wrapText(font, (.1, (pos[1] + v) + .08 + v / 4), self.text)
  159.         font.render(cursor, pos)
  160.      
  161.     finally:
  162.       self.engine.view.resetProjection()
  163.  
  164. class GetKey(Layer, KeyListener):
  165.   """Key choosing layer."""
  166.   def __init__(self, engine, prompt = "", key = None):
  167.     self.key = key
  168.     self.prompt = prompt
  169.     self.engine = engine
  170.     self.time = 0
  171.     self.accepted = False
  172.    
  173.   def shown(self):
  174.     self.engine.input.addKeyListener(self, priority = True)
  175.    
  176.   def hidden(self):
  177.     self.engine.input.removeKeyListener(self)
  178.    
  179.   def keyPressed(self, key, unicode):
  180.     c = self.engine.input.controls.getMapping(key)
  181.     if c in [Player.CANCEL, Player.KEY2] and not self.accepted:
  182.       self.key = None
  183.       self.engine.view.popLayer(self)
  184.       self.accepted = True
  185.     elif not self.accepted:
  186.       self.key = key
  187.       self.engine.view.popLayer(self)
  188.       self.accepted = True
  189.     return True
  190.    
  191.   def run(self, ticks):
  192.     self.time += ticks / 50.0
  193.  
  194.   def render(self, visibility, topMost):
  195.     self.engine.view.setOrthogonalProjection(normalize = True)
  196.     font = self.engine.data.font
  197.    
  198.     try:
  199.       v = (1 - visibility) ** 2
  200.      
  201.       fadeScreen(v)
  202.       Theme.setBaseColor(1 - v)
  203.  
  204.       pos = wrapText(font, (.1, .33 - v), self.prompt)
  205.  
  206.       Theme.setSelectedColor(1 - v)
  207.  
  208.       if self.key is not None:
  209.         text = pygame.key.name(self.key).capitalize()
  210.         pos = wrapText(font, (.1, (pos[1] + v) + .08 + v / 4), text)
  211.      
  212.     finally:
  213.       self.engine.view.resetProjection()
  214.  
  215. class LoadingScreen(Layer, KeyListener):
  216.   """Loading screen layer."""
  217.   def __init__(self, engine, condition, text, allowCancel = False):
  218.     self.engine       = engine
  219.     self.text         = text
  220.     self.condition    = condition
  221.     self.ready        = False
  222.     self.allowCancel  = allowCancel
  223.     self.time         = 0.0
  224.  
  225.   def shown(self):
  226.     self.engine.input.addKeyListener(self, priority = True)
  227.  
  228.   def keyPressed(self, key, unicode):
  229.     c = self.engine.input.controls.getMapping(key)
  230.     if self.allowCancel and c == Player.CANCEL:
  231.       self.engine.view.popLayer(self)
  232.     return True
  233.    
  234.   def hidden(self):
  235.     self.engine.boostBackgroundThreads(False)
  236.     self.engine.input.removeKeyListener(self)
  237.  
  238.   def run(self, ticks):
  239.     self.time += ticks / 50.0
  240.     if not self.ready and self.condition():
  241.       self.engine.view.popLayer(self)
  242.       self.ready = True
  243.  
  244.   def render(self, visibility, topMost):
  245.     self.engine.view.setOrthogonalProjection(normalize = True)
  246.     font = self.engine.data.font
  247.  
  248.     if not font:
  249.       return
  250.  
  251.     if visibility > 0.9:
  252.       self.engine.boostBackgroundThreads(True)
  253.     else:
  254.       self.engine.boostBackgroundThreads(False)
  255.    
  256.     try:
  257.       v = (1 - visibility) ** 2
  258.       fadeScreen(v)
  259.  
  260.       w, h = self.engine.view.geometry[2:4]
  261.       self.engine.data.loadingImage.transform.reset()
  262.       self.engine.data.loadingImage.transform.translate(w / 2, (1.0 - v * .25) * h / 2)
  263.       self.engine.data.loadingImage.transform.scale(1, -1)
  264.       self.engine.data.loadingImage.draw(color = (1, 1, 1, visibility))
  265.  
  266.       Theme.setBaseColor(1 - v)
  267.       w, h = font.getStringSize(self.text)
  268.       x = .5 - w / 2
  269.       y = .6 - h / 2 + v * .5
  270.      
  271.       font.render(self.text, (x, y))
  272.      
  273.     finally:
  274.       self.engine.view.resetProjection()
  275.  
  276. class MessageScreen(Layer, KeyListener):
  277.   """Message screen layer."""
  278.   def __init__(self, engine, text, prompt = _("<OK>")):
  279.     self.engine = engine
  280.     self.text = text
  281.     self.time = 0.0
  282.     self.prompt = prompt
  283.  
  284.   def shown(self):
  285.     self.engine.input.addKeyListener(self, priority = True)
  286.  
  287.   def keyPressed(self, key, unicode):
  288.     c = self.engine.input.controls.getMapping(key)
  289.     if c in [Player.KEY1, Player.KEY2, Player.CANCEL] or key == pygame.K_RETURN:
  290.       self.engine.view.popLayer(self)
  291.     return True
  292.    
  293.   def hidden(self):
  294.     self.engine.input.removeKeyListener(self)
  295.  
  296.   def run(self, ticks):
  297.     self.time += ticks / 50.0
  298.  
  299.   def render(self, visibility, topMost):
  300.     self.engine.view.setOrthogonalProjection(normalize = True)
  301.     font = self.engine.data.font
  302.  
  303.     if not font:
  304.       return
  305.    
  306.     try:
  307.       v = (1 - visibility) ** 2
  308.       fadeScreen(v)
  309.  
  310.       x = .1
  311.       y = .3 + v * 2
  312.       Theme.setBaseColor(1 - v)
  313.       pos = wrapText(font, (x, y), self.text, visibility = v)
  314.  
  315.       w, h = font.getStringSize(self.prompt, scale = 0.001)
  316.       x = .5 - w / 2
  317.       y = pos[1] + 3 * h + v * 2
  318.       Theme.setSelectedColor(1 - v)
  319.       font.render(self.prompt, (x, y), scale = 0.001)
  320.      
  321.     finally:
  322.       self.engine.view.resetProjection()
  323.      
  324. class SongChooser(Layer, KeyListener):
  325.   """Song choosing layer."""
  326.   def __init__(self, engine, prompt = "", selectedSong = None, selectedLibrary = None):
  327.     self.prompt         = prompt
  328.     self.engine         = engine
  329.     self.time           = 0
  330.     self.accepted       = False
  331.     self.selectedIndex  = 0
  332.     self.camera         = Camera()
  333.     self.cassetteHeight = .8
  334.     self.cassetteWidth  = 4.0
  335.     self.libraryHeight  = 1.2
  336.     self.libraryWidth   = 4.0
  337.     self.itemAngles     = None
  338.     self.itemLabels     = None
  339.     self.selectedOffset = 0.0
  340.     self.cameraOffset   = 0.0
  341.     self.selectedItem   = None
  342.     self.song           = None
  343.     self.songCountdown  = 1024
  344.     self.songLoader     = None
  345.     self.initialItem    = selectedSong
  346.     self.library        = selectedLibrary
  347.     self.searchText     = ""
  348.     self.playSongName   = ""
  349.  
  350.     self.cassetteShow   = not self.engine.config.get("game", "compactlist")
  351.     self.autoPreview    = self.engine.config.get("game", "autopreview")
  352.     self.artistSort     = self.engine.config.get("game", "artistsort")
  353.     # Use the default library if this one doesn't exist
  354.     if not self.library or not os.path.isdir(self.engine.resource.fileName(self.library)):
  355.       self.library = Song.DEFAULT_LIBRARY
  356.  
  357.     self.loadCollection()
  358.     self.engine.resource.load(self, "cassette",     lambda: Mesh(self.engine.resource.fileName("cassette.dae")), synch = True)
  359.     self.engine.resource.load(self, "label",        lambda: Mesh(self.engine.resource.fileName("label.dae")), synch = True)
  360.     self.engine.resource.load(self, "libraryMesh",  lambda: Mesh(self.engine.resource.fileName("library.dae")), synch = True)
  361.     self.engine.resource.load(self, "libraryLabel", lambda: Mesh(self.engine.resource.fileName("library_label.dae")), synch = True)
  362.    
  363.     self.engine.loadSvgDrawing(self, "background", "cassette.svg")
  364.  
  365.   def loadCollection(self):
  366.     self.loaded = False
  367.     self.engine.resource.load(self, "libraries", lambda: Song.getAvailableLibraries(self.engine, self.library), onLoad = self.libraryListLoaded)
  368.     showLoadingScreen(self.engine, lambda: self.loaded, text = _("Browsing Collection..."))
  369.  
  370.   def libraryListLoaded(self, libraries):
  371.     self.engine.resource.load(self, "songs",     lambda: Song.getAvailableSongs(self.engine, self.library), onLoad = self.songListLoaded)
  372.  
  373.   def songListLoaded(self, songs):
  374.     if self.songLoader:
  375.       self.songLoader.cancel()
  376.     self.selectedIndex = 0
  377.     self.items         = self.libraries + self.songs
  378.     self.itemAngles    = [0.0] * len(self.items)
  379.     self.itemLabels    = [None] * len(self.items)
  380.     self.loaded        = True
  381.     self.searchText    = ""
  382.     if self.initialItem is not None:
  383.       for i, item in enumerate(self.items):
  384.         if isinstance(item, Song.SongInfo) and self.initialItem == item.songName:
  385.           self.selectedIndex =  i
  386.           break
  387.         elif isinstance(item, Song.LibraryInfo) and self.initialItem == item.libraryName:
  388.           self.selectedIndex =  i
  389.           break
  390.     # Load labels for libraries right away
  391.     for i, item in enumerate(self.items):
  392.       if isinstance(item, Song.LibraryInfo):
  393.         self.loadItemLabel(i)
  394.     self.updateSelection()
  395.    
  396.   def shown(self):
  397.     self.engine.input.addKeyListener(self, priority = True)
  398.     self.engine.input.enableKeyRepeat()
  399.    
  400.   def hidden(self):
  401.     if self.songLoader:
  402.       self.songLoader.cancel()
  403.     if self.song:
  404.       self.song.fadeout(1000)
  405.       self.song = None
  406.     self.engine.input.removeKeyListener(self)
  407.     self.engine.input.disableKeyRepeat()
  408.    
  409.   def getSelectedSong(self):
  410.     if isinstance(self.selectedItem, Song.SongInfo):
  411.       return self.selectedItem.songName
  412.  
  413.   def getSelectedLibrary(self):
  414.     return self.library
  415.  
  416.   def loadItemLabel(self, i):
  417.     # Load the item label if it isn't yet loaded
  418.     item = self.items[i]
  419.     if self.itemLabels[i] is None:
  420.       if isinstance(item, Song.SongInfo):
  421.         label = self.engine.resource.fileName(self.library, item.songName,    "label.png")
  422.       else:
  423.         assert isinstance(item, Song.LibraryInfo)
  424.         label = self.engine.resource.fileName(item.libraryName, "label.png")
  425.       if os.path.exists(label):
  426.         self.itemLabels[i] = Texture(label)
  427.  
  428.   def updateSelection(self):
  429.     self.selectedItem  = self.items[self.selectedIndex]
  430.     self.songCountdown = 1024
  431.     self.loadItemLabel(self.selectedIndex)
  432.    
  433.   def keyPressed(self, key, unicode):
  434.     if not self.items or self.accepted:
  435.       return
  436.  
  437.     c = self.engine.input.controls.getMapping(key)
  438.     if c in [Player.KEY1] or key == pygame.K_RETURN:
  439.       if self.matchesSearch(self.selectedItem):
  440.         if isinstance(self.selectedItem, Song.LibraryInfo):
  441.           self.library     = self.selectedItem.libraryName
  442.           self.initialItem = None
  443.           self.loadCollection()
  444.         else:
  445.           self.engine.view.popLayer(self)
  446.           self.accepted = True
  447.         if not self.song and self.autoPreview:
  448.           self.engine.data.acceptSound.play()
  449.     elif c in [Player.CANCEL, Player.KEY2]:
  450.       if self.library != Song.DEFAULT_LIBRARY:
  451.         self.initialItem = self.library
  452.         self.library     = os.path.dirname(self.library)
  453.         self.loadCollection()
  454.       else:
  455.         self.selectedItem = None
  456.         self.engine.view.popLayer(self)
  457.         self.accepted = True
  458.       if not self.song:
  459.         self.engine.data.cancelSound.play()
  460.     elif c in [Player.UP, Player.ACTION1]:
  461.       if self.matchesSearch(self.items[self.selectedIndex]):
  462.         while 1:
  463.           self.selectedIndex = (self.selectedIndex - 1) % len(self.items)
  464.           if self.matchesSearch(self.items[self.selectedIndex]):
  465.             break
  466.       self.updateSelection()
  467.       if not self.song and self.autoPreview:
  468.         self.engine.data.selectSound.play()
  469.     elif c in [Player.DOWN, Player.ACTION2]:
  470.       if self.matchesSearch(self.items[self.selectedIndex]):
  471.         while 1:
  472.           self.selectedIndex = (self.selectedIndex + 1) % len(self.items)
  473.           if self.matchesSearch(self.items[self.selectedIndex]):
  474.             break
  475.       self.updateSelection()
  476.       if not self.song and self.autoPreview:
  477.         self.engine.data.selectSound.play()
  478.     elif key == pygame.K_PAGEUP:
  479.       if self.matchesSearch(self.items[self.selectedIndex]):
  480.         while 1:
  481.           self.selectedIndex = (self.selectedIndex - 10) % len(self.items)
  482.           if self.matchesSearch(self.items[self.selectedIndex]):
  483.             break
  484.       self.updateSelection()
  485.       if not self.song and self.autoPreview:
  486.         self.engine.data.selectSound.play()
  487.     elif key == pygame.K_PAGEDOWN:
  488.       if self.matchesSearch(self.items[self.selectedIndex]):
  489.         while 1:
  490.           self.selectedIndex = (self.selectedIndex + 10) % len(self.items)
  491.           if self.matchesSearch(self.items[self.selectedIndex]):
  492.             break
  493.       self.updateSelection()
  494.       if not self.song and self.autoPreview:
  495.        self.engine.data.selectSound.play()
  496.     elif key == pygame.K_BACKSPACE and not self.accepted:
  497.       self.searchText = self.searchText[:-1]
  498.       self.doSearch()
  499.     elif key == pygame.K_SPACE:
  500.       if self.playSongName == self.getSelectedSong():
  501.         self.playSongName = ""
  502.         self.song.fadeout(1000)
  503.       else:
  504.         self.playSelectedSong(forceplay=1)
  505.     elif key == pygame.K_HOME:
  506.       self.artistSort = (self.artistSort + 1) % 2
  507.       if self.artistSort:
  508.         self.items.sort(key=lambda l: (l.artist.lower() if isinstance(l, Song.SongInfo) else '0'))
  509.       else:
  510.         self.items.sort(key=lambda l: (l.name.lower()))
  511.     elif key == pygame.K_TAB:
  512.       self.cassetteShow = not self.cassetteShow
  513.     elif unicode and ord(unicode) > 31 and not self.accepted:
  514.       self.searchText += str(unicode)
  515.       self.doSearch()
  516.     return True
  517.  
  518.   def matchesSearch(self, item):
  519.     if not self.searchText:
  520.       return True
  521.     if isinstance(item, Song.SongInfo):
  522.       if self.searchText.lower() in item.name.lower() or self.searchText.lower() in item.artist.lower():
  523.         return True
  524.     elif isinstance(item, Song.LibraryInfo):
  525.       if self.searchText.lower() in item.name.lower():
  526.         return True
  527.     return False
  528.  
  529.   def doSearch(self):
  530.     if not self.searchText:
  531.       return
  532.      
  533.     for i, item in enumerate(self.items):
  534.       if self.matchesSearch(item):
  535.           self.selectedIndex =  i
  536.           self.updateSelection()
  537.           break
  538.  
  539.   def songLoaded(self, song):
  540.     self.songLoader = None
  541.  
  542.     if self.song:
  543.       self.song.stop()
  544.    
  545.     song.setGuitarVolume(self.engine.config.get("audio", "guitarvol"))
  546.     song.setBackgroundVolume(self.engine.config.get("audio", "songvol"))
  547.     song.setRhythmVolume(self.engine.config.get("audio", "rhythmvol"))
  548.     song.play()
  549.     self.song = song
  550.  
  551.   def playSelectedSong(self, forceplay = 0):
  552.     song = self.getSelectedSong()
  553.     if not song or (not self.autoPreview and not forceplay):
  554.       return
  555.    
  556.     if self.songLoader:
  557.       self.songLoader.cancel()
  558.       # Don't start a new song loader until the previous one is finished
  559.       if self.songLoader.isAlive():
  560.         self.songCountdown = 256
  561.         return
  562.  
  563.     if self.song:
  564.       self.playSongName = ""
  565.       self.song.fadeout(1000)
  566.       self.song = None
  567.  
  568.     self.songLoader = self.engine.resource.load(self, None, lambda: Song.loadSong(self.engine, song, playbackOnly = True, library = self.library),
  569.                                                 onLoad = self.songLoaded)
  570.     self.playSongName = self.getSelectedSong()
  571.    
  572.   def run(self, ticks):
  573.     self.time += ticks / 50.0
  574.  
  575.     if self.songCountdown > 0:
  576.       self.songCountdown -= ticks
  577.       if self.songCountdown <= 0:
  578.         self.playSelectedSong()
  579.  
  580.     d = self.cameraOffset - self.selectedOffset
  581.     self.cameraOffset -= d * ticks / 192.0
  582.    
  583.     for i in range(len(self.itemAngles)):
  584.       if i == self.selectedIndex:
  585.         self.itemAngles[i] = min(90, self.itemAngles[i] + ticks / 2.0)
  586.       else:
  587.         self.itemAngles[i] = max(0,  self.itemAngles[i] - ticks / 2.0)
  588.    
  589.   def renderCassette(self, color, label):
  590.     if not self.cassette:
  591.       return
  592.  
  593.     if color:
  594.       glColor3f(*color)
  595.  
  596.     glEnable(GL_COLOR_MATERIAL)
  597.     self.cassette.render("Mesh_001")
  598.     glColor3f(.1, .1, .1)
  599.     self.cassette.render("Mesh")
  600.  
  601.     # Draw the label if there is one
  602.     if label is not None:
  603.       glEnable(GL_TEXTURE_2D)
  604.       label.bind()
  605.       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
  606.       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
  607.       glColor3f(1, 1, 1)
  608.       glMatrixMode(GL_TEXTURE)
  609.       glScalef(1, -1, 1)
  610.       glMatrixMode(GL_MODELVIEW)
  611.       self.label.render("Mesh_001")
  612.       glMatrixMode(GL_TEXTURE)
  613.       glLoadIdentity()
  614.       glMatrixMode(GL_MODELVIEW)
  615.       glDisable(GL_TEXTURE_2D)
  616.  
  617.   def renderLibrary(self, color, label):
  618.     if not self.libraryMesh:
  619.       return
  620.  
  621.     if color:
  622.       glColor3f(*color)
  623.  
  624.     glEnable(GL_NORMALIZE)
  625.     glEnable(GL_COLOR_MATERIAL)
  626.     self.libraryMesh.render("Mesh_001")
  627.     glColor3f(.1, .1, .1)
  628.     self.libraryMesh.render("Mesh")
  629.  
  630.     # Draw the label if there is one
  631.     if label is not None:
  632.       glEnable(GL_TEXTURE_2D)
  633.       label.bind()
  634.       glColor3f(1, 1, 1)
  635.       glMatrixMode(GL_TEXTURE)
  636.       glScalef(1, -1, 1)
  637.       glMatrixMode(GL_MODELVIEW)
  638.       self.libraryLabel.render()
  639.       glMatrixMode(GL_TEXTURE)
  640.       glLoadIdentity()
  641.       glMatrixMode(GL_MODELVIEW)
  642.       glDisable(GL_TEXTURE_2D)
  643.     glDisable(GL_NORMALIZE)
  644.  
  645.   def render(self, visibility, topMost):
  646.     v = (1 - visibility) ** 2
  647.  
  648.     # render the background
  649.     t = self.time / 100
  650.     w, h, = self.engine.view.geometry[2:4]
  651.     r = .5
  652.     self.background.transform.reset()
  653.     self.background.transform.translate(v * 2 * w + w / 2 + math.sin(t / 2) * w / 2 * r, h / 2 + math.cos(t) * h / 2 * r)
  654.     self.background.transform.rotate(-t)
  655.     self.background.transform.scale(math.sin(t / 8) + 2, math.sin(t / 8) + 2)
  656.     self.background.draw()
  657.      
  658.     x = .6
  659.     y = .15
  660.  
  661.     if self.cassetteShow:
  662.       # render the item list
  663.       try:
  664.         glMatrixMode(GL_PROJECTION)
  665.         glPushMatrix()
  666.         glLoadIdentity()
  667.         gluPerspective(60, self.engine.view.aspectRatio, 0.1, 1000)
  668.         glMatrixMode(GL_MODELVIEW)
  669.         glLoadIdentity()
  670.      
  671.         glEnable(GL_DEPTH_TEST)
  672.         glDisable(GL_CULL_FACE)
  673.         glDepthMask(1)
  674.      
  675.         offset = 10 * (v ** 2)
  676.         self.camera.origin = (-10 + offset, -self.cameraOffset, 4   + offset)
  677.         self.camera.target = (  0 + offset, -self.cameraOffset, 2.5 + offset)
  678.         self.camera.apply()
  679.      
  680.         y = 0.0
  681.         for i, item in enumerate(self.items):
  682.           if not self.matchesSearch(item):
  683.             continue
  684.        
  685.           c = math.sin(self.itemAngles[i] * math.pi / 180)
  686.        
  687.           if isinstance(item, Song.SongInfo):
  688.             h = c * self.cassetteWidth + (1 - c) * self.cassetteHeight
  689.           else:
  690.             h = c * self.libraryWidth + (1 - c) * self.libraryHeight
  691.        
  692.           d = (y + h * .5 + self.camera.origin[1]) / (4 * (self.camera.target[2] - self.camera.origin[2]))
  693.  
  694.           if i == self.selectedIndex:
  695.             self.selectedOffset = y + h / 2
  696.             Theme.setSelectedColor()
  697.           else:
  698.             Theme.setBaseColor()
  699.          
  700.           glTranslatef(0, -h / 2, 0)
  701.        
  702.           glPushMatrix()
  703.           if abs(d) < 1.2:
  704.             if isinstance(item, Song.SongInfo):
  705.               glRotate(self.itemAngles[i], 0, 0, 1)
  706.               self.renderCassette(item.cassetteColor, self.itemLabels[i])
  707.             elif isinstance(item, Song.LibraryInfo):
  708.               glRotate(-self.itemAngles[i], 0, 0, 1)
  709.               if i == self.selectedIndex:
  710.                 glRotate(self.time * 4, 1, 0, 0)
  711.               self.renderLibrary(item.color, self.itemLabels[i])
  712.           glPopMatrix()
  713.        
  714.           glTranslatef(0, -h / 2, 0)
  715.           y += h
  716.  
  717.         glDisable(GL_DEPTH_TEST)
  718.         glDisable(GL_CULL_FACE)
  719.         glDepthMask(0)
  720.      
  721.       finally:
  722.         glMatrixMode(GL_PROJECTION)
  723.         glPopMatrix()
  724.         glMatrixMode(GL_MODELVIEW)
  725.     else:
  726.       self.engine.view.setOrthogonalProjection(normalize = True)
  727.       font = self.engine.data.font
  728.    
  729.       try:
  730.         glEnable(GL_BLEND)
  731.         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  732.         glEnable(GL_COLOR_MATERIAL)
  733.         n = (0, 0)
  734.         glBegin(GL_QUADS)
  735.         glColor4f(0,0,0, .2)
  736.         glVertex2f(.04, .02)
  737.         glVertex2f(.04, .652)
  738.         glVertex2f(.58, .652)
  739.         glVertex2f(.58, .02)
  740.         glEnd()
  741.         glBegin(GL_LINE_LOOP)
  742.         Theme.setBaseColor(1 - v)
  743.         glVertex2f(.04, .02)
  744.         glVertex2f(.04, .652)
  745.         glVertex2f(.58, .652)
  746.         glVertex2f(.58, .02)
  747.         glEnd()
  748.  
  749.         length = 0
  750.         select = 0
  751.         it = 0
  752.         for i, item in enumerate(self.items):
  753.           if not self.matchesSearch(item):
  754.             continue
  755.           if isinstance(item, Song.SongInfo) or isinstance(item, Song.LibraryInfo):
  756.             length+=1
  757.             if self.selectedIndex == i:
  758.               select = length
  759.  
  760.         Theme.setSelectedColor(1 - v)
  761.         scale = 0.0008
  762.         for i, item in enumerate(self.items):
  763.           if not self.matchesSearch(item):
  764.             continue
  765.           if isinstance(item, Song.SongInfo) or isinstance(item, Song.LibraryInfo):
  766.             it+=1
  767.             if it >= (select - 5) or it >= (select + 11):
  768.               if self.selectedIndex == i:
  769.                 glBegin(GL_QUADS)
  770.                 glColor4f(1,1,1, .1)
  771.               else:
  772.                 glBegin(GL_QUADS)
  773.                 if it % 2 == 0:
  774.                   glColor4f(0,0,0, .3)
  775.                 else:
  776.                   glColor4f(0,0,0, .5)
  777.               glVertex2f(.045, n[1] + font.getHeight() * scale)
  778.               glVertex2f(.045, n[1] + 3*font.getHeight() * scale)
  779.               glVertex2f(.575, n[1] + 3*font.getHeight() * scale)
  780.               glVertex2f(.575, n[1] + font.getHeight() * scale)
  781.               glEnd()
  782.               Theme.setSelectedColor(1 - v)
  783.               if self.artistSort:
  784.                 n = wrapText(font, (.05, n[1] + font.getHeight() * scale), item.artist if isinstance(item, Song.SongInfo) else _("Songs library"), 0.57, visibility = 0.0, scale = scale, hide = 1, hidestring = "...")
  785.                 Theme.setBaseColor(1 - v)
  786.                 n = wrapText(font, (.07, n[1] + font.getHeight() * scale), item.name, 0.57, visibility = 0.0, scale = scale, hide = 1, hidestring = "..." )
  787.               else:
  788.                 n = wrapText(font, (.05, n[1] + font.getHeight() * scale), item.name, 0.57, visibility = 0.0, scale = scale, hide = 1, hidestring = "...")
  789.                 Theme.setBaseColor(1 - v)
  790.                 n = wrapText(font, (.07, n[1] + font.getHeight() * scale), item.artist if isinstance(item, Song.SongInfo) else _("Songs library"), 0.57, visibility = 0.0, scale = scale, hide = 1, hidestring = "..." )
  791.               if ((n[1] + 2*font.getHeight() * scale) >= .65):
  792.                 break
  793.  
  794.         # draw the scrollbar
  795.         perc = float(select - 1)/float(length - 1) if length > 1 else 0
  796.         glBegin(GL_QUADS)
  797.         Theme.setBaseColor(1 - v)
  798.         glVertex2f(.575, .02 + .59*perc)
  799.         Theme.setBaseColor((1 - v) * .3)
  800.         glVertex2f(.575, .02 + .59*perc + .04)
  801.         Theme.setBaseColor(1 - v)
  802.         glColor4f(1,1,1,.8)
  803.         Theme.setBaseColor((1 - v) * .3)
  804.         glVertex2f(.586, .02 + .59*perc + .04)
  805.         glVertex2f(.586, .02 + .59*perc)
  806.         glEnd()
  807.  
  808.       finally:
  809.         self.engine.view.resetProjection()
  810.     self.engine.view.setOrthogonalProjection(normalize = True)
  811.     font = self.engine.data.font
  812.     # render the song info
  813.     try:
  814.       glEnable(GL_BLEND)
  815.       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  816.       glEnable(GL_COLOR_MATERIAL)
  817.       Theme.setBaseColor(1 - v)
  818.  
  819.       if self.searchText:
  820.         text = _("Filter: %s") % (self.searchText) + "|"
  821.         if not self.matchesSearch(self.items[self.selectedIndex]):
  822.           text += " (%s)" % _("Not found")
  823.         font.render(text, (.05, .7 + v), scale = 0.001)
  824.       elif self.songLoader:
  825.         font.render(_("Loading Preview..."), (.05, .7 + v), scale = 0.001)
  826.       elif not self.autoPreview:
  827.         font.render(_("Press Space to Preview"), (.05, .7 + v), scale = 0.001)
  828.  
  829.       x = .6
  830.       y = .15
  831.       font.render(self.prompt, (x, .05 - v))
  832.  
  833.       Theme.setSelectedColor(1 - v)
  834.      
  835.       item  = self.items[self.selectedIndex]
  836.  
  837.       if self.matchesSearch(item):
  838.         angle = self.itemAngles[self.selectedIndex]
  839.         f = ((90.0 - angle) / 90.0) ** 2
  840.         pos = wrapText(font, (x, y), item.name, visibility = f, scale = 0.0016)
  841.  
  842.         if isinstance(item, Song.SongInfo):
  843.           Theme.setBaseColor(1 - v)
  844.           wrapText(font, (x, pos[1] + font.getHeight() * 0.0016), item.artist, visibility = f, scale = 0.0016)
  845.  
  846.           Theme.setSelectedColor(1 - v)
  847.           scale = 0.0011
  848.           w, h = font.getStringSize(self.prompt, scale = scale)
  849.           x = .6
  850.           y = .5 + f / 2.0
  851.           if len(item.difficulties) > 3:
  852.             y = .42 + f / 2.0
  853.            
  854.           for d in item.difficulties:
  855.             scores = item.getHighscores(d)
  856.             if scores:
  857.               score, stars, name = scores[0]
  858.             else:
  859.               score, stars, name = "---", 0, "---"
  860.             Theme.setBaseColor(1 - v)
  861.             font.render(unicode(d),     (x, y),           scale = scale)
  862.             font.render(unicode(Data.STAR2 * stars + Data.STAR1 * (5 - stars)), (x, y + h), scale = scale * .9)
  863.             Theme.setSelectedColor(1 - v)
  864.             font.render(unicode(score), (x + .15, y),     scale = scale)
  865.             font.render(name,       (x + .15, y + h),     scale = scale)
  866.             y += 2 * h + f / 4.0
  867.         elif isinstance(item, Song.LibraryInfo):
  868.           Theme.setBaseColor(1 - v)
  869.           if item.songCount == 1:
  870.             songCount = _("One song in this library")
  871.           else:
  872.             songCount = _("%d songs in this library") % item.songCount
  873.           wrapText(font, (x, pos[1] + 3 * font.getHeight() * 0.0016), songCount, visibility = f, scale = 0.0016)
  874.     finally:
  875.       self.engine.view.resetProjection()
  876.  
  877. class FileChooser(BackgroundLayer, KeyListener):
  878.   """File choosing layer."""
  879.   def __init__(self, engine, masks, path, prompt = ""):
  880.     self.masks          = masks
  881.     self.path           = path
  882.     self.prompt         = prompt
  883.     self.engine         = engine
  884.     self.accepted       = False
  885.     self.selectedFile   = None
  886.     self.time           = 0.0
  887.     self.menu           = None
  888.  
  889.     self.engine.loadSvgDrawing(self, "background", "editor.svg")
  890.  
  891.   def _getFileCallback(self, fileName):
  892.     return lambda: self.chooseFile(fileName)
  893.  
  894.   def _getFileText(self, fileName):
  895.     f = os.path.join(self.path, fileName)
  896.     if fileName == "..":
  897.       return _("[Parent Folder]")
  898.     if os.path.isdir(f):
  899.       return _("%s [Folder]") % fileName
  900.     return fileName
  901.  
  902.   def getFiles(self):
  903.     files = [".."]
  904.     for fn in os.listdir(self.path):
  905.       if fn.startswith("."): continue
  906.       f = os.path.join(self.path, fn)
  907.       for mask in self.masks:
  908.         if fnmatch.fnmatch(fn, mask):
  909.           break
  910.       else:
  911.         if not os.path.isdir(f):
  912.           continue
  913.       files.append(fn)
  914.     files.sort()
  915.     return files
  916.  
  917.   def updateFiles(self):
  918.     if self.menu:
  919.       self.engine.view.popLayer(self.menu)
  920.     self.menu = Menu(self.engine, choices = [(self._getFileText(f), self._getFileCallback(f)) for f in self.getFiles()], onClose = self.close, onCancel = self.cancel)
  921.     self.engine.view.pushLayer(self.menu)
  922.  
  923.   def chooseFile(self, fileName):
  924.     path = os.path.abspath(os.path.join(self.path, fileName))
  925.     if os.path.isdir(path):
  926.       self.path = path
  927.       self.updateFiles()
  928.       return
  929.     self.selectedFile = path
  930.     accepted = True
  931.     self.engine.view.popLayer(self.menu)
  932.     self.engine.view.popLayer(self)
  933.     self.menu = None
  934.    
  935.   def cancel(self):
  936.     self.accepted = True
  937.     self.engine.view.popLayer(self)
  938.  
  939.   def close(self):
  940.     if not self.menu:
  941.       self.accepted = True
  942.       self.engine.view.popLayer(self)
  943.    
  944.   def shown(self):
  945.     self.updateFiles()
  946.    
  947.   def getSelectedFile(self):
  948.     return self.selectedFile
  949.  
  950.   def run(self, ticks):
  951.     self.time += ticks / 50.0
  952.    
  953.   def render(self, visibility, topMost):
  954.     v = (1 - visibility) ** 2
  955.  
  956.     # render the background    
  957.     t = self.time / 100
  958.     w, h, = self.engine.view.geometry[2:4]
  959.     r = .5
  960.     self.background.transform.reset()
  961.     self.background.transform.translate(v * 2 * w + w / 2 + math.sin(t / 2) * w / 2 * r, h / 2 + math.cos(t) * h / 2 * r)
  962.     self.background.transform.rotate(-t)
  963.     self.background.transform.scale(math.sin(t / 8) + 2, math.sin(t / 8) + 2)
  964.     self.background.draw()
  965.      
  966.     self.engine.view.setOrthogonalProjection(normalize = True)
  967.     font = self.engine.data.font
  968.    
  969.     try:
  970.       glEnable(GL_BLEND)
  971.       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  972.       glEnable(GL_COLOR_MATERIAL)
  973.       Theme.setBaseColor(1 - v)
  974.       wrapText(font, (.1, .05 - v), self.prompt)
  975.     finally:
  976.       self.engine.view.resetProjection()
  977.  
  978. class ItemChooser(BackgroundLayer, KeyListener):
  979.   """Item menu layer."""
  980.   def __init__(self, engine, items, selected = None, prompt = ""):
  981.     self.prompt         = prompt
  982.     self.engine         = engine
  983.     self.accepted       = False
  984.     self.selectedItem   = None
  985.     self.time           = 0.0
  986.     self.menu = Menu(self.engine, choices = [(c, self._callbackForItem(c)) for c in items], onClose = self.close, onCancel = self.cancel)
  987.     if selected and selected in items:
  988.       self.menu.selectItem(items.index(selected))
  989.     self.engine.loadSvgDrawing(self, "background", "editor.svg")
  990.    
  991.   def _callbackForItem(self, item):
  992.     def cb():
  993.       self.chooseItem(item)
  994.     return cb
  995.    
  996.   def chooseItem(self, item):
  997.     self.selectedItem = item
  998.     accepted = True
  999.     self.engine.view.popLayer(self.menu)
  1000.     self.engine.view.popLayer(self)
  1001.    
  1002.   def cancel(self):
  1003.     self.accepted = True
  1004.     self.engine.view.popLayer(self)
  1005.  
  1006.   def close(self):
  1007.     self.accepted = True
  1008.     self.engine.view.popLayer(self)
  1009.    
  1010.   def shown(self):
  1011.     self.engine.view.pushLayer(self.menu)
  1012.    
  1013.   def getSelectedItem(self):
  1014.     return self.selectedItem
  1015.  
  1016.   def run(self, ticks):
  1017.     self.time += ticks / 50.0
  1018.    
  1019.   def render(self, visibility, topMost):
  1020.     v = (1 - visibility) ** 2
  1021.  
  1022.     # render the background    
  1023.     t = self.time / 100
  1024.     w, h, = self.engine.view.geometry[2:4]
  1025.     r = .5
  1026.     self.background.transform.reset()
  1027.     self.background.transform.translate(v * 2 * w + w / 2 + math.sin(t / 2) * w / 2 * r, h / 2 + math.cos(t) * h / 2 * r)
  1028.     self.background.transform.rotate(-t)
  1029.     self.background.transform.scale(math.sin(t / 8) + 2, math.sin(t / 8) + 2)
  1030.     self.background.draw()
  1031.      
  1032.     self.engine.view.setOrthogonalProjection(normalize = True)
  1033.     font = self.engine.data.font
  1034.    
  1035.     try:
  1036.       glEnable(GL_BLEND)
  1037.       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  1038.       glEnable(GL_COLOR_MATERIAL)
  1039.       Theme.setBaseColor(1 - v)
  1040.       wrapText(font, (.1, .05 - v), self.prompt)
  1041.     finally:
  1042.       self.engine.view.resetProjection()
  1043.      
  1044.      
  1045. class BpmEstimator(Layer, KeyListener):
  1046.   """Beats per minute value estimation layer."""
  1047.   def __init__(self, engine, song, prompt = ""):
  1048.     self.prompt         = prompt
  1049.     self.engine         = engine
  1050.     self.song           = song
  1051.     self.accepted       = False
  1052.     self.bpm            = None
  1053.     self.time           = 0.0
  1054.     self.beats          = []
  1055.    
  1056.   def shown(self):
  1057.     self.engine.input.addKeyListener(self, priority = True)
  1058.     self.song.play()
  1059.  
  1060.   def hidden(self):
  1061.     self.engine.input.removeKeyListener(self)
  1062.     self.song.fadeout(1000)
  1063.    
  1064.   def keyPressed(self, key, unicode):
  1065.     if self.accepted:
  1066.       return True
  1067.      
  1068.     c = self.engine.input.controls.getMapping(key)
  1069.     if key == pygame.K_SPACE:
  1070.       self.beats.append(self.time)
  1071.       if len(self.beats) > 12:
  1072.         diffs = [self.beats[i + 1] - self.beats[i] for i in range(len(self.beats) - 1)]
  1073.         self.bpm = 60000.0 / (sum(diffs) / float(len(diffs)))
  1074.         self.beats = self.beats[-12:]
  1075.     elif c in [Player.CANCEL, Player.KEY2]:
  1076.       self.engine.view.popLayer(self)
  1077.       self.accepted = True
  1078.       self.bpm      = None
  1079.     elif c in [Player.KEY1] or key == pygame.K_RETURN:
  1080.       self.engine.view.popLayer(self)
  1081.       self.accepted = True
  1082.      
  1083.     return True
  1084.  
  1085.   def run(self, ticks):
  1086.     self.time += ticks
  1087.    
  1088.   def render(self, visibility, topMost):
  1089.     v = (1 - visibility) ** 2
  1090.  
  1091.     self.engine.view.setOrthogonalProjection(normalize = True)
  1092.     font = self.engine.data.font
  1093.    
  1094.     fadeScreen(v)
  1095.          
  1096.     try:
  1097.       glEnable(GL_BLEND)
  1098.       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  1099.       glEnable(GL_COLOR_MATERIAL)
  1100.       Theme.setBaseColor(1 - v)
  1101.       wrapText(font, (.1, .2 - v), self.prompt)
  1102.      
  1103.       if self.bpm is not None:
  1104.         Theme.setSelectedColor(1 - v)
  1105.         wrapText(font, (.1, .5 + v),  _("%.2f beats per minute") % (self.bpm))
  1106.     finally:
  1107.       self.engine.view.resetProjection()
  1108.      
  1109. class KeyTester(Layer, KeyListener):
  1110.   """Keyboard configuration testing layer."""
  1111.   def __init__(self, engine, prompt = ""):
  1112.     self.prompt         = prompt
  1113.     self.engine         = engine
  1114.     self.accepted       = False
  1115.     self.time           = 0.0
  1116.     self.controls       = Player.Controls()
  1117.     self.fretColors     = Theme.fretColors
  1118.    
  1119.   def shown(self):
  1120.     self.engine.input.addKeyListener(self, priority = True)
  1121.  
  1122.   def hidden(self):
  1123.     self.engine.input.removeKeyListener(self)
  1124.    
  1125.   def keyPressed(self, key, unicode):
  1126.     if self.accepted:
  1127.       return True
  1128.  
  1129.     self.controls.keyPressed(key)
  1130.     c = self.engine.input.controls.getMapping(key)
  1131.     if c in [Player.CANCEL]:
  1132.       self.engine.view.popLayer(self)
  1133.       self.accepted = True
  1134.     return True
  1135.  
  1136.   def keyReleased(self, key):
  1137.     self.controls.keyReleased(key)
  1138.  
  1139.   def run(self, ticks):
  1140.     self.time += ticks
  1141.    
  1142.   def render(self, visibility, topMost):
  1143.     v = (1 - visibility) ** 2
  1144.  
  1145.     self.engine.view.setOrthogonalProjection(normalize = True)
  1146.     font = self.engine.data.font
  1147.    
  1148.     fadeScreen(v)
  1149.          
  1150.     try:
  1151.       glEnable(GL_BLEND)
  1152.       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
  1153.       glEnable(GL_COLOR_MATERIAL)
  1154.       Theme.setBaseColor(1 - v)
  1155.       wrapText(font, (.1, .2 - v), self.prompt)
  1156.  
  1157.       for n, c in enumerate(Guitar.KEYS):
  1158.         if self.controls.getState(c):
  1159.           glColor3f(*self.fretColors[n])
  1160.         else:
  1161.           glColor3f(.4, .4, .4)
  1162.         font.render("#%d" % (n + 1), (.5 - .15 * (2 - n), .4 + v))
  1163.  
  1164.       if self.controls.getState(Player.ACTION1) or \
  1165.          self.controls.getState(Player.ACTION2):
  1166.         Theme.setSelectedColor(1 - v)
  1167.       else:
  1168.         glColor3f(.4, .4, .4)
  1169.       font.render(_("Pick!"), (.45, .5 + v))
  1170.        
  1171.     finally:
  1172.       self.engine.view.resetProjection()
  1173.      
  1174. def _runDialog(engine, dialog):
  1175.   """Run a dialog in a sub event loop until it is finished."""
  1176.   if not engine.running:
  1177.     return
  1178.  
  1179.   engine.view.pushLayer(dialog)
  1180.  
  1181.   while engine.running and dialog in engine.view.layers:
  1182.     engine.run()
  1183.  
  1184. def getText(engine, prompt, text = ""):
  1185.   """
  1186.  Get a string of text from the user.
  1187.  
  1188.  @param engine:  Game engine
  1189.  @param prompt:  Prompt shown to the user
  1190.  @param text:    Default text
  1191.  """
  1192.   d = GetText(engine, prompt, text)
  1193.   _runDialog(engine, d)
  1194.   return d.text
  1195.  
  1196. def getKey(engine, prompt, key = None):
  1197.   """
  1198.  Ask the user to choose a key.
  1199.  
  1200.  @param engine:  Game engine
  1201.  @param prompt:  Prompt shown to the user
  1202.  @param key:     Default key
  1203.  """
  1204.   d = GetKey(engine, prompt, key)
  1205.   _runDialog(engine, d)
  1206.   return d.key
  1207.  
  1208. def chooseSong(engine, prompt = _("Choose a Song"), selectedSong = None, selectedLibrary = None):
  1209.   """
  1210.  Ask the user to select a song.
  1211.  
  1212.  @param engine:           Game engine
  1213.  @param prompt:           Prompt shown to the user
  1214.  @param selectedSong:     Name of song to select initially
  1215.  @param selectedLibrary:  Name of the library where to search for the songs or None for the default library
  1216.  
  1217.  @returns a (library, song) pair
  1218.  """
  1219.   d = SongChooser(engine, prompt, selectedLibrary = selectedLibrary, selectedSong = selectedSong)
  1220.   _runDialog(engine, d)
  1221.   return (d.getSelectedLibrary(), d.getSelectedSong())
  1222.  
  1223. def chooseFile(engine, masks = ["*.*"], path = ".", prompt = _("Choose a File")):
  1224.   """
  1225.  Ask the user to select a file.
  1226.  
  1227.  @param engine:  Game engine
  1228.  @param masks:   List of glob masks for files that are acceptable
  1229.  @param path:    Initial path
  1230.  @param prompt:  Prompt shown to the user
  1231.  """
  1232.   d = FileChooser(engine, masks, path, prompt)
  1233.   _runDialog(engine, d)
  1234.   return d.getSelectedFile()
  1235.  
  1236. def chooseItem(engine, items, prompt, selected = None):
  1237.   """
  1238.  Ask the user to one item from a list.
  1239.  
  1240.  @param engine:    Game engine
  1241.  @param items:     List of items
  1242.  @param prompt:    Prompt shown to the user
  1243.  @param selected:  Item selected by default
  1244.  """
  1245.   d = ItemChooser(engine, items, prompt = prompt, selected = selected)
  1246.   _runDialog(engine, d)
  1247.   return d.getSelectedItem()
  1248.  
  1249. def testKeys(engine, prompt = _("Play with the keys and press Escape when you're done.")):
  1250.   """
  1251.  Have the user test the current keyboard configuration.
  1252.  
  1253.  @param engine:  Game engine
  1254.  @param prompt:  Prompt shown to the user
  1255.  """
  1256.   d = KeyTester(engine, prompt = prompt)
  1257.   _runDialog(engine, d)
  1258.  
  1259. def showLoadingScreen(engine, condition, text = _("Loading..."), allowCancel = False):
  1260.   """
  1261.  Show a loading screen until a condition is met.
  1262.  
  1263.  @param engine:      Game engine
  1264.  @param condition:   A function that will be polled until it returns a true value
  1265.  @param text:        Text shown to the user
  1266.  @type  allowCancel: bool
  1267.  @param allowCancel: Can the loading be canceled
  1268.  @return:            True if the condition was met, Fales if the loading was canceled.
  1269.  """
  1270.  
  1271.   # poll the condition first for some time
  1272.   n = 0
  1273.   while n < 32:
  1274.     n += 1
  1275.     if condition():
  1276.       return True
  1277.     engine.run()
  1278.  
  1279.   d = LoadingScreen(engine, condition, text, allowCancel)
  1280.   _runDialog(engine, d)
  1281.   return d.ready
  1282.  
  1283. def showMessage(engine, text):
  1284.   """
  1285.  Show a message to the user.
  1286.  
  1287.  @param engine:  Game engine
  1288.  @param text:    Message text
  1289.  """
  1290.   Log.notice("%s" % text)
  1291.   d = MessageScreen(engine, text)
  1292.   _runDialog(engine, d)
  1293.  
  1294. def estimateBpm(engine, song, prompt):
  1295.   """
  1296.  Ask the user to estimate the beats per minute value of a song.
  1297.  
  1298.  @param engine:  Game engine
  1299.  @param song:    Song instance
  1300.  @param prompt:  Prompt shown to the user
  1301.  """
  1302.   d = BpmEstimator(engine, song, prompt)
  1303.   _runDialog(engine, d)
  1304.   return d.bpm
Add Comment
Please, Sign In to add comment