#Boa:FramePanel:ImageEditorPanel

import os, math, tempfile
from cStringIO import StringIO

import wx
from wx.lib.anchors import LayoutAnchors

import Utils, Plugins, Models.ResourceSupport
from Utils import _

# draw destination consts
ddCanvas = 1
ddGrid = 2

[wxID_IMAGEEDITORPANEL, wxID_IMAGEEDITORPANELBGCOLBTN, 
 wxID_IMAGEEDITORPANELBRUSHCOLBTN, wxID_IMAGEEDITORPANELEDITWINDOW, 
 wxID_IMAGEEDITORPANELFGCOLBTN, wxID_IMAGEEDITORPANELMODECHOICE, 
 wxID_IMAGEEDITORPANELPENBRUSHWINDOW, wxID_IMAGEEDITORPANELSLIDER1, 
 wxID_IMAGEEDITORPANELSPINBUTTON1, wxID_IMAGEEDITORPANELSPINBUTTON2, 
 wxID_IMAGEEDITORPANELSPINBUTTON3, wxID_IMAGEEDITORPANELSTATICTEXT1, 
] = [wx.NewId() for _init_ctrls in range(12)]

class ImageEditorPanel(wx.Panel):
    def _init_utils(self):
        # generated method, don't edit
        self.cursorCross = wx.StockCursor(id=wx.CURSOR_CROSS)

        self.cursorMove = wx.StockCursor(id=wx.CURSOR_SIZING)

        self.cursorDraw = wx.StockCursor(id=wx.CURSOR_PENCIL)

    def _init_ctrls(self, prnt):
        # generated method, don't edit
        wx.Panel.__init__(self, id=wxID_IMAGEEDITORPANEL,
              name='ImageEditorPanel', parent=prnt, pos=wx.Point(466, 318),
              size=wx.Size(586, 356),
              style=wx.SUNKEN_BORDER | wx.TAB_TRAVERSAL)
        self._init_utils()
        self.SetAutoLayout(True)
        self.SetClientSize(wx.Size(578, 329))

        self.modeChoice = wx.Choice(choices=self.drawingModes,
              id=wxID_IMAGEEDITORPANELMODECHOICE, name='modeChoice',
              parent=self, pos=wx.Point(8, 7), size=wx.Size(64, 21), style=0)
        self.modeChoice.SetToolTipString(_('Current drawing mode'))
        self.modeChoice.Bind(wx.EVT_CHOICE, self.OnModeChoiceChoice,
              id=wxID_IMAGEEDITORPANELMODECHOICE)

        self.FGColBtn = wx.Button(id=wxID_IMAGEEDITORPANELFGCOLBTN, label='',
              name='FGColBtn', parent=self, pos=wx.Point(78, 5),
              size=wx.Size(24, 24), style=0)
        self.FGColBtn.SetBackgroundColour(wx.Colour(0, 0, 0))
        self.FGColBtn.SetToolTipString(_('Pen colour'))
        self.FGColBtn.Bind(wx.EVT_BUTTON, self.OnFgcolbtnButton,
              id=wxID_IMAGEEDITORPANELFGCOLBTN)

        self.spinButton3 = wx.SpinButton(id=wxID_IMAGEEDITORPANELSPINBUTTON3,
              name='spinButton3', parent=self, pos=wx.Point(106, 5),
              size=wx.Size(16, 24), style=wx.SP_VERTICAL)
        self.spinButton3.SetRange(0, 32)
        self.spinButton3.SetToolTipString(_('Pen width'))
        self.spinButton3.Bind(wx.EVT_COMMAND_SCROLL,
              self.OnSpinbutton3CommandScroll,
              id=wxID_IMAGEEDITORPANELSPINBUTTON3)

        self.spinButton2 = wx.SpinButton(id=wxID_IMAGEEDITORPANELSPINBUTTON2,
              name='spinButton2', parent=self, pos=wx.Point(122, 5),
              size=wx.Size(16, 24), style=wx.SP_VERTICAL)
        self.spinButton2.SetRange(0, 11)
        self.spinButton2.SetToolTipString(_('Pen style'))
        self.spinButton2.Bind(wx.EVT_COMMAND_SCROLL,
              self.OnSpinbutton2CommandScroll,
              id=wxID_IMAGEEDITORPANELSPINBUTTON2)

        self.penBrushWindow = wx.Window(id=wxID_IMAGEEDITORPANELPENBRUSHWINDOW,
              name='penBrushWindow', parent=self, pos=wx.Point(137, 5),
              size=wx.Size(32, 24), style=wx.SUNKEN_BORDER)
        self.penBrushWindow.SetToolTipString(_('Pen / Brush preview'))
        self.penBrushWindow.Bind(wx.EVT_PAINT, self.OnPenBrushWindowPaint)

        self.spinButton1 = wx.SpinButton(id=wxID_IMAGEEDITORPANELSPINBUTTON1,
              name='spinButton1', parent=self, pos=wx.Point(168, 5),
              size=wx.Size(16, 24), style=wx.SP_VERTICAL)
        self.spinButton1.SetRange(0, 7)
        self.spinButton1.SetToolTipString(_('Brush style'))
        self.spinButton1.Bind(wx.EVT_COMMAND_SCROLL,
              self.OnSpinbutton1CommandScroll,
              id=wxID_IMAGEEDITORPANELSPINBUTTON1)

        self.brushColBtn = wx.Button(id=wxID_IMAGEEDITORPANELBRUSHCOLBTN,
              label='', name='brushColBtn', parent=self, pos=wx.Point(189, 5),
              size=wx.Size(24, 24), style=0)
        self.brushColBtn.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.brushColBtn.SetToolTipString(_('Brush colour'))
        self.brushColBtn.Bind(wx.EVT_BUTTON, self.OnBrushcolbtnButton,
              id=wxID_IMAGEEDITORPANELBRUSHCOLBTN)

        self.BGColBtn = wx.Button(id=wxID_IMAGEEDITORPANELBGCOLBTN, label='',
              name='BGColBtn', parent=self, pos=wx.Point(213, 5),
              size=wx.Size(24, 24), style=0)
        self.BGColBtn.SetBackgroundColour(wx.Colour(192, 192, 192))
        self.BGColBtn.SetToolTipString(_('Background brush colour'))
        self.BGColBtn.Bind(wx.EVT_BUTTON, self.OnBgcolbtnButton,
              id=wxID_IMAGEEDITORPANELBGCOLBTN)

        self.slider1 = wx.Slider(id=wxID_IMAGEEDITORPANELSLIDER1, maxValue=25,
              minValue=1, name='slider1', parent=self, point=wx.Point(244, 5),
              size=wx.Size(108, 24), style=wx.SL_HORIZONTAL, value=16)
        self.slider1.SetToolTipString(_('Zoom factor'))
        self.slider1.Bind(wx.EVT_SCROLL, self.OnSlider1ScrollThumbtrack)

        self.editWindow = wx.ScrolledWindow(id=wxID_IMAGEEDITORPANELEDITWINDOW,
              name='editWindow', parent=self, pos=wx.Point(8, 34),
              size=wx.Size(561, 288), style=wx.SUNKEN_BORDER)
        self.editWindow.SetBackgroundColour(wx.Colour(255, 255, 255))
        self.editWindow.SetConstraints(LayoutAnchors(self.editWindow, True,
              True, True, True))
        self.editWindow.Bind(wx.EVT_PAINT, self.OnEditWindowPaint)
        self.editWindow.Bind(wx.EVT_LEFT_DOWN, self.OnEditWindowLeftDown)
        self.editWindow.Bind(wx.EVT_LEFT_UP, self.OnEditWindowLeftUp)
        self.editWindow.Bind(wx.EVT_MOTION, self.OnEditWindowMotion)
        self.editWindow.Bind(wx.EVT_SCROLLWIN, self.OnEditWindowScroll)

        self.staticText1 = wx.StaticText(id=wxID_IMAGEEDITORPANELSTATICTEXT1,
              label=_('Image info'), name='staticText1', parent=self,
              pos=wx.Point(360, 2), size=wx.Size(208, 27),
              style=wx.ST_NO_AUTORESIZE)

    def __init__(self, parent):
        self.drawingModes = [_('Select'), _('Draw'), _('Line'), _('Circle'), _('Box'), _('Fill'), _('Colour')]
        self._init_ctrls(parent)

        self.currentCursor = None

        self.drawingMethMap = {_('Select'): (ddCanvas|ddGrid, self.drawSelection),
                               _('Draw')  : (ddCanvas,        self.drawPoint),
                               _('Line')  : (ddCanvas|ddGrid, self.drawLine),
                               _('Circle'): (ddCanvas|ddGrid, self.drawCircle),
                               _('Box')   : (ddCanvas|ddGrid, self.drawBox),
                               _('Fill')  : (ddCanvas,        self.drawFill),
                               _('Move')  : (ddCanvas|ddGrid, self.drawMove),
                               _('Colour'): (ddCanvas,        self.drawPickColour),
                              }

        self.mode = ''
        self.setMode(_('Draw'))

        self.mDC = self.bmp = None

        self.fgcol = wx.BLACK
        self.fgpen = wx.Pen(self.fgcol, 1, wx.SOLID)
        self.bgcol = wx.LIGHT_GREY
        self.bgbsh = wx.Brush(self.bgcol)
        self.brush = wx.Brush(wx.WHITE, wx.TRANSPARENT)
        self.invpen = wx.Pen(wx.BLUE, 0, wx.TRANSPARENT)
        self.selpen = wx.Pen(wx.WHITE, 2, wx.SOLID)

        self.x = self.y = 0

        self.modeChoice.SetSelection(1)

        self.offset = 0, 0

        self.prevSelRect = ()
        self.prevLineSeg = ()
        self.prevPointCol = ()


    brushStyles = [wx.TRANSPARENT, wx.SOLID, wx.BDIAGONAL_HATCH, wx.CROSSDIAG_HATCH,
                   wx.FDIAGONAL_HATCH, wx.CROSS_HATCH, wx.HORIZONTAL_HATCH,
                   wx.VERTICAL_HATCH]

    penStyles = [wx.SOLID, wx.TRANSPARENT, wx.DOT, wx.LONG_DASH, wx.SHORT_DASH, wx.DOT_DASH,
                 wx.BDIAGONAL_HATCH, wx.CROSSDIAG_HATCH, wx.FDIAGONAL_HATCH, wx.CROSS_HATCH,
                 wx.HORIZONTAL_HATCH, wx.VERTICAL_HATCH]

    extTypeMap = {'.bmp': wx.BITMAP_TYPE_BMP,
                  '.gif': wx.BITMAP_TYPE_GIF,
                  '.jpg': wx.BITMAP_TYPE_JPEG,
                  '.png': wx.BITMAP_TYPE_PNG,
                  '.ico': wx.BITMAP_TYPE_ICO,
                  }

