DZone Snippets is a public source code repository. Easily build up your personal collection of code snippets, categorize them with tags / keywords, and share them with the world
  • submit to reddit
        unit StreamAdapter.pas

//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com

unit StreamAdapter;

interface

uses
  Classes;

type
  IStream = interface( IInterface )
    ['{FBEF199A-09BC-4B61-89EA-1EF8B22C93A5}']
    function Read(var Buffer; const Count: Longint): Longint;
    function Write(const Buffer; const Count: Longint): Longint;
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;
    procedure ReadBuffer(var Buffer; const Count: Longint);
    procedure WriteBuffer(const Buffer; const Count: Longint);
    function CopyFrom(Source: TStream; const Count: Int64): Int64;
    function WriteTo(Dest: TStream; const Count: Int64): Int64;

    procedure SetPosition( const Value: Int64 );
    procedure SetSize( const Value: Int64 );
    function GetPosition: Int64;
    function GetSize: Int64;

    property Position: Int64 read GetPosition write SetPosition;
    property Size: Int64 read GetSize write SetSize;
  end;

  TStreamAdapter = class( TInterfacedObject, IStream )
  private
    FStream: TStream;
    procedure SetPosition( const Value: Int64 );
    procedure SetSize( const Value: Int64 );
    function GetPosition: Int64;
    function GetSize: Int64;

  public
    constructor Create( Stream: TStream );
    destructor Destroy; override;

    function Read(var Buffer; const Count: Longint): Longint;
    function Write(const Buffer; const Count: Longint): Longint;

    procedure ReadBuffer(var Buffer; const Count: Longint);
    procedure WriteBuffer(const Buffer; const Count: Longint);

    function CopyFrom(Source: TStream; const Count: Int64): Int64;
    function WriteTo(Dest: TStream; const Count: Int64): Int64;

    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64;

    property Position: Int64 read GetPosition write SetPosition;
    property Size: Int64 read GetSize write SetSize;
  end;

implementation

{ TStreamAdapter }

function TStreamAdapter.CopyFrom(Source: TStream; const Count: Int64): Int64;
begin
  Result := FStream.CopyFrom( Source, Count );
end;

constructor TStreamAdapter.Create(Stream: TStream);
begin
  FStream := Stream;
end;

destructor TStreamAdapter.Destroy;
begin
  FStream.Free;
  inherited;
end;

function TStreamAdapter.GetPosition: Int64;
begin
  Result := FStream.Position;
end;

function TStreamAdapter.GetSize: Int64;
begin
  Result := FStream.Size;
end;

function TStreamAdapter.Read(var Buffer; const Count: Integer): Longint;
begin
  Result := FStream.Read( Buffer, Count );
end;

procedure TStreamAdapter.ReadBuffer(var Buffer; const Count: Integer);
begin
  FStream.ReadBuffer( Buffer, Count );
end;

function TStreamAdapter.Seek(const Offset: Int64;
  Origin: TSeekOrigin): Int64;
begin
  Result := FStream.Seek( Offset, Origin );
end;

procedure TStreamAdapter.SetPosition(const Value: Int64);
begin
  FStream.Position := Value;
end;

procedure TStreamAdapter.SetSize(const Value: Int64);
begin
  FStream.Size := Value;
end;

function TStreamAdapter.Write(const Buffer; const Count: Integer): Longint;
begin
  Result := FStream.Write( Buffer, Count );
end;

procedure TStreamAdapter.WriteBuffer(const Buffer; const Count: Integer);
begin
  FStream.WriteBuffer( Buffer, Count );
end;

function TStreamAdapter.WriteTo(Dest: TStream; const Count: Int64): Int64;
begin
  Result := Dest.CopyFrom( FStream, Count );
end;

end.


unit PersistentTree.pas
//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com

unit PersistentTree;

interface

uses
  Windows, Classes, SysUtils, StreamAdapter;

