# PyUI
# Copyright (C) 2001-2002 Sean C. Riley
# 
# This library is free software; you can redistribute it and/or
# modify it under the terms of version 2.1 of the GNU Lesser General Public
# License as published by the Free Software Foundation.
# 
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
# 
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

"""Pyui Themes.
Themes are a method of customizing the drawing of widgets in a pyui GUI.

This modules keeps NO state for the drable objects - it just draws them on demand
from the widgets themselves which hold all the state.

The constants for the theme objects live in pyui/locals.py
"""

import locals
import pyui
from pyui.desktop import getRenderer

class dataTheme:
    """Theme objects (like buttons) are drawn through methods for each type of widget.
    It returns the rect that the object draw fit into.

    The rect passed in should always be in window coordinates.

    Each theme has a 3 "BACK" fills.
    """

    emptyRect = [0,0,0,0]
    
    def __init__(self, renderer, w, h):
        self.frameBorderLeft = 5
        self.frameBorderRight = 5
        self.frameBorderTop = 22
        self.frameBorderBottom = 5
        self.tabsHeight = int(pyui.locals.TEXT_HEIGHT * 1.3)
        self.colorKey = (0,0,0,255)
        self.renderer = renderer

        # theme colors        
        self.fgColor = renderer.packColor(255,255,255)
        self.bgSelect = renderer.packColor(150,160,140)
        self.bgColor1 = renderer.packColor(150,140,150,224)
        self.bgColor2 = renderer.packColor(60,40,50,128)
        self.bgColor3 = renderer.packColor(60,50,50,128)
        self.bgMenuBar = renderer.packColor(60,60,60)
        self.bgMenuPanel = renderer.packColor(60,60,70,224)
        self.bgMenuSelect = renderer.packColor(125,120,120)
        self.black = renderer.packColor(0,0,0)
        self.white = renderer.packColor(255,255,255)
        self.shadeHi0 = renderer.packColor(255,255,255,0)
        self.shadeHi1 = renderer.packColor(255,255,255,255)
        self.shadeLo0 = renderer.packColor(0,0,0,0)
        self.shadeLo1 = renderer.packColor(0,0,0,255)
        self.buttonUpA = renderer.packColor(210,200,215)
        self.buttonUpB = renderer.packColor(150,150,160)
        self.buttonRoA = renderer.packColor(230,240,250)
        self.buttonRoB = renderer.packColor(180,190,200)
        self.buttonDnA = renderer.packColor(100,110,120)
        self.buttonDnB = renderer.packColor(180,180,200)
        self.buttonFocus = renderer.packColor(128,192,255,128)
        self.captionA = renderer.packColor(40,40,50)
        self.captionB = renderer.packColor(130,130,140)
        self.frameColor = renderer.packColor(120,120,140)
        self.shadowLight = renderer.packColor(0,0,0,64)
        self.shadowMedium = renderer.packColor(0,0,0,128)
        self.shadowDark = renderer.packColor(0,0,0,192)
        self.themeConstants = {}
        self.themeConstants[locals.THEME_SCREEN_WIDTH]     = w
        self.themeConstants[locals.THEME_SCREEN_HEIGHT]    = h
        
        self.arrowCursor = None #pygame.cursors.compile(pyui.mouse.arrow_strings, ".", "X")
        self.resizeCursor = None #pygame.cursors.compile(pyui.mouse.diagResize1_strings, ".", "X")
        self.buttonCursor = None #pygame.cursors.compile(pyui.mouse.hand_strings, ".", "X")
        self.waitCursor = None #pygame.cursors.compile(pyui.mouse.hourglass_strings, ".", "X")


    def getThemeConstant(self, constantID):
        """Gets a theme constant.
        """
        if not self.themeConstants.has_key(constantID):
            return None
        return self.themeConstants[constantID]

    ### Information about the theme..
    def getFrameBorderTop(self):
        return self.frameBorderTop

    def getFrameBorderLeft(self):
        return self.frameBorderLeft
    
    def getFrameBorderRight(self):
        return self.frameBorderRight
    
    def getFrameBorderBottom(self):
        return self.frameBorderBottom
    
    def getTabsHeight(self):
        return self.tabsHeight
    
    def getColorKey(self):
        return self.colorKey

    def getFgColor(self):
        return self.fgColor

    def getBgColor(self):
        return self.bgColor1

    
    #####################################################################
    ###
    ###   Utility drawing functions not specific to any widgets
    ###
    #####################################################################    

    def draw3DRect(self, rect, color, reverse):
        """Draw a 3D rectangle to the screen or a surface. Defaults to the screen.
        (internal)
        """
        (r,g,b,a) = self.renderer.unpackColor(color)
        colorLo = self.renderer.packColor(r/2, g/2, b/2, a)
        colorHi = self.renderer.packColor(128 + r/2, 128 + g/2, 128 + b/2, a)

        if reverse:
            (colorLo, colorHi) = (colorHi, colorLo)
            
        (x,y,w,h) = rect
        if w < 2 or h < 2:
            return
        
        getRenderer().drawRect( colorHi, (x, y, w-1, 1) )
        getRenderer().drawRect( colorHi, (x, y+1, 1, h-2) )
        if w > 2 and h > 2:
            getRenderer().drawRect( color, (x+1, y+1, w-2, h-2) )
        getRenderer().drawRect( colorLo, (x+1, y+h-1, w-1, 1) )
        getRenderer().drawRect( colorLo, (x+w-1, y+1, 1, h-2) )

    def shadeBorder(self, rect, d, reverse):
        """Draw a 3D rectangle to the screen or a surface. Defaults to the screen.
        (internal)
        """
        (x,y,w,h) = rect
        if reverse:
            getRenderer().drawGradient((x,y,d,h), self.shadeLo1, self.shadeLo0, self.shadeLo1, self.shadeLo0)
            getRenderer().drawGradient((x,y,w,d), self.shadeLo1, self.shadeLo1, self.shadeLo0, self.shadeLo0)
            getRenderer().drawGradient((x+w-d,y,d,h), self.shadeHi0, self.shadeHi1, self.shadeHi0, self.shadeHi1)
            getRenderer().drawGradient((x,y+h-d,w,d), self.shadeHi0, self.shadeHi0, self.shadeHi1, self.shadeHi1)
        else:
            getRenderer().drawGradient((x,y,d,h), self.shadeHi1, self.shadeHi0, self.shadeHi1, self.shadeHi0)
            getRenderer().drawGradient((x,y,w,d), self.shadeHi1, self.shadeHi1, self.shadeHi0, self.shadeHi0)
            getRenderer().drawGradient((x+w-d,y,d,h), self.shadeLo0, self.shadeLo1, self.shadeLo0, self.shadeLo1)
            getRenderer().drawGradient((x,y+h-d,w,d), self.shadeLo0, self.shadeLo0, self.shadeLo1, self.shadeLo1)

    def drawOutlineRect(self, rect, color, thick=1):
        (x,y,w,h) = rect
        getRenderer().drawRect(color, (x,y,w,thick))
        getRenderer().drawRect(color, (x,y+thick,thick,h-2*thick))
        getRenderer().drawRect(color, (x+w-thick,y+thick,thick,h-2*thick))
        getRenderer().drawRect(color, (x,y+h-thick,w,thick))


    def drawGradient(self, rect, vertical, c1, c2):
        if vertical:
            getRenderer().drawGradient(rect, c1, c2, c1, c2)            
        else:
            getRenderer().drawGradient(rect, c1, c1, c2, c2)
        
    ### mouse cursor functions
    def setArrowCursor(self):
        #pygame.mouse.set_cursor( (24,24), (0,0), self.arrowCursor[0], self.arrowCursor[1])
        pass

    def setResizeCursor(self):
        #pygame.mouse.set_cursor( (24,24), (0,0), self.resizeCursor[0], self.resizeCursor[1])
        pass

    def setButtonCursor(self):
        #pygame.mouse.set_cursor( (24,24), (0,0), self.buttonCursor[0], self.buttonCursor[1])
        pass

    def setWaitCursor(self):
        #pygame.mouse.set_cursor( (24,24), (0,0), self.waitCursor[0], self.waitCursor[1])
        pass
        
    #####################################################################
    ###
    ### Widgets specific drawing functions.
    ###
    #####################################################################    
    def drawButton(self, rect, title, hasFocus, status, enabled):
        """Options are:  button title and the up/down status.
        status == 0: normal
        status == 1: rollover
        status == 2: down
        """
        (x,y,w,h) = rect
        color = self.fgColor        
        if enabled == 0:
            self.drawGradient(rect, 0, self.bgColor1, self.bgColor2)
            self.shadeBorder(rect, 4, 1)
            color = self.buttonUpB
        elif status == 0:
            self.drawGradient(rect, 0, self.buttonUpA, self.buttonUpB)
            self.shadeBorder(rect, 4, 0)
        elif status == 1:
            self.drawGradient(rect, 0, self.buttonRoA, self.buttonRoB)
            self.shadeBorder(rect, 4, 0)
        elif status == 2:
            self.drawGradient(rect, 0, self.buttonDnA, self.buttonDnB)
            self.shadeBorder(rect, 4, 1)
            x += 3
            y += 3

        if hasFocus:
            self.drawOutlineRect(rect, self.buttonFocus, 3)

        size = getRenderer().getTextSize(title)
        getRenderer().drawText( title, (x + (w-size[0])/2, y + (h-size[1])/2), color)
        return rect

    def drawImageButton(self, rect, filename, title, hasFocus, status):
        if status == 2:
            self.draw3DRect(rect, self.bgColor1, 1)
        else:
            self.draw3DRect(rect, self.bgColor1, 0)
        getRenderer().drawImage((rect[0] + status, rect[1] + status, rect[2]-2, rect[3]-2), filename)
        self.shadeBorder(rect, 4, 0)
        (w,h) = getRenderer().getTextSize(title)

        if hasFocus:
            self.drawOutlineRect(rect, self.buttonFocus, 3)
        
        getRenderer().drawText( title, (rect[0] + (rect[2]-w)/2 + status, rect[1] + (rect[3]-h)/2 + status-1), self.black)
        getRenderer().drawText( title, (rect[0] + (rect[2]-w)/2 + status-1, rect[1] + (rect[3]-h)/2 + status), self.white)

    def drawLabel(self, rect, title, color = None):
        #(title, color) = options
        if not color:
            color = self.fgColor
        (w,h) = getRenderer().getTextSize(title)
        getRenderer().drawText(title, (rect[0], rect[1]), color)
        return (rect[0], rect[1], w, h)

    def drawCheckBox(self, rect, text, checkState):
        w = rect[2]
        h = rect[3]
        checkboxXpos = rect[0] + 8        
        checkboxYpos = rect[1] + (h/2) - 8

        self.draw3DRect( (checkboxXpos, checkboxYpos, 16, 16), self.bgColor1, 1)
        getRenderer().drawText( text, (checkboxXpos + 20, checkboxYpos ), self.fgColor)

        if checkState:
            getRenderer().drawLine( checkboxXpos + 2, checkboxYpos + 2, checkboxXpos + 14, checkboxYpos + 14, pyui.colors.white)
            getRenderer().drawLine( checkboxXpos + 14, checkboxYpos + 2, checkboxXpos + 2 , checkboxYpos + 14, pyui.colors.white)            

    def drawSliderBar(self, rect, range, position, BARWIDTH=8):
        interval = rect[2] / float(range)
        half = rect[1] + rect[3]/2
        getRenderer().drawRect(self.bgSelect, rect)
        ## could draw little lines here....