#---Public methods--------------------------------------------------------------

    def initImageData(self, ext, data):
        """ Initialise editor with data """
        if data:
            self.mDC = wx.MemoryDC()
            self.bmp = wx.BitmapFromImage(wx.ImageFromStream(StringIO(data)))
            self.mDC.SelectObject(self.bmp)
        else:
            self.mDC, self.bmp = self.getTempMemDC(16, 16)
            brush = wx.Brush(self.bgcol)
            self.mDC.SetBackground(brush)
            self.mDC.Clear()

        # Default back to png when opening data from source
        if ext == '.py':
            ext = '.png'

        self.imgExt = ext

        self.editWindow.Refresh()

        self.mDCundo, self.bmpundo = self.getTempMemDC(self.bmp.GetWidth(),
                                     self.bmp.GetHeight())
        self.selundo = None
        self.snapshot()

        self.updateScrollbars()
        self.updateImageInfo()

    def getImageData(self, ext=''):
        """ Returns the current bitmap data """
        if not ext:
            ext = self.imgExt

        fn = tempfile.mktemp()
        #if ext == '?': ext = '.png'
        tpe = self.extTypeMap[ext.lower()]
        self.bmp.SaveFile(fn, tpe)
        try:
            return open(fn, 'rb').read()
        finally:
            os.remove(fn)

    def imageModified(self):
        """ Called whenever image is modified, override to catch """
        pass