type
  EPersistentTree = class( Exception );

  TPersistentTree = class;

  TPersistentTreeClass = class of TPersistentTree;

  TPersistentTree = class( TStream )
  private
    FStream: IStream;
    FList: TList;
    FBaseClass: TPersistentTreeClass;
    FOwner, FParent: TPersistentTree;
    FOwnStream: Boolean;
    FDataFilename, FFilename: string;
    FLastPosition, FDataBegin, FDataLength: Int64;

    function GetItem(const Index: Integer): TPersistentTree;
    function GetCount: Integer;
    function GetStream: TStream;
    function Import( Item: TPersistentTree ): Boolean;
    procedure ClearData;
    procedure RecreateStream( const Pos: Int64; const Deep: Boolean = False );
    procedure Synchronize;

  protected
    //override to provide writing/reading notifications
    procedure Loaded; virtual;
    procedure Saving; virtual;

    //derived from TStream
    function GetSize: Int64; override;
    procedure SetSize(NewSize: Longint); override;
    procedure SetSize(const NewSize: Int64); override;

  public
    constructor Create; virtual;
    destructor Destroy; override;

    //derived from TStream
    function Read( var Buffer; Count: Longint ): Longint; override;
    function Write( const Buffer; Count: Longint ): Longint; override;
    function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; override;

    function Truncate: Int64;
    function ReadString: string;
    procedure WriteString( const Data: string );

    procedure Save( const AFilename: string ); overload;
    procedure Save( Stream: TStream ); overload;
    procedure Load( const AFilename: string ); overload;
    procedure Load( Stream: IStream ); overload;
    procedure Load( Stream: TStream ); overload;

    function Add: TPersistentTree; overload;
    function Add( Item: TPersistentTree ): Integer; overload;
    procedure Insert( const Index: Integer; Item: TPersistentTree);
    function IndexOf( Item: TPersistentTree ): Integer;
    function Remove( Item: TPersistentTree ): Integer;
    procedure Delete( const Index: Integer);
    function Extract( Item: TPersistentTree ): TPersistentTree;
    procedure Exchange( const IndexA, IndexB: Integer );
    procedure Move(const CurIndex, NewIndex: Integer);
    procedure Clear;

    property Items[ const Index: Integer ]: TPersistentTree read GetItem; default;
    property Count: Integer read GetCount;
    property Owner: TPersistentTree read FOwner;
    property Parent: TPersistentTree read FParent;
    property Filename: string read FFilename;
    property BaseClass: TPersistentTreeClass read FBaseClass write FBaseClass;
  end;

  TPersistentTreeHeader = packed record
    Sig: array[0..4] of Char;
    Ver: Word;
  end;

const
  PERSISTENT_TREE_HEADER: TPersistentTreeHeader = ( Sig: 'PTREE'; Ver: 1 );

function GetTempFile: string;


implementation

function GetTempFile: string;
var
  Path: array[0..MAX_PATH-1] of Char;
begin
  GetTempPath( MAX_PATH, Path );
  GetTempFileName( Path, 'BUF', 0, Path );
  Result := Path;
end;

{ TPersistentTree }

procedure TPersistentTree.Clear;
var
  I: Integer;
begin
  for I := FList.Count - 1 downto 0 do
  begin
    TPersistentTree( FList[I] ).Free;
    FList.Delete( I );
  end;
end;

constructor TPersistentTree.Create;
begin
  FBaseClass := TPersistentTreeClass( Self.ClassType );
  FList := TList.Create;
  FStream := TStreamAdapter.Create( GetStream );
  FOwnStream := True;
end;

destructor TPersistentTree.Destroy;
begin
  ClearData;
  FList.Free;
  inherited;
end;

procedure TPersistentTree.Exchange(const IndexA, IndexB: Integer);
begin
  FList.Exchange( IndexA, IndexB );
end;

function TPersistentTree.GetCount: Integer;
begin
  Result := FList.Count;
end;

function TPersistentTree.GetItem(const Index: Integer): TPersistentTree;
begin
  Result := FList[ Index ];
end;

function TPersistentTree.IndexOf(
  Item: TPersistentTree): Integer;
begin
  Result := FList.IndexOf( Item );
end;

procedure TPersistentTree.Load(const AFilename: string);
var
  FS: TFileStream;
  //Header: TPersistentTreeHeader;