#        for i in xrange(1,range):
#            getRenderer().drawLine( rect[0] + i*interval, half-4, rect[0] + i*interval, half+4, pyui.colors.black)        
        getRenderer().drawLine( rect[0] + 1, half, rect[0] + rect[2] -2, half, pyui.colors.white)
        getRenderer().drawLine( rect[0] + 1, half + 2, rect[0] + rect[2] -2, half + 2, pyui.colors.black)
        diff = rect[2] - BARWIDTH
        xpos = float(diff) / range * position
        self.drawGradient((rect[0] + xpos, rect[1] + 2, BARWIDTH, rect[3] - 4), 0, pyui.colors.grey, pyui.colors.white)
        self.shadeBorder((rect[0] + xpos, rect[1] + 2, BARWIDTH, rect[3] - 4), 3, 0)

        getRenderer().drawText("%s/%s" % (position,range), (rect[0] + rect[2] - 40, rect[1]), pyui.colors.red)

            
    def drawEdit(self, rect, text, hasFocus, caretPos, selectPos):
        self.draw3DRect(rect, self.bgColor2, 0)
        (x,y,w,h) = rect
        if hasFocus:
            caretX = getRenderer().getTextSize(text[0:caretPos])[0]
            if selectPos != None:
                # draw selection highlight
                selectX = getRenderer().getTextSize(text[0:selectPos])[0]
                selectX0 = min(caretX, selectX)
                selectX1 = max(caretX, selectX)
                if selectX0 < selectX1:
                    getRenderer().drawRect(self.bgSelect, (x+1+selectX0, y+1, selectX1-selectX0, locals.TEXT_HEIGHT))

        getRenderer().drawText(text, (x+1, y+1), self.fgColor)

        # draw caret        
        if hasFocus:
            getRenderer().drawRect(self.fgColor, (x+1+caretX, y+1, 1, locals.TEXT_HEIGHT))

        return rect

    def drawFrame(self, rect, title):
        """Draw frame objects.
        Frame objects are complex multi-part widgets. In order to allow the theme some flexibility of how to
        position and draw various interactive frame elements, the drawFrame() method returns a list of hit
        regions. Each element is a tuple consisting of a (region Id, rect). Region IDs are defined in locals.py.
        The hit list is in top to bottom order for easier iteration.
        """
        offset = 8
        hitList = []

        # draw caption bar        
        (x,y,w,h)=(rect[0], rect[1], rect[2], self.frameBorderTop)
        self.drawGradient((x, y, w, h), 0, self.captionA, self.captionB)
        getRenderer().drawText( title, (x + self.frameBorderLeft + 1, y + self.frameBorderTop/2 - 8), self.fgColor)
        self.shadeBorder((x,y,w,h), 3, 0)
        hitList.append((locals.HIT_FRAME_MOVE, (x,y,w,h)))

        # draw close button        
        (x,y,w,h)=(rect[0]+rect[2]-self.frameBorderTop+3,rect[1]+3,self.frameBorderTop-6,self.frameBorderTop-6)
        self.drawGradient((x,y,w,h), 0, self.buttonUpA, self.buttonUpB)
        self.shadeBorder((x,y,w,h), 3, 0)
        self.shadeBorder((x+4,y+4,w-8,h-8), 3, 1)
        hitList.append((locals.HIT_FRAME_CLOSE, (x,y,w,h)))

        # left border
        (x,y,w,h)=(rect[0],rect[1]+self.frameBorderTop,self.frameBorderLeft,rect[3]-self.frameBorderTop-self.frameBorderBottom)
        getRenderer().drawRect(self.frameColor,(x,y,w,h))
        self.shadeBorder((x,y,w,h), 2, 0)

        # right border                
        (x,y,w,h)=(rect[0]+rect[2]-self.frameBorderRight,rect[1]+self.frameBorderTop,self.frameBorderRight,rect[3]-self.frameBorderTop-self.frameBorderBottom)
        getRenderer().drawRect(self.frameColor, (x,y,w,h))
        self.shadeBorder((x,y,w,h), 2, 0)
        hitList.append((locals.HIT_FRAME_RESIZE_RIGHT, (x,y,w,h)))

        # bottom border
        (x,y,w,h)=(rect[0]+self.frameBorderLeft,rect[1]+rect[3]-self.frameBorderBottom,rect[2]-self.frameBorderLeft-self.frameBorderRight,self.frameBorderBottom)
        getRenderer().drawRect(self.frameColor, (x,y,w,h))
        self.shadeBorder((x,y,w,h), 2, 0)
        hitList.append((locals.HIT_FRAME_RESIZE_BOTTOM, (x,y,w,h)))

        # lower left corner
        (x,y,w,h)=(rect[0],rect[1]+rect[3]-self.frameBorderBottom,self.frameBorderLeft,self.frameBorderBottom)
        getRenderer().drawRect(self.frameColor, (x,y,w,h))
        self.shadeBorder((x,y,w,h), 2, 0)

        # lower right corner
        (x,y,w,h)=(rect[0]+rect[2]-self.frameBorderRight,rect[1]+rect[3]-self.frameBorderBottom,self.frameBorderRight,self.frameBorderBottom)
        getRenderer().drawRect(self.frameColor, (x,y,w,h))
        self.shadeBorder((x,y,w,h), 2, 0)
        hitList.append((locals.HIT_FRAME_RESIZE_BOTTOM_RIGHT, (x,y,w,h)))
        
        # draw shadow
        (x,y,w,h)=(rect[0], rect[1], rect[2], rect[3])
        getRenderer().drawRect( self.shadowMedium, (x+w, y+offset, offset, h - offset) )
        getRenderer().drawRect( self.shadowMedium, (x+offset, y+h, w, offset) )

        hitList.reverse()        
        return hitList        
        
    def drawBack1(self, rect):
        self.drawGradient(rect, 0, self.bgColor1, self.bgColor2)
        return rect

    def drawBack2(self, rect):
        self.drawGradient(rect, 0, self.bgColor2, self.fgColor)
        return rect

    def drawBack3(self, rect):
        self.drawGradient(rect, 0, self.bgColor3, self.bgColor1)        
        return rect
    
    def drawScrollButtonUp(self, rect):
        self.draw3DRect(rect, self.bgColor1, 0)
        return rect

    def drawScrollButtonDown(self, rect):
        self.draw3DRect(rect, self.bgColor1, 0)
        return rect

    def drawScrollBar(self, rect):
        self.draw3DRect(rect, self.bgColor1, 0)
        return rect


    def drawTabItem(self, rect, title, active):
        gap = 3
        (x,y,w,h) = rect
        getRenderer().drawRect(self.black, (x, y+h-1, gap, 1))
        x += gap
        title = " " + title + " "
        w = getRenderer().getTextSize(title)[0]
        if active:
            getRenderer().drawRect(self.black, (x, y, w, 1))
            getRenderer().drawRect(self.black, (x, y, 1, h))
            getRenderer().drawRect(self.black, (x+w-1, y, 1, h))
        else:
            getRenderer().drawRect(self.shadowLight, (x, y, w, h))
            self.drawOutlineRect((x, y, w, h), self.black)
        getRenderer().drawRect(self.shadowMedium, (x+w, y+gap, gap, h-gap))

        getRenderer().drawText( title, (x, y), self.fgColor)
        return (x,y,w,h)

    def drawTabHeader(self, rect):
        (x,y,w,h) = rect
        getRenderer().drawRect(self.black, (x, y+h-1, w, 1))
        return rect

    def drawMenuBar(self, rect):
        self.draw3DRect(rect, self.bgMenuBar, 0)
        return rect

    def drawMenuBarItem(self, rect, title, selected):
        title = "   " + title + "   "
        (w,h) = getRenderer().getTextSize(title)
        if selected:
            getRenderer().drawRect(self.bgMenuSelect, (rect[0], rect[1], w, rect[3]))
            getRenderer().drawText(title, (rect[0], rect[1]), self.fgColor)
        else:
            getRenderer().drawText(title, (rect[0], rect[1]), self.fgColor)
        return (rect[0], rect[1], w, rect[3])

    def drawMenu(self, rect):
        self.draw3DRect(rect, self.bgMenuPanel, 0)
        # draw shadow
        offset = 4
        (x,y,w,h)=(rect[0], rect[1], rect[2], rect[3])
        getRenderer().drawRect( self.shadowMedium, (x+w, y+offset, offset, h - offset) )
        getRenderer().drawRect( self.shadowMedium, (x+offset, y+h, w, offset) )
        return rect
    
    def drawMenuItem(self, rect, title, selected, icon = None):
        iconSize = 12
        if selected:
            getRenderer().drawRect(self.bgMenuSelect, (rect[0], rect[1], rect[2], locals.TEXT_HEIGHT))
        if title == '-':
            self.draw3DRect((rect[0], rect[1] + locals.TEXT_HEIGHT / 2- 1, rect[2], 2), self.bgMenuPanel, 1)
        else:
            getRenderer().drawText(title, (rect[0] + iconSize, rect[1]), self.fgColor)
        if icon:
            getRenderer().drawImage((rect[0], rect[1]+(locals.TEXT_HEIGHT - iconSize) / 2, iconSize, iconSize), icon)
        return (rect[0], rect[1], rect[2], locals.TEXT_HEIGHT)

    def drawListBoxItem(self, rect, title, selected, color):
        if selected:
            self.draw3DRect( (rect[0] - 1, rect[1] - 1, rect[2]- 1 - 11, rect[3] ), self.fgColor, 0)
            getRenderer().drawText(title, (rect[0], rect[1]), self.bgColor2)
        else:
            getRenderer().drawText(title, (rect[0], rect[1]), color)
        return rect
            
    def drawSplitter(self, rect):
        (x,y,w,h) = rect
        getRenderer().drawRect(self.frameColor, (x,y,w,h))
        self.shadeBorder((x,y,w,h), 2, 0)
        #self.draw3DRect(rect,self.bgColor3, 0)

    def drawToolTip(self, text, rect):
        getRenderer().drawRect(self.black, rect)
        getRenderer().drawRect(pyui.colors.yellow, (rect[0] + 1, rect[1] + 1, rect[2] - 2, rect[3] - 2))
        getRenderer().drawText(text, (rect[0] + 2, rect[1] + 2), pyui.colors.black)