#---Utils-----------------------------------------------------------------------

    def setMode(self, mode, updateGUI=False):
        if self.mode != mode:
            if mode in (_('Draw'), _('Line'), _('Circle'), _('Box'), _('Fill')):
                self.currentCursor = self.cursorDraw
            else:
                self.currentCursor = self.cursorCross
            self.editWindow.SetCursor(self.currentCursor)

            self.clearState()

            self.mode = mode
            self.drawDest, self.drawMeth = self.drawingMethMap[self.mode]

            if updateGUI:
                self.modeChoice.SetStringSelection(mode)

    def clearState(self):
        self.dragoffset = self.dragpos = self.dragbmp = self.dragsrcrect = None
        self.sel = self.line = self.circle = self.box = None
        self.editWindow.SetCursor(self.currentCursor)

    def snapshot(self):
        self.mDC.SetUserScale(1.0, 1.0)
        self.mDCundo.Blit(0, 0, self.bmp.GetWidth(), self.bmp.GetHeight(),
              self.mDC, 0, 0)
        if self.sel: self.selundo = self.sel[:]
        else: self.selundo = None

    def getImgPos(self, event):
        x, y = event.GetPositionTuple()
        x, y = self.editWindow.CalcUnscrolledPosition(x, y)
        scale = self.slider1.GetValue()
        return ((x-self.offset[0]) / scale, (y-self.offset[1]) / scale)

    def getTempMemDC(self, width, height):
        bmp = wx.EmptyBitmap(width, height)
        memDC = wx.MemoryDC()
        memDC.SelectObject(bmp)
        return memDC, bmp

    def getSelBmp(self):
        if self.sel:
            x1, y1, x2, y2 = self.sel
            mDC, bmp = self.getTempMemDC(x2-x1+1, y2-y1+1)
            self.mDC.SetUserScale(1.0, 1.0)
            mDC.Blit(0, 0, x2-x1+1, y2-y1+1, self.mDC, x1, y1)
            mDC.SelectObject(wx.NullBitmap)
            return bmp
        return None

    def setMemDCBmp(self, bmp):
        if not bmp or not bmp.Ok():
            raise Exception, _('Invalid bitmap')
        self.mDC.SelectObject(wx.NullBitmap)
        self.bmp = bmp
        self.mDC.SelectObject(self.bmp)

    def updateScrollbars(self):
        scale = self.slider1.GetValue()
        xPos, yPos = self.editWindow.GetViewStart()
        self.editWindow.SetScrollbars(scale, scale,
              self.bmp.GetWidth(), self.bmp.GetHeight(), xPos, yPos)

    def updateImageInfo(self):
        if self.imgExt: ext = self.imgExt[1:].upper()
        else:           ext = 'UNKNOWN'

        w, h, d = self.bmp.GetWidth(), self.bmp.GetHeight(), self.bmp.GetDepth()

        msk = self.bmp.GetMask()
        if msk: m = 'Image is masked'
        else:   m = 'Image is not masked'

        if self.bmp.Ok(): x = ''
        else:             x = 'The bitmap is not valid!'

        text = '%s: (%s, %s), depth: %s\n%s. %s'%(ext, w, h, d, m, x)
        self.staticText1.SetLabel(text)