begin
  FS := TFileStream.Create( AFilename, fmOpenRead or fmShareDenyWrite );
  try
    //FS.Read( Header, SizeOf( TPersistentTreeHeader ) );
    //if not CompareMem( @Header, @PERSISTENT_TREE_HEADER, SizeOf( TPersistentTreeHeader ) ) then
    //  raise EPersistentTree.CreateFmt( '%s.LoadFromFile :: "%s" Not Recognized', [ClassName, AFilename] );
    Load( FS );
    FFilename := AFilename;
  except
    FS.Free;
    raise;
  end;
end;

procedure TPersistentTree.Load(Stream: TStream);
begin
  Load( TStreamAdapter.Create( Stream ) );
end;

function TPersistentTree.Remove(Item: TPersistentTree): Integer;
begin
  Result := FList.Remove( Item );
  if Result >= 0 then
    Item.Free;
end;

procedure TPersistentTree.Save( const AFilename: string );
var
  FS: TFileStream;
begin
  FS := TFileStream.Create( AFilename, fmCreate or fmShareDenyWrite );
  try
    //FS.Write( PERSISTENT_TREE_HEADER, SizeOf( TPersistentTreeHeader ) );
    Save( FS );
  finally
    FS.Free;
  end;
end;

procedure TPersistentTree.Save(Stream: TStream);
var
  I: LongInt;
begin
  Seek( 0, soBeginning );
  Saving;

  FDataLength := Size;
  Stream.Write( FDataLength, SizeOf( FDataLength ) );
  Stream.CopyFrom( Self, 0 );

  I := FList.Count;
  Stream.Write( I, SizeOf( I ) );
  for I := 0 to FList.Count-1 do
    Self[I].Save( Stream );
end;

function TPersistentTree.Write( const Buffer; Count: Longint ): Longint;
begin
  if FOwnStream then
    Result := FStream.Write( Buffer, Count )
  else
  begin
    Synchronize;
    if Position + Count > Size then
      RecreateStream( Position );
    Result := FStream.Write( Buffer, Count );
    FLastPosition := FStream.Position;          
  end;

end;

function TPersistentTree.Read( var Buffer; Count: Longint): Longint;
begin
  if FOwnStream then
    Result := FStream.Read( Buffer, Count )
  else
  begin
    Synchronize;
    if Count < 0 then
      Count := 0
    else if Count > Size - Position then
      Count := Size - Position;
    Result := FStream.Read( Buffer, Count );
    FLastPosition := FStream.Position;
  end
end;

function TPersistentTree.Seek(const Offset: Int64;
  Origin: TSeekOrigin): Int64;
begin
  if FOwnStream then
    Result := FStream.Seek( Offset, Origin )
  else
  begin
    Synchronize;
    case Origin of
      soBeginning: Result := FDataBegin + Offset;
      soCurrent: Result := FStream.Position + Offset;
      soEnd: Result := FDataBegin + Size - Offset;
    else
      Result := 0;
    end;
    if Result > -1 then
      if Result <= FDataBegin + Size then
        Result := FStream.Seek( Result, soBeginning ) - FDataBegin
      else
      begin
        RecreateStream( Size );
        Result := FStream.Seek( Result, soBeginning );
      end;
    FLastPosition := FStream.Position;
  end;
end;

procedure TPersistentTree.SetSize(const NewSize: Int64);
begin
  if FOwnStream then
    FStream.Size := NewSize
  else begin
    if NewSize <= 0 then
      RecreateStream( 0 )
    else if NewSize > Size then
      RecreateStream( Size )
    else
    begin
      FDataLength := NewSize;
      Seek( 0, soEnd );
    end;
    FLastPosition := FStream.Position;
  end;
end;

procedure TPersistentTree.Synchronize;
begin
  if not FOwnStream and ( ( FStream.Position < FDataBegin ) or ( FStream.Position - FDataBegin > FDataLength ) ) then
    FStream.Seek( FLastPosition, soBeginning );
end;

procedure TPersistentTree.Load( Stream: IStream);
var
  I: LongInt;
begin
  ClearData;

  FStream := Stream;
  FOwnStream := False;

  Stream.Read( FDataLength, SizeOf( FDataLength ) );
  FDataBegin := FStream.Position;
  FLastPosition := FDataBegin;

  Stream.Seek( FDataLength, soCurrent );

  Stream.Read( I, SizeOf( I ) );
  for I := I - 1 downto 0 do
    Add.Load( FStream );

  //Seek( 0, soBeginning ); it isnt needed since synchonize will do it anyway
  Loaded;
  FStream.Seek( FDataBegin + FDataLength + SizeOf( I ), soBeginning );
