First attempt at distutils support now that the locations have been updated
authorEd Page <eopage@byu.net>
Wed, 10 Aug 2011 02:26:48 +0000 (21:26 -0500)
committerEd Page <eopage@byu.net>
Wed, 10 Aug 2011 02:26:48 +0000 (21:26 -0500)
36 files changed:
DialCentral
Makefile
data/app/LICENSE [deleted file]
data/app/bell.flac [deleted file]
data/app/bell.wav [deleted file]
data/app/contacts.png [deleted file]
data/app/dialpad.png [deleted file]
data/app/history.png [deleted file]
data/app/messages.png [deleted file]
data/app/missed.png [deleted file]
data/app/placed.png [deleted file]
data/app/received.png [deleted file]
data/template.desktop
dialcentral/backends/gv_backend.py
dialcentral/backends/qt_backend.py
dialcentral/constants.py
dialcentral/data/LICENSE [new file with mode: 0644]
dialcentral/data/bell.flac [new file with mode: 0644]
dialcentral/data/bell.wav [new file with mode: 0644]
dialcentral/data/contacts.png [new file with mode: 0644]
dialcentral/data/dialpad.png [new file with mode: 0644]
dialcentral/data/history.png [new file with mode: 0644]
dialcentral/data/messages.png [new file with mode: 0644]
dialcentral/data/missed.png [new file with mode: 0644]
dialcentral/data/placed.png [new file with mode: 0644]
dialcentral/data/received.png [new file with mode: 0644]
dialcentral/dialcentral_qt.py
dialcentral/util/qml_utils.py [new file with mode: 0644]
dialcentral/util/qore_utils.py
dialcentral/util/qt_compat.py
dialcentral/util/qwrappers.py
setup.py [new file with mode: 0755]
support/builddeb.py [deleted file]
support/fake_py2deb.py [deleted file]
support/py2deb.py [deleted file]
support/scale.py [new file with mode: 0755]

index a20d4fe..f483f56 100755 (executable)
@@ -2,13 +2,7 @@
 # -*- coding: utf-8 -*-
 
 
-import sys
-
-
-sys.path.append("/opt/dialcentral/lib")
-
-
-import dialcentral_qt
+from dialcentral import dialcentral_qt
 
 
 if __name__ == "__main__":
index 9f128fa..24cb868 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,12 +1,19 @@
 PROJECT_NAME=dialcentral
-SOURCE_PATH=src
+PACKAGE_NAME=$(PROJECT_NAME)
+
+SOURCE_PATH=$(PACKAGE_NAME)
 SOURCE=$(shell find $(SOURCE_PATH) -iname "*.py")
-PROGRAM=$(SOURCE_PATH)/$(PROJECT_NAME).py
-DATA_PATH=data
-DATA_TYPES=*.ini *.map *.glade *.png
-DATA=$(foreach type, $(DATA_TYPES), $(shell find $(DATA_PATH) -iname "$(type)"))
+
+PROGRAM=DialCentral
+ICON_SIZES=26 32 48 80
+ICONS=$(foreach size, $(ICON_SIZES), data/icons/$(size)/$(PROJECT_NAME).png)
+PACKAGE_VARIANTS=fremantle harmattan ubuntu
+DESKTOP_FILES=$(foreach variant, $(PACKAGE_VARIANTS), data/$(variant)/$(PROJECT_NAME).desktop)
+SETUP_FILES=$(foreach variant, $(PACKAGE_VARIANTS), ./setup.$(variant).py)
+DIST_BASE_PATH=./dist
+DIST_PATHS=$(foreach variant, $(PACKAGE_VARIANTS), $(DIST_BASE_PATH)_$(variant)) $(DIST_BASE_PATH)_diablo
+
 OBJ=$(SOURCE:.py=.pyc)
-BUILD_PATH=./build
 TAG_FILE=~/.ctags/$(PROJECT_NAME).tags
 TODO_FILE=./TODO
 
@@ -21,12 +28,13 @@ PROFILE_VIEW=python -m pstats .profile
 TODO_FINDER=support/todo.py
 CTAGS=ctags-exuberant
 
+
 .PHONY: all run profile debug test build lint tags todo clean distclean
 
 all: test
 
 run: $(OBJ)
-       $(SOURCE_PATH)/$(PROJECT_NAME)_qt.py
+       $(PROGRAM)
 
 profile: $(OBJ)
        $(PROFILE_GEN) $(PROGRAM)
@@ -38,36 +46,27 @@ debug: $(OBJ)
 test: $(OBJ)
        $(UNIT_TEST)
 