#---Drawing methods-------------------------------------------------------------

    def undo(self):
        self.mDC.SetUserScale(1.0, 1.0)
        self.mDC.Blit(0, 0, self.bmp.GetWidth(), self.bmp.GetHeight(),
              self.mDCundo, 0, 0)

    def drawSelection(self, event, state, dc=None):
        if not dc:
            if state == 'start':
                self.sel = self.getImgPos(event) * 2
            elif state in ('drag', 'end'):
                self.sel = self.sel[:2] + self.getImgPos(event)
                if state == 'end':
                    x1, y1, x2, y2 = self.sel
                    self.sel = min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2)

            if self.sel != self.prevSelRect:
                if state == 'end':
                    self.prevSelRect = ()
                else:
                    self.prevSelRect = self.sel
                self.editWindow.Refresh()

        if dc and self.sel:
            x1, y1, x2, y2 = self.sel
            x1, y1, x2, y2 = min(x1, x2), min(y1, y2), max(x1, x2), max(y1, y2)
            xoffset, yoffset = self.offset
            scale = self.slider1.GetValue()
            dc.SetLogicalFunction(wx.XOR)
            dc.SetPen(self.selpen)
            dc.SetBrush(wx.TRANSPARENT_BRUSH)
            dc.DrawRectangle(xoffset + x1 * scale - 1, yoffset + y1 * scale - 1,
                   (x2 - x1 + 1) * scale + 4, (y2 - y1 + 1) * scale + 4)

    def drawPoint(self, event, state):
        if state == 'start':
            self.snapshot()

        imgx, imgy = self.getImgPos(event)
        self.mDC.SetPen(self.fgpen)
        self.mDC.SetUserScale(1.0, 1.0)
        self.mDC.DrawPoint(imgx, imgy)
        self.editWindow.Refresh()

        if imgx >=0 and imgx < self.bmp.GetWidth() and \
           imgy >=0 and imgy < self.bmp.GetHeight():
            self.imageModified()

    def drawLine(self, event, state, dc=None):
        if not dc:
            if state == 'start':
                self.line = self.getImgPos(event) * 2
            elif self.line:
                self.line = self.line[:2] + self.getImgPos(event)
                if state == 'end':
                    self.snapshot()
                    self.mDC.SetLogicalFunction(wx.COPY)
                    self.mDC.SetPen(self.fgpen)
                    self.mDC.SetUserScale(1.0, 1.0)
                    self.mDC.DrawLine(*self.line)
                    self.mDC.DrawPoint(self.line[2], self.line[3])

                    x1, y1, x2, y2 = self.line
                    w, h = self.bmp.GetWidth(), self.bmp.GetHeight()
                    if not ((x1 < 0 and x2 < 0) or (x1 >= w and x2 >= w) or \
                            (y1 < 0 and y2 < 0) or (y1 >= h and y2 >= h)):
                        self.imageModified()

                    self.line = None

            if self.prevLineSeg != self.line:
                if state == 'end':
                    self.prevLineSeg = ()
                else:
                    self.prevLineSeg = self.line

                self.editWindow.Refresh()

        if dc and self.line:
            x1, y1, x2, y2 = self.line
            xoffset, yoffset = self.offset
            scale = self.slider1.GetValue()
            dc.SetLogicalFunction(wx.XOR)
            dc.SetPen(self.selpen)
            dc.DrawLine(xoffset + x1 * scale + scale/2, yoffset + y1 * scale + scale/2,
                        xoffset + x2 * scale + scale/2, yoffset + y2 * scale + scale/2)

    def drawBox(self, event, state, dc=None):
        if not dc:
            if state == 'start':
                self.box = self.getImgPos(event) * 2
            else:
                self.box = self.box[:2] + self.getImgPos(event)
                if state == 'end':
                    self.snapshot()
                    self.mDC.SetLogicalFunction(wx.COPY)
                    self.mDC.SetPen(self.fgpen)
                    self.mDC.SetUserScale(1.0, 1.0)
                    self.mDC.SetBrush(self.brush)
                    x1, y1, x2, y2 = self.box
                    self.mDC.DrawRectangle(x1, y1, x2-x1+1, y2-y1+1)
                    self.editWindow.Refresh()

                    x1, y1, x2, y2 = self.box
                    w, h = self.bmp.GetWidth(), self.bmp.GetHeight()
                    if not ((x1 < 0 and x2 < 0) or (x1 >= w and x2 >= w) or \
                            (y1 < 0 and y2 < 0) or (y1 >= h and y2 >= h)):
                        self.imageModified()

                    self.box = None

            self.editWindow.Refresh()

        if dc and self.box:
            x1, y1, x2, y2 = self.box
            xoffset, yoffset = self.offset
            scale = self.slider1.GetValue()
            dc.SetLogicalFunction(wx.XOR)
            dc.SetPen(self.selpen)
            dc.SetBrush(wx.TRANSPARENT_BRUSH)
            dc.DrawRectangle(xoffset + x1 * scale - 1, yoffset + y1 * scale - 1,
                       (x2 - x1 + 1) * scale + 4, (y2 - y1 + 1) * scale + 4)

    def drawCircle(self, event, state, dc=None):
        if not dc:
            if state == 'start':
                self.circle = self.getImgPos(event) + (0,)
            elif state in ('drag', 'end') and self.circle:
                x1, y1, x2, y2 = self.circle[:2] + self.getImgPos(event)
                rad = math.sqrt(pow(x2-x1, 2) + pow(y2-y1, 2)) + 0.5
                self.circle = (x1, y1, rad)
                if state == 'end':
                    self.snapshot()
                    self.mDC.SetLogicalFunction(wx.COPY)
                    self.mDC.SetPen(self.fgpen)
                    self.mDC.SetUserScale(1.0, 1.0)
                    self.mDC.SetBrush(self.brush)
                    self.mDC.DrawEllipse(int(x1-rad+0.5), int(y1-rad+0.5),
                                         int(rad*2), int(rad*2))

                    x, y, rad = self.circle
                    x1, y1 = x-rad+0.5, y-rad+0.5
                    x2, y2 = x1 +rad*2, y1 +rad*2
                    w, h = self.bmp.GetWidth(), self.bmp.GetHeight()
                    if not ((x1 < 0 and x2 < 0) or (x1 >= w and x2 >= w) or \
                            (y1 < 0 and y2 < 0) or (y1 >= h and y2 >= h)):
                        self.imageModified()

                    self.circle = None

            self.editWindow.Refresh()

        if dc and self.circle:
            x, y, rad = self.circle
            xoffset, yoffset = self.offset
            scale = self.slider1.GetValue()
            dc.SetLogicalFunction(wx.XOR)
            dc.SetPen(self.selpen)
            dc.SetBrush(wx.TRANSPARENT_BRUSH)
            dc.DrawEllipse(int(xoffset + (x-rad+0.5) * scale),
                           int(yoffset + (y-rad+0.5) * scale),
                           int(rad*scale*2), int(rad*scale*2))

    def drawFill(self, event, state):
        self.snapshot()
        x, y = self.getImgPos(event)
        brush = wx.Brush(self.fgcol)
        self.mDC.SetBrush(brush)
        self.mDC.SetUserScale(1.0, 1.0)
        self.mDC.FloodFill(x, y, self.mDC.GetPixel(x, y))
        self.editWindow.Refresh()

        if x >=0 and x < self.bmp.GetWidth() and \
           y >=0 and y < self.bmp.GetHeight():
            self.imageModified()

    def drawMove(self, event, state, dc=None):
        x1, y1, x2, y2 = self.sel
        if not dc:
            x, y = self.getImgPos(event)
            if state == 'start':
                self.dragoffset = (x1 - x, y1 - y)
                if not self.dragbmp:
                    self.snapshot()
                    self.dragbmp = self.getSelBmp()
                    self.dragsrcrect = x1, y1, x2-x1+1, y2-y1+1
            elif state in ('drag', 'end'):
                # better to only do this once
                self.undo()

                dox, doy = self.dragoffset
                dx, dy = self.dragpos = dox + x, doy + y

                self.mDC.SetUserScale(1.0, 1.0)
                self.mDC.SetLogicalFunction(wx.COPY)
                self.mDC.SetPen(self.invpen)
                self.mDC.SetBrush(self.bgbsh)
                self.mDC.DrawRectangle(*self.dragsrcrect)

                self.mDC.DrawBitmap(self.dragbmp, dx, dy)

                if state == 'end':
                    self.sel = dx, dy, dx + x2-x1, dy + y2-y1

                self.editWindow.Refresh()

                self.imageModified()

        if dc and self.sel and self.dragpos:
            xoffset, yoffset = self.offset
            dx, dy = self.dragpos
            scale = self.slider1.GetValue()
            dc.SetLogicalFunction(wx.XOR)
            dc.SetPen(self.selpen)
            dc.SetBrush(wx.TRANSPARENT_BRUSH)
            dc.DrawRectangle(xoffset + dx * scale - 1, yoffset + dy * scale - 1,
                       (x2 - x1 + 1) * scale + 4, (y2 - y1 + 1) * scale + 4)

    def drawPickColour(self, event, state):
        if state == 'end':
            x, y = self.getImgPos(event)
            newcol = self.mDC.GetPixel(x, y)

            self.fgcol = newcol
            self.fgpen.SetColour(newcol)
            self.FGColBtn.SetBackgroundColour(self.fgcol)

            self.setMode('Draw', updateGUI=True)

    def drawGrid(self, dc):
        pen = wx.Pen(wx.WHITE)
        dc.SetPen(pen)
        dc.SetLogicalFunction(wx.XOR)
        scale = self.slider1.GetValue()
        lines = []
        height = self.bmp.GetHeight()
        width = self.bmp.GetWidth()
        xoffset, yoffset = self.offset
        for y in range(height):
            lines.append( (xoffset, y * scale + yoffset,
                           width * scale + xoffset, y * scale + yoffset) )
        for x in range(width):
            lines.append( (x * scale + xoffset, yoffset,
                           x * scale + xoffset, height * scale + yoffset) )

        dc.DrawLineList(lines)