end;

function TPersistentTree.Extract( Item: TPersistentTree): TPersistentTree;
begin
  Result := FList.Extract( Item );
  if Assigned( Result ) then begin
    Result.FParent := nil;
    Result.FOwner := nil;
    Result.RecreateStream( Size, True );
  end;
end;


function TPersistentTree.GetSize: Int64;
begin
  if FOwnStream then
    Result := FStream.Size
  else
    Result := FDataLength;
end;

procedure TPersistentTree.WriteString(const Data: string);
var
  I: LongWord;
begin
  I := Length( Data );
  Write( I, SizeOf( I ) );
  Write( Pointer( Data )^, I );
end;

function TPersistentTree.ReadString: string;
var
  I: LongWord;
begin
  Read( I, SizeOf( I ) );
  SetLength( Result, I );
  Read( Pointer( Result )^, I );
end;

procedure TPersistentTree.SetSize(NewSize: Integer);
begin
  SetSize( Int64( NewSize ) );
end;

procedure TPersistentTree.RecreateStream( const Pos: Int64; const Deep: Boolean );
var
  FS: TStream;
  I: Integer;
begin
  if not FOwnStream then
  begin
    FS := GetStream;
    if Pos > 0 then
    begin
      Seek( 0, soBeginning );
      FS.CopyFrom( Self, Pos );
    end;
    FStream := TStreamAdapter.Create( FS );
    FOwnStream := True;
  end;
  if Deep then
    for I := 0 to FList.Count - 1 do
      Self[I].RecreateStream( Self[I].Size, True );
end;

procedure TPersistentTree.ClearData;
begin
  FStream := nil;
  if FOwnStream then
    DeleteFile( FDataFilename );
  Clear;
end;

function TPersistentTree.GetStream: TStream;
begin
  FDataFilename := GetTempFile;
  Result := TFileStream.Create( FDataFilename, fmCreate or fmShareDenyWrite );
end;

function TPersistentTree.Add: TPersistentTree;
begin
  Result := TPersistentTreeClass( FBaseClass ).Create;
  Add( Result );
end;

function TPersistentTree.Add( Item: TPersistentTree): Integer;
begin
  if Import( Item ) then
    Result := FList.Add( Item )
  else
    Result := FList.IndexOf( Item );
end;

procedure TPersistentTree.Delete(const Index: Integer);
begin
  TPersistentTree( FList[Index] ).Free;
  FList.Delete( Index );
end;

procedure TPersistentTree.Insert(const Index: Integer; Item: TPersistentTree);
begin
  if Import( Item ) then
    FList.Insert( Index, Item )
  else
    FList.Move( FList.IndexOf( Item ), Index );
end;

procedure TPersistentTree.Move(const CurIndex, NewIndex: Integer);
begin
  FList.Move( CurIndex, NewIndex );
end;

function TPersistentTree.Truncate: Int64;
begin
  Result := Position;
  Size := Result;
end;

function TPersistentTree.Import(Item: TPersistentTree): Boolean;
begin
  Result := not Assigned( Item.FParent ) or ( ( Item.FParent <> Self ) and Assigned( Item.FParent.Extract( Item ) ) );
  if Result then
  begin
    Item.FParent := Self;
    if FOwner <> nil then
      Item.FOwner := FOwner
    else
      Item.FOwner := Self;
  end;
end;

procedure TPersistentTree.Saving;
begin
//override to provide extra save features
end;

procedure TPersistentTree.Loaded;
begin
//override to provide extra load features
end;

end.

    
        this code analyses the first 5*1024 bytes of data from a file and tells which break type it uses, or if it's a binary file...

//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com

type
  TBreakType = ( btNone, btWin, btMac, btLin, btBin );

---------

function GetBreakType( const Filename: string; const MaxDataToRead: Cardinal = 5*1024 ): TBreakType;
var
  FS: TFileStream;
  Buffer, BufferStart, BufferEnd: PChar;