-package: $(OBJ)
-       rm -Rf $(BUILD_PATH)
-
-       mkdir -p $(BUILD_PATH)/generic
-       cp $(SOURCE_PATH)/constants.py  $(BUILD_PATH)/generic
-       cp $(SOURCE_PATH)/$(PROJECT_NAME).py  $(BUILD_PATH)/generic
-       $(foreach file, $(DATA), cp $(file) $(BUILD_PATH)/generic/$(subst /,-,$(file)) ; )
-       $(foreach file, $(SOURCE), cp $(file) $(BUILD_PATH)/generic/$(subst /,-,$(file)) ; )
-       cp support/$(PROJECT_NAME).desktop $(BUILD_PATH)/generic
-       cp support/icons/hicolor/26x26/hildon/$(PROJECT_NAME).png $(BUILD_PATH)/generic/26x26-$(PROJECT_NAME).png
-       cp support/icons/hicolor/64x64/hildon/$(PROJECT_NAME).png $(BUILD_PATH)/generic/64x64-$(PROJECT_NAME).png
-       cp support/icons/hicolor/scalable/hildon/$(PROJECT_NAME).png $(BUILD_PATH)/generic/scale-$(PROJECT_NAME).png
-       cp support/builddeb.py $(BUILD_PATH)/generic
-       cp support/py2deb.py $(BUILD_PATH)/generic
-       cp support/fake_py2deb.py $(BUILD_PATH)/generic
-
-       mkdir -p $(BUILD_PATH)/diablo
-       cp -R $(BUILD_PATH)/generic/* $(BUILD_PATH)/diablo
-       cd $(BUILD_PATH)/diablo ; python builddeb.py diablo
-       mkdir -p $(BUILD_PATH)/fremantle
-       cp -R $(BUILD_PATH)/generic/* $(BUILD_PATH)/fremantle
-       cd $(BUILD_PATH)/fremantle ; python builddeb.py fremantle
-       mkdir -p $(BUILD_PATH)/debian
-       cp -R $(BUILD_PATH)/generic/* $(BUILD_PATH)/debian
-       cd $(BUILD_PATH)/debian ; python builddeb.py debian
+package: $(OBJ) $(ICONS) $(SETUP_FILES) $(DESKTOP_FILES)
+       rm -Rf $(DIST_BASE_PATH)_*/*
+       ./setup.fremantle.py sdist_diablo \
+               -d $(DIST_BASE_PATH)_diablo \
+               --install-purelib=/usr/lib/python2.5/site-packages
+       ./setup.fremantle.py sdist_fremantle \
+               -d $(DIST_BASE_PATH)_fremantle \
+               --install-purelib=/usr/lib/python2.5/site-packages
+       ./setup.harmattan.py sdist_harmattan \
+               -d $(DIST_BASE_PATH)_harmattan
+               --install-purelib=/usr/lib/python2.6/dist-packages
+       ./setup.ubuntu.py sdist_ubuntu \
+               -d $(DIST_BASE_PATH)_ubuntu
+       mkdir $(DIST_BASE_PATH)_ubuntu/build
+       cd $(DIST_BASE_PATH)_ubuntu/build ; tar -zxvf ../*.tar.gz
+       cd $(DIST_BASE_PATH)_ubuntu/build ; dpkg-buildpackage -tc -rfakeroot -us -uc
 
 upload:
-       dput fremantle-extras-builder $(BUILD_PATH)/fremantle/$(PROJECT_NAME)*.changes
-       dput diablo-extras-builder $(BUILD_PATH)/diablo/$(PROJECT_NAME)*.changes
-       cp $(BUILD_PATH)/debian/*.deb ./www/$(PROJECT_NAME).deb
+       dput diablo-extras-builder $(DIST_BASE_PATH)_diablo/$(PROJECT_NAME)*.changes
+       dput fremantle-extras-builder $(DIST_BASE_PATH)_fremantle/$(PROJECT_NAME)*.changes
+       cp $(DIST_BASE_PATH)_ubuntu/*.deb www/$(PROJECT_NAME).deb
 
 lint: $(OBJ)
        $(foreach file, $(SOURCE), $(LINT) $(file) ; )
@@ -78,18 +77,60 @@ todo: $(TODO_FILE)
 
 clean:
        rm -Rf $(OBJ)
-       rm -Rf $(BUILD_PATH)
        rm -Rf $(TODO_FILE)
+       rm -f $(ICONS) $(SETUP_FILES) $(DESKTOP_FILES)
+       rm -Rf $(DIST_PATHS)
 
-distclean:
-       rm -Rf $(OBJ)
-       rm -Rf $(BUILD_PATH)
-       rm -Rf $(TAG_FILE)
+distclean: clean
        find $(SOURCE_PATH) -name "*.*~" | xargs rm -f
        find $(SOURCE_PATH) -name "*.swp" | xargs rm -f
        find $(SOURCE_PATH) -name "*.bak" | xargs rm -f
        find $(SOURCE_PATH) -name ".*.swp" | xargs rm -f
 
+
+$(SETUP_FILES): VARIANT=$(word 2, $(subst ., ,$@))
+
+setup.fremantle.py: setup.py src/constants.py
+       cog.py -c \
+               -D DESKTOP_FILE_PATH=/usr/share/applications/hildon \
+               -D INPUT_DESKTOP_FILE=data/$(VARIANT)/$(PROJECT_NAME).desktop \
+               -D ICON_CATEGORY=hildon \
+               -D ICON_SIZES=26,32,48 \
+               -o $@ $<
+       chmod +x $@
+
+setup.harmattan.py: setup.py src/constants.py
+       cog.py -c \
+               -D DESKTOP_FILE_PATH=/usr/share/applications \
+               -D INPUT_DESKTOP_FILE=data/$(VARIANT)/$(PROJECT_NAME).desktop \
+               -D ICON_CATEGORY=hildon \
+               -D ICON_SIZES=32,80 \
+               -o $@ $<
+       chmod +x $@
+
+setup.ubuntu.py: setup.py src/constants.py
+       cog.py -c \
+               -D DESKTOP_FILE_PATH=/usr/share/applications \
+               -D INPUT_DESKTOP_FILE=data/$(VARIANT)/$(PROJECT_NAME).desktop \
+               -D ICON_CATEGORY=apps \
+               -D ICON_SIZES=32,48 \
+               -o $@ $<
+       chmod +x $@
+
+$(ICONS): SIZE=$(word 3, $(subst /, ,$@))
+$(ICONS): data/$(PROJECT_NAME).png support/scale.py
+       mkdir -p $(dir $@)
+       support/scale.py --input $< --output $@ --size $(SIZE)
+
+$(DESKTOP_FILES): VARIANT=$(word 2, $(subst /, ,$@))
+$(DESKTOP_FILES): data/template.desktop
+       mkdir -p $(dir $@)
+       cog.py -d \
+               -D VARIANT=$(VARIANT) \
+               -D PROGRAM=$(PROGRAM) \
+               -o $@ $<
+
+
 $(TAG_FILE): $(OBJ)
        mkdir -p $(dir $(TAG_FILE))
        $(CTAGS) -o $(TAG_FILE) $(SOURCE)
diff --git a/data/app/LICENSE b/data/app/LICENSE
deleted file mode 100644 (file)
index fb44a62..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-http://www.gentleface.com/free_icon_set.html
-The Creative Commons Attribution-NonCommercial -- FREE
-http://creativecommons.org/licenses/by-nc-nd/3.0/
-
-Sound:
-http://www.freesound.org/samplesViewSingle.php?id=2166
-http://creativecommons.org/licenses/sampling+/1.0/
-
-placed.png, received.png, placed.png
-Free for commercial use
-http://www.iconeden.com/icon/free/get/bright-free-stock-iconset
diff --git a/data/app/bell.flac b/data/app/bell.flac
deleted file mode 100644 (file)
index 419420e..0000000
Binary files a/data/app/bell.flac and /dev/null differ
diff --git a/data/app/bell.wav b/data/app/bell.wav
deleted file mode 100644 (file)
index 6b7fc1b..0000000
Binary files a/data/app/bell.wav and /dev/null differ
diff --git a/data/app/contacts.png b/data/app/contacts.png
deleted file mode 100644 (file)
index aa1a7ce..0000000
Binary files a/data/app/contacts.png and /dev/null differ
diff --git a/data/app/dialpad.png b/data/app/dialpad.png
deleted file mode 100644 (file)
index b54013b..0000000
Binary files a/data/app/dialpad.png and /dev/null differ
diff --git a/data/app/history.png b/data/app/history.png
deleted file mode 100644 (file)
index 887989a..0000000
Binary files a/data/app/history.png and /dev/null differ
diff --git a/data/app/messages.png b/data/app/messages.png
deleted file mode 100644 (file)
index e117918..0000000
Binary files a/data/app/messages.png and /dev/null differ
diff --git a/data/app/missed.png b/data/app/missed.png
deleted file mode 100644 (file)
index 34f71c4..0000000
Binary files a/data/app/missed.png and /dev/null differ
diff --git a/data/app/placed.png b/data/app/placed.png
deleted file mode 100644 (file)
index 329771d..0000000
Binary files a/data/app/placed.png and /dev/null differ
diff --git a/data/app/received.png b/data/app/received.png
deleted file mode 100644 (file)
index 2b45263..0000000
Binary files a/data/app/received.png and /dev/null differ
index 3b446d7..8a72f1e 100644 (file)
@@ -1,8 +1,22 @@
 [Desktop Entry]
-Encoding=UTF-8
-Version=1.0
-Type=Application
 Name=DialCentral
-Exec=/usr/bin/run-standalone.sh /opt/dialcentral/bin/dialcentral.py
+GenericName=Google Voice Client
+Comment=Google Voice Client
+#[[[cog
+#      if VARIANT == "fremantle":
+#              cog.outl("Exec=/usr/bin/run-standalone.sh /usr/local/bin/%s" % PROGRAM)
+#      elif VARIANT == "harmattan":
+#              cog.outl("Exec=/usr/bin/invoker --single-instance --type=e /usr/local/bin/%s" % PROGRAM)
+#      elif VARIANT == "ubuntu":
+#              cog.outl("Exec=/usr/local/bin/%s" % PROGRAM)
+#      else:
+#              raise RuntimeError("Unsupported desktop file flavor %r" % PROGRAM)
+#]]]
+Exec=/usr/local/bin/DialCentral
+#[[[end]]]
 Icon=dialcentral
 Categories=Network;InstantMessaging;Qt;
+Type=Application
+Encoding=UTF-8
+Version=1.0
+X-Osso-Type=application/x-executable
index 17bbc90..3281591 100644 (file)
@@ -32,7 +32,7 @@ import logging
 
 from gvoice import gvoice
 
-from util import io as io_utils
+from dialcentral.util import io as io_utils
 
 
 _moduleLogger = logging.getLogger(__name__)
index 88e52fa..da088e5 100644 (file)
@@ -5,7 +5,7 @@ from __future__ import division
 
 import logging
 
-import util.qt_compat as qt_compat
+from dialcentral.util import qt_compat
 if qt_compat.USES_PYSIDE:
        try:
                import QtMobility.Contacts as _QtContacts
index b9d3c79..56ebddc 100644 (file)
@@ -10,4 +10,5 @@ _user_settings_ = "%s/settings.ini" % _data_path_
 _custom_notifier_settings_ = "%s/notifier.ini" % _data_path_
 _user_logpath_ = "%s/%s.log" % (_data_path_, __app_name__)
 _notifier_logpath_ = "%s/notifier.log" % _data_path_
+
 IS_MAEMO = True
diff --git a/dialcentral/data/LICENSE b/dialcentral/data/LICENSE
new file mode 100644 (file)
index 0000000..fb44a62
--- /dev/null
@@ -0,0 +1,11 @@
+http://www.gentleface.com/free_icon_set.html
+The Creative Commons Attribution-NonCommercial -- FREE
+http://creativecommons.org/licenses/by-nc-nd/3.0/
+
+Sound:
+http://www.freesound.org/samplesViewSingle.php?id=2166
+http://creativecommons.org/licenses/sampling+/1.0/
+
+placed.png, received.png, placed.png
+Free for commercial use
+http://www.iconeden.com/icon/free/get/bright-free-stock-iconset
diff --git a/dialcentral/data/bell.flac b/dialcentral/data/bell.flac
new file mode 100644 (file)
index 0000000..419420e
Binary files /dev/null and b/dialcentral/data/bell.flac differ
diff --git a/dialcentral/data/bell.wav b/dialcentral/data/bell.wav
new file mode 100644 (file)
index 0000000..6b7fc1b
Binary files /dev/null and b/dialcentral/data/bell.wav differ
diff --git a/dialcentral/data/contacts.png b/dialcentral/data/contacts.png
new file mode 100644 (file)
index 0000000..aa1a7ce
Binary files /dev/null and b/dialcentral/data/contacts.png differ
diff --git a/dialcentral/data/dialpad.png b/dialcentral/data/dialpad.png
new file mode 100644 (file)
index 0000000..b54013b
Binary files /dev/null and b/dialcentral/data/dialpad.png differ
diff --git a/dialcentral/data/history.png b/dialcentral/data/history.png
new file mode 100644 (file)
index 0000000..887989a
Binary files /dev/null and b/dialcentral/data/history.png differ
diff --git a/dialcentral/data/messages.png b/dialcentral/data/messages.png
new file mode 100644 (file)
index 0000000..e117918
Binary files /dev/null and b/dialcentral/data/messages.png differ
diff --git a/dialcentral/data/missed.png b/dialcentral/data/missed.png
new file mode 100644 (file)
index 0000000..34f71c4
Binary files /dev/null and b/dialcentral/data/missed.png differ
diff --git a/dialcentral/data/placed.png b/dialcentral/data/placed.png
new file mode 100644 (file)
index 0000000..329771d
Binary files /dev/null and b/dialcentral/data/placed.png differ
diff --git a/dialcentral/data/received.png b/dialcentral/data/received.png
new file mode 100644 (file)
index 0000000..2b45263
Binary files /dev/null and b/dialcentral/data/received.png differ
index a464ad6..3dcf46f 100755 (executable)
@@ -30,8 +30,7 @@ _moduleLogger = logging.getLogger(__name__)
 class Dialcentral(qwrappers.ApplicationWrapper):
 
        _DATA_PATHS = [
-               os.path.join(os.path.dirname(__file__), "../share"),
-               os.path.join(os.path.dirname(__file__), "../data"),
+               os.path.join(os.path.dirname(__file__), "data"),
        ]
 
        def __init__(self, app):
diff --git a/dialcentral/util/qml_utils.py b/dialcentral/util/qml_utils.py
new file mode 100644 (file)
index 0000000..fbbca93
--- /dev/null
@@ -0,0 +1,54 @@
+#!/usr/bin/env python
+
+"""
+QML Tips:
+       Large images:
+               QML asynchronous = true; cache = false; [1]
+       Insert properties at top of element declarations [1]
+       Non-visible items: set opacity to 0 [2]
+       Use Loader [1]
+       Keep QML files small [1]
+
+[1] http://sf2011.meego.com/program/sessions/performance-tips-and-tricks-qtqml-applications-0
+[2] http://doc.qt.nokia.com/4.7/qdeclarativeperformance.html
+"""
+
+from __future__ import with_statement
+from __future__ import division
+
+import logging
+
+import util.qt_compat as qt_compat
+QtCore = qt_compat.QtCore
+QtGui = qt_compat.import_module("QtGui")
+QtDeclarative = qt_compat.import_module("QtDeclarative")
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+class DeclarativeView(QtDeclarative.QDeclarativeView):
+
+       def __init__(self):
+               QtDeclarative.QDeclarativeView.__init__(self)
+
+       closing = qt_compat.Signal()
+
+       def closeEvent(self, event):
+               self.closing.emit()
+               event.ignore()
+
+
+def disable_default_window_painting(view):
+       """
+       See http://doc.qt.nokia.com/4.7-snapshot/qdeclarativeperformance.html
+       """
+       view.setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
+       view.setAttribute(QtCore.Qt.WA_NoSystemBackground)
+       view.viewport().setAttribute(QtCore.Qt.WA_OpaquePaintEvent)
+       view.viewport().setAttribute(QtCore.Qt.WA_NoSystemBackground)
+
+
+if __name__ == "__main__":
+       pass
+
index 153558d..9e85c58 100644 (file)
@@ -1,3 +1,6 @@
+#!/usr/bin/env python
+
+import contextlib
 import logging
 
 import qt_compat
@@ -97,3 +100,323 @@ class FutureThread(QtCore.QObject):
                        callback(result)
                except Exception:
                        _moduleLogger.exception("Callback errored")
+
+
+def create_single_column_list_model(columnName, **kwargs):
+       """
+       >>> class Single(object): pass
+       >>> SingleListModel = create_single_column_list_model("s")
+       >>> slm = SingleListModel([Single(), Single(), Single()])
+       """
+
+       class SingleColumnListModel(QtCore.QAbstractListModel):
+
+               def __init__(self, l = None):
+                       QtCore.QAbstractListModel.__init__(self)
+                       self._list = l if l is not None else []
+                       self.setRoleNames({0: columnName})
+
+               def __len__(self):
+                       return len(self._list)
+
+               def __getitem__(self, key):
+                       return self._list[key]
+
+               def __setitem__(self, key, value):
+                       with scoped_model_reset(self):
+                               self._list[key] = value
+
+               def __delitem__(self, key):
+                       with scoped_model_reset(self):
+                               del self._list[key]
+
+               def __iter__(self):
+                       return iter(self._list)
+
+               def __repr__(self):
+                       return '<%s (%s)>' % (
+                               self.__class__.__name__,
+                               columnName,
+                       )
+
+               def rowCount(self, parent=QtCore.QModelIndex()):
+                       return len(self._list)
+
+               def data(self, index, role):
+                       if index.isValid() and role == 0:
+                               return self._list[index.row()]
+                       return None
+
+       if "name" in kwargs:
+               SingleColumnListModel.__name__ = kwargs["name"]
+
+       return SingleColumnListModel
+
+
+def create_tupled_list_model(*columnNames, **kwargs):
+       """
+       >>> class Column0(object): pass
+       >>> class Column1(object): pass
+       >>> class Column2(object): pass
+       >>> MultiColumnedListModel = create_tupled_list_model("c0", "c1", "c2")
+       >>> mclm = MultiColumnedListModel([(Column0(), Column1(), Column2())])
+       """
+
+       class TupledListModel(QtCore.QAbstractListModel):
+
+               def __init__(self, l = None):
+                       QtCore.QAbstractListModel.__init__(self)
+                       self._list = l if l is not None else []
+                       self.setRoleNames(dict(enumerate(columnNames)))
+
+               def __len__(self):
+                       return len(self._list)
+
+               def __getitem__(self, key):
+                       return self._list[key]
+
+               def __setitem__(self, key, value):
+                       with scoped_model_reset(self):
+                               self._list[key] = value
+
+               def __delitem__(self, key):
+                       with scoped_model_reset(self):
+                               del self._list[key]
+
+               def __iter__(self):
+                       return iter(self._list)
+
+               def __repr__(self):
+                       return '<%s (%s)>' % (
+                               self.__class__.__name__,
+                               ', '.join(columnNames),
+                       )
+
+               def rowCount(self, parent=QtCore.QModelIndex()):
+                       return len(self._list)
+
+               def data(self, index, role):
+                       if index.isValid() and 0 <= role and role < len(columnNames):
+                               return self._list[index.row()][role]
+                       return None
+
+       if "name" in kwargs:
+               TupledListModel.__name__ = kwargs["name"]
+
+       return TupledListModel
+
+
+class FileSystemModel(QtCore.QAbstractListModel):
+       """
+       Wrapper around QtGui.QFileSystemModel
+       """
+
+       FILEINFOS = [
+               "fileName",
+               "isDir",
+               "filePath",
+               "completeSuffix",
+               "baseName",
+       ]
+
+       EXTINFOS = [
+               "type",
+       ]
+
+       ALLINFOS = FILEINFOS + EXTINFOS
+
+       def __init__(self, model, path):
+               QtCore.QAbstractListModel.__init__(self)
+               self._path = path
+
+               self._model = model
+               self._rootIndex = self._model.index(self._path)
+
+               self._child = None
+               self.setRoleNames(dict(enumerate(self.ALLINFOS)))
+               self._model.directoryLoaded.connect(self._on_directory_loaded)
+
+       childChanged = qt_compat.Signal(QtCore.QObject)
+
+       def _child(self):
+               assert self._child is not None
+               return self._child
+
+       child = qt_compat.Property(QtCore.QObject, _child, notify=childChanged)
+
+       backendChanged = qt_compat.Signal()
+
+       def _parent(self):
+               finfo = self._model.fileInfo(self._rootIndex)
+               return finfo.fileName()
+
+       parent = qt_compat.Property(str, _parent, notify=backendChanged)
+
+       @qt_compat.Slot(str)
+       def browse_to(self, path):
+               if self._child is None:
+                       self._child = FileSystemModel(self._model, path)
+               else:
+                       self._child.switch_to(path)
+               self.childChanged.emit()
+               return self._child
+
+       @qt_compat.Slot(str)
+       def switch_to(self, path):
+               with scoped_model_reset(self):
+                       self._path = path
+                       self._rootIndex = self._model.index(self._path)
+               self.backendChanged.emit()
+
+       def __len__(self):
+               return self._model.rowCount(self._rootIndex)
+
+       def __getitem__(self, key):
+               return self._model.index(key, 0, self._rootIndex)
+
+       def __iter__(self):
+               return (self[i] for i in xrange(len(self)))
+
+       def rowCount(self, parent=QtCore.QModelIndex()):
+               return len(self)
+
+       def data(self, index, role):
+               if index.isValid() and 0 <= role and role < len(self.ALLINFOS):
+                       internalIndex = self._translate_index(index)
+                       info = self._model.fileInfo(internalIndex)
+                       if role < len(self.FILEINFOS):
+                               field = self.FILEINFOS[role]
+                               value = getattr(info, field)()
+                       else:
+                               role -= len(self.FILEINFOS)
+                               field = self.EXTINFOS[role]
+                               if field == "type":
+                                       return self._model.type(internalIndex)
+                               else:
+                                       raise NotImplementedError("Out of range that was already checked")
+                       return value
+               return None
+
+       def _on_directory_loaded(self, path):
+               if self._path == path:
+                       self.backendChanged.emit()
+                       self.reset()
+
+       def _translate_index(self, externalIndex):
+               internalIndex = self._model.index(externalIndex.row(), 0, self._rootIndex)
+               return internalIndex
+
+
+@contextlib.contextmanager
+def scoped_model_reset(model):
+       model.beginResetModel()
+       try:
+               yield
+       finally:
+               model.endResetModel()
+
+
+def create_qobject(*classDef, **kwargs):
+       """
+       >>> Car = create_qobject(
+       ...     ('model', str),
+       ...     ('brand', str),
+       ...     ('year', int),
+       ...     ('inStock', bool),
+       ...     name='Car'
+       ... )
+       >>> print Car
+       <class '__main__.AutoQObject'>
+       >>>  
+       >>> c = Car(model='Fiesta', brand='Ford', year=1337)
+       >>> print c.model, c.brand, c.year, c.inStock
+       Fiesta Ford 1337 False
+       >>> print c
+       <Car (model='Fiesta', brand='Ford', year=1337, inStock=False)>
+       >>>  
+       >>> c.inStock = True
+       >>>  
+       >>> print c.model, c.brand, c.year, c.inStock
+       Fiesta Ford 1337 True
+       >>> print c
+       <Car (model='Fiesta', brand='Ford', year=1337, inStock=True)>
+       """
+
+       class AutoQObject(QtCore.QObject):
+
+               def __init__(self, **initKwargs):
+                       QtCore.QObject.__init__(self)
+                       for key, val in classDef:
+                               setattr(self, '_'+key, initKwargs.get(key, val()))
+
+               def __repr__(self):
+                       values = (
+                               '%s=%r' % (key, getattr(self, '_'+key))
+                               for key, value in classDef
+                       )
+                       return '<%s (%s)>' % (
+                               kwargs.get('name', self.__class__.__name__),
+                               ', '.join(values),
+                       )
+
+               for key, value in classDef:
+                       nfy = locals()['_nfy_'+key] = qt_compat.Signal()
+
+                       def _get(key):
+                               def f(self):
+                                       return self.__dict__['_'+key]
+                               return f
+
+                       def _set(key):
+                               def f(self, value):
+                                       setattr(self, '_'+key, value)
+                                       getattr(self, '_nfy_'+key).emit()
+                               return f
+
+                       setter = locals()['_set_'+key] = _set(key)
+                       getter = locals()['_get_'+key] = _get(key)
+
+                       locals()[key] = qt_compat.Property(value, getter, setter, notify=nfy)
+               del nfy, _get, _set, getter, setter
+
+       return AutoQObject
+
+
+class QObjectProxy(object):
+       """
+       Proxy for accessing properties and slots as attributes
+
+       This class acts as a proxy for the object for which it is
+       created, and makes property access more Pythonic while
+       still allowing access to slots (as member functions).
+
+       Attribute names starting with '_' are not proxied.
+       """
+
+       def __init__(self, rootObject):
+               self._rootObject = rootObject
+               m = self._rootObject.metaObject()
+               self._properties = [
+                       m.property(i).name()
+                       for i in xrange(m.propertyCount())
+               ]
+
+       def __getattr__(self, key):
+               value = self._rootObject.property(key)
+
+               # No such property, so assume we call a slot
+               if value is None and key not in self._properties:
+                       return getattr(self._rootObject, key)
+
+               return value
+
+       def __setattr__(self, key, value):
+               if key.startswith('_'):
+                       object.__setattr__(self, key, value)
+               else:
+                       self._rootObject.setProperty(key, value)
+
+
+if __name__ == "__main__":
+       import doctest
+       print doctest.testmod()
index 2ab7fa4..409c00d 100644 (file)
@@ -3,12 +3,15 @@
 from __future__ import with_statement
 from __future__ import division
 
-#try:
-#      import PySide.QtCore as _QtCore
-#      QtCore = _QtCore
-#      USES_PYSIDE = True
-#except ImportError:
-if True:
+_TRY_PYSIDE = False
+
+try:
+       if not _TRY_PYSIDE:
+               raise ImportError()
+       import PySide.QtCore as _QtCore
+       QtCore = _QtCore
+       USES_PYSIDE = True
+except ImportError:
        import sip
        sip.setapi('QString', 2)
        sip.setapi('QVariant', 2)
index 2c50c8a..09270cd 100644 (file)
@@ -9,8 +9,8 @@ import qt_compat
 QtCore = qt_compat.QtCore
 QtGui = qt_compat.import_module("QtGui")
 
-from util import qui_utils
-from util import misc as misc_utils
+import qui_utils
+import misc as misc_utils
 
 
 _moduleLogger = logging.getLogger(__name__)
diff --git a/setup.py b/setup.py
new file mode 100755 (executable)
index 0000000..8eb7735
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,167 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import sys
+reload(sys).setdefaultencoding("UTF-8")
+import os
+
+try:
+       from sdist_maemo import sdist_maemo as _sdist_maemo
+       sdist_maemo = _sdist_maemo
+except ImportError:
+       sdist_maemo = None
+       print 'sdist_maemo command not available'
+
+from distutils.core import setup
+
+
+#[[[cog
+#      import cog
+#      from dialcentral import constants
+#      cog.outl('APP_NAME="%s"' % constants.__app_name__)
+#      cog.outl('PRETTY_APP_NAME="%s"' % constants.__pretty_app_name__)
+#      cog.outl('VERSION="%s"' % constants.__version__)
+#      cog.outl('BUILD="%s"' % constants.__build__)
+#      cog.outl('DESKTOP_FILE_PATH="%s"' % DESKTOP_FILE_PATH)
+#      cog.outl('INPUT_DESKTOP_FILE="%s"' % INPUT_DESKTOP_FILE)
+#      cog.outl('ICON_CATEGORY="%s"' % ICON_CATEGORY)
+#      cog.outl('ICON_SIZES=[%s]' % ICON_SIZES)
+#]]]
+APP_NAME="dialcentral"
+PRETTY_APP_NAME="DialCentral"
+VERSION="1.3.6"
+BUILD="0"
+DESKTOP_FILE_PATH="/usr/share/applications"
+INPUT_DESKTOP_FILE="data/ubuntu/dialcentral.desktop"
+ICON_CATEGORY="apps"
+ICON_SIZES=[32,48]
+#[[[end]]] (checksum: 6b362845c8509854ec12f330afd9c7b7)
+
+CHANGES = """Switching from py2deb.py to sdist_maemo
+""".strip()
+BUGTRACKER_URL = "https://bugs.maemo.org/enter_bug.cgi?product=Dialcentral"
+
+
+def is_package(path):
+       return (
+               os.path.isdir(path) and
+               os.path.isfile(os.path.join(path, '__init__.py'))
+       )
+
+
+def find_packages(path, base="", includeRoot=False):
+       """ Find all packages in path """
+       if includeRoot:
+               assert not base, "Base not supported with includeRoot: %r" % base
+               rootPath, module_name = os.path.split(path)
+               yield module_name
+               base = module_name
+       for item in os.listdir(path):
+               dir = os.path.join(path, item)
+               if is_package( dir ):
+                       if base:
+                               module_name = "%(base)s.%(item)s" % vars()
+                       else:
+                               module_name = item
+                       yield module_name
+                       for mname in find_packages(dir, module_name):
+                               yield mname
+
+
+setup(
+       name=APP_NAME,
+       version=VERSION,
+       description="Touch screen enhanced interface to the GoogleVoice phone service",
+       long_description="Touch screen enhanced interface to the GoogleVoice phone service",
+       author="Ed Page",
+       author_email="eopage@byu.net",
+       maintainer="Ed Page",
+       maintainer_email="eopage@byu.net",
+       url="http://wiki.maemo.org/DialCentral",
+       license="GNU LGPLv2.1",
+       scripts=[
+               "DialCentral",
+       ],
+       packages=list(find_packages(APP_NAME, includeRoot=True)),
+       package_data={
+               "dialcentral": ["data/*.wav", "data/*.png"],
+       },
+       data_files=[
+               (DESKTOP_FILE_PATH, [INPUT_DESKTOP_FILE]),
+       ] +
+       [
+               (
+                       "/usr/share/icons/hicolor/%sx%s/%s" % (size, size, ICON_CATEGORY),
+                       ["data/icons/%s/%s.png" % (size, APP_NAME)]
+               )
+               for size in ICON_SIZES
+       ],
+       requires=[
+               "PySide",
+               "simplejson",
+               "xml",
+       ],
+       cmdclass={
+               'sdist_ubuntu': sdist_maemo,
+               'sdist_diablo': sdist_maemo,
+               'sdist_fremantle': sdist_maemo,
+               'sdist_harmattan': sdist_maemo,
+       },
+       options={
+               "sdist_ubuntu": {
+                       "debian_package": APP_NAME,
+                       "section": "comm",
+                       "copyright": "lgpl",
+                       "changelog": CHANGES,
+                       "buildversion": str(BUILD),
+                       "depends": "python, python-pyside.qtcore, python-pyside.qtgui, python-simplejson, python-xml, python-dbus, python-gst0.10",
+                       "architecture": "any",
+               },
+               "sdist_diablo": {
+                       "debian_package": APP_NAME,
+                       "Maemo_Display_Name": PRETTY_APP_NAME,
+                       #"Maemo_Upgrade_Description": CHANGES,
+                       "Maemo_Bugtracker": BUGTRACKER_URL,
+                       "Maemo_Icon_26": "data/icons/26/%s.png" % APP_NAME,
+                       "section": "user/network",
+                       "copyright": "lgpl",
+                       "changelog": CHANGES,
+                       "buildversion": str(BUILD),
+                       "depends": "python2.5, python2.5-qt4-core, python2.5-qt4-gui, python-simplejson, python-xml | python2.5-xml, python-dbus | python2.5-dbus",
+                       "architecture": "any",
+               },
+               "sdist_fremantle": {
+                       "debian_package": APP_NAME,
+                       "Maemo_Display_Name": PRETTY_APP_NAME,
+                       #"Maemo_Upgrade_Description": CHANGES,
+                       "Maemo_Bugtracker": BUGTRACKER_URL,
+                       "Maemo_Icon_26": "data/icons/48/%s.png" % APP_NAME,
+                       "section": "user/network",
+                       "copyright": "lgpl",
+                       "changelog": CHANGES,
+                       "buildversion": str(BUILD),
+                       "depends": "python, python-pyside.qtcore, python-pyside.qtgui, python-pyside.qtmaemo5, python-simplejson, python-gst0.10, python-xml | python2.5-xml, python-dbus | python2.5-dbus",
+                       "architecture": "any",
+               },
+               "sdist_harmattan": {
+                       "debian_package": APP_NAME,
+                       "Maemo_Display_Name": PRETTY_APP_NAME,
+                       #"Maemo_Upgrade_Description": CHANGES,
+                       "Maemo_Bugtracker": BUGTRACKER_URL,
+                       "Maemo_Icon_26": "data/icons/48/%s.png" % APP_NAME,
+                       "MeeGo_Desktop_Entry_Filename": APP_NAME,
+                       #"MeeGo_Desktop_Entry": "",
+                       "section": "user/science",
+                       "copyright": "lgpl",
+                       "changelog": CHANGES,
+                       "buildversion": str(BUILD),
+                       "depends": "python, python-pyside.qtcore, python-pyside.qtgui, python-simplejson, python-xml",
+                       "architecture": "any",
+               },
+               "bdist_rpm": {
+                       "requires": "REPLACEME",
+                       "icon": "data/icons/48/%s.png" % APP_NAME,
+                       "group": "REPLACEME",
+               },
+       },
+)
diff --git a/support/builddeb.py b/support/builddeb.py
deleted file mode 100755 (executable)
index 7b904e4..0000000
+++ /dev/null
@@ -1,164 +0,0 @@
-#!/usr/bin/python2.5
-
-import os
-import sys
-
-try:
-       import py2deb
-except ImportError:
-       import fake_py2deb as py2deb
-
-import constants
-
-
-__appname__ = constants.__app_name__
-__description__ = """Touch screen enhanced interface to the GoogleVoice phone service
-Features:
-.
-* Dialpad for quick call
-.
-* Checking voicemails, texts, call history
-.
-* Sending texts
-.
-* Notification support for texts, voicemail, and/or missed calls
-.
-Homepage: http://gc-dialer.garage.maemo.org/
-"""
-__author__ = "Ed Page"
-__email__ = "eopage@byu.net"
-__version__ = constants.__version__
-__build__ = constants.__build__
-__changelog__ = """
-* Adding back in GTalk callback for those who can use it
-* Polishing button sizing and some wording
-* Fixed a bug with alert LED on Maemo 4.1
-""".strip()
-
-
-__postinstall__ = """#!/bin/sh -e
-
-gtk-update-icon-cache -f /usr/share/icons/hicolor
-""" % {"name": constants.__app_name__}
-
-__preremove__ = """#!/bin/sh -e
-
-python /opt/dialcentral/lib/alarm_handler.py -d || true
-"""
-
-
-def find_files(prefix, path):
-       for root, dirs, files in os.walk(path):
-               for file in files:
-                       if file.startswith(prefix+"-"):
-                               fileParts = file.split("-")
-                               unused, relPathParts, newName = fileParts[0], fileParts[1:-1], fileParts[-1]
-                               assert unused == prefix
-                               relPath = os.sep.join(relPathParts)
-                               yield relPath, file, newName
-
-
-def unflatten_files(files):
-       d = {}
-       for relPath, oldName, newName in files:
-               if relPath not in d:
-                       d[relPath] = []
-               d[relPath].append((oldName, newName))
-       return d
-
-
-def build_package(distribution):
-       try:
-               os.chdir(os.path.dirname(sys.argv[0]))
-       except:
-               pass
-
-       py2deb.Py2deb.SECTIONS = py2deb.SECTIONS_BY_POLICY[distribution]
-       p = py2deb.Py2deb(__appname__)
-       p.prettyName = constants.__pretty_app_name__
-       p.description = __description__
-       p.bugTracker = "https://bugs.maemo.org/enter_bug.cgi?product=Dialcentral"
-       p.author = __author__
-       p.mail = __email__
-       p.license = "lgpl"
-       p.depends = ", ".join([
-               "python2.6 | python2.5",
-               "python-xml | python2.5-xml",
-               "python-dbus | python2.5-dbus",
-               "python-simplejson",
-       ])
-       p.depends += {
-               "debian": ", python-qt4, python-gst0.10",
-               "diablo": ", python2.5-qt4-core, python2.5-qt4-gui",
-               "fremantle": ", python2.5-qt4-core, python2.5-qt4-gui, python2.5-qt4-maemo5, python-gst0.10",
-               #"fremantle": ", python-pyside.qtgui, python-pyside.qtcore, python-pyside.qtmaemo5, python-qtmobility.contacts",
-       }[distribution]
-       p.recommends = ", ".join([
-       ])
-       p.section = {
-               "debian": "comm",
-               "diablo": "user/network",
-               "fremantle": "user/network",
-       }[distribution]
-       p.arch = "all"
-       p.urgency = "low"
-       p.distribution = "diablo fremantle debian"
-       p.repository = "extras"
-       p.changelog = __changelog__
-       p.postinstall = __postinstall__
-       p.preremove = __preremove__
-       p.icon = {
-               "debian": "26x26-dialcentral.png",
-               "diablo": "26x26-dialcentral.png",
-               "fremantle": "64x64-dialcentral.png", # Fremantle natively uses 48x48
-       }[distribution]
-       p["/opt/%s/bin" % __appname__] = [ "%s.py" % __appname__ ]
-       for relPath, files in unflatten_files(find_files("src", ".")).iteritems():
-               fullPath = "/opt/%s/lib" % __appname__
-               if relPath:
-                       fullPath += os.sep+relPath
-               p[fullPath] = list(
-                       "|".join((oldName, newName))
-                       for (oldName, newName) in files
-               )
-       for relPath, files in unflatten_files(find_files("data", ".")).iteritems():
-               fullPath = "/opt/%s/share" % __appname__
-               if relPath:
-                       fullPath += os.sep+relPath
-               p[fullPath] = list(
-                       "|".join((oldName, newName))
-                       for (oldName, newName) in files
-               )
-       p["/usr/share/applications/hildon"] = ["dialcentral.desktop"]
-       p["/usr/share/icons/hicolor/26x26/hildon"] = ["26x26-dialcentral.png|dialcentral.png"]
-       p["/usr/share/icons/hicolor/64x64/hildon"] = ["64x64-dialcentral.png|dialcentral.png"]
-       p["/usr/share/icons/hicolor/scalable/hildon"] = ["scale-dialcentral.png|dialcentral.png"]
-
-       print p
-       if distribution == "debian":
-               print p.generate(
-                       version="%s-%s" % (__version__, __build__),
-                       changelog=__changelog__,
-                       build=True,
-                       tar=False,
-                       changes=False,
-                       dsc=False,
-               )
-       else:
-               print p.generate(
-                       version="%s-%s" % (__version__, __build__),
-                       changelog=__changelog__,
-                       build=False,
-                       tar=True,
-                       changes=True,
-                       dsc=True,
-               )
-       print "Building for %s finished" % distribution
-
-
-if __name__ == "__main__":
-       if len(sys.argv) == 1:
-               distribution = "fremantle"
-       else:
-               distribution = sys.argv[1]
-       build_package(distribution)
diff --git a/support/fake_py2deb.py b/support/fake_py2deb.py
deleted file mode 100644 (file)
index 5d6149d..0000000
+++ /dev/null
@@ -1,56 +0,0 @@
-import pprint
-
-
-class Py2deb(object):
-
-       def __init__(self, appName):
-               self._appName = appName
-               self.description = ""
-               self.author = ""
-               self.mail = ""
-               self.license = ""
-               self.depends = ""
-               self.section = ""
-               self.arch = ""
-               self.ugency = ""
-               self.distribution = ""
-               self.repository = ""
-               self.changelog = ""
-               self.postinstall = ""
-               self.icon = ""
-               self._install = {}
-
-       def generate(self, appVersion, appBuild, changelog, tar, dsc, changes, build, src):
-               return """
-Package: %s
-version: %s-%s
-Changes:
-%s
-
-Build Options:
-       Tar: %s
-       Dsc: %s
-       Changes: %s
-       Build: %s
-       Src: %s
-               """ % (
-                       self._appName, appVersion, appBuild, changelog, tar, dsc, changes, build, src
-               )
-
-       def __str__(self):
-               parts = []
-               parts.append("%s Package Settings:" % (self._appName, ))
-               for settingName in dir(self):
-                       if settingName.startswith("_"):
-                               continue
-                       parts.append("\t%s: %s" % (settingName, getattr(self, settingName)))
-
-               parts.append(pprint.pformat(self._install))
-
-               return "\n".join(parts)
-
-       def __getitem__(self, key):
-               return self._install[key]
-
-       def __setitem__(self, key, item):
-               self._install[key] = item
diff --git a/support/py2deb.py b/support/py2deb.py
deleted file mode 100644 (file)
index 0518480..0000000
+++ /dev/null
@@ -1,994 +0,0 @@
-#!/usr/bin/env python
-# -*- coding: utf-8 -*-
-##
-##    Copyright (C) 2009 manatlan manatlan[at]gmail(dot)com
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published
-## by the Free Software Foundation; version 2 only.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-## GNU General Public License for more details.
-##
-"""
-Known limitations :
-- don't sign package (-us -uc)
-- no distinctions between author and maintainer(packager)
-
-depends on :
-- dpkg-dev (dpkg-buildpackage)
-- alien
-- python
-- fakeroot
-
-changelog
- - ??? ?/??/20?? (By epage)
-    - PEP8
-    - added recommends
-    - fixed bug where it couldn't handle the contents of the pre/post scripts being specified
-    - Added customization based on the targeted policy for sections (Maemo support)
-    - Added maemo specific tarball, dsc, changes file generation support (including icon support)
-    - Added armel architecture
-    - Reduced the size of params being passed around by reducing the calls to locals()
-    - Added respository, distribution, priority
-    - Made setting control file a bit more flexible
- - 0.5 05/09/2009
-    - pre/post install/remove scripts enabled
-    - deb package install py2deb in dist-packages for py2.6
- - 0.4 14/10/2008
-    - use os.environ USERNAME or USER (debian way)
-    - install on py 2.(4,5,6) (*FIX* do better here)
-
-"""
-
-import os
-import hashlib
-import sys
-import shutil
-import time
-import string
-import StringIO
-import stat
-import commands
-import base64
-import tarfile
-from glob import glob
-from datetime import datetime
-import socket # gethostname()
-from subprocess import Popen, PIPE
-
-#~ __version__ = "0.4"
-__version__ = "0.5"
-__author__ = "manatlan"
-__mail__ = "manatlan@gmail.com"
-
-
-PERMS_URW_GRW_OR = stat.S_IRUSR | stat.S_IWUSR | \
-                   stat.S_IRGRP | stat.S_IWGRP | \
-                   stat.S_IROTH
-
-UID_ROOT = 0
-GID_ROOT = 0
-
-
-def run(cmds):
-    p = Popen(cmds, shell=False, stdout=PIPE, stderr=PIPE)
-    time.sleep(0.01)    # to avoid "IOError: [Errno 4] Interrupted system call"
-    out = string.join(p.stdout.readlines()).strip()
-    outerr = string.join(p.stderr.readlines()).strip()
-    return out
-
-
-def deb2rpm(file):
-    txt=run(['alien', '-r', file])
-    return txt.split(" generated")[0]
-
-
-def py2src(TEMP, name):
-    l=glob("%(TEMP)s/%(name)s*.tar.gz" % locals())
-    if len(l) != 1:
-        raise Py2debException("don't find source package tar.gz")
-
-    tar = os.path.basename(l[0])
-    shutil.move(l[0], tar)
-
-    return tar
-
-
-def md5sum(filename):
-    f = open(filename, "r")
-    try:
-        return hashlib.md5(f.read()).hexdigest()
-    finally:
-        f.close()
-
-
-class Py2changes(object):
-
-    def __init__(self, ChangedBy, description, changes, files, category, repository, **kwargs):
-      self.options = kwargs # TODO: Is order important?
-      self.description = description
-      self.changes=changes
-      self.files=files
-      self.category=category
-      self.repository=repository
-      self.ChangedBy=ChangedBy
-
-    def getContent(self):
-        content = ["%s: %s" % (k, v)
-                   for k,v in self.options.iteritems()]
-
-        if self.description:
-            description=self.description.replace("\n","\n ")
-            content.append('Description: ')
-            content.append(' %s' % description)
-        if self.changes:
-            changes=self.changes.replace("\n","\n ")
-            content.append('Changes: ')
-            content.append(' %s' % changes)
-        if self.ChangedBy:
-            content.append("Changed-By: %s" % self.ChangedBy)
-
-        content.append('Files:')
-
-        for onefile in self.files:
-            md5 = md5sum(onefile)
-            size = os.stat(onefile).st_size.__str__()
-            content.append(' ' + md5 + ' ' + size + ' ' + self.category +' '+self.repository+' '+os.path.basename(onefile))
-
-        return "\n".join(content) + "\n\n"
-
-
-def py2changes(params):
-    changescontent = Py2changes(
-        "%(author)s <%(mail)s>" % params,
-        "%(description)s" % params,
-        "%(changelog)s" % params,
-        (
-            "%(TEMP)s/%(name)s_%(version)s.tar.gz" % params,
-            "%(TEMP)s/%(name)s_%(version)s.dsc" % params,
-        ),
-        "%(section)s" % params,
-        "%(repository)s" % params,
-        Format='1.7',
-        Date=time.strftime("%a, %d %b %Y %H:%M:%S +0000", time.gmtime()),
-        Source="%(name)s" % params,
-        Architecture="%(arch)s" % params,
-        Version="%(version)s" % params,
-        Distribution="%(distribution)s" % params,
-        Urgency="%(urgency)s" % params,
-        Maintainer="%(author)s <%(mail)s>" % params
-    )
-    f = open("%(TEMP)s/%(name)s_%(version)s.changes" % params,"wb")
-    f.write(changescontent.getContent())
-    f.close()
-
-    fileHandle = open('/tmp/py2deb2.tmp', 'w')
-    fileHandle.write('#!/bin/sh\n')
-    fileHandle.write("cd " +os.getcwd()+ "\n")
-    # TODO Renable signing
-    # fileHandle.write("gpg --local-user %(mail)s --clearsign %(TEMP)s/%(name)s_%(version)s.changes\n" % params)
-    fileHandle.write("mv %(TEMP)s/%(name)s_%(version)s.changes.asc %(TEMP)s/%(name)s_%(version)s.changes\n" % params)
-    fileHandle.write('\nexit')
-    fileHandle.close()
-    commands.getoutput("chmod 777 /tmp/py2deb2.tmp")
-    commands.getoutput("/tmp/py2deb2.tmp")
-
-    ret = []
-
-    l=glob("%(TEMP)s/%(name)s*.tar.gz" % params)
-    if len(l)!=1:
-        raise Py2debException("don't find source package tar.gz")
-    tar = os.path.basename(l[0])
-    shutil.move(l[0],tar)
-    ret.append(tar)
-
-    l=glob("%(TEMP)s/%(name)s*.dsc" % params)
-    if len(l)!=1:
-        raise Py2debException("don't find source package dsc")
-    tar = os.path.basename(l[0])
-    shutil.move(l[0],tar)
-    ret.append(tar)
-
-    l = glob("%(TEMP)s/%(name)s*.changes" % params)
-    if len(l)!=1:
-        raise Py2debException("don't find source package changes")
-    tar = os.path.basename(l[0])
-    shutil.move(l[0],tar)
-    ret.append(tar)
-
-    return ret
-
-
-class Py2dsc(object):
-
-    def __init__(self, StandardsVersion, BuildDepends, files, **kwargs):
-      self.options = kwargs # TODO: Is order important?
-      self.StandardsVersion = StandardsVersion
-      self.BuildDepends=BuildDepends
-      self.files=files
-
-    @property
-    def content(self):
-        content = ["%s: %s" % (k, v)
-                   for k,v in self.options.iteritems()]
-
-        if self.BuildDepends:
-            content.append("Build-Depends: %s" % self.BuildDepends)
-        if self.StandardsVersion:
-            content.append("Standards-Version: %s" % self.StandardsVersion)
-
-        content.append('Files:')
-
-        for onefile in self.files:
-            print onefile
-            md5 = md5sum(onefile)
-            size = os.stat(onefile).st_size.__str__()
-            content.append(' '+md5 + ' ' + size +' '+os.path.basename(onefile))
-
-        return "\n".join(content)+"\n\n"
-
-
-def py2dsc(TEMP, name, version, depends, author, mail, arch):
-    dsccontent = Py2dsc(
-        "%(version)s" % locals(),
-        "%(depends)s" % locals(),
-        ("%(TEMP)s/%(name)s_%(version)s.tar.gz" % locals(),),
-        Format='1.0',
-        Source="%(name)s" % locals(),
-        Version="%(version)s" % locals(),
-        Maintainer="%(author)s <%(mail)s>" % locals(),
-        Architecture="%(arch)s" % locals(),
-    )
-
-    filename = "%(TEMP)s/%(name)s_%(version)s.dsc" % locals()
-
-    f = open(filename, "wb")
-    try:
-        f.write(dsccontent.content)
-    finally:
-        f.close()
-
-    fileHandle = open('/tmp/py2deb.tmp', 'w')
-    try:
-        fileHandle.write('#!/bin/sh\n')
-        fileHandle.write("cd " + os.getcwd() + "\n")
-        # TODO Renable signing
-        # fileHandle.write("gpg --local-user %(mail)s --clearsign %(TEMP)s/%(name)s_%(version)s.dsc\n" % locals())
-        fileHandle.write("mv %(TEMP)s/%(name)s_%(version)s.dsc.asc %(filename)s\n" % locals())
-        fileHandle.write('\nexit')
-        fileHandle.close()
-    finally:
-        f.close()
-
-    commands.getoutput("chmod 777 /tmp/py2deb.tmp")
-    commands.getoutput("/tmp/py2deb.tmp")
-
-    return filename
-
-
-class Py2tar(object):
-
-    def __init__(self, dataDirectoryPath):
-        self._dataDirectoryPath = dataDirectoryPath
-
-    def packed(self):
-        return self._getSourcesFiles()
-
-    def _getSourcesFiles(self):
-        directoryPath = self._dataDirectoryPath
-
-        outputFileObj = StringIO.StringIO() # TODO: Do more transparently?
-
-        tarOutput = tarfile.TarFile.open('sources',
-                                 mode = "w:gz",
-                                 fileobj = outputFileObj)
-
-        # Note: We can't use this because we need to fiddle permissions:
-        #       tarOutput.add(directoryPath, arcname = "")
-
-        for root, dirs, files in os.walk(directoryPath):
-            archiveRoot = root[len(directoryPath):]
-
-            tarinfo = tarOutput.gettarinfo(root, archiveRoot)
-            # TODO: Make configurable?
-            tarinfo.uid = UID_ROOT
-            tarinfo.gid = GID_ROOT
-            tarinfo.uname = ""
-            tarinfo.gname = ""
-            tarOutput.addfile(tarinfo)
-
-            for f in  files:
-                tarinfo = tarOutput.gettarinfo(os.path.join(root, f),
-                                               os.path.join(archiveRoot, f))
-                tarinfo.uid = UID_ROOT
-                tarinfo.gid = GID_ROOT
-                tarinfo.uname = ""
-                tarinfo.gname = ""
-                tarOutput.addfile(tarinfo, file(os.path.join(root, f)))
-
-        tarOutput.close()
-
-        data_tar_gz = outputFileObj.getvalue()
-
-        return data_tar_gz
-
-
-def py2tar(DEST, TEMP, name, version):
-    tarcontent = Py2tar("%(DEST)s" % locals())
-    filename = "%(TEMP)s/%(name)s_%(version)s.tar.gz" % locals()
-    f = open(filename, "wb")
-    try:
-        f.write(tarcontent.packed())
-    finally:
-        f.close()
-    return filename
-
-
-class Py2debException(Exception):
-    pass
-
-
-SECTIONS_BY_POLICY = {
-    # http://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections
-    "debian": "admin, base, comm, contrib, devel, doc, editors, electronics, embedded, games, gnome, graphics, hamradio, interpreters, kde, libs, libdevel, mail, math, misc, net, news, non-free, oldlibs, otherosfs, perl, python, science, shells, sound, tex, text, utils, web, x11",
-    # http://maemo.org/forrest-images/pdf/maemo-policy.pdf
-    "chinook": "accessories, communication, games, multimedia, office, other, programming, support, themes, tools",
-    # http://wiki.maemo.org/Task:Package_categories
-    "diablo": "user/desktop, user/development, user/education, user/games, user/graphics, user/multimedia, user/navigation, user/network, user/office, user/science, user/system, user/utilities",
-    # http://wiki.maemo.org/Task:Fremantle_application_categories
-    "mer": "user/desktop, user/development, user/education, user/games, user/graphics, user/multimedia, user/navigation, user/network, user/office, user/science, user/system, user/utilities",
-    # http://wiki.maemo.org/Task:Fremantle_application_categories
-    "fremantle": "user/desktop, user/development, user/education, user/games, user/graphics, user/multimedia, user/navigation, user/network, user/office, user/science, user/system, user/utilities",
-}
-
-
-LICENSE_AGREEMENT = {
-        "gpl": """
-    This package is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2 of the License, or
-    (at your option) any later version.
-
-    This package is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this package; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-
-On Debian systems, the complete text of the GNU General
-Public License can be found in `/usr/share/common-licenses/GPL'.
-""",
-        "lgpl":"""
-    This package is free software; you can redistribute it and/or
-    modify it under the terms of the GNU Lesser General Public
-    License as published by the Free Software Foundation; either
-    version 2 of the License, or (at your option) any later version.
-
-    This package is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-    Lesser General Public License for more details.
-
-    You should have received a copy of the GNU Lesser General Public
-    License along with this package; if not, write to the Free Software
-    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
-
-On Debian systems, the complete text of the GNU Lesser General
-Public License can be found in `/usr/share/common-licenses/LGPL'.
-""",
-        "bsd": """
-    Redistribution and use in source and binary forms, with or without
-    modification, are permitted under the terms of the BSD License.
-
-    THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
-    ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-    IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-    ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
-    FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-    DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-    OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-    HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-    LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-    OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-    SUCH DAMAGE.
-
-On Debian systems, the complete text of the BSD License can be
-found in `/usr/share/common-licenses/BSD'.
-""",
-        "artistic": """
-    This program is free software; you can redistribute it and/or modify it
-    under the terms of the "Artistic License" which comes with Debian.
-
-    THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
-    WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES
-    OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
-
-On Debian systems, the complete text of the Artistic License
-can be found in `/usr/share/common-licenses/Artistic'.
-"""
-}
-
-
-class Py2deb(object):
-    """
-    heavily based on technic described here :
-    http://wiki.showmedo.com/index.php?title=LinuxJensMakingDeb
-    """
-    ## STATICS
-    clear = False  # clear build folder after py2debianization
-
-    SECTIONS = SECTIONS_BY_POLICY["debian"]
-
-    #http://www.debian.org/doc/debian-policy/footnotes.html#f69
-    ARCHS = "all i386 ia64 alpha amd64 armeb arm hppa m32r m68k mips mipsel powerpc ppc64 s390 s390x sh3 sh3eb sh4 sh4eb sparc darwin-i386 darwin-ia64 darwin-alpha darwin-amd64 darwin-armeb darwin-arm darwin-hppa darwin-m32r darwin-m68k darwin-mips darwin-mipsel darwin-powerpc darwin-ppc64 darwin-s390 darwin-s390x darwin-sh3 darwin-sh3eb darwin-sh4 darwin-sh4eb darwin-sparc freebsd-i386 freebsd-ia64 freebsd-alpha freebsd-amd64 freebsd-armeb freebsd-arm freebsd-hppa freebsd-m32r freebsd-m68k freebsd-mips freebsd-mipsel freebsd-powerpc freebsd-ppc64 freebsd-s390 freebsd-s390x freebsd-sh3 freebsd-sh3eb freebsd-sh4 freebsd-sh4eb freebsd-sparc kfreebsd-i386 kfreebsd-ia64 kfreebsd-alpha kfreebsd-amd64 kfreebsd-armeb kfreebsd-arm kfreebsd-hppa kfreebsd-m32r kfreebsd-m68k kfreebsd-mips kfreebsd-mipsel kfreebsd-powerpc kfreebsd-ppc64 kfreebsd-s390 kfreebsd-s390x kfreebsd-sh3 kfreebsd-sh3eb kfreebsd-sh4 kfreebsd-sh4eb kfreebsd-sparc knetbsd-i386 knetbsd-ia64 knetbsd-alpha knetbsd-amd64 knetbsd-armeb knetbsd-arm knetbsd-hppa knetbsd-m32r knetbsd-m68k knetbsd-mips knetbsd-mipsel knetbsd-powerpc knetbsd-ppc64 knetbsd-s390 knetbsd-s390x knetbsd-sh3 knetbsd-sh3eb knetbsd-sh4 knetbsd-sh4eb knetbsd-sparc netbsd-i386 netbsd-ia64 netbsd-alpha netbsd-amd64 netbsd-armeb netbsd-arm netbsd-hppa netbsd-m32r netbsd-m68k netbsd-mips netbsd-mipsel netbsd-powerpc netbsd-ppc64 netbsd-s390 netbsd-s390x netbsd-sh3 netbsd-sh3eb netbsd-sh4 netbsd-sh4eb netbsd-sparc openbsd-i386 openbsd-ia64 openbsd-alpha openbsd-amd64 openbsd-armeb openbsd-arm openbsd-hppa openbsd-m32r openbsd-m68k openbsd-mips openbsd-mipsel openbsd-powerpc openbsd-ppc64 openbsd-s390 openbsd-s390x openbsd-sh3 openbsd-sh3eb openbsd-sh4 openbsd-sh4eb openbsd-sparc hurd-i386 hurd-ia64 hurd-alpha hurd-amd64 hurd-armeb hurd-arm hurd-hppa hurd-m32r hurd-m68k hurd-mips hurd-mipsel hurd-powerpc hurd-ppc64 hurd-s390 hurd-s390x hurd-sh3 hurd-sh3eb hurd-sh4 hurd-sh4eb hurd-sparc armel".split(" ")
-
-    # license terms taken from dh_make
-    LICENSES = list(LICENSE_AGREEMENT.iterkeys())
-
-    def __setitem__(self, path, files):
-
-        if not type(files)==list:
-            raise Py2debException("value of key path '%s' is not a list"%path)
-        if not files:
-            raise Py2debException("value of key path '%s' should'nt be empty"%path)
-        if not path.startswith("/"):
-            raise Py2debException("key path '%s' malformed (don't start with '/')"%path)
-        if path.endswith("/"):
-            raise Py2debException("key path '%s' malformed (shouldn't ends with '/')"%path)
-
-        nfiles=[]
-        for file in files:
-
-            if ".." in file:
-                raise Py2debException("file '%s' contains '..', please avoid that!"%file)
-
-
-            if "|" in file:
-                if file.count("|")!=1:
-                    raise Py2debException("file '%s' is incorrect (more than one pipe)"%file)
-
-                file, nfile = file.split("|")
-            else:
-                nfile=file  # same localisation
-
-            if os.path.isdir(file):
-                raise Py2debException("file '%s' is a folder, and py2deb refuse folders !"%file)
-
-            if not os.path.isfile(file):
-                raise Py2debException("file '%s' doesn't exist"%file)
-
-            if file.startswith("/"):    # if an absolute file is defined
-                if file==nfile:         # and not renamed (pipe trick)
-                    nfile=os.path.basename(file)   # it's simply copied to 'path'
-
-            nfiles.append((file, nfile))
-
-        nfiles.sort(lambda a, b: cmp(a[1], b[1]))    #sort according new name (nfile)
-
-        self.__files[path]=nfiles
-
-    def __getitem__(self, k):
-        return self.__files[k]
-
-    def __delitem__(self, k):
-        del self.__files[k]
-
-    def __init__(self,
-                    name,
-                    description="no description",
-                    license="gpl",
-                    depends="",
-                    section="utils",
-                    arch="all",
-
-                    url="",
-                    author = None,
-                    mail = None,
-
-                    preinstall = None,
-                    postinstall = None,
-                    preremove = None,
-                    postremove = None
-                ):
-
-        if author is None:
-            author = ("USERNAME" in os.environ) and os.environ["USERNAME"] or None
-            if author is None:
-                author = ("USER" in os.environ) and os.environ["USER"] or "unknown"
-
-        if mail is None:
-            mail = author+"@"+socket.gethostname()
-
-        self.name = name
-        self.prettyName = ""
-        self.description = description
-        self.upgradeDescription = ""
-        self.bugTracker = ""
-        self.license = license
-        self.depends = depends
-        self.recommends = ""
-        self.section = section
-        self.arch = arch
-        self.url = url
-        self.author = author
-        self.mail = mail
-        self.icon = ""
-        self.distribution = ""
-        self.respository = ""
-        self.urgency = "low"
-
-        self.preinstall = preinstall
-        self.postinstall = postinstall
-        self.preremove = preremove
-        self.postremove = postremove
-
-        self.__files={}
-
-    def __repr__(self):
-        name = self.name
-        license = self.license
-        description = self.description
-        depends = self.depends
-        recommends = self.recommends
-        section = self.section
-        arch = self.arch
-        url = self.url
-        author = self.author
-        mail = self.mail
-
-        preinstall = self.preinstall
-        postinstall = self.postinstall
-        preremove = self.preremove
-        postremove = self.postremove
-
-        paths=self.__files.keys()
-        paths.sort()
-        files=[]
-        for path in paths:
-            for file, nfile in self.__files[path]:
-                #~ rfile=os.path.normpath(os.path.join(path, nfile))
-                rfile=os.path.join(path, nfile)
-                if nfile==file:
-                    files.append(rfile)
-                else:
-                    files.append(rfile + " (%s)"%file)
-
-        files.sort()
-        files = "\n".join(files)
-
-
-        lscripts = [    preinstall and "preinst",
-                        postinstall and "postinst",
-                        preremove and "prerm",
-                        postremove and "postrm",
-                    ]
-        scripts = lscripts and ", ".join([i for i in lscripts if i]) or "None"
-        return """
-----------------------------------------------------------------------
-NAME        : %(name)s
-----------------------------------------------------------------------
-LICENSE     : %(license)s
-URL         : %(url)s
-AUTHOR      : %(author)s
-MAIL        : %(mail)s
-----------------------------------------------------------------------
-DEPENDS     : %(depends)s
-RECOMMENDS  : %(recommends)s
-ARCH        : %(arch)s
-SECTION     : %(section)s
-----------------------------------------------------------------------
-DESCRIPTION :
-%(description)s
-----------------------------------------------------------------------
-SCRIPTS : %(scripts)s
-----------------------------------------------------------------------
-FILES :
-%(files)s
-""" % locals()
-
-    def generate(self, version, changelog="", rpm=False, src=False, build=True, tar=False, changes=False, dsc=False):
-        """ generate a deb of version 'version', with or without 'changelog', with or without a rpm
-            (in the current folder)
-            return a list of generated files
-        """
-        if not sum([len(i) for i in self.__files.values()])>0:
-            raise Py2debException("no files are defined")
-
-        if not changelog:
-            changelog="* no changelog"
-
-        name = self.name
-        description = self.description
-        license = self.license
-        depends = self.depends
-        recommends = self.recommends
-        section = self.section
-        arch = self.arch
-        url = self.url
-        distribution = self.distribution
-        repository = self.repository
-        urgency = self.urgency
-        author = self.author
-        mail = self.mail
-        files = self.__files
-        preinstall = self.preinstall
-        postinstall = self.postinstall
-        preremove = self.preremove
-        postremove = self.postremove
-
-        if section not in Py2deb.SECTIONS:
-            raise Py2debException("section '%s' is unknown (%s)" % (section, str(Py2deb.SECTIONS)))
-
-        if arch not in Py2deb.ARCHS:
-            raise Py2debException("arch '%s' is unknown (%s)"% (arch, str(Py2deb.ARCHS)))
-
-        if license not in Py2deb.LICENSES:
-            raise Py2debException("License '%s' is unknown (%s)" % (license, str(Py2deb.LICENSES)))
-
-        # create dates (buildDate, buildDateYear)
-        d=datetime.now()
-        buildDate=d.strftime("%a, %d %b %Y %H:%M:%S +0000")
-        buildDateYear=str(d.year)
-
-        #clean description (add a space before each next lines)
-        description=description.replace("\r", "").strip()
-        description = "\n ".join(description.split("\n"))
-
-        #clean changelog (add 2 spaces before each next lines)
-        changelog=changelog.replace("\r", "").strip()
-        changelog = "\n  ".join(changelog.split("\n"))
-
-        TEMP = ".py2deb_build_folder"
-        DEST = os.path.join(TEMP, name)
-        DEBIAN = os.path.join(DEST, "debian")
-
-        packageContents = locals()
-
-        # let's start the process
-        try:
-            shutil.rmtree(TEMP)
-        except:
-            pass
-
-        os.makedirs(DEBIAN)
-        try:
-            rules=[]
-            dirs=[]
-            for path in files:
-                for ofile, nfile in files[path]:
-                    if os.path.isfile(ofile):
-                        # it's a file
-
-                        if ofile.startswith("/"): # if absolute path
-                            # we need to change dest
-                            dest=os.path.join(DEST, nfile)
-                        else:
-                            dest=os.path.join(DEST, ofile)
-
-                        # copy file to be packaged
-                        destDir = os.path.dirname(dest)
-                        if not os.path.isdir(destDir):
-                            os.makedirs(destDir)
-
-                        shutil.copy2(ofile, dest)
-
-                        ndir = os.path.join(path, os.path.dirname(nfile))
-                        nname = os.path.basename(nfile)
-
-                        # make a line RULES to be sure the destination folder is created
-                        # and one for copying the file
-                        fpath = "/".join(["$(CURDIR)", "debian", name+ndir])
-                        rules.append('mkdir -p "%s"' % fpath)
-                        rules.append('cp -a "%s" "%s"' % (ofile, os.path.join(fpath, nname)))
-
-                        # append a dir
-                        dirs.append(ndir)
-
-                    else:
-                        raise Py2debException("unknown file '' "%ofile) # shouldn't be raised (because controlled before)
-
-            # make rules right
-            rules= "\n\t".join(rules) + "\n"
-            packageContents["rules"] = rules
-
-            # make dirs right
-            dirs= [i[1:] for i in set(dirs)]
-            dirs.sort()
-
-            #==========================================================================
-            # CREATE debian/dirs
-            #==========================================================================
-            open(os.path.join(DEBIAN, "dirs"), "w").write("\n".join(dirs))
-
-            #==========================================================================
-            # CREATE debian/changelog
-            #==========================================================================
-            clog="""%(name)s (%(version)s) stable; urgency=low
-
-  %(changelog)s
-
- -- %(author)s <%(mail)s>  %(buildDate)s
-""" % packageContents
-
-            open(os.path.join(DEBIAN, "changelog"), "w").write(clog)
-
-            #==========================================================================
-            #Create pre/post install/remove
-            #==========================================================================
-            def mkscript(name, dest):
-                if name and name.strip()!="":
-                    if os.path.isfile(name):    # it's a file
-                        content = file(name).read()
-                    else:   # it's a script
-                        content = name
-                    open(os.path.join(DEBIAN, dest), "w").write(content)
-
-            mkscript(preinstall, "preinst")
-            mkscript(postinstall, "postinst")
-            mkscript(preremove, "prerm")
-            mkscript(postremove, "postrm")
-
-
-            #==========================================================================
-            # CREATE debian/compat
-            #==========================================================================
-            open(os.path.join(DEBIAN, "compat"), "w").write("5\n")
-
-            #==========================================================================
-            # CREATE debian/control
-            #==========================================================================
-            generalParagraphFields = [
-                "Source: %(name)s",
-                "Maintainer: %(author)s <%(mail)s>",
-                "Section: %(section)s",
-                "Priority: extra",
-                "Build-Depends: debhelper (>= 5)",
-                "Standards-Version: 3.7.2",
-            ]
-
-            specificParagraphFields = [
-                "Package: %(name)s",
-                "Architecture: %(arch)s",
-                "Depends: %(depends)s",
-                "Recommends: %(recommends)s",
-                "Description: %(description)s",
-            ]
-
-            if self.prettyName:
-                prettyName = "XSBC-Maemo-Display-Name: %s" % self.prettyName.strip()
-                specificParagraphFields.append("\n  ".join(prettyName.split("\n")))
-
-            if self.bugTracker:
-                bugTracker = "XSBC-Bugtracker: %s" % self.bugTracker.strip()
-                specificParagraphFields.append("\n  ".join(bugTracker.split("\n")))
-
-            if self.upgradeDescription:
-                upgradeDescription = "XSBC-Maemo-Upgrade-Description: %s" % self.upgradeDescription.strip()
-                specificParagraphFields.append("\n  ".join(upgradeDescription.split("\n")))
-
-            if self.icon:
-                f = open(self.icon, "rb")
-                try:
-                    rawIcon = f.read()
-                finally:
-                    f.close()
-                uueIcon = base64.b64encode(rawIcon)
-                uueIconLines = []
-                for i, c in enumerate(uueIcon):
-                    if i % 60 == 0:
-                        uueIconLines.append("")
-                    uueIconLines[-1] += c
-                uueIconLines[0:0] = ("XSBC-Maemo-Icon-26:", )
-                specificParagraphFields.append("\n  ".join(uueIconLines))
-
-            generalParagraph = "\n".join(generalParagraphFields)
-            specificParagraph = "\n".join(specificParagraphFields)
-            controlContent = "\n\n".join((generalParagraph, specificParagraph)) % packageContents
-            open(os.path.join(DEBIAN, "control"), "w").write(controlContent)
-
-            #==========================================================================
-            # CREATE debian/copyright
-            #==========================================================================
-            packageContents["txtLicense"] = LICENSE_AGREEMENT[license]
-            packageContents["pv"] =__version__
-            txt="""This package was py2debianized(%(pv)s) by %(author)s <%(mail)s> on
-%(buildDate)s.
-
-It was downloaded from %(url)s
-
-Upstream Author: %(author)s <%(mail)s>
-
-Copyright: %(buildDateYear)s by %(author)s
-
-License:
-
-%(txtLicense)s
-
-The Debian packaging is (C) %(buildDateYear)s, %(author)s <%(mail)s> and
-is licensed under the GPL, see above.
-
-
-# Please also look if there are files or directories which have a
-# different copyright/license attached and list them here.
-""" % packageContents
-            open(os.path.join(DEBIAN, "copyright"), "w").write(txt)
-
-            #==========================================================================
-            # CREATE debian/rules
-            #==========================================================================
-            txt="""#!/usr/bin/make -f
-# -*- makefile -*-
-# Sample debian/rules that uses debhelper.
-# This file was originally written by Joey Hess and Craig Small.
-# As a special exception, when this file is copied by dh-make into a
-# dh-make output file, you may use that output file without restriction.
-# This special exception was added by Craig Small in version 0.37 of dh-make.
-
-# Uncomment this to turn on verbose mode.
-#export DH_VERBOSE=1
-
-
-
-
-CFLAGS = -Wall -g
-
-ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
-       CFLAGS += -O0
-else
-       CFLAGS += -O2
-endif
-
-configure: configure-stamp
-configure-stamp:
-       dh_testdir
-       # Add here commands to configure the package.
-
-       touch configure-stamp
-
-
-build: build-stamp
-
-build-stamp: configure-stamp
-       dh_testdir
-       touch build-stamp
-
-clean:
-       dh_testdir
-       dh_testroot
-       rm -f build-stamp configure-stamp
-       dh_clean
-
-install: build
-       dh_testdir
-       dh_testroot
-       dh_clean -k
-       dh_installdirs
-
-       # ======================================================
-       #$(MAKE) DESTDIR="$(CURDIR)/debian/%(name)s" install
-       mkdir -p "$(CURDIR)/debian/%(name)s"
-
-       %(rules)s
-       # ======================================================
-
-# Build architecture-independent files here.
-binary-indep: build install
-# We have nothing to do by default.
-
-# Build architecture-dependent files here.
-binary-arch: build install
-       dh_testdir
-       dh_testroot
-       dh_installchangelogs debian/changelog
-       dh_installdocs
-       dh_installexamples
-#      dh_install
-#      dh_installmenu
-#      dh_installdebconf
-#      dh_installlogrotate
-#      dh_installemacsen
-#      dh_installpam
-#      dh_installmime
-#      dh_python
-#      dh_installinit
-#      dh_installcron
-#      dh_installinfo
-       dh_installman
-       dh_link
-       dh_strip
-       dh_compress
-       dh_fixperms
-#      dh_perl
-#      dh_makeshlibs
-       dh_installdeb
-       dh_shlibdeps
-       dh_gencontrol
-       dh_md5sums
-       dh_builddeb
-
-binary: binary-indep binary-arch
-.PHONY: build clean binary-indep binary-arch binary install configure
-""" % packageContents
-            open(os.path.join(DEBIAN, "rules"), "w").write(txt)
-            os.chmod(os.path.join(DEBIAN, "rules"), 0755)
-
-            ###########################################################################
-            ###########################################################################
-            ###########################################################################
-
-            generatedFiles = []
-
-            if build:
-                #http://www.debian.org/doc/manuals/maint-guide/ch-build.fr.html
-                ret = os.system('cd "%(DEST)s"; dpkg-buildpackage -tc -rfakeroot -us -uc' % packageContents)
-                if ret != 0:
-                    raise Py2debException("buildpackage failed (see output)")
-
-                l=glob("%(TEMP)s/%(name)s*.deb" % packageContents)
-                if len(l) != 1:
-                    raise Py2debException("didn't find builded deb")
-
-                tdeb = l[0]
-                deb = os.path.basename(tdeb)
-                shutil.move(tdeb, deb)
-
-                generatedFiles = [deb, ]
-
-                if rpm:
-                    rpmFilename = deb2rpm(deb)
-                    generatedFiles.append(rpmFilename)
-
-                if src:
-                    tarFilename = py2src(TEMP, name)
-                    generatedFiles.append(tarFilename)
-
-            if tar:
-                tarFilename = py2tar(DEST, TEMP, name, version)
-                generatedFiles.append(tarFilename)
-
-            if dsc:
-                dscFilename = py2dsc(TEMP, name, version, depends, author, mail, arch)
-                generatedFiles.append(dscFilename)
-
-            if changes:
-                changesFilenames = py2changes(packageContents)
-                generatedFiles.extend(changesFilenames)
-
-            return generatedFiles
-
-        #~ except Exception,m:
-            #~ raise Py2debException("build error :"+str(m))
-
-        finally:
-            if Py2deb.clear:
-                shutil.rmtree(TEMP)
-
-
-if __name__ == "__main__":
-    try:
-        os.chdir(os.path.dirname(sys.argv[0]))
-    except:
-        pass
-
-    p=Py2deb("python-py2deb")
-    p.description="Generate simple deb(/rpm/tgz) from python (2.4, 2.5 and 2.6)"
-    p.url = "http://www.manatlan.com/page/py2deb"
-    p.author=__author__
-    p.mail=__mail__
-    p.depends = "dpkg-dev, fakeroot, alien, python"
-    p.section="python"
-    p["/usr/lib/python2.6/dist-packages"] = ["py2deb.py", ]
-    p["/usr/lib/python2.5/site-packages"] = ["py2deb.py", ]
-    p["/usr/lib/python2.4/site-packages"] = ["py2deb.py", ]
-    #~ p.postinstall = "s.py"
-    #~ p.preinstall = "s.py"
-    #~ p.postremove = "s.py"
-    #~ p.preremove = "s.py"
-    print p
-    print p.generate(__version__, changelog = __doc__, src=True)
diff --git a/support/scale.py b/support/scale.py
new file mode 100755 (executable)
index 0000000..f9eb784
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/env python
+
+from __future__ import with_statement
+from __future__ import division
+
+import os
+import logging
+
+from PIL import Image
+
+
+_moduleLogger = logging.getLogger(__name__)
+
+
+def main(args):
+       import optparse
+       parser = optparse.OptionParser()
+       parser.add_option(
+               "--input", dest="input",
+               help="Input image to scale", metavar="INPUT"
+       )
+       parser.add_option(
+               "--output", dest="output",
+               help="Scaled image", metavar="OUTPUT"
+       )
+       parser.add_option(
+               "--size", dest="size",
+               help="Icon size", metavar="SIZE"
+       )
+       options, positional  = parser.parse_args(args)
+       if positional:
+               parser.error("No positional arguments supported")
+       if None in [options.input, options.output, options.size]:
+               parser.error("Missing argument")
+       if options.size == "guess":
+               parts = reversed(os.path.split(options.output))
+               for part in parts:
+                       try:
+                               options.size = int(part)
+                               _moduleLogger.info("Assuming image size of %r" % options.size)
+                               break
+                       except ValueError:
+                               pass
+
+       icon = Image.open(options.input)
+       icon.thumbnail((options.size, options.size), Image.ANTIALIAS)
+       icon.save(options.output)
+
+
+if __name__ == "__main__":
+       import sys
+       retcode = main(sys.argv[1:])
+       sys.exit(retcode)