#---View control events---------------------------------------------------------

    def OnEditWindowPaint(self, event):
        dc = wx.PaintDC(self.editWindow)
        self.editWindow.PrepareDC(dc)

        if not self.mDC or not self.bmp:
            return

        dc.BeginDrawing()
        try:
            scale = self.slider1.GetValue()
            self.mDC.SetUserScale(1.0/scale, 1.0/scale)
            width = self.bmp.GetWidth()*scale
            height = self.bmp.GetHeight()*scale
            xoffset = max((self.editWindow.GetSize().x - width) / 2, 0)
            yoffset = max((self.editWindow.GetSize().y - height) / 2, 0)
            dc.Blit(xoffset, yoffset, width, height, self.mDC, 0, 0)
            self.mDC.SetUserScale(1.0, 1.0)
            self.offset = xoffset, yoffset
            if scale > 5:
                self.drawGrid(dc)
                framesize = 3
            else:
                framesize = 1

            dc.SetLogicalFunction(wx.COPY)
            pen = wx.Pen(wx.BLACK, framesize, wx.SOLID)
            dc.SetPen(pen)
            dc.SetBrush(wx.TRANSPARENT_BRUSH)
            dc.DrawRectangle(xoffset-1 , yoffset-1, width+2, height+2)

            if (self.drawDest & ddGrid) and self.drawMeth:
                self.drawMeth(None, '', dc)
        finally:
            dc.EndDrawing()

    def OnPenBrushWindowPaint(self, event):
        dc = wx.PaintDC(self.penBrushWindow)
        dc.SetBrush(self.brush)
        dc.SetBackground(self.bgbsh)
        dc.SetPen(self.invpen)
        dc.BeginDrawing()
        try:
            cs = self.penBrushWindow.GetClientSize()
            dc.DrawRectangle(0, 0, cs.x, cs.y)
            dc.SetPen(self.fgpen)
            dc.DrawRectangle(cs.x/3-3, cs.y/3-3, cs.x/3+8, cs.y/3+6)
        finally:
            dc.EndDrawing()

    def OnEditWindowLeftDown(self, event):
        self.editWindow.CaptureMouse()

        if self.mode == _('Select') and self.sel:
            x1, y1, x2, y2 = self.sel
            x, y = self.getImgPos(event)
            if x >= x1 and x <= x2 and y >= y1 and y <= y2:
                self.setMode(_('Move'))
                self.sel = x1, y1, x2, y2
        elif self.mode == _('Move') and self.sel:
            x1, y1, x2, y2 = self.sel
            x, y = self.getImgPos(event)
            if not (x >= x1 and x <= x2 and y >= y1 and y <= y2):
                self.setMode(_('Select'))
                self.sel = x1, y1, x2, y2

        if (self.drawDest & ddCanvas) and self.drawMeth:
            self.drawMeth(event, 'start')

    def OnEditWindowLeftUp(self, event):
        if self.editWindow.HasCapture():
            self.editWindow.ReleaseMouse()
            if (self.drawDest & ddCanvas) and self.drawMeth:
                self.drawMeth(event, 'end')

    def OnEditWindowMotion(self, event):
        if event.Dragging() and event.LeftIsDown():
            if (self.drawDest & ddCanvas) and self.drawMeth:
                self.drawMeth(event, 'drag')

        if self.sel and not event.Dragging() and not event.LeftIsDown():
            x1, y1, x2, y2 = self.sel
            x, y = self.getImgPos(event)
            if x >= x1 and x <= x2 and y >= y1 and y <= y2:
                self.editWindow.SetCursor(self.cursorMove)
            else:
                self.editWindow.SetCursor(self.currentCursor)

    def OnSlider1ScrollThumbtrack(self, event):
        self.updateScrollbars()
        self.editWindow.Refresh()

    def showColDlg(self, col):
        data = wxStdColourData(col)
        dlg = wx.ColourDialog(self, data)
        try:
            if dlg.ShowModal() == wx.ID_OK:
                return dlg.GetColourData().GetColour()
            return None
        finally:
            dlg.Destroy()

    def OnFgcolbtnButton(self, event):
        newcol = self.showColDlg(self.fgcol)

        if newcol:
            self.fgcol = newcol
            self.fgpen.SetColour(newcol)
            self.FGColBtn.SetBackgroundColour(self.fgcol)
            self.penBrushWindow.Refresh()

    def OnBgcolbtnButton(self, event):
        newcol = self.showColDlg(self.bgcol)

        if newcol:
            self.bgcol = newcol
            self.bgbsh.SetColour(newcol)
            self.BGColBtn.SetBackgroundColour(self.bgcol)
            self.penBrushWindow.Refresh()

    def OnBrushcolbtnButton(self, event):
        newcol = self.showColDlg(self.brush.GetColour())

        if newcol:
            self.brush.SetColour(newcol)
            self.brushColBtn.SetBackgroundColour(newcol)
            self.penBrushWindow.Refresh()

    def OnModeChoiceChoice(self, event):
        self.setMode(self.modeChoice.GetStringSelection())
        self.editWindow.Refresh()

    def OnEditWindowScroll(self, event):
        event.Skip()
        self.editWindow.Refresh()

    def OnSpinbutton1CommandScroll(self, event):
        self.brush.SetStyle(self.brushStyles[self.spinButton1.GetValue()])
        self.penBrushWindow.Refresh()

    def OnSpinbutton2CommandScroll(self, event):
        self.fgpen.SetStyle(self.penStyles[self.spinButton2.GetValue()])
        self.penBrushWindow.Refresh()

    def OnSpinbutton3CommandScroll(self, event):
        self.fgpen.SetWidth(self.spinButton3.GetValue())
        self.penBrushWindow.Refresh()

