Initial checkin of multilist
authorEd Page <eopage@byu.net>
Thu, 18 Mar 2010 22:45:18 +0000 (17:45 -0500)
committerEd Page <eopage@byu.net>
Thu, 18 Mar 2010 22:45:18 +0000 (17:45 -0500)
33 files changed:
Makefile [new file with mode: 0644]
build-stamp [new file with mode: 0644]
data/high/multilist.png [new file with mode: 0644]
data/low/multilist.png [new file with mode: 0644]
data/multilist.desktop [new file with mode: 0644]
data/multilist.png [new file with mode: 0644]
data/multilist.service [new file with mode: 0644]
data/scale/multilist.png [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/dirs [new file with mode: 0644]
debian/docs [new file with mode: 0644]
debian/files [new file with mode: 0644]
debian/postinst [new file with mode: 0644]
debian/rules [new file with mode: 0755]
po/de.po [new file with mode: 0644]
po/ru.po [new file with mode: 0644]
remake [new file with mode: 0755]
setup.py [new file with mode: 0644]
src/copydb.py [new file with mode: 0755]
src/multilist [new file with mode: 0755]
src/multilistclasses/__init__.py [new file with mode: 0755]
src/multilistclasses/libbottombar.py [new file with mode: 0644]
src/multilistclasses/libliststorehandler.py [new file with mode: 0644]
src/multilistclasses/libmultilist.py [new file with mode: 0755]
src/multilistclasses/libselection.py [new file with mode: 0644]
src/multilistclasses/libspeichern.py [new file with mode: 0644]
src/multilistclasses/libsqldialog.py [new file with mode: 0755]
src/multilistclasses/libsync.py [new file with mode: 0755]
src/multilistclasses/libview.py [new file with mode: 0644]
src/upload.sh [new file with mode: 0755]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..ab363c7
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,26 @@
+all: build_mo
+       python2.5 setup.py build  
+clean: 
+       rm -rf ./locale ./po/templates.pot
+       python2.5 setup.py clean --all
+install: build_mo
+       python2.5 setup.py install --root $(DESTDIR) 
+
+TEXT_DOMAIN=multilist
+POTFILES=src/multilist $(wildcard src/multilistclasses/*.py)
+
+update_po: po/templates.pot
+       @for lang in $(basename $(notdir $(wildcard po/*.po))); do \
+               msgmerge -U --strict --no-wrap po/$$lang.po po/templates.pot; \
+       done
+
+po/templates.pot: $(POTFILES)
+       xgettext --language=Python --strict --no-wrap --output=$@ $(POTFILES)
+
+build_mo:
+       @for lang in $(basename $(notdir $(wildcard po/*.po))); do \
+               mkdir -p locale/$$lang/LC_MESSAGES; \
+               msgfmt --statistics -c -o locale/$$lang/LC_MESSAGES/$(TEXT_DOMAIN).mo po/$$lang.po; \
+       done
+
+.PHONES: update_po build_mo
diff --git a/build-stamp b/build-stamp
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/data/high/multilist.png b/data/high/multilist.png
new file mode 100644 (file)
index 0000000..a62085d
Binary files /dev/null and b/data/high/multilist.png differ
diff --git a/data/low/multilist.png b/data/low/multilist.png
new file mode 100644 (file)
index 0000000..d978b6d
Binary files /dev/null and b/data/low/multilist.png differ
diff --git a/data/multilist.desktop b/data/multilist.desktop
new file mode 100644 (file)
index 0000000..0613db5
--- /dev/null
@@ -0,0 +1,9 @@
+[Desktop Entry]
+Version=0.3.0
+Encoding=UTF-8
+Name=Multilist
+Exec=/usr/bin/multilist
+Icon=multilist
+Type=Application
+#X-Osso-Service=multilist
+X-Osso-Type=application/x-executable 
diff --git a/data/multilist.png b/data/multilist.png
new file mode 100644 (file)
index 0000000..d978b6d
Binary files /dev/null and b/data/multilist.png differ
diff --git a/data/multilist.service b/data/multilist.service
new file mode 100644 (file)
index 0000000..35e320e
--- /dev/null
@@ -0,0 +1,3 @@
+[D-BUS Service] 
+Name=com.nokia.multilist
+Exec=/usr/bin/multilist
diff --git a/data/scale/multilist.png b/data/scale/multilist.png
new file mode 100644 (file)
index 0000000..6caa276
Binary files /dev/null and b/data/scale/multilist.png differ
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..2cdb77e
--- /dev/null
@@ -0,0 +1,12 @@
+multilist (0.3.1) unstable; urgency=low
+
+  * I18N, extract de.po, add ru.po.
+
+ -- Nikolay Logvinov <NLogvinov@gmail.com>  Fri, 13 Mar 2009 13:42:23 +0300
+
+multilist (0.3.0) unstable; urgency=low
+
+  * Initial Release.
+
+ -- unknown <n800@axique.de>  Mon,  6 Aug 2007 08:12:53 +0100
+
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..b8626c4
--- /dev/null
@@ -0,0 +1 @@
+4
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..aa57b00
--- /dev/null
@@ -0,0 +1,20 @@
+Source: multilist
+Section: user/other
+Priority: optional
+Maintainer: Christoph Würstle <n800 at axique de>
+Build-Depends: debhelper (>= 4.0.0), python2.5-dev
+Standards-Version: 3.6.1
+
+Package: multilist
+Architecture: all
+Depends: python2.5, python2.5-hildon, python2.5-gtk2, python2.5-osso, python2.5-dbus, python2.5-gstreamer
+Description: Multilist
+XB-Maemo-Icon-26:
+ iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAAXNSR0IArs4c6QAAAAZiS0dEAAAA
+ AAAA+UO7fwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9gCBQkmAyqhfqEAAAAddEVYdENv
+ bW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAAP9JREFUSMfNVsERgzAMk7lMAGPwYv8h
+ eDEGrKA+eiluEqcOBdrc5S5AkCxbmAhJ4obR4aYR4kJELiMh+QNFmv2sobPUWRtKszZGOWgGkm+z
+ BDrKDa4bBVjoVxa+rYNXVVcr5Kf6RFWpshJ5sOrjSVkJcDFeldiCYtQkTQWt1teYh1yHSex1nKf2
+ uhLhzIuaqgae6W9B7iY7yQ4c1UzSRuQqehq9vi4QZq4De/V0U+zP+8Sag2Ao31euy7s31ucG9hkp
+ SQiGfZ9B0NZUFYCl4pRfuY42BSVWt5K669gDskFQqFslsFoGgpk2pq6xU+VRF1q+n6jwpTRZ/9dx
+ 6+pz5APOkH7tCcRtTQAAAABJRU5ErkJggg==
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..3c80733
--- /dev/null
@@ -0,0 +1,12 @@
+This package was debianized by Christoph Würstle <n800 !at! axique .dotx de> on
+Mon, 22 Jan 2007 22:44:33 +0200.
+
+It was downloaded from <fill in ftp site>
+
+Copyright:
+
+Upstream Author(s): <put author(s) name and email here>
+
+License:
+
+Special drop a mail
diff --git a/debian/dirs b/debian/dirs
new file mode 100644 (file)
index 0000000..ca882bb
--- /dev/null
@@ -0,0 +1,2 @@
+usr/bin
+usr/sbin
diff --git a/debian/docs b/debian/docs
new file mode 100644 (file)
index 0000000..724e084
--- /dev/null
@@ -0,0 +1,2 @@
+README
+TODO
diff --git a/debian/files b/debian/files
new file mode 100644 (file)
index 0000000..4e73ea0
--- /dev/null
@@ -0,0 +1 @@
+multilist_0.3.1_all.deb user/other optional
diff --git a/debian/postinst b/debian/postinst
new file mode 100644 (file)
index 0000000..1df47d1
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh -e
+gtk-update-icon-cache -f /usr/share/icons/hicolor
+exit 0
\ No newline at end of file
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..185e870
--- /dev/null
@@ -0,0 +1,93 @@
+#!/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
+
+
+
+
+ifneq (,$(findstring noopt,$(DEB_BUILD_OPTIONS)))
+       INSTALL_PROGRAM += -s
+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
+
+       # Add here commands to compile the package.
+       $(MAKE)
+
+       touch build-stamp
+
+clean:
+       dh_testdir
+       dh_testroot
+       rm -f build-stamp configure-stamp
+
+       # Add here commands to clean up after the build process.
+       -$(MAKE) clean
+
+       dh_clean
+
+install: build
+       dh_testdir
+       dh_testroot
+       dh_clean -k
+       dh_installdirs
+
+       # Add here commands to install the package into debian/multilist
+       $(MAKE) install DESTDIR=$(CURDIR)/debian/multilist
+
+
+# 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
+#      dh_installdocs
+       dh_installexamples
+#      dh_install
+#      dh_installmenu
+#      dh_installdebconf       
+#      dh_installlogrotate
+#      dh_installemacsen
+#      dh_installpam
+#      dh_installmime
+#      dh_installinit
+#      dh_installcron
+#      dh_installinfo
+       dh_installman
+       dh_link
+       dh_strip
+       dh_compress
+       dh_fixperms
+#      dh_perl
+#      dh_python
+#      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
diff --git a/po/de.po b/po/de.po
new file mode 100644 (file)
index 0000000..7ec3e0f
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,250 @@
+# German translations for multilist package
+# German messages for multilist.
+# Copyright (C) 2009 THE multilist'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the multilist package.
+# Christoph Würstle <chris@axique.de>, 2009.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: multilist\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-03-13 15:05+0300\n"
+"PO-Revision-Date: 2009-03-13 13:07+0300\n"
+"Last-Translator: Christoph Würstle <chris@axique.de>\n"
+"Language-Team: German\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+#
+# File: src/multilist, line: 44
+msgid "Multilist is already running. Start anyway? (Could result in db problems!)"
+msgstr ""
+#
+# File: src/multilistclasses/libbottombar.py, line: 39
+msgid "New item name:"
+msgstr ""
+#
+# File: src/multilistclasses/libbottombar.py, line: 60
+msgid "Delete current item?"
+msgstr ""
+#
+# File: src/multilistclasses/libbottombar.py, line: 67
+msgid "No item selected!"
+msgstr ""
+#
+# File: src/multilistclasses/libbottombar.py, line: 75
+msgid "Really checkout all items?"
+msgstr ""
+#
+# File: src/multilistclasses/libbottombar.py, line: 91
+msgid "New category name:"
+msgstr ""
+#
+# File: src/multilistclasses/libbottombar.py, line: 112
+msgid "New list name:"
+msgstr ""
+#
+# File: src/multilistclasses/libbottombar.py, line: 141
+msgid "New item"
+msgstr ""
+#
+# File: src/multilistclasses/libbottombar.py, line: 148
+msgid "Search:"
+msgstr ""
+#
+# File: src/multilistclasses/libbottombar.py, line: 157
+msgid "Checkout all items"
+msgstr ""
+#
+# File: src/multilistclasses/libbottombar.py, line: 161
+msgid "Del item"
+msgstr ""
+#
+# File: src/multilistclasses/libmultilist.py, line: 109
+msgid "Sync"
+msgstr ""
+#
+# File: src/multilistclasses/libmultilist.py, line: 128
+# File: src/multilistclasses/libmultilist.py, line: 246
+msgid "Choose columns"
+msgstr ""
+#
+# File: src/multilistclasses/libmultilist.py, line: 215
+msgid "Choose database file"
+msgstr "DB auswählen"
+#
+# File: src/multilistclasses/libmultilist.py, line: 219
+msgid "SQL history"
+msgstr "SQL History anschauen"
+#
+# File: src/multilistclasses/libmultilist.py, line: 223
+msgid "SQL optimize"
+msgstr "SQL optimieren"
+#
+# File: src/multilistclasses/libmultilist.py, line: 227
+msgid "Sync items"
+msgstr ""
+#
+# File: src/multilistclasses/libmultilist.py, line: 233
+msgid "Quit"
+msgstr "Beenden"
+#
+# File: src/multilistclasses/libmultilist.py, line: 238
+msgid "File"
+msgstr "Datei"
+#
+# File: src/multilistclasses/libmultilist.py, line: 250
+msgid "Rename Category"
+msgstr ""
+#
+# File: src/multilistclasses/libmultilist.py, line: 254
+msgid "Rename List"
+msgstr ""
+#
+# File: src/multilistclasses/libmultilist.py, line: 258
+msgid "Tools"
+msgstr ""
+#
+# File: src/multilistclasses/libmultilist.py, line: 264
+msgid "About"
+msgstr "Über"
+#
+# File: src/multilistclasses/libmultilist.py, line: 268
+msgid "Help"
+msgstr "Hilfe"
+#
+# File: src/multilistclasses/libmultilist.py, line: 360
+msgid "Select SQL export file"
+msgstr "Wähle SQL-Export-Datei"
+#
+# File: src/multilistclasses/libmultilist.py, line: 390
+msgid "Choose your database file"
+msgstr "Wähle Datenbank-Datei"
+#
+# File: src/multilistclasses/libselection.py, line: 66
+# File: src/multilistclasses/libselection.py, line: 69
+# File: src/multilistclasses/libselection.py, line: 129
+msgid "all"
+msgstr ""
+#
+# File: src/multilistclasses/libselection.py, line: 164
+msgid "List:"
+msgstr ""
+#
+# File: src/multilistclasses/libselection.py, line: 171
+msgid "  Category:"
+msgstr ""
+#
+# File: src/multilistclasses/libselection.py, line: 181
+msgid "  View:"
+msgstr ""
+#
+# File: src/multilistclasses/libselection.py, line: 184
+msgid "All"
+msgstr ""
+#
+# File: src/multilistclasses/libselection.py, line: 186
+msgid "Active"
+msgstr ""
+#
+# File: src/multilistclasses/libsqldialog.py, line: 54
+msgid "SQL History (the past two days):"
+msgstr "SQL History (der letzten 2 Tage (max. 50)):"
+#
+# File: src/multilistclasses/libsqldialog.py, line: 57
+msgid "Export"
+msgstr ""
+#
+# File: src/multilistclasses/libsqldialog.py, line: 73
+msgid "Date"
+msgstr "Datum"
+#
+# File: src/multilistclasses/libsqldialog.py, line: 74
+msgid "SQL"
+msgstr ""
+#
+# File: src/multilistclasses/libsqldialog.py, line: 75
+msgid "Parameter"
+msgstr ""
+#
+# File: src/multilistclasses/libsqldialog.py, line: 116
+msgid " (Reduced parameter) "
+msgstr " (Parameter gekürzt) "
+#
+# File: src/multilistclasses/libsync.py, line: 44
+msgid "Sync process"
+msgstr ""
+#
+# File: src/multilistclasses/libsync.py, line: 49
+msgid "Sync process running...please wait"
+msgstr ""
+#
+# File: src/multilistclasses/libsync.py, line: 51
+msgid "(this can take some minutes)"
+msgstr ""
+#
+# File: src/multilistclasses/libsync.py, line: 91
+msgid "Query"
+msgstr "Abfrage"
+#
+# File: src/multilistclasses/libsync.py, line: 198
+# File: src/multilistclasses/libsync.py, line: 290
+msgid "sync process running"
+msgstr ""
+#
+# File: src/multilistclasses/libsync.py, line: 243
+msgid "Syncserver running..."
+msgstr ""
+#
+# File: src/multilistclasses/libsync.py, line: 251
+msgid "Sync server could not start. Please check IP and port."
+msgstr "Konnte Sync-Server nicht starten. Bitte IP und Port überprüfen."
+#
+# File: src/multilistclasses/libsync.py, line: 264
+msgid "Syncserver not running..."
+msgstr ""
+#
+# File: src/multilistclasses/libsync.py, line: 281
+# File: src/multilistclasses/libsync.py, line: 317
+# File: src/multilistclasses/libsync.py, line: 325
+# File: src/multilistclasses/libsync.py, line: 332
+# File: src/multilistclasses/libsync.py, line: 424
+msgid "no sync process (at the moment)"
+msgstr ""
+#
+# File: src/multilistclasses/libsync.py, line: 319
+msgid "Synchronization successfully completed"
+msgstr "Synchronisation erfolgreich beendet"
+#
+# File: src/multilistclasses/libsync.py, line: 326
+msgid "The clocks are not synchronized between stations"
+msgstr "Zeit differiert zu viel zwischen den Systemen"
+#
+# File: src/multilistclasses/libsync.py, line: 333
+msgid "Sync failed, reason: "
+msgstr "Sync gescheitert. Fehler:"
+#
+# File: src/multilistclasses/libsync.py, line: 389
+msgid "Local SyncServer (port "
+msgstr "LokalerSync-Server (Port "
+#
+# File: src/multilistclasses/libsync.py, line: 407
+msgid "Start SyncServer"
+msgstr "SyncServer starten"
+#
+# File: src/multilistclasses/libsync.py, line: 411
+msgid "Syncserver not running"
+msgstr ""
+#
+# File: src/multilistclasses/libsync.py, line: 414
+msgid "RemoteSync-Server (Port "
+msgstr ""
+#
+# File: src/multilistclasses/libsync.py, line: 420
+msgid "Connect to remote SyncServer"
+msgstr "Verbinde zu Remote-SyncServer"
+#
+# File: src/multilistclasses/libview.py, line: 63
+msgid "Columns"
+msgstr ""
diff --git a/po/ru.po b/po/ru.po
new file mode 100644 (file)
index 0000000..a004694
--- /dev/null
+++ b/po/ru.po
@@ -0,0 +1,249 @@
+# Russian translations for multilist package
+# Copyright (C) 2008 THE multilist'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the multilist package.
+# Nikolay Logvinov <NLogvinov@gmail.com>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: multilist\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2009-03-13 15:05+0300\n"
+"PO-Revision-Date: 2009-03-13 05:07+0400\n"
+"Last-Translator: Nikolay Logvinov <NLogvinov@gmail.com>\n"
+"Language-Team: Russian\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
+#
+# File: src/multilist, line: 44
+msgid "Multilist is already running. Start anyway? (Could result in db problems!)"
+msgstr ""
+#
+# File: src/multilistclasses/libbottombar.py, line: 39
+msgid "New item name:"
+msgstr "Новый элемент"
+#
+# File: src/multilistclasses/libbottombar.py, line: 60
+msgid "Delete current item?"
+msgstr "Удалить текущий элемент?"
+#
+# File: src/multilistclasses/libbottombar.py, line: 67
+msgid "No item selected!"
+msgstr "Сначала выберите элемент"
+#
+# File: src/multilistclasses/libbottombar.py, line: 157
+msgid "Really checkout all items?"
+msgstr "Снять все пометки?"
+#
+# File: src/multilistclasses/libbottombar.py, line: 91
+msgid "New category name:"
+msgstr "Новая категория"
+#
+# File: src/multilistclasses/libbottombar.py, line: 112
+msgid "New list name:"
+msgstr "Новый список"
+#
+# File: src/multilistclasses/libbottombar.py, line: 141
+msgid "New item"
+msgstr "Добавить"
+#
+# File: src/multilistclasses/libbottombar.py, line: 148
+msgid "Search:"
+msgstr "Искать:"
+#
+# File: src/multilistclasses/libbottombar.py, line: 157
+msgid "Checkout all items"
+msgstr "Снять все пометки"
+#
+# File: src/multilistclasses/libbottombar.py, line: 161
+msgid "Del item"
+msgstr "Удалить"
+#
+# File: src/multilistclasses/libmultilist.py, line: 109
+msgid "Sync"
+msgstr "Синхронизация"
+#
+# File: src/multilistclasses/libmultilist.py, line: 128
+# File: src/multilistclasses/libmultilist.py, line: 246
+msgid "Choose columns"
+msgstr "Выбрать столбцы"
+#
+# File: src/multilistclasses/libmultilist.py, line: 215
+msgid "Choose database file"
+msgstr "Выберите файл для хранения списков"
+#
+# File: src/multilistclasses/libmultilist.py, line: 219
+msgid "SQL history"
+msgstr ""
+#
+# File: src/multilistclasses/libmultilist.py, line: 223
+msgid "SQL optimize"
+msgstr ""
+#
+# File: src/multilistclasses/libmultilist.py, line: 227
+msgid "Sync items"
+msgstr "Синхронизация"
+#
+# File: src/multilistclasses/libmultilist.py, line: 233
+msgid "Quit"
+msgstr "Выход"
+#
+# File: src/multilistclasses/libmultilist.py, line: 238
+msgid "File"
+msgstr "Файл"
+#
+# File: src/multilistclasses/libmultilist.py, line: 250
+msgid "Rename Category"
+msgstr "Изменить название категории"
+#
+# File: src/multilistclasses/libmultilist.py, line: 254
+msgid "Rename List"
+msgstr "Изменить имя списка"
+#
+# File: src/multilistclasses/libmultilist.py, line: 258
+msgid "Tools"
+msgstr "Инструменты"
+#
+# File: src/multilistclasses/libmultilist.py, line: 264
+msgid "About"
+msgstr "О программе"
+#
+# File: src/multilistclasses/libmultilist.py, line: 268
+msgid "Help"
+msgstr "Справка"
+#
+# File: src/multilistclasses/libmultilist.py, line: 360
+msgid "Select SQL export file"
+msgstr ""
+#
+# File: src/multilistclasses/libmultilist.py, line: 390
+msgid "Choose your database file"
+msgstr "Выберите файл для хранения списков"
+#
+# File: src/multilistclasses/libselection.py, line: 66
+# File: src/multilistclasses/libselection.py, line: 69
+# File: src/multilistclasses/libselection.py, line: 129
+msgid "all"
+msgstr "всё"
+#
+# File: src/multilistclasses/libselection.py, line: 164
+msgid "List:"
+msgstr "Список:"
+#
+# File: src/multilistclasses/libselection.py, line: 171
+msgid "  Category:"
+msgstr "  Категория:"
+#
+# File: src/multilistclasses/libselection.py, line: 181
+msgid "  View:"
+msgstr "  Вид:"
+#
+# File: src/multilistclasses/libselection.py, line: 184
+msgid "All"
+msgstr "Все"
+#
+# File: src/multilistclasses/libselection.py, line: 186
+msgid "Active"
+msgstr "Исп."
+#
+# File: src/multilistclasses/libsqldialog.py, line: 54
+msgid "SQL History (the past two days):"
+msgstr ""
+#
+# File: src/multilistclasses/libsqldialog.py, line: 57
+msgid "Export"
+msgstr ""
+#
+# File: src/multilistclasses/libsqldialog.py, line: 73
+msgid "Date"
+msgstr "Дата"
+#
+# File: src/multilistclasses/libsqldialog.py, line: 74
+msgid "SQL"
+msgstr ""
+#
+# File: src/multilistclasses/libsqldialog.py, line: 75
+msgid "Parameter"
+msgstr "Параметры"
+#
+# File: src/multilistclasses/libsqldialog.py, line: 116
+msgid " (Reduced parameter) "
+msgstr ""
+#
+# File: src/multilistclasses/libsync.py, line: 44
+msgid "Sync process"
+msgstr "Синхронизация"
+#
+# File: src/multilistclasses/libsync.py, line: 49
+msgid "Sync process running...please wait"
+msgstr "Синхронизируются списки, подождите ..."
+#
+# File: src/multilistclasses/libsync.py, line: 51
+msgid "(this can take some minutes)"
+msgstr "(некоторое время)"
+#
+# File: src/multilistclasses/libsync.py, line: 91
+msgid "Query"
+msgstr "Запрос"
+#
+# File: src/multilistclasses/libsync.py, line: 198
+# File: src/multilistclasses/libsync.py, line: 290
+msgid "sync process running"
+msgstr "идёт синхронизация"
+#
+# File: src/multilistclasses/libsync.py, line: 243
+msgid "Syncserver running..."
+msgstr "Сервер синхронизации работает ..."
+#
+# File: src/multilistclasses/libsync.py, line: 251
+msgid "Sync server could not start. Please check IP and port."
+msgstr "Не удаётся стартовать Сервер синхронизации. Проверьте IP-адрес и порт."
+#
+# File: src/multilistclasses/libsync.py, line: 264
+msgid "Syncserver not running..."
+msgstr "Сервер синхронизации остановлен ..."
+#
+# File: src/multilistclasses/libsync.py, line: 281
+# File: src/multilistclasses/libsync.py, line: 317
+# File: src/multilistclasses/libsync.py, line: 325
+# File: src/multilistclasses/libsync.py, line: 332
+# File: src/multilistclasses/libsync.py, line: 424
+msgid "no sync process (at the moment)"
+msgstr "сейчас синхронизация не выполняется"
+#
+# File: src/multilistclasses/libsync.py, line: 319
+msgid "Synchronization successfully completed"
+msgstr "Синхронизация успешно завершенa"
+#
+# File: src/multilistclasses/libsync.py, line: 326
+msgid "The clocks are not synchronized between stations"
+msgstr "Согласуйте время между системами"
+#
+# File: src/multilistclasses/libsync.py, line: 333
+msgid "Sync failed, reason: "
+msgstr "Синхронизовать заметки не удалось по причине: "
+#
+# File: src/multilistclasses/libsync.py, line: 414
+msgid "Local SyncServer (port "
+msgstr "Местный сервер синхронизации (порт "
+#
+# File: src/multilistclasses/libsync.py, line: 407
+msgid "Start SyncServer"
+msgstr "Запуск сервера синхронизации"
+#
+# File: src/multilistclasses/libsync.py, line: 411
+msgid "Syncserver not running"
+msgstr "Сервер синхронизации остановлен"
+#
+# File: src/multilistclasses/libsync.py, line: 414
+msgid "RemoteSync-Server (Port "
+msgstr "Удалённый сервер синхронизации (порт "
+#
+# File: src/multilistclasses/libsync.py, line: 420
+msgid "Connect to remote SyncServer"
+msgstr "Установить соединение"
+#
+# File: src/multilistclasses/libview.py, line: 63
+msgid "Columns"
+msgstr "Столбцы"
diff --git a/remake b/remake
new file mode 100755 (executable)
index 0000000..67a93f3
--- /dev/null
+++ b/remake
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+make clean
+make 
+dpkg-buildpackage -rfakeroot 
+dpkg -i ../multilist_0.3.1_all.deb
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..b40d031
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,24 @@
+#!/usr/bin/env python2.5
+# -*- coding: utf-8 -*-
+#
+#
+
+from distutils.core import setup  
+
+setup(name='multilist',
+       version='0.3.1',
+       scripts=['src/multilist'],
+       packages=['multilistclasses'],
+       package_dir={'multilistclasses': 'src/multilistclasses'},
+       data_files = [
+                    ('share/icons/hicolor/26x26/hildon', ['data/low/multilist.png']),
+                   ('share/icons/hicolor/40x40/hildon', ['data/high/multilist.png']),
+                   ('share/icons/hicolor/scalable/hildon', ['data/scale/multilist.png']),
+                    #('share/pixmaps',             ['data/multilist.png']),                
+                    ('share/applications/hildon', ['data/multilist.desktop']),           
+                    ('share/dbus-1/services',     ['data/multilist.service']),      
+                    # I18N
+                    ('share/locale/de/LC_MESSAGES', ['locale/de/LC_MESSAGES/multilist.mo']),
+                    ('share/locale/ru/LC_MESSAGES', ['locale/ru/LC_MESSAGES/multilist.mo']),
+                    ]
+      ) 
diff --git a/src/copydb.py b/src/copydb.py
new file mode 100755 (executable)
index 0000000..e50f303
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+import time
+import sqlite3
+import shelve
+import uuid
+
+db="/home/chris/Documents/Schule/Schule/schulplaner/schuljahr200708enni.s3db"
+conn = sqlite3.connect(db)
+cur = conn.cursor()
+
+sql="UPDATE sync SET syncpartner=? WHERE syncpartner=?"
+cur.execute(sql,(str(uuid.uuid4()),"self")) #Eigene Id ändern feststellen
+conn.commit()
+
+sql="DELETE FROM sync WHERE syncpartner=?"
+cur.execute(sql,("self",))
+conn.commit()
+
+sql="INSERT INTO sync (syncpartner,uuid,pcdatum) VALUES (?,?,?)"
+cur.execute(sql,("self",str(uuid.uuid4()),int(time.time())))
+
+sql="UPDATE sync SET pcdatum=?"
+cur.execute(sql,(int(time.time()),))
+
+conn.commit()
\ No newline at end of file
diff --git a/src/multilist b/src/multilist
new file mode 100755 (executable)
index 0000000..aa7cc92
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+  
+"""
+    This file is part of Multilist.
+
+    Multilist 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 3 of the License, or
+    (at your option) any later version.
+
+    Multilist 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 Multilist.  If not, see <http://www.gnu.org/licenses/>.
+    
+    Copyright (C) 2008 Christoph Würstle
+"""
+
+import os
+import sys
+
+##
+## I18N
+##
+import locale
+import gettext
+gettext.install('multilist', unicode=1)
+                       
+if __name__ == "__main__":
+       
+       try:
+               import tempfile
+               import gtk
+               tmpdir=tempfile.gettempdir()
+               
+               os.mkdir(os.path.join(tmpdir, "multilist_lock"))
+       except OSError:
+               ## Failed: another instance is running
+               
+               mbox=gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_ERROR,gtk.BUTTONS_YES_NO,_("Multilist is already running. Start anyway? (Could result in db problems!)")) 
+               response=mbox.run() 
+               mbox.hide() 
+               mbox.destroy() 
+               if response==gtk.RESPONSE_NO:
+                       sys.exit()
+
+       try:
+               
+               from multilistclasses import libmultilist
+               #print dir(eggtimerclasses)
+               app = libmultilist.multilistclass() 
+               app.main() 
+   
+       finally:
+               ## Remove the PID file
+               # (...)
+               ## Delete directory
+               os.rmdir(os.path.join(tmpdir, "multilist_lock"))
+               
+       
diff --git a/src/multilistclasses/__init__.py b/src/multilistclasses/__init__.py
new file mode 100755 (executable)
index 0000000..105eaaf
--- /dev/null
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+  
+"""
+    This file is part of Multilist.
+
+    Multilist 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 3 of the License, or
+    (at your option) any later version.
+
+    Multilist 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 Multilist.  If not, see <http://www.gnu.org/licenses/>.
+    
+    Copyright (C) 2008 Christoph Würstle
+""" 
diff --git a/src/multilistclasses/libbottombar.py b/src/multilistclasses/libbottombar.py
new file mode 100644 (file)
index 0000000..654c72d
--- /dev/null
@@ -0,0 +1,166 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+  
+"""
+    This file is part of Multilist.
+
+    Multilist 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 3 of the License, or
+    (at your option) any later version.
+
+    Multilist 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 Multilist.  If not, see <http://www.gnu.org/licenses/>.
+    
+    Copyright (C) 2008 Christoph Würstle
+"""
+
+
+import gobject
+import time
+import logging
+
+import gtk
+
+class Bottombar(gtk.HBox):
+       
+       __gsignals__ = {
+               'changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,(gobject.TYPE_STRING,gobject.TYPE_STRING)),
+               #'changedCategory': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,(gobject.TYPE_STRING,gobject.TYPE_STRING))
+       }
+
+               
+       def new_item(self,widget=None,data1=None,data2=None):
+               dialog = gtk.Dialog(_("New item name:"),None,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+               
+               dialog.set_position(gtk.WIN_POS_CENTER)
+               entryKlasse=gtk.Entry()
+               entryKlasse.set_text("")
+               
+               dialog.vbox.pack_start(entryKlasse, True, True, 0)
+               
+               dialog.vbox.show_all()
+               #dialog.set_size_request(400,300)
+
+               if dialog.run() == gtk.RESPONSE_ACCEPT:
+                       #logging.info("new category name "+entryKlasse.get_text())
+                       #self.view.liststorehandler.rename_category(entryKlasse.get_text())
+                       self.view.liststorehandler.add_row(entryKlasse.get_text())
+               dialog.destroy()
+
+               
+       def del_item(self,widget=None,data1=None,data2=None):
+               path, col = self.view.treeview.get_cursor()
+               if path!=None:
+                       mbox=gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_QUESTION,gtk.BUTTONS_YES_NO,_("Delete current item?")) 
+                       response=mbox.run() 
+                       mbox.hide() 
+                       mbox.destroy() 
+                       if response==gtk.RESPONSE_YES:
+                               self.view.del_active_row()
+               else:
+                       mbox=gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_ERROR,gtk.BUTTONS_OK,_("No item selected!")) 
+                       response=mbox.run() 
+                       mbox.hide() 
+                       mbox.destroy()                  
+               
+               
+       def checkout_items(self,widget=None,data1=None,data2=None):
+               #self.view.del_active_row()
+               mbox=gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_QUESTION,gtk.BUTTONS_YES_NO,(_("Really checkout all items?"))) 
+               response=mbox.run() 
+               mbox.hide() 
+               mbox.destroy() 
+               if response==gtk.RESPONSE_YES:
+                       self.view.liststorehandler.checkout_rows()
+                       #n=len(self.view.liststorehandler.get_liststore())
+                       #for i in range(n):
+                       #       self.view.liststorehandler.checkout_rows()
+                       #       #print i
+                       
+       def search_list(self,widget=None,data1=None,data2=None):
+               self.view.liststorehandler.get_liststore(widget.get_text())
+                       
+       
+       def rename_category(self,widget=None,data1=None,data2=None):
+               dialog = gtk.Dialog(_("New category name:"),None,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+               
+               dialog.set_position(gtk.WIN_POS_CENTER)
+               entryKlasse=gtk.Entry()
+               entryKlasse.set_text(self.view.liststorehandler.selection.get_category())
+               
+               dialog.vbox.pack_start(entryKlasse, True, True, 0)
+               
+               dialog.vbox.show_all()
+               #dialog.set_size_request(400,300)
+
+               if dialog.run() == gtk.RESPONSE_ACCEPT:
+                       logging.info("new category name "+entryKlasse.get_text())
+                       self.view.liststorehandler.rename_category(entryKlasse.get_text())
+               else:
+                       #print "Cancel",res
+                       pass
+               dialog.destroy()
+               
+               
+       def rename_list(self,widget=None,data1=None,data2=None):
+               dialog = gtk.Dialog(_("New list name:"),None,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,(gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT,gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+               
+               dialog.set_position(gtk.WIN_POS_CENTER)
+               entryKlasse=gtk.Entry()
+               entryKlasse.set_text(self.view.liststorehandler.selection.get_list())
+               
+               dialog.vbox.pack_start(entryKlasse, True, True, 0)
+               
+               dialog.vbox.show_all()
+               #dialog.set_size_request(400,300)
+
+               if dialog.run() == gtk.RESPONSE_ACCEPT:
+                       logging.info("new list name "+entryKlasse.get_text())
+                       self.view.liststorehandler.rename_list(entryKlasse.get_text())
+               else:
+                       #print "Cancel",res
+                       pass
+               dialog.destroy()
+       
+       def __init__(self,db,view,isHildon):
+               gtk.HBox.__init__(self,homogeneous=False, spacing=3)
+               
+               self.db=db
+               self.isHildon=isHildon
+               self.view=view
+               
+               logging.info("libBottomBar, init")
+                       
+               
+               button=gtk.Button(_("New item"))
+               button.connect("clicked",self.new_item)
+               self.pack_start(button, expand=False, fill=True, padding=0)
+               
+               label=gtk.Label("  ")
+               self.pack_start(label, expand=True, fill=True, padding=0)
+               
+               label=gtk.Label(_("Search:"))
+               self.pack_start(label, expand=False, fill=True, padding=0)
+               searchEntry=gtk.Entry()
+               searchEntry.connect("changed",self.search_list)
+               self.pack_start(searchEntry, expand=True, fill=True, padding=0)
+               
+               label=gtk.Label("  ")
+               self.pack_start(label, expand=True, fill=True, padding=0)
+               
+               button=gtk.Button(_("Checkout all items"))
+               button.connect("clicked",self.checkout_items)
+               self.pack_start(button, expand=False, fill=True, padding=0)
+               
+               button=gtk.Button(_("Del item"))
+               button.connect("clicked",self.del_item)
+               self.pack_start(button, expand=False, fill=True, padding=0)
+               
+               
+               
diff --git a/src/multilistclasses/libliststorehandler.py b/src/multilistclasses/libliststorehandler.py
new file mode 100644 (file)
index 0000000..933a90c
--- /dev/null
@@ -0,0 +1,218 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+  
+"""
+    This file is part of Multilist.
+
+    Multilist 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 3 of the License, or
+    (at your option) any later version.
+
+    Multilist 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 Multilist.  If not, see <http://www.gnu.org/licenses/>.
+    
+    Copyright (C) 2008 Christoph Würstle
+"""
+
+import gtk
+import gobject
+import libspeichern
+import logging
+
+
+class Liststorehandler():
+       
+       def get_unitsstore(self):
+               if (self.unitsstore==None):
+                       self.unitsstore=gtk.ListStore(str, str, str,str,str,  str,str, str, str,str, str, str,str)
+               self.unitsstore.clear()
+               #row(3) quantities
+               #row 4 units
+               #row 6 priority
+               self.unitsstore.append(["-1","-1","","","","","","","","","","",""])    
+               self.unitsstore.append(["-1","-1","","1","g","","0","","","","","",""]) 
+               self.unitsstore.append(["-1","-1","","2","kg","","1","","","","","",""])        
+               self.unitsstore.append(["-1","-1","","3","liter","","2","","","","","",""])     
+               self.unitsstore.append(["-1","-1","","4","packs","","3","","","","","",""])     
+               self.unitsstore.append(["-1","-1","","5","","","4","","","","","",""])  
+               self.unitsstore.append(["-1","-1","","6","","","5","","","","","",""])  
+               self.unitsstore.append(["-1","-1","","7","","","6","","","","","",""])  
+               self.unitsstore.append(["-1","-1","","8","","","7","","","","","",""])  
+               self.unitsstore.append(["-1","-1","","9","","","8","","","","","",""])  
+               self.unitsstore.append(["-1","-1","","","","","9","","","","","",""])   
+               
+               return self.unitsstore
+               
+               
+       
+       
+       def get_liststore(self,titlesearch=""):
+               if (self.liststore==None):
+                       self.liststore=gtk.ListStore(str, str, str,str,str,  str,str, str, str,str, str, str,str)
+               self.liststore.clear()
+               
+               titlesearch=titlesearch+"%"
+               
+               
+               if (self.selection.get_status()=="0"): #only 0 and 1 (active items)
+                       sql="SELECT uid,status,title,quantitiy,unit,price,priority,date,private,stores,note,custom1,custom2 FROM items WHERE list=? AND category LIKE ? AND status>=? AND title like ? ORDER BY category, status, title"
+                       rows=self.db.ladeSQL(sql,(self.selection.get_list(),self.selection.get_category(True),self.selection.get_status(),titlesearch))
+               else:
+                       sql="SELECT uid,status,title,quantitiy,unit,price,priority,date,private,stores,note,custom1,custom2 FROM items WHERE list=? AND category LIKE ? AND title LIKE ? ORDER BY category, title ASC"
+                       rows=self.db.ladeSQL(sql,(self.selection.get_list(),self.selection.get_category(True),titlesearch))
+                       
+               #print rows
+               if ((rows!=None)and(len(rows)>0)):
+                       for row in rows:
+                               uid,status,title,quantitiy,unit,price,priority,date,private,stores,note,custom1,custom2 = row
+                               if unit==None:
+                                       pass
+                                       #unit=""
+                               self.liststore.append([uid,status,title,quantitiy,unit,price,priority,date,private,stores,note,custom1,custom2])
+                       #else:
+                       #self.liststore.append(["-1","-1",""," ","","","","","","","","",""])   
+               #import uuid
+               #self.liststore.append(["-1","-1","","","","","","","","","","",""])
+               
+               return self.liststore
+       
+       
+       def emptyValueExists(self):
+               for child in self.liststore:
+                       if child[2]=="":
+                               return True
+               return False
+               
+       
+
+       def update_row(self,irow,icol,new_text):
+               #print "liststore 1"
+               #for x in self.liststore:
+               #       print x[0],x[2]
+               
+               if (irow>-1)and(self.liststore[irow][0]!="-1")and(self.liststore[irow][0]!=None):
+                       sql = "UPDATE items SET "+self.collist[icol]+"=? WHERE uid=?"
+                       self.db.speichereSQL(sql,(new_text,self.liststore[irow][0]),rowid=self.liststore[irow][0])
+                       
+                       logging.info("Updated row: "+self.collist[icol]+" new text "+new_text+" Titel: "+str(self.liststore[irow][2])+" with uid "+str(self.liststore[irow][0]))
+                       
+                       self.liststore[irow][icol]=new_text 
+               else:
+                       logging.warning("update_row: row does not exist")
+                       return
+                       #if (self.emptyValueExists()==True)and(icol<2):
+                       #       #print "letzter Eintrag ohne inhalt"
+                       #       return
+                       
+               #print "liststore 2"
+               #for x in self.liststore:
+               #       print x[0],x[2]
+               
+               
+       def checkout_rows(self):
+               sql = "UPDATE items SET status=? WHERE list=? AND category LIKE ? AND status=?"
+               self.db.speichereSQL(sql,("-1",self.selection.get_list(),self.selection.get_category(True),"1"))
+               for i in range(len(self.liststore)):
+                       if self.liststore[i][1]=="1":
+                               self.liststore[i][1]="-1"
+                       
+                       
+               
+               
+       def add_row(self,title=""):
+               #self.update_row(-1,1,"-1")
+               #for x in self.liststore:
+               #       print x[0],x[2]
+               status=self.selection.get_status()
+               import uuid
+               uid=str(uuid.uuid4())
+               sql = "INSERT INTO items (uid,list,category,status, title) VALUES (?,?,?,?,?)"
+               self.db.speichereSQL(sql,(uid,self.selection.get_list(),self.selection.get_category(),status,title),rowid=uid)
+               logging.info("Insertet row: status = "+status+" with uid "+str(uid))
+                       #self.liststore[irow][0]=str(uuid.uuid4())
+                       
+               self.liststore.append([uid,status,title," ","","","","","","","","",""])
+               self.selection.comboLists_check_for_update()
+               #       if (irow>-1):
+               #               self.liststore[irow][icol]=new_text
+               #               self.liststore[irow][0]=uid
+               #       else:
+               #               self.liststore.append([uid,"-1",""," ","","","","","","","","",""])
+                               #print "xy",self.liststore[len(self.liststore)-1][0]
+                       #Check if a new list/category is opened
+               #       self.selection.comboLists_check_for_update()
+               
+       def del_row(self,irow,row_iter):
+               uid=self.liststore[irow][0]
+               self.liststore.remove(row_iter)
+               sql = "DELETE FROM items WHERE uid=?"
+               self.db.speichereSQL(sql,(uid,))
+               #
+               
+               
+       def get_colname(self,i):
+               if i<len(self.collist):
+                       return self.collist[i]
+               else:
+                       return None
+               
+       def get_colcount(self):
+               return len(self.collist)
+
+       
+       def rename_category(self,new_name):
+               sql = "UPDATE items SET category=? WHERE list=? AND category=?"
+               self.db.speichereSQL(sql,(new_name,self.selection.get_list(),self.selection.get_category()))
+               self.selection.comboList_changed()
+               self.selection.set_category(new_name)
+                               
+       def rename_list(self,new_name):
+               sql = "UPDATE items SET list=? WHERE list=?"
+               self.db.speichereSQL(sql,(new_name,self.selection.get_list(),))
+               self.selection.load()
+               self.selection.set_list(new_name)       
+       
+       
+       #def update_category(self,widget=None,data=None,data2=None,data3=None):
+       #       self.get_liststore()
+               
+       def update_list(self,widget=None,data=None,data2=None,data3=None):
+               self.get_liststore()
+               
+       def __init__(self,db,selection):
+               self.db=db
+               self.liststore=None
+               self.unitsstore=None
+               self.selection=selection
+               self.collist=("uid","status","title","quantitiy","unit","price","priority","date","private","stores","note","custom1","custom2")
+               
+               #sql="DROP TABLE items"
+               #self.db.speichereSQL(sql)
+               
+               sql = "CREATE TABLE items (uid TEXT, list TEXT, category TEXT, status TEXT, title TEXT, quantitiy TEXT, unit TEXT, price TEXT, priority TEXT, date TEXT, pcdate TEXT, private TEXT, stores TEXT, note TEXT, custom1 TEXT, custom2 TEXT)"
+               self.db.speichereSQL(sql)
+               
+               
+               self.selection.load()
+               self.selection.connect("changed",self.update_list)
+               #self.selection.connect("changedCategory",self.update_category)
+               
+
+               """
+               sql = "INSERT INTO items (uid,list,category,title) VALUES (?,?,?,?)"
+               import uuid
+               self.db.speichereSQL(sql,(str(uuid.uuid4()),"default","default","atitel1"))
+               self.db.speichereSQL(sql,(str(uuid.uuid4()),"default","default","btitel2"))
+               self.db.speichereSQL(sql,(str(uuid.uuid4()),"default","default","ctitel3"))
+               
+               print "inserted"
+               """
+
+               
+               
\ No newline at end of file
diff --git a/src/multilistclasses/libmultilist.py b/src/multilistclasses/libmultilist.py
new file mode 100755 (executable)
index 0000000..69321c6
--- /dev/null
@@ -0,0 +1,401 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+  
+"""
+    This file is part of Multilist.
+
+    Multilist 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 3 of the License, or
+    (at your option) any later version.
+
+    Multilist 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 Multilist.  If not, see <http://www.gnu.org/licenses/>.
+    
+    Copyright (C) 2008 Christoph Würstle
+"""
+
+#/scratchbox/login
+#Xephyr :2 -host-cursor -screen 800x480x16 -dpi 96 -ac
+#af-sb-init.sh start
+#run-standalone.sh ./eggtimer.py
+#
+#https://stage.maemo.org/svn/maemo/projects/haf/trunk/
+#http://www.maemo.org/platform/docs/pymaemo/pyosso_context.html
+#http://maemo-hackers.org/apt/
+
+import time
+import os
+import sys
+import logging
+
+try:
+        import gtk
+        #import gtk.glade
+except:
+       print "gtk import failed"
+        sys.exit(1)
+       
+try:
+       import hildon
+       import osso
+       isHildon=True
+except:
+       isHildon=False
+       class hildon():
+               def __init__(self):
+                       print "PseudoClass hildon"
+               class Program():
+                       def __init__(self):
+                               print "PseudoClass hildon.Program"
+
+#import libextdatei
+import libspeichern
+import libsqldialog
+import libselection
+import libview
+import libliststorehandler
+import libsync
+import libbottombar
+
+version = "0.3.0"
+app_name = "multilist"
+
+               
+       
+
+class multilistclass(hildon.Program):
+               
+       def on_key_press(self, widget, event, *args):
+               #Hildon Fullscreen Modus
+               if (isHildon==False): return
+               if event.keyval == gtk.keysyms.F6: 
+                       # The "Full screen" hardware key has been pressed 
+                       if self.window_in_fullscreen: 
+                               self.window.unfullscreen () 
+                       else: 
+                               self.window.fullscreen () 
+               
+       def on_window_state_change(self, widget, event, *args): 
+               if event.new_window_state & gtk.gdk.WINDOW_STATE_FULLSCREEN: 
+                       self.window_in_fullscreen = True 
+               else: 
+                       self.window_in_fullscreen = False 
+
+       
+       def speichereAlles(self,data=None,data2=None):
+               logging.info("Speichere alles")
+
+
+       def ladeAlles(self,data=None,data2=None):
+               logging.info("Lade alles")
+               
+       def beforeSync(self,data=None,data2=None):
+               logging.info("Lade alles")
+
+
+       def sync_finished(self,data=None,data2=None):
+               self.selection.comboList_changed()
+               self.selection.comboCategory_changed()
+               self.liststorehandler.update_list()
+               
+       
+       def prepare_sync_dialog(self):
+               self.sync_dialog = gtk.Dialog(_("Sync"),None,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+               
+               self.sync_dialog.set_position(gtk.WIN_POS_CENTER)
+               sync=libsync.Sync(self.db,self.window,50503)
+               sync.connect("syncFinished",self.sync_finished)
+               self.sync_dialog.vbox.pack_start(sync, True, True, 0)
+               self.sync_dialog.set_size_request(500,350)
+               self.sync_dialog.vbox.show_all()
+               sync.connect("syncFinished",self.sync_finished)
+       
+       
+       def sync_notes(self,widget=None,data=None):
+               if self.sync_dialog==None:
+                       self.prepare_sync_dialog()
+               self.sync_dialog.run()
+               self.sync_dialog.hide()
+
+
+       def show_columns_dialog(self,widget=None,data=None):
+               col_dialog = gtk.Dialog(_("Choose columns"),self.window,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+               
+               col_dialog.set_position(gtk.WIN_POS_CENTER)
+               cols=libview.Columns_dialog(self.db,self.liststorehandler)
+
+               col_dialog.vbox.pack_start(cols, True, True, 0)
+               col_dialog.set_size_request(500,350)
+               col_dialog.vbox.show_all()
+               
+               resp=col_dialog.run()
+               col_dialog.hide()
+               if resp==gtk.RESPONSE_ACCEPT:
+                       logging.info("changing columns")
+                       cols.save_column_setting()
+                       self.view.reload_view()
+                       #children=self.vbox.get_children()
+                       #while len(children)>1:
+                       #       self.vbox.remove(children[1])
+
+                       #self.vbox.pack_end(self.bottombar, expand=True, fill=True, padding=0)
+                       #self.vbox.pack_end(view, expand=True, fill=True, padding=0)
+                       #self.vbox.pack_end(self.selection, expand=False, fill=True, padding=0)
+                       
+
+               col_dialog.destroy()
+               
+
+
+       def __init__(self):
+               home_dir = os.path.expanduser('~')
+               dblog=os.path.join(home_dir, "multilist.log") 
+               logging.basicConfig(level=logging.INFO,format='%(asctime)s %(levelname)-8s %(message)s', datefmt='%a, %d %b %Y %H:%M:%S', filename=dblog,filemode='a')
+               #logging.getLogger('').addHandler(console)
+               
+               # define a Handler which writes INFO messages or higher to the sys.stderr
+               console = logging.StreamHandler()
+               console.setLevel(logging.INFO)
+               # set a format which is simpler for console use
+               formatter = logging.Formatter('%(asctime)s  %(levelname)-8s %(message)s')
+               # tell the handler to use this format
+               console.setFormatter(formatter)
+               # add the handler to the root logger
+               logging.getLogger('').addHandler(console)
+               
+               logging.info('Starting Multilist')
+               
+               if (isHildon==True): 
+                       logging.info('Hildon erkannt, rufe Hildon constructor auf')
+                       hildon.Program.__init__(self)
+                               
+                #Get the Main Window, and connect the "destroy" event
+               if (isHildon==False):
+                       self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
+                       self.window.set_default_size(700,500)
+               else:
+                       self.window = hildon.Window()
+                       self.add_window(self.window)
+                       
+               #print "1b: ",time.clock() 
+               
+                if (self.window):
+                       self.window.connect("delete_event", self.delete_event)
+                       self.window.connect("destroy", self.destroy)
+                       self.window.set_title("Multilist")
+                       
+                       
+                       
+                       if (isHildon==True): self.window.connect("key-press-event", self.on_key_press) 
+                       self.window.connect("window-state-event", self.on_window_state_change) 
+                       self.window_in_fullscreen = False #The window isn't in full screen mode initially.
+
+
+               self.db=libspeichern.Speichern()
+               
+               self.selection=libselection.Selection(self.db,isHildon)
+               self.liststorehandler=libliststorehandler.Liststorehandler(self.db,self.selection)
+               self.view=libview.View(self.db,self.liststorehandler,self.window)
+               self.bottombar=libbottombar.Bottombar(self.db,self.view,isHildon)
+               
+               #Haupt vbox für alle Elemente
+               self.vbox = gtk.VBox(homogeneous=False, spacing=0)
+               
+               
+               
+               #Menue
+               dateimenu = gtk.Menu()
+               
+               menu_items = gtk.MenuItem(_("Choose database file"))
+               dateimenu.append(menu_items)
+               menu_items.connect("activate", self.select_db_dialog, None)
+               
+               menu_items = gtk.MenuItem(_("SQL history"))
+               dateimenu.append(menu_items)
+               menu_items.connect("activate", self.view_sql_history, None)
+               
+               menu_items = gtk.MenuItem(_("SQL optimize"))
+               dateimenu.append(menu_items)
+               menu_items.connect("activate", self.optimizeSQL, None)
+               
+               menu_items = gtk.MenuItem(_("Sync items"))
+               self.prepare_sync_dialog()
+               dateimenu.append(menu_items)
+               menu_items.connect("activate", self.sync_notes, None)
+               
+               
+               menu_items = gtk.MenuItem(_("Quit"))
+               dateimenu.append(menu_items)
+               menu_items.connect("activate", self.destroy, None)
+               #menu_items.show()
+               
+               datei_menu = gtk.MenuItem(_("File"))
+               datei_menu.show()
+               datei_menu.set_submenu(dateimenu)
+               
+               
+               toolsmenu = gtk.Menu()
+               
+               
+               menu_items = gtk.MenuItem(_("Choose columns"))
+               toolsmenu.append(menu_items)
+               menu_items.connect("activate", self.show_columns_dialog, None)
+               
+               menu_items = gtk.MenuItem(_("Rename Category"))
+               toolsmenu.append(menu_items)
+               menu_items.connect("activate", self.bottombar.rename_category, None)
+               
+               menu_items = gtk.MenuItem(_("Rename List"))
+               toolsmenu.append(menu_items)
+               menu_items.connect("activate", self.bottombar.rename_list, None)
+               
+               tools_menu = gtk.MenuItem(_("Tools"))
+               tools_menu.show()
+               tools_menu.set_submenu(toolsmenu)
+               
+               
+               hilfemenu = gtk.Menu()
+               menu_items = gtk.MenuItem(_("About"))
+               hilfemenu.append(menu_items)
+               menu_items.connect("activate", self.show_about, None)
+               
+               hilfe_menu = gtk.MenuItem(_("Help"))
+               hilfe_menu.show()
+               hilfe_menu.set_submenu(hilfemenu)
+               
+               menu_bar = gtk.MenuBar()
+               menu_bar.show()
+               menu_bar.append (datei_menu)
+               menu_bar.append (tools_menu)
+               # unten -> damit als letztes menu_bar.append (hilfe_menu)
+               #Als letztes menü
+               menu_bar.append (hilfe_menu)
+               
+               if (isHildon==True):
+                       menu = gtk.Menu() 
+                       for child in menu_bar.get_children():
+                               child.reparent(menu) 
+                       self.window.set_menu(menu)
+                       menu_bar.destroy()
+               else:
+                       self.vbox.pack_start(menu_bar, False, False, 2)
+               
+               
+               
+
+               #add to vbox below (to get it on top)
+               
+               
+               
+               self.vbox.pack_end(self.bottombar, expand=False, fill=True, padding=0)
+               self.vbox.pack_end(self.view, expand=True, fill=True, padding=0)
+               self.vbox.pack_end(self.selection, expand=False, fill=True, padding=0)
+               
+
+               if (isHildon==True): self.osso_c = osso.Context(app_name, version, False)
+               self.window.add(self.vbox)
+               self.window.show_all()
+               
+               #print "8a"
+               self.ladeAlles()
+               
+               
+               #print "9: ",time.clock()
+                       
+       def main(self):
+               gtk.main()
+               if (isHildon==True): self.osso_c.close()
+               
+       def destroy(self, widget=None, data=None):
+               self.speichereAlles()
+               self.db.close()
+               gtk.main_quit()
+               
+               
+       def delete_event(self, widget, event, data=None):
+               #print "delete event occurred"
+               return False
+       
+       def dlg_delete(self,widget,event,data=None):
+               return False
+
+
+       def show_about(self, widget=None,data=None):
+               dialog = gtk.AboutDialog()
+               dialog.set_position(gtk.WIN_POS_CENTER)
+               dialog.set_name(app_name)
+               dialog.set_version(version)
+               dialog.set_copyright("")
+               dialog.set_website("http://axique.de/f=Multilist")
+               comments = "%s is a program to handle multiple lists." % app_name
+               dialog.set_comments(comments)        
+               dialog.run()     
+               dialog.destroy()
+       
+       def on_info1_activate(self,menuitem):
+               self.show_about(menuitem)
+
+  
+       def view_sql_history(self,widget=None,data=None,data2=None):
+               sqldiag=libsqldialog.sqlDialog(self.db)
+               res=sqldiag.run()
+               sqldiag.hide()
+               if res==444:
+                       logging.info("exporting sql")
+                       
+                       if (isHildon==False):
+                               dlg = gtk.FileChooserDialog(parent = self.window, action = gtk.FILE_CHOOSER_ACTION_SAVE)
+                               dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
+                               dlg.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK)
+                       else:
+                               #dlg = hildon.FileChooserDialog(parent = self.window, action = gtk.FILE_CHOOSER_ACTION_SAVE)
+                               dlg=hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_SAVE)
+                       
+                       dlg.set_title(_("Select SQL export file"))
+                       if dlg.run() == gtk.RESPONSE_OK:
+                               fileName = dlg.get_filename()
+                               dlg.destroy()
+                               sqldiag.exportSQL(fileName)
+                       else:
+                               dlg.destroy()
+                               
+               sqldiag.destroy()
+
+               
+       def optimizeSQL(self,widget=None,data=None,data2=None): 
+               #optimiere sql
+               self.db.speichereSQL("VACUUM",log=False)
+               
+  
+
+  
+       def select_db_dialog(self,widget=None,data=None,data2=None):
+               if (isHildon==False):
+                       dlg = gtk.FileChooserDialog(parent = self.window, action = gtk.FILE_CHOOSER_ACTION_SAVE)
+                       dlg.add_button( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
+                       dlg.add_button( gtk.STOCK_OK, gtk.RESPONSE_OK)
+               else:
+                       #dlg = hildon.FileChooserDialog(parent = self.window, action = gtk.FILE_CHOOSER_ACTION_SAVE)
+                       dlg=hildon.FileChooserDialog(self.window, gtk.FILE_CHOOSER_ACTION_SAVE)
+                       
+               
+               if self.db.ladeDirekt('datenbank'):
+                       dlg.set_filename(self.db.ladeDirekt('datenbank'))
+               dlg.set_title(_("Choose your database file"))
+               if dlg.run() == gtk.RESPONSE_OK:
+                       fileName = dlg.get_filename()
+                       self.db.speichereDirekt('datenbank',fileName)
+                       self.speichereAlles()
+                       self.db.openDB()
+                       self.ladeAlles()
+               dlg.destroy()
+               
+               
+               
+
diff --git a/src/multilistclasses/libselection.py b/src/multilistclasses/libselection.py
new file mode 100644 (file)
index 0000000..9ab8651
--- /dev/null
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+  
+"""
+    This file is part of Multilist.
+
+    Multilist 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 3 of the License, or
+    (at your option) any later version.
+
+    Multilist 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 Multilist.  If not, see <http://www.gnu.org/licenses/>.
+    
+    Copyright (C) 2008 Christoph Würstle
+"""
+
+
+import gobject
+import time
+import logging
+
+import gtk
+
+class Selection(gtk.HBox):
+       
+       __gsignals__ = {
+               'changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,(gobject.TYPE_STRING,gobject.TYPE_STRING)),
+               #'changedCategory': (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,(gobject.TYPE_STRING,gobject.TYPE_STRING))
+       }
+
+       def load(self):
+               model=self.comboList.get_model()
+               model.clear()
+               #self.comboList.remove(0)
+                       
+               
+               sql="SELECT DISTINCT list FROM items ORDER BY list"
+               rows=self.db.ladeSQL(sql)
+               if ((rows!=None)and(len(rows)>0)):
+                       for row in rows:
+                               self.comboList.append_text(row[0])
+               else:
+                       self.comboList.append_text("default")
+                       
+               s=self.db.ladeDirekt("comboListText")
+               if s!="":
+                       self.comboList.get_child().set_text(s)
+               else:
+                       self.comboList.set_active(0)
+
+       def comboList_changed(self, widget=None, data=None):
+               #self.comboCategory.set_model(None)
+               #print "reload categories"
+               while len(self.comboCategory.get_model())>0:
+                       self.comboCategory.remove_text(0)
+               
+               sql="SELECT DISTINCT category FROM items WHERE list=? ORDER BY category"
+               rows=self.db.ladeSQL(sql,(self.get_list(),))
+               
+               self.comboCategory.append_text(_("all"))
+               if ((rows!=None)and(len(rows)>0)):
+                       for row in rows:
+                               if (row[0]!=_("all")):
+                                       self.comboCategory.append_text(row[0])
+               
+               s=self.db.ladeDirekt("comboCategoryText"+self.comboList.get_child().get_text())
+               if len(s)>0:
+                       self.comboCategory.get_child().set_text(s)
+               else:
+                       self.comboCategory.set_active(0)
+               
+               self.emit("changed","list","")
+               self.db.speichereDirekt("comboListText",self.comboList.get_child().get_text())
+               
+
+               
+       def comboCategory_changed(self, widget=None, data=None):
+               #logging.info("Klasse geaendert zu ")
+               #self.hauptRegister.set_current_page(0)
+               self.emit("changed","category","")
+               if self.comboCategory.get_active()>-1:
+                       self.db.speichereDirekt("comboCategoryText"+self.comboList.get_child().get_text(),self.comboCategory.get_child().get_text())
+               
+       def radioActive_changed(self, widget, data=None):
+               self.emit("changed","radio","")
+
+       def comboLists_check_for_update(self):
+               if self.comboCategory.get_active()==-1:
+                       model=self.comboCategory.get_model()
+                       found=False
+                       cat=self.get_category()
+                       for x in model:
+                               if x[0]==cat:
+                                       found=True
+                       if found==False:
+                               self.comboCategory.append_text(self.get_category())
+                               self.comboCategory.set_active(len(self.comboCategory.get_model())-1)
+               if self.comboList.get_active()==-1:
+                       model=self.comboList.get_model()
+                       found=False
+                       list=self.get_list()
+                       for x in model:
+                               if x[0]==list:
+                                       found=True
+                       if found==False:
+                               self.comboList.append_text(self.get_list())
+                               self.comboList.set_active(len(self.comboList.get_model())-1)
+               
+
+       def lade(self):
+               logging.warning("Laden der aktuellen position noch nicht implementiert")
+
+       
+       def speichere(self):
+               logging.warning("Speichern der aktuellen position noch nicht implementiert")
+       
+       
+       def getIsHildon(self):
+               return self.isHildon
+       
+       def get_category(self,select=False):
+               s=self.comboCategory.get_child().get_text()
+               if s==_("all"):
+                       if (select==False):
+                               return "undefined"
+                       else:
+                               return "%"
+               else:
+                       return s
+       def set_category(self,category):
+               self.comboCategory.get_child().set_text(category)
+                       
+       def set_list(self,listname):
+               self.comboList.get_child().set_text(listname)
+               
+       def get_list(self):
+               return self.comboList.get_child().get_text()
+
+
+       
+       def get_status(self):
+               #return self.comboCategory.get_child().get_text()
+               if self.radio_all.get_active()==True:
+                       return "-1"
+               else:
+                       return "0"
+               
+       
+       def __init__(self,db,isHildon):
+               gtk.HBox.__init__(self,homogeneous=False, spacing=3)
+               
+               self.db=db
+               self.isHildon=isHildon
+               
+               logging.info("libSelection, init")
+                       
+               
+               label=gtk.Label(_("List:"))
+               self.pack_start(label, expand=False, fill=True, padding=0)
+               
+               self.comboList = gtk.combo_box_entry_new_text()
+               self.comboList.set_size_request(180,-1)
+               self.pack_start(self.comboList, expand=False, fill=True, padding=0)
+                       
+               label=gtk.Label(_("  Category:"))
+               self.pack_start(label, expand=False, fill=True, padding=0)
+               
+               self.comboCategory = gtk.combo_box_entry_new_text()
+               self.comboCategory.set_size_request(180,-1)
+               self.pack_start(self.comboCategory, expand=False, fill=True, padding=0)
+               
+               self.comboList.connect("changed", self.comboList_changed, None)
+               self.comboCategory.connect("changed", self.comboCategory_changed, None)
+               
+               label=gtk.Label(_("  View:"))
+               self.pack_start(label, expand=False, fill=True, padding=0)
+               
+               self.radio_all=gtk.RadioButton(group=None, label=_("All"), use_underline=True)
+               self.pack_start(self.radio_all, expand=False, fill=True, padding=0)
+               self.radio_active=gtk.RadioButton(group=self.radio_all, label=_("Active"), use_underline=True)
+               self.pack_start(self.radio_active, expand=False, fill=True, padding=0)
+               self.radio_all.connect("toggled",self.radioActive_changed, None)
+               
+               
+               
diff --git a/src/multilistclasses/libspeichern.py b/src/multilistclasses/libspeichern.py
new file mode 100644 (file)
index 0000000..e887340
--- /dev/null
@@ -0,0 +1,191 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+  
+"""
+    This file is part of Multilist.
+
+    Multilist 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 3 of the License, or
+    (at your option) any later version.
+
+    Multilist 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 Multilist.  If not, see <http://www.gnu.org/licenses/>.
+    
+    Copyright (C) 2008 Christoph Würstle
+"""
+
+import time
+import sqlite3
+import shelve
+import sys
+import string
+import shutil
+import os
+import logging
+
+class Speichern():
+       def speichereDirekt(self,schluessel,daten):
+               self.d[schluessel]=daten
+               logging.info("speichereDirekt "+str(schluessel)+" "+str(daten)+" lesen: "+str(self.d[schluessel]))
+
+       
+       def ladeDirekt(self,schluessel,default=""):
+               #print "ladeDirekt",schluessel, "Schluessel vorhanden",self.d.has_key(schluessel)
+               if (self.d.has_key(schluessel)==True):
+                       data=self.d[schluessel]
+                       #print data
+                       return data
+               else:
+                       return default
+                               
+                               
+       def speichereSQL(self,sql,tupel=None,commit=True,host="self",log=True,pcdatum=None,rowid=""):
+               #print "speichereSQL:",sql,tupel
+               try:
+                       programSQLError=True
+                       if (tupel==None):
+                               self.cur.execute(sql)
+                       else:
+                               self.cur.execute(sql,tupel)
+                       programSQLError=False   
+                       
+                       #print int(time.time()), sql, pickle.dumps(tupel), host
+                       if (log==True):
+                               strtupel=[]
+                               if (tupel!=None):
+                                       for t in tupel:
+                                               strtupel.append(str(t))
+
+
+                               if pcdatum==None: pcdatum=int(time.time())
+                               self.cur.execute("INSERT INTO logtable ( pcdatum,sql,param,host,rowid ) VALUES (?,?,?,?,?)",(pcdatum, sql, string.join(strtupel," <<Tren-ner>> "), host,str(rowid) ))
+                       if (commit==True): self.conn.commit()
+                       
+                       return True
+               except:
+                       s=str(sys.exc_info())
+                       if (s.find(" already exists")==-1):
+                       #if len(s)>0:
+                               if (programSQLError==True):
+                                       logging.error("speichereSQL-Exception "+str(sys.exc_info())+" "+str(sql)+" "+str(tupel))
+                               else:
+                                       logging.error("speichereSQL-Exception in Logging!!!! :"+str(sys.exc_info())+" "+str(sql)+" "+str(tupel))
+                       return False
+
+       def commitSQL(self):
+               self.conn.commit()
+               
+               
+       def ladeSQL(self,sql,tupel=None):
+               #print sql,tupel
+               try:
+                       if (tupel==None):
+                               self.cur.execute(sql)
+                       else:
+                               self.cur.execute(sql,tupel)
+                       return self.cur.fetchall()
+               except:
+                       logging.error("ladeSQL-Exception "+str(sys.exc_info())+" "+str(sql)+" "+str(tupel))
+                       return ()
+               
+       def ladeHistory(self,sql_condition,param_condition):
+               sql="SELECT * FROM logtable WHERE sql LIKE '%"+str(sql_condition)+"%' AND param LIKE '%"+str(param_condition)+"%'"
+               rows=self.ladeSQL(sql)
+               #print rows 
+               i=0
+               erg=[]
+               while i<len(rows):
+                       datum=time.strftime("%d.%m.%y %H:%M:%S", (time.localtime(rows[i][1])))
+                       erg.append([rows[i][1],datum,rows[i][2],rows[i][3],rows[i][3].split(" <<Tren-ner>> ")])
+                                       #pcdatum #datum #sql # Param_org #param 
+                       
+                       i+=1
+                       
+               return erg
+               
+       def delHistory(self,sql_condition,param_condition,exceptTheLastXSeconds=0):
+               pcdatum=int(time.time())-exceptTheLastXSeconds
+               sql="DELETE FROM logtable WHERE sql LIKE '%"+str(sql_condition)+"%' AND param LIKE '%"+str(param_condition)+"%' AND pcdatum<?"
+               self.speichereSQL(sql,(pcdatum,))
+               
+       def delHistoryWithRowID(self,rowid,sql_condition=" ",exceptTheLastXSeconds=0):
+               pcdatum=int(time.time())-exceptTheLastXSeconds
+               sql="DELETE FROM logtable WHERE rowid=? AND pcdatum<? AND sql LIKE '%"+str(sql_condition)+"%'"
+               self.speichereSQL(sql,(rowid,pcdatum,))
+               
+       def openDB(self):
+               try:
+                       self.cur.close()
+               except:
+                       pass
+               try:
+                       self.conn.close()
+               except:
+                       pass
+               
+               db=self.ladeDirekt("datenbank")
+               if db=="": 
+                       home_dir = os.path.expanduser('~')
+                       db=os.path.join(home_dir, "multilist.s3db") 
+                       
+               
+               datum=time.strftime("%Y-%m-%d--", (time.localtime(time.time())))+str(int(time.time()))+"--"
+               if (os.path.exists(db))and(os.path.exists(os.path.dirname(db)+os.sep+"backup/")):
+                       try:
+                               shutil.copyfile(db,str(os.path.dirname(db))+os.sep+"backup"+os.sep+datum+os.path.basename(db))
+                               #logging.debug(str(os.path.dirname(db))+os.sep+"backup"+os.sep+datum+os.path.basename(db))
+                       except:
+                               logging.info("Achtung Backup-Datei NICHT (!!!) angelegt!")
+                               #print db,str(os.path.dirname(db))+os.sep+"backup"+os.sep+datum+os.path.basename(db)
+               
+               self.conn = sqlite3.connect(db)         
+               self.cur = self.conn.cursor()
+               try:
+                       sql="CREATE TABLE logtable (id INTEGER PRIMARY KEY AUTOINCREMENT, pcdatum INTEGER ,sql TEXT, param TEXT, host TEXT, rowid TEXT)"
+                       self.cur.execute(sql)
+                       self.conn.commit()
+               except:
+                       pass
+               
+               #Add rowid line (not in old versions included)
+               try:
+                       sql="ALTER TABLE logtable ADD rowid TEXT"
+                       self.cur.execute(sql)
+                       self.conn.commit()
+               except:
+                       pass
+               
+               
+       def __init__(self):
+               home_dir = os.path.expanduser('~')
+               filename=os.path.join(home_dir, ".multilist.dat") 
+               self.d = shelve.open(filename)
+               self.openDB()
+
+       
+
+               
+               
+       def close(self):
+               try:
+                       self.d.close()
+               except:
+                       pass
+               try:
+                       self.cur.close()
+               except:
+                       pass
+               try:
+                       self.conn.close()
+               except:
+                       pass
+               logging.info("Alle Daten gespeichert")
+               
+       def __del__(self):
+               self.close()
\ No newline at end of file
diff --git a/src/multilistclasses/libsqldialog.py b/src/multilistclasses/libsqldialog.py
new file mode 100755 (executable)
index 0000000..2f384a8
--- /dev/null
@@ -0,0 +1,125 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+  
+"""
+    This file is part of Multilist.
+
+    Multilist 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 3 of the License, or
+    (at your option) any later version.
+
+    Multilist 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 Multilist.  If not, see <http://www.gnu.org/licenses/>.
+    
+    Copyright (C) 2008 Christoph Würstle
+"""
+
+import gobject
+import time
+import string
+import gtk
+import sys
+import logging
+
+import libspeichern 
+class sqlDialog(gtk.Dialog):
+       
+       def exportSQL(self,filename):
+               f = open(filename, 'w')
+               msgstring=""
+               sql="SELECT pcdatum,sql,param FROM logtable WHERE pcdatum>? ORDER BY pcdatum DESC"
+               rows=self.db.ladeSQL(sql,(time.time()-2*24*3600,))
+               for row in rows:
+                       pcdatum,sql,param = row
+                       datum=str(time.strftime("%d.%m.%y %H:%M:%S ", (time.localtime(pcdatum))))
+                       f.write( datum +"\t" + sql + "\t\t" + param+ "\n")
+                       
+                       
+               f.close()
+               
+       
+       def __init__(self,db):
+               self.db=db
+               
+               logging.info("sqldialog, init")
+               
+               gtk.Dialog.__init__(self,_("SQL History (the past two days):"),None,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,(gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+               
+               
+               self.add_button(_("Export"), 444)
+               self.set_position(gtk.WIN_POS_CENTER)
+               
+               
+               self.liststore = gtk.ListStore(str, str, str)
+
+               # create the TreeView using liststore
+               self.treeview = gtk.TreeView(self.liststore)
+               
+               # create a CellRenderers to render the data
+               self.cell1 = gtk.CellRendererText()
+               self.cell2 = gtk.CellRendererText()
+               self.cell3 = gtk.CellRendererText()
+               #self.cell1.set_property('markup', 1)
+               
+               # create the TreeViewColumns to display the data
+               self.tvcolumn1 = gtk.TreeViewColumn(_('Date'))
+               self.tvcolumn2 = gtk.TreeViewColumn(_('SQL'))
+               self.tvcolumn3 = gtk.TreeViewColumn(_('Parameter'))
+               
+               # add columns to treeview
+               self.treeview.append_column(self.tvcolumn1)
+               self.treeview.append_column(self.tvcolumn2)
+               self.treeview.append_column(self.tvcolumn3)
+               
+               
+               self.tvcolumn1.pack_start(self.cell1, True)
+               self.tvcolumn2.pack_start(self.cell2, True)
+               self.tvcolumn3.pack_start(self.cell3, True)
+               
+               self.tvcolumn1.set_attributes(self.cell1, text=0) #Spalten setzten hier!!!!
+               self.tvcolumn2.set_attributes(self.cell2, text=1)
+               self.tvcolumn3.set_attributes(self.cell3, text=2)
+
+               # make treeview searchable
+               #self.treeview.set_search_column(0)
+               #self.tvcolumn.set_sort_column_id(0)
+               
+               # Allow NOT drag and drop reordering of rows
+               self.treeview.set_reorderable(False)
+               
+               scrolled_window = gtk.ScrolledWindow()
+               scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
+               scrolled_window.add(self.treeview)
+               #self.pack_start(scrolled_window, expand=True, fill=True, padding=0)
+               
+               
+               self.vbox.pack_start(scrolled_window, True, True, 0)
+               
+               self.vbox.show_all()
+               
+               msgstring=""
+               sql="SELECT pcdatum,sql,param FROM logtable WHERE pcdatum>? ORDER BY pcdatum DESC"
+               rows=db.ladeSQL(sql,(time.time()-3*24*3600,))
+               i=0
+               for row in rows:
+                       pcdatum,sql,param = row
+                       datum=str(time.strftime("%d.%m.%y %H:%M:%S ", (time.localtime(pcdatum))))
+                       if len(param)>100:
+                               param=param[:20]+_(" (Reduced parameter) ")+param[20:]
+                       self.liststore.append([datum, sql,param])
+                       i+=1
+                       if (i>50):
+                               break
+                       
+               self.set_size_request(500,400)
+               
+               
+               
diff --git a/src/multilistclasses/libsync.py b/src/multilistclasses/libsync.py
new file mode 100755 (executable)
index 0000000..a793a16
--- /dev/null
@@ -0,0 +1,436 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+    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, either version 3 of the License, or
+    (at your option) any later version.
+
+    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.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+"""
+
+import gobject
+import time
+import string
+from SimpleXMLRPCServer import SimpleXMLRPCServer 
+import random
+import socket 
+socket.setdefaulttimeout(60) # Timeout auf 60 sec. setzen 
+import xmlrpclib 
+import select
+#import fcntl
+import struct
+import gtk
+import uuid
+import sys
+import logging
+
+import libspeichern 
+class ProgressDialog(gtk.Dialog):
+       
+       def pulse(self):
+               #self.progressbar.pulse()
+               pass
+       
+       def __init__(self,title=_("Sync process"), parent=None):
+               gtk.Dialog.__init__(self,title,parent,gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT,())
+               
+               logging.info("ProgressDialog, init")
+               
+               label=gtk.Label(_("Sync process running...please wait"))
+               self.vbox.pack_start(label, True, True, 0)
+               label=gtk.Label(_("(this can take some minutes)"))
+               self.vbox.pack_start(label, True, True, 0)
+               
+               #self.progressbar=gtk.ProgressBar()
+               #self.vbox.pack_start(self.progressbar, True, True, 0)
+               
+               #self.set_keep_above(True)
+               self.vbox.show_all()
+               self.show()
+class Sync(gtk.VBox): 
+       
+       __gsignals__ = {
+               'syncFinished' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,(gobject.TYPE_STRING,)),
+               'syncBeforeStart' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,(gobject.TYPE_STRING,)),
+       }
+       
+       def changeSyncStatus(self,active,title):
+               self.syncStatusLabel.set_text(title)
+               if active==True:
+                       if self.progress==None:
+                               self.progress=ProgressDialog(parent=self.parentwindow)
+                               self.emit("syncBeforeStart","syncBeforeStart")
+                               
+                               
+               else:
+                       if self.progress!=None:
+                               self.progress.hide()            
+                               self.progress.destroy()
+                               self.progress=None
+                               self.emit("syncFinished","syncFinished")
+       
+       def pulse(self):
+               if self.progress!=None:
+                       self.progress.pulse()
+               #if self.server!=None:
+               #       self.server.pulse()
+               
+       
+       def getUeberblickBox(self):
+               frame=gtk.Frame(_("Query"))
+               return frame
+                       
+       def handleRPC(self):
+               try:
+                       if (self.rpcserver==None): return False
+               except:
+                       return False
+                       
+               while (len(self.poll.poll(0))>0):
+                       self.rpcserver.handle_request()
+               return True
+
+       def get_ip_address(self,ifname):
+               return socket.gethostbyname(socket.gethostname())
+               #try:
+               #       s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+               #       ip=socket.inet_ntoa(fcntl.ioctl(s.fileno(),0x8915,struct.pack('256s', ifname[:15]))[20:24])
+               #       s.close()
+               #except:
+               #       ip=socket.gethostbyname(socket.gethostname())
+               #       s.close()
+               
+               #return ip FixME
+               
+       def getLastSyncDate(self,sync_uuid):
+               sql="SELECT syncpartner,pcdatum FROM sync WHERE uuid=?"
+               rows=self.db.ladeSQL(sql,(sync_uuid,))
+               if (rows!=None)and(len(rows)==1): 
+                       syncpartner,pcdatum = rows[0]
+               else:
+                       pcdatum=-1
+               logging.info("LastSyncDatum: "+str(pcdatum)+" Jetzt "+str(int(time.time())))
+               return pcdatum
+               
+               
+       def check4commit(self,newSQL,lastdate):
+               logging.info("check4commit 1")
+               if self.concernedRows==None:
+                       logging.info("check4commit Updatung concernedRows")
+                       sql="SELECT pcdatum,rowid FROM logtable WHERE pcdatum>? ORDER BY pcdatum DESC"
+                       self.concernedRows=self.db.ladeSQL(sql,(lastdate,))
+                       
+                       
+               if (self.concernedRows!=None)and(len(self.concernedRows)>0):
+                       #logging.info("check4commit 2")
+                       id1, pcdatum,sql, param, host, rowid = newSQL
+                       
+                       if len(rowid)>0:
+                               for x in self.concernedRows:
+                                       #logging.info("check4commit 3")
+                                       if (x[1]==rowid):
+                                               if (x[0]>pcdatum):
+                                                       logging.info("newer sync entry, ignoring old one")
+                                                       #logging.info("check4commit 9.1")
+                                                       return False
+                                               else:
+                                                       #logging.info("check4commit 9.2")
+                                                       return True
+                                                       
+               #logging.info("check4commit 9.3")
+               return True
+       
+       def writeSQLTupel(self,newSQLs,lastdate):
+               if (newSQLs==None):
+                        return
+               
+               self.concernedRows=None
+               pausenzaehler=0
+               logging.info("writeSQLTupel got "+str(len(newSQLs))+" sql tupels")
+               for newSQL in newSQLs:
+                       #print ""
+                       #print "SQL1: ",newSQL[1]
+                       #print "SQL2: ",newSQL[2]
+                       #print "SQL3: ",newSQL[3]
+                       #print "Param:",string.split(newSQL[3]," <<Tren-ner>> ")
+                       #print ""
+                       if (newSQL[3]!=""):
+                               param=string.split(newSQL[3]," <<Tren-ner>> ")
+                       else:
+                               param=None
+               
+                       if (len(newSQL)>2):
+                               commitSQL=True
+
+                               if (newSQL[5]!=None)and(len(newSQL[5])>0):
+                                       commitSQL=self.check4commit(newSQL,lastdate)
+                                       
+                               if (commitSQL==True):
+                                       self.db.speichereSQL(newSQL[2],param,commit=False,pcdatum=newSQL[1],rowid=newSQL[5])
+                       else: 
+                               logging.error("writeSQLTupel: Error")
+                               
+                       pausenzaehler+=1
+                       if (pausenzaehler % 10)==0:
+                               self.pulse()
+                               while (gtk.events_pending()):
+                                       gtk.main_iteration();
+                               
+               logging.info("Alle SQLs an sqlite geschickt, commiting now")
+               self.db.commitSQL()
+               logging.info("Alle SQLs commited")
+               
+       
+       def doSync(self,sync_uuid,pcdatum,newSQLs,pcdatumjetzt):
+               #print uuid,pcdatum,newSQLs
+               #logging.info("doSync 0")
+               self.changeSyncStatus(True,_("sync process running"))
+               self.pulse()
+               #logging.info("doSync 1")
+               
+               while (gtk.events_pending()):
+                       gtk.main_iteration();
+               diff=time.time()-pcdatumjetzt
+               if diff<0:
+                       diff=diff*(-1)
+               if diff>30:
+                       return -1
+               
+               logging.info("doSync read sqls")
+               sql="SELECT * FROM logtable WHERE pcdatum>?"
+               rows=self.db.ladeSQL(sql,(pcdatum,))
+               logging.info("doSync read sqls")
+               self.writeSQLTupel(newSQLs,pcdatum)
+               logging.info("doSync wrote "+str(len(newSQLs))+" sqls")
+               logging.info("doSync sending "+str(len(rows))+" sqls")
+               i=0
+               return rows
+               
+       def getRemoteSyncUUID(self):
+               return self.sync_uuid
+       
+       
+       def startServer(self, widget, data=None):
+               #Starte RPCServer
+               self.db.speichereDirekt("syncServerIP",self.comboIP.get_child().get_text())
+               
+               if (widget.get_active()==True):
+                       logging.info("Starting Server")
+                       
+                       try:
+                               ip=self.comboIP.get_child().get_text()
+                               self.rpcserver = SimpleXMLRPCServer((ip, self.port),allow_none=True) 
+                               self.rpcserver.register_function(pow)
+                               self.rpcserver.register_function(self.getLastSyncDate)
+                               self.rpcserver.register_function(self.doSync)
+                               self.rpcserver.register_function(self.getRemoteSyncUUID)
+                               self.rpcserver.register_function(self.doSaveFinalTime)
+                               self.rpcserver.register_function(self.pulse)
+                               self.poll=select.poll()
+                               self.poll.register(self.rpcserver.fileno())
+                               gobject.timeout_add(1000, self.handleRPC)
+                               self.syncServerStatusLabel.set_text(_("Syncserver running..."))
+                       
+                               #save
+                               self.db.speichereDirekt("startSyncServer",True)
+                       
+                       except:
+                               s=str(sys.exc_info())
+                               logging.error("libsync: could not start server. Error: "+s)
+                               mbox=gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_ERROR,gtk.BUTTONS_OK,_("Sync server could not start. Please check IP and port.")) #gtk.DIALOG_MODAL
+                               mbox.set_modal(False)
+                               response=mbox.run() 
+                               mbox.hide() 
+                               mbox.destroy() 
+                               widget.set_active(False)
+                               
+               else:
+                       logging.info("Stopping Server")
+                       try:
+                               del self.rpcserver      
+                       except:
+                               pass
+                       self.syncServerStatusLabel.set_text(_("Syncserver not running..."))
+                       #save
+                       self.db.speichereDirekt("startSyncServer",False)
+               
+       def doSaveFinalTime(self,sync_uuid,pcdatum=None):
+               if (pcdatum==None): pcdatum=int(time.time())
+               if (time.time()>pcdatum):
+                       pcdatum=int(time.time()) #größere Zeit nehmen
+                       
+               self.pulse()
+               
+               #fime save time+uuid
+               sql="DELETE FROM sync WHERE uuid=?"
+               self.db.speichereSQL(sql,(sync_uuid,),log=False)
+               sql="INSERT INTO sync (syncpartner,uuid,pcdatum) VALUES (?,?,?)"
+               self.db.speichereSQL(sql,("x",str(sync_uuid),pcdatum),log=False)
+               self.pulse()
+               self.changeSyncStatus(False,_("no sync process (at the moment)"))
+               return (self.sync_uuid,pcdatum)
+               
+       
+       def syncButton(self, widget, data=None):
+               logging.info("Syncing")
+               #sql="DELETE FROM logtable WHERE sql LIKE externeStundenplanung"
+               #self.db.speichereSQL(sql)
+               
+               self.changeSyncStatus(True,_("sync process running"))
+               while (gtk.events_pending()):
+                       gtk.main_iteration();
+
+               self.db.speichereDirekt("syncRemoteIP",self.comboRemoteIP.get_child().get_text())
+               try:
+                       self.server = xmlrpclib.ServerProxy("http://"+self.comboRemoteIP.get_child().get_text()+":"+str(self.port),allow_none=True) 
+                       #lastDate=server.getLastSyncDate(str(self.sync_uuid))
+                       server_sync_uuid=self.server.getRemoteSyncUUID()
+                       lastDate=self.getLastSyncDate(str(server_sync_uuid))
+                       
+                       #print ("LastSyncDate: "+str(lastDate)+" Now: "+str(int(time.time())))
+               
+                       sql="SELECT * FROM logtable WHERE pcdatum>?"
+                       rows=self.db.ladeSQL(sql,(lastDate,))
+                       
+                       logging.info("loaded concerned rows")
+               
+                       newSQLs=self.server.doSync(self.sync_uuid,lastDate,rows,time.time())
+               
+                       logging.info("did do sync, processing sqls now")
+                       if newSQLs!=-1:
+                               self.writeSQLTupel(newSQLs,lastDate)
+       
+                               sync_uuid, finalpcdatum=self.server.doSaveFinalTime(self.sync_uuid)
+                               self.doSaveFinalTime(sync_uuid, finalpcdatum)
+                       
+                               self.changeSyncStatus(False,_("no sync process (at the moment)"))
+                               
+                               mbox =  gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_INFO,gtk.BUTTONS_OK,_("Synchronization successfully completed"))
+                               response = mbox.run() 
+                               mbox.hide() 
+                               mbox.destroy() 
+                       else:
+                               logging.warning("Zeitdiff zu groß/oder anderer db-Fehler")
+                               self.changeSyncStatus(False,_("no sync process (at the moment)"))
+                               mbox =  gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_INFO,gtk.BUTTONS_OK,_("The clocks are not synchronized between stations"))
+                               response = mbox.run() 
+                               mbox.hide() 
+                               mbox.destroy() 
+               except:
+                               logging.warning("Sync connect failed")
+                               self.changeSyncStatus(False,_("no sync process (at the moment)"))
+                               mbox =  gtk.MessageDialog(None,gtk.DIALOG_MODAL,gtk.MESSAGE_INFO,gtk.BUTTONS_OK,_("Sync failed, reason: ")+unicode(sys.exc_info()[1][1]))
+                               response = mbox.run() 
+                               mbox.hide() 
+                               mbox.destroy() 
+                               self.server=None
+               self.server=None
+                               
+
+                       
+       
+       def __init__(self,db,parentwindow,port):
+               gtk.VBox.__init__(self,homogeneous=False, spacing=0)
+               
+               logging.info("Sync, init")
+               self.db=db
+               self.progress=None
+               self.server=None
+               self.port=int(port)
+               self.parentwindow=parentwindow
+               self.concernedRows=None
+               
+               #print "Sync, 2"
+               #sql = "DROP TABLE sync"
+               #self.db.speichereSQL(sql,log=False)
+               
+               sql = "CREATE TABLE sync (id INTEGER PRIMARY KEY, syncpartner TEXT, uuid TEXT, pcdatum INTEGER)"
+               self.db.speichereSQL(sql,log=False)
+               
+               #print "Sync, 3"
+               
+               sql="SELECT uuid,pcdatum FROM sync WHERE syncpartner=?"
+               rows=self.db.ladeSQL(sql,("self",)) #Eigene Id feststellen
+               
+               #print "Sync, 3a"
+               if (rows==None)or(len(rows)!=1):
+                       sql="DELETE FROM sync WHERE syncpartner=?"
+                       self.db.speichereSQL(sql,("self",),log=False)
+
+                       #uuid1=uuid()
+                       #print "Sync, 3b"
+                       
+                       #print "Sync, 3bb"
+                       self.sync_uuid=str(uuid.uuid4())
+                       sql="INSERT INTO sync (syncpartner,uuid,pcdatum) VALUES (?,?,?)"
+                       self.db.speichereSQL(sql,("self",str(self.sync_uuid),int(time.time())),log=False)
+                       #print "Sync, 3c"
+               else:
+                       sync_uuid,pcdatum = rows[0]
+                       self.sync_uuid=sync_uuid
+               #print "x1"
+               
+               
+               
+               #print "Sync, 4"
+
+               
+               frame=gtk.Frame(_("Local SyncServer (port ")+str(self.port)+")")
+       
+               
+               
+               self.comboIP=gtk.combo_box_entry_new_text()
+               
+               
+               self.comboIP.append_text("") #self.get_ip_address("eth0"))
+               #self.comboIP.append_text(self.get_ip_address("eth1")) #fixme
+               #self.comboIP.append_text(self.get_ip_address("eth2"))
+               #self.comboIP.append_text(self.get_ip_address("eth3"))
+               #print "Sync, 4d"
+               #self.comboIP.append_text(self.get_ip_address("wlan0"))
+               #self.comboIP.append_text(self.get_ip_address("wlan1"))
+               
+               #print "Sync, 4e"
+               
+               frame.add(self.comboIP)
+               serverbutton=gtk.ToggleButton(_("Start SyncServer"))
+               serverbutton.connect("clicked",self.startServer,(None,))
+               self.pack_start(frame, expand=False, fill=True, padding=1)
+               self.pack_start(serverbutton, expand=False, fill=True, padding=1)
+               self.syncServerStatusLabel=gtk.Label(_("Syncserver not running"))
+               self.pack_start(self.syncServerStatusLabel, expand=False, fill=True, padding=1)         
+                               
+               frame=gtk.Frame(_("RemoteSync-Server (Port ")+str(self.port)+")")
+               self.comboRemoteIP=gtk.combo_box_entry_new_text()
+               self.comboRemoteIP.append_text("192.168.0.?")
+               self.comboRemoteIP.append_text("192.168.1.?")
+               self.comboRemoteIP.append_text("192.168.176.?")
+               frame.add(self.comboRemoteIP)
+               syncbutton=gtk.Button(_("Connect to remote SyncServer"))
+               syncbutton.connect("clicked",self.syncButton,(None,))
+               self.pack_start(frame, expand=False, fill=True, padding=1)
+               self.pack_start(syncbutton, expand=False, fill=True, padding=1)
+               self.syncStatusLabel=gtk.Label(_("no sync process (at the moment)"))
+               self.pack_start(self.syncStatusLabel, expand=False, fill=True, padding=1)
+
+
+               #self.comboRemoteIP.set_text_column("Test")
+               self.comboRemoteIP.get_child().set_text(self.db.ladeDirekt("syncRemoteIP"))
+               self.comboIP.get_child().set_text(self.db.ladeDirekt("syncServerIP"))
+               
+               #load
+               if (self.db.ladeDirekt("startSyncServer",False)==True):
+                       serverbutton.set_active(True)
+                       
+               #print "Sync, 9"
diff --git a/src/multilistclasses/libview.py b/src/multilistclasses/libview.py
new file mode 100644 (file)
index 0000000..481464c
--- /dev/null
@@ -0,0 +1,563 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+  
+"""
+    This file is part of Multilist.
+
+    Multilist 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 3 of the License, or
+    (at your option) any later version.
+
+    Multilist 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 Multilist.  If not, see <http://www.gnu.org/licenses/>.
+    
+    Copyright (C) 2008 Christoph Würstle
+"""
+  
+import gtk
+import gobject
+import logging
+import pango
+import libliststorehandler
+  
+
+class Columns_dialog(gtk.VBox):
+       
+       def is_col_selected(self, icol):
+               children=self.framebox.get_children()
+               if icol<len(children):
+                       return children[icol].get_active()
+               else:
+                       return None
+       
+       def save_column_setting(self):
+               i=1 #uid can not be shown
+               while self.liststorehandler.get_colname(i)!=None:
+                       name=str(self.liststorehandler.get_colname(i))
+                       if self.is_col_selected(i-1)==True:
+                               self.db.speichereDirekt("showcol_"+name,"1")
+                       else:
+                               self.db.speichereDirekt("showcol_"+name,"0")
+                       i=i+1
+               
+       
+
+       
+       def __init__(self,db,liststorehandler):
+               gtk.VBox.__init__(self,homogeneous=False, spacing=0)
+               
+               self.db=db
+               self.liststorehandler=liststorehandler
+               
+               #serverbutton=gtk.ToggleButton("SyncServer starten")
+               #serverbutton.connect("clicked",self.startServer,(None,))
+               #self.pack_start(serverbutton, expand=False, fill=True, padding=1)
+               #print "x1"
+               
+               frame=gtk.Frame(_("Columns"))
+               self.framebox=gtk.VBox(homogeneous=False, spacing=0)
+               
+               self.scrolled_window = gtk.ScrolledWindow()
+               self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
+
+               self.scrolled_window.add_with_viewport(self.framebox)
+               
+               
+               i=1 #uid can not be shown
+               while self.liststorehandler.get_colname(i)!=None:
+                       name=str(self.liststorehandler.get_colname(i))
+                       checkbutton=gtk.CheckButton(name)
+                       if self.db.ladeDirekt("showcol_"+name)=="1":
+                               checkbutton.set_active(True)
+                               
+                       self.framebox.pack_start(checkbutton)
+                       i=i+1
+               
+               frame.add(self.scrolled_window)
+               self.pack_start(frame, expand=True, fill=True, padding=1)
+               
+               
+               
+       
+
+
+class CellRendererTriple(gtk.GenericCellRenderer):
+       __gproperties__ = {
+                "status": (gobject.TYPE_STRING, "Status",
+                "Status", "", gobject.PARAM_READWRITE),
+        }
+       
+       __gsignals__ = {
+               'status_changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,(gobject.TYPE_INT,gobject.TYPE_STRING)),
+       }
+
+
+       def __init__(self):
+               #self.__gobject_init__()
+               #gtk.GenericCellRenderer.__init__(self,*args,**kwargs)
+               gtk.GenericCellRenderer.__init__(self)
+               #self.__gobject_init__()
+               self.status=-1
+               self.xpad = 2
+               self.ypad = 2
+               self.mode = gtk.CELL_RENDERER_MODE_ACTIVATABLE
+               self.xpad = -2; self.ypad = -2
+               self.xalign = 0.5; self.yalign = 0.5
+               self.active = 0
+               self.widget=None
+               self.last_cell=None
+               self.connect('editing-started', self.on_clicked)
+
+       def do_set_property(self,property,value):
+               setattr(self, property.name, value)
+
+       def do_get_property(self, property):
+               return getattr(self, property.name)
+
+       def get_layout(self, widget):
+               '''Gets the Pango layout used in the cell in a TreeView widget.'''
+
+               layout = pango.Layout(widget.get_pango_context())
+               layout.set_width(-1)    # Do not wrap text.
+
+               layout.set_text('  ')
+
+               return layout
+
+       def on_get_size(self, widget, cell_area=None):
+               xpad = 2
+               ypad = 2
+
+               xalign = 0
+               yalign = 0.5
+
+               layout = self.get_layout(widget)
+               width, height = layout.get_pixel_size()
+
+               x_offset = xpad
+               y_offset = ypad
+
+               if cell_area:
+
+                       x_offset = xalign * (cell_area.width - width)
+                       x_offset = max(x_offset, xpad)
+                       x_offset = int(round(x_offset, 0))
+
+                       y_offset = yalign * (cell_area.height - height)
+                       y_offset = max(y_offset, ypad)
+                       y_offset = int(round(y_offset, 0))
+
+               width  = width  + (xpad * 2)
+               height = height + (ypad * 2)
+
+               
+               return x_offset, y_offset, width, height
+               
+       def on_clicked(self,  widget, data):
+               print widget,data
+
+       def clicked(self, widget, data1=None):
+               x,y=widget.get_pointer()
+               widget.realize()
+               
+               path=widget.get_path_at_pos(x,y)
+               
+               #print "a",widget.get_cursor()
+               #print path
+               
+       
+               
+               path=widget.get_cursor()[0]
+               
+               if path!=None:
+                       irow=path[0]    #path[0][0]-1
+                       rect=widget.get_cell_area(irow, widget.get_column(0)) #FixME 0 is hardcoded
+                       if x<rect.x+rect.width:
+                               self.emit("status_changed",irow,self.status)
+               else:
+                       return
+               
+               
+                       #workarround -1 means last item, because bug in treeview?!
+                       #print "not in list"
+                       rect=widget.get_visible_rect() #widget.get_cell_area(-1, widget.get_column(0))
+                       #print rect.x,rect.y,rect.width,rect.height,x,y
+                       irow=-1
+                       rect=widget.get_cell_area(0, widget.get_column(0)) #FixME 0 is hardcoded
+                       if x<rect.x+rect.width:
+                               self.emit("status_changed",irow,"-1")
+               
+
+       def on_render(self, window, widget, background_area, cell_area, expose_area, flags ):
+               if (self.widget==None):
+                       #print widget
+                       self.widget=widget
+                       self.widget.connect("cursor-changed",self.clicked) #button-press-event
+               
+               self.last_cell=cell_area
+               
+               x=int(cell_area.x+(cell_area.width-2)/2-(cell_area.height-2)/2)
+               y=int(cell_area.y+1)
+               height=int(cell_area.height-2)
+               width=int(height)
+
+               if (self.status=="1"):
+                       widget.style.paint_check(window,gtk.STATE_NORMAL, gtk.SHADOW_IN,cell_area, widget, "cellradio",x,y,width,height)
+               elif (self.status=="0"):
+                       #width=height
+                       height=height-3
+                       width=height
+                       
+                       widget.style.paint_flat_box(window, gtk.STATE_NORMAL, gtk.SHADOW_NONE, cell_area, widget, "cellunselected",x,y,width,height)
+                       
+                       widget.style.paint_hline(window, gtk.STATE_NORMAL,cell_area, widget, "cellunselected",x,x+width,y)
+                       widget.style.paint_hline(window, gtk.STATE_NORMAL,cell_area, widget, "cellunselected",x,x+width,y+height)
+                       widget.style.paint_vline(window, gtk.STATE_NORMAL,cell_area, widget, "cellunselected",y,y+height,x)
+                       widget.style.paint_vline(window, gtk.STATE_NORMAL,cell_area, widget, "cellunselected",y,y+height,x+width)
+                       
+               else:
+                       widget.style.paint_diamond(window, gtk.STATE_NORMAL, gtk.SHADOW_IN, cell_area, widget, "cellunselected",x,y,width,height)
+                       
+               #widget.show_all()
+               #print "render"
+               pass
+    
+       def on_start_editing(self, event, widget, path, background_area, cell_area, flags):
+               print "on_start_editing",path
+               return None
+       
+       
+       def on_activate(self, event, widget, path, background_area, cell_area, flags):
+               print "activate",path
+               return False
+
+               
+               
+class CellRendererCombo2(gtk.GenericCellRenderer):
+       __gproperties__ = {
+                "text": (gobject.TYPE_STRING, "text",
+                "Text", "", gobject.PARAM_READWRITE),
+        }
+       
+       __gsignals__ = {
+               'status_changed' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE,(gobject.TYPE_INT,gobject.TYPE_STRING)),
+       }
+
+
+       def __init__(self):
+               #self.__gobject_init__()
+               #gtk.GenericCellRenderer.__init__(self,*args,**kwargs)
+               gtk.GenericCellRenderer.__init__(self)
+               #self.__gobject_init__()
+               self.status=-1
+               self.xpad = 2
+               self.ypad = 2
+               self.mode = gtk.CELL_RENDERER_MODE_ACTIVATABLE
+               self.xpad = -2; self.ypad = -2
+               self.xalign = 0.5; self.yalign = 0.5
+               self.active = 0
+               self.widget=None
+               self.last_cell=None
+               self.text="(none)"
+               self.connect('editing-started', self.on_clicked)
+
+       def do_set_property(self,property,value):
+               #print property,value
+               setattr(self, property.name, value)
+
+       def do_get_property(self, property):
+               return getattr(self, property.name)
+
+       def get_layout(self, widget):
+               '''Gets the Pango layout used in the cell in a TreeView widget.'''
+
+               layout = pango.Layout(widget.get_pango_context())
+               layout.set_width(-1)    # Do not wrap text.
+
+               layout.set_text(self.text)
+
+               return layout
+
+       def on_get_size(self, widget, cell_area=None):
+               xpad = 2
+               ypad = 2
+
+               xalign = 0
+               yalign = 0.5
+
+               layout = self.get_layout(widget)
+               width, height = layout.get_pixel_size()
+
+               x_offset = xpad
+               y_offset = ypad
+
+               if cell_area:
+
+                       x_offset = xalign * (cell_area.width - width)
+                       x_offset = max(x_offset, xpad)
+                       x_offset = int(round(x_offset, 0))
+
+                       y_offset = yalign * (cell_area.height - height)
+                       y_offset = max(y_offset, ypad)
+                       y_offset = int(round(y_offset, 0))
+
+               width  = width  + (xpad * 2)
+               height = height + (ypad * 2)
+
+               
+               return x_offset, y_offset, width, height
+               
+       def on_clicked(self,  widget, data):
+               print widget,data
+
+       def clicked(self, widget, data1=None):
+               return
+               x,y=widget.get_pointer()
+               widget.realize()
+               
+               #path=widget.get_path_at_pos(x,y)
+               
+               path=widget.get_cursor()[0]
+               
+               if path!=None:
+                       irow=path[0]    #path[0][0]-1
+                       rect=widget.get_cell_area(irow, widget.get_column(0)) #FixME 0 is hardcoded
+                       if x<rect.x+rect.width:
+                               self.emit("status_changed",irow,self.status)
+               else:
+                       return
+               
+               
+                       #workarround -1 means last item, because bug in treeview?!
+                       #print "not in list"
+                       rect=widget.get_visible_rect() #widget.get_cell_area(-1, widget.get_column(0))
+                       #print rect.x,rect.y,rect.width,rect.height,x,y
+                       irow=-1
+                       rect=widget.get_cell_area(0, widget.get_column(0)) #FixME 0 is hardcoded
+                       if x<rect.x+rect.width:
+                               self.emit("status_changed",irow,"-1")
+               
+
+       def on_render(self, window, widget, background_area, cell_area, expose_area, flags ):
+               if (self.widget==None):
+                       self.widget=widget
+                       self.widget.connect("cursor-changed",self.clicked) #button-press-event
+               
+               self.last_cell=cell_area
+               
+               x=int(cell_area.x+(cell_area.width-2)/2-(cell_area.height-2)/2)
+               y=int(cell_area.y+1)
+               height=int(cell_area.height-2)
+               width=int(height)
+               
+               widget.style.paint_layout(window,gtk.STATE_NORMAL, True, cell_area, widget, "cellradio",x,y,self.get_layout(widget))
+                       
+               #widget.show_all()
+    
+       def on_start_editing(self, event, widget, path, background_area, cell_area, flags):
+               print "on_start_editing",path
+               return None
+       
+       
+       def on_activate(self, event, widget, path, background_area, cell_area, flags):
+               print "activate",path
+               return False
+
+
+gobject.type_register(CellRendererCombo2)
+gobject.type_register(CellRendererTriple)
+
+       
+  
+class View(gtk.VBox):
+       
+       def loadList(self):
+               ls=self.liststorehandler.get_liststore()
+               self.treeview.set_model(ls)
+               #self.tvcolumn[i].add_attribute( self.cell[i], "active", 1)
+               #print "setup",ls
+               
+               
+       
+       
+       def col_edited(self,cell, irow, new_text,icol=None):
+               if (irow!=4):
+                       self.liststorehandler.update_row(irow,icol,new_text)
+               else:
+                       print cell, irow, new_text,icol
+               
+       def col_toggled(self,widget,irow, status ):
+               #print irow,ls[irow][1],status
+               ls=self.treeview.get_model()
+               
+               if self.liststorehandler.selection.get_status()=="0":
+                       if ls[irow][1]=="0":
+                               self.liststorehandler.update_row(irow,1,"1")
+                       else:
+                               self.liststorehandler.update_row(irow,1,"0")
+               else:
+                       if ls[irow][1]=="1":
+                               self.liststorehandler.update_row(irow,1,"-1")
+                       elif ls[irow][1]=="0":
+                               self.liststorehandler.update_row(irow,1,"1")
+                       else:
+                               self.liststorehandler.update_row(irow,1,"0")
+                       
+               #self.tvcolumn[i].set_attributes( self.cell[i], active=i)
+               
+               
+       
+       def convert(self,s):
+               #print s
+               if (s=="1"):
+                       return 1
+               else:
+                       return 0
+               
+               
+       def del_active_row(self):
+               path, col = self.treeview.get_cursor()
+               #print path, col
+               if path!=None:
+                       irow=path[0]
+                       row_iter=self.treeview.get_model().get_iter(path)
+                       self.liststorehandler.del_row(irow,row_iter)
+
+                       
+               #treemodel.get_iter()
+               
+               
+               
+       def sort_func_function(self,model, iter1, iter2, data=None):
+               print "sorting"
+               
+               
+       def reload_view(self):
+               # create the TreeView using liststore
+               self.modelsort = gtk.TreeModelSort(self.liststorehandler.get_liststore())
+               self.modelsort.set_sort_column_id(2, gtk.SORT_ASCENDING)
+               
+               self.treeview = gtk.TreeView(self.modelsort)
+               self.treeview.set_headers_visible(True)
+               
+               
+               self.cell=range(self.liststorehandler.get_colcount())
+               self.tvcolumn=range(self.liststorehandler.get_colcount())
+
+               
+               m = self.liststorehandler.get_unitsstore()
+               
+               for i in range(self.liststorehandler.get_colcount()):
+                       
+                       if i>5:
+                               default="0"
+                       else:
+                               default="1"
+                       if self.db.ladeDirekt("showcol_"+str(self.liststorehandler.get_colname(i)),default)=="1":
+
+                               if (i==1):
+                                       self.cell[i] = CellRendererTriple()
+                                       self.tvcolumn[i] =      gtk.TreeViewColumn(self.liststorehandler.get_colname(i),self.cell[i])
+                                       self.cell[i].connect( 'status_changed', self.col_toggled)
+                                       self.tvcolumn[i].set_attributes( self.cell[i], status=i)
+                               
+                               elif (i==3)or(i==4)or(i==6):
+                                       self.cell[i] = gtk.CellRendererCombo()
+                                       self.tvcolumn[i] =      gtk.TreeViewColumn(self.liststorehandler.get_colname(i),self.cell[i])
+                                       self.cell[i].set_property("model",m)
+                                       self.cell[i].set_property('text-column', i)
+                                       self.cell[i].set_property('editable',True)
+                                       self.cell[i].connect("edited", self.col_edited,i) 
+                                       self.tvcolumn[i].set_attributes( self.cell[i], text=i)
+                               else:
+                                       self.cell[i] = gtk.CellRendererText()
+                                       self.tvcolumn[i] = gtk.TreeViewColumn(self.liststorehandler.get_colname(i),self.cell[i])
+                                       self.cell[i].set_property('editable',True)
+                                       self.cell[i].set_property('editable-set',True)
+                                       self.cell[i].connect("edited", self.col_edited,i) 
+                                       #self.cell[i].connect("editing-canceled", self.col_edited2,i) 
+                                       self.tvcolumn[i].set_attributes(self.cell[i], text=i)
+
+                               self.cell[i].set_property('cell-background', 'lightgray')
+                               self.tvcolumn[i].set_sort_column_id(i)
+                               self.tvcolumn[i].set_resizable(True)
+                               
+                               
+                               if (i>0):
+                                       self.treeview.append_column(self.tvcolumn[i])
+                               
+               
+               # Allow NOT drag and drop reordering of rows
+               self.treeview.set_reorderable(False)
+               
+               
+               if self.scrolled_window != None:
+                       self.scrolled_window.destroy()
+               
+               self.scrolled_window = gtk.ScrolledWindow()
+               self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC,gtk.POLICY_AUTOMATIC)
+
+               self.scrolled_window.add(self.treeview)
+               self.pack_start(self.scrolled_window, expand=True, fill=True, padding=0)
+               self.loadList()
+               
+               self.show_all()
+       
+       def __init__(self,db,liststorehandler,parent_window):
+               
+               self.db=db
+               self.parent_window=parent_window
+               self.liststorehandler = liststorehandler
+               
+               
+               gtk.VBox.__init__(self,homogeneous=False, spacing=0)
+               
+               logging.info("libview, init")
+
+               self.scrolled_window = None
+               self.reload_view()
+               
+               
+               
+               
+               
+               """
+               bearbeitenFrame=gtk.Frame("Verteilung kopieren nach")
+               bearbeitenvBox=gtk.VBox(homogeneous=False, spacing=0)
+               
+               bearbeitenhBox=gtk.HBox(homogeneous=False, spacing=0)
+               self.comboKlassen = gtk.combo_box_new_text()
+               bearbeitenhBox.pack_start(self.comboKlassen, expand=False, fill=True, padding=0)
+               button=gtk.Button("Kopieren")
+               button.connect("clicked", self.kopiereStoffverteilung, None)
+               bearbeitenhBox.pack_start(button, expand=False, fill=True, padding=0)
+               
+               label=gtk.Label("   ")
+               bearbeitenhBox.pack_start(label, expand=False, fill=True, padding=0)
+               
+               button=gtk.Button("Export in CSV-Datei")
+               button.connect("clicked", self.exportStoffverteilung, None)
+               bearbeitenhBox.pack_start(button, expand=False, fill=True, padding=0)
+               
+               bearbeitenvBox.pack_start(bearbeitenhBox, expand=False, fill=True, padding=0)
+               
+       
+               bearbeitenFrame.add(bearbeitenvBox)
+               self.pack_start(bearbeitenFrame, expand=False, fill=True, padding=0)
+               """
+               
+               #self.connect("unmap", self.speichere) 
+               #self.connect("map", self.ladeWirklich) 
+
+               #self.show_all()
+               
+               
+
+               #print "libstoffverteilung 9: ",time.clock()
+
diff --git a/src/upload.sh b/src/upload.sh
new file mode 100755 (executable)
index 0000000..3a3fd46
--- /dev/null
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+scp ../../multilist_0.3.0_all.deb user@192.168.0.34:/media/mmc2/
+
+
+
+