begin
  Result := btNone;
  if not FileExists( Filename ) then
    raise Exception.Create( 'GetBreakType: nome de arquivo inválido' );
  try
    FS := TFileStream.Create( Filename, fmOpenRead );
    GetMem( Buffer, MaxDataToRead+1 );
    BufferEnd := ( Buffer + FS.Read( Buffer^, MaxDataToRead ) );
    BufferStart := Buffer;
    BufferEnd^ := #0;
  except
    raise Exception.Create( 'GetBreakType: erro alocando memória.' );
  end;
  try
    while Buffer^ <> #0 do begin
      if Result = btNone then
        if Buffer^ = ASCII_CR then begin
          if (Buffer+1)^ = ASCII_LF then begin
            Result := btWin;
            Inc( Buffer );
          end
          else
            Result := btMac;
        end
        else if Buffer^ = ASCII_LF then
          Result := btLin;
      Inc( Buffer );
    end;
    if Buffer <> BufferEnd then
      Result := btBin;
  finally
    FreeMem( BufferStart, MaxDataToRead+1 );
    FS.Free;
  end;
end;
    
        <a href="http://www.jsfromhell.com/number/fmt-money">
Formats strings/numbers into "money format" without loops :)

Defaults:
float ploint cutoff = 2 decimal places
decimal separator = ','
thousands separator = '.'

[UPDATED CODE AND HELP CAN BE FOUND HERE]
</a>


//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/number/fmt-money [v1.1]

Number.prototype.formatMoney = function(c, d, t){
	var n = this, c = isNaN(c = Math.abs(c)) ? 2 : c, d = d == undefined ? "," : d, t = t == undefined ? "." : t, s = n < 0 ? "-" : "", i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0;
	return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : "");
};


Usage

(123456789.12345).formatMoney(2, '.', ',');
    
        <a href="http://jsfromhell.com/forms/enter-as-tab">
Tabulation on fields through the enter key, i didnt allowed it to work on <select> and <textarea> since the enter is required to use this fields, so find a nice place to use it :]

Usage: just add the code on the end of your page or call it on the onload event (worse solution) and "the enter tabulation" will be added for all fields enclosed by the <form> tag.

[UPDATED CODE AND HELP CAN BE FOUND HERE]
</a>

@REQUIRES <a href="http://jsfromhell.com/geral/event-listener">Event-Listener</a>

//Requires http://jsfromhell.com/geral/event-listener

//+ Jonas Raoni Soares Silva
//@ http://jsfromhell.com/forms/enter-as-tab [v1.0]

enterAsTab = function(){
	for( var f, i = ( f = document.forms ).length; i; addEventListener( f[--i], "keypress", function( evt ){
		var el, l, k = evt.key == 13, e = evt.target;
		if( k && !/textarea|select/i.test( e.type ) && !evt.preventDefault() ){
			for( l = k = ( el = e.form.elements ).length; el[--k] != e; );
			while( !(e = el[ k = ++k * ( k < l ) ]).type || e.disabled );
			e.focus();
		}
	} ) );
};


Example:

<form action="">
	<p>
	<input type="text" />
	<br /><input type="text" />
	<br /> <input type="radio" name="aaa" /> opção 1
	<input type="radio" name="aaa" checked="checked" /> opção 2
	<br /><textarea rows="3" cols="15">aaaaaa pode dar enter q ele naum pula hahah</textarea>
	<br /><select><option>opção blablablabla</option><option>opção bleblebleble</option></select>
	<br /><input type="text" />
	</p>
</form>


<script type="text/javascript">
//<![CDATA[

enterAsTab();

//]]>
</script>
    
        pys60 1.1.3 provide some graphics capabilitis.
One of them is using icons in Listbox
(code taken from pys60 API reference)
icon1 = appuifw.Icon(u"z:\\system\\data\\avkon.mbm", 28, 29)
icon2 = appuifw.Icon(u"z:\\system\\data\\avkon.mbm ", 40, 41)
entries = [(u"Signal", icon1),
           (u"Battery", icon2)]
lb = appuifw.Listbox(entries, lbox_observe)
Listbox is one of the 3 types that can be assigned to app.body
(i.e. Text, Listbox, Canvas)
So, to make code above able to run, you must add
appuifw.app.body = lb
and the function lbox_observe that will handle the selection
need to be defined.    
        
