Setting up support for branches and tags
authorepage <eopage@byu.net>
Sat, 16 May 2009 12:51:59 +0000 (12:51 +0000)
committerepage <eopage@byu.net>
Sat, 16 May 2009 12:51:59 +0000 (12:51 +0000)
git-svn-id: file:///svnroot/quicknote/trunk@31 bb7704e3-badb-4cfa-9ab3-9374dc87eaa2

34 files changed:
Makefile [new file with mode: 0644]
data/40/quicknote.png [new file with mode: 0644]
data/48/quicknote.png [new file with mode: 0644]
data/low/quicknote.png [new file with mode: 0644]
data/quicknote.desktop [new file with mode: 0644]
data/quicknote.png [new file with mode: 0644]
data/quicknote.service [new file with mode: 0644]
data/scale/quicknote.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/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]
setup.py [new file with mode: 0644]
src/__init__.py [new file with mode: 0755]
src/fakehildon.py [new file with mode: 0644]
src/libhistory.py [new file with mode: 0644]
src/libkopfzeile.py [new file with mode: 0644]
src/libnotizen.py [new file with mode: 0644]
src/libquicknote.py [new file with mode: 0644]
src/libspeichern.py [new file with mode: 0644]
src/libsqldialog.py [new file with mode: 0755]
src/libsync.py [new file with mode: 0755]
src/quicknote.py [new file with mode: 0755]
src/simple_list.py [new file with mode: 0644]
support/builddeb.py [new file with mode: 0755]
support/fake_py2deb.py [new file with mode: 0644]
support/pylint.rc [new file with mode: 0644]
support/test_syntax.py [new file with mode: 0755]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
index 0000000..03aed86
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,95 @@
+PROJECT_NAME=quicknote
+PROJECT_VERSION=0.7.4
+SOURCE_PATH=src
+SOURCE=$(shell find $(SOURCE_PATH) -iname "*.py")
+LOCALE_PATH=locale
+LOCALE_FILES=$(shell find $(LOCALE_PATH) -iname "*.mo")
+PROGRAM=$(SOURCE_PATH)/$(PROJECT_NAME).py
+OBJ=$(SOURCE:.py=.pyc)
+BUILD_PATH=./builddeb/
+
+TEXT_DOMAIN=$(PROJECT_NAME)
+POTFILES=$(wildcard src/quicknoteclasses/*.py)
+
+UNIT_TEST=nosetests --with-doctest -w .
+SYNTAX_TEST=support/test_syntax.py
+LINT_RC=./support/pylint.rc
+LINT=pylint --rcfile=$(LINT_RC)
+PROFILE_GEN=python -m cProfile -o .profile
+PROFILE_VIEW=python -m pstats .profile
+
+.PHONY: all run profile test lint clean distclean install update_po build_mo
+
+all: build_mo
+       python2.5 setup.py build  
+
+run:
+       $(PROGRAM)
+
+profile:
+       $(PROFILE_GEN) $(PROGRAM)
+       $(PROFILE_VIEW)
+
+test: $(OBJ)
+       $(UNIT_TEST)
+
+package: clean $(OBJ) all
+       dpkg-buildpackage -rfakeroot 
+       dpkg -i ../$(PROJECT_NAME)_$(PROJECT_VERSION)_all.deb
+
+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
+
+build: $(OBJ) build_mo
+       rm -Rf $(BUILD_PATH)
+       mkdir $(BUILD_PATH)
+       cp $(PROGRAM)  $(BUILD_PATH)
+       $(foreach file, $(DATA), cp $(file) $(BUILD_PATH)/$(subst /,-,$(file)) ; )
+       $(foreach file, $(SOURCE), cp $(file) $(BUILD_PATH)/$(subst /,-,$(file)) ; )
+       $(foreach file, $(OBJ), cp $(file) $(BUILD_PATH)/$(subst /,-,$(file)) ; )
+       $(foreach file, $(LOCALE_FILES), cp $(file) $(BUILD_PATH)/$(subst /,-,$(file)) ; )
+       cp data/$(PROJECT_NAME).desktop $(BUILD_PATH)
+       cp data/$(PROJECT_NAME).service $(BUILD_PATH)
+       cp data/low/$(PROJECT_NAME).png $(BUILD_PATH)/26x26-$(PROJECT_NAME).png
+       cp data/40/$(PROJECT_NAME).png $(BUILD_PATH)/40x40-$(PROJECT_NAME).png
+       cp data/48/$(PROJECT_NAME).png $(BUILD_PATH)/48x48-$(PROJECT_NAME).png
+       cp data/scale/$(PROJECT_NAME).png $(BUILD_PATH)/scale-$(PROJECT_NAME).png
+       cp support/builddeb.py $(BUILD_PATH)
+       cp support/fake_py2deb.py $(BUILD_PATH)
+
+lint: $(OBJ)
+       $(foreach file, $(SOURCE), $(LINT) $(file) ; )
+
+clean: 
+       rm -rf ./locale
+       rm -rf $(OBJ)
+       rm -Rf $(BUILD_PATH)
+       python2.5 setup.py clean --all
+
+distclean: clean
+       find $(SOURCE_PATH) -name "*.*~" | xargs rm -f
+       find $(SOURCE_PATH) -name "*.swp" | xargs rm -f
+       find $(SOURCE_PATH) -name "*.bak" | xargs rm -f
+       find $(SOURCE_PATH) -name ".*.swp" | xargs rm -f
+
+install: build_mo
+       python2.5 setup.py install --root $(DESTDIR) 
+
+%.pyc: %.py
+       $(SYNTAX_TEST) $<
+
+#Makefile Debugging
+#Target to print any variable, can be added to the dependencies of any other target
+#Userfule flags for make, -d, -p, -n
+print-%: ; @$(error $* is $($*) ($(value $*)) (from $(origin $*)))
diff --git a/data/40/quicknote.png b/data/40/quicknote.png
new file mode 100644 (file)
index 0000000..d6aa8d9
Binary files /dev/null and b/data/40/quicknote.png differ
diff --git a/data/48/quicknote.png b/data/48/quicknote.png
new file mode 100644 (file)
index 0000000..7c9f7af
Binary files /dev/null and b/data/48/quicknote.png differ
diff --git a/data/low/quicknote.png b/data/low/quicknote.png
new file mode 100644 (file)
index 0000000..f7fc618
Binary files /dev/null and b/data/low/quicknote.png differ
diff --git a/data/quicknote.desktop b/data/quicknote.desktop
new file mode 100644 (file)
index 0000000..84ed6e2
--- /dev/null
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Encoding=UTF-8
+Name=Quicknote
+Comment=Quicknote
+Type=Application
+Exec=/usr/bin/quicknote.py
+Icon=quicknote
+X-Window-Icon=quicknote
+X-Window-Icon-Dimmed=quicknote
+#X-Osso-Service=quicknote
+X-Osso-Type=application/x-executable
diff --git a/data/quicknote.png b/data/quicknote.png
new file mode 100644 (file)
index 0000000..46c8c02
Binary files /dev/null and b/data/quicknote.png differ
diff --git a/data/quicknote.service b/data/quicknote.service
new file mode 100644 (file)
index 0000000..3e94c03
--- /dev/null
@@ -0,0 +1,3 @@
+[D-BUS Service] 
+Name=com.nokia.quicknote
+Exec=/usr/bin/quicknote.py
diff --git a/data/scale/quicknote.png b/data/scale/quicknote.png
new file mode 100644 (file)
index 0000000..da2d5f5
Binary files /dev/null and b/data/scale/quicknote.png differ
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..624ba89
--- /dev/null
@@ -0,0 +1,35 @@
+quicknote (0.7.4) unstable; urgency=low
+
+  * fixed small bugs
+  * move category
+
+ -- unknown <n800@axique.de>  Thu,  22 May 2008 08:12:53 +0100
+
+
+quicknote (0.7.3) unstable; urgency=low
+
+  * fixed small bugs
+  * move category
+
+ -- unknown <n800@axique.de>  Wed,  28 Jan 2008 08:12:53 +0100
+
+quicknote (0.7.2) unstable; urgency=low
+
+  * improved sync, fixed a small bug
+
+ -- unknown <n800@axique.de>  Wed,  26 Nov 2007 08:12:53 +0100
+
+quicknote (0.7.1) unstable; urgency=low
+
+  * improved sync
+
+ -- unknown <n800@axique.de>  Wed,  7 Nov 2007 08:12:53 +0100
+
+
+
+quicknote (0.7.0) unstable; urgency=low
+
+  * Initial Release.
+
+ -- unknown <n800@axique.de>  Mon,  6 Nov 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..90ad0c2
--- /dev/null
@@ -0,0 +1,21 @@
+Source: quicknote
+Section: user/other
+Priority: optional
+Maintainer: Christoph Würstle <n800 at axique de>
+Build-Depends: debhelper (>= 4.0.0), python2.5
+Standards-Version: 3.6.1
+
+Package: quicknote
+Architecture: all
+Depends: python2.5-hildon, python2.5-gtk2, python2.5-osso, python2.5-runtime
+Description: Quicknote
+XB-Maemo-Icon-26:
+ iVBORw0KGgoAAAANSUhEUgAAABoAAAAaCAYAAACpSkzOAAAAAXNSR0IArs4c6QAAAAZiS0dEAP8A
+ /wD/oL2nkwAAAAlwSFlzAAALEwAACxMBAJqcGAAAAAd0SU1FB9cLAwkANf/o52QAAAAddEVYdENv
+ bW1lbnQAQ3JlYXRlZCB3aXRoIFRoZSBHSU1Q72QlbgAAARNJREFUSMfNlsENhCAQRT9oPFiDmFCN
+ iRW7DViBnj1gEXog7AkXERUQk/0nCMpzZr4DRCkFAGjbVg3DgLIs8VRSSlBK0XUd4ZwDAHK9OI4j
+ +r5HKtV1jWVZtjnVgxSR2KKUHkFv6xLEGANj7F2QDyDkQ/IriBDiFqqfiQLZL/tuFl2jUDWsigfZ
+ aYs1SO67uRDiUDvfaE5Brs1ckIZV+Ih5m5vjKDPcGcIGvm6GIJDvj+pbm9PUmUV3pc1c+4jZG0Sv
+ amR3hivXRdnbhpn/jg25M8Et6G9aUGiHyEOOCbNmLtM8PibOIghJaZLOEFQjKWXybqCvcruIsixL
+ dj/QIoT8xpo6TZNa13W3+FScc1IUBQDgC9noggh/sT9oAAAAAElFTkSuQmCC
+
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/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..273008d
--- /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/quicknote
+       $(MAKE) install DESTDIR=$(CURDIR)/debian/quicknote
+
+
+# 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..a83382f
--- /dev/null
+++ b/po/de.po
@@ -0,0 +1,228 @@
+# German translations for quicknote package
+# German messages for quicknote.
+# Copyright (C) 2008 THE quicknote'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the quicknote package.
+# Christoph Würstle <chris@axique.de>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: quicknote\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-06-03 16:25+0400\n"
+"PO-Revision-Date: 2008-06-03 16:07+0400\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/quicknoteclasses/libhistory.py, line: 30
+msgid "History:"
+msgstr "Historie:"
+#
+# File: src/quicknoteclasses/libhistory.py, line: 43
+# File: src/quicknoteclasses/libsqldialog.py, line: 65
+msgid "Timestamp"
+msgstr "Zeitstempel"
+#
+# File: src/quicknoteclasses/libhistory.py, line: 44
+# File: src/quicknoteclasses/libnotizen.py, line: 234
+msgid "Note"
+msgstr "Notiz"
+#
+# File: src/quicknoteclasses/libkopfzeile.py, line: 48
+# File: src/quicknoteclasses/libkopfzeile.py, line: 106
+msgid "all"
+msgstr "alle"
+#
+# File: src/quicknoteclasses/libkopfzeile.py, line: 131
+msgid "Search:  "
+msgstr "Suche:"
+#
+# File: src/quicknoteclasses/libkopfzeile.py, line: 141
+msgid "Category:  "
+msgstr "Kategorie"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 105
+msgid "Last change: %d.%m.%y %H:%M"
+msgstr "Letzte Änderung: %d.%m.%y %H:%M"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 116
+msgid "Really delete?"
+msgstr "Wirklich löschen?"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 131
+msgid "new Note"
+msgstr "neue Notiz"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 150
+# File: src/quicknoteclasses/libquicknote.py, line: 183
+msgid "No note selected."
+msgstr "Keine Notiz markiert"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 209
+msgid "Titles"
+msgstr "Titel"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 213
+msgid "add note"
+msgstr "Notiz hinzufügen"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 217
+msgid "del note"
+msgstr "Notiz löschen"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 247
+msgid "History"
+msgstr "Historie"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 78
+msgid "Choose database file"
+msgstr "Datenbankdatei auswählen"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 95
+#, python-format
+msgid "%s is a note taking program; it is optimised for quick save and search of notes"
+msgstr "%s ist ein Programm um Notizen zu verwaltn; es ist für schnelles speichern und finden optimiert"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 117
+msgid "Select SQL export file"
+msgstr "Wähle SQL-Export-Datei"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 130
+msgid "This category can not be deleted"
+msgstr "Diese Kategorie kann nicht gelöscht werden"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 136
+msgid "Are you sure to delete the current category?"
+msgstr "Wirklich aktuell gewählte Kategorie löschen?"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 152
+msgid "Choose category"
+msgstr "Wähle Kategorie"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 196
+msgid "Sync"
+msgstr "Syncronisation"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 269
+msgid "Set DB file"
+msgstr "Setzte Datenbaknkdatei"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 273
+msgid "SQL History"
+msgstr "SQL Historie"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 277
+msgid "Sync notes"
+msgstr "Syncronisiere Notizen"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 281
+msgid "Quit"
+msgstr "Beenden"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 285
+msgid "File"
+msgstr "Datei"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 296
+msgid "delete"
+msgstr "löschen"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 300
+msgid "move to category"
+msgstr "verschiebe zu Kategorie"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 304
+msgid "Category"
+msgstr "Kategorie"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 310
+msgid "About"
+msgstr "Über"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 314
+msgid "Help"
+msgstr "Hilfe"
+#
+# File: src/quicknoteclasses/libsqldialog.py, line: 46
+msgid "SQL History (the past two days):"
+msgstr "SQL Historie (der letzten 2 Tage):"
+#
+# File: src/quicknoteclasses/libsqldialog.py, line: 67
+msgid "Parameter"
+msgstr "Parameter"
+#
+# File: src/quicknoteclasses/libsqldialog.py, line: 105
+msgid "%d.%m.%y %H:%M:%S "
+msgstr "%d.%m.%y %H:%M:%S "
+#
+# File: src/quicknoteclasses/libsync.py, line: 43
+msgid "Sync process"
+msgstr "Syncronisationsprozess"
+#
+# File: src/quicknoteclasses/libsync.py, line: 48
+msgid "Sync process running...please wait"
+msgstr "Syncronisationsprozess läuft...bitte warten"
+#
+# File: src/quicknoteclasses/libsync.py, line: 50
+msgid "(this can take some minutes)"
+msgstr "(dies kann einige Minuten dauern)"
+#
+# File: src/quicknoteclasses/libsync.py, line: 90
+msgid "Query"
+msgstr "Anfrage"
+#
+# File: src/quicknoteclasses/libsync.py, line: 242
+msgid "Syncserver running..."
+msgstr "Syncronisationsprozess läuft..."
+#
+# File: src/quicknoteclasses/libsync.py, line: 250
+msgid "Could not start SyncServer. Check IP, port settings."
+msgstr "Konnte Syncronisationsserver nicht starten. Bitte IP und Port-Einstellungen überprüfen."
+#
+# File: src/quicknoteclasses/libsync.py, line: 263
+# File: src/quicknoteclasses/libsync.py, line: 409
+msgid "SyncServer stopped"
+msgstr "Syncronisationsprozess gestoppt."
+#
+# File: src/quicknoteclasses/libsync.py, line: 280
+# File: src/quicknoteclasses/libsync.py, line: 316
+# File: src/quicknoteclasses/libsync.py, line: 324
+# File: src/quicknoteclasses/libsync.py, line: 331
+# File: src/quicknoteclasses/libsync.py, line: 422
+msgid "no sync process (at the moment)"
+msgstr "Kein Syncronisationsprozess (im Moment)"
+#
+# File: src/quicknoteclasses/libsync.py, line: 289
+msgid "sync process running"
+msgstr "Syncronisationsprozess läuft"
+#
+# File: src/quicknoteclasses/libsync.py, line: 318
+msgid "Synchronization successfully completed"
+msgstr "Synchronisation erfolgreich beendet"
+#
+# File: src/quicknoteclasses/libsync.py, line: 325
+msgid "The clocks are not synchronized between stations"
+msgstr "Zeit differiert zu viel zwischen den Systemen"
+#
+# File: src/quicknoteclasses/libsync.py, line: 332
+msgid "Sync failed, reason: "
+msgstr "Sync gescheitert. Fehler:"
+#
+# File: src/quicknoteclasses/libsync.py, line: 387
+msgid "Local SyncServer (port "
+msgstr "LokalerSync-Server (Port "
+#
+# File: src/quicknoteclasses/libsync.py, line: 405
+msgid "Start/Stop SyncServer"
+msgstr "SyncServer starten/stoppen"
+#
+# File: src/quicknoteclasses/libsync.py, line: 412
+msgid "Remote SyncServer (port "
+msgstr "RemoteSync-Server (Port "
+#
+# File: src/quicknoteclasses/libsync.py, line: 418
+msgid "Connect to remote SyncServer"
+msgstr "Verbinde zu Remote-SyncServer"
diff --git a/po/ru.po b/po/ru.po
new file mode 100644 (file)
index 0000000..398d498
--- /dev/null
+++ b/po/ru.po
@@ -0,0 +1,226 @@
+# Russian translations for quicknote package
+# Copyright (C) 2008 THE quicknote'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the quicknote package.
+# Nikolay Logvinov <NLogvinov@gmail.com>, 2008.
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: quicknote\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2008-06-03 16:25+0400\n"
+"PO-Revision-Date: 2008-06-03 15:38+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/quicknoteclasses/libhistory.py, line: 30
+msgid "History:"
+msgstr "История изменений"
+#
+# File: src/quicknoteclasses/libhistory.py, line: 43
+# File: src/quicknoteclasses/libsqldialog.py, line: 65
+msgid "Timestamp"
+msgstr "Время"
+#
+# File: src/quicknoteclasses/libhistory.py, line: 44
+# File: src/quicknoteclasses/libnotizen.py, line: 234
+msgid "Note"
+msgstr "Заметка"
+#
+# File: src/quicknoteclasses/libkopfzeile.py, line: 48
+# File: src/quicknoteclasses/libkopfzeile.py, line: 106
+msgid "all"
+msgstr "все"
+#
+# File: src/quicknoteclasses/libkopfzeile.py, line: 131
+msgid "Search:  "
+msgstr "Поиск:  "
+#
+# File: src/quicknoteclasses/libkopfzeile.py, line: 141
+msgid "Category:  "
+msgstr "Категория:  "
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 105
+msgid "Last change: %d.%m.%y %H:%M"
+msgstr "Редактировалась %d.%m.%y в %H:%M"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 116
+msgid "Really delete?"
+msgstr "Всё-таки удалять?"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 131
+msgid "new Note"
+msgstr "__ новая __"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 150
+# File: src/quicknoteclasses/libquicknote.py, line: 183
+msgid "No note selected."
+msgstr "Выберите сначала заметку"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 209
+msgid "Titles"
+msgstr "Заголовки"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 213
+msgid "add note"
+msgstr "добавить Заметку"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 217
+msgid "del note"
+msgstr "удалить Заметку"
+#
+# File: src/quicknoteclasses/libnotizen.py, line: 247
+msgid "History"
+msgstr "История изменений"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 78
+msgid "Choose database file"
+msgstr "Выберите файл для хранения заметок"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 95
+msgid "%s is a note taking program; it is optimised for quick save and search of notes"
+msgstr "%s - приложение для создания, хранения и быстрого поиска заметок"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 117
+msgid "Select SQL export file"
+msgstr "Выберите файл для экспорта запросов SQL"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 130
+msgid "This category can not be deleted"
+msgstr "Эту категорию нельзя удалять"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 136
+msgid "Are you sure to delete the current category?"
+msgstr "Вы уверены в удалении текущей категории?"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 152
+msgid "Choose category"
+msgstr "Категория:"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 196
+msgid "Sync"
+msgstr "Синхронизация"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 269
+msgid "Set DB file"
+msgstr "Хранилище"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 273
+msgid "SQL History"
+msgstr "SQL запросы"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 277
+msgid "Sync notes"
+msgstr "Синхронизация"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 281
+msgid "Quit"
+msgstr "Выход"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 285
+msgid "File"
+msgstr "Файл"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 296
+msgid "delete"
+msgstr "убрать"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 300
+msgid "move to category"
+msgstr "назначить"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 304
+msgid "Category"
+msgstr "Категория"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 310
+msgid "About"
+msgstr "О программе"
+#
+# File: src/quicknoteclasses/libquicknote.py, line: 314
+msgid "Help"
+msgstr "Справка"
+#
+# File: src/quicknoteclasses/libsqldialog.py, line: 46
+msgid "SQL History (the past two days):"
+msgstr "SQL запросы (за 2 последних дня)"
+#
+# File: src/quicknoteclasses/libsqldialog.py, line: 67
+msgid "Parameter"
+msgstr "Параметры"
+#
+# File: src/quicknoteclasses/libsqldialog.py, line: 105
+msgid "%d.%m.%y %H:%M:%S "
+msgstr "%d.%m.%y %H:%M:%S "
+#
+# File: src/quicknoteclasses/libsync.py, line: 43
+msgid "Sync process"
+msgstr "Синхронизация"
+#
+# File: src/quicknoteclasses/libsync.py, line: 48
+msgid "Sync process running...please wait"
+msgstr "Синхронизируются заметки, подождите ..."
+#
+# File: src/quicknoteclasses/libsync.py, line: 50
+msgid "(this can take some minutes)"
+msgstr "(некоторое время)"
+#
+# File: src/quicknoteclasses/libsync.py, line: 90
+msgid "Query"
+msgstr "Запрос"
+#
+# File: src/quicknoteclasses/libsync.py, line: 242
+msgid "Syncserver running..."
+msgstr "Сервер синхронизации работает ..."
+#
+# File: src/quicknoteclasses/libsync.py, line: 250
+msgid "Could not start SyncServer. Check IP, port settings."
+msgstr "Не удаётся стартовать Сервер синхронизации. Проверьте IP-адрес и порт."
+#
+# File: src/quicknoteclasses/libsync.py, line: 263
+# File: src/quicknoteclasses/libsync.py, line: 409
+msgid "SyncServer stopped"
+msgstr "Сервер синхронизации остановлен"
+#
+# File: src/quicknoteclasses/libsync.py, line: 280
+# File: src/quicknoteclasses/libsync.py, line: 316
+# File: src/quicknoteclasses/libsync.py, line: 324
+# File: src/quicknoteclasses/libsync.py, line: 331
+# File: src/quicknoteclasses/libsync.py, line: 422
+msgid "no sync process (at the moment)"
+msgstr "сейчас синхронизация не выполняется"
+#
+# File: src/quicknoteclasses/libsync.py, line: 289
+msgid "sync process running"
+msgstr "идёт синхронизация"
+#
+# File: src/quicknoteclasses/libsync.py, line: 318
+msgid "Synchronization successfully completed"
+msgstr "Синхронизация успешно завершенa"
+#
+# File: src/quicknoteclasses/libsync.py, line: 325
+msgid "The clocks are not synchronized between stations"
+msgstr "Согласуйте время между системами"
+#
+# File: src/quicknoteclasses/libsync.py, line: 332
+msgid "Sync failed, reason: "
+msgstr "Синхронизовать заметки не удалось по причине: "
+#
+# File: src/quicknoteclasses/libsync.py, line: 387
+msgid "Local SyncServer (port "
+msgstr "Местный сервер синхронизации (порт "
+#
+# File: src/quicknoteclasses/libsync.py, line: 405
+msgid "Start/Stop SyncServer"
+msgstr "Запуск/Остановка сервера синхронизации"
+#
+# File: src/quicknoteclasses/libsync.py, line: 412
+msgid "Remote SyncServer (port "
+msgstr "Удалённый сервер синхронизации (порт "
+#
+# File: src/quicknoteclasses/libsync.py, line: 418
+msgid "Connect to remote SyncServer"
+msgstr "Установить соединение"
diff --git a/setup.py b/setup.py
new file mode 100644 (file)
index 0000000..0fcf954
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,25 @@
+#!/usr/bin/env python2.5
+# -*- coding: utf-8 -*-
+
+
+from distutils.core import setup
+
+
+setup(
+       name='quicknote',
+       version='1.0',
+       scripts=['src/quicknote.py'],
+       packages=['quicknote'],
+       package_dir={'quicknote': 'src/'},
+       data_files = [
+               ('share/icons/hicolor/26x26/hildon', ['data/low/quicknote.png']),
+               ('share/icons/hicolor/40x40/hildon', ['data/40/quicknote.png']),
+               #('share/icons/hicolor/48x48/apps', ['data/48/quicknote.png']),
+               ('share/icons/hicolor/scalable/hildon', ['data/scale/quicknote.png']),
+               ('share/applications/hildon', ['data/quicknote.desktop']),
+               ('share/dbus-1/services', ['data/quicknote.service']),
+               # I18N
+               ('share/locale/de/LC_MESSAGES', ['locale/de/LC_MESSAGES/quicknote.mo']),
+               ('share/locale/ru/LC_MESSAGES', ['locale/ru/LC_MESSAGES/quicknote.mo']),
+       ]
+)
diff --git a/src/__init__.py b/src/__init__.py
new file mode 100755 (executable)
index 0000000..8b13789
--- /dev/null
@@ -0,0 +1 @@
+
diff --git a/src/fakehildon.py b/src/fakehildon.py
new file mode 100644 (file)
index 0000000..cf32c9f
--- /dev/null
@@ -0,0 +1,31 @@
+#/usr/bin/env python2.5
+# -*- coding: utf-8 -*-
+
+import gtk
+
+
+class FileChooserDialog(gtk.FileChooserDialog):
+       """
+       @bug The buttons currently don't do anything
+       """
+
+       def __init__(self, *args, **kwds):
+               super(FileChooserDialog, self).__init__(*args, **kwds)
+               self.add_button(gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL)
+               self.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
+
+
+class Window(gtk.Window):
+
+       def __init__(self):
+               super(Window, self).__init__(gtk.WINDOW_TOPLEVEL)
+               self.set_default_size(700, 500)
+
+
+class Program(object):
+
+       def __init__(self):
+               pass
+
+       def add_window(self, window):
+               pass
diff --git a/src/libhistory.py b/src/libhistory.py
new file mode 100644 (file)
index 0000000..4c23f73
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/env python2.5
+# -*- coding: utf-8 -*-
+
+"""
+ Copyright (C) 2007 Christoph Würstle
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+"""
+
+
+import gtk
+
+
+try:
+       _
+except NameError:
+       _ = lambda x: x
+
+
+class Dialog(gtk.Dialog):
+
+       def __init__(self, daten = None):
+               gtk.Dialog.__init__(self, _("History:"), None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, (gtk.STOCK_CANCEL, gtk.RESPONSE_REJECT, gtk.STOCK_OK, gtk.RESPONSE_ACCEPT))
+               self.set_position(gtk.WIN_POS_CENTER)
+
+               self.liststore = gtk.ListStore(int, str, str, str, str)
+               #pcdatum, datum, sql, param # param schön
+
+               # 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()
+
+               # create the TreeViewColumns to display the data
+               self.tvcolumn1 = gtk.TreeViewColumn(_('Timestamp'))
+               self.tvcolumn2 = gtk.TreeViewColumn(_('Note'))
+               # add columns to treeview
+               self.treeview.append_column(self.tvcolumn1)
+               self.treeview.append_column(self.tvcolumn2)
+
+               # add the cells to the columns - 2 in the first
+               self.tvcolumn1.pack_start(self.cell1, True)
+               self.tvcolumn2.pack_start(self.cell2, True)
+               self.tvcolumn1.set_attributes(self.cell1, text = 1) #Spalten setzten hier!!!!
+               self.tvcolumn2.set_attributes(self.cell2, text = 4)
+
+               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.vbox.pack_start(scrolled_window, expand = True, fill = True, padding = 0)
+
+               self.liststore.clear()
+
+               if daten is not None:
+                       for data in daten:
+                               self.liststore.append(data)
+
+       def get_selected_row(self):
+               path = self.treeview.get_cursor()[0]
+               if path is None or path == "":
+                       return None
+
+               iter1 = self.treeview.get_model().get_iter(path)
+               return self.treeview.get_model().get(iter1, 0, 1, 2, 3, 4)
diff --git a/src/libkopfzeile.py b/src/libkopfzeile.py
new file mode 100644 (file)
index 0000000..90d297d
--- /dev/null
@@ -0,0 +1,142 @@
+#!/usr/bin/env python2.5
+# -*- coding: utf-8 -*-
+
+"""
+ Copyright (C) 2007 Christoph Würstle
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+"""
+
+
+import logging
+
+import gobject
+import gtk
+
+
+try:
+       _
+except NameError:
+       _ = lambda x: x
+
+
+class Kopfzeile(gtk.HBox):
+
+       __gsignals__ = {
+               'reload_notes' : (gobject.SIGNAL_RUN_LAST, gobject.TYPE_NONE, ()),
+       }
+
+       def __init__(self, db):
+               self.lastCategory = ""
+               self.db = db
+
+               gtk.HBox.__init__(self, homogeneous = False, spacing = 3)
+               logging.info("libkopfzeile, init")
+
+               categoryHBox = gtk.HBox()
+               self.pack_start(categoryHBox, expand = False, fill = True, padding = 0)
+
+               label = gtk.Label(_("Category:  "))
+               categoryHBox.pack_start(label, expand = False, fill = True, padding = 0)
+
+               self.comboCategory = gtk.combo_box_entry_new_text()
+               categoryHBox.pack_start(self.comboCategory, expand = True, fill = True, padding = 0)
+               self.loadCategories()
+               self.comboCategory.connect("changed", self.comboCategoryChanged, None)
+
+               searchHBox = gtk.HBox()
+               self.pack_start(searchHBox, expand = True, fill = True, padding = 0)
+
+               label = gtk.Label(_("Search:  "))
+               searchHBox.pack_start(label, expand = False, fill = True, padding = 0)
+
+               self.searchEntry = gtk.Entry()
+               searchHBox.pack_start(self.searchEntry, expand = True, fill = True, padding = 0)
+               self.searchEntry.connect("changed", self.searchEntryChanged, None)
+
+       def comboCategoryChanged(self, widget = None, data = None):
+               logging.debug("comboCategoryChanged")
+               if self.lastCategory != self.comboCategory.get_active():
+                       sql = "UPDATE categories SET liste = ? WHERE id = 1"
+                       self.db.speichereSQL(sql, (self.comboCategory.get_active(), ))
+
+               self.emit("reload_notes")
+
+       def searchEntryChanged(self, widget = None, data = None):
+               logging.debug("searchEntryChanged")
+               self.emit("reload_notes")
+
+       def getCategory(self):
+               entry = self.comboCategory.get_child()
+               category = entry.get_text()
+               if category == _("all"):
+                       category = "%"
+               if category == "":
+                       category = "undefined"
+                       self.comboCategory.set_active(1)
+                       self.comboCategory.show()
+               return category
+
+       def defineThisCategory(self):
+               category = self.getCategory()
+
+               model = self.comboCategory.get_model()
+               n = len(self.comboCategory.get_model())
+               i = 0
+               active = -1
+               cats = []
+               while i < n:
+                       if (model[i][0] == category):
+                               #self.comboCategory.set_active(i)
+                               active = i
+                       if (model[i][0]!= "%"):
+                               cats.append(model[i][0])
+                       i += 1
+
+               if (active == -1) and (category!= "%"):
+                       self.comboCategory.append_text(category)
+                       sql = "INSERT INTO categories  (id, liste) VALUES (0, ?)"
+                       self.db.speichereSQL(sql, (category, ))
+                       self.comboCategory.set_active(i)
+
+       def getSearchPattern(self):
+               return self.searchEntry.get_text()
+
+       def loadCategories(self):
+               sql = "CREATE TABLE categories (id TEXT , liste TEXT)"
+               self.db.speichereSQL(sql)
+
+               sql = "SELECT id, liste FROM categories WHERE id = 0 ORDER BY liste"
+               rows = self.db.ladeSQL(sql)
+               cats = []
+               if rows is not None and 0 < len(rows):
+                       for row in rows:
+                               cats.append(row[1])
+
+               sql = "SELECT * FROM categories WHERE id = 1"
+               rows = self.db.ladeSQL(sql)
+               if rows is None or len(rows) == 0:
+                       sql = "INSERT INTO categories (id, liste) VALUES (1, 1)"
+                       self.db.speichereSQL(sql)
+
+               #self.comboCategory.clear()
+               while 0 < len(self.comboCategory.get_model()):
+                       self.comboCategory.remove_text(0)
+
+               self.comboCategory.append_text(_('all'))
+               self.comboCategory.append_text('undefined')
+
+               if cats is not None and 0 < len(cats):
+                       for cat in cats:
+                               self.comboCategory.append_text(cat)
+
+               sql = "SELECT * FROM categories WHERE id = 1"
+               rows = self.db.ladeSQL(sql)
+               if rows is not None and 0 < len(rows):
+                       self.comboCategory.set_active(int(rows[0][1]))
+               else:
+                       self.comboCategory.set_active(1)
+
+               self.lastCategory = self.comboCategory.get_active()
diff --git a/src/libnotizen.py b/src/libnotizen.py
new file mode 100644 (file)
index 0000000..a7f6e6a
--- /dev/null
@@ -0,0 +1,254 @@
+#!/usr/bin/env python2.5
+# -*- coding: utf-8 -*-
+
+"""
+ Copyright (C) 2007 Christoph Würstle
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+@todo Add hiding of the history button on zoom
+"""
+
+import time
+import logging
+import uuid
+
+import gobject
+import gtk
+
+import simple_list
+
+
+try:
+       _
+except NameError:
+       _ = lambda x: x
+
+
+class Notizen(gtk.HBox):
+
+       def __init__(self, db, topBox):
+               self.db = db
+               self.topBox = topBox
+               self.noteid = -1
+               self.pos = -1
+               self.note = None #Last notetext
+               self.category = ""
+
+               gtk.HBox.__init__(self, homogeneous = False, spacing = 0)
+               logging.info("libnotizen, init")
+
+               self.noteslist = simple_list.SimpleList()
+               self.noteslist.set_eventfunction__cursor_changed(self.noteslist_changed)
+
+               self.noteslist.set_size_request(250, -1)
+
+               vbox = gtk.VBox(homogeneous = False, spacing = 0)
+
+               frame = gtk.Frame(_("Titles"))
+               frame.add(self.noteslist)
+               vbox.pack_start(frame, expand = True, fill = True, padding = 3)
+
+               buttonHBox = gtk.HBox()
+               vbox.pack_start(buttonHBox, expand = False, fill = True, padding = 3)
+
+               button = gtk.Button(stock = gtk.STOCK_ADD)
+               button.connect("clicked", self.add_note, None)
+               buttonHBox.pack_start(button, expand = True, fill = True, padding = 3)
+
+               button = gtk.Button(stock = gtk.STOCK_DELETE)
+               button.connect("clicked", self.del_note, None)
+               buttonHBox.pack_start(button, expand = True, fill = True, padding = 3)
+
+               self.pack_start(vbox, expand = False, fill = True, padding = 3)
+
+               self.textviewNote = gtk.TextView()
+               self.textviewNote.connect("focus-out-event", self.saveNote, "focus-out-event")
+               buf = self.textviewNote.get_buffer()
+               buf.set_text("")
+               buf.connect("changed", self.noteChanged, None)
+
+               #self.textviewNotiz.set_size_request(-1, 50)
+               self.scrolled_window = gtk.ScrolledWindow()
+               self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+               self.scrolled_window.add(self.textviewNote)
+
+               frame = gtk.Frame(_("Note"))
+               frame.add(self.scrolled_window)
+
+               vbox = gtk.VBox(homogeneous = False, spacing = 0)
+               vbox.pack_start(frame, expand = True, fill = True, padding = 3)
+
+               hbox = gtk.HBox(homogeneous = False, spacing = 0)
+
+               self.statuslabel = gtk.Label("Test")
+               self.statuslabel.set_alignment(0.0, 0.5)
+               hbox.pack_start(self.statuslabel, expand = True, fill = True, padding = 3)
+
+               button = gtk.Button(_("History"))
+               button.connect("clicked", self.show_history, None)
+               hbox.pack_start(button, expand = True, fill = True, padding = 3)
+
+               vbox.pack_start(hbox, expand = False, fill = True, padding = 3)
+
+               self.pack_start(vbox, expand = True, fill = True, padding = 3)
+
+               self.loadNotes()
+               self.topBox.connect("reload_notes", self.loadNotes)
+
+       def set_wordwrap(self, enableWordWrap):
+               if enableWordWrap:
+                       self.scrolled_window.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC)
+                       self.textviewNote.set_wrap_mode(gtk.WRAP_WORD)
+               else:
+                       self.scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
+                       self.textviewNote.set_wrap_mode(gtk.WRAP_NONE)
+
+       def getTitle(self, buf):
+               eol = buf.find("\n")
+               if -1 == eol:
+                       eol = len(buf)
+               title = buf[:eol]
+               return title
+
+       def noteChanged(self, widget = None, data = None):
+               if self.pos == -1 or self.noteid == -1:
+                       return
+
+               buf = self.textviewNote.get_buffer().get_text(self.textviewNote.get_buffer().get_start_iter(), self.textviewNote.get_buffer().get_end_iter())
+
+               title = self.getTitle(buf)
+               value, key = self.noteslist.get_item(self.pos)
+
+               if value != title:
+                       self.noteslist.change_item(self.pos, title, self.noteid)
+
+       def saveNote(self, widget = None, data = None, data2 = None):
+               logging.info("saveNote params: pos:"+str(self.pos)+" noteid:"+str(self.noteid))
+               #print "params:", data, data2
+               buf = self.textviewNote.get_buffer().get_text(self.textviewNote.get_buffer().get_start_iter(), self.textviewNote.get_buffer().get_end_iter())
+               if buf is None or len(buf) == 0:
+                       return
+
+               if buf == self.note:
+                       return
+
+               logging.info("Saving note: "+buf)
+               if self.pos == -1 or self.noteid == -1:
+                       self.pos = -1
+                       self.noteid = str(uuid.uuid4())
+                       self.db.saveNote(self.noteid, buf, self.category)
+                       self.noteslist.append_item(self.getTitle(buf), self.noteid)
+                       self.pos = self.noteslist.select_last_item()
+               else:
+                       self.db.saveNote(self.noteid, buf, self.category)
+
+               self.topBox.defineThisCategory()
+
+       def setFocus(self):
+               self.textviewNote.grab_focus()
+               return False
+
+       def noteslist_changed(self, data = None, data2 = None):
+               if self.pos != -1:
+                       self.saveNote()
+
+               try:
+                       (pos, key, value) = self.noteslist.get_selection_data()
+                       if (pos == -1):
+                               return
+               except StandardError:
+                       if data != "new":
+                               return
+                       key = None
+
+               if key == "new" or data == "new":
+                       #both methods supported click add note or new note (first one disabled)
+                       self.noteid = str(uuid.uuid4())
+                       self.db.saveNote(self.noteid, "", self.category)
+                       self.pos = -1
+                       self.noteslist.append_item("", self.noteid)
+                       self.textviewNote.get_buffer().set_text("")
+                       self.pos = self.noteslist.select_last_item()
+               else:
+                       self.pos = pos
+                       self.noteid, pcdatum, self.category, self.note = self.db.loadNote(key)
+                       self.statuslabel.set_text(time.strftime(_("Last change: %d.%m.%y %H:%M"), time.localtime(pcdatum)))
+                       buf = self.textviewNote.get_buffer()
+                       buf.set_text(self.note)
+
+               gobject.timeout_add(200, self.setFocus)
+
+       def add_note(self, widget = None, data = None):
+               self.noteslist_changed("new")
+
+       def del_note(self, widget = None, data = None):
+               if (self.noteid == -1):
+                       return
+               mbox = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO, _("Really delete?"))
+               response = mbox.run()
+               mbox.hide()
+               mbox.destroy()
+               if response == gtk.RESPONSE_YES:
+                       self.db.delNote(self.noteid)
+                       self.noteid = -1
+                       self.noteslist.remove_item(self.pos)
+                       self.pos = -1
+                       self.textviewNote.get_buffer().set_text("")
+
+       def loadNotes(self, data = None):
+               logging.info("loadNotes params: pos:"+str(self.pos)+" noteid:"+str(self.noteid))
+               self.noteslist.clear_items()
+               self.noteslist.append_item(_("new Note"), "new")
+
+               self.category = self.topBox.getCategory()
+               search = self.topBox.getSearchPattern()
+               notes = self.db.searchNotes(search, self.category)
+
+               if notes is not None:
+                       for note in notes:
+                               noteid, category, noteText = note
+                               title = self.getTitle(noteText)
+                               self.noteslist.append_item(title, noteid)
+
+               self.noteid = -1
+               self.pos = -1
+               self.textviewNote.get_buffer().set_text("")
+
+       def show_history(self, widget = None, data = None, label = None):
+               if self.noteid == -1:
+                       mbox =  gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_INFO, gtk.BUTTONS_OK, _("No note selected."))
+                       response = mbox.run()
+                       mbox.hide()
+                       mbox.destroy()
+                       return
+
+               rows = self.db.getNoteHistory(self.noteid)
+
+               import libhistory
+               dialog = libhistory.Dialog()
+
+               lastNoteStr = ""
+               for row in rows:
+                       #for x in row:
+                       #       print x
+                       daten = row[4][1]
+                       if (daten != "")and(lastNoteStr != daten):
+                               lastNoteStr = daten
+                               dialog.liststore.append([row[0], row[1], row[2], row[3], daten+"\n"])
+
+               dialog.vbox.show_all()
+               dialog.set_size_request(600, 380)
+
+               if dialog.run() == gtk.RESPONSE_ACCEPT:
+                       print "saving"
+                       self.saveNote()
+                       data = dialog.get_selected_row()
+                       if data is not None:
+                               self.db.speichereSQL(data[2], data[3].split(" <<Tren-ner>> "), rowid = self.noteid)
+                               logging.info("loading History")
+                               self.noteslist_changed()
+
+               dialog.destroy()
diff --git a/src/libquicknote.py b/src/libquicknote.py
new file mode 100644 (file)
index 0000000..a89cb94
--- /dev/null
@@ -0,0 +1,355 @@
+#/usr/bin/env python2.5
+# -*- coding: utf-8 -*-
+
+"""
+ Copyright (C) 2007 Christoph Würstle
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+
+@todo Add an edit menu with select all, cut, copy, and paste
+@todo Add undo directly into the UI
+@todo Add Note Export (txt File) and Export All (json dump?)
+@todo Save word wrap and zoom setting 
+"""
+
+
+import os
+import gc
+import logging
+
+import gtk
+
+try:
+       import hildon
+       IS_HILDON = True
+except ImportError:
+       import fakehildon as hildon
+       IS_HILDON = False
+
+try:
+       import osso
+except ImportError:
+       osso = None
+
+import libspeichern
+import libkopfzeile
+import libnotizen
+import libsync
+
+
+try:
+       _
+except NameError:
+       _ = lambda x: x
+
+
+class quicknoteclass(hildon.Program):
+
+       __pretty_app_name__ = "quicknote"
+       __app_name__ = "quicknote"
+       __version__ = "0.7.5"
+
+       def __init__(self):
+               super(quicknoteclass, self).__init__()
+
+               home_dir = os.path.expanduser('~')
+               dblog = os.path.join(home_dir, "quicknote.log")
+
+               # define a Handler which writes INFO messages or higher to the sys.stderr
+               console = logging.StreamHandler()
+               console.setLevel(logging.DEBUG)
+               # 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 quicknote')
+
+               if osso is not None:
+                       self._osso_c = osso.Context(self.__app_name__, self.__version__, False)
+                       self.device = osso.DeviceState(self._osso_c)
+                       self.device.set_device_state_callback(self._on_device_state_change, 0)
+               else:
+                       self._osso_c = None
+
+               #Get the Main Window, and connect the "destroy" event
+               self.window = hildon.Window()
+               self.add_window(self.window)
+
+               self.window.set_title(self.__pretty_app_name__)
+               self.window.connect("delete_event", self.delete_event)
+               self.window.connect("destroy", self.destroy)
+               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.prepare_sync_dialog()
+
+               #Create GUI main vbox
+               vbox = gtk.VBox(homogeneous = False, spacing = 0)
+
+               #Create Menu and apply it for hildon
+               filemenu = gtk.Menu()
+
+               menu_items = gtk.MenuItem(_("Set DB file"))
+               filemenu.append(menu_items)
+               menu_items.connect("activate", self.set_db_file, None)
+
+               menu_items = gtk.MenuItem(_("SQL History"))
+               filemenu.append(menu_items)
+               menu_items.connect("activate", self.view_sql_history, None)
+
+               menu_items = gtk.MenuItem(_("Sync notes"))
+               filemenu.append(menu_items)
+               menu_items.connect("activate", self.sync_notes, None)
+
+               menu_items = gtk.MenuItem(_("Quit"))
+               filemenu.append(menu_items)
+               menu_items.connect("activate", self.destroy, None)
+
+               file_menu = gtk.MenuItem(_("File"))
+               file_menu.show()
+               file_menu.set_submenu(filemenu)
+
+               categorymenu = gtk.Menu()
+
+               menu_items = gtk.MenuItem(_("delete"))
+               categorymenu.append(menu_items)
+               menu_items.connect("activate", self.delete_Category, None)
+
+               menu_items = gtk.MenuItem(_("move to category"))
+               categorymenu.append(menu_items)
+               menu_items.connect("activate", self.move_Category, None)
+
+               category_menu = gtk.MenuItem(_("Category"))
+               category_menu.show()
+               category_menu.set_submenu(categorymenu)
+
+               viewmenu = gtk.Menu()
+
+               menu_items = gtk.MenuItem(_("Word Wrap"))
+               viewmenu.append(menu_items)
+               menu_items.connect("activate", self.toggle_word_wrap, None)
+               self._wordWrapEnabled = False
+
+               view_menu = gtk.MenuItem(_("View"))
+               view_menu.show()
+               view_menu.set_submenu(viewmenu)
+
+               helpmenu = gtk.Menu()
+
+               menu_items = gtk.MenuItem(_("About"))
+               helpmenu.append(menu_items)
+               menu_items.connect("activate", self.show_about, None)
+
+               help_menu = gtk.MenuItem(_("Help"))
+               help_menu.show()
+               help_menu.set_submenu(helpmenu)
+
+               menu_bar = gtk.MenuBar()
+               menu_bar.show()
+               menu_bar.append (file_menu)
+               menu_bar.append (category_menu)
+               menu_bar.append (view_menu)
+               menu_bar.append (help_menu)
+
+               menu_bar.show()
+               if IS_HILDON:
+                       menu = gtk.Menu()
+                       for child in menu_bar.get_children():
+                               child.reparent(menu)
+                       self.window.set_menu(menu)
+                       menu_bar.destroy()
+               else:
+                       vbox.pack_start(menu_bar, False, False, 0)
+
+               #Create GUI elements
+               self.topBox = libkopfzeile.Kopfzeile(self.db)
+               vbox.pack_start(self.topBox, False, False, 0)
+
+               self.notizen = libnotizen.Notizen(self.db, self.topBox)
+               vbox.pack_start(self.notizen, True, True, 0)
+
+               self.window.add(vbox)
+               self.window.show_all()
+               self.toggle_word_wrap()
+
+       def set_db_file(self, widget = None, data = None):
+               dlg = hildon.FileChooserDialog(parent=self.window, action=gtk.FILE_CHOOSER_ACTION_SAVE)
+
+               if self.db.ladeDirekt('datenbank'):
+                       dlg.set_filename(self.db.ladeDirekt('datenbank'))
+
+               dlg.set_title(_("Choose database file"))
+               if dlg.run() == gtk.RESPONSE_OK:
+                       fileName = dlg.get_filename()
+                       self.db.speichereDirekt('datenbank', fileName)
+
+                       self.db.openDB()
+                       self.topBox.loadCategories()
+                       self.notizen.loadNotes()
+                       dlg.destroy()
+
+       def show_about(self, widget = None, data = None):
+               dialog = gtk.AboutDialog()
+               dialog.set_position(gtk.WIN_POS_CENTER)
+               dialog.set_name(self.__pretty_app_name__)
+               dialog.set_version(self.__version__)
+               dialog.set_copyright("")
+               dialog.set_website("http://axique.de/index.php?f=Quicknote")
+               comments = _("%s is a note taking program; it is optimised for quick save and search of notes") % self.__pretty_app_name__
+               dialog.set_comments(comments)
+               dialog.run()
+               dialog.destroy()
+
+       def view_sql_history(self, widget = None, data = None, data2 = None):
+               import libsqldialog
+               sqldiag = libsqldialog.sqlDialog(self.db)
+               res = sqldiag.run()
+               sqldiag.hide()
+               if res == 444:
+                       logging.info("exporting sql")
+
+                       dlg = hildon.FileChooserDialog(parent=self.window, action=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 delete_Category(self, widget = None, data = None):
+               if (self.topBox.getCategory() == "%") or (self.topBox.getCategory() == "undefined"):
+                       mbox = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("This category can not be deleted"))
+                       response = mbox.run()
+                       mbox.hide()
+                       mbox.destroy()
+                       return
+
+               mbox = gtk.MessageDialog(None, gtk.DIALOG_MODAL, gtk.MESSAGE_WARNING, gtk.BUTTONS_YES_NO, _("Are you sure to delete the current category?"))
+               response = mbox.run()
+               mbox.hide()
+               mbox.destroy()
+               if response == gtk.RESPONSE_YES:
+                       sql = "UPDATE notes SET category = ? WHERE category = ?"
+                       self.db.speichereSQL(sql, ("undefined", self.topBox.getCategory()))
+                       sql = "DELETE FROM categories WHERE liste = ?"
+                       self.db.speichereSQL(sql, (self.topBox.getCategory(), ))
+                       model = self.topBox.comboCategory.get_model()
+                       pos = self.topBox.comboCategory.get_active()
+                       if (pos>1):
+                               self.topBox.comboCategory.remove_text(pos)
+                               self.topBox.comboCategory.set_active(0)
+
+       def move_Category(self, widget = None, data = None):
+               dialog = gtk.Dialog(_("Choose category"), self.window, 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)
+               comboCategory = gtk.combo_box_new_text()
+
+               comboCategory.append_text('undefined')
+               sql = "SELECT id, liste FROM categories WHERE id = 0 ORDER BY liste"
+               rows = self.db.ladeSQL(sql)
+               for row in rows:
+                       comboCategory.append_text(row[1])
+
+               dialog.vbox.pack_start(comboCategory, True, True, 0)
+
+               dialog.vbox.show_all()
+               #dialog.set_size_request(400, 300)
+
+               if dialog.run() == gtk.RESPONSE_ACCEPT:
+                       n = comboCategory.get_active()
+                       if (n>-1) and (self.notizen.noteid!= -1):
+                               model = comboCategory.get_model()
+                               active = comboCategory.get_active()
+                               if active < 0:
+                                       return None
+                               cat_id = model[active][0]
+
+                               noteid, category, note = self.db.loadNote(self.notizen.noteid)
+                               #print noteid, category, cat_id
+                               self.db.saveNote(noteid, note, cat_id, pcdatum = None)
+                               self.topBox.comboCategoryChanged()
+                       else:
+                               mbox = gtk.MessageDialog(self.window, gtk.DIALOG_MODAL, gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, _("No note selected."))
+                               response = mbox.run()
+                               mbox.hide()
+                               mbox.destroy()
+
+               dialog.destroy()
+
+       def sync_finished(self, data = None, data2 = None):
+               self.topBox.loadCategories()
+               self.notizen.loadNotes()
+
+       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, 50504)
+               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):
+               self.sync_dialog.run()
+               self.sync_dialog.hide()
+
+       def toggle_word_wrap(self, *args):
+               self._wordWrapEnabled = not self._wordWrapEnabled
+               self.notizen.set_wordwrap(self._wordWrapEnabled)
+
+       def delete_event(self, widget, event, data = None):
+               return False
+
+       def destroy(self, widget = None, data = None):
+               self.db.close()
+               if self._osso_c:
+                       self._osso_c.close()
+               gtk.main_quit()
+
+       def _on_device_state_change(self, shutdown, save_unsaved_data, memory_low, system_inactivity, message, userData):
+               """
+               For system_inactivity, we have no background tasks to pause
+
+               @note Hildon specific
+               """
+               if memory_low:
+                       gc.collect()
+
+               if save_unsaved_data or shutdown:
+                       pass
+
+       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 on_key_press(self, widget, event, *args):
+               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 ()
+               elif event.keyval == gtk.keysyms.F7:
+                       # Zoom In
+                       self.topBox.hide()
+               elif event.keyval == gtk.keysyms.F8:
+                       # Zoom Out
+                       self.topBox.show()
+
+       def main(self):
+               gtk.main()
diff --git a/src/libspeichern.py b/src/libspeichern.py
new file mode 100644 (file)
index 0000000..6e0d9e4
--- /dev/null
@@ -0,0 +1,205 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+ Copyright (C) 2007 Christoph Würstle
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+"""
+
+
+import sys
+import os
+import time
+import sqlite3
+import shelve
+import logging
+
+
+try:
+       _
+except NameError:
+       _ = lambda x: x
+
+
+class Speichern():
+
+       def __init__(self):
+               home_dir = os.path.expanduser('~')
+               filename = os.path.join(home_dir, ".quicknote.dat")
+               self.d = shelve.open(filename)
+               self.openDB()
+
+       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 = ""):
+               if (self.d.has_key(schluessel) == True):
+                       data = self.d[schluessel]
+                       return data
+               else:
+                       return default
+
+       def speichereSQL(self, sql, tupel = None, commit = True, host = "self", log = True, pcdatum = None, rowid = ""):
+               try:
+                       programSQLError = True
+                       if tupel is None:
+                               self.cur.execute(sql)
+                       else:
+                               self.cur.execute(sql, tupel)
+                       programSQLError = False
+
+                       if (log == True):
+                               strtupel = []
+                               if tupel is not None:
+                                       for t in tupel:
+                                               strtupel.append(str(t))
+
+                               if pcdatum is None:
+                                       pcdatum = int(time.time())
+                               self.cur.execute("INSERT INTO logtable ( pcdatum, sql, param, host, rowid ) VALUES (?, ?, ?, ?, ?)", (pcdatum, sql, " <<Tren-ner>> ".join(strtupel), host, str(rowid) ))
+                       if commit:
+                               self.conn.commit()
+
+                       return True
+               except StandardError:
+                       s = str(sys.exc_info())
+                       if s.find(" already exists") == -1:
+                               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 is None:
+                               self.cur.execute(sql)
+                       else:
+                               self.cur.execute(sql, tupel)
+                       return self.cur.fetchall()
+               except StandardError:
+                       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 
+               erg = []
+               for row in rows:
+                       datum = time.strftime("%d.%m.%y %H:%M:%S", (time.localtime(row[1])))
+                       erg.append([row[1], datum, row[2], row[3], row[3].split(" <<Tren-ner>> ")])
+
+               return erg
+
+       def openDB(self):
+               try:
+                       self.cur.close()
+               except StandardError:
+                       pass
+               try:
+                       self.conn.close()
+               except StandardError:
+                       pass
+
+               db = self.ladeDirekt("datenbank")
+               if db == "":
+                       home_dir = os.path.expanduser('~')
+
+                       #on hildon user not home-dir but /home/user/MyDocs
+                       if home_dir == "/home/user":
+                               if os.path.exists(home_dir+os.sep+"MyDocs/"):
+                                       home_dir = home_dir+os.sep+"MyDocs/"
+                       db = os.path.join(home_dir, "quicknote.s3db")
+
+               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 StandardError:
+                       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 StandardError:
+                       pass
+
+               #Create notes table
+               try:
+                       sql = "CREATE TABLE notes (noteid TEXT, pcdatum INTEGER , category TEXT, note TEXT)"
+                       self.cur.execute(sql)
+                       self.conn.commit()
+               except StandardError:
+                       pass
+
+       def saveNote(self, noteid, note, category, pcdatum = None):
+               if category == "%":
+                       category = ""
+               sql = "SELECT noteid, pcdatum, category, note FROM notes WHERE noteid = ?"
+               rows = self.ladeSQL(sql, (noteid, ))
+
+               if rows is None or len(rows) == 0:
+                       sql = "INSERT INTO notes (noteid, pcdatum, category, note) VALUES (?, ?, ?, ?)"
+                       if pcdatum is None:
+                               pcdatum = int(time.time())
+                       self.speichereSQL(sql, (noteid, pcdatum, category, note), rowid = noteid)
+               else:
+                       sql = "UPDATE notes SET category = ?, note = ?, pcdatum = ? WHERE noteid = ?"
+                       self.speichereSQL(sql, (category, note, str(int(time.time())), noteid), rowid = noteid)
+
+       def loadNote(self, noteid):
+               if noteid is None or str(noteid) == "":
+                       return (None, None, None)
+               sql = "SELECT noteid, pcdatum, category, note FROM notes WHERE noteid = ?"
+               rows = self.ladeSQL(sql, (noteid, ))
+               if rows is None or len(rows) == 0:
+                       return None
+               else:
+                       noteid, pcdatum, category, note = rows[0]
+                       return (noteid, pcdatum, category, note)
+
+       def delNote(self, noteid):
+               sql = "DELETE FROM notes WHERE noteid = ?"
+               self.speichereSQL(sql, (noteid, ), rowid = noteid)
+
+       def searchNotes(self, searchstring, category):
+               sql = "SELECT noteid, category, note FROM notes WHERE note like ? AND category like ? ORDER BY note"
+               rows = self.ladeSQL(sql, ("%"+searchstring+"%", category))
+               if rows is None or len(rows) == 0:
+                       return None
+               else:
+                       return rows
+
+       def getNoteHistory(self, noteid):
+               return self.ladeHistory("UPDATE notes ", noteid)
+
+       def close(self):
+               try:
+                       self.d.close()
+               except StandardError:
+                       pass
+               try:
+                       self.cur.close()
+               except StandardError:
+                       pass
+               try:
+                       self.conn.close()
+               except StandardError:
+                       pass
+               logging.info("Alle Data saved")
+
+       def __del__(self):
+               self.close()
diff --git a/src/libsqldialog.py b/src/libsqldialog.py
new file mode 100755 (executable)
index 0000000..e674692
--- /dev/null
@@ -0,0 +1,98 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+ Copyright (C) 2007 Christoph Würstle
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+"""
+
+
+import time
+import logging
+
+import gtk
+
+
+try:
+       _
+except NameError:
+       _ = lambda x: x
+
+
+class sqlDialog(gtk.Dialog):
+
+       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()
+
+               # create the TreeViewColumns to display the data
+               self.tvcolumn1 = gtk.TreeViewColumn(_('Timestamp'))
+               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)
+
+               # 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.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, ))
+               for row in rows:
+                       pcdatum, sql, param = row
+                       datum = str(time.strftime(_("%d.%m.%y %H:%M:%S "), (time.localtime(pcdatum))))
+                       self.liststore.append([datum, sql, param])
+
+               self.set_size_request(500, 400)
+
+       def exportSQL(self, filename):
+               f = open(filename, 'w')
+               try:
+                       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")
+               finally:
+                       f.close()
diff --git a/src/libsync.py b/src/libsync.py
new file mode 100755 (executable)
index 0000000..48bcc86
--- /dev/null
@@ -0,0 +1,365 @@
+#!/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 sys
+import time
+import SimpleXMLRPCServer
+import xmlrpclib
+import select
+#import fcntl
+import uuid
+import logging
+import socket
+socket.setdefaulttimeout(60) # Timeout auf 60 sec. setzen
+
+import gtk
+import gobject
+
+
+try:
+       _
+except NameError:
+       _ = lambda x: x
+
+
+class ProgressDialog(gtk.Dialog):
+
+       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.vbox.show_all()
+               self.show()
+
+       def pulse(self):
+               pass
+
+
+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 __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
+
+               sql = "CREATE TABLE sync (id INTEGER PRIMARY KEY, syncpartner TEXT, uuid TEXT, pcdatum INTEGER)"
+               self.db.speichereSQL(sql, log = False)
+
+               sql = "SELECT uuid, pcdatum FROM sync WHERE syncpartner = ?"
+               rows = self.db.ladeSQL(sql, ("self", )) #Eigene Id feststellen
+
+               if (rows is None)or(len(rows)!= 1):
+                       sql = "DELETE FROM sync WHERE syncpartner = ?"
+                       self.db.speichereSQL(sql, ("self", ), log = False)
+
+                       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)
+               else:
+                       sync_uuid, pcdatum = rows[0]
+                       self.sync_uuid = sync_uuid
+
+               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"))
+
+               frame.add(self.comboIP)
+               serverbutton = gtk.ToggleButton(_("Start/Stop 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 stopped"))
+               self.pack_start(self.syncServerStatusLabel, expand = False, fill = True, padding = 1)
+
+               frame = gtk.Frame(_("Remote SyncServer (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.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):
+                       serverbutton.set_active(True)
+
+       def changeSyncStatus(self, active, title):
+               self.syncStatusLabel.set_text(title)
+               if active == True:
+                       if self.progress is None:
+                               self.progress = ProgressDialog(parent = self.parentwindow)
+                               self.emit("syncBeforeStart", "syncBeforeStart")
+               else:
+                       if self.progress is not None:
+                               self.progress.hide()
+                               self.progress.destroy()
+                               self.progress = None
+                               self.emit("syncFinished", "syncFinished")
+
+       def pulse(self):
+               if self.progress is not None:
+                       self.progress.pulse()
+
+       def getUeberblickBox(self):
+               frame = gtk.Frame(_("Query"))
+               return frame
+
+       def handleRPC(self):
+               try:
+                       if self.rpcserver is None:
+                               return False
+               except StandardError:
+                       return False
+
+               while 0 < len(self.poll.poll(0)):
+                       self.rpcserver.handle_request()
+               return True
+
+       def get_ip_address(self, ifname):
+               return socket.gethostbyname(socket.gethostname())
+
+       def getLastSyncDate(self, sync_uuid):
+               sql = "SELECT syncpartner, pcdatum FROM sync WHERE uuid = ?"
+               rows = self.db.ladeSQL(sql, (sync_uuid, ))
+               if rows is not 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 is 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 is not None and 0 < len(self.concernedRows):
+                       id1, pcdatum, sql, param, host, rowid = newSQL
+
+                       if 0 < len(rowid):
+                               for x in self.concernedRows:
+                                       if x[1] == rowid:
+                                               if pcdatum < x[0]:
+                                                       logging.info("newer sync entry, ignoring old one")
+                                                       return False
+                                               else:
+                                                       return True
+
+               return True
+
+       def writeSQLTupel(self, newSQLs, lastdate):
+               if newSQLs is None:
+                       return
+
+               self.concernedRows = None
+               pausenzaehler = 0
+               logging.info("writeSQLTupel got "+str(len(newSQLs))+" sql tupels")
+               for newSQL in newSQLs:
+                       if newSQL[3] != "":
+                               param = newSQL[3].split(" <<Tren-ner>> ")
+                       else:
+                               param = None
+
+                       if 2 < len(newSQL):
+                               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):
+               self.changeSyncStatus(True, "sync process running")
+               self.pulse()
+
+               while gtk.events_pending():
+                       gtk.main_iteration()
+               diff = abs(time.time() - pcdatumjetzt)
+               if 30 < diff:
+                       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")
+               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():
+                       logging.info("Starting Server")
+
+                       try:
+                               ip = self.comboIP.get_child().get_text()
+                               self.rpcserver = SimpleXMLRPCServer.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 StandardError:
+                               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, _("Could not start SyncServer. Check IP, port settings.")) #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 StandardError:
+                               pass
+                       self.syncServerStatusLabel.set_text(_("SyncServer stopped"))
+                       #save
+                       self.db.speichereDirekt("startSyncServer", False)
+
+       def doSaveFinalTime(self, sync_uuid, pcdatum = None):
+               if pcdatum is None:
+                       pcdatum = int(time.time())
+               if pcdatum < time.time():
+                       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")
+
+               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)
+                       server_sync_uuid = self.server.getRemoteSyncUUID()
+                       lastDate = self.getLastSyncDate(str(server_sync_uuid))
+
+                       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 StandardError:
+                       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
diff --git a/src/quicknote.py b/src/quicknote.py
new file mode 100755 (executable)
index 0000000..c471891
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+
+"""
+Copyright (C) 2007 Christoph Würstle
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License version 2 as
+published by the Free Software Foundation.
+"""
+
+import os
+import sys
+sys.path.append('/usr/lib/quicknote')
+
+import locale
+import gettext
+gettext.install('quicknote', unicode = 1)
+
+import libquicknote
+
+if __name__ == "__main__":
+       app = libquicknote.quicknoteclass()
+       app.main()
diff --git a/src/simple_list.py b/src/simple_list.py
new file mode 100644 (file)
index 0000000..027185a
--- /dev/null
@@ -0,0 +1,147 @@
+#!/usr/bin/env python2.5\r
+# -*- coding: utf-8 -*-\r
+\r
+"""\r
+ Copyright (C) 2007 Christoph Würstle and in big parts by Gerold Penz\r
+\r
+This program is free software; you can redistribute it and/or modify\r
+it under the terms of the GNU General Public License version 2 as\r
+published by the Free Software Foundation.\r
+"""\r
+\r
+\r
+import pygtk\r
+pygtk.require("2.0")\r
+import gtk\r
+\r
+\r
+try:\r
+       _\r
+except NameError:\r
+       _ = lambda x: x\r
+\r
+\r
+class SimpleList(gtk.ScrolledWindow):\r
+       """\r
+       Stellt eine einfache Liste mit Laufbalken dar. Das wird mit\r
+       den Objekten ScrolledWindow und TreeView erreicht.\r
+       """\r
+\r
+       def __init__(self):\r
+               """\r
+               Initialisieren\r
+               """\r
+\r
+               gtk.ScrolledWindow.__init__(self)\r
+               self.selected_item = None # (<Position>, <Key>, <Value>)\r
+\r
+               # Liste\r
+               self.list_store = gtk.ListStore(str, str)\r
+\r
+               # ScrolledWindow\r
+               self.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)\r
+               self.set_shadow_type(gtk.SHADOW_IN)\r
+\r
+               # Treeview\r
+               self.tree_view = gtk.TreeView(self.list_store)\r
+               self.tree_view.set_headers_visible(False)\r
+               self.tree_view.get_selection().set_mode(gtk.SELECTION_BROWSE)\r
+               self.tree_view.connect("cursor-changed", self._on_cursor_changed)\r
+               self.tree_view.connect("row-activated", self._on_row_activated)\r
+               self.tree_view.show()\r
+\r
+               # Key-Spalte hinzuf�gen\r
+               self.key_cell = gtk.CellRendererText()\r
+               self.key_column = gtk.TreeViewColumn("Key")\r
+               self.key_column.pack_start(self.key_cell, True)\r
+               self.key_column.add_attribute(self.key_cell, "text", 0)\r
+               self.key_column.set_visible(False)\r
+               self.tree_view.append_column(self.key_column)\r
+\r
+               # Value-Spalte hinzufügen\r
+               self.value_cell = gtk.CellRendererText()\r
+               self.value_column = gtk.TreeViewColumn("Caption")\r
+               self.value_column.pack_start(self.value_cell, True)\r
+               self.value_column.add_attribute(self.value_cell, "text", 1)\r
+               self.tree_view.append_column(self.value_column)\r
+\r
+               # Suchspalte setzen\r
+               # Leider funktioniert die Suche im Moment nicht so \r
+               # wie ich das möchte. Deshalb habe ich die Suche abgeschaltet.\r
+               self.tree_view.set_enable_search(False)\r
+\r
+               # Anzeigen\r
+               self.add(self.tree_view)\r
+               self.show()\r
+\r
+       def append_item(self, value, key = ""):\r
+               """\r
+               F�gt der Liste Werte und wenn gew�nscht, Schl�ssel hinzu.\r
+               """\r
+\r
+               self.list_store.append([key, value])\r
+\r
+       def select_last_item(self):\r
+               path = str(len(self.list_store)-1)\r
+               self.tree_view.set_cursor(path, self.value_column)\r
+               return len(self.list_store)-1\r
+\r
+       def change_item(self, pos, value, key = ""):\r
+               self.list_store[pos] = [key, value]\r
+\r
+       def remove_item(self, pos):\r
+               model = self.tree_view.get_model()\r
+               self.list_store.remove(model.get_iter(str(pos)))\r
+\r
+       def get_item(self, pos):\r
+               return self.list_store[pos]\r
+\r
+       def clear_items(self):\r
+               self.list_store.clear()\r
+\r
+       def _on_row_activated(self, treeview, path, view_column, data = None):\r
+               """\r
+               Setzt den Wert von self.selected_items. Dieser Wert kann\r
+               mit der Methode "get_selection_data" abgerufen werden.\r
+               """\r
+\r
+               iter = self.list_store.get_iter(path)\r
+\r
+               if iter:\r
+                       self.selected_item = (\r
+                               path[0], # Position\r
+                               self.list_store.get_value(iter, 0), # Key\r
+                               self.list_store.get_value(iter, 1) # Value\r
+                       )\r
+\r
+       def _on_cursor_changed(self, widget, data1 = None, data2 = None):\r
+               """\r
+               Setzt den Wert von self.selected_items. Dieser Wert kann\r
+               mit der Methode "get_selection_data" abgerufen werden.\r
+               """\r
+\r
+               selection = widget.get_selection()\r
+               (model, iter) = selection.get_selected()\r
+\r
+               if iter:\r
+                       self.selected_item = (\r
+                               int(selection.get_selected_rows()[1][0][0]), # Position\r
+                               str(model.get_value(iter, 0)), # Key\r
+                               str(model.get_value(iter, 1)) # Value\r
+                       )\r
+\r
+       def get_selection_data(self):\r
+               """\r
+               Gibt die Variable self.selected_item zur�ck.\r
+               Diese enth�lt ein Tupel. (<Position>, <Key>, <Value>)\r
+               """\r
+\r
+               return self.selected_item  # (<Position>, <Key>, <Value>)\r
+\r
+       def set_eventfunction__cursor_changed(self, function):\r
+               """\r
+               Verbindet die �bergebene Funktion mit dem \r
+               Signal "cursor-changed".\r
+               """\r
+\r
+               self.tree_view.connect("cursor-changed", function)\r
diff --git a/support/builddeb.py b/support/builddeb.py
new file mode 100755 (executable)
index 0000000..3b5b76c
--- /dev/null
@@ -0,0 +1,118 @@
+#!/usr/bin/python2.5
+
+import os
+
+try:
+       import py2deb
+except ImportError:
+       import fake_py2deb as py2deb
+
+
+__appname__ = "quicknote"
+__description__ = "Simple note taking application in a similar vein as PalmOS Memos"
+__author__ = "Christoph Wurstle"
+__email__ = "n800@axique.net"
+__version__ = "0.7.6"
+__build__ = 1
+__changelog__ = '''\
+0.7.6
+  * Line-wrap
+  * Zoom
+
+0.7.4
+  * fixed small bugs
+  * move category
+
+0.7.3
+  * fixed small bugs
+  * move category
+
+0.7.2
+  * improved sync, fixed a small bug
+
+0.7.1
+  * improved sync
+
+0.7.0
+  * Initial Release.
+'''
+
+
+__postinstall__ = '''#!/bin/sh -e
+
+gtk-update-icon-cache -f /usr/share/icons/hicolor
+exit 0
+'''
+
+
+def find_files(path, root):
+       print path, root
+       for unusedRoot, dirs, files in os.walk(path):
+               for file in files:
+                       if file.startswith(root+"-"):
+                               print "\t", root, file
+                               fileParts = file.split("-")
+                               unused, relPathParts, newName = fileParts[0], fileParts[1:-1], fileParts[-1]
+                               assert unused == root
+                               relPath = os.sep.join(relPathParts)
+                               yield relPath, file, newName
+
+
+def unflatten_files(files):
+       d = {}
+       for relPath, oldName, newName in files:
+               if relPath not in d:
+                       d[relPath] = []
+               d[relPath].append((oldName, newName))
+       return d
+
+
+if __name__ == "__main__":
+       try:
+               os.chdir(os.path.dirname(sys.argv[0]))
+       except:
+               pass
+
+       p = py2deb.Py2deb(__appname__)
+       p.description = __description__
+       p.author = __author__
+       p.mail = __email__
+       p.license = "lgpl"
+       p.depends = "python2.5, python2.5-gtk2"
+       p.section = "user/other"
+       p.arch = "all"
+       p.urgency = "low"
+       p.distribution = "chinook diablo"
+       p.repository = "extras-devel"
+       p.changelog = __changelog__
+       p.postinstall = __postinstall__
+       p.icon = "26x26-quicknote.png"
+       p["/usr/bin"] = [ "quicknote.py" ]
+       for relPath, files in unflatten_files(find_files(".", "locale")).iteritems():
+               fullPath = "/usr/share/locale"
+               if relPath:
+                       fullPath += os.sep+relPath
+               p[fullPath] = list(
+                       "|".join((oldName, newName))
+                       for (oldName, newName) in files
+               )
+       for relPath, files in unflatten_files(find_files(".", "src")).iteritems():
+               fullPath = "/usr/lib/quicknote"
+               if relPath:
+                       fullPath += os.sep+relPath
+               p[fullPath] = list(
+                       "|".join((oldName, newName))
+                       for (oldName, newName) in files
+               )
+       p["/usr/share/applications/hildon"] = ["quicknote.desktop"]
+       p["/usr/share/dbus-1/services"] = ["quicknote.service"]
+       p["/usr/share/icons/hicolor/26x26/hildon"] = ["26x26-quicknote.png|quicknote.png"]
+       p["/usr/share/icons/hicolor/40x40/hildon"] = ["40x40-quicknote.png|quicknote.png"]
+       p["/usr/share/icons/hicolor/48x48/hildon"] = ["48x48-quicknote.png|quicknote.png"]
+       p["/usr/share/icons/hicolor/scalable/hildon"] = ["scale-quicknote.png|quicknote.png"]
+
+       print p
+       print p.generate(
+               __version__, __build__, changelog=__changelog__,
+               tar=True, dsc=True, changes=True, build=False, src=True
+       )
diff --git a/support/fake_py2deb.py b/support/fake_py2deb.py
new file mode 100644 (file)
index 0000000..5d6149d
--- /dev/null
@@ -0,0 +1,56 @@
+import pprint
+
+
+class Py2deb(object):
+
+       def __init__(self, appName):
+               self._appName = appName
+               self.description = ""
+               self.author = ""
+               self.mail = ""
+               self.license = ""
+               self.depends = ""
+               self.section = ""
+               self.arch = ""
+               self.ugency = ""
+               self.distribution = ""
+               self.repository = ""
+               self.changelog = ""
+               self.postinstall = ""
+               self.icon = ""
+               self._install = {}
+
+       def generate(self, appVersion, appBuild, changelog, tar, dsc, changes, build, src):
+               return """
+Package: %s
+version: %s-%s
+Changes:
+%s
+
+Build Options:
+       Tar: %s
+       Dsc: %s
+       Changes: %s
+       Build: %s
+       Src: %s
+               """ % (
+                       self._appName, appVersion, appBuild, changelog, tar, dsc, changes, build, src
+               )
+
+       def __str__(self):
+               parts = []
+               parts.append("%s Package Settings:" % (self._appName, ))
+               for settingName in dir(self):
+                       if settingName.startswith("_"):
+                               continue
+                       parts.append("\t%s: %s" % (settingName, getattr(self, settingName)))
+
+               parts.append(pprint.pformat(self._install))
+
+               return "\n".join(parts)
+
+       def __getitem__(self, key):
+               return self._install[key]
+
+       def __setitem__(self, key, item):
+               self._install[key] = item
diff --git a/support/pylint.rc b/support/pylint.rc
new file mode 100644 (file)
index 0000000..37b9725
--- /dev/null
@@ -0,0 +1,305 @@
+# lint Python modules using external checkers.
+#
+# This is the main checker controling the other ones and the reports
+# generation. It is itself both a raw checker and an astng checker in order
+# to:
+# * handle message activation / deactivation at the module level
+# * handle some basic but necessary stats'data (number of classes, methods...)
+#
+[MASTER]
+
+# Specify a configuration file.
+#rcfile=
+
+# Python code to execute, usually for sys.path manipulation such as
+# pygtk.require().
+#init-hook=
+
+# Profiled execution.
+profile=no
+
+# Add <file or directory> to the black list. It should be a base name, not a
+# path. You may set this option multiple times.
+ignore=CVS
+
+# Pickle collected data for later comparisons.
+persistent=yes
+
+# Set the cache size for astng objects.
+cache-size=500
+
+# List of plugins (as comma separated values of python modules names) to load,
+# usually to register additional checkers.
+load-plugins=
+
+
+[MESSAGES CONTROL]
+
+# Enable only checker(s) with the given id(s). This option conflicts with the
+# disable-checker option
+#enable-checker=
+
+# Enable all checker(s) except those with the given id(s). This option
+# conflicts with the enable-checker option
+#disable-checker=
+
+# Enable all messages in the listed categories.
+#enable-msg-cat=
+
+# Disable all messages in the listed categories.
+#disable-msg-cat=
+
+# Enable the message(s) with the given id(s).
+#enable-msg=
+
+# Disable the message(s) with the given id(s).
+disable-msg=W0403,W0612,W0613,C0103,C0111,C0301,R0903,W0142,W0603,R0904
+
+[REPORTS]
+
+# set the output format. Available formats are text, parseable, colorized, msvs
+# (visual studio) and html
+output-format=colorized
+
+# Include message's id in output
+include-ids=yes
+
+# Put messages in a separate file for each module / package specified on the
+# command line instead of printing them on stdout. Reports (if any) will be
+# written in a file name "pylint_global.[txt|html]".
+files-output=no
+
+# Tells wether to display a full report or only the messages
+reports=no
+
+# Python expression which should return a note less than 10 (10 is the highest
+# note).You have access to the variables errors warning, statement which
+# respectivly contain the number of errors / warnings messages and the total
+# number of statements analyzed. This is used by the global evaluation report
+# (R0004).
+evaluation=10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10)
+
+# Add a comment according to your evaluation note. This is used by the global
+# evaluation report (R0004).
+comment=no
+
+# Enable the report(s) with the given id(s).
+#enable-report=
+
+# Disable the report(s) with the given id(s).
+#disable-report=
+
+
+# checks for
+# * unused variables / imports
+# * undefined variables
+# * redefinition of variable from builtins or from an outer scope
+# * use of variable before assigment
+#
+[VARIABLES]
+
+# Tells wether we should check for unused import in __init__ files.
+init-import=no
+
+# A regular expression matching names used for dummy variables (i.e. not used).
+dummy-variables-rgx=_|dummy
+
+# List of additional names supposed to be defined in builtins. Remember that
+# you should avoid to define new builtins when possible.
+additional-builtins=
+
+
+# checks for :
+# * doc strings
+# * modules / classes / functions / methods / arguments / variables name
+# * number of arguments, local variables, branchs, returns and statements in
+# functions, methods
+# * required module attributes
+# * dangerous default values as arguments
+# * redefinition of function / method / class
+# * uses of the global statement
+#
+[BASIC]
+
+# Required attributes for module, separated by a comma
+required-attributes=
+
+# Regular expression which should only match functions or classes name which do
+# not require a docstring
+no-docstring-rgx=__.*__
+
+# Regular expression which should only match correct module names
+module-rgx=(([a-z_][a-z0-9_]*)|([A-Z][a-zA-Z0-9]+))$
+
+# Regular expression which should only match correct module level names
+const-rgx=(([A-Z_][A-Z1-9_]*)|(__.*__))$
+
+# Regular expression which should only match correct class names
+class-rgx=[A-Z_][a-zA-Z0-9]+$
+
+# Regular expression which should only match correct function names
+function-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct method names
+method-rgx=[a-z_][a-z0-9_]{2,30}$
+
+# Regular expression which should only match correct instance attribute names
+attr-rgx=[a-z_][a-zA-Z0-9_]{2,30}$
+
+# Regular expression which should only match correct argument names
+argument-rgx=[a-z_][a-zA-Z0-9_]{2,30}$
+
+# Regular expression which should only match correct variable names
+variable-rgx=[a-z_][a-zA-Z0-9_]{2,30}$
+
+# Regular expression which should only match correct list comprehension /
+# generator expression variable names
+inlinevar-rgx=[A-Za-z_][A-Za-z0-9_]*$
+
+# Good variable names which should always be accepted, separated by a comma
+good-names=i,j,k,ex,Run,_
+
+# Bad variable names which should always be refused, separated by a comma
+bad-names=foo,bar,baz,toto,tutu,tata
+
+# List of builtins function names that should not be used, separated by a comma
+bad-functions=map,filter,apply,input
+
+
+# try to find bugs in the code using type inference
+# 
+[TYPECHECK]
+
+# Tells wether missing members accessed in mixin class should be ignored. A
+# mixin class is detected if its name ends with "mixin" (case insensitive).
+ignore-mixin-members=yes
+
+# When zope mode is activated, consider the acquired-members option to ignore
+# access to some undefined attributes.
+zope=no
+
+# List of members which are usually get through zope's acquisition mecanism and
+# so shouldn't trigger E0201 when accessed (need zope=yes to be considered).
+acquired-members=REQUEST,acl_users,aq_parent
+
+
+# checks for sign of poor/misdesign:
+# * number of methods, attributes, local variables...
+# * size, complexity of functions, methods
+#
+[DESIGN]
+
+# Maximum number of arguments for function / method
+max-args=5
+
+# Maximum number of locals for function / method body
+max-locals=15
+
+# Maximum number of return / yield for function / method body
+max-returns=6
+
+# Maximum number of branch for function / method body
+max-branchs=12
+
+# Maximum number of statements in function / method body
+max-statements=50
+
+# Maximum number of parents for a class (see R0901).
+max-parents=7
+
+# Maximum number of attributes for a class (see R0902).
+max-attributes=15
+
+# Minimum number of public methods for a class (see R0903).
+min-public-methods=1
+
+# Maximum number of public methods for a class (see R0904).
+max-public-methods=20
+
+
+# checks for :
+# * methods without self as first argument
+# * overridden methods signature
+# * access only to existant members via self
+# * attributes not defined in the __init__ method
+# * supported interfaces implementation
+# * unreachable code
+#
+[CLASSES]
+
+# List of interface methods to ignore, separated by a comma. This is used for
+# instance to not check methods defines in Zope's Interface base class.
+ignore-iface-methods=isImplementedBy,deferred,extends,names,namesAndDescriptions,queryDescriptionFor,getBases,getDescriptionFor,getDoc,getName,getTaggedValue,getTaggedValueTags,isEqualOrExtendedBy,setTaggedValue,isImplementedByInstancesOf,adaptWith,is_implemented_by
+
+# List of method names used to declare (i.e. assign) instance attributes.
+defining-attr-methods=__init__,__new__,setUp
+
+
+# checks for
+# * external modules dependencies
+# * relative / wildcard imports
+# * cyclic imports
+# * uses of deprecated modules
+#
+[IMPORTS]
+
+# Deprecated modules which should not be used, separated by a comma
+deprecated-modules=regsub,string,TERMIOS,Bastion,rexec
+
+# Create a graph of every (i.e. internal and external) dependencies in the
+# given file (report R0402 must not be disabled)
+import-graph=
+
+# Create a graph of external dependencies in the given file (report R0402 must
+# not be disabled)
+ext-import-graph=
+
+# Create a graph of internal dependencies in the given file (report R0402 must
+# not be disabled)
+int-import-graph=
+
+
+# checks for similarities and duplicated code. This computation may be
+# memory / CPU intensive, so you should disable it if you experiments some
+# problems.
+#
+[SIMILARITIES]
+
+# Minimum lines number of a similarity.
+min-similarity-lines=4
+
+# Ignore comments when computing similarities.
+ignore-comments=yes
+
+# Ignore docstrings when computing similarities.
+ignore-docstrings=yes
+
+
+# checks for:
+# * warning notes in the code like FIXME, XXX
+# * PEP 263: source code with non ascii character but no encoding declaration
+#
+[MISCELLANEOUS]
+
+# List of note tags to take in consideration, separated by a comma.
+notes=FIXME,XXX,TODO
+
+
+# checks for :
+# * unauthorized constructions
+# * strict indentation
+# * line length
+# * use of <> instead of !=
+#
+[FORMAT]
+
+# Maximum number of characters on a single line.
+# @note Limiting this to the most extreme cases
+max-line-length=100
+
+# Maximum number of lines in a module
+max-module-lines=1000
+
+# String used as indentation unit. This is usually " " (4 spaces) or "\t" (1
+# tab).
+indent-string='\t'
diff --git a/support/test_syntax.py b/support/test_syntax.py
new file mode 100755 (executable)
index 0000000..65a373c
--- /dev/null
@@ -0,0 +1,45 @@
+#!/usr/bin/env python
+
+import commands
+
+
+verbose = False
+
+
+def syntax_test(file):
+       commandTemplate = """
+       python -t -t -W all -c "import py_compile; py_compile.compile ('%(filename)s', doraise=False)" """
+       compileCommand = commandTemplate % {"filename": file}
+       (status, text) = commands.getstatusoutput (compileCommand)
+       text = text.rstrip()
+       passed = len(text) == 0
+
+       if passed:
+               output = ("Syntax is correct for "+file) if verbose else ""
+       else:
+               output = ("Syntax is invalid for %s\n" % file) if verbose else ""
+               output += text
+       return (passed, output)
+
+
+if __name__ == "__main__":
+       import sys
+       import os
+       import optparse
+
+       opar = optparse.OptionParser()
+       opar.add_option("-v", "--verbose", dest="verbose", help="Toggle verbosity", action="store_true", default=False)
+       options, args = opar.parse_args(sys.argv[1:])
+       verbose = options.verbose
+
+       completeOutput = []
+       allPassed = True
+       for filename in args:
+               passed, output = syntax_test(filename)
+               if not passed:
+                       allPassed = False
+               if output.strip():
+                       completeOutput.append(output)
+       print "\n".join(completeOutput)
+
+       sys.exit(0 if allPassed else 1);