#---View action events----------------------------------------------------------

    def OnCopy(self, event):
        clip = wx.TheClipboard
        clip.Open()
        try:
            bmp = self.getSelBmp()
            clip.SetData(wx.BitmapDataObject(bmp))
        finally:
            clip.Close()

    def OnPaste(self, event):
        clip = wx.TheClipboard
        clip.Open()
        try:
            data = wx.BitmapDataObject()
            try:
                clip.GetData(data)
            except:
                wx.LogError(_('Not a picture'))
            else:
                self.modeChoice.SetSelection(0)
                self.setMode('Move')
                self.snapshot()
                self.dragbmp = data.GetBitmap()
                self.dragsrcrect = -1, -1, 0, 0
                self.dragpos = 0, 0
                self.sel = 0, 0, self.dragbmp.GetWidth()-1, self.dragbmp.GetHeight()-1
                self.mDC.DrawBitmap(self.dragbmp, 0, 0)
                self.editWindow.Refresh()

                self.imageModified()
        finally:
            clip.Close()

    def OnUndo(self, event):
        self.undo()

        self.editWindow.Refresh()
        self.updateImageInfo()

    def OnClear(self, event):
        self.snapshot()
        if self.sel:
            x1, y1, x2, y2 = self.sel
            self.mDC.SetClippingRegion(x1, y1, x2-x1, y2-y1)
            self.mDC.Clear()
            self.mDC.DestroyClippingRegion()
        else:
            self.mDC.Clear()

        self.editWindow.Refresh()
        self.imageModified()

    def OnSetTransparentMask(self, event):
        self.mDC.SelectObject(wx.NullBitmap)
        self.bmp.SetMask(wx.Mask(self.bmp, self.fgcol))
        self.mDC.SelectObject(self.bmp)

        self.updateImageInfo()
        self.imageModified()

    def OnClearTransparentMask(self, event):
        self.mDC.SelectObject(wx.NullBitmap)
        self.bmp.SetMask(None)
        self.mDC.SelectObject(self.bmp)

        self.updateImageInfo()
        self.imageModified()

    def OnResize(self, event):
        dlg = wx.TextEntryDialog(self, _('Enter a tuple for the new size'),
              _('Resize'), '%s, %s'%(self.bmp.GetWidth(), self.bmp.GetHeight()))
        try:
            if dlg.ShowModal() != wx.ID_OK:
                return
            width, height = eval(dlg.GetValue())
        finally:
            dlg.Destroy()

        # Create new bitmap of required size and copy current one to it
        mDC, bmp = self.getTempMemDC(width, height)
        mDC.Blit(0, 0, self.bmp.GetWidth(), self.bmp.GetHeight(),
                 self.mDC, 0, 0)
        mDC.SelectObject(wx.NullBitmap)

        self.setMemDCBmp(bmp)

        self.editWindow.Refresh()
        self.updateImageInfo()
        self.imageModified()

    def OnScale(self, event):
        dlg = wx.TextEntryDialog(self, _('Enter a tuple for the new size'),
              _('Scale'), '%s, %s'%(self.bmp.GetWidth(), self.bmp.GetHeight()))
        try:
            if dlg.ShowModal() != wx.ID_OK:
                return
            width1, height1 = self.bmp.GetWidth(), self.bmp.GetHeight()
            width2, height2 = eval(dlg.GetValue())
        finally:
            dlg.Destroy()

        # draw a new version of the current bmp scaled to the user requested size
        xScale, yScale = float(width2)/width1, float(height2)/height1
        self.mDC.SetUserScale(1/xScale, 1/yScale)
        width, height = self.bmp.GetWidth()*xScale, self.bmp.GetHeight()*yScale
        mDC, bmp = self.getTempMemDC(width, height)
        mDC.Blit(0, 0, width, height, self.mDC, 0, 0)
        mDC.SelectObject(wx.NullBitmap)

        self.mDC.SetUserScale(1.0, 1.0)
        self.setMemDCBmp(bmp)

        self.editWindow.Refresh()
        self.updateImageInfo()
        self.imageModified()

    def OnCrop(self, event):
        self.setMemDCBmp(self.getSelBmp())

        self.clearState()

        self.editWindow.Refresh()
        self.updateImageInfo()
        self.imageModified()