appuifw.app.menu = [(u"item 1", item1),
  (u"Submenu 1", ((u"sub item 1", subitem1),
  (u"sub item 2", subitem2) ))
]
# item1, subitem1, subitem2 are callback
    
        In pys60 1.1.3 and later, you can specify different screen size
appuifw.app.screen='normal' #(a normal screen with title pane and softkeys)
appuifw.app.screen='large' #(only softkeys visible)
appuifw.app.screen='full' #(a full screen)
    
        Presuming the element you want the text from is element:

REXML::XPath.match(element, './/text()').inject { |str, text| str << text.to_s }

    
        Here is a really cheap way in Perl to append an XML fragment to an existing XML doc. It appends it just before the last closing tag. Note that there is no validation or well-formedness checking. Use at your own risk. That said, it can be handy if you know that your XML is going to be OK.

#!/usr/bin/perl

sub xappend(\$$) { ${$_[0]} =~ s/(<\/[^>]+>\s*)$/$_[1]$1/s }

$a = "<doc><item>foo</item></doc>";
xappend($a,"<item>bar</item>");

print $a; # prints <doc><item>foo</item><item>bar</item></doc>
    
        Taken (will some mod.) from 
http://discussion.forum.nokia.com/forum/showthread.php?s=&threadid=63464

pys60 (the full name is python for series 60) 1.1.3
provide both camera and graphics display.
So, it is trivial to take photo and preview.
from appuifw import *
import camera

canvas=Canvas()
app.body=canvas

running=1
def quit():
    global running
    running=0
app.exit_key_handler=quit

while running:
    image = camera.take_photo(size= (160,120))
    canvas.blit(image, target=(8, 12, 168, 132))  # center it
Here I use the small size (160x120) for preview.
When the real photo is taken I can use the full (defualt)
size of (640x480) on my 6600 phone.    
        I got 1000 word files. Each contains 1 main image and some decorations.
(This is actually a big book scanned into 1-file-per-page format)
I need to extract all the images. What do I do?

Python can do some automation using COM. (or something like that)
import pythoncom, win32com.client

app = win32com.client.gencache.EnsureDispatch("Word.Application")

doc = 'C:\\lang\\try\\bdham\\p1'
app.Documents.Open(doc + '.doc')
app.ActiveDocument.SaveAs(doc + '.html', FileFormat=win32com.client.constants.wdFormatHTML)
app.ActiveDocument.Close()
# now repeat with p2, p3, etc.
Actually, I should put it in a loop. But this non-loop version
is easier to read and remember.    
        Some of my friends who practise mindfulness meditation
use an alarm device that vibrate every 2 minutes and
he will become mindful then.
Danny O'Brien of Life Hacks fame asked me about this too.
So, here's a short example (without parameter setting GUI)
that does exactly this.

You need to have miso library install. Only Series 60 
2nd Ed FP2 device (Nokia 6630, Nokia 6680) can be used.
See vibrate(...) in miso documentation
http://pdis.hiit.fi/pdis/download/miso/miso-1.40-api.html
import appuifw, miso, e32

# run-and-break type of app
running = 1
def set_exit():
    global running
    running = 0
appuifw.app.exit_key_handler= set_exit

# main loop
while running:
    miso.vibrate(500, 100)  # vibrate for 500 millisec, at full speed
    e32.ao_sleep(10)   # vibrate every 10 seconds
I don't have a FP2 phone to test this. Though the code is
pretty straight forward, please report if there is a problem.    
        py_s60 1.1.3 was released a week ago.
I wonder why there's no more people playing with it.
So, I have created a simple game as an example.

I don't know what this game is called. It has 4x4 square box
with 15 pieces (1 piece missing). You need to move all the
pieces into sorting order.

Since the program is so simple, I decided to make the
moving of a piece smoother by moving it bit by bit.
Hope this doesn't make the code to hard to read.
from appuifw import *
from key_codes import *
import e32, random

# run and break-loop type of app
sleep = e32.ao_sleep
running = 1
def set_exit():
    global running
    running = 0
app.exit_key_handler= set_exit

