# HG changeset patch # User Erik Grinaker # Date 1093905939 0 # Node ID a207757f8451c93d40a7620b523ac7a6c169f6d2 # Parent ec17fa90357a05ae5d96698b2df457224cf1f6b8 use gnu autotools for installation diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 ChangeLog --- a/ChangeLog Mon Aug 30 19:42:22 2004 +0000 +++ b/ChangeLog Mon Aug 30 22:45:39 2004 +0000 @@ -8,6 +8,9 @@ * reorganized source file tree + * use GNU autotools instead of python distutils for + installation + ---------------[ 2004-08-30 : 0.3.3 ]--------------- diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Makefile.am Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,2 @@ +SUBDIRS = src data + diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 NEWS --- a/NEWS Mon Aug 30 19:42:22 2004 +0000 +++ b/NEWS Mon Aug 30 22:45:39 2004 +0000 @@ -6,6 +6,7 @@ Code changes: - reorganized source file tree +- use GNU autotools for installation instead of python distutils 2004-08-30: Revelation 0.3.3 diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 TODO --- a/TODO Mon Aug 30 19:42:22 2004 +0000 +++ b/TODO Mon Aug 30 22:45:39 2004 +0000 @@ -1,5 +1,4 @@ 0.4.x: -- will introduce a gnome 2.6 dependency - use autotools for installation - gnome integration (gnome-vfs, session management, recent docs etc) - add support for "copy username/password to clipboard", useful when @@ -34,6 +33,7 @@ - improve the file format - remove namespacing for field identifiers - add a unique id for each entry (md5sum or something) +- file merging (union - this *really* need unique entry ids) - gnome panel applets (account lookup, password generator etc) - accessibility improvements - help / documentation diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 autogen.sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/autogen.sh Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,40 @@ +#!/bin/sh +# +# autogen.sh +# $Id$ +# +# Generates initial makefiles etc +# + +: ${AUTOCONF=autoconf} +: ${AUTOMAKE=automake} +: ${ACLOCAL=aclocal} + +srcdir=`dirname $0` +test -z "$srcdir" && srcdir="." + +# avoid using caches +rm -rf autom4te.cache +rm -f aclocal.m4 + +# generates makefiles etc +echo "Running $ACLOCAL..." +WANT_AUTOMAKE="1.7" $ACLOCAL || exit 1 +test -f aclocal.m4 || \ + { echo "aclocal failed to generate aclocal.m4" 2>&1; exit 1; } + +echo "Running $AUTOCONF..." +WANT_AUTOMAKE="1.7" $AUTOCONF || exit 1 +test -f configure || \ + { echo "autoconf failed to generate configure" 2>&1; exit 1; } + +echo "Running $AUTOMAKE..." +WANT_AUTOMAKE="1.7" $AUTOMAKE || exit 1 +test -f Makefile.in || \ + { echo "automake failed to generate Makefile.in" 2>&1; exit 1; } + +# run configure +echo "Running $srcdir/configure $conf_flags " "$@" ... +$srcdir/configure --cache-file=config.cache $conf_flags "$@" && \ + echo "Now type 'make'" + diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 configure.ac --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/configure.ac Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,28 @@ +# initialize autoconf/automake +AC_PREREQ(2.53) +AC_INIT(src/revelation.in) +AM_INIT_AUTOMAKE(revelation, 0.4.0) + + +dnl check for dependencies +AM_PATH_PYTHON(2.3) +PKG_CHECK_MODULES(PYGTK, pygtk-2.0 >= 2.3.90) +PKG_CHECK_MODULES(GNOME_PYTHON, gnome-python-2.0 >= 2.5.90) + +AC_PATH_PROG(GCONFTOOL, gconftool-2, no) +if test x"$GCONFTOOL" = xno; then + AC_MSG_ERROR([gconftool-2 executable not found in your path - should be installed with GConf]) +fi +AM_GCONF_SOURCE_2 + + +dnl output files +AC_OUTPUT([ + Makefile + data/Makefile + data/images/Makefile + src/Makefile + src/lib/Makefile + src/lib/datahandler/Makefile +]) + diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 data/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/Makefile.am Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,12 @@ +SUBDIRS = images + +desktopdir = $(datadir)/applications +desktop_DATA = revelation.desktop + +schemadir = @GCONF_SCHEMA_FILE_DIR@ +schema_DATA = revelation.schemas + +install-schemas: + GCONF_CONFIG_SOURCE=$(GCONF_SCHEMA_CONFIG_SOURCE) \ + $(GCONFTOOL) --makefile-install-rule $(schema_DATA) + diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 data/images/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/data/images/Makefile.am Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,18 @@ +imagesdir = $(pkgdatadir)/images +images_DATA = \ + account-creditcard.png \ + account-cryptokey.png \ + account-database.png \ + account-door.png \ + account-email.png \ + account-ftp.png \ + account-generic.png \ + account-phone.png \ + account-shell.png \ + account-website.png \ + folder-open.png \ + folder.png \ + password.png \ + revelation-16x16.png \ + revelation.png + diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 install-sh --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/install-sh Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,294 @@ +#!/bin/sh +# +# install - install a program, script, or datafile +# +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# `make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. It can only install one file at a time, a restriction +# shared with many OS's install programs. + + +# set DOITPROG to echo to test this script + +# Don't use :- since 4.3BSD and earlier shells don't like it. +doit="${DOITPROG-}" + + +# put in absolute paths if you don't have them in your path; or use env. vars. + +mvprog="${MVPROG-mv}" +cpprog="${CPPROG-cp}" +chmodprog="${CHMODPROG-chmod}" +chownprog="${CHOWNPROG-chown}" +chgrpprog="${CHGRPPROG-chgrp}" +stripprog="${STRIPPROG-strip}" +rmprog="${RMPROG-rm}" +mkdirprog="${MKDIRPROG-mkdir}" + +transformbasename="" +transform_arg="" +instcmd="$mvprog" +chmodcmd="$chmodprog 0755" +chowncmd="" +chgrpcmd="" +stripcmd="" +rmcmd="$rmprog -f" +mvcmd="$mvprog" +src="" +dst="" +dir_arg="" + +while [ x"$1" != x ]; do + case $1 in + -c) instcmd=$cpprog + shift + continue;; + + -d) dir_arg=true + shift + continue;; + + -m) chmodcmd="$chmodprog $2" + shift + shift + continue;; + + -o) chowncmd="$chownprog $2" + shift + shift + continue;; + + -g) chgrpcmd="$chgrpprog $2" + shift + shift + continue;; + + -s) stripcmd=$stripprog + shift + continue;; + + -t=*) transformarg=`echo $1 | sed 's/-t=//'` + shift + continue;; + + -b=*) transformbasename=`echo $1 | sed 's/-b=//'` + shift + continue;; + + *) if [ x"$src" = x ] + then + src=$1 + else + # this colon is to work around a 386BSD /bin/sh bug + : + dst=$1 + fi + shift + continue;; + esac +done + +if [ x"$src" = x ] +then + echo "$0: no input file specified" >&2 + exit 1 +else + : +fi + +if [ x"$dir_arg" != x ]; then + dst=$src + src="" + + if [ -d "$dst" ]; then + instcmd=: + chmodcmd="" + else + instcmd=$mkdirprog + fi +else + +# Waiting for this to be detected by the "$instcmd $src $dsttmp" command +# might cause directories to be created, which would be especially bad +# if $src (and thus $dsttmp) contains '*'. + + if [ -f "$src" ] || [ -d "$src" ] + then + : + else + echo "$0: $src does not exist" >&2 + exit 1 + fi + + if [ x"$dst" = x ] + then + echo "$0: no destination specified" >&2 + exit 1 + else + : + fi + +# If destination is a directory, append the input filename; if your system +# does not like double slashes in filenames, you may need to add some logic + + if [ -d "$dst" ] + then + dst=$dst/`basename "$src"` + else + : + fi +fi + +## this sed command emulates the dirname command +dstdir=`echo "$dst" | sed -e 's,[^/]*$,,;s,/$,,;s,^$,.,'` + +# Make sure that the destination directory exists. +# this part is taken from Noah Friedman's mkinstalldirs script + +# Skip lots of stat calls in the usual case. +if [ ! -d "$dstdir" ]; then +defaultIFS=' + ' +IFS="${IFS-$defaultIFS}" + +oIFS=$IFS +# Some sh's can't handle IFS=/ for some reason. +IFS='%' +set - `echo "$dstdir" | sed -e 's@/@%@g' -e 's@^%@/@'` +IFS=$oIFS + +pathcomp='' + +while [ $# -ne 0 ] ; do + pathcomp=$pathcomp$1 + shift + + if [ ! -d "$pathcomp" ] ; + then + $mkdirprog "$pathcomp" + else + : + fi + + pathcomp=$pathcomp/ +done +fi + +if [ x"$dir_arg" != x ] +then + $doit $instcmd "$dst" && + + if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dst"; else : ; fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dst"; else : ; fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dst"; else : ; fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dst"; else : ; fi +else + +# If we're going to rename the final executable, determine the name now. + + if [ x"$transformarg" = x ] + then + dstfile=`basename "$dst"` + else + dstfile=`basename "$dst" $transformbasename | + sed $transformarg`$transformbasename + fi + +# don't allow the sed command to completely eliminate the filename + + if [ x"$dstfile" = x ] + then + dstfile=`basename "$dst"` + else + : + fi + +# Make a couple of temp file names in the proper directory. + + dsttmp=$dstdir/_inst.$$_ + rmtmp=$dstdir/_rm.$$_ + +# Trap to clean up temp files at exit. + + trap 'status=$?; rm -f "$dsttmp" "$rmtmp" && exit $status' 0 + trap '(exit $?); exit' 1 2 13 15 + +# Move or copy the file name to the temp name + + $doit $instcmd "$src" "$dsttmp" && + +# and set any options; do chmod last to preserve setuid bits + +# If any of these fail, we abort the whole thing. If we want to +# ignore errors from any of these, just make sure not to ignore +# errors from the above "$doit $instcmd $src $dsttmp" command. + + if [ x"$chowncmd" != x ]; then $doit $chowncmd "$dsttmp"; else :;fi && + if [ x"$chgrpcmd" != x ]; then $doit $chgrpcmd "$dsttmp"; else :;fi && + if [ x"$stripcmd" != x ]; then $doit $stripcmd "$dsttmp"; else :;fi && + if [ x"$chmodcmd" != x ]; then $doit $chmodcmd "$dsttmp"; else :;fi && + +# Now remove or move aside any old file at destination location. We try this +# two ways since rm can't unlink itself on some systems and the destination +# file might be busy for other reasons. In this case, the final cleanup +# might fail but the new file should still install successfully. + +{ + if [ -f "$dstdir/$dstfile" ] + then + $doit $rmcmd -f "$dstdir/$dstfile" 2>/dev/null || + $doit $mvcmd -f "$dstdir/$dstfile" "$rmtmp" 2>/dev/null || + { + echo "$0: cannot unlink or rename $dstdir/$dstfile" >&2 + (exit 1); exit + } + else + : + fi +} && + +# Now rename the file to the real destination. + + $doit $mvcmd "$dsttmp" "$dstdir/$dstfile" + +fi && + +# The final little trick to "correctly" pass the exit status to the exit trap. + +{ + (exit 0); exit +} diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 missing --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/missing Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,336 @@ +#! /bin/sh +# Common stub for a few missing GNU programs while installing. +# Copyright (C) 1996, 1997, 1999, 2000, 2002, 2003 Free Software Foundation, Inc. +# Originally by Fran,cois Pinard , 1996. + +# 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 2, 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +if test $# -eq 0; then + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 +fi + +run=: + +# In the cases where this matters, `missing' is being run in the +# srcdir already. +if test -f configure.ac; then + configure_ac=configure.ac +else + configure_ac=configure.in +fi + +case "$1" in +--run) + # Try to run requested program, and just exit if it succeeds. + run= + shift + "$@" && exit 0 + ;; +esac + +# If it does not exist, or fails to run (possibly an outdated version), +# try to emulate it. +case "$1" in + + -h|--h|--he|--hel|--help) + echo "\ +$0 [OPTION]... PROGRAM [ARGUMENT]... + +Handle \`PROGRAM [ARGUMENT]...' for when PROGRAM is missing, or return an +error status if there is no known handling for PROGRAM. + +Options: + -h, --help display this help and exit + -v, --version output version information and exit + --run try to run the given command, and emulate it if it fails + +Supported PROGRAM values: + aclocal touch file \`aclocal.m4' + autoconf touch file \`configure' + autoheader touch file \`config.h.in' + automake touch all \`Makefile.in' files + bison create \`y.tab.[ch]', if possible, from existing .[ch] + flex create \`lex.yy.c', if possible, from existing .c + help2man touch the output file + lex create \`lex.yy.c', if possible, from existing .c + makeinfo touch the output file + tar try tar, gnutar, gtar, then tar without non-portable flags + yacc create \`y.tab.[ch]', if possible, from existing .[ch]" + ;; + + -v|--v|--ve|--ver|--vers|--versi|--versio|--version) + echo "missing 0.4 - GNU automake" + ;; + + -*) + echo 1>&2 "$0: Unknown \`$1' option" + echo 1>&2 "Try \`$0 --help' for more information" + exit 1 + ;; + + aclocal*) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acinclude.m4' or \`${configure_ac}'. You might want + to install the \`Automake' and \`Perl' packages. Grab them from + any GNU archive site." + touch aclocal.m4 + ;; + + autoconf) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`${configure_ac}'. You might want to install the + \`Autoconf' and \`GNU m4' packages. Grab them from any GNU + archive site." + touch configure + ;; + + autoheader) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`acconfig.h' or \`${configure_ac}'. You might want + to install the \`Autoconf' and \`GNU m4' packages. Grab them + from any GNU archive site." + files=`sed -n 's/^[ ]*A[CM]_CONFIG_HEADER(\([^)]*\)).*/\1/p' ${configure_ac}` + test -z "$files" && files="config.h" + touch_files= + for f in $files; do + case "$f" in + *:*) touch_files="$touch_files "`echo "$f" | + sed -e 's/^[^:]*://' -e 's/:.*//'`;; + *) touch_files="$touch_files $f.in";; + esac + done + touch $touch_files + ;; + + automake*) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified \`Makefile.am', \`acinclude.m4' or \`${configure_ac}'. + You might want to install the \`Automake' and \`Perl' packages. + Grab them from any GNU archive site." + find . -type f -name Makefile.am -print | + sed 's/\.am$/.in/' | + while read f; do touch "$f"; done + ;; + + autom4te) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. + You can get \`$1' as part of \`Autoconf' from any GNU + archive site." + + file=`echo "$*" | sed -n 's/.*--output[ =]*\([^ ]*\).*/\1/p'` + test -z "$file" && file=`echo "$*" | sed -n 's/.*-o[ ]*\([^ ]*\).*/\1/p'` + if test -f "$file"; then + touch $file + else + test -z "$file" || exec >$file + echo "#! /bin/sh" + echo "# Created by GNU Automake missing as a replacement of" + echo "# $ $@" + echo "exit 0" + chmod +x $file + exit 1 + fi + ;; + + bison|yacc) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.y' file. You may need the \`Bison' package + in order for those modifications to take effect. You can get + \`Bison' from any GNU archive site." + rm -f y.tab.c y.tab.h + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.y) + SRCFILE=`echo "$LASTARG" | sed 's/y$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.c + fi + SRCFILE=`echo "$LASTARG" | sed 's/y$/h/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" y.tab.h + fi + ;; + esac + fi + if [ ! -f y.tab.h ]; then + echo >y.tab.h + fi + if [ ! -f y.tab.c ]; then + echo 'main() { return 0; }' >y.tab.c + fi + ;; + + lex|flex) + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.l' file. You may need the \`Flex' package + in order for those modifications to take effect. You can get + \`Flex' from any GNU archive site." + rm -f lex.yy.c + if [ $# -ne 1 ]; then + eval LASTARG="\${$#}" + case "$LASTARG" in + *.l) + SRCFILE=`echo "$LASTARG" | sed 's/l$/c/'` + if [ -f "$SRCFILE" ]; then + cp "$SRCFILE" lex.yy.c + fi + ;; + esac + fi + if [ ! -f lex.yy.c ]; then + echo 'main() { return 0; }' >lex.yy.c + fi + ;; + + help2man) + if test -z "$run" && ($1 --version) > /dev/null 2>&1; then + # We have it, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a dependency of a manual page. You may need the + \`Help2man' package in order for those modifications to take + effect. You can get \`Help2man' from any GNU archive site." + + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed -n 's/.*--output=\([^ ]*\).*/\1/p'` + fi + if [ -f "$file" ]; then + touch $file + else + test -z "$file" || exec >$file + echo ".ab help2man is required to generate this page" + exit 1 + fi + ;; + + makeinfo) + if test -z "$run" && (makeinfo --version) > /dev/null 2>&1; then + # We have makeinfo, but it failed. + exit 1 + fi + + echo 1>&2 "\ +WARNING: \`$1' is missing on your system. You should only need it if + you modified a \`.texi' or \`.texinfo' file, or any other file + indirectly affecting the aspect of the manual. The spurious + call might also be the consequence of using a buggy \`make' (AIX, + DU, IRIX). You might want to install the \`Texinfo' package or + the \`GNU make' package. Grab either from any GNU archive site." + file=`echo "$*" | sed -n 's/.*-o \([^ ]*\).*/\1/p'` + if test -z "$file"; then + file=`echo "$*" | sed 's/.* \([^ ]*\) *$/\1/'` + file=`sed -n '/^@setfilename/ { s/.* \([^ ]*\) *$/\1/; p; q; }' $file` + fi + touch $file + ;; + + tar) + shift + if test -n "$run"; then + echo 1>&2 "ERROR: \`tar' requires --run" + exit 1 + fi + + # We have already tried tar in the generic part. + # Look for gnutar/gtar before invocation to avoid ugly error + # messages. + if (gnutar --version > /dev/null 2>&1); then + gnutar "$@" && exit 0 + fi + if (gtar --version > /dev/null 2>&1); then + gtar "$@" && exit 0 + fi + firstarg="$1" + if shift; then + case "$firstarg" in + *o*) + firstarg=`echo "$firstarg" | sed s/o//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + case "$firstarg" in + *h*) + firstarg=`echo "$firstarg" | sed s/h//` + tar "$firstarg" "$@" && exit 0 + ;; + esac + fi + + echo 1>&2 "\ +WARNING: I can't seem to be able to run \`tar' with the given arguments. + You may want to install GNU tar or Free paxutils, or check the + command line arguments." + exit 1 + ;; + + *) + echo 1>&2 "\ +WARNING: \`$1' is needed, and you do not seem to have it handy on your + system. You might have modified some files without having the + proper tools for further handling them. Check the \`README' file, + it often tells you about the needed prerequisites for installing + this package. You may also peek at any GNU archive site, in case + some other package would contain this missing \`$1' program." + exit 1 + ;; +esac + +exit 0 diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 mkinstalldirs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mkinstalldirs Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,111 @@ +#! /bin/sh +# mkinstalldirs --- make directory hierarchy +# Author: Noah Friedman +# Created: 1993-05-16 +# Public domain + +errstatus=0 +dirmode="" + +usage="\ +Usage: mkinstalldirs [-h] [--help] [-m mode] dir ..." + +# process command line arguments +while test $# -gt 0 ; do + case $1 in + -h | --help | --h*) # -h for help + echo "$usage" 1>&2 + exit 0 + ;; + -m) # -m PERM arg + shift + test $# -eq 0 && { echo "$usage" 1>&2; exit 1; } + dirmode=$1 + shift + ;; + --) # stop option processing + shift + break + ;; + -*) # unknown option + echo "$usage" 1>&2 + exit 1 + ;; + *) # first non-opt arg + break + ;; + esac +done + +for file +do + if test -d "$file"; then + shift + else + break + fi +done + +case $# in + 0) exit 0 ;; +esac + +case $dirmode in + '') + if mkdir -p -- . 2>/dev/null; then + echo "mkdir -p -- $*" + exec mkdir -p -- "$@" + fi + ;; + *) + if mkdir -m "$dirmode" -p -- . 2>/dev/null; then + echo "mkdir -m $dirmode -p -- $*" + exec mkdir -m "$dirmode" -p -- "$@" + fi + ;; +esac + +for file +do + set fnord `echo ":$file" | sed -ne 's/^:\//#/;s/^://;s/\// /g;s/^#/\//;p'` + shift + + pathcomp= + for d + do + pathcomp="$pathcomp$d" + case $pathcomp in + -*) pathcomp=./$pathcomp ;; + esac + + if test ! -d "$pathcomp"; then + echo "mkdir $pathcomp" + + mkdir "$pathcomp" || lasterr=$? + + if test ! -d "$pathcomp"; then + errstatus=$lasterr + else + if test ! -z "$dirmode"; then + echo "chmod $dirmode $pathcomp" + lasterr="" + chmod "$dirmode" "$pathcomp" || lasterr=$? + + if test ! -z "$lasterr"; then + errstatus=$lasterr + fi + fi + fi + fi + + pathcomp="$pathcomp/" + done +done + +exit $errstatus + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# End: +# mkinstalldirs ends here diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 py-compile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/py-compile Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,92 @@ +#!/bin/sh + +# py-compile - Compile a Python program +# Copyright 2000, 2001 Free Software Foundation, Inc. + +# 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 2, 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +# 02111-1307, USA. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# called as "py-compile [--basedir DIR] PY_FILES ... + +if [ -z "$PYTHON" ]; then + PYTHON=python +fi + +basedir= + +case "$1" in + --basedir) + basedir=$2 + shift 2 + ;; + --help) + echo "Usage: py-compile [--basedir DIR] PY_FILES ..." + echo "Byte compile some python scripts. This should be performed" + echo "after they have been moved to the final installation location" + exit 0 + ;; + --version) + echo "py-compile version 0.0" + exit 0 + ;; +esac + +if [ $# = 0 ]; then + echo "No files given to $0" 1>&2 + exit 1 +fi + +# if basedir was given, then it should be prepended to filenames before +# byte compilation. +if [ -z "$basedir" ]; then + trans="path = file" +else + trans="path = os.path.join('$basedir', file)" +fi + +$PYTHON -c " +import sys, os, string, py_compile + +files = '''$*''' +print 'Byte-compiling python modules...' +for file in string.split(files): + $trans + if not os.path.exists(path) or not (len(path) >= 3 and path[-3:] == '.py'): + continue + print file, + sys.stdout.flush() + py_compile.compile(path) +print" || exit $? + +# this will fail for python < 1.5, but that doesn't matter ... +$PYTHON -O -c " +import sys, os, string, py_compile + +files = '''$*''' +print 'Byte-compiling python modules (optimized versions) ...' +for file in string.split(files): + $trans + if not os.path.exists(path) or not (len(path) >= 3 and path[-3:] == '.py'): + continue + print file, + sys.stdout.flush() + py_compile.compile(path) +print" 2>/dev/null || : + diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 setup.py --- a/setup.py Mon Aug 30 19:42:22 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -#!/usr/bin/env python -# $Id$ - -from distutils.core import setup -import sys, os - -setup( - name = 'Revelation', - version = '0.3.3', - description = 'Password manager for GNOME 2', - author = 'Erik Grinaker', - author_email = 'erikg@codepoet.no', - url = 'http://oss.codepoet.no/revelation/', - - packages = [ 'revelation', 'revelation.datahandler' ], - package_dir = { 'revelation' : 'src/lib' }, - - scripts = [ 'src/revelation' ], - - data_files = [ - ( 'share/pixmaps', [ - 'data/images/revelation.png' - ] ), - - ( 'share/revelation/pixmaps', [ - 'data/images/account-creditcard.png', - 'data/images/account-cryptokey.png', - 'data/images/account-database.png', - 'data/images/account-door.png', - 'data/images/account-email.png', - 'data/images/account-ftp.png', - 'data/images/account-generic.png', - 'data/images/account-phone.png', - 'data/images/account-shell.png', - 'data/images/account-website.png', - 'data/images/folder.png', - 'data/images/folder-open.png', - 'data/images/password.png', - 'data/images/revelation.png', - 'data/images/revelation-16x16.png' - ] ), - - ( 'share/applications', [ - 'data/revelation.desktop' - ] ), - - ( '/etc/gconf/schemas', [ - 'data/revelation.schemas' - ] ) - ] -) - diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 src/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/Makefile.am Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,9 @@ +SUBDIRS = lib +bin_SCRIPTS = revelation +CLEANFILES = revelation + +revelation: Makefile revelation.in + sed \ + -e "s|\@pythondir\@|$(pythondir)|" \ + revelation.in > revelation + diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 src/lib/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/Makefile.am Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,23 @@ +librevelationdir = $(pythondir)/revelation +librevelation_PYTHON = \ + __init__.py \ + data.py \ + dialog.py \ + entry.py \ + io.py \ + misc.py \ + stock.py \ + widget.py + +CLEANFILES = \ + __init__.py + + +__init__.py: Makefile __init__.py.in + sed \ + -e "s|\@GCONFTOOL\@|$(GCONFTOOL)|" \ + -e "s|\@VERSION\@|$(VERSION)|" \ + -e "s|\@datadir\@|$(pkgdatadir)|" \ + -e "s|\@schemadir\@|@GCONF_SCHEMA_FILE_DIR@|" \ + __init__.py.in > __init__.py + diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 src/lib/__init__.py --- a/src/lib/__init__.py Mon Aug 30 19:42:22 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,52 +0,0 @@ -# -# Revelation 0.3.3 - a password manager for GNOME 2 -# http://oss.codepoet.no/revelation/ -# $Id$ -# -# Library initialization script -# -# -# Copyright (c) 2003-2004 Erik Grinaker -# -# 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 2 -# 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# - - -import datahandler, misc, stock, entry, widget, data, io, dialog, sys - -APPNAME = "Revelation" -VERSION = "0.3.3" -DATAVERSION = 1 -RELNAME = "Colorless green ideas sleep furiously" -URL = "http://oss.codepoet.no/revelation/" -AUTHOR = "Erik Grinaker " -COPYRIGHT = "Copyright \302\251 2003-2004 Erik Grinaker" - -PREFIX = sys.prefix -DATADIR = PREFIX + "/share/revelation" - -# set up some exceptions -class Error(Exception): - """Base class for errors""" - pass - -class CancelError(Error): - """Exception for user cancellation""" - pass - -class FileError(Error): - """Exception for file errors""" - pass - diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 src/lib/__init__.py.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/__init__.py.in Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,55 @@ +# +# Revelation 0.3.3 - a password manager for GNOME 2 +# http://oss.codepoet.no/revelation/ +# $Id$ +# +# Library initialization script +# +# +# Copyright (c) 2003-2004 Erik Grinaker +# +# 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 2 +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + + +import datahandler, misc, stock, entry, widget, data, io, dialog, sys + +APPNAME = "Revelation" +VERSION = "@VERSION@" +DATAVERSION = 1 +RELNAME = "Colorless green ideas sleep furiously" +URL = "http://oss.codepoet.no/revelation/" +AUTHOR = "Erik Grinaker " +COPYRIGHT = "Copyright \302\251 2003-2004 Erik Grinaker" + +DIR_GCONFSCHEMAS= "@schemadir@" +DIR_IMAGES = "@datadir@/images" + +FILE_GCONFTOOL = "@GCONFTOOL@" + + +# set up some exceptions +class Error(Exception): + """Base class for errors""" + pass + +class CancelError(Error): + """Exception for user cancellation""" + pass + +class FileError(Error): + """Exception for file errors""" + pass + diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 src/lib/data.py --- a/src/lib/data.py Mon Aug 30 19:42:22 2004 +0000 +++ b/src/lib/data.py Mon Aug 30 22:45:39 2004 +0000 @@ -207,7 +207,7 @@ if not revelation.io.file_exists("/etc/gconf/schemas/revelation.schemas"): return gtk.FALSE - revelation.io.execute("gconftool-2 --install-schema-file=/etc/gconf/schemas/revelation.schemas") + revelation.io.execute(revelation.FILE_GCONFTOOL + " --install-schema-file=" + revelation.DIR_GCONFSCHEMAS + "/revelation.schemas") return self.check() diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 src/lib/datahandler/Makefile.am --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/lib/datahandler/Makefile.am Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,10 @@ +datahandlerdir = $(pkglibdir)/datahandler +datahandler_DATA = \ + __init__.py \ + base.py \ + fpm.py \ + gpass.py \ + netrc.py \ + rvl.py \ + xhtml.py + diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 src/lib/dialog.py --- a/src/lib/dialog.py Mon Aug 30 19:42:22 2004 +0000 +++ b/src/lib/dialog.py Mon Aug 30 22:45:39 2004 +0000 @@ -767,7 +767,7 @@ self, revelation.APPNAME, revelation.VERSION, revelation.COPYRIGHT, "\"" + revelation.RELNAME + "\"\n\nRevelation is a password manager for the GNOME 2 desktop.", [ revelation.AUTHOR ], None, "", - gtk.gdk.pixbuf_new_from_file(revelation.DATADIR + "/pixmaps/revelation.png") + gtk.gdk.pixbuf_new_from_file(revelation.DIR_IMAGES + "/revelation.png") ) if parent is not None: diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 src/lib/stock.py --- a/src/lib/stock.py Mon Aug 30 19:42:22 2004 +0000 +++ b/src/lib/stock.py Mon Aug 30 22:45:39 2004 +0000 @@ -109,7 +109,7 @@ } for id, filename in icons.items(): - iconset = gtk.IconSet(gtk.gdk.pixbuf_new_from_file(revelation.DATADIR + "/pixmaps/" + filename)) + iconset = gtk.IconSet(gtk.gdk.pixbuf_new_from_file(revelation.DIR_IMAGES + "/" + filename)) self.add(id, iconset) itemicons = { diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 src/revelation --- a/src/revelation Mon Aug 30 19:42:22 2004 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,937 +0,0 @@ -#!/usr/bin/env python - -# -# Revelation 0.3.3 - a password manager for GNOME 2 -# http://oss.codepoet.no/revelation/ -# $Id$ -# -# Copyright (c) 2003-2004 Erik Grinaker -# -# 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 2 -# 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, write to the Free Software -# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. -# - -import pygtk -pygtk.require("2.0") - -import gtk, gnome, revelation, os, os.path, sys, gobject - - -class Revelation(revelation.widget.App): - "Main application class" - - def __init__(self): - sys.excepthook = self.__cb_exception - - gnome.init(revelation.APPNAME, revelation.APPNAME) - revelation.widget.App.__init__(self, revelation.APPNAME) - - os.umask(0077) - - gtk.window_set_default_icon_list( - gtk.gdk.pixbuf_new_from_file(revelation.DATADIR + "/pixmaps/revelation.png"), - gtk.gdk.pixbuf_new_from_file(revelation.DATADIR + "/pixmaps/revelation-16x16.png") - ) - - self.__init_facilities() - self.__init_menu() - self.__init_toolbar() - self.__init_mainarea() - self.__init_states() - - - - # init methods - def __init_facilities(self): - "Sets up various application facilities" - - try: - self.config = revelation.data.Config() - - except revelation.data.ConfigError: - revelation.dialog.Error( - None, "Configuration error", - "The Revelation configuration data could not be found in gconf. This indicates a fault in your installation, please re-install Revelation." - ).run() - - sys.exit(1) - - - self.icons = revelation.stock.IconFactory(self) - self.data = revelation.data.EntryStore() - self.clipboard = revelation.data.EntryClipboard() - self.undoqueue = revelation.data.UndoQueue(self.data) - self.finder = revelation.data.EntrySearch(self.data) - - self.data.connect("file-changed", self.__cb_state_file) - - self.clipboard.connect("copy", self.__cb_state_clipboard) - self.clipboard.connect("cut", self.__cb_state_clipboard) - - self.undoqueue.connect("changed", self.__cb_state_undo) - - self.finder.connect("changed", self.__cb_state_find) - - - def __init_mainarea(self): - "Sets up the main application area" - - self.tree = revelation.widget.Tree(self.data) - self.tree.connect("popup", self.__cb_popup_tree) - self.tree.connect("doubleclick", lambda w,d: self.entry_edit()) - self.tree.connect("key-press-event", self.__cb_keypress_tree) - self.tree.selection.connect("changed", lambda w: self.dataview.display_entry(self.data.get_entry(self.tree.get_active()))) - self.tree.selection.connect("changed", self.__cb_state_entry) - scrolledwindow = revelation.widget.ScrolledWindow(self.tree) - - self.dataview = revelation.widget.DataView() - alignment = gtk.Alignment(0.5, 0.4, 0, 0) - alignment.add(self.dataview) - - self.hpaned = revelation.widget.HPaned(scrolledwindow, alignment, self.config.get("view/pane-position")) - self.set_contents(self.hpaned) - - - def __init_menu(self): - "Sets up the application menu" - - self.create_menu(( - ("/_File", None, None, None, 0, ""), - ("/File/_New", "N", "Create a new file", lambda w,d: self.file_new(), 0, "", gtk.STOCK_NEW), - ("/File/_Open...", "O", "Open a file", lambda w,d: self.file_open(), 0, "", gtk.STOCK_OPEN), - ("/File/sep1", None, None, None, 0, ""), - ("/File/_Save", "S", "Save data to file", lambda w,d: self.file_save(self.data.file, self.data.password), 0, "", gtk.STOCK_SAVE), - ("/File/Save _As...", "S", "Save data to different file", lambda w,d: self.file_save(), 0, "", gtk.STOCK_SAVE_AS), - ("/File/_Revert", None, "Revert to the saved copy of the file", lambda w,d: self.file_revert(), 0, "", gtk.STOCK_REVERT_TO_SAVED), - ("/File/sep2", None, None, None, 0, ""), - ("/File/Change _Password...", None, "Change password of current file", lambda w,d: self.change_password(), 0, "", revelation.stock.STOCK_PASSWORD), - ("/File/_Lock...", "L", "Lock the current data file", lambda w,d: self.file_lock(), 0, "", revelation.stock.STOCK_LOCK), - ("/File/sep3", None, None, None, 0, ""), - ("/File/_Import...", None, "Import data from a foreign file", lambda w,d: self.file_import(), 0, "", revelation.stock.STOCK_IMPORT), - ("/File/_Export...", None, "Export data to a different format", lambda w,d: self.file_export(), 0, "", revelation.stock.STOCK_EXPORT), - ("/File/sep4", None, None, None, 0, ""), - ("/File/_Close", "W", "Close the application", lambda w,d: self.quit(), 0, "", gtk.STOCK_CLOSE), - ("/File/_Quit", "Q", "Quit the application", lambda w,d: self.quit(), 0, "", gtk.STOCK_QUIT), - - ("/_Edit", None, None, None, 0, ""), - ("/Edit/_Add Entry...", "Insert", "Create a new entry", lambda w,d: self.entry_add(), 0, "", revelation.stock.STOCK_ADD), - ("/Edit/_Edit", "Return", "Edit the selected entry", lambda w,d: self.entry_edit(), 0, "", revelation.stock.STOCK_EDIT), - ("/Edit/Re_move", "Delete", "Remove the selected entry", lambda w,d: self.entry_remove(), 0, "", revelation.stock.STOCK_REMOVE), - ("/Edit/_Launch", "L", "Launch the selected entry", lambda w,d: self.entry_launch(), 0, "", revelation.stock.STOCK_LAUNCH), - ("/Edit/sep1", None, None, None, 0, ""), - ("/Edit/_Undo", "Z", "Undo the last action", lambda w,d: self.undo(), 0, "", gtk.STOCK_UNDO), - ("/Edit/_Redo", "Z", "Redo the previously undone action", lambda w,d: self.redo(), 0, "", gtk.STOCK_REDO), - ("/Edit/sep2", None, None, None, 0, ""), - ("/Edit/Cu_t", "X", "Cut the entry to the clipboard", lambda w,d: self.clip_cut(), 0, "", gtk.STOCK_CUT), - ("/Edit/_Copy", "C", "Copy the entry to the clipboard", lambda w,d: self.clip_copy(), 0, "", gtk.STOCK_COPY), - ("/Edit/_Paste", "V", "Paste entry from clipboard", lambda w,d: self.clip_paste(), 0, "", gtk.STOCK_PASTE), - ("/Edit/sep3", None, None, None, 0, ""), - ("/Edit/_Find...", "F", "Search for an entry", lambda w,d: self.entry_find(), 0, "", gtk.STOCK_FIND), - ("/Edit/Find Ne_xt", "G", "Find the next search match", lambda w,d: self.__entry_find(self, revelation.data.SEARCH_NEXT), 0, ""), - ("/Edit/Find Pre_vious", "G", "Find the previous search match", lambda w,d: self.__entry_find(self, revelation.data.SEARCH_PREV), 0, ""), - ("/Edit/sep4", None, None, None, 0, ""), - ("/Edit/_Select All", "A", "Select all entries", lambda w,d: self.tree.select_all(), 0, ""), - ("/Edit/_Deselect All", "A", "Deselect all entries", lambda w,d: self.tree.unselect_all(), 0, ""), - ("/Edit/sep5", None, None, None, 0, ""), - ("/Edit/Prefere_nces", None, "Edit preferences", lambda w,d: revelation.dialog.Preferences(self, self.config).run(), 0, "", gtk.STOCK_PREFERENCES), - - ("/_View", None, None, None, 0, ""), - ("/View/_Main Toolbar", None, "Toggle display of the main toolbar", None, 0, ""), - ("/View/S_earch Toolbar", None, "Toggle display of the search toolbar", None, 0, ""), - ("/View/_Statusbar", None, "Toggle display of the statusbar", None, 0, ""), - ("/View/sep1", None, None, None, 0, ""), - ("/View/Password _Generator", None, "Open a password generator", lambda w,d: revelation.dialog.PasswordGenerator(self, self.config).run(), 0, "", revelation.stock.STOCK_GENERATE), - ("/View/Show _Passwords", "P", "Show passwords", None, 0, ""), - - ("/_Help", None, None, None, 0, ""), - ("/Help/_Homepage", None, "Visit the Revelation homepage", lambda w,d: gnome.url_show(revelation.URL), 0, "", gtk.STOCK_HOME), - ("/Help/_About", None, "Show info about this application", lambda w,d: revelation.dialog.About(self).run(), 0, "", "gnome-stock-about") - )) - - - def __init_states(self): - "Sets the initial application state" - - self.set_default_size(self.config.get("view/window-width"), self.config.get("view/window-height")) - self.move(self.config.get("view/window-position-x"), self.config.get("view/window-position-y")) - self.connect("delete_event", lambda w,d: gtk.TRUE ^ self.quit()) - - self.tree.select(None) - self.dataview.display_info() - self.data.set_file(None) - - self.if_menu.get_widget("
/Edit/Find Next").set_sensitive(gtk.FALSE) - self.if_menu.get_widget("
/Edit/Find Previous").set_sensitive(gtk.FALSE) - self.if_menu.get_widget("
/Edit/Undo").set_sensitive(gtk.FALSE) - self.if_menu.get_widget("
/Edit/Redo").set_sensitive(gtk.FALSE) - - self.show_all() - - self.config.bind_widget("view/passwords", self.if_menu.get_widget("
/View/Show Passwords")) - self.config.bind_widget("view/searchbar", self.if_menu.get_widget("
/View/Search Toolbar")) - self.config.bind_widget("view/statusbar", self.if_menu.get_widget("
/View/Statusbar")) - self.config.bind_widget("view/toolbar", self.if_menu.get_widget("
/View/Main Toolbar")) - - self.config.notify_add("view/searchbar", self.__cb_config_searchbar) - self.config.notify_add("view/statusbar", self.__cb_config_statusbar) - self.config.notify_add("view/toolbar", self.__cb_config_toolbar) - - - def __init_toolbar(self): - "Sets up the application toolbar" - - self.toolbar.button_new = self.toolbar.append_stock(gtk.STOCK_NEW, "New file", lambda w,d: self.file_new()) - self.toolbar.button_open = self.toolbar.append_stock(gtk.STOCK_OPEN, "Open file", lambda w,d: self.file_open()) - self.toolbar.button_save = self.toolbar.append_stock(gtk.STOCK_SAVE, "Save file", lambda w,d: self.file_save(self.data.file, self.data.password)) - - self.toolbar.append_space() - - self.toolbar.button_entry_add = self.toolbar.append_stock(revelation.stock.STOCK_ADD, "Add a new entry", lambda w,d: self.entry_add()) - self.toolbar.button_entry_edit = self.toolbar.append_stock(revelation.stock.STOCK_EDIT, "Edit the selected entry", lambda w,d: self.entry_edit()) - self.toolbar.button_entry_remove = self.toolbar.append_stock(revelation.stock.STOCK_REMOVE, "Remove the selected entry", lambda w,d: self.entry_remove()) - self.toolbar.button_entry_launch = self.toolbar.append_stock(revelation.stock.STOCK_LAUNCH, "Launch the selected entry", lambda w,d: self.entry_launch()) - - - # search-bar - self.searchbar = revelation.widget.Searchbar() - self.add_toolbar(self.searchbar, "searchbar", 2) - - self.searchbar.button.connect("clicked", lambda w: self.__entry_find(self, string = self.searchbar.entry.get_text())) - - - - - # exception callback - def __cb_exception(self, type, value, trace): - "Callback for unhandled exceptions" - - traceback = revelation.io.trace_exception(type, value, trace) - sys.stderr.write(traceback) - revelation.dialog.Exception(self, traceback).run() - sys.exit(1) - - - # config callbacks - def __cb_config_searchbar(self, config, value, data): - "Config callback for searchbar changes" - - if value == gtk.TRUE: - self.searchbar.show() - - else: - self.searchbar.hide() - - def __cb_config_statusbar(self, config, value, data): - "Config callback for statusbar changes" - - if value == gtk.TRUE: - self.statusbar.show() - - else: - self.statusbar.hide() - - - def __cb_config_toolbar(self, config, value, data): - "Config callback for toolbar changes" - - if value == gtk.TRUE: - self.toolbar.show() - - else: - self.toolbar.hide() - - - - # callbacks for handling ui states - def __cb_state_clipboard(self, widget, data = None): - "Sets clipboard item sensitivity as appropriate" - - self.if_menu.get_widget("
/Edit/Paste").set_sensitive(self.clipboard.has_contents()) - - - def __cb_state_entry(self, widget, data = None): - "Sets state for entry-dependent ui items" - - selected = self.tree.get_selected() - active = self.tree.get_active() - - self.toolbar.button_entry_add.set_sensitive(len(selected) < 2) - self.if_menu.get_widget("
/Edit/Add Entry...").set_sensitive(len(selected) < 2) - self.if_menu.get_widget("
/Edit/Paste").set_sensitive(len(selected) < 2 and self.clipboard.has_contents()) - - self.toolbar.button_entry_edit.set_sensitive(len(selected) == 1) - self.if_menu.get_widget("
/Edit/Edit").set_sensitive(len(selected) == 1) - - self.toolbar.button_entry_remove.set_sensitive(len(selected) > 0) - self.if_menu.get_widget("
/Edit/Remove").set_sensitive(len(selected) > 0) - self.if_menu.get_widget("
/Edit/Cut").set_sensitive(len(selected) > 0) - self.if_menu.get_widget("
/Edit/Copy").set_sensitive(len(selected) > 0) - - - for iter in selected: - - if self.data.get_entry(iter).can_launch() == gtk.TRUE: - launch = gtk.TRUE - break - - else: - launch = gtk.FALSE - - self.toolbar.button_entry_launch.set_sensitive(launch) - self.if_menu.get_widget("
/Edit/Launch").set_sensitive(launch) - - - def __cb_state_file(self, widget, file = None): - "Sets various states based on current file" - - self.if_menu.get_widget("
/File/Revert").set_sensitive(file is not None) - self.if_menu.get_widget("
/File/Lock...").set_sensitive(file is not None) - - if file is None: - self.set_title("[New file]") - - else: - self.set_title(os.path.basename(file)) - os.chdir(os.path.dirname(file)) - - - def __cb_state_find(self, widget, data = None): - "Sets ui item states for find-related items" - - self.if_menu.get_widget("
/Edit/Find Next").set_sensitive(self.finder.string != "") - self.if_menu.get_widget("
/Edit/Find Previous").set_sensitive(self.finder.string != "") - - - def __cb_state_undo(self, widget, data = None): - "Sets states for undo-related ui items" - - # update undo widgets - widget = self.if_menu.get_widget("
/Edit/Undo") - action = "_Undo" - - if self.undoqueue.can_undo(): - widget.get_children()[0].set_label(action + " " + self.undoqueue.get_action(revelation.data.UNDO).name) - widget.set_sensitive(gtk.TRUE) - - else: - widget.get_children()[0].set_label(action) - widget.set_sensitive(gtk.FALSE) - - # update redo widgets - widget = self.if_menu.get_widget("
/Edit/Redo") - action = "_Redo" - - if self.undoqueue.can_redo(): - widget.get_children()[0].set_label(action + " " + self.undoqueue.get_action(revelation.data.REDO).name) - widget.set_sensitive(gtk.TRUE) - - else: - widget.get_children()[0].set_label(action) - widget.set_sensitive(gtk.FALSE) - - - - # other, normal callbacks - def __cb_keypress_tree(self, widget, data = None): - "Handles key presses for the tree" - - # return - if data.keyval == 65293: - self.entry_edit() - - # insert - elif data.keyval == 65379: - self.entry_add() - - # delete - elif data.keyval == 65535: - self.entry_remove() - - - def __cb_popup_tree(self, widget, menuitems): - "Create a popup-menu for the treeview" - - # create the popup menu - iters = self.tree.get_selected() - - if len(iters) == 0 or (len(iters) == 1 and type(self.data.get_entry(iters[0])) == revelation.entry.FolderEntry): - menuitems.append(("/_Add Entry...", None, "Create a new entry", lambda w,d: self.entry_add(), 0, "", revelation.stock.STOCK_ADD)) - - if len(iters) == 1: - menuitems.append(("/_Edit", None, "Edit the selected entry", lambda w,d: self.entry_edit(), 0, "", revelation.stock.STOCK_EDIT)) - - if len(iters) > 0: - menuitems.append(("/Re_move", None, "Remove the selected entry", lambda w,d: self.entry_remove(), 0, "", revelation.stock.STOCK_REMOVE)) - - for iter in iters: - if self.data.get_entry(iter).can_launch() == gtk.TRUE: - menuitems.append(("/_Launch", None, "Launch the selected entry", lambda w,d: self.entry_launch(), 0, "", revelation.stock.STOCK_LAUNCH)) - break - - clipboardmenu = [] - - if len(iters) > 0: - clipboardmenu.append(("/Cu_t", "", "Cut the selected entry to the clipboard", lambda w,d: self.clip_cut(), 0, "", gtk.STOCK_CUT)) - clipboardmenu.append(("/_Copy", "", "Copy the selected entry to the keyboard", lambda w,d: self.clip_copy(), 0, "", gtk.STOCK_COPY)) - - if len(iters) < 2 and self.clipboard.has_contents(): - clipboardmenu.append(("/_Paste", "", "Paste entry from clipboard", lambda w,d: self.clip_paste(), 0, "", gtk.STOCK_PASTE)) - - - if len(clipboardmenu) > 0: - menuitems.append(("/sep1", None, None, None, 0, "")) - menuitems.extend(clipboardmenu) - - - - # various private methods - def __entry_find(self, parent, direction = revelation.data.SEARCH_NEXT, string = None): - "Searches for an entry" - - if string is not None: - self.finder.string = string - - self.finder.folders = self.config.get("search/folders") - self.finder.casesens = self.config.get("search/casesens") - self.finder.namedesc = self.config.get("search/namedesc") - - match = self.finder.find(self.tree.get_active(), direction) - - if match is None: - revelation.dialog.Error(parent, "No match found", "The string you searched for did not match any entries. Try using a different search-phrase.").run() - - else: - self.tree.select(match) - - - def __file_autosave(self): - "Autosaves the current file, due to data modification" - - if self.data.file is None or self.data.password is None: - return - - if not self.config.get("file/autosave"): - return - - self.file_save(self.data.file, self.data.password) - - - def __file_load(self, datafile): - "Loads data from a file into an entrystore" - - try: - if datafile.handler is None: - datafile.detect_type() - - datafile.check_file() - - dialog = revelation.dialog.PasswordLoad(self, datafile.file) - entrystore = None - - while 1: - - # load datafile, ask for password if needed - try: - if datafile.needs_password() and datafile.password is None: - datafile.password = dialog.run() - - entrystore = datafile.load() - - except revelation.datahandler.PasswordError: - datafile.password = None - revelation.dialog.Error( - self, "Incorrect password", - "The password you entered for the file'" + datafile.file + "' was not correct." - ).run() - - except: - dialog.destroy() - raise - - else: - dialog.destroy() - break - - return entrystore - - except revelation.datahandler.FormatError: - self.statusbar.set_status("Open failed") - revelation.dialog.Error( - self, "Invalid file format", - "The file '" + datafile.file + "' contains invalid data." - ).run() - - except revelation.entry.EntryError: - self.statusbar.set_status("Open failed") - revelation.dialog.Error( - self, "Unknown data", - "The file '" + datafile.file + "' contains unknown data. It may have been created by a future version of Revelation, try upgrading to a newer version." - ).run() - - except revelation.datahandler.VersionError: - self.statusbar.set_status("Open failed") - revelation.dialog.Error( - self, "Unknown data version", - "The file '" + datafile.file + "' has a future version number - upgrade Revelation to a more recent version to open it." - ).run() - - except revelation.io.DetectError: - self.statusbar.set_status("Open failed") - revelation.dialog.Error( - self, "Filetype autodetection failed", - "The format of the file '" + datafile.file + "' could not be detected automatically. It may still be possible to open the file, try specifying the file type manually." - ).run() - - except IOError: - self.statusbar.set_status("Open failed") - revelation.dialog.Error( - self, "Unable to open file", - "The file '" + datafile.file + "' could not be opened. Make sure that the file exists, and that you have the proper permissions to open it." - ).run() - - except revelation.CancelError: - dialog.destroy() - raise - - - def __file_save(self, datafile): - "Saves data to a file" - - try: - if datafile.file != self.data.file and revelation.io.file_exists(datafile.file): - revelation.dialog.FileOverwrite(self, datafile.file).run() - - if not datafile.needs_password(): - revelation.dialog.FileExportInsecure(self).run() - - elif datafile.password is None: - try: - dialog = revelation.dialog.PasswordSave(self, datafile.file) - datafile.password = dialog.run() - dialog.destroy() - - except revelation.CancelError: - dialog.destroy() - raise - - - datafile.save(self.data) - - return gtk.TRUE - - except IOError: - revelation.dialog.Error(self, "Unable to write to file", "The file '" + datafile.file + "' could not be opened for writing. Make sure that you have the proper permissions to write to it.").run() - self.statusbar.set_status("Save failed") - - return gtk.FALSE - - - def __save_changes(self, dialog): - "Asks the user if she wants to save her changes" - - try: - if not self.data.changed: - return gtk.TRUE - - if dialog(self).run() == gtk.TRUE: - return self.file_save(self.data.file, self.data.password) - - return gtk.TRUE - - except revelation.CancelError: - return gtk.FALSE - - - - # public methods - def change_password(self): - "Changes the password of the current data file" - - try: - dialog = revelation.dialog.PasswordChange(self, self.data.password) - self.data.password = dialog.run() - - self.__file_autosave() - self.statusbar.set_status("Password changed") - - except revelation.CancelError: - self.statusbar.set_status("Password change cancelled") - - dialog.destroy() - - - def clip_copy(self): - "Copies selected entries to the clipboard" - - iters = self.data.filter_parents(self.tree.get_selected()) - self.clipboard.copy(self.data, iters) - - - def clip_cut(self): - "Cuts selected entries to the clipboard" - - iters = self.data.filter_parents(self.tree.get_selected()) - self.undoqueue.add_action(revelation.data.UNDO_ACTION_CUT, iters) - self.clipboard.cut(self.data, iters) - self.__file_autosave() - self.tree.unselect_all() - - - def clip_paste(self): - "Pastes entries from the clipboard" - - if not self.clipboard.has_contents(): - return - - iters = self.clipboard.paste(self.data, self.tree.get_active()) - self.undoqueue.add_action(revelation.data.UNDO_ACTION_PASTE, iters) - self.__file_autosave() - self.tree.select(iters[0]) - - - def entry_add(self): - "Adds an entry" - - try: - entry = revelation.dialog.EntryEdit(self, "Add entry").run() - iter = self.data.add_entry(self.tree.get_active(), entry) - - self.undoqueue.add_action(revelation.data.UNDO_ACTION_ADD, iter) - self.__file_autosave() - - self.tree.select(iter) - self.statusbar.set_status("Added entry '" + entry.name + "'") - - except revelation.CancelError: - self.statusbar.set_status("Add entry cancelled") - - - def entry_edit(self): - "Edits an entry" - - iter = self.tree.get_active() - - if iter is None: - return - - try: - entry = self.data.get_entry(iter) - dialog = revelation.dialog.EntryEdit(self, "Edit entry", entry) - - if type(entry) == revelation.entry.FolderEntry and self.data.iter_n_children(iter) > 0: - dialog.set_typechange_allowed(gtk.FALSE) - - newentry = dialog.run() - self.data.update_entry(iter, newentry) - self.undoqueue.add_action(revelation.data.UNDO_ACTION_EDIT, iter, entry) - - self.__file_autosave() - self.tree.select(iter) - self.statusbar.set_status("Updated entry '" + newentry.name + "'") - - except revelation.CancelError: - self.statusbar.set_status("Update entry cancelled") - - - def entry_find(self): - "Searches for an entry" - - dialog = revelation.dialog.Find(self, self.config) - dialog.entry_phrase.set_text(self.finder.string) - dialog.dropdown.set_type(self.finder.type) - - while 1: - response = dialog.run() - self.finder.string = dialog.entry_phrase.get_text() - self.finder.type = dialog.dropdown.get_type() - - if response == revelation.dialog.RESPONSE_NEXT: - self.__entry_find(dialog, revelation.data.SEARCH_NEXT) - - elif response == revelation.dialog.RESPONSE_PREVIOUS: - self.__entry_find(dialog, revelation.data.SEARCH_PREV) - - else: - dialog.destroy() - break - - - - def entry_launch(self): - "Launches an entry" - - iters = self.tree.get_selected() - - for iter in iters: - try: - entry = self.data.get_entry(iter) - - if entry.can_launch(): - entry.launch() - - self.statusbar.set_status("Launched entry '" + entry.name + "'") - - except revelation.entry.LaunchDataError: - revelation.dialog.Error(self, "Missing launcher data", "The entry '" + entry.name + "' does not have all the data required to launch it.").run() - - except revelation.entry.LaunchFormatError: - revelation.dialog.Error(self, "Invalid launcher format", "The launch command for '" + entry.typename + "' entries is invalid, please correct this in the preferences.").run() - - - - def entry_remove(self): - "Removes one or more entries" - - iters = self.tree.get_selected() - - if len(iters) == 0: - return - - entries = self.data.get_entries(iters) - - - try: - if revelation.dialog.EntryRemove(self, entries).run() != gtk.TRUE: - raise revelation.CancelError - - if len(iters) == 1: - statustext = "Removed entry '" + entries[0].name + "'" - - else: - statustext = "Removed " + str(len(entries)) + " entries" - - iters = self.data.filter_parents(iters) - self.undoqueue.add_action(revelation.data.UNDO_ACTION_REMOVE, iters) - - for iter in iters: - self.data.remove_entry(iter) - - self.__file_autosave() - self.tree.unselect_all() - self.statusbar.set_status(statustext) - - except revelation.CancelError: - self.statusbar.set_status("Remove entry cancelled") - - - - def file_export(self): - "Exports data to a foreign file format" - - try: - file, handler = revelation.dialog.ExportFileSelector(self).run() - datafile = revelation.io.DataFile(file, handler) - self.__file_save(datafile) - self.statusbar.set_status("Data exported to " + datafile.file) - - except revelation.CancelError: - self.statusbar.set_status("Export cancelled") - - - def file_import(self): - "Imports data from a foreign file" - - try: - file, handler = revelation.dialog.ImportFileSelector(self).run() - datafile = revelation.io.DataFile(file, handler) - entrystore = self.__file_load(datafile) - - if entrystore is None: - return - - iters = self.data.import_entrystore(entrystore) - self.undoqueue.add_action(revelation.data.UNDO_ACTION_IMPORT, iters) - self.__file_autosave() - self.statusbar.set_status("Data imported from " + datafile.file) - - except revelation.CancelError: - self.statusbar.set_status("Import cancelled") - - - def file_lock(self): - "Locks the current data file" - - if self.data.password is None: - return - - iter = self.tree.get_active() - self.tree.set_model(None) - self.dataview.clear() - self.statusbar.set_status("File locked") - - dialog = revelation.dialog.PasswordLock(self) - - while 1: - if dialog.run() == self.data.password: - break - - else: - revelation.dialog.Error( - dialog, "Incorrect password", - "The password you entered was not correct, please try again." - ).run() - - - dialog.destroy() - - self.tree.set_model(self.data) - self.tree.select(iter) - self.statusbar.set_status("File unlocked") - - - def file_new(self): - "Opens a new file" - - if not self.__save_changes(revelation.dialog.FileChangedNew): - self.statusbar.set_status("New file cancelled") - return - - self.data.clear() - self.statusbar.set_status("New file created") - - - def file_open(self, file = None, password = None): - "Opens a data file" - - try: - if not self.__save_changes(revelation.dialog.FileChangedOpen): - raise revelation.CancelError - - if file is None: - file = revelation.dialog.OpenFileSelector(self).run() - - datafile = revelation.io.DataFile(file, revelation.datahandler.Revelation, password) - entrystore = self.__file_load(datafile) - - if entrystore is None: - return - - self.data.replace_entrystore(entrystore) - self.statusbar.set_status("Opened file " + datafile.file) - - - except revelation.CancelError: - self.statusbar.set_status("Open cancelled") - - - def file_revert(self): - "Reverts to the saved version of the file" - - if not self.__save_changes(revelation.dialog.FileChangedRevert): - self.statusbar.set_status("Revert cancelled") - return gtk.FALSE - - self.data.changed = gtk.FALSE - self.file_open(self.data.file, self.data.filepassword) - - - def file_save(self, file = None, password = None): - "Saves a data file" - - try: - if file is None: - file = revelation.dialog.SaveFileSelector(self).run() - - datafile = revelation.io.DataFile(file, revelation.datahandler.Revelation, password) - - if self.__file_save(datafile) == gtk.TRUE: - self.data.set_file(file, password) - self.statusbar.set_status("Data saved to file " + file) - - return gtk.TRUE - - except revelation.CancelError: - self.statusbar.set_status("Save cancelled") - return gtk.FALSE - - - def quit(self): - "Quits the application" - - if not self.__save_changes(revelation.dialog.FileChangedQuit): - self.statusbar.set_status("Quit cancelled") - return gtk.FALSE - - width, height = self.get_size() - self.config.set("view/window-width", width) - self.config.set("view/window-height", height) - - x, y = self.get_position() - self.config.set("view/window-position-x", x) - self.config.set("view/window-position-y", y) - - self.config.set("view/pane-position", self.hpaned.get_position()) - - gtk.main_quit() - return gtk.TRUE - - - def redo(self): - "Redo the previously undone operation" - - if not self.undoqueue.can_redo(): - return - - action = self.undoqueue.get_action(revelation.data.REDO) - iters = self.undoqueue.redo() - - if len(iters) > 0: - self.tree.select(iters[0]) - - else: - self.tree.unselect_all() - - self.__file_autosave() - self.statusbar.set_status(action.name.capitalize() + " redone") - - - def run(self): - "Run the application" - - if len(sys.argv) > 1: - self.file_open(os.path.abspath(sys.argv[1])) - - elif self.config.get("file/autoload") and self.config.get("file/autoload_file") != "": - self.file_open(self.config.get("file/autoload_file")) - - gtk.main() - - - def undo(self): - "Attempts to undo the previous operation" - - if not self.undoqueue.can_undo(): - return - - action = self.undoqueue.get_action() - iters = self.undoqueue.undo() - - if len(iters) > 0: - self.tree.select(iters[0]) - - else: - self.tree.unselect_all() - - self.__file_autosave() - self.statusbar.set_status(action.name.capitalize() + " undone") - - - -if __name__ == "__main__": - try: - Revelation().run() - - except KeyboardInterrupt: - sys.exit(1) - diff -r ec17fa90357a05ae5d96698b2df457224cf1f6b8 -r a207757f8451c93d40a7620b523ac7a6c169f6d2 src/revelation.in --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/revelation.in Mon Aug 30 22:45:39 2004 +0000 @@ -0,0 +1,939 @@ +#!/usr/bin/env python + +# +# Revelation 0.3.3 - a password manager for GNOME 2 +# http://oss.codepoet.no/revelation/ +# $Id$ +# +# Copyright (c) 2003-2004 Erik Grinaker +# +# 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 2 +# 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, write to the Free Software +# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# + +import sys + +if "@pythondir@" not in sys.path: + sys.path.append("@pythondir@") + +import gtk, gnome, revelation, os, os.path, gobject + + +class Revelation(revelation.widget.App): + "Main application class" + + def __init__(self): + sys.excepthook = self.__cb_exception + + gnome.init(revelation.APPNAME, revelation.APPNAME) + revelation.widget.App.__init__(self, revelation.APPNAME) + + os.umask(0077) + + gtk.window_set_default_icon_list( + gtk.gdk.pixbuf_new_from_file(revelation.DIR_IMAGES + "/revelation.png"), + gtk.gdk.pixbuf_new_from_file(revelation.DIR_IMAGES + "/revelation-16x16.png") + ) + + self.__init_facilities() + self.__init_menu() + self.__init_toolbar() + self.__init_mainarea() + self.__init_states() + + + + # init methods + def __init_facilities(self): + "Sets up various application facilities" + + try: + self.config = revelation.data.Config() + + except revelation.data.ConfigError: + revelation.dialog.Error( + None, "Configuration error", + "The Revelation configuration data could not be found in gconf. This indicates a fault in your installation, please re-install Revelation." + ).run() + + sys.exit(1) + + + self.icons = revelation.stock.IconFactory(self) + self.data = revelation.data.EntryStore() + self.clipboard = revelation.data.EntryClipboard() + self.undoqueue = revelation.data.UndoQueue(self.data) + self.finder = revelation.data.EntrySearch(self.data) + + self.data.connect("file-changed", self.__cb_state_file) + + self.clipboard.connect("copy", self.__cb_state_clipboard) + self.clipboard.connect("cut", self.__cb_state_clipboard) + + self.undoqueue.connect("changed", self.__cb_state_undo) + + self.finder.connect("changed", self.__cb_state_find) + + + def __init_mainarea(self): + "Sets up the main application area" + + self.tree = revelation.widget.Tree(self.data) + self.tree.connect("popup", self.__cb_popup_tree) + self.tree.connect("doubleclick", lambda w,d: self.entry_edit()) + self.tree.connect("key-press-event", self.__cb_keypress_tree) + self.tree.selection.connect("changed", lambda w: self.dataview.display_entry(self.data.get_entry(self.tree.get_active()))) + self.tree.selection.connect("changed", self.__cb_state_entry) + scrolledwindow = revelation.widget.ScrolledWindow(self.tree) + + self.dataview = revelation.widget.DataView() + alignment = gtk.Alignment(0.5, 0.4, 0, 0) + alignment.add(self.dataview) + + self.hpaned = revelation.widget.HPaned(scrolledwindow, alignment, self.config.get("view/pane-position")) + self.set_contents(self.hpaned) + + + def __init_menu(self): + "Sets up the application menu" + + self.create_menu(( + ("/_File", None, None, None, 0, ""), + ("/File/_New", "N", "Create a new file", lambda w,d: self.file_new(), 0, "", gtk.STOCK_NEW), + ("/File/_Open...", "O", "Open a file", lambda w,d: self.file_open(), 0, "", gtk.STOCK_OPEN), + ("/File/sep1", None, None, None, 0, ""), + ("/File/_Save", "S", "Save data to file", lambda w,d: self.file_save(self.data.file, self.data.password), 0, "", gtk.STOCK_SAVE), + ("/File/Save _As...", "S", "Save data to different file", lambda w,d: self.file_save(), 0, "", gtk.STOCK_SAVE_AS), + ("/File/_Revert", None, "Revert to the saved copy of the file", lambda w,d: self.file_revert(), 0, "", gtk.STOCK_REVERT_TO_SAVED), + ("/File/sep2", None, None, None, 0, ""), + ("/File/Change _Password...", None, "Change password of current file", lambda w,d: self.change_password(), 0, "", revelation.stock.STOCK_PASSWORD), + ("/File/_Lock...", "L", "Lock the current data file", lambda w,d: self.file_lock(), 0, "", revelation.stock.STOCK_LOCK), + ("/File/sep3", None, None, None, 0, ""), + ("/File/_Import...", None, "Import data from a foreign file", lambda w,d: self.file_import(), 0, "", revelation.stock.STOCK_IMPORT), + ("/File/_Export...", None, "Export data to a different format", lambda w,d: self.file_export(), 0, "", revelation.stock.STOCK_EXPORT), + ("/File/sep4", None, None, None, 0, ""), + ("/File/_Close", "W", "Close the application", lambda w,d: self.quit(), 0, "", gtk.STOCK_CLOSE), + ("/File/_Quit", "Q", "Quit the application", lambda w,d: self.quit(), 0, "", gtk.STOCK_QUIT), + + ("/_Edit", None, None, None, 0, ""), + ("/Edit/_Add Entry...", "Insert", "Create a new entry", lambda w,d: self.entry_add(), 0, "", revelation.stock.STOCK_ADD), + ("/Edit/_Edit", "Return", "Edit the selected entry", lambda w,d: self.entry_edit(), 0, "", revelation.stock.STOCK_EDIT), + ("/Edit/Re_move", "Delete", "Remove the selected entry", lambda w,d: self.entry_remove(), 0, "", revelation.stock.STOCK_REMOVE), + ("/Edit/_Launch", "L", "Launch the selected entry", lambda w,d: self.entry_launch(), 0, "", revelation.stock.STOCK_LAUNCH), + ("/Edit/sep1", None, None, None, 0, ""), + ("/Edit/_Undo", "Z", "Undo the last action", lambda w,d: self.undo(), 0, "", gtk.STOCK_UNDO), + ("/Edit/_Redo", "Z", "Redo the previously undone action", lambda w,d: self.redo(), 0, "", gtk.STOCK_REDO), + ("/Edit/sep2", None, None, None, 0, ""), + ("/Edit/Cu_t", "X", "Cut the entry to the clipboard", lambda w,d: self.clip_cut(), 0, "", gtk.STOCK_CUT), + ("/Edit/_Copy", "C", "Copy the entry to the clipboard", lambda w,d: self.clip_copy(), 0, "", gtk.STOCK_COPY), + ("/Edit/_Paste", "V", "Paste entry from clipboard", lambda w,d: self.clip_paste(), 0, "", gtk.STOCK_PASTE), + ("/Edit/sep3", None, None, None, 0, ""), + ("/Edit/_Find...", "F", "Search for an entry", lambda w,d: self.entry_find(), 0, "", gtk.STOCK_FIND), + ("/Edit/Find Ne_xt", "G", "Find the next search match", lambda w,d: self.__entry_find(self, revelation.data.SEARCH_NEXT), 0, ""), + ("/Edit/Find Pre_vious", "G", "Find the previous search match", lambda w,d: self.__entry_find(self, revelation.data.SEARCH_PREV), 0, ""), + ("/Edit/sep4", None, None, None, 0, ""), + ("/Edit/_Select All", "A", "Select all entries", lambda w,d: self.tree.select_all(), 0, ""), + ("/Edit/_Deselect All", "A", "Deselect all entries", lambda w,d: self.tree.unselect_all(), 0, ""), + ("/Edit/sep5", None, None, None, 0, ""), + ("/Edit/Prefere_nces", None, "Edit preferences", lambda w,d: revelation.dialog.Preferences(self, self.config).run(), 0, "", gtk.STOCK_PREFERENCES), + + ("/_View", None, None, None, 0, ""), + ("/View/_Main Toolbar", None, "Toggle display of the main toolbar", None, 0, ""), + ("/View/S_earch Toolbar", None, "Toggle display of the search toolbar", None, 0, ""), + ("/View/_Statusbar", None, "Toggle display of the statusbar", None, 0, ""), + ("/View/sep1", None, None, None, 0, ""), + ("/View/Password _Generator", None, "Open a password generator", lambda w,d: revelation.dialog.PasswordGenerator(self, self.config).run(), 0, "", revelation.stock.STOCK_GENERATE), + ("/View/Show _Passwords", "P", "Show passwords", None, 0, ""), + + ("/_Help", None, None, None, 0, ""), + ("/Help/_Homepage", None, "Visit the Revelation homepage", lambda w,d: gnome.url_show(revelation.URL), 0, "", gtk.STOCK_HOME), + ("/Help/_About", None, "Show info about this application", lambda w,d: revelation.dialog.About(self).run(), 0, "", "gnome-stock-about") + )) + + + def __init_states(self): + "Sets the initial application state" + + self.set_default_size(self.config.get("view/window-width"), self.config.get("view/window-height")) + self.move(self.config.get("view/window-position-x"), self.config.get("view/window-position-y")) + self.connect("delete_event", lambda w,d: gtk.TRUE ^ self.quit()) + + self.tree.select(None) + self.dataview.display_info() + self.data.set_file(None) + + self.if_menu.get_widget("
/Edit/Find Next").set_sensitive(gtk.FALSE) + self.if_menu.get_widget("
/Edit/Find Previous").set_sensitive(gtk.FALSE) + self.if_menu.get_widget("
/Edit/Undo").set_sensitive(gtk.FALSE) + self.if_menu.get_widget("
/Edit/Redo").set_sensitive(gtk.FALSE) + + self.show_all() + + self.config.bind_widget("view/passwords", self.if_menu.get_widget("
/View/Show Passwords")) + self.config.bind_widget("view/searchbar", self.if_menu.get_widget("
/View/Search Toolbar")) + self.config.bind_widget("view/statusbar", self.if_menu.get_widget("
/View/Statusbar")) + self.config.bind_widget("view/toolbar", self.if_menu.get_widget("
/View/Main Toolbar")) + + self.config.notify_add("view/searchbar", self.__cb_config_searchbar) + self.config.notify_add("view/statusbar", self.__cb_config_statusbar) + self.config.notify_add("view/toolbar", self.__cb_config_toolbar) + + + def __init_toolbar(self): + "Sets up the application toolbar" + + self.toolbar.button_new = self.toolbar.append_stock(gtk.STOCK_NEW, "New file", lambda w,d: self.file_new()) + self.toolbar.button_open = self.toolbar.append_stock(gtk.STOCK_OPEN, "Open file", lambda w,d: self.file_open()) + self.toolbar.button_save = self.toolbar.append_stock(gtk.STOCK_SAVE, "Save file", lambda w,d: self.file_save(self.data.file, self.data.password)) + + self.toolbar.append_space() + + self.toolbar.button_entry_add = self.toolbar.append_stock(revelation.stock.STOCK_ADD, "Add a new entry", lambda w,d: self.entry_add()) + self.toolbar.button_entry_edit = self.toolbar.append_stock(revelation.stock.STOCK_EDIT, "Edit the selected entry", lambda w,d: self.entry_edit()) + self.toolbar.button_entry_remove = self.toolbar.append_stock(revelation.stock.STOCK_REMOVE, "Remove the selected entry", lambda w,d: self.entry_remove()) + self.toolbar.button_entry_launch = self.toolbar.append_stock(revelation.stock.STOCK_LAUNCH, "Launch the selected entry", lambda w,d: self.entry_launch()) + + + # search-bar + self.searchbar = revelation.widget.Searchbar() + self.add_toolbar(self.searchbar, "searchbar", 2) + + self.searchbar.button.connect("clicked", lambda w: self.__entry_find(self, string = self.searchbar.entry.get_text())) + + + + + # exception callback + def __cb_exception(self, type, value, trace): + "Callback for unhandled exceptions" + + traceback = revelation.io.trace_exception(type, value, trace) + sys.stderr.write(traceback) + revelation.dialog.Exception(self, traceback).run() + sys.exit(1) + + + # config callbacks + def __cb_config_searchbar(self, config, value, data): + "Config callback for searchbar changes" + + if value == gtk.TRUE: + self.searchbar.show() + + else: + self.searchbar.hide() + + def __cb_config_statusbar(self, config, value, data): + "Config callback for statusbar changes" + + if value == gtk.TRUE: + self.statusbar.show() + + else: + self.statusbar.hide() + + + def __cb_config_toolbar(self, config, value, data): + "Config callback for toolbar changes" + + if value == gtk.TRUE: + self.toolbar.show() + + else: + self.toolbar.hide() + + + + # callbacks for handling ui states + def __cb_state_clipboard(self, widget, data = None): + "Sets clipboard item sensitivity as appropriate" + + self.if_menu.get_widget("
/Edit/Paste").set_sensitive(self.clipboard.has_contents()) + + + def __cb_state_entry(self, widget, data = None): + "Sets state for entry-dependent ui items" + + selected = self.tree.get_selected() + active = self.tree.get_active() + + self.toolbar.button_entry_add.set_sensitive(len(selected) < 2) + self.if_menu.get_widget("
/Edit/Add Entry...").set_sensitive(len(selected) < 2) + self.if_menu.get_widget("
/Edit/Paste").set_sensitive(len(selected) < 2 and self.clipboard.has_contents()) + + self.toolbar.button_entry_edit.set_sensitive(len(selected) == 1) + self.if_menu.get_widget("
/Edit/Edit").set_sensitive(len(selected) == 1) + + self.toolbar.button_entry_remove.set_sensitive(len(selected) > 0) + self.if_menu.get_widget("
/Edit/Remove").set_sensitive(len(selected) > 0) + self.if_menu.get_widget("
/Edit/Cut").set_sensitive(len(selected) > 0) + self.if_menu.get_widget("
/Edit/Copy").set_sensitive(len(selected) > 0) + + + for iter in selected: + + if self.data.get_entry(iter).can_launch() == gtk.TRUE: + launch = gtk.TRUE + break + + else: + launch = gtk.FALSE + + self.toolbar.button_entry_launch.set_sensitive(launch) + self.if_menu.get_widget("
/Edit/Launch").set_sensitive(launch) + + + def __cb_state_file(self, widget, file = None): + "Sets various states based on current file" + + self.if_menu.get_widget("
/File/Revert").set_sensitive(file is not None) + self.if_menu.get_widget("
/File/Lock...").set_sensitive(file is not None) + + if file is None: + self.set_title("[New file]") + + else: + self.set_title(os.path.basename(file)) + os.chdir(os.path.dirname(file)) + + + def __cb_state_find(self, widget, data = None): + "Sets ui item states for find-related items" + + self.if_menu.get_widget("
/Edit/Find Next").set_sensitive(self.finder.string != "") + self.if_menu.get_widget("
/Edit/Find Previous").set_sensitive(self.finder.string != "") + + + def __cb_state_undo(self, widget, data = None): + "Sets states for undo-related ui items" + + # update undo widgets + widget = self.if_menu.get_widget("
/Edit/Undo") + action = "_Undo" + + if self.undoqueue.can_undo(): + widget.get_children()[0].set_label(action + " " + self.undoqueue.get_action(revelation.data.UNDO).name) + widget.set_sensitive(gtk.TRUE) + + else: + widget.get_children()[0].set_label(action) + widget.set_sensitive(gtk.FALSE) + + # update redo widgets + widget = self.if_menu.get_widget("
/Edit/Redo") + action = "_Redo" + + if self.undoqueue.can_redo(): + widget.get_children()[0].set_label(action + " " + self.undoqueue.get_action(revelation.data.REDO).name) + widget.set_sensitive(gtk.TRUE) + + else: + widget.get_children()[0].set_label(action) + widget.set_sensitive(gtk.FALSE) + + + + # other, normal callbacks + def __cb_keypress_tree(self, widget, data = None): + "Handles key presses for the tree" + + # return + if data.keyval == 65293: + self.entry_edit() + + # insert + elif data.keyval == 65379: + self.entry_add() + + # delete + elif data.keyval == 65535: + self.entry_remove() + + + def __cb_popup_tree(self, widget, menuitems): + "Create a popup-menu for the treeview" + + # create the popup menu + iters = self.tree.get_selected() + + if len(iters) == 0 or (len(iters) == 1 and type(self.data.get_entry(iters[0])) == revelation.entry.FolderEntry): + menuitems.append(("/_Add Entry...", None, "Create a new entry", lambda w,d: self.entry_add(), 0, "", revelation.stock.STOCK_ADD)) + + if len(iters) == 1: + menuitems.append(("/_Edit", None, "Edit the selected entry", lambda w,d: self.entry_edit(), 0, "", revelation.stock.STOCK_EDIT)) + + if len(iters) > 0: + menuitems.append(("/Re_move", None, "Remove the selected entry", lambda w,d: self.entry_remove(), 0, "", revelation.stock.STOCK_REMOVE)) + + for iter in iters: + if self.data.get_entry(iter).can_launch() == gtk.TRUE: + menuitems.append(("/_Launch", None, "Launch the selected entry", lambda w,d: self.entry_launch(), 0, "", revelation.stock.STOCK_LAUNCH)) + break + + clipboardmenu = [] + + if len(iters) > 0: + clipboardmenu.append(("/Cu_t", "", "Cut the selected entry to the clipboard", lambda w,d: self.clip_cut(), 0, "", gtk.STOCK_CUT)) + clipboardmenu.append(("/_Copy", "", "Copy the selected entry to the keyboard", lambda w,d: self.clip_copy(), 0, "", gtk.STOCK_COPY)) + + if len(iters) < 2 and self.clipboard.has_contents(): + clipboardmenu.append(("/_Paste", "", "Paste entry from clipboard", lambda w,d: self.clip_paste(), 0, "", gtk.STOCK_PASTE)) + + + if len(clipboardmenu) > 0: + menuitems.append(("/sep1", None, None, None, 0, "")) + menuitems.extend(clipboardmenu) + + + + # various private methods + def __entry_find(self, parent, direction = revelation.data.SEARCH_NEXT, string = None): + "Searches for an entry" + + if string is not None: + self.finder.string = string + + self.finder.folders = self.config.get("search/folders") + self.finder.casesens = self.config.get("search/casesens") + self.finder.namedesc = self.config.get("search/namedesc") + + match = self.finder.find(self.tree.get_active(), direction) + + if match is None: + revelation.dialog.Error(parent, "No match found", "The string you searched for did not match any entries. Try using a different search-phrase.").run() + + else: + self.tree.select(match) + + + def __file_autosave(self): + "Autosaves the current file, due to data modification" + + if self.data.file is None or self.data.password is None: + return + + if not self.config.get("file/autosave"): + return + + self.file_save(self.data.file, self.data.password) + + + def __file_load(self, datafile): + "Loads data from a file into an entrystore" + + try: + if datafile.handler is None: + datafile.detect_type() + + datafile.check_file() + + dialog = revelation.dialog.PasswordLoad(self, datafile.file) + entrystore = None + + while 1: + + # load datafile, ask for password if needed + try: + if datafile.needs_password() and datafile.password is None: + datafile.password = dialog.run() + + entrystore = datafile.load() + + except revelation.datahandler.PasswordError: + datafile.password = None + revelation.dialog.Error( + self, "Incorrect password", + "The password you entered for the file'" + datafile.file + "' was not correct." + ).run() + + except: + dialog.destroy() + raise + + else: + dialog.destroy() + break + + return entrystore + + except revelation.datahandler.FormatError: + self.statusbar.set_status("Open failed") + revelation.dialog.Error( + self, "Invalid file format", + "The file '" + datafile.file + "' contains invalid data." + ).run() + + except revelation.entry.EntryError: + self.statusbar.set_status("Open failed") + revelation.dialog.Error( + self, "Unknown data", + "The file '" + datafile.file + "' contains unknown data. It may have been created by a future version of Revelation, try upgrading to a newer version." + ).run() + + except revelation.datahandler.VersionError: + self.statusbar.set_status("Open failed") + revelation.dialog.Error( + self, "Unknown data version", + "The file '" + datafile.file + "' has a future version number - upgrade Revelation to a more recent version to open it." + ).run() + + except revelation.io.DetectError: + self.statusbar.set_status("Open failed") + revelation.dialog.Error( + self, "Filetype autodetection failed", + "The format of the file '" + datafile.file + "' could not be detected automatically. It may still be possible to open the file, try specifying the file type manually." + ).run() + + except IOError: + self.statusbar.set_status("Open failed") + revelation.dialog.Error( + self, "Unable to open file", + "The file '" + datafile.file + "' could not be opened. Make sure that the file exists, and that you have the proper permissions to open it." + ).run() + + except revelation.CancelError: + dialog.destroy() + raise + + + def __file_save(self, datafile): + "Saves data to a file" + + try: + if datafile.file != self.data.file and revelation.io.file_exists(datafile.file): + revelation.dialog.FileOverwrite(self, datafile.file).run() + + if not datafile.needs_password(): + revelation.dialog.FileExportInsecure(self).run() + + elif datafile.password is None: + try: + dialog = revelation.dialog.PasswordSave(self, datafile.file) + datafile.password = dialog.run() + dialog.destroy() + + except revelation.CancelError: + dialog.destroy() + raise + + + datafile.save(self.data) + + return gtk.TRUE + + except IOError: + revelation.dialog.Error(self, "Unable to write to file", "The file '" + datafile.file + "' could not be opened for writing. Make sure that you have the proper permissions to write to it.").run() + self.statusbar.set_status("Save failed") + + return gtk.FALSE + + + def __save_changes(self, dialog): + "Asks the user if she wants to save her changes" + + try: + if not self.data.changed: + return gtk.TRUE + + if dialog(self).run() == gtk.TRUE: + return self.file_save(self.data.file, self.data.password) + + return gtk.TRUE + + except revelation.CancelError: + return gtk.FALSE + + + + # public methods + def change_password(self): + "Changes the password of the current data file" + + try: + dialog = revelation.dialog.PasswordChange(self, self.data.password) + self.data.password = dialog.run() + + self.__file_autosave() + self.statusbar.set_status("Password changed") + + except revelation.CancelError: + self.statusbar.set_status("Password change cancelled") + + dialog.destroy() + + + def clip_copy(self): + "Copies selected entries to the clipboard" + + iters = self.data.filter_parents(self.tree.get_selected()) + self.clipboard.copy(self.data, iters) + + + def clip_cut(self): + "Cuts selected entries to the clipboard" + + iters = self.data.filter_parents(self.tree.get_selected()) + self.undoqueue.add_action(revelation.data.UNDO_ACTION_CUT, iters) + self.clipboard.cut(self.data, iters) + self.__file_autosave() + self.tree.unselect_all() + + + def clip_paste(self): + "Pastes entries from the clipboard" + + if not self.clipboard.has_contents(): + return + + iters = self.clipboard.paste(self.data, self.tree.get_active()) + self.undoqueue.add_action(revelation.data.UNDO_ACTION_PASTE, iters) + self.__file_autosave() + self.tree.select(iters[0]) + + + def entry_add(self): + "Adds an entry" + + try: + entry = revelation.dialog.EntryEdit(self, "Add entry").run() + iter = self.data.add_entry(self.tree.get_active(), entry) + + self.undoqueue.add_action(revelation.data.UNDO_ACTION_ADD, iter) + self.__file_autosave() + + self.tree.select(iter) + self.statusbar.set_status("Added entry '" + entry.name + "'") + + except revelation.CancelError: + self.statusbar.set_status("Add entry cancelled") + + + def entry_edit(self): + "Edits an entry" + + iter = self.tree.get_active() + + if iter is None: + return + + try: + entry = self.data.get_entry(iter) + dialog = revelation.dialog.EntryEdit(self, "Edit entry", entry) + + if type(entry) == revelation.entry.FolderEntry and self.data.iter_n_children(iter) > 0: + dialog.set_typechange_allowed(gtk.FALSE) + + newentry = dialog.run() + self.data.update_entry(iter, newentry) + self.undoqueue.add_action(revelation.data.UNDO_ACTION_EDIT, iter, entry) + + self.__file_autosave() + self.tree.select(iter) + self.statusbar.set_status("Updated entry '" + newentry.name + "'") + + except revelation.CancelError: + self.statusbar.set_status("Update entry cancelled") + + + def entry_find(self): + "Searches for an entry" + + dialog = revelation.dialog.Find(self, self.config) + dialog.entry_phrase.set_text(self.finder.string) + dialog.dropdown.set_type(self.finder.type) + + while 1: + response = dialog.run() + self.finder.string = dialog.entry_phrase.get_text() + self.finder.type = dialog.dropdown.get_type() + + if response == revelation.dialog.RESPONSE_NEXT: + self.__entry_find(dialog, revelation.data.SEARCH_NEXT) + + elif response == revelation.dialog.RESPONSE_PREVIOUS: + self.__entry_find(dialog, revelation.data.SEARCH_PREV) + + else: + dialog.destroy() + break + + + + def entry_launch(self): + "Launches an entry" + + iters = self.tree.get_selected() + + for iter in iters: + try: + entry = self.data.get_entry(iter) + + if entry.can_launch(): + entry.launch() + + self.statusbar.set_status("Launched entry '" + entry.name + "'") + + except revelation.entry.LaunchDataError: + revelation.dialog.Error(self, "Missing launcher data", "The entry '" + entry.name + "' does not have all the data required to launch it.").run() + + except revelation.entry.LaunchFormatError: + revelation.dialog.Error(self, "Invalid launcher format", "The launch command for '" + entry.typename + "' entries is invalid, please correct this in the preferences.").run() + + + + def entry_remove(self): + "Removes one or more entries" + + iters = self.tree.get_selected() + + if len(iters) == 0: + return + + entries = self.data.get_entries(iters) + + + try: + if revelation.dialog.EntryRemove(self, entries).run() != gtk.TRUE: + raise revelation.CancelError + + if len(iters) == 1: + statustext = "Removed entry '" + entries[0].name + "'" + + else: + statustext = "Removed " + str(len(entries)) + " entries" + + iters = self.data.filter_parents(iters) + self.undoqueue.add_action(revelation.data.UNDO_ACTION_REMOVE, iters) + + for iter in iters: + self.data.remove_entry(iter) + + self.__file_autosave() + self.tree.unselect_all() + self.statusbar.set_status(statustext) + + except revelation.CancelError: + self.statusbar.set_status("Remove entry cancelled") + + + + def file_export(self): + "Exports data to a foreign file format" + + try: + file, handler = revelation.dialog.ExportFileSelector(self).run() + datafile = revelation.io.DataFile(file, handler) + self.__file_save(datafile) + self.statusbar.set_status("Data exported to " + datafile.file) + + except revelation.CancelError: + self.statusbar.set_status("Export cancelled") + + + def file_import(self): + "Imports data from a foreign file" + + try: + file, handler = revelation.dialog.ImportFileSelector(self).run() + datafile = revelation.io.DataFile(file, handler) + entrystore = self.__file_load(datafile) + + if entrystore is None: + return + + iters = self.data.import_entrystore(entrystore) + self.undoqueue.add_action(revelation.data.UNDO_ACTION_IMPORT, iters) + self.__file_autosave() + self.statusbar.set_status("Data imported from " + datafile.file) + + except revelation.CancelError: + self.statusbar.set_status("Import cancelled") + + + def file_lock(self): + "Locks the current data file" + + if self.data.password is None: + return + + iter = self.tree.get_active() + self.tree.set_model(None) + self.dataview.clear() + self.statusbar.set_status("File locked") + + dialog = revelation.dialog.PasswordLock(self) + + while 1: + if dialog.run() == self.data.password: + break + + else: + revelation.dialog.Error( + dialog, "Incorrect password", + "The password you entered was not correct, please try again." + ).run() + + + dialog.destroy() + + self.tree.set_model(self.data) + self.tree.select(iter) + self.statusbar.set_status("File unlocked") + + + def file_new(self): + "Opens a new file" + + if not self.__save_changes(revelation.dialog.FileChangedNew): + self.statusbar.set_status("New file cancelled") + return + + self.data.clear() + self.statusbar.set_status("New file created") + + + def file_open(self, file = None, password = None): + "Opens a data file" + + try: + if not self.__save_changes(revelation.dialog.FileChangedOpen): + raise revelation.CancelError + + if file is None: + file = revelation.dialog.OpenFileSelector(self).run() + + datafile = revelation.io.DataFile(file, revelation.datahandler.Revelation, password) + entrystore = self.__file_load(datafile) + + if entrystore is None: + return + + self.data.replace_entrystore(entrystore) + self.statusbar.set_status("Opened file " + datafile.file) + + + except revelation.CancelError: + self.statusbar.set_status("Open cancelled") + + + def file_revert(self): + "Reverts to the saved version of the file" + + if not self.__save_changes(revelation.dialog.FileChangedRevert): + self.statusbar.set_status("Revert cancelled") + return gtk.FALSE + + self.data.changed = gtk.FALSE + self.file_open(self.data.file, self.data.filepassword) + + + def file_save(self, file = None, password = None): + "Saves a data file" + + try: + if file is None: + file = revelation.dialog.SaveFileSelector(self).run() + + datafile = revelation.io.DataFile(file, revelation.datahandler.Revelation, password) + + if self.__file_save(datafile) == gtk.TRUE: + self.data.set_file(file, password) + self.statusbar.set_status("Data saved to file " + file) + + return gtk.TRUE + + except revelation.CancelError: + self.statusbar.set_status("Save cancelled") + return gtk.FALSE + + + def quit(self): + "Quits the application" + + if not self.__save_changes(revelation.dialog.FileChangedQuit): + self.statusbar.set_status("Quit cancelled") + return gtk.FALSE + + width, height = self.get_size() + self.config.set("view/window-width", width) + self.config.set("view/window-height", height) + + x, y = self.get_position() + self.config.set("view/window-position-x", x) + self.config.set("view/window-position-y", y) + + self.config.set("view/pane-position", self.hpaned.get_position()) + + gtk.main_quit() + return gtk.TRUE + + + def redo(self): + "Redo the previously undone operation" + + if not self.undoqueue.can_redo(): + return + + action = self.undoqueue.get_action(revelation.data.REDO) + iters = self.undoqueue.redo() + + if len(iters) > 0: + self.tree.select(iters[0]) + + else: + self.tree.unselect_all() + + self.__file_autosave() + self.statusbar.set_status(action.name.capitalize() + " redone") + + + def run(self): + "Run the application" + + if len(sys.argv) > 1: + self.file_open(os.path.abspath(sys.argv[1])) + + elif self.config.get("file/autoload") and self.config.get("file/autoload_file") != "": + self.file_open(self.config.get("file/autoload_file")) + + gtk.main() + + + def undo(self): + "Attempts to undo the previous operation" + + if not self.undoqueue.can_undo(): + return + + action = self.undoqueue.get_action() + iters = self.undoqueue.undo() + + if len(iters) > 0: + self.tree.select(iters[0]) + + else: + self.tree.unselect_all() + + self.__file_autosave() + self.statusbar.set_status(action.name.capitalize() + " undone") + + + +if __name__ == "__main__": + try: + Revelation().run() + + except KeyboardInterrupt: + sys.exit(1) +