view orpg/map/_canvas.py @ 86:bdbeafcb2ef4 alpha

Traipse Alpha 'OpenRPG' {090917-00} Traipse is a distribution of OpenRPG that is designed to be easy to setup and go. Traipse also makes it easy for developers to work on code without fear of sacrifice. 'Ornery-Orc' continues the trend of 'Grumpy' and adds fixes to the code. 'Ornery-Orc''s main goal is to offer more advanced features and enhance the productivity of the user. Update Summary: Adds menu changes to draw attention to important updates, errors, or other events. (image info coming soon) Traipse URL is not included in the repos tab and is set as default.
author sirebral
date Thu, 17 Sep 2009 01:14:48 -0500
parents 4385a7d0efd1
children
line wrap: on
line source

from threading import Lock
import mimetypes
import xml.dom.minidom as minidom

import wx

import orpg.dirpath
from orpg.orpgCore import *
from orpg.tools.rgbhex import RGBHex

from _object import *

from _circles import MapCircle
from _text import MapText
from _lines import MapLine
from _grid import GridLayer
from _fog import FogLayer

USE_BUFFER = True
if "wxMAC" in wx.PlatformInfo:
    USE_BUFFER = False

class MapCanvas(wx.ScrolledWindow):
    def __init__(self, parent, openrpg):
        wx.ScrolledWindow.__init__(self, parent, wx.ID_ANY, style=wx.HSCROLL | wx.VSCROLL | wx.NO_FULL_REPAINT_ON_RESIZE | wx.SUNKEN_BORDER)

        self.openrpg = openrpg
        self.log = self.openrpg.get_component("log")
        self.xml = self.openrpg.get_component("xml")
        self.dir_struct = self.openrpg.get_component("dir_struct")
        self.validate = self.openrpg.get_component("validate")
        self.settings = self.openrpg.get_component("settings")
        self.session = self.openrpg.get_component("session")
        self.chat = self.openrpg.get_component("chat")

        self.lock = Lock()

        self.RGBHex = RGBHex()

        self.toolWnd = parent

        self.shift = False
        self.ctrl = False

        self.selectedObjects = []
        self.overObjects = []
        self._objectId = 0

        self.gridLayer = GridLayer(self)
        self.circleLayer = MapCircle(self)
        self.textLayer = MapText(self)
        self.lineLayer = MapLine(self)
        self.fogLayer = FogLayer(self)

        self.zOrder = {}
        self.zOrder['tiles'] = []
        self.zOrder["back"] = []
        self.zOrder["front"] = []

        self.bgImage = None
        self.bgType = 'Image'
        self.bgPath = None
        self.backgroundColor = '#008040'

        self.gridType = 'Square'
        self.gridLines = wx.SOLID
        self.gridSnap = True
        self.gridSize = 60
        self.gridColor = "#000000"

        self.whiteboardColor = "#000000"

        self.zoomScale = 1.0
        self.lastZoomTime = time.time()
        self.lastZoomScale = 1.0

        self.useFog = False
        self.fogRegion = []
        self.fogColor = "#000000"

        self.zoomScale = 1.0
        self.lastZoomTime = time.time()
        self.lastZoomScale = 1.0
        self.zoomTimer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnZoomTimer, self.zoomTimer)
        #self.zoomTimer.Start(1000)

        self.imageCache = {}

        self._SetSize((1000,1000))

        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.Bind(wx.EVT_SIZE, self.OnSize)
        self.Bind(wx.EVT_MOUSEWHEEL, self.OnZoom)
        self.Bind(wx.EVT_LEFT_DOWN, self.OnLeftDown)
        self.Bind(wx.EVT_LEFT_DCLICK, self.OnLeftDClick)
        self.Bind(wx.EVT_LEFT_UP, self.OnLeftUp)
        self.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
        self.Bind(wx.EVT_MOTION, self.OnMotion)
        self.Bind(wx.EVT_SCROLLWIN, self.OnScroll)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.Bind(wx.EVT_ERASE_BACKGROUND, self.OnBackground)
        self.Bind(wx.EVT_KEY_DOWN, self.OnKey)
        self.Bind(wx.EVT_KEY_UP, self.OnKey)

        self.Bind(EVT_ENTER_OBJECT, self.EnterObject)
        self.Bind(EVT_LEAVE_OBJECT, self.LeaveObject)
        self.Bind(EVT_SELECT_OBJECT, self.ObjectSelected)
        self.Bind(EVT_DESELECT_OBJECT, self.ObjectDeselected)

        self.roleTimer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.OnRoleTimer, self.roleTimer)

        wx.CallAfter(self.OnSize, None)


    #Public API
    def UpdateMap(self, send=True):
        cdc = wx.ClientDC(self)
        self.PrepareDC(cdc)
        cdc.SetBackgroundMode(wx.TRANSPARENT)
        if USE_BUFFER:
            bdc = wx.BufferedDC(cdc, self._buffer)
            bdc.Clear()
            dc = wx.GraphicsContext.Create(bdc)
        else:
            cdc.Clear()
            dc = wx.GraphicsContext.Create(cdc)


        dc.SetPen(wx.TRANSPARENT_PEN)
        dc.SetBrush(wx.TRANSPARENT_BRUSH)

        #Draw BG Color
        r,g,b = self.RGBHex.rgb_tuple(self.backgroundColor)
        brush = wx.Brush(wx.Color(r,g,b,255))
        dc.SetBrush(brush)

        path = dc.CreatePath()

        dc.PushState()
        path.AddRectangle(0, 0, self.size[0]+2, self.size[1]+2)
        dc.DrawPath(path)
        dc.PopState()

        dc.SetBrush(wx.NullBrush)

        #Set the Zoom
        dc.Scale(self.zoomScale, self.zoomScale)

        #Draw BG Image
        if self.bgImage != None:
            if self.bgType == 'Image':
                dc.DrawBitmap(self.bgImage, self.offset[0], self.offset[1], self.bgImage.GetWidth(), self.bgImage.GetHeight())
            else:
                bmpW = self.bgImage.GetWidth()
                bmpH = self.bgImage.GetHeight()

                pos = wx.Point(self.offset[0], self.offset[1])
                while pos.x < self.size[0]:
                    dc.DrawBitmap(self.bgImage, pos.x, pos.y, self.bgImage.GetWidth(), self.bgImage.GetHeight())
                    while pos.y < self.size[1]:
                        pos.y += bmpH
                        dc.DrawBitmap(self.bgImage, pos.x, pos.y, self.bgImage.GetWidth(), self.bgImage.GetHeight())
                    pos.y = 0
                    pos.x += bmpW

        #Draw Tiles
        for tile in self.zOrder['tiles']:
            tile.Draw(dc)

        #Draw Grid
        self.gridLayer.Draw(dc)

        #Draw Objects
        for object in self.zOrder['back']:
            object.Draw(dc)

        zl = self.zOrder.keys()
        zl.remove('back')
        zl.remove('front')
        zl.remove('tiles')
        zl.sort()

        for layer in zl:
            for object in self.zOrder[layer]:
                object.Draw(dc)

        for object in self.zOrder['front']:
            object.Draw(dc)


        #Draw Fog
        if self.useFog:
            self.fogLayer.Draw(dc)

        dc.SetBrush(wx.NullBrush)

        dc.Scale(1/self.zoomScale, 1/self.zoomScale)

        if self.zoomScale != 1.0:
            pos = self.GetViewStart()
            unit = self.GetScrollPixelsPerUnit()
            pos = [pos[0]*unit[0],pos[1]*unit[1]]
            font = wx.Font(8, wx.DEFAULT, wx.NORMAL, wx.NORMAL)
            dc.SetFont(font, wx.BLACK)

            dc.DrawText("Zoom Factor: " + str(self.zoomScale), pos[0], pos[1], dc.CreateBrush(wx.WHITE_BRUSH))

    def Clear(self):
        self._SetSize((1000,1000))
        self.selectedObjects = []
        self.overObjects = []
        self._objectId = 0
        self.bgImage = None
        self.bgType = 'Image'
        self.bgPath = None

        self.backgroundColor = '#008040'
        r, g, b = self.RGBHex.rgb_tuple(self.backgroundColor)
        self.toolWnd._SetColorBtn(wx.Color(r, g, b, 255), self.toolWnd.BGColorBtn)

        self.gridType = 'Square'
        self.gridLines = wx.SOLID
        self.gridSnap = True
        self.gridSize = 60
        self.gridColor = "#000000"

        self.whiteboardColor = "#000000"
        r, g, b = self.RGBHex.rgb_tuple(self.whiteboardColor)
        self.toolWnd._SetColorBtn(wx.Color(r, g, b, 255), self.toolWnd.ColorBtn)

        self.zoomScale = 1.0
        self.lastZoomTime = time.time()
        self.lastZoomScale = 1.0

        self.useFog = False
        self.fogRegion = []
        self.fogColor = "#000000"

        self.OnRemoveAllObjects(None)

        self.toolWnd.Freeze()
        for btn in self.toolWnd.exclusiveToolList:
            self.toolWnd.exclusiveToolList[btn].SetToggled(False)

        self.toolWnd.FogBtn.SetToggled(False)
        self.toolWnd.SelectorBtn.SetToggled(True)
        self.toolWnd.Thaw()

    def GetNewObjectId(self):
        return str(self._objectId+1)

    #Map Events
    def OnBackground(self, event):
        #Dont do it
        pass

    def OnPaint(self, event):
        if USE_BUFFER:
            dc = wx.PaintDC(self)
            self.PrepareDC(dc)
            dc.DrawBitmap(self._buffer, 0, 0)
        else:
            event.Skip()


    def OnSize(self, event):
        self._buffer = wx.EmptyBitmap(self.size[0], self.size[1])
        self._FixScroll()
        wx.CallAfter(self.UpdateMap)


    def OnZoom(self, event):
        if event.GetWheelRotation() < 0:
            self.zoomScale -= .1
            if self.zoomScale < .5:
                self.zoomScale = .5
            else:
                self.lastZoomTime = time.time()
                self._FixScroll()
                self.UpdateMap()
        else:
            self.zoomScale += .1

            if self.zoomScale > 1.5:
                self.zoomScale = 1.5
            else:
                self.lastZoomTime = time.time()
                self._FixScroll()
                self.UpdateMap()

    def OnKey(self, event):
        self.shift = False
        self.ctrl = False
        if event.ShiftDown():
            self.shift = True
        elif event.ControlDown():
            self.ctrl = True


    def EnterObject(self, event):
        obj = event.GetObject()
        self.overObjects.append(obj)
        obj.Highlight()

    def LeaveObject(self, event):
        obj = event.GetObject()
        try:
            self.overObjects.remove(obj)
        except:
            pass
        obj.UnHighlight()

    def ObjectSelected(self, event):
        obj = event.GetObject()
        self.selectedObjects.append(obj)
        try:
            self.overObjects.remove(obj)
        except:
            pass
        obj.UnHighlight()

    def ObjectDeselected(self, event):
        obj = event.GetObject()
        try:
            self.selectedObjects.remove(obj)
        except:
            pass
        obj.Update()

    def OnLeftDown(self, event):
        dc = wx.ClientDC(self)
        self.PrepareDC(dc)
        pos = event.GetLogicalPosition(dc)
        pos.x /= self.zoomScale
        pos.y /= self.zoomScale

        if self.toolWnd.AddShapeBtn.GetToggled() and self.toolWnd.currentShape == 'Circle':
            self.circleLayer.OnLeftDown(pos)

        elif self.toolWnd.AddTextBtn.GetToggled():
            self.textLayer.OnLeftDown(pos)

        elif self.toolWnd.DrawBtn.GetToggled():
            self.lineLayer.OnLeftDown(pos)

        elif self.toolWnd.SelectorBtn.GetToggled() and (self.selectedObjects == [] or self.ctrl or self.shift) and not (self.useFog and self.fogLayer.region.Contains(pos.x, pos.y) and not self.toolWnd.gmToolBar.IsShown()):
            self.initiatPos = pos
            self.lxd = 0
            self.lyd = 0
            if len(self.overObjects) == 0:
                return
            elif len(self.overObjects) == 1:
                self.overObjects[0].Select()
            else:
                if not self.shift:
                    menu = wx.Menu("Object Selection")
                    id = 0
                    for obj in self.overObjects:
                        menu.Append(id, obj.GetName())
                        id += 1

                    def selectmenu(event):
                        id = event.GetId()
                        self.overObjects[id].Select()
                        self.Unbind(wx.EVT_MENU)

                    self.Bind(wx.EVT_MENU, selectmenu)
                    self.PopupMenu(menu)
                else:
                    for i in xrange(len(self.overObjects)):
                        self.overObjects[0].Select()

        elif self.toolWnd.SelectorBtn.GetToggled() and not self.selectedObjects == []:
            xd = (self.initiatPos.x+pos.x)*(self.initiatPos.x+pos.x)
            yd = (self.initiatPos.y+pos.y)*(self.initiatPos.y+pos.y)

            for i in xrange(len(self.selectedObjects)):
                self.selectedObjects[0].Deselect()

        elif self.toolWnd.FogToolBtn.GetToggled():
            self.fogLayer.OnLeftDown(pos)

    def OnLeftDClick(self, event):
        dc = wx.ClientDC(self)
        self.PrepareDC(dc)
        pos = event.GetLogicalPosition(dc)
        pos.x /= self.zoomScale
        pos.y /= self.zoomScale

        if self.toolWnd.DrawBtn.GetToggled():
            self.lineLayer.OnLeftDClick(pos)

    def OnLeftUp(self, event):
        dc = wx.ClientDC(self)
        self.PrepareDC(dc)
        pos = event.GetLogicalPosition(dc)
        pos.x /= self.zoomScale
        pos.y /= self.zoomScale

        if self.toolWnd.AddShapeBtn.GetToggled() and self.toolWnd.currentShape == 'Circle':
            self.circleLayer.OnLeftUp(pos)

        elif self.toolWnd.FogToolBtn.GetToggled():
            self.fogLayer.OnLeftUp(pos)

        elif self.toolWnd.DrawBtn.GetToggled():
            self.lineLayer.OnLeftUp(pos)

        elif self.toolWnd.SelectorBtn.GetToggled() and self.selectedObjects == []:
            rgn = wx.Region(self.initiatPos.x, self.initiatPos.y, self.lxd, self.lyd)

            for object in self.zOrder['back']:
                if rgn.Contains(object.start.x, object.start.y):
                    object.Select()

            zl = self.zOrder.keys()
            zl.remove('back')
            zl.remove('front')
            zl.remove('tiles')
            zl.sort()

            for layer in zl:
                for object in self.zOrder[layer]:
                    if rgn.Contains(object.start.x, object.start.y):
                        object.Select()

            for object in self.zOrder['front']:
                if rgn.Contains(object.start.x, object.start.y):
                    object.Select()

            self.lxd = 0
            self.lyd = 0
            self.initiatPos = pos
        self.Refresh()

    def OnMotion(self, event):
        dc = wx.ClientDC(self)
        self.PrepareDC(dc)
        pos = event.GetLogicalPosition(dc)
        pos.x /= self.zoomScale
        pos.y /= self.zoomScale


        #HitTest
        for object in self.zOrder['back']:
            object.HitTest(pos)

        zl = self.zOrder.keys()
        zl.remove('back')
        zl.remove('front')
        zl.remove('tiles')
        zl.sort()

        for layer in zl:
            for object in self.zOrder[layer]:
                object.HitTest(pos)

        for object in self.zOrder['front']:
            object.HitTest(pos)

        if self.toolWnd.AddShapeBtn.GetToggled() and event.m_leftDown and self.toolWnd.currentShape == 'Circle':
            self.circleLayer.OnMotion(pos)

        elif self.toolWnd.DrawBtn.GetToggled() and self.lineLayer.start != wx.Point(0,0):
            self.lineLayer.OnMotion(pos)

        elif self.toolWnd.SelectorBtn.GetToggled() and self.selectedObjects != [] and not (self.ctrl or self.shift):
            xd = (pos.x-self.initiatPos.x)
            yd = (pos.y-self.initiatPos.y)
            for obj in self.selectedObjects:
                obj.start.x += xd
                obj.start.y += yd
                obj.Update()
                self.initiatPos = pos


        elif self.toolWnd.SelectorBtn.GetToggled() and self.selectedObjects == [] and event.m_leftDown:
            dc.SetBrush(wx.TRANSPARENT_BRUSH)
            pen = wx.Pen(wx.BLACK, 3, wx.DOT)
            dc.SetPen(pen)
            dc.SetLogicalFunction(wx.INVERT)

            xd = (pos.x-self.initiatPos.x)
            yd = (pos.y-self.initiatPos.y)

            if self.lxd != 0 and self.lyd != 0:
                r = wx.Rect(self.initiatPos.x, self.initiatPos.y, self.lxd, self.lyd)
                dc.DrawRectangleRect(r)

            self.lxd = xd
            self.lyd = yd
            r = wx.Rect(self.initiatPos.x, self.initiatPos.y, self.lxd, self.lyd)
            dc.DrawRectangleRect(r)

        elif (self.toolWnd.FogToolBtn.GetToggled()) and event.m_leftDown:
            self.fogLayer.OnMotion(pos)

    def OnRightDown(self, event):
        mapmenu = wx.Menu()

        item = wx.MenuItem(mapmenu, wx.ID_ANY, "Load Map", "Load Map")
        #self.Bind(wx.EVT_MENU, self.OnOpenBtn, item)
        mapmenu.AppendItem(item)

        item = wx.MenuItem(mapmenu, wx.ID_ANY, "Save Map", "Save Map")
        #self.Bind(wx.EVT_MENU, self.OnSaveBtn, item)
        mapmenu.AppendItem(item)

        item = wx.MenuItem(mapmenu, wx.ID_ANY, "Default Map", "Default Map")
        self.Bind(wx.EVT_MENU, self.OnDefaultBtn, item)
        mapmenu.AppendItem(item)

        item = wx.MenuItem(mapmenu, wx.ID_ANY, "Map Properties", "Map Properties")
        #self.Bind(wx.EVT_MENU, OnMapPropsBtn, item)
        mapmenu.AppendItem(item)

        bgmenu = wx.Menu()

        item = wx.MenuItem(bgmenu, wx.ID_ANY, "Change Background Image", "Change Background Image")
        self.Bind(wx.EVT_MENU, self.OnBGBtn, item)
        bgmenu.AppendItem(item)

        item = wx.MenuItem(bgmenu, wx.ID_ANY, "Change Background Color", "Change Background Color")
        self.Bind(wx.EVT_MENU, self.OnBGColorBtn, item)
        bgmenu.AppendItem(item)

        item = wx.MenuItem(bgmenu, wx.ID_ANY, "Grid Properties", "Grid Properties")
        #self.Bind(wx.EVT_MENU, self.OnGridBtn, item)
        bgmenu.AppendItem(item)

        fogmenu = wx.Menu()

        item = wx.MenuItem(fogmenu, wx.ID_ANY, "Toggle Fog", "Toggle Fog")
        self.Bind(wx.EVT_MENU, self.OnFogBtn, item)
        fogmenu.AppendItem(item)

        item = wx.MenuItem(fogmenu, wx.ID_ANY, "Fog Color", "Fog Color")
        self.Bind(wx.EVT_MENU, self.OnFogColorBtn, item)
        fogmenu.AppendItem(item)

        menu = wx.Menu()

        if self.toolWnd.gmToolBar.IsShown():
            menu.AppendMenu(wx.ID_ANY, "Map", mapmenu)
            menu.AppendMenu(wx.ID_ANY, "Background", bgmenu)
            menu.AppendMenu(wx.ID_ANY, "Fog", fogmenu)
            menu.AppendSeparator()
            item = wx.MenuItem(menu, wx.ID_ANY, "Miniture Properties", "Miniture Properties")
            #self.Bind(wx.EVT_MENU, self.OnColorBtn, item)
            menu.AppendItem(item)
            menu.AppendSeparator()

        item = wx.MenuItem(menu, wx.ID_ANY, "Whiteboard Color", "Whiteboard Color")
        self.Bind(wx.EVT_MENU, self.OnColorBtn, item)
        menu.AppendItem(item)


        def ObjectMenu(event):
            id = event.GetId()
            objid = int(menu.GetHelpString(id))
            menuname = menu.GetLabel(id)
            obj = self.overObjects[objid]

            if menuname == "Move To Back":
                self.MoveToBack(obj)

            elif menuname == "Move Back":
                self.MoveBack(obj)

            elif menuname == "Move Forward":
                self.MoveForward(obj)

            elif menuname == "Move To Front":
                self.MoveToFront(obj)

            elif menuname == "Remove":
                self.zOrder[obj.zOrder].remove(obj)
                obj.Update()

            self.Unbind(wx.EVT_MENU)
            self.overObjects.remove(obj)


        if len(self.overObjects):
            menu.AppendSeparator()

        id = 0
        for obj in self.overObjects:
            if obj.IsShown() or self.toolWnd.gmToolBar.IsShown():
                objmenu = wx.Menu()
                item = wx.MenuItem(objmenu, wx.ID_ANY, "Move To Back", str(id))
                self.Bind(wx.EVT_MENU, ObjectMenu, item)
                objmenu.AppendItem(item)
                item = wx.MenuItem(objmenu, wx.ID_ANY, "Move Back", str(id))
                self.Bind(wx.EVT_MENU, ObjectMenu, item)
                objmenu.AppendItem(item)
                item = wx.MenuItem(objmenu, wx.ID_ANY, "Move Forward", str(id))
                self.Bind(wx.EVT_MENU, ObjectMenu, item)
                objmenu.AppendItem(item)
                item = wx.MenuItem(objmenu, wx.ID_ANY, "Move To Front", str(id))
                self.Bind(wx.EVT_MENU, ObjectMenu, item)
                objmenu.AppendItem(item)
                objmenu.AppendSeparator()
                if obj.IsShown():
                    item = wx.MenuItem(objmenu, wx.ID_ANY, "Hide", str(id))
                    self.Bind(wx.EVT_MENU, obj.Hide, item)
                    objmenu.AppendItem(item)
                    objmenu.AppendSeparator()
                elif self.toolWnd.gmToolBar.IsShown():
                    item = wx.MenuItem(objmenu, wx.ID_ANY, "Show", str(id))
                    self.Bind(wx.EVT_MENU, obj.Show, item)
                    objmenu.AppendItem(item)
                    objmenu.AppendSeparator()
                item = wx.MenuItem(objmenu, wx.ID_ANY, "Remove", str(id))
                self.Bind(wx.EVT_MENU, ObjectMenu, item)
                objmenu.AppendItem(item)
                item = wx.MenuItem(objmenu, wx.ID_ANY, "Properties", str(id))
                self.Bind(wx.EVT_MENU, obj.ShowProperties, item)
                objmenu.AppendItem(item)
                menu.AppendMenu(wx.ID_ANY, obj.GetName(), objmenu)

        menu.AppendSeparator()
        item = wx.MenuItem(menu, wx.ID_ANY, "Remove All Objects", "Remove All Whiteboard Items")
        self.Bind(wx.EVT_MENU, self.OnRemoveAllObjects, item)
        menu.AppendItem(item)

        self.PopupMenu(menu)


    def OnRemoveAllObjects(self, event):
        for layer in self.zOrder:
            for i in xrange(len(self.zOrder[layer])):
                del self.zOrder[layer][0]

        self.zOrder = {}
        self.zOrder['tiles'] = []
        self.zOrder["back"] = []
        self.zOrder["front"] = []
        if event != None:
            self.UpdateMap()

    def MoveToBack(self, object):
        self.zOrder[object.zOrder].remove(object)
        self.zOrder['back'].append(object)
        object.zOrder = 'back'
        self.UpdateMap()

    def MoveToFront(self, object):
        self.zOrder[object.zOrder].remove(object)
        self.zOrder['front'].append(object)
        object.zOrder = 'front'
        self.UpdateMap()

    def MoveBack(self, object):
        self.zOrder[object.zOrder].remove(object)

        zl = self.zOrder.keys()
        zl.remove('back')
        zl.remove('front')
        zl.remove('tiles')
        zl.sort()
        lzo = 1
        if len(zl):
            lzo = zl.pop()

        if object.zOrder == 'back' or object.zOrder == 1:
            self.zOrder['back'].append(object)
            object.zOrder = 'back'
        elif object.zOrder == 'front':
            if not self.zOrder.has_key(lzo):
                self.zOrder[lzo] = []
            self.zOrder[lzo].append(object)
            object.zOrder = lzo
        else:
            object.zOrder -= 1
            if not self.zOrder.has_key(object.zOrder):
                self.zOrder[object.zOrder] = []
            self.zOrder[object.zOrder].append(object)
        self.UpdateMap()

    def MoveForward(self, object):
        self.zOrder[object.zOrder].remove(object)

        zl = self.zOrder.keys()
        zl.remove('back')
        zl.remove('front')
        zl.remove('tiles')
        zl.sort()
        lzo = 1
        if len(zl):
            lzo = zl.pop()

        if object.zOrder == 'back':
            if not self.zOrder.has_key(1):
                self.zOrder[1] = []
            self.zOrder[1].append(object)
            object.zOrder = 1
        elif z == 'front':
            self.zOrder['front'].append(object)
            object.zOrder = 'front'
        else:
            object.zOrder += 1
            if not self.zOrder.has_key(object.zOrder):
                self.zOrder[object.zOrder] = []
            self.zOrder[object.zOrder].append(object)
        self.UpdateMap()

    def OnScroll(self, event):
        event.Skip()
        self.Refresh()

    def OnZoomTimer(self, event):
        if (time.time() - self.lastZoomTime) >= 3 and self.lastZoomScale != self.zoomScale:
            #Send Zoome Notice to other clients
            self.lastZoomTime = time.time()
            self.lastZoomScale = self.zoomScale

    def OnRoleTimer(self, event):
        #Figure out the users role
        if self.session.my_role() == self.session.ROLE_GM:
            self.role = 'GM'
        elif self.session.my_role() == self.session.ROLE_PLAYER:
            self.role = 'Player'
        else:
            self.role = 'Lurker'

        if self.role == 'GM' and not self.toolWnd.gmToolBar.IsShown() and not (str(self.session.group_id) == '0' and str(self.session.status) == '1'):
            self.toolWnd.Freeze()
            self.toolWnd.gmToolBar.Show()
            self.toolWnd.Thaw()
        elif self.role == 'Player' and not (str(self.session.group_id) == '0' and str(self.session.status) == '1'):
            if self.toolWnd.gmToolBar.IsShown():
                self.toolWnd.Freeze()
                self.toolWnd.gmToolBar.Hide()
                self.toolWnd.Thaw()

            if not self.toolWnd.playerToolBar.IsShown():
                self.toolWnd.Freeze()
                self.toolWnd.playerToolBar.Show()
                self.toolWnd.Thaw()
        elif self.role == 'Lurker' or (str(self.session.group_id) == '0' and str(self.session.status) == '1'):
            if self.toolWnd.playerToolBar.IsShown():
                self.toolWnd.Freeze()
                self.toolWnd.gmToolBar.Hide()
                self.toolWnd.playerToolBar.Hide()
                self.toolWnd.Thaw()

        try:
            self.toolWnd.Layout()
        except:
            pass

    def OnClose(self, event):
        self.zoomTimer.Stop()
        self.roleTimer.Stop()
        event.Skip()

    #Toolbar Events
    def OnDefaultBtn(self, event):
        self.Clear()
        wx.CallAfter(self.UpdateMap)

    def OnColorBtn(self, event):
        newcolor = self.RGBHex.do_hex_color_dlg(self.toolWnd)
        if newcolor == None:
            return

        self.whiteboardColor = newcolor
        r, g, b = self.RGBHex.rgb_tuple(self.whiteboardColor)
        self.toolWnd._SetColorBtn(wx.Color(r, g, b, 255), self.toolWnd.ColorBtn)

    def OnBGColorBtn(self, event):
        newcolor = self.RGBHex.do_hex_color_dlg(self.toolWnd)
        if newcolor == None:
            return

        self.backgroundColor = newcolor
        r, g, b = self.RGBHex.rgb_tuple(self.backgroundColor)
        self.toolWnd._SetColorBtn(wx.Color(r, g, b, 255), self.toolWnd.BGColorBtn)
        self.UpdateMap()

    def OnFogColorBtn(self, event):
        newcolor = self.RGBHex.do_hex_color_dlg(self.toolWnd)
        if newcolor == None:
            return

        self.fogColor = newcolor
        r, g, b = self.RGBHex.rgb_tuple(self.fogColor)
        self.toolWnd._SetColorBtn(wx.Color(r, g, b, 255), self.toolWnd.FogColorBtn)
        self.UpdateMap()

    def OnExlusiveBtn(self, event):
        id = event.GetId()
        #This is backwards because the Toggle Switch does not get set until AFTER The mouse gets released
        if not self.toolWnd.exclusiveToolList[id].GetToggled():
            self.toolWnd.Freeze()
            #Disable all mutualy exclusive tools
            for btn in self.toolWnd.exclusiveToolList:
                if self.toolWnd.exclusiveToolList[btn].GetId() != id:
                    self.toolWnd.exclusiveToolList[btn].SetToggled(False)
            self.toolWnd.Thaw()
        else:
            wx.CallAfter(self.toolWnd.SelectorBtn.SetToggled, True)

    def OnFogBtn(self, event):
        if not self.toolWnd.FogBtn.GetToggled():
            self.useFog = True
        else:
            self.useFog = False
            self.toolWnd.Freeze()
            self.toolWnd.SelectorBtn.SetToggled(True)
            self.toolWnd.FogToolBtn.SetToggled(False)
            self.toolWnd.Thaw()
        self.fogRegion = []
        self.UpdateMap()

    def OnBGBtn(self, event):
        dlg = wx.Dialog(self.toolWnd, wx.ID_ANY, title="Background Properties")
        sizer = wx.BoxSizer(wx.HORIZONTAL)

        filename = wx.TextCtrl(dlg, wx.ID_ANY)
        filename.Hide()

        bgpath = wx.TextCtrl(dlg, wx.ID_ANY)
        if self.bgPath != None:
            bgpath.SetValue(self.bgPath)

        bgtype = wx.Choice(dlg, wx.ID_ANY, choices=['Image', 'Texture'])
        bgtype.SetStringSelection(self.bgType)

        browsebtn = wx.Button(dlg, wx.ID_ANY, "Browse")
        okbtn = wx.Button(dlg, wx.ID_OK)
        cancelbtn = wx.Button(dlg, wx.ID_CANCEL)

        sizer.Add(wx.StaticText(dlg, wx.ID_ANY, "Image Path"), 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 2)
        sizer.Add(bgpath, 0, wx.EXPAND|wx.ALL, 3)
        sizer.Add(wx.StaticText(dlg, wx.ID_ANY, "Image Type"), 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 2)
        sizer.Add(bgtype, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 3)
        sizer.Add(browsebtn, 0, wx.EXPAND|wx.ALL, 2)
        sizer.Add(okbtn, 0, wx.EXPAND|wx.ALL, 3)
        sizer.Add(cancelbtn, 0, wx.EXPAND|wx.ALL, 2)

        dlg.SetSizer(sizer)
        dlg.SetAutoLayout(True)
        dlg.Fit()

        def OnBrowse(event):
            filedlg = wx.FileDialog(self, "Select an Image File", self.dir_struct["user"], wildcard="Image files (*.bmp, *.gif, *.jpg, *.png)|*.bmp;*.gif;*.jpg;*.png", style=wx.HIDE_READONLY|wx.OPEN)
            if filedlg.ShowModal() != wx.ID_OK:
                filedlg.Destroy()
                return

            bgpath.SetValue(filedlg.GetPath())
            filename.SetValue(filedlg.GetFilename())

        dlg.Bind(wx.EVT_BUTTON, OnBrowse, browsebtn)
        dlg.Show()

        if not dlg.ShowModal() == wx.ID_OK:
            dlg.Destroy()
            return

        self.bgType = bgtype.GetStringSelection()

        if bgpath.GetValue().lower().find('http:') == -1:
            file = open(bgpath.GetValue(), "rb")
            imgdata = file.read()
            file.close()

            (imgtype,j) = mimetypes.guess_type(filename.GetValue())

            postdata = urllib.urlencode({'filename':filename.GetValue(), 'imgdata':imgdata, 'imgtype':imgtype})

            thread.start_new_thread(self.__Upload, (postdata, bgpath.GetValue(), "Background"))
        else:
            self.bgImage = self._LoadImage(bgpath.GetValue())
            self.UpdateMap()


    #Private Methods
    def _SetSize(self, size):
        if size[0] == -1:
            size[0] = self.size[0]
        if size[1] == -1:
            size[1] = self.size[1]

        if size[0] < 300:
            size = (300, size[1])
        if size[1] < 300:
            size = (size[0], 300)

        size1  = self.GetClientSizeTuple()

        if size[0] < size1[0]:
            size = (size1[0], size[1])
        if size[1] < size1[1]:
            size = (size[0], size1[1])

        self.sizeChanged = 1
        self.size = size
        self._FixScroll()

    def _FixScroll(self):
        scale = self.zoomScale
        pos = self.GetViewStart()
        unit = self.GetScrollPixelsPerUnit()
        pos = [pos[0]*unit[0],pos[1]*unit[1]]
        size = self.GetClientSize()
        unit = [10*scale,10*scale]
        if (unit[0] == 0 or unit[1] == 0):
            return
        pos[0] /= unit[0]
        pos[1] /= unit[1]
        mx = [int(self.size[0]*scale/unit[0])+1, int(self.size[1]*scale/unit[1]+1)]
        self.SetScrollbars(unit[0], unit[1], mx[0], mx[1], pos[0], pos[1])

    def _LoadImage(self, path, miniId=None):
        if self.imageCache.has_key(path):
            return self.imageCache[path]

        while len(self.imageCache) > int(self.settings.get_setting("ImageCacheSize")):
            keys = self.imageCache.keys()
            del self.imageCache[keys[0]]


        thread.start_new_thread(self.__DownloadImage, (path, miniId))

        return wx.Bitmap(orpg.dirpath.dir_struct["icon"] + "fetching.png", wx.BITMAP_TYPE_PNG)

    def _ClearCache(self):
        for key in self.imageCache:
            del self.imageCache[key]

    #Threads
    def __Upload(self, postdata, filename, type="Background"):
        self.lock.acquire()

        url = self.settings.get_setting('ImageServerBaseURL')
        file = urllib.urlopen(url, postdata)
        recvdata = file.read()
        file.close()
        try:
            xml_dom = minidom.parseString(recvdata)._get_documentElement()

            if xml_dom.nodeName == 'path':
                path = xml_dom.getAttribute('url')
                path = urllib.unquote(path)

                if type == 'Background':
                    self.bgImage = self._LoadImage(path)
                    self.bgPath = path

                else:
                    self.minis.append(self.mapLayer.AddMiniture(path))

                self.UpdateMap()

            else:
                self.chat.InfoPost(xml_dom.getAttribute('msg'))
        except Exception, e:
            print e
            print recvdata

        self.lock.release()

    def __DownloadImage(self, path, miniId):
        self.lock.acquire()

        uriPath = urllib.unquote(path)
        try:
            data = urllib.urlretrieve(uriPath)

            if data[0] and data[1].getmaintype() == "image":
                imageType = data[1].gettype()
                img = wx.ImageFromMime(data[0], imageType).ConvertToBitmap()
                self.imageCache[path] = img

                if miniId == None:
                    self.bgImage = img
                    if self.bgType == 'Image':
                        self._SetSize((img.GetHeight(), img.GetWidth()))

                else:
                    mini = self.GetMiniById(miniId)
                    mini.image = img

                self.UpdateMap()
        except Exception, e:
            self.chat.InfoPost("Unable to resolve/open the specified URI; image was NOT laoded:" + path)

        urllib.urlcleanup()
        self.lock.release()