# canvas and typical colors
c = Canvas()
app.body = c
red, green, blue, gray, white = 0xff0000, 0x00ff00, 0x0000ff, 0x777777, 0xffffff

# randomize number order for pieces
seq = range(1,17)
random.shuffle(seq)
b = [seq[0:4], seq[4:8], seq[8:12], seq[12:16]]

def piece(i, j, n):
    if n < 10:
        c.text( (12+20*i, 20+20*j), unicode(n))
    else:
        c.text( (7+20*i, 20+20*j), unicode(n))

def box(i, j, color, fill=None):
    c.rectangle( [5+20*i, 5+20*j, 25+20*i, 25+20*j], color, fill)

# draw board pieces
for k in range(16):
    j, i = divmod(k, 4)
    piece(i, j, b[j][i])

y, x = divmod(seq.index(16), 4)
box(x, y, white, white)
moving = 0     # cursor to lock if animating

# move cursor in dx, dy direction
def move(dx, dy):
    global x,y, moving
    if moving:
        return
    moving = 1
    if 0 <= x-dx < 4 and 0 <= y-dy < 4:
        b[y][x] = b[y-dy][x-dx]
        animate(x-dx, y-dy, dx, dy, b[y][x])
        x -= dx  # the hole move in opposite direction
        y -= dy
    moving = 0

# moving a piece for x,y to dx, dy direction
def animate(x, y, dx, dy, n):
    if n < 10:
        px = 12
    else:
        px = 7
    for i in range(5):
        c.text( (px+20*x + 4*i*dx, 20+20*y + 4*i*dy), unicode(n))
        sleep(0.05)
        c.text( (px+20*x + 4*i*dx, 20+20*y + 4*i*dy), unicode(n), white)
    c.text( [px+20*(x+dx), 20+20*(y+dy)], unicode(n) )

# bind arrow keys
c.bind(EKeyRightArrow,lambda:move(1, 0))
c.bind(EKeyLeftArrow,lambda:move(-1, 0))
c.bind(EKeyUpArrow,lambda:move(0, -1))
c.bind(EKeyDownArrow,lambda:move(0, 1))

# main loop, just wait
while running:
    sleep(0.1)    
        py_s60 1.1.3 has its drawing API very similar to PIL.
Porting from PIL to S60 is very easy. Here's an example
that I port a sparkline drawing function from 
Joe Gregorio's article at xml.com
# modified from  Joe Gregorio's article at
# http://www.xml.com/pub/a/2005/06/22/sparklines.html

from appuifw import *
import e32

lock = e32.Ao_lock()
c = Canvas()
app.body = c
draw = c._draw

red, green, blue, gray = 0xff0000, 0x00ff00, 0x0000ff, 0x777777

def plot_sparkline(results, step=2, height=20, \
    min_m=None, max_m=None, last_m=None, \
    min_color=blue, max_color=green, last_color=red):
    coords = zip(range(1,len(results)*step+1, step), \
       [height - 3  - y/(101.0/(height-4)) for y in results])
    draw.line(coords, gray)
    if min_m:
        min_pt = coords[results.index(min(results))]
        draw.rectangle([min_pt[0]-1, min_pt[1]-1, min_pt[0]+1, min_pt[1]+1], fill=min_color)
    if max_m:
        max_pt = coords[results.index(max(results))]
        draw.rectangle([max_pt[0]-1, max_pt[1]-1, max_pt[0]+1, max_pt[1]+1], fill=max_color)
    if last_m:
        end = coords[-1]
        draw.rectangle([end[0]-1, end[1]-1, end[0]+1, end[1]+1], fill=last_color)


results = [88,84,82,92,82,86,66,82,44,64,66,88,96,80,24,26, \
        14,0,0,26,8,6,6,24,52,66,36,6,10,14,30]
plot_sparkline(results, 3, 30, min_m=1, max_m=1, last_m=1)

app.exit_key_handler = lock.signal
lock.wait()
    
        This will take a phrase and truncate it at the word level
<?php

function trunc($phrase, $max_words)
{
   $phrase_array = explode(' ',$phrase);
   if(count($phrase_array) > $max_words && $max_words > 0)
      $phrase = implode(' ',array_slice($phrase_array, 0, $max_words)).'...'  
   return $phrase;
}
?>