RE: Problem with UNICODE filenames still not fixed :( (part 2 of 3)

"Simon C" <[email protected]>
Newsgroups gmane.comp.mobile.bitpim.devel
Message-ID <000401c5cc3b$5aadbf00$6400a8c0@HOME>
Part 2 of 3


Simon
phonebook.py (text/plain, 109.7 KB) - not displayed
phonebook.py.diff (application/octet-stream, 811 B)
Index: phonebook.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/phonebook.py,v
retrieving revision 1.136
diff -u -r1.136 phonebook.py
--- phonebook.py	24 Jul 2005 06:07:19 -0000	1.136
+++ phonebook.py	8 Oct 2005 18:40:22 -0000
@@ -151,7 +151,10 @@
             self.xcp.setupxcopy(template)
         if self.xcpstyles is None:
             self.xcpstyles={}
-            execfile(self.stylesfile,  self.xcpstyles, self.xcpstyles)
+            try:
+                execfile(self.stylesfile,  self.xcpstyles, self.xcpstyles)
+            except UnicodeError:
+                common.unicode_execfile(self.stylesfile,  self.xcpstyles, self.xcpstyles)
         self.xcpstyles['entry']=entry
         text=self.xcp.xcopywithdns(self.xcpstyles)
         try:
ringers.py (text/plain, 27.5 KB)
### BITPIM
###
### Copyright (C) 2003-2004 Roger Binns <[email protected]>
### Copyright (C) 2003-2004 Steven Palm <[email protected]>
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of the BitPim license as detailed in the LICENSE file.
###
### $Id: ringers.py,v 1.42 2005/09/19 22:24:35 djpham Exp $

import os
import time
import wx
from wx.lib import masked

import guiwidgets
import guihelper
import pubsub
import aggregatedisplay
import wallpaper
import common
import fileinfo
import conversions
import helpids

import rangedslider

###
###  Ringers
###

class DisplayItem(guiwidgets.FileViewDisplayItem):

    datakey='ringtone-index'
    datatype='Audio' # used in the tooltip


class RingerView(guiwidgets.FileView):
    CURRENTFILEVERSION=2

    # this is only used to prevent the pubsub module
    # from being GC while any instance of this class exists
    __publisher=pubsub.Publisher


    organizetypes=("Origin", "Audio Type", "File Size")
    media_notification_type=pubsub.ringtone_type
    
    def __init__(self, mainwindow, parent, id=-1):
        self.mainwindow=mainwindow
        self._data={'ringtone-index': {}}
        self.updateprofilevariables(self.mainwindow.phoneprofile)
        self.organizemenu=wx.Menu()
        guiwidgets.FileView.__init__(self, mainwindow, parent, "ringtone-watermark")
        self.wildcard="Audio files|*.wav;*.mid;*.qcp;*.mp3;*.pmd|Midi files|*.mid|Purevoice files|*.qcp|MP3 files|*.mp3|PMD/CMX files|*.pmd|All files|*.*"

        self.organizeinfo={}

        for k in self.organizetypes:
            id=wx.NewId()
            self.organizemenu.AppendRadioItem(id, k)
            wx.EVT_MENU(self, id, self.OrganizeChange)
            self.organizeinfo[id]=getattr(self, "organizeby_"+k.replace(" ",""))

        self.modified=False
        wx.EVT_IDLE(self, self.OnIdle)
        pubsub.subscribe(self.OnListRequest, pubsub.REQUEST_RINGTONES)
        pubsub.subscribe(self.OnDictRequest, pubsub.REQUEST_RINGTONE_INDEX)

    def updateprofilevariables(self, profile):
        self.maxlen=profile.MAX_RINGTONE_BASENAME_LENGTH
        self.filenamechars=profile.RINGTONE_FILENAME_CHARS

    def OnListRequest(self, msg=None):
        l=[self._data['ringtone-index'][x]['name'] for x in self._data['ringtone-index']]
        l.sort()
        pubsub.publish(pubsub.ALL_RINGTONES, l)

    def OnDictRequest(self, msg=None):
        pubsub.publish(pubsub.ALL_RINGTONE_INDEX, self._data['ringtone-index'].copy())

    def OnIdle(self, _):
        "Save out changed data"
        if self.modified:
            self.modified=False
            self.populatefs(self._data)
            self.OnListRequest() # broadcast changes

    def getdata(self,dict,want=guiwidgets.FileView.NONE):
        return self.genericgetdata(dict, want, self.mainwindow.ringerpath, 'ringtone', 'ringtone-index')

    def GetItemThumbnail(self, item, w, h):
        assert w==self.thumbnail.GetWidth() and h==self.thumbnail.GetHeight()
        return self.thumbnail

    def OrganizeChange(self, evt):
        evt.GetEventObject().Check(evt.GetId(), True)
        self.OnRefresh()

    def GetSections(self):
        # work out section and item sizes
        self.thumbnail=wx.Image(guihelper.getresourcefile('ringer.png')).ConvertToBitmap()
        
        dc=wx.MemoryDC()
        dc.SelectObject(wx.EmptyBitmap(100,100)) # unused bitmap needed to keep wxMac happy
        h=dc.GetTextExtent("I")[1]
        itemsize=self.thumbnail.GetWidth()+160, max(self.thumbnail.GetHeight(), h*4+DisplayItem.PADDING)+DisplayItem.PADDING*2
        
        # get all the items
        items=[DisplayItem(self, key, self.mainwindow.ringerpath) for key in self._data['ringtone-index']]
        # prune out ones we don't have images for
        items=[item for item in items if os.path.exists(item.filename)]

        self.sections=[]

        if len(items)==0:
            return self.sections
        
        # get the current sorting type
        for i in range(len(self.organizetypes)):
            item=self.organizemenu.FindItemByPosition(i)
            if self.organizemenu.IsChecked(item.GetId()):
                for sectionlabel, items in self.organizeinfo[item.GetId()](items):
                    sh=aggregatedisplay.SectionHeader(sectionlabel)
                    sh.itemsize=itemsize
                    for item in items:
                        item.thumbnailsize=self.thumbnail.GetWidth(), self.thumbnail.GetHeight()
                    # sort items by name
                    items=[(item.name.lower(), item) for item in items]
                    items.sort()
                    items=[item for name,item in items]
                    self.sections.append( (sh, items) )
                return [sh for sh,items in self.sections]
        assert False, "Can't get here"

    def GetItemsFromSection(self, sectionnumber, sectionheader):
        return self.sections[sectionnumber][1]

    def organizeby_AudioType(self, items):
        types={}
        for item in items:
            t=item.fileinfo.format
            if t is None: t="<Unknown>"
            l=types.get(t, [])
            l.append(item)
            types[t]=l

        keys=types.keys()
        keys.sort()
        return [ (key, types[key]) for key in types]

    def organizeby_Origin(self, items):
        types={}
        for item in items:
            t=item.origin
            if t is None: t="Default"
            l=types.get(t, [])
            l.append(item)
            types[t]=l

        keys=types.keys()
        keys.sort()
        return [ (key, types[key]) for key in types]
        
    def organizeby_FileSize(self, items):
        
        sizes={0: ('Less than 8kb', []),
               8192: ('8 kilobytes', []),
               16384: ('16 kilobytes', []),
               32768: ('32 kilobytes', []),
               65536: ('64 kilobytes', []),
               131052: ('128 kilobytes', []),
               524208: ('512 kilobytes', []),
               1024*1024: ('One megabyte', [])}

        keys=sizes.keys()
        keys.sort()

        for item in items:
            t=item.size
            if t>=keys[-1]:
                sizes[keys[-1]][1].append(item)
                continue
            for i,k in enumerate(keys):
                if t<keys[i+1]:
                    sizes[k][1].append(item)
                    break

        return [sizes[k] for k in keys if len(sizes[k][1])]   

    def GetItemSize(self, sectionnumber, sectionheader):
        return sectionheader.itemsize

    def GetFileInfo(self, filename):
        return fileinfo.identify_audiofile(filename)

    def RemoveFromIndex(self, names):
        for name in names:
            wp=self._data['ringtone-index']
            for k in wp.keys():
                if wp[k]['name']==name:
                    del wp[k]
                    self.modified=True
            
    def OnAddFiles(self, filenames):
        for file in filenames:
            if file is None: continue  # failed dragdrop?
            # do we want to convert file?
            afi=fileinfo.identify_audiofile(file)
            if afi.size<=0: continue # zero length file or other issues
            newext,convertinfo=self.mainwindow.phoneprofile.QueryAudio(None, common.getext(file), afi)
            if convertinfo is not afi:
                filedata=None
                wx.EndBusyCursor()
                try:
                    filedata=self.ConvertFormat(file, convertinfo)
                finally:
                    # ensure they match up
                    wx.BeginBusyCursor()
                if filedata is None:
                    continue
            else:
                filedata=open(file, "rb").read()
            # check for the size limit on the file, if specified
            max_size=getattr(convertinfo, 'MAXSIZE', None)
            if max_size is not None and len(filedata)>max_size:
                # the data is too big
                self.log('ringtone %s is too big!'%common.basename(file))
                dlg=wx.MessageDialog(self,
                                     'Ringtone %s may be too big.  Do you want to proceed anway?'%common.basename(file),
                                     'Warning',
                                     style=wx.YES_NO|wx.ICON_ERROR)
                dlg_resp=dlg.ShowModal()
                dlg.Destroy()
                if dlg_resp==wx.ID_NO:
                    continue
            self.thedir=self.mainwindow.ringerpath
            decoded_file=self.decodefilename(file)
            target=self.getshortenedbasename(decoded_file, newext)
            open(target, "wb").write(filedata)
            self.AddToIndex(str(os.path.basename(target)).decode(guiwidgets.media_codec))
        self.OnRefresh()

    OnAddFiles=guihelper.BusyWrapper(OnAddFiles)

    def AddToIndex(self, file):
        for i in self._data['ringtone-index']:
            if self._data['ringtone-index'][i]['name']==file:
                return
        keys=self._data['ringtone-index'].keys()
        idx=10000
        while idx in keys:
            idx+=1
        self._data['ringtone-index'][idx]={'name': file}
        self.modified=True

    def ConvertFormat(self, file, convertinfo):
        dlg=ConvertDialog(self, file, convertinfo)
        if dlg.ShowModal()==wx.ID_OK:
            res=dlg.newfiledata
        else:
            res=None
        dlg.Destroy()
        return res

    def updateindex(self, index):
        if index!=self._data['ringtone-index']:
            self._data['ringtone-index']=index.copy()
            self.modified=True

    def populatefs(self, dict):
        self.thedir=self.mainwindow.ringerpath
        return self.genericpopulatefs(dict, 'ringtone', 'ringtone-index', self.CURRENTFILEVERSION)
            
    def populate(self, dict):
        if self._data['ringtone-index']!=dict['ringtone-index']:
            self._data['ringtone-index']=dict['ringtone-index'].copy()
            self.modified=True
        self.OnRefresh()
        
    def getfromfs(self, result):
        self.thedir=self.mainwindow.ringerpath
        return self.genericgetfromfs(result, None, 'ringtone-index', self.CURRENTFILEVERSION)

    def updateindex(self, index):
        if index!=self._data['ringtone-index']:
            self._data['ringtone-index']=index.copy()
            self.modified=True

    def versionupgrade(self, dict, version):
        """Upgrade old data format read from disk

        @param dict:  The dict that was read in
        @param version: version number of the data on disk
        """

        # version 0 to 1 upgrade
        if version==0:
            version=1  # the are the same

        # 1 to 2 etc
        if version==1:
            print "converting to version 2"
            version=2
            d={}
            input=dict.get('ringtone-index', {})
            for i in input:
                d[i]={'name': input[i]}
            dict['ringtone-index']=d
        return dict

class ConvertDialog(wx.Dialog):

    ID_CONVERT=wx.NewId()
    ID_PLAY=wx.NewId()
    ID_PLAY_CLIP=wx.NewId()
    ID_STOP=wx.NewId()
    ID_TIMER=wx.NewId()
    ID_SLIDER=wx.NewId()

    # we need to offer all types here rather than using derived classes as the phone may support several
    # alternatives

    PARAMETERS={
        'MP3':  {
        'formats': ["MP3"],
        'samplerates': ["16000", "22050", "24000", "32000", "44100", "48000"],
        'channels': ["1", "2"],
        'bitrates': ["8", "16", "24", "32", "40", "48", "56", "64", "80", "96", "112", "128", "144", "160", "192", "224", "256", "320"],
        'setup': 'mp3setup',
        'convert': 'mp3convert',
        'filelength': 'mp3filelength',
        'final': 'mp3final',
        },

        'QCP': {
        'formats': ["QCP"],
        'samplerates': ["8000"],
        'channels': ["1"],
        'bitrates': ["13000"],
        'optimization': ['0-Best Sound Quality', '1', '2', '3-Smallest File Size'],
        'setup': 'qcpsetup',
        'convert': 'qcpconvert',
        'filelength': 'qcpfilelength',
        'final': 'qcpfinal',
        }
        
        }
       
        

    def __init__(self, parent, file, convertinfo):
        wx.Dialog.__init__(self, parent, title="Convert Audio File", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.SYSTEM_MENU|wx.MAXIMIZE_BOX)
        self.file=file
        self.convertinfo=convertinfo
        self.afi=None
        self.temporaryfiles=[]
        self.wavfile=common.gettempfilename("wav")      # full length wav equivalent
        self.clipwavfile=common.gettempfilename("wav")  # used for clips from full length wav
        self.temporaryfiles.extend([self.wavfile, self.clipwavfile])

        getattr(self, self.PARAMETERS[convertinfo.format]['setup'])()
        
        vbs=wx.BoxSizer(wx.VERTICAL)
        # create the covert controls
        self.create_convert_pane(vbs, file, convertinfo)
        # and the crop controls
        self.create_crop_panel(vbs)

        vbs.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALL|wx.ALIGN_RIGHT, 5)

        self.SetSizer(vbs)
        vbs.Fit(self)

        # diable various things
        self.FindWindowById(wx.ID_OK).Enable(False)
        for i in self.cropids:
            self.FindWindowById(i).Enable(False)
        

        # Events
        wx.EVT_BUTTON(self, wx.ID_OK, self.OnOk)
        wx.EVT_BUTTON(self, wx.ID_CANCEL, self.OnCancel)
        wx.EVT_TIMER(self, self.ID_TIMER, self.OnTimer)
        wx.EVT_BUTTON(self, wx.ID_HELP, lambda _: wx.GetApp().displayhelpid(helpids.ID_DLG_AUDIOCONVERT))

        # timers and sounds
        self.sound=None
        self.timer=wx.Timer(self, self.ID_TIMER)

        # wxPython wrapper on Mac raises NotImplemented for wx.Sound.Stop() so
        # we hack around that by supplying a zero length wav to play instead
        if guihelper.IsMac():
            self.zerolenwav=guihelper.getresourcefile("zerolen.wav")


    def create_convert_pane(self, vbs, file, convertinfo):
        params=self.PARAMETERS[convertinfo.format]
        # convert bit
        bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, "Convert"), wx.VERTICAL)
        bs.Add(wx.StaticText(self, -1, "Input File: "+file), 0, wx.ALL, 5)
        gs=wx.FlexGridSizer(2, 4, 5, 5)
        gs.Add(wx.StaticText(self, -1, "New Type"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
        self.type=wx.ComboBox(self, style=wx.CB_DROPDOWN|wx.CB_READONLY, choices=params['formats'])
        gs.Add(self.type, 0, wx.ALL|wx.EXPAND, 5)
        gs.Add(wx.StaticText(self, -1, "Sample Rate (per second)"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
        self.samplerate=wx.ComboBox(self, style=wx.CB_DROPDOWN|wx.CB_READONLY, choices=params['samplerates'])
        gs.Add(self.samplerate, 0, wx.ALL|wx.EXPAND, 5)
        gs.Add(wx.StaticText(self, -1, "Channels (Mono/Stereo)"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
        self.channels=wx.ComboBox(self, style=wx.CB_DROPDOWN|wx.CB_READONLY, choices=params['channels'])
        gs.Add(self.channels, 0, wx.ALL|wx.EXPAND, 5)
        gs.Add(wx.StaticText(self, -1, "Bitrate (kbits per second)"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
        self.bitrate=wx.ComboBox(self, style=wx.CB_DROPDOWN|wx.CB_READONLY, choices=params['bitrates'])
        gs.Add(self.bitrate, 0, wx.ALL|wx.EXPAND, 5)
        if params.has_key('optimization'):
            gs.Add(wx.StaticText(self, -1, 'Optimization'), 0,
                   wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
            self.optimization=wx.ComboBox(self,
                                          style=wx.CB_DROPDOWN|wx.CB_READONLY,
                                          choices=params['optimization'])
            self.optimization.SetSelection(1)
            gs.Add(self.optimization, 0, wx.ALL|wx.EXPAND, 5)
        gs.AddGrowableCol(1, 1)
        gs.AddGrowableCol(3, 1)
        bs.Add(gs, 0, wx.EXPAND)

        bs.Add(wx.Button(self, self.ID_CONVERT, "Convert"), 0, wx.ALIGN_RIGHT|wx.ALL, 5)

        vbs.Add(bs, 0, wx.EXPAND|wx.ALL, 5)
        # Fill out fields - we explicitly set even when not necessary due to bugs on wxMac
        if self.type.GetCount()==1:
            self.type.SetSelection(0)
        else:
            self.type.SetStringSelection(convertinfo.format)

        if self.channels.GetCount()==1:
            self.channels.SetSelection(0)
        else:
            self.channels.SetStringSelection(`convertinfo.channels`)

        if self.bitrate.GetCount()==1:
            self.bitrate.SetSelection(0)
        else:
            self.bitrate.SetStringSelection(`convertinfo.bitrate`)

        if self.samplerate.GetCount()==1:
            self.samplerate.SetSelection(0)
        else:
            self.samplerate.SetStringSelection(`convertinfo.samplerate`)
            
        # Events
        wx.EVT_BUTTON(self, self.ID_CONVERT, self.OnConvert)

    def create_crop_panel(self, vbs):
        # crop bit
        bs=wx.StaticBoxSizer(wx.StaticBox(self, -1, "Crop"), wx.VERTICAL)
        hbs=wx.BoxSizer(wx.HORIZONTAL)
        hbs.Add(wx.StaticText(self, -1, "Current Position"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
        self.positionlabel=wx.StaticText(self, -1, "0                 ")
        hbs.Add(self.positionlabel, 0, wx.ALL, 5)
        hbs.Add(wx.StaticText(self, -1, "Est. Clip File length"), 0, wx.ALL|wx.ALIGN_CENTRE_VERTICAL, 5)
        self.lengthlabel=wx.StaticText(self, -1, "0                   ")
        hbs.Add(self.lengthlabel, 0, wx.ALL, 5)
        bs.Add(hbs, 0, wx.ALL, 5)
        # the start & end manual entry items
        hbs=wx.GridSizer(-1, 2, 0, 0)
        hbs.Add(wx.StaticText(self, -1, 'Clip Start (sec):'), 0, wx.EXPAND|wx.ALL, 5)
        self.clip_start=masked.NumCtrl(self, wx.NewId(), fractionWidth=2)
        hbs.Add(self.clip_start, 1, wx.EXPAND|wx.ALL, 5)
        hbs.Add(wx.StaticText(self, -1, 'Clip Duration (sec):'), 0, wx.EXPAND|wx.ALL, 5)
        self.clip_duration=masked.NumCtrl(self, wx.NewId(), fractionWidth=2)
        hbs.Add(self.clip_duration, 1, wx.EXPAND|wx.ALL, 5)
        hbs.Add(wx.StaticText(self, -1, 'Volume Adjustment (dB):'), 0,
                wx.EXPAND|wx.ALL, 5)
        self.clip_volume=masked.NumCtrl(self, wx.NewId(), fractionWidth=1)
        hbs.Add(self.clip_volume, 1, wx.EXPAND|wx.ALL, 5)
        clip_set_btn=wx.Button(self, wx.NewId(), 'Set')
        hbs.Add(clip_set_btn, 0, wx.EXPAND|wx.ALL, 5)
        bs.Add(hbs, 0, wx.EXPAND|wx.ALL, 5)
        hbs=wx.BoxSizer(wx.HORIZONTAL)
        self.slider=rangedslider.RangedSlider(self, id=self.ID_SLIDER, size=(-1, 30))
        hbs.Add(self.slider, 1, wx.EXPAND|wx.ALL, 5)
        bs.Add(hbs, 1, wx.EXPAND|wx.ALL, 5)
        hbs=wx.BoxSizer(wx.HORIZONTAL)
        hbs.Add(wx.Button(self, self.ID_STOP, "Stop"), 0, wx.ALL, 5)
        hbs.Add(wx.Button(self, self.ID_PLAY, "Play Position"), 0, wx.ALL, 5)
        hbs.Add(wx.Button(self, self.ID_PLAY_CLIP, "Play Clip"), 0, wx.ALL, 5)
        bs.Add(hbs, 0, wx.ALL|wx.ALIGN_RIGHT, 5)
        vbs.Add(bs, 0, wx.EXPAND|wx.ALL, 5)
        wx.EVT_BUTTON(self, self.ID_PLAY, self.OnPlayPosition)
        wx.EVT_BUTTON(self, self.ID_PLAY_CLIP, self.OnPlayClip)
        wx.EVT_BUTTON(self, self.ID_STOP, self.OnStop)
        wx.EVT_BUTTON(self, clip_set_btn.GetId(), self.OnSetClip)

        rangedslider.EVT_POS_CHANGED(self, self.ID_SLIDER, self.OnSliderCurrentChanged)
        rangedslider.EVT_CHANGING(self, self.ID_SLIDER, self.OnSliderChanging)

        self.cropids=[self.ID_SLIDER, self.ID_STOP, self.ID_PLAY,
                      self.ID_PLAY_CLIP, self.clip_start.GetId(),
                      self.clip_duration.GetId(), self.clip_volume.GetId(),
                      clip_set_btn.GetId()]


    def OnConvert(self, _):
        self.OnStop()
        for i in self.cropids:
            self.FindWindowById(i).Enable(False)
        self.FindWindowById(wx.ID_OK).Enable(False)
        getattr(self, self.PARAMETERS[self.convertinfo.format]['convert'])()
        self.wfi=fileinfo.getpcmfileinfo(self.wavfile)
        max_duration=round(self.wfi.duration, 2)+0.01
        self.clip_start.SetParameters(min=0.0, max=max_duration, limited=True)
        self.clip_duration.SetParameters(min=0.0, max=max_duration, limited=True)
        self.UpdateCrop()
        for i in self.cropids:
            self.FindWindowById(i).Enable(True)
        self.FindWindowById(wx.ID_OK).Enable(True)

    OnConvert=guihelper.BusyWrapper(OnConvert)


    def UpdateCrop(self):
        self.positionlabel.SetLabel("%.1f secs" % (self.slider.GetCurrent()*self.wfi.duration),)
        duration=(self.slider.GetEnd()-self.slider.GetStart())*self.wfi.duration
        self.clip_start.SetValue(self.slider.GetStart()*self.wfi.duration)
        self.clip_duration.SetValue(duration)
        v=getattr(self, self.PARAMETERS[self.convertinfo.format]['filelength'])(duration)
        self.lengthlabel.SetLabel("%s" % (v,))



    def OnPlayClip(self,_):
        self._Play(self.slider.GetStart(), self.slider.GetEnd(),
                   self.clip_volume.GetValue())
              
    def OnPlayPosition(self, _):
        self._Play(self.slider.GetCurrent(), 1.0)

    def _Play(self, start, end, volume=None):
        self.OnStop()
        assert start<=end
        self.playstart=start
        self.playend=end
        
        self.playduration=(self.playend-self.playstart)*self.wfi.duration

        conversions.trimwavfile(self.wavfile, self.clipwavfile,
                                self.playstart*self.wfi.duration,
                                self.playduration, volume)
        self.sound=wx.Sound(self.clipwavfile)
        assert self.sound.IsOk()
        res=self.sound.Play(wx.SOUND_ASYNC)
        assert res
        self.starttime=time.time()
        self.endtime=self.starttime+self.playduration
        self.timer.Start(100, wx.TIMER_CONTINUOUS)
        
    def OnTimer(self,_):
        now=time.time()
        if now>self.endtime:
            self.timer.Stop()
            # assert wx.Sound.IsPlaying()==False
            self.slider.SetCurrent(self.playend)
            self.UpdateCrop()
            return
        # work out where the slider should go
        newval=self.playstart+((now-self.starttime)/(self.endtime-self.starttime))*(self.playend-self.playstart)
        self.slider.SetCurrent(newval)
        self.UpdateCrop()            


    def OnStop(self, _=None):
        self.timer.Stop()
        if self.sound is not None:
            if guihelper.IsMac():
                # There is no stop method for Mac so we play a one centisecond wav
                # which cancels the existing playing sound
                self.sound=None
                sound=wx.Sound(self.zerolenwav)
                sound.Play(wx.SOUND_ASYNC)
            else:
                self.sound.Stop()
                self.sound=None

    def OnSliderCurrentChanged(self, evt):
        self.OnStop()
        wx.CallAfter(self.UpdateCrop)
        
    def OnSliderChanging(self, _):
        wx.CallAfter(self.UpdateCrop)

    def _removetempfiles(self):
        for file in self.temporaryfiles:
            if os.path.exists(file):
                os.remove(file)
        
    def OnOk(self, evt):
        self.OnStop()
        # make new data
        start=self.slider.GetStart()*self.wfi.duration
        duration=(self.slider.GetEnd()-self.slider.GetStart())*self.wfi.duration
        self.newfiledata=getattr(self, self.PARAMETERS[self.convertinfo.format]['final'])(
            start, duration, self.clip_volume.GetValue())
        # now remove files
        self._removetempfiles()
        # use normal handler to quit dialog
        evt.Skip()

    def OnCancel(self, evt):
        self.OnStop()
        self._removetempfiles()
        evt.Skip()

    def OnSetClip(self, _=None):
        s=self.clip_start.GetValue()
        d=self.clip_duration.GetValue()
        e=s+d
        if e<=self.wfi.duration:
            self.slider.SetStart(s/self.wfi.duration)
            self.slider.SetEnd(e/self.wfi.duration)
        self.UpdateCrop()

    ###
    ###  MP3 functions
    ###

    def mp3setup(self):
        self.mp3file=common.gettempfilename("mp3")
        self.tmp_mp3file=common.gettempfilename('mp3')
        self.temporaryfiles.append(self.mp3file)
        self.temporaryfiles.append(self.tmp_mp3file)

    def mp3convert(self):
        # make mp3 to work with
        open(self.mp3file, "wb").write(conversions.converttomp3(self.file, int(self.bitrate.GetStringSelection()), int(self.samplerate.GetStringSelection()), int(self.channels.GetStringSelection())))
        self.afi=fileinfo.getmp3fileinfo(self.mp3file)
        print "result is",len(self.afi.frames),"frames"
        # and corresponding wav to play
        conversions.converttowav(self.mp3file, self.wavfile)

    def mp3filelength(self, duration):
        # mp3 specific file length calculation
        frames=self.afi.frames
        self.beginframe=int(self.slider.GetStart()*len(frames))
        self.endframe=int(self.slider.GetEnd()*len(frames))
        length=sum([frames[frame].nextoffset-frames[frame].offset for frame in range(self.beginframe, self.endframe)])
        return length

    def _trim_mp3(self, start, duration):
        # mp3 writing out
        f=None
        try:
            frames=self.afi.frames
            offset=frames[self.beginframe].offset
            length=frames[self.endframe-1].nextoffset-offset
            f=open(self.mp3file, "rb", 0)
            f.seek(offset)
            return f.read(length)
        finally:
            if f is not None:
                f.close()

    def _trim_and_adjust_vol_mp3(self, start, duration, volume):
        # trim, adjust volume, and write mp3 out
        # use the original to make a new wav, not the one that went through foo -> mp3 -> wav
        conversions.converttowav(self.file, self.wavfile,
                                 start=start, duration=duration)
        # adjust the volume
        conversions.adjustwavfilevolume(self.wavfile, volume)
        # convert to mp3
        return conversions.converttomp3(self.wavfile,
                                        int(self.bitrate.GetStringSelection()),
                                        int(self.samplerate.GetStringSelection()),
                                        int(self.channels.GetStringSelection()))

    def mp3final(self, start, duration, volume=None):
        if volume:
            # need to adjust volume
            return self._trim_and_adjust_vol_mp3(start, duration, volume)
        else:
            return self._trim_mp3(start, duration)

    ###
    ###  QCP/PureVoice functions
    ###

    def qcpsetup(self):
        self.qcpfile=common.gettempfilename("qcp")
        self.temporaryfiles.append(self.qcpfile)

    def qcpconvert(self):
        # we verify the pvconv binary exists first
        conversions.getpvconvbinary()
        # convert to wav first
        conversions.converttowav(self.file, self.wavfile, samplerate=8000, channels=1)
        # then to qcp
        conversions.convertwavtoqcp(self.wavfile, self.qcpfile,
                                    self.optimization.GetSelection())
        # and finally the wav from the qcp so we can accurately hear what it sounds like
        conversions.convertqcptowav(self.qcpfile, self.wavfile)

    def qcpfilelength(self, duration):
        # we don't actually know unless we do a conversion as QCP is
        # variable bitrate, so just assume the worst case of 13000
        # bits per second (1625 bytes per second) with an additional
        # 5% overhead due to the file format (I often see closer to 7%)
        return int(duration*1625*1.05) 

    def qcpfinal(self, start, duration, volume=None):
        # use the original to make a new wav, not the one that went through foo -> qcp -> wav
        conversions.converttowav(self.file, self.wavfile, samplerate=8000, channels=1, start=start, duration=duration)
        if volume is not None:
            conversions.adjustwavfilevolume(self.wavfile, volume)
        conversions.convertwavtoqcp(self.wavfile, self.qcpfile,
                                    self.optimization.GetSelection())
        return open(self.qcpfile, "rb").read()
ringers.py.diff (application/octet-stream, 739 B)
Index: ringers.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/ringers.py,v
retrieving revision 1.42
diff -u -r1.42 ringers.py
--- ringers.py	19 Sep 2005 22:24:35 -0000	1.42
+++ ringers.py	8 Oct 2005 06:53:51 -0000
@@ -235,7 +235,7 @@
                 if dlg_resp==wx.ID_NO:
                     continue
             self.thedir=self.mainwindow.ringerpath
-            decoded_file=str(file).decode(guiwidgets.media_codec)
+            decoded_file=self.decodefilename(file)
             target=self.getshortenedbasename(decoded_file, newext)
             open(target, "wb").write(filedata)
             self.AddToIndex(str(os.path.basename(target)).decode(guiwidgets.media_codec))
wallpaper.py (text/plain, 42 KB)
### BITPIM
###
### Copyright (C) 2003-2005 Roger Binns <[email protected]>
###
### This program is free software; you can redistribute it and/or modify
### it under the terms of the BitPim license as detailed in the LICENSE file.
###
### $Id: wallpaper.py,v 1.77 2005/09/09 05:28:24 djpham Exp $

"Deals with wallpaper and related views"

# standard modules
import os
import sys
import cStringIO
import random

# wx modules
import wx
import wx.lib.colourselect

# my modules
import conversions
import guiwidgets
import brewcompressedimage
import guihelper
import common
import helpids
import pubsub
import aggregatedisplay
import fileinfo

###
###  Wallpaper pane
###

class DisplayItem(guiwidgets.FileViewDisplayItem):

    datakey="wallpaper-index"
    datatype="Image"  # this is used in the tooltip
        
thewallpapermanager=None

class WallpaperView(guiwidgets.FileView):
    CURRENTFILEVERSION=2
    ID_DELETEFILE=2
    ID_IGNOREFILE=3

    # this is only used to prevent the pubsub module
    # from being GC while any instance of this class exists
    __publisher=pubsub.Publisher

    _bitmaptypemapping={
        # the extensions we use and corresponding wx types
        'bmp': wx.BITMAP_TYPE_BMP,
        'jpg': wx.BITMAP_TYPE_JPEG,
        'png': wx.BITMAP_TYPE_PNG,
        }

    organizetypes=("Origin", "Image Type", "File Size") # Image Size
    media_notification_type=pubsub.wallpaper_type

    def __init__(self, mainwindow, parent):
        global thewallpapermanager
        thewallpapermanager=self
        self.mainwindow=mainwindow
        self.usewidth=10
        self.useheight=10
        wx.FileSystem_AddHandler(BPFSHandler(self))
        self._data={'wallpaper-index': {}}
        self.updateprofilevariables(self.mainwindow.phoneprofile)

        self.organizemenu=wx.Menu()
        guiwidgets.FileView.__init__(self, mainwindow, parent, "wallpaper-watermark")

        self.wildcard="Image files|*.bmp;*.jpg;*.jpeg;*.png;*.gif;*.pnm;*.tiff;*.ico;*.bci;*.bit"


##        self.bgmenu.Insert(1,guihelper.ID_FV_PASTE, "Paste")
##        wx.EVT_MENU(self.bgmenu, guihelper.ID_FV_PASTE, self.OnPaste)

        self.organizeinfo={}
        last_mode=mainwindow.config.Read('imageorganizedby',
                                         self.organizetypes[0])
        for k in self.organizetypes:
            id=wx.NewId()
            self.organizemenu.AppendRadioItem(id, k)
            wx.EVT_MENU(self, id, self.OrganizeChange)
            self.organizeinfo[id]=getattr(self, "organizeby_"+k.replace(" ",""))
            if k==last_mode:
                self.organizemenu.Check(id, True)
            
        self.modified=False
        wx.EVT_IDLE(self, self.OnIdle)
        pubsub.subscribe(self.OnListRequest, pubsub.REQUEST_WALLPAPERS)
        pubsub.subscribe(self.OnPhoneModelChanged, pubsub.PHONE_MODEL_CHANGED)
        self._raw_image=self._shift_down=False
        wx.EVT_KEY_DOWN(self.aggdisp, self._OnKey)
        wx.EVT_KEY_UP(self.aggdisp, self._OnKey)

    def OnPhoneModelChanged(self, msg):
        phonemodule=msg.data
        self.updateprofilevariables(phonemodule.Profile)
        self.OnRefresh()

    def updateprofilevariables(self, profile):
        self.usewidth=profile.WALLPAPER_WIDTH
        self.useheight=profile.WALLPAPER_HEIGHT
        self.maxlen=profile.MAX_WALLPAPER_BASENAME_LENGTH
        self.filenamechars=profile.WALLPAPER_FILENAME_CHARS
        self.convertextension=profile.WALLPAPER_CONVERT_FORMAT
        self.convertwxbitmaptype=self._bitmaptypemapping[self.convertextension.lower()]
        if hasattr(profile,"OVERSIZE_PERCENTAGE"):
            self.woversize_percentage=profile.OVERSIZE_PERCENTAGE
            self.hoversize_percentage=profile.OVERSIZE_PERCENTAGE
        else:
            self.woversize_percentage=120
            self.hoversize_percentage=120
        
    def OnListRequest(self, msg=None):
        # temporaty quick-fix to not include video items in the list!
        l=[self._data['wallpaper-index'][x]['name'] \
           for x in self._data['wallpaper-index']\
               if self._data['wallpaper-index'][x].get('origin', None)!='video' ]
        l.sort()
        pubsub.publish(pubsub.ALL_WALLPAPERS, l)

    def OnIdle(self, _):
        "Save out changed data"
        if self.modified:
            self.modified=False
            self.populatefs(self._data)
            self.OnListRequest() # broadcast changes

    def _OnKey(self, evt):
        self._shift_down=evt.ShiftDown()
        evt.Skip()

    def OnAdd(self, evt=None):
        self._raw_image=self._shift_down
        super(WallpaperView, self).OnAdd(evt)
        # reset the fla
        self._shift_down=False

    def OrganizeChange(self, evt):
        self.mainwindow.config.Write('imageorganizedby',
                                     evt.GetEventObject().GetLabel(evt.GetId()))
        evt.GetEventObject().Check(evt.GetId(), True)
        self.OnRefresh()

    def GetSections(self):
        # get all the items
        items=[DisplayItem(self, key, self.mainwindow.wallpaperpath) for key in self._data['wallpaper-index']]
        # prune out ones we don't have images for
        items=[item for item in items if os.path.exists(item.filename)]

        self.sections=[]

        if len(items)==0:
            return self.sections
        
        # get the current sorting type
        for i in range(len(self.organizetypes)):
            item=self.organizemenu.FindItemByPosition(i)
            if self.organizemenu.IsChecked(item.GetId()):
                for sectionlabel, items in self.organizeinfo[item.GetId()](items):
                    sh=aggregatedisplay.SectionHeader(sectionlabel)
                    sh.itemsize=(self.usewidth+120, self.useheight+DisplayItem.PADDING*2)
                    for item in items:
                        item.thumbnailsize=self.usewidth, self.useheight
                    # sort items by name
                    items=[(item.name.lower(), item) for item in items]
                    items.sort()
                    items=[item for name,item in items]
                    self.sections.append( (sh, items) )
                return [sh for sh,items in self.sections]
        assert False, "Can't get here"

    def GetItemSize(self, sectionnumber, sectionheader):
        return sectionheader.itemsize

    def GetItemsFromSection(self, sectionnumber, sectionheader):
        return self.sections[sectionnumber][1]

    def organizeby_ImageType(self, items):
        types={}
        for item in items:
            t=item.fileinfo.format
            if t is None: t="<Unknown>"
            l=types.get(t, [])
            l.append(item)
            types[t]=l

        keys=types.keys()
        keys.sort()
        return [ (key, types[key]) for key in types]

    def organizeby_Origin(self, items):
        types={}
        for item in items:
            t=item.origin
            if t is None: t="Default"
            l=types.get(t, [])
            l.append(item)
            types[t]=l

        keys=types.keys()
        keys.sort()
        return [ (key, types[key]) for key in types]
        
    def organizeby_FileSize(self, items):
        
        sizes={0: ('Less than 8kb', []),
               8192: ('8 kilobytes', []),
               16384: ('16 kilobytes', []),
               32768: ('32 kilobytes', []),
               65536: ('64 kilobytes', []),
               131052: ('128 kilobytes', []),
               524208: ('512 kilobytes', []),
               1024*1024: ('One megabyte', [])}

        keys=sizes.keys()
        keys.sort()

        for item in items:
            t=item.size
            if t>=keys[-1]:
                sizes[keys[-1]][1].append(item)
                continue
            for i,k in enumerate(keys):
                if t<keys[i+1]:
                    sizes[k][1].append(item)
                    break

        return [sizes[k] for k in keys if len(sizes[k][1])]
            

    def isBCI(self, filename):
        """Returns True if the file is a Brew Compressed Image"""
        # is it a bci file?
        return open(filename, "rb").read(4)=="BCI\x00"

    def getdata(self,dict,want=guiwidgets.FileView.NONE):
        return self.genericgetdata(dict, want, self.mainwindow.wallpaperpath, 'wallpapers', 'wallpaper-index')

    def RemoveFromIndex(self, names):
        for name in names:
            wp=self._data['wallpaper-index']
            for k in wp.keys():
                if wp[k]['name']==name:
                    del wp[k]
                    self.modified=True

    def GetItemThumbnail(self, name, width, height):
        img,_=self.GetImage(name.encode(guiwidgets.media_codec))
        if img is None or not img.Ok():
            # unknown image file, display wallpaper.png
            img=wx.Image(guihelper.getresourcefile('wallpaper.png'))
        if width!=img.GetWidth() or height!=img.GetHeight():
            # scale the image. 
            sfactorw=float(width)/img.GetWidth()
            sfactorh=float(height)/img.GetHeight()
            sfactor=min(sfactorw,sfactorh) # preserve aspect ratio
            newwidth=int(img.GetWidth()*sfactor)
            newheight=int(img.GetHeight()*sfactor)
            img.Rescale(newwidth, newheight)
        bitmap=img.ConvertToBitmap()
        return bitmap

    def GetImage(self, file):
        """Gets the named image

        @return: (wxImage, filesize)
        """
        file,cons = self.GetImageConstructionInformation(file)
        
        return cons(file), int(os.stat(file).st_size)

    # This function exists because of the constraints of the HTML
    # filesystem stuff.  The previous code was reading in the file to
    # a wx.Image, saving it as a PNG to disk (wx.Bitmap.SaveFile
    # doesn't have save to memory implemented), reading it back from
    # disk and supplying it to the HTML code.  Needless to say that
    # involves unnecessary conversions and gets slower with larger
    # images. We supply the info so that callers can make the minimum
    # number of conversions possible
    
    def GetImageConstructionInformation(self, file):
        """Gets information for constructing an Image from the file

        @return: (filename to use, function to call that returns wxImage)
        """
        file=os.path.join(self.mainwindow.wallpaperpath, file)
        fi=self.GetFileInfo(file)
        if file.endswith(".mp4") or not os.path.isfile(file):
            return guihelper.getresourcefile('wallpaper.png'), wx.Image
        if self.isBCI(file):
            return file, lambda name: brewcompressedimage.getimage(brewcompressedimage.FileInputStream(file))
        if fi is not None and fi.format=='AVI':
            # return the 1st frame of the AVI file
            return file, conversions.convertavitobmp
        # LG phones may return a proprietary wallpaper media file, LGBIT
        if fi is not None and fi.format=='LGBIT':
            return file, conversions.convertfilelgbittobmp
        return file, wx.Image

    def GetFileInfo(self, filename):
        return fileinfo.identify_imagefile(filename)

    def GetImageStatInformation(self, file):
        """Returns the statinfo for file"""
        file=os.path.join(self.mainwindow.wallpaperpath, file)
        return statinfo(file)
        
    def updateindex(self, index):
        if index!=self._data['wallpaper-index']:
            self._data['wallpaper-index']=index.copy()
            self.modified=True
        
    def populate(self, dict):
        if self._data['wallpaper-index']!=dict['wallpaper-index']:
            self._data['wallpaper-index']=dict['wallpaper-index'].copy()
            self.modified=True
        self.OnRefresh()
                    
    def OnPaste(self, evt=None):
        super(WallpaperView, self).OnPaste(evt)
        if not wx.TheClipboard.Open():
            # can't access the clipboard
            return
        if wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_BITMAP)):
            do=wx.BitmapDataObject()
            success=wx.TheClipboard.GetData(do)
        else:
            success=False
        wx.TheClipboard.Close()
        if success:
            # work out a name for it
            self.OnAddImage(wx.ImageFromBitmap(do.GetBitmap()), None)

    def CanPaste(self):
        """ Return True if can accept clipboard data, False otherwise
        """
        if not wx.TheClipboard.Open():
            return False
        r=wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_FILENAME)) or\
           wx.TheClipboard.IsSupported(wx.DataFormat(wx.DF_BITMAP))
        wx.TheClipboard.Close()
        return r

    def AddToIndex(self, file, origin):
        for i in self._data['wallpaper-index']:
            if self._data['wallpaper-index'][i]['name']==file:
                self._data['wallpaper-index'][i]['origin']=origin
                return
        keys=self._data['wallpaper-index'].keys()
        idx=10000
        while idx in keys:
            idx+=1
        self._data['wallpaper-index'][idx]={'name': file, 'origin': origin}
        self.modified=True

    def OnAddFiles(self, filenames):
        for file in filenames:
            if self._raw_image:
                decoded_file=self.decodefilename(file)
                targetfilename=self.getshortenedbasename(decoded_file)
                open(targetfilename, 'wb').write(open(file, 'rb').read())
                self.AddToIndex(str(os.path.basename(targetfilename)).decode(guiwidgets.media_codec),
                                'images')
            else:
                # :::TODO:: do i need to handle bci specially here?
                # The proper way to handle custom image types, e.g. BCI and LGBIT,
                # is to add a wx.ImageHandler for it. Unfortunately wx.Image_AddHandler
                # is broken in the current wxPython, so . . .
                fi=self.GetFileInfo(file)
                if fi is not None and fi.format in ('LGBIT', 'BCI'):
                    if fi.format=='LGBIT':
                        img=conversions.convertfilelgbittobmp(file)
                    else:
                        img=brewcompressedimage.getimage(brewcompressedimage.FileInputStream(file))
                else:
                    img=wx.Image(file)
                if not img.Ok():
                    dlg=wx.MessageDialog(self, "Failed to understand the image in '"+file+"'",
                                        "Image not understood", style=wx.OK|wx.ICON_ERROR)
                    dlg.ShowModal()
                    continue
                self.OnAddImage(img,file,refresh=False)
        self.OnRefresh()


    def OnAddImage(self, img, file, refresh=True):
        # ::TODO:: if file is None, find next basename in our directory for
        # clipboard99 where 99 is next unused number
        
        dlg=ImagePreviewDialog(self, img, file, self.mainwindow.phoneprofile)
        if dlg.ShowModal()!=wx.ID_OK:
            dlg.Destroy()
            return

        img=dlg.GetResultImage()
        imgparams=dlg.GetResultParams()
        origin=dlg.GetResultOrigin()

        # ::TODO:: temporary hack - this should really be an imgparam
        extension={'BMP': 'bmp', 'JPEG': 'jpg', 'PNG': 'png'}[imgparams['format']]

        # munge name
        decoded_file=self.decodefilename(file)
        targetfilename=self.getshortenedbasename(decoded_file, extension)

        res=getattr(self, "saveimage_"+imgparams['format'])(
            img,
            targetfilename, imgparams)
        if not res:
            try:    os.remove(targetfilename)
            except: pass
            dlg=wx.MessageDialog(self, "Failed to convert the image in '"+file+"'",
                                "Image not converted", style=wx.OK|wx.ICON_ERROR)
            dlg.ShowModal()
            return

        self.AddToIndex(str(os.path.basename(targetfilename)).decode(guiwidgets.media_codec), origin)
        if refresh:
            self.OnRefresh()

    def saveimage_BMP(self, img, targetfilename, imgparams):
        if img.ComputeHistogram(wx.ImageHistogram())<=236: # quantize only does 236 or less
            img.SetOptionInt(wx.IMAGE_OPTION_BMP_FORMAT, wx.BMP_8BPP)
        return img.SaveFile(targetfilename, wx.BITMAP_TYPE_BMP)

    def saveimage_JPEG(self, img, targetfilename, imgparams):
        img.SetOptionInt("quality", 100)        
        return img.SaveFile(targetfilename, wx.BITMAP_TYPE_JPEG)

    def saveimage_PNG(self, img, targetfilename, imgparams):
        # ::TODO:: this is where the file size constraints should be examined
        # and obeyed
        return img.SaveFile(targetfilename, wx.BITMAP_TYPE_PNG)
    
    def populatefs(self, dict):
        self.thedir=self.mainwindow.wallpaperpath
        return self.genericpopulatefs(dict, 'wallpapers', 'wallpaper-index', self.CURRENTFILEVERSION)

    def getfromfs(self, result):
        self.thedir=self.mainwindow.wallpaperpath
        return self.genericgetfromfs(result, None, 'wallpaper-index', self.CURRENTFILEVERSION)

    def versionupgrade(self, dict, version):
        """Upgrade old data format read from disk

        @param dict:  The dict that was read in
        @param version: version number of the data on disk
        """

        # version 0 to 1 upgrade
        if version==0:
            version=1  # they are the same

        # 1 to 2 etc
        if version==1:
            print "converting to version 2"
            version=2
            d={}
            input=dict.get('wallpaper-index', {})
            for i in input:
                d[i]={'name': input[i]}
            dict['wallpaper-index']=d
        return dict

class WallpaperPreview(wx.PyWindow):

    def __init__(self, parent, image=None, id=1, size=wx.DefaultSize, pos=wx.DefaultPosition, style=0):
        wx.PyWindow.__init__(self, parent, id=id, size=size, pos=pos, style=style|wx.FULL_REPAINT_ON_RESIZE)
        self.bg=wx.Brush(parent.GetBackgroundColour())
        self._bufbmp=None
        
        wx.EVT_ERASE_BACKGROUND(self, lambda evt: None)
        wx.EVT_PAINT(self, self.OnPaint)

        self.SetImage(image)

    def SetImage(self, name):
        if name is None:
            self.theimage=None
        else:
            self.theimage, _=thewallpapermanager.GetImage(name)
        self.thesizedbitmap=None
        self.Refresh(False)

    def OnPaint(self, _):
        sz=self.GetClientSize()
        if self._bufbmp is None or sz.width>self._bufbmp.GetWidth() or sz.height>self._bufbmp.GetHeight():
            self._bufbmp=wx.EmptyBitmap((sz.width+64)&~8, (sz.height+64)&~8)
        dc=wx.BufferedPaintDC(self, self._bufbmp, style=wx.BUFFER_VIRTUAL_AREA)
        dc.SetBackground(self.bg)
        dc.Clear()
        if self.theimage is None: return
        # work out what size the scaled bitmap should be to retain its aspect ratio and fit within sz
        sfactorw=float(sz.width)/self.theimage.GetWidth()
        sfactorh=float(sz.height)/self.theimage.GetHeight()
        sfactor=min(sfactorw,sfactorh)
        newwidth=int(self.theimage.GetWidth()*sfactor)
        newheight=int(self.theimage.GetHeight()*sfactor)
        if self.thesizedbitmap is None or self.thesizedbitmap.GetWidth()!=newwidth or \
           self.thesizedbitmap.GetHeight()!=newheight:
            self.thesizedbitmap=self.theimage.Scale(newwidth, newheight).ConvertToBitmap()
        dc.DrawBitmap(self.thesizedbitmap, sz.width/2-newwidth/2, sz.height/2-newheight/2, True)
        
    


def ScaleImageIntoBitmap(img, usewidth, useheight, bgcolor=None, valign="center"):
    """Scales the image and returns a bitmap

    @param usewidth: the width of the new image
    @param useheight: the height of the new image
    @param bgcolor: the background colour as a string ("ff0000" is red etc).  If this
                    is none then the background is made transparent"""
    if bgcolor is None:
        bitmap=wx.EmptyBitmap(usewidth, useheight, 24) # have to use 24 bit for transparent background
    else:
        bitmap=wx.EmptyBitmap(usewidth, useheight)
    mdc=wx.MemoryDC()
    mdc.SelectObject(bitmap)
    # scale the source. 
    sfactorw=usewidth*1.0/img.GetWidth()
    sfactorh=useheight*1.0/img.GetHeight()
    sfactor=min(sfactorw,sfactorh) # preserve aspect ratio
    newwidth=int(img.GetWidth()*sfactor/1.0)
    newheight=int(img.GetHeight()*sfactor/1.0)

    img.Rescale(newwidth, newheight)
    # deal with bgcolor/transparency
    if bgcolor is not None:
        transparent=None
        assert len(bgcolor)==6
        red=int(bgcolor[0:2],16)
        green=int(bgcolor[2:4],16)
        blue=int(bgcolor[4:6],16)
        mdc.SetBackground(wx.TheBrushList.FindOrCreateBrush(wx.Colour(red,green,blue), wx.SOLID))
    else:
        transparent=wx.Colour(*(img.FindFirstUnusedColour()[1:]))
        mdc.SetBackground(wx.TheBrushList.FindOrCreateBrush(transparent, wx.SOLID))
    mdc.Clear()
    mdc.SelectObject(bitmap)
    # figure where to place image to centre it
    posx=usewidth-(usewidth+newwidth)/2
    if valign in ("top", "clip"):
        posy=0
    elif valign=="center":
        posy=useheight-(useheight+newheight)/2
    else:
        assert False, "bad valign "+valign
        posy=0
    # draw the image
    mdc.DrawBitmap(img.ConvertToBitmap(), posx, posy, True)
    # clean up
    mdc.SelectObject(wx.NullBitmap)
    # deal with transparency
    if transparent is not None:
            mask=wx.Mask(bitmap, transparent)
            bitmap.SetMask(mask)
    if valign=="clip" and newheight!=useheight:
        return bitmap.GetSubBitmap( (0,0,usewidth,newheight) )
    return bitmap

###
### Virtual filesystem where the images etc come from for the HTML stuff
###

statinfo=common.statinfo

class BPFSHandler(wx.FileSystemHandler):

    CACHELOWWATER=80
    CACHEHIGHWATER=100

    def __init__(self, wallpapermanager):
        wx.FileSystemHandler.__init__(self)
        self.wpm=wallpapermanager
        self.cache={}

    def _GetCache(self, location, statinfo):
        """Return the cached item, or None

        Note that the location value includes the filename and the parameters such as width/height
        """
        if statinfo is None:
            print "bad location",location
            return None
        return self.cache.get( (location, statinfo), None)

    def _AddCache(self, location, statinfo, value):
        "Add the item to the cache"
        # we also prune it down in size if necessary
        if len(self.cache)>=self.CACHEHIGHWATER:
            print "BPFSHandler cache flush"
            # random replacement - almost as good as LRU ...
            while len(self.cache)>self.CACHELOWWATER:
                del self.cache[random.choice(self.cache.keys())]
        self.cache[(location, statinfo)]=value

    def CanOpen(self, location):

        # The call to self.GetProtocol causes an exception if the
        # location starts with a pathname!  This typically happens
        # when the help file is opened.  So we work around that bug
        # with this quick check.
        
        if location.startswith("/"):
            return False
        proto=self.GetProtocol(location)
        if proto=="bpimage" or proto=="bpuserimage":
            return True
        return False

    def OpenFile(self,filesystem,location):
        try:
            res=self._OpenFile(filesystem,location)
        except:
            res=None
            print "Exception in getting image file - you can't do that!"
            print common.formatexception()
        if res is not None:
            # we have to seek the file object back to the begining and make a new
            # wx.FSFile each time as wxPython doesn't do the reference counting
            # correctly
            res[0].seek(0)
            args=(wx.InputStream(res[0]),)+res[1:]
            res=wx.FSFile(*args)
        return res

    def _OpenFile(self, filesystem, location):
        proto=self.GetProtocol(location)
        r=self.GetRightLocation(location)
        params=r.split(';')
        r=params[0]
        params=params[1:]
        p={}
        for param in params:
            x=param.find('=')
            key=str(param[:x])
            value=param[x+1:]
            if key=='width' or key=='height':
                p[key]=int(value)
            else:
                p[key]=value
        if proto=="bpimage":
            return self.OpenBPImageFile(location, r, **p)
        elif proto=="bpuserimage":
            return self.OpenBPUserImageFile(location, r, **p)
        return None

    def OpenBPUserImageFile(self, location, name, **kwargs):
        si=self.wpm.GetImageStatInformation(name)
        res=self._GetCache(location, si)
        if res is not None: return res
        file,cons=self.wpm.GetImageConstructionInformation(name)
        if cons == wx.Image:
            res=BPFSImageFile(self, location, file, **kwargs)
        else:
            res=BPFSImageFile(self, location, img=cons(file), **kwargs)
        self._AddCache(location, si, res)
        return res

    def OpenBPImageFile(self, location, name, **kwargs):
        f=guihelper.getresourcefile(name)
        if not os.path.isfile(f):
            print f,"doesn't exist"
            return None
        si=statinfo(f)
        res=self._GetCache(location, si)
        if res is not None: return res
        res=BPFSImageFile(self, location, name=f, **kwargs)
        self._AddCache(location, si, res)
        return res

def BPFSImageFile(fshandler, location, name=None, img=None, width=-1, height=-1, valign="center", bgcolor=None):
    """Handles image files

    If we have to do any conversion on the file then we return PNG
    data.  This used to be a class derived from wx.FSFile, but due to
    various wxPython bugs it instead returns the parameters to make a
    wx.FSFile since a new one has to be made every time.
    """
        # special fast path if we aren't resizing or converting image
    if img is None and width<0 and height<0:
        mime=guihelper.getwxmimetype(name)
        # wxPython 2.5.3 has a new bug and fails to read bmp files returned as a stream
        if mime not in (None, "image/x-bmp"):
            return (open(name, "rb"), location, mime, "", wx.DateTime_Now())

    if img is None:
        img=wx.Image(name)

    if width>0 and height>0:
        b=ScaleImageIntoBitmap(img, width, height, bgcolor, valign)
    else:
        b=img.ConvertToBitmap()

    f=common.gettempfilename("png")
    if not b.SaveFile(f, wx.BITMAP_TYPE_PNG):
        raise Exception, "Saving to png failed"

    data=open(f, "rb").read()
    os.remove(f)

    return (cStringIO.StringIO(data), location, "image/png", "", wx.DateTime_Now())

    
class ImageCropSelect(wx.ScrolledWindow):

    def __init__(self, parent, image, previewwindow=None, id=1, resultsize=(100,100), size=wx.DefaultSize, pos=wx.DefaultPosition, style=0):
        wx.ScrolledWindow.__init__(self, parent, id=id, size=size, pos=pos, style=style|wx.FULL_REPAINT_ON_RESIZE)
        self.previewwindow=previewwindow
        self.bg=wx.Brush(wx.WHITE)
        self.parentbg=wx.Brush(parent.GetBackgroundColour())
        self._bufbmp=None

        self.anchors=None
        
        wx.EVT_ERASE_BACKGROUND(self, lambda evt: None)
        wx.EVT_PAINT(self, self.OnPaint)

        self.image=image
        self.origimage=image
        self.setresultsize(resultsize)

        # cursors for outside, inside, on selection, pressing bad mouse button
        self.cursors=[wx.StockCursor(c) for c in (wx.CURSOR_ARROW, wx.CURSOR_HAND, wx.CURSOR_SIZING, wx.CURSOR_NO_ENTRY)]
        self.clickpoint=None 
        wx.EVT_MOTION(self, self.OnMotion)
        wx.EVT_LEFT_DOWN(self, self.OnLeftDown)
        wx.EVT_LEFT_UP(self, self.OnLeftUp)

    def SetPreviewWindow(self, previewwindow):
        self.previewwindow=previewwindow

    def OnPaint(self, _):
        sz=self.thebmp.GetWidth(), self.thebmp.GetHeight()
        sz2=self.GetClientSize()
        sz=max(sz[0],sz2[0])+32,max(sz[1],sz2[1])+32
        if self._bufbmp is None or self._bufbmp.GetWidth()<sz[0] or self._bufbmp.GetHeight()<sz[1]:
            self._bufbmp=wx.EmptyBitmap((sz[0]+64)&~8, (sz[1]+64)&~8)
        dc=wx.BufferedPaintDC(self, self._bufbmp, style=wx.BUFFER_VIRTUAL_AREA)
        if sz2[0]<sz[0] or sz2[1]<sz[1]:
            dc.SetBackground(self.parentbg)
            dc.Clear()
        dc.DrawBitmap(self.thebmp, 0, 0, False)
        # draw bounding box next
        l,t,r,b=self.anchors
        points=(l,t), (r,t), (r,b), (l,b)
        dc.DrawLines( points+(points[0],) )
        for x,y in points:
            dc.DrawRectangle(x-5, y-5, 10, 10)

    OUTSIDE=0
    INSIDE=1
    HANDLE_LT=2
    HANDLE_RT=3
    HANDLE_RB=4
    HANDLE_LB=5
            
    def _hittest(self, evt):
        l,t,r,b=self.anchors
        within=lambda x,y,l,t,r,b:  l<=x<=r and t<=y<=b
        x,y=self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
        for i,(ptx,pty) in enumerate(((l,t), (r,t), (r,b), (l,b))):
            if within(x,y,ptx-5, pty-5, ptx+5,pty+5):
                return self.HANDLE_LT+i
        if within(x,y,l,t,r,b):
            return self.INSIDE
        return self.OUTSIDE
            
    def OnMotion(self, evt):
        if evt.Dragging():
            return self.OnMotionDragging(evt)
        self.UpdateCursor(evt)

    def UpdateCursor(self, evt):
        ht=self._hittest(evt)
        self.SetCursor(self.cursors[min(2,ht)])

    def OnMotionDragging(self, evt):
        if not evt.LeftIsDown() or self.clickpoint is None:
            self.SetCursor(self.cursors[3])
            return
        xx,yy=self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
        deltax=xx-self.origevtpos[0]
        deltay=yy-self.origevtpos[1]

        if self.clickpoint==self.INSIDE:
            newanchors=self.origanchors[0]+deltax, self.origanchors[1]+deltay, \
                        self.origanchors[2]+deltax, self.origanchors[3]+deltay
            iw=self.dimensions[0]
            ih=self.dimensions[1]
            # would box be out of bounds?
            if newanchors[0]<0:
                newanchors=0,newanchors[1], self.origanchors[2]-self.origanchors[0], newanchors[3]
            if newanchors[1]<0:
                newanchors=newanchors[0], 0, newanchors[2], self.origanchors[3]-self.origanchors[1]
            if newanchors[2]>iw:
                newanchors=iw-(self.origanchors[2]-self.origanchors[0]),newanchors[1],iw, newanchors[3]
            if newanchors[3]>ih:
                newanchors=newanchors[0],ih-(self.origanchors[3]-self.origanchors[1]), newanchors[2],ih
            self.anchors=newanchors
            self.Refresh(False)
            self.updatepreview()
            return
        # work out how to do this with left top and then expand code
        if self.clickpoint==self.HANDLE_LT:
            aa=0,1,-1,-1
        elif self.clickpoint==self.HANDLE_RT:
            aa=2,1,+1,-1
        elif self.clickpoint==self.HANDLE_RB:
            aa=2,3,+1,+1
        elif self.clickpoint==self.HANDLE_LB:
            aa=0,3,-1,+1
        else:
            assert False, "can't get here"
            
        na=[self.origanchors[0],self.origanchors[1],self.origanchors[2],self.origanchors[3]]
        na[aa[0]]=na[aa[0]]+deltax
        na[aa[1]]=na[aa[1]]+deltay
        neww=na[2]-na[0]
        newh=na[3]-na[1]
        ar=float(neww)/newh
        if ar<self.aspectratio:
            na[aa[0]]=na[aa[0]]+(self.aspectratio*newh-neww)*aa[2]
        elif ar>self.aspectratio:
            na[aa[1]]=na[aa[1]]+(neww/self.aspectratio-newh)*aa[3]
            
        # ignore if image would be smaller than 10 pixels in any direction
        if neww<10 or newh<10:
            return
        # if any point is off screen, we need to fix things up
        if na[0]<0:
            xdiff=-na[0]
            ydiff=xdiff/self.aspectratio
            na[0]=0
            na[1]+=ydiff
        if na[1]<0:
            ydiff=-na[1]
            xdiff=ydiff*self.aspectratio
            na[1]=0
            na[0]-=xdiff
        if na[2]>self.dimensions[0]:
            xdiff=na[2]-self.dimensions[0]
            ydiff=xdiff/self.aspectratio
            na[2]=na[2]-xdiff
            na[3]=na[3]-ydiff
        if na[3]>self.dimensions[1]:
            ydiff=na[3]-self.dimensions[1]
            xdiff=ydiff*self.aspectratio
            na[2]=na[2]-xdiff
            na[3]=na[3]-ydiff
        if na[0]<0 or na[1]<0 or na[2]>self.dimensions[0] or na[3]>self.dimensions[1]:
            print "offscreen fixup not written yet"
            return

        # work out aspect ratio
        self.anchors=na
        self.Refresh(False)
        self.updatepreview()
        return
            
        
    def OnLeftDown(self, evt):
        ht=self._hittest(evt)
        if ht==self.OUTSIDE:
            self.SetCursor(self.cursors[3])
            return
        self.clickpoint=ht
        xx,yy=self.CalcUnscrolledPosition(evt.GetX(), evt.GetY())
        self.origevtpos=xx,yy
        self.origanchors=self.anchors
        
    def OnLeftUp(self, evt):
        self.clickpoint=None
        self.UpdateCursor(evt)

    def setlbcolour(self, colour):
        self.bg=wx.Brush(colour)
        self.remakebitmap()

    def SetZoom(self, factor):
        curzoom=float(self.image.GetWidth())/self.origimage.GetWidth()
        self.anchors=[a*factor/curzoom for a in self.anchors]
        self.image=self.origimage.Scale(self.origimage.GetWidth()*factor, self.origimage.GetHeight()*factor)
        self.setresultsize(self.resultsize)

    def setresultsize(self, (w,h)):
        self.resultsize=w,h
        self.aspectratio=ratio=float(w)/h
        imgratio=float(self.image.GetWidth())/self.image.GetHeight()
        
        neww=self.image.GetWidth()
        newh=self.image.GetHeight()
        if imgratio<ratio:
            neww*=ratio/imgratio
        elif imgratio>ratio:
            newh*=imgratio/ratio
            
        # ensure a minimum size
        neww=max(neww, 50)
        newh=max(newh, 50)
        
        # update anchors if never set
        if self.anchors==None:
            self.anchors=0.1 * neww, 0.1 * newh, 0.9 * neww, 0.9 * newh

        # fixup anchors
        l,t,r,b=self.anchors
        l=min(neww-40, l)
        r=min(neww-10, r)
        if r-l<20: r=40
        t=min(newh-40, t)
        b=min(newh-10, b)
        if b-t<20: b=40
        aratio=float(r-l)/(b-t)
        if aratio<ratio:
            b=t+(r-l)/ratio
        elif aratio>ratio:
            r=l+(b-t)*ratio
        self.anchors=l,t,r,b

        self.dimensions=neww,newh
        self.thebmp=wx.EmptyBitmap(neww, newh)

        self.remakebitmap()


    def remakebitmap(self):
        w,h=self.dimensions
        dc=wx.MemoryDC()
        dc.SelectObject(self.thebmp)
        dc.SetBackground(self.bg)
        dc.Clear()
        dc.DrawBitmap(self.image.ConvertToBitmap(), w/2-self.image.GetWidth()/2, h/2-self.image.GetHeight()/2, True)
        dc.SelectObject(wx.NullBitmap)
        self.imageofthebmp=None
        self.SetVirtualSize( (w, h) )
        self.SetScrollRate(1,1)

        self.updatepreview()
        self.Refresh(False)

    # updating the preview is expensive so it is done on demand.  We
    # tell the preview window there has been an update and it calls
    # back from its paint method
    def updatepreview(self):
        if self.previewwindow:
            self.previewwindow.SetUpdated(self.GetPreview)

    def GetPreview(self):
        w,h=self.resultsize
        l,t,r,b=self.anchors
        scale=max(float(w+0.99999)/(r-l), float(h+0.99999)/(b-t))

        # we are fine using this to scale down
        if True and scale<1:
            sub=wx.EmptyBitmap(w,h)
            mdcsub=wx.MemoryDC()
            mdcsub.SelectObject(sub)
            mdcsub.SetUserScale(scale, scale)
            mdc=wx.MemoryDC()
            mdc.SelectObject(self.thebmp)
            mdcsub.Blit(0,0,r-l,b-t,mdc,l,t)

            mdc.SelectObject(wx.NullBitmap)
            mdcsub.SelectObject(wx.NullBitmap)
            return sub

        sub=self.thebmp.GetSubBitmap( (l,t,(r-l),(b-t)) )
        sub=sub.ConvertToImage()
        sub.Rescale(w,h)
        return sub.ConvertToBitmap()

class ImagePreview(wx.PyWindow):

    def __init__(self, parent):
        wx.PyWindow.__init__(self, parent)
        wx.EVT_ERASE_BACKGROUND(self, lambda evt: None)
        wx.EVT_PAINT(self, self.OnPaint)
        self.bmp=wx.EmptyBitmap(1,1)
        self.updater=None

    def SetUpdated(self, updater):
        self.updater=updater
        self.Refresh(True)

    def OnPaint(self, _):
        if self.updater is not None:
            self.bmp=self.updater()
            self.updater=None
        dc=wx.PaintDC(self)
        dc.DrawBitmap(self.bmp, 0, 0, False)


class ImagePreviewDialog(wx.Dialog):

    SCALES=[ (0.25, "1/4"),
             (0.5,  "1/2"),
             (1, "1"),
             (2, "2"),
             (4, "4")]

    def __init__(self, parent, image, filename, phoneprofile):
        wx.Dialog.__init__(self, parent, -1, "Image Preview", style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER|wx.SYSTEM_MENU|wx.MAXIMIZE_BOX|wx.MINIMIZE_BOX)
        self.phoneprofile=phoneprofile
        self.filename=filename
        self.image=image

        vbsouter=wx.BoxSizer(wx.VERTICAL)

        hbs=wx.BoxSizer(wx.HORIZONTAL)
        self.cropselect=ImageCropSelect(self, image)

        vbs=wx.BoxSizer(wx.VERTICAL)
        self.colourselect=wx.lib.colourselect.ColourSelect(self, wx.NewId(), "Background ...", (255,255,255))
        vbs.Add(self.colourselect, 0, wx.ALL|wx.EXPAND, 5)
        wx.lib.colourselect.EVT_COLOURSELECT(self, self.colourselect.GetId(), self.OnBackgroundColour)
        vbs.Add(wx.StaticText(self, -1, "Origin"), 0, wx.ALL, 5)
        self.originbox=wx.ListBox(self, size=(-1, 100))
        vbs.Add(self.originbox, 0, wx.ALL|wx.EXPAND, 5)
        vbs.Add(wx.StaticText(self, -1, "Target"), 0, wx.ALL, 5)
        self.targetbox=wx.ListBox(self, size=(-1,100))
        vbs.Add(self.targetbox, 0, wx.EXPAND|wx.ALL, 5)
        vbs.Add(wx.StaticText(self, -1, "Scale"), 0, wx.ALL, 5)

        for one,(s,_) in enumerate(self.SCALES):
            if s==1: break
        self.slider=wx.Slider(self, -1, one, 0, len(self.SCALES)-1, style=wx.HORIZONTAL|wx.SL_AUTOTICKS)
        wx.EVT_SCROLL(self, self.SetZoom)
        vbs.Add(self.slider, 0, wx.ALL|wx.EXPAND, 5)
        self.zoomlabel=wx.StaticText(self, -1, self.SCALES[one][1])
        vbs.Add(self.zoomlabel, 0, wx.ALL|wx.ALIGN_CENTRE_HORIZONTAL, 5)
        
        vbs.Add(wx.StaticText(self, -1, "Preview"), 0, wx.ALL, 5)
        self.imagepreview=ImagePreview(self)
        self.cropselect.SetPreviewWindow(self.imagepreview)
        vbs.Add(self.imagepreview, 0, wx.ALL, 5)


        hbs.Add(vbs, 0, wx.ALL, 5)
        hbs.Add(self.cropselect, 1, wx.ALL|wx.EXPAND, 5)

        vbsouter.Add(hbs, 1, wx.EXPAND|wx.ALL, 5)
        
        vbsouter.Add(wx.StaticLine(self, -1, style=wx.LI_HORIZONTAL), 0, wx.EXPAND|wx.ALL, 5)
        vbsouter.Add(self.CreateButtonSizer(wx.OK|wx.CANCEL|wx.HELP), 0, wx.ALIGN_CENTRE|wx.ALL, 5)

        wx.EVT_LISTBOX(self, self.originbox.GetId(), self.OnOriginSelect)
        wx.EVT_LISTBOX_DCLICK(self, self.originbox.GetId(), self.OnOriginSelect)

        wx.EVT_LISTBOX(self, self.targetbox.GetId(), self.OnTargetSelect)
        wx.EVT_LISTBOX_DCLICK(self, self.targetbox.GetId(), self.OnTargetSelect)

        wx.EVT_BUTTON(self, wx.ID_HELP, lambda _:
                      wx.GetApp().displayhelpid(helpids.ID_DLG_IMAGEPREVIEW))
        self.originbox.Set(phoneprofile.GetImageOrigins().keys())
        self.originbox.SetSelection(0)
        self.OnOriginSelect(None)
        
        self.SetSizer(vbsouter)
        vbsouter.Fit(self)
        guiwidgets.set_size("wallpaperpreview", self, 80, 1.0)

    def ShowModal(self):
        res=wx.Dialog.ShowModal(self)
        guiwidgets.save_size("wallpaperpreview", self.GetRect())
        return res

    def SetZoom(self, evt):
        self.cropselect.SetZoom(self.SCALES[evt.GetPosition()][0])
        self.zoomlabel.SetLabel(self.SCALES[evt.GetPosition()][1])
        return

    def OnBackgroundColour(self, evt):
        self.cropselect.setlbcolour(evt.GetValue())

    def OnOriginSelect(self, _):
        v=self.originbox.GetStringSelection()
        assert v is not None
        t=self.targetbox.GetStringSelection()
        self.targets=self.phoneprofile.GetTargetsForImageOrigin(v)
        keys=self.targets.keys()
        keys.sort()
        self.targetbox.Set(keys)
        if t in keys:
            self.targetbox.SetSelection(keys.index(t))
        else:
            self.targetbox.SetSelection(0)
        self.OnTargetSelect(None)

    def OnTargetSelect(self, _):
        v=self.targetbox.GetStringSelection()
        print "target is",v
        w,h=self.targets[v]['width'],self.targets[v]['height']
        self.imagepreview.SetSize( (w,h) )
        self.cropselect.setresultsize( (w, h) )
        sz=self.GetSizer()
        if sz is not None:
            # sizer doesn't autmatically size when we change preview size
            # so this forces that, as well as the repaint due to screen corruption
            sz.Layout()
            self.Refresh(True)
        
    def GetResultImage(self):
        return self.imagepreview.bmp.ConvertToImage()

    def GetResultParams(self):
        return self.targets[self.targetbox.GetStringSelection()]

    def GetResultOrigin(self):
        return self.originbox.GetStringSelection()



if __name__=='__main__':

    if __debug__:
        def profile(filename, command):
            import hotshot, hotshot.stats, os
            file=os.path.abspath(filename)
            profile=hotshot.Profile(file)
            profile.run(command)
            profile.close()
            del profile
            howmany=100
            stats=hotshot.stats.load(file)
            stats.strip_dirs()
            stats.sort_stats('time', 'calls')
            stats.print_stats(100)
            stats.sort_stats('cum', 'calls')
            stats.print_stats(100)
            stats.sort_stats('calls', 'time')
            stats.print_stats(100)
            sys.exit(0)

    class FakeProfile:

        def GetImageOrigins(self):
            return {"images": {'description': 'General images'},
                    "mms": {'description': 'Multimedia Messages'},
                    "camera": {'description': 'Camera images'}}

        def GetTargetsForImageOrigin(self, origin):
            return {"wallpaper": {'width': 100, 'height': 200, 'description': 'Display as wallpaper'},
                    "photoid": {'width': 100, 'height': 150, 'description': 'Display as photo id'},
                    "outsidelcd": {'width': 90, 'height': 80, 'description': 'Display on outside screen'}}

    def run():
        app=wx.PySimpleApp()
        dlg=ImagePreviewDialog(None, wx.Image("test.jpg"), "foobar.png", FakeProfile())
        dlg.ShowModal()

    if __debug__ and True:
        profile("wp.prof", "run()")
    run()
wallpaper.py.diff (application/octet-stream, 1.1 KB)
Index: wallpaper.py
===================================================================
RCS file: /cvsroot/bitpim/bitpim/wallpaper.py,v
retrieving revision 1.77
diff -u -r1.77 wallpaper.py
--- wallpaper.py	9 Sep 2005 05:28:24 -0000	1.77
+++ wallpaper.py	8 Oct 2005 06:53:38 -0000
@@ -363,7 +363,7 @@
     def OnAddFiles(self, filenames):
         for file in filenames:
             if self._raw_image:
-                decoded_file=str(file).decode(guiwidgets.media_codec)
+                decoded_file=self.decodefilename(file)
                 targetfilename=self.getshortenedbasename(decoded_file)
                 open(targetfilename, 'wb').write(open(file, 'rb').read())
                 self.AddToIndex(str(os.path.basename(targetfilename)).decode(guiwidgets.media_codec),
@@ -407,7 +407,7 @@
         extension={'BMP': 'bmp', 'JPEG': 'jpg', 'PNG': 'png'}[imgparams['format']]
 
         # munge name
-        decoded_file=str(file).decode(guiwidgets.media_codec)
+        decoded_file=self.decodefilename(file)
         targetfilename=self.getshortenedbasename(decoded_file, extension)
 
         res=getattr(self, "saveimage_"+imgparams['format'])(
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.