class wxStdColourData(wx.ColourData):
    stdcolours = [(0, 0, 0), (128, 128, 128), (128, 0, 0), (128, 128, 0),
                  (0, 128, 0), (0, 128, 128), (0, 0, 128), (128, 0, 128),
                  (255, 255, 255), (192, 192, 192), (255, 0, 0), (255, 255, 0),
                  (0, 255, 0), (0, 255, 255), (0, 0, 255), (255, 0, 255)]

    def __init__(self, col=None):
        wx.ColourData.__init__(self)
        if col: self.SetColour(col)

        i = 0
        for r, g, b in self.stdcolours:
            self.SetCustomColour(i, wx.Colour(r, g, b))
            i = i + 1

#-Tester------------------------------------------------------------------------

if __name__ == '__main__':
    app = wx.PySimpleApp()
    wx.InitAllImageHandlers()
    frame = wx.Frame(None, -1, 'Image Edit Test')
    panel = ImageEditorPanel(frame)
    frame.Show(True)
    panel.initImageData('.png', open('Images/Modules/Pyrex.png', 'rb').read())
    app.MainLoop()
    import sys
    sys.exit()

#-------------------------------------------------------------------------------

import Preferences

from Models import EditorModels, Controllers, EditorHelper
from Views import EditorViews

class ImageView(wx.Panel, EditorViews.EditorView):
    viewName = 'View'
    viewTitle = 'View'
    def __init__(self, parent, model):
        wx.Panel.__init__(self, parent, -1, style= wx.SUNKEN_BORDER)
        self.staticBitmapSmall = wx.StaticBitmap(self, -1, wx.NullBitmap)
        #self.staticBitmapBig = wx.StaticBitmap(self, -1, wx.NullBitmap)
        EditorViews.EditorView.__init__(self, model, (), -1)
        self.active = True

    imgsep = 16
    def refreshCtrl(self):
        if self.model.data:
            sio = StringIO(self.model.data)
            bmp = wx.BitmapFromImage(wx.ImageFromStream(sio))
            self.staticBitmapSmall.SetBitmap(bmp)
            self.staticBitmapSmall.SetDimensions(self.imgsep, self.imgsep,
                                                 bmp.GetWidth(), bmp.GetHeight())
            self.staticBitmapSmall.Refresh()                                     
            #self.staticBitmapBig.SetBitmap(bmp)
            #self.staticBitmapBig.SetDimensions(bmp.GetWidth()+self.imgsep*2,
            #      self.imgsep, bmp.GetWidth()*2, bmp.GetHeight()*2)

class ImageEditorView(ImageEditorPanel, EditorViews.EditorView):
    viewName = 'Edit'
    viewTitle = _('Edit')

    refreshBmp = 'Images/Editor/Refresh.png'
    copyBmp = 'Images/Shared/Copy.png'
    pasteBmp = 'Images/Shared/Paste.png'
    undoBmp = 'Images/Shared/Undo.png'
    def __init__(self, parent, model, actions=()):
        ImageEditorPanel.__init__(self, parent)
        EditorViews.EditorView.__init__(self, model, (
          (_('Refresh'), self.OnRefresh, self.refreshBmp, ''),
          ('-', None, '-', ''),
          (_('Copy'), self.OnCopy, self.copyBmp, ''),
          (_('Paste'), self.OnPaste, self.pasteBmp, ''),
          (_('Undo last change'), self.OnUndo, self.undoBmp, ''),
          (_('Undo view changes'), self.OnUndoViewChanges, '-', ''),
          (_('Clear'), self.OnClear, '-', ''),
          ('-', None, '-', ''),
          (_('Crop'), self.OnCrop, '-', ''),
          (_('Resize...'), self.OnResize, '-', ''),
          (_('Scale...'), self.OnScale, '-', ''),
          ('-', None, '-', ''),
          (_('Use current colour to set transparent mask'), self.OnSetTransparentMask, '-', ''),
          (_('Clear current transparent mask'), self.OnClearTransparentMask, '-', ''),
          ('-', None, '-', ''),
        ) + actions, -1)

        self.editWindow.Bind(wx.EVT_RIGHT_DOWN, self.OnRightDown)
        self.editWindow.Bind(wx.EVT_RIGHT_UP, self.OnRightClick)

        self.active = True
        self.subImage = None

    def refreshCtrl(self, subImage=None):
        if subImage is None and self.subImage is None:
            data = self.model.data
        else:
            if not self.subImage:
                self.subImage = subImage
            data = self.subImage['data']

        ext = os.path.splitext(self.model.filename)[-1]

        self.initImageData(ext, data)
        self.editWindow.Refresh()

        self.modified = False
        self.updateViewState()

    def refreshModel(self):
        ext = os.path.splitext(self.model.filename)[-1]
        if ext == '.py':
            ext = '.png'

        data = self.getImageData(ext)

        if self.subImage:
            modelData = self.subImage['data']
        else:
            modelData = self.model.data

        if modelData != data:
            if self.subImage:
                self.model.updateData(data, self.subImage)
            else:
                self.model.data = data
                self.model.modified = True

        if self.model.viewsModified.count(self.viewName):
            self.model.viewsModified.remove(self.viewName)
        self.modified = False
        EditorViews.EditorView.refreshModel(self)

        self.updateEditor()
        self.updateViewState()

    def imageModified(self):
        self.modified = True
        self.updateViewState()

    def OnRefresh(self, event):
        self.refreshModel()

    def OnUndoViewChanges(self, event):
        self.refreshCtrl()


class BitmapEditorFileController(Controllers.PersistentController):
    Model = EditorModels.BitmapFileModel
    DefaultViews    = [ImageView]
    AdditionalViews = [ImageEditorView]

    editBmpBmp = 'Images/EditBitmap.png'
    conv2ModBmp = 'Images/Modules/PyResBitmap.png'

    def actions(self, model):
        return Controllers.PersistentController.actions(self, model) + [
              ('-', None, '-', ''),
              (_('Edit image'), self.OnGotoEditView, self.editBmpBmp, ''),
              (_('Convert to module'), self.OnConvertToModule, self.conv2ModBmp, ''),]

    def OnGotoEditView(self, event):
        model = self.getModel()
        if not model.views.has_key('Edit'):
            modPge = self.editor.getActiveModulePage()
            for View, wid in modPge.adtViews:
                if View == ImageEditorView:
                    self.editor.mainMenu.Check(wid, True)
                    break
            view = modPge.addView(ImageEditorView)
            view.refreshCtrl()
        else:
            view = model.views['Edit']
        view.focus()

    def OnConvertToModule(self, event):
        model = self.getModel()
        imgPath = model.localFilename()
        Models.ResourceSupport.ConvertImgToPy(imgPath, self.editor)


class CloseableImageEditorView(ImageEditorView, EditorViews.CloseableViewMix):
    def __init__(self, parent, model):
        EditorViews.CloseableViewMix.__init__(self, 'image editor')
        ImageEditorView.__init__(self, parent, model, self.closingActionItems)


from Models import ResourceSupport

class PyResourceImagesViewPlugin:
    editImgBmp = 'Images/EditBitmap.png'

    def __init__(self, model, view, actions):
        self.model = model
        self.view = view
        actions.extend( (
              (_('Edit image'), self.OnEditImage, self.editImgBmp, ''),
        ) )

    def OnEditImage(self, event):
        if self.view.selected != -1:
            name, (dataStartLn, bmpStartLine), zipped, icon = \
                  self.view.imageSrcInfo[self.view.selected]
            viewName = ResourceSupport.PyResourceImagesView.viewName
            if name:
                viewName += ':'+name

            if not self.model.views.has_key(viewName):
                modPge = self.model.editor.getActiveModulePage()
                view = modPge.addView(CloseableImageEditorView, viewName)
                view.tabName = view.viewName = viewName
                data = self.view.functions.imageFunctions['get%sData'%name]()
                subImage = {'data': data, 'name': name, 'start': dataStartLn+1,
                            'end': bmpStartLine-2, 'zip': zipped,
                            'icon': icon, 'cat': self.view.cataloged,
                            'eol': self.view.eol}
                view.refreshCtrl(subImage)
            else:
                view = self.model.views[viewName]
            view.focus()

ResourceSupport.PyResourceImagesView.plugins += (PyResourceImagesViewPlugin,)

#-------------------------------------------------------------------------------

Plugins.registerFileType(BitmapEditorFileController)

#-------------------------------------------------------------------------------
#Boa:PyImgResource:EditBitmap
def getEditBitmapData():
    return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\x08\x06\
\x00\x00\x00\x1f\xf3\xffa\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\
\x00\x00\x91IDATx\x9c\xa5\x92\xbb\x11\xc30\x0c\xc5\xc0\xa4Q\xa9Q=\x02\xbd\
\x81F\xcaHo\x03\xa6\x89e\xe9\xf2\xa3\xce\xecX\x00\x07\x9dh\xb5V\xae\xccm\x15\
\xd8\xb6-\xc6\xddV\n$E\x00\x06\xd4Zm\xa9\xe0\x80\x01|(I\x15H\x8a\x88\xc0\xcc\
\xf0\x97\xe0(\xb8\x97RR0\x18f\xe0\x8f\x13\xfe\xfb\x84\x11\xdewp\x9f\xe1\x9f\
\x82\x0c\xfcU\x90\x85?\nV\xe07\xc1*<\t\xfa\xbfZ\x1e\x86\xe1\x0e$M\'\x9a\x81{\
\xc1x\xdf\xee\x9e\x86{\x81\xa4pwZkip\x12\\\x99\'\xc3{a\x05\x01 \x1c\xda\x00\
\x00\x00\x00IEND\xaeB`\x82'

def getBitmapData():
    return \
'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x18\x00\x00\x00\x18\x08\x06\
\x00\x00\x00\xe0w=\xf8\x00\x00\x00\x04sBIT\x08\x08\x08\x08|\x08d\x88\x00\x00\
\x01\x18IDATH\x89\xdd\x95MR\xc4 \x10\x85_3s))g!\xd7"\xc1Q\xe4dI\x85K\xe9\xb4\
\x8b\xc4a(~u\xccB\xbb*\x8b\x00\xf5\xbe\xee\xe6\x01D\xe2\x80=C\xec\xaa\xfe/\
\x00\xc7\xdc\xa0=\xbfp\xaf\xc0x~\xa6o\x03\x00@\x8f\xba)\xeeg\x0f\x00\\\x83\
\xdc\xdd"=\xeaj\xc5\xc5\nr\xe1(Nt\x88\xa7\x99\xc4!\xa9\xa4\x1b\xe0\xac\x03^\
\xdf\xc2\x80\x19\xc0\xbc&\xeeg\x0fy\x92\xe0\xcb;\x938F\x90n\x806q\xbe.Y\x91\
\xefR\x13@6$Tj\xb4<=\x14\xb1U\x00Y\x02\x8f\xc0\x17\x82L\n\t\xdb\xe2@"uS\xd3E\
%\xff\xa9i\x89\xc4\x93-\xef\x05$\xc1\x0c0C>\x86\xb6,\x93*.\xaf\x02x\xe4\xb5\
\x04\x02`pu\x8d\xb3n\xeb\x8d\x03s\xfd@6+`\xe6\xf5\x03\x00"8\xeb\xa0\x9e\xd46\
\xd7>\xedm\x9bn\x8d\xbe\xfa\xc3\x0c\x90\x08\xd5\xdc\x05\xb8=\xb9\xfaF\xb0\
\x9dw\x07\xc0\xcf\x1ejZ\xa2\xff\x9f\x04\xe5\x9eL\xbe|t_\xd7\x91X\xe6.\xca\
\x02~3\xfe\xfe\x93\xb9;\xe0\x13]\x82[\x14\xe0\xb9\xd5\x88\x00\x00\x00\x00IEN\
D\xaeB`\x82' 

Preferences.IS.registerImage('Images/EditBitmap.png', getEditBitmapData())
Preferences.IS.registerImage('Images/Palette/Bitmap.png', getBitmapData())
