Erik Grinaker is sharing code with you
Bitbucket is a code hosting site. Unlimited public and private repositories. Free for small teams.
Don't show this againRevelation / src / revelation.in
- commit
- 8d3661dc2974
- parent
- 117efb089a91
- branch
- default
- tags
- revelation-0.4.0-pre1
don't use stock module
1 |
a207757f8451
|
#!/usr/bin/env python |
2 |
a207757f8451
|
|
3 |
a207757f8451
|
# |
4 |
b6047522c64d
|
# Revelation 0.4.0 - a password manager for GNOME 2 |
5 |
a207757f8451
|
# http://oss.codepoet.no/revelation/ |
6 |
a207757f8451
|
# $Id$ |
7 |
a207757f8451
|
# |
8 |
07c1fb2b0c27
|
# Copyright (c) 2003-2005 Erik Grinaker |
9 |
a207757f8451
|
# |
10 |
a207757f8451
|
# This program is free software; you can redistribute it and/or |
11 |
a207757f8451
|
# modify it under the terms of the GNU General Public License |
12 |
a207757f8451
|
# as published by the Free Software Foundation; either version 2 |
13 |
a207757f8451
|
# of the License, or (at your option) any later version. |
14 |
a207757f8451
|
# |
15 |
a207757f8451
|
# This program is distributed in the hope that it will be useful, |
16 |
a207757f8451
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 |
a207757f8451
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
18 |
a207757f8451
|
# GNU General Public License for more details. |
19 |
a207757f8451
|
# |
20 |
a207757f8451
|
# You should have received a copy of the GNU General Public License |
21 |
a207757f8451
|
# along with this program; if not, write to the Free Software |
22 |
a207757f8451
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
23 |
a207757f8451
|
# |
24 |
a207757f8451
|
|
25 |
f5f9aadf2e19
|
import gnome, gtk, gtk.gdk, os, pwd, sys |
26 |
a207757f8451
|
|
27 |
a207757f8451
|
if "@pythondir@" not in sys.path: |
28 |
e63d5ad72ae4
|
sys.path.insert(0, "@pythondir@") |
29 |
a207757f8451
|
|
30 |
5d99fc2aa75a
|
from revelation import authmanager, config, data, datahandler, dialog, entry, io, ui, util |
31 |
a207757f8451
|
|
32 |
a207757f8451
|
|
33 |
c9d89f3ff91d
|
|
34 |
c9d89f3ff91d
|
class Revelation(ui.App): |
35 |
c9d89f3ff91d
|
"The Revelation application" |
36 |
a207757f8451
|
|
37 |
a207757f8451
|
def __init__(self): |
38 |
a207757f8451
|
sys.excepthook = self.__cb_exception |
39 |
086abbb6fc3c
|
os.umask(0077) |
40 |
a207757f8451
|
|
41 |
59be49691b29
|
self.program = gnome.init( |
42 |
59be49691b29
|
config.APPNAME, config.APPNAME, |
43 |
59be49691b29
|
gnome.libgnome_module_info_get(), sys.argv, [] |
44 |
59be49691b29
|
) |
45 |
59be49691b29
|
|
46 |
5d99fc2aa75a
|
authmanager.gnome_authentication_manager_init() |
47 |
c9d89f3ff91d
|
ui.App.__init__(self, config.APPNAME) |
48 |
a207757f8451
|
|
49 |
c9d89f3ff91d
|
self.connect("delete-event", lambda w,d: True ^ self.quit()) |
50 |
a207757f8451
|
|
51 |
c9d89f3ff91d
|
try: |
52 |
c9d89f3ff91d
|
self.__init_facilities() |
53 |
c9d89f3ff91d
|
self.__init_ui() |
54 |
c9d89f3ff91d
|
self.__init_states() |
55 |
a207757f8451
|
|
56 |
c9d89f3ff91d
|
except IOError: |
57 |
c9d89f3ff91d
|
dialog.Error(self, "Missing data files", "Some of Revelations system files could not be found, please reinstall Revelation.").run() |
58 |
c9d89f3ff91d
|
sys.exit(1) |
59 |
c9d89f3ff91d
|
|
60 |
c9d89f3ff91d
|
except config.ConfigError: |
61 |
c9d89f3ff91d
|
dialog.Error(self, "Missing configuration data", "Revelation could not find its configuration data, please reinstall Revelation.").run() |
62 |
c9d89f3ff91d
|
sys.exit(1) |
63 |
c9d89f3ff91d
|
|
64 |
c9d89f3ff91d
|
except ui.DataError: |
65 |
c9d89f3ff91d
|
dialog.Error(self, "Invalid data files", "Some of Revelations system files contain invalid data, please reinstall Revelation.").run() |
66 |
c9d89f3ff91d
|
sys.exit(1) |
67 |
c9d89f3ff91d
|
|
68 |
c9d89f3ff91d
|
|
69 |
c9d89f3ff91d
|
def __init_facilities(self): |
70 |
c9d89f3ff91d
|
"Sets up various facilities" |
71 |
c9d89f3ff91d
|
|
72 |
c9d89f3ff91d
|
self.clipboard = data.Clipboard() |
73 |
c9d89f3ff91d
|
self.config = config.Config() |
74 |
c9d89f3ff91d
|
self.datafile = io.DataFile(datahandler.Revelation) |
75 |
c9d89f3ff91d
|
self.entryclipboard = data.EntryClipboard() |
76 |
c9d89f3ff91d
|
self.entrystore = data.EntryStore() |
77 |
c9d89f3ff91d
|
self.entrysearch = data.EntrySearch(self.entrystore) |
78 |
c9d89f3ff91d
|
self.items = ui.ItemFactory(self) |
79 |
59be49691b29
|
self.sessionclient = gnome.ui.master_client() |
80 |
c9d89f3ff91d
|
self.undoqueue = data.UndoQueue() |
81 |
c9d89f3ff91d
|
|
82 |
c9d89f3ff91d
|
self.datafile.connect("changed", lambda w,f: self.__state_file(f)) |
83 |
c9d89f3ff91d
|
self.entryclipboard.connect("content-toggled", lambda w,d: self.__state_clipboard(d)) |
84 |
59be49691b29
|
self.sessionclient.connect("die", lambda w: self.quit()) |
85 |
59be49691b29
|
self.sessionclient.connect("save-yourself", self.__cb_session_save) |
86 |
c9d89f3ff91d
|
self.undoqueue.connect("changed", lambda w: self.__state_undo(self.undoqueue.get_undo_action(), self.undoqueue.get_redo_action())) |
87 |
c9d89f3ff91d
|
|
88 |
c9d89f3ff91d
|
self.config.monitor("search/folders", lambda k,v,d: setattr(self.entrysearch, "folders", v)) |
89 |
c9d89f3ff91d
|
self.config.monitor("search/namedesc", lambda k,v,d: setattr(self.entrysearch, "namedesconly", v)) |
90 |
c9d89f3ff91d
|
self.config.monitor("search/casesens", lambda k,v,d: setattr(self.entrysearch, "casesensitive", v)) |
91 |
c9d89f3ff91d
|
|
92 |
c9d89f3ff91d
|
|
93 |
c9d89f3ff91d
|
def __init_states(self): |
94 |
c9d89f3ff91d
|
"Sets the initial application state" |
95 |
c9d89f3ff91d
|
|
96 |
c9d89f3ff91d
|
# set window states |
97 |
c9d89f3ff91d
|
self.set_default_size( |
98 |
c9d89f3ff91d
|
self.config.get("view/window-width"), |
99 |
c9d89f3ff91d
|
self.config.get("view/window-height") |
100 |
c9d89f3ff91d
|
) |
101 |
c9d89f3ff91d
|
|
102 |
c9d89f3ff91d
|
self.move( |
103 |
c9d89f3ff91d
|
self.config.get("view/window-position-x"), |
104 |
c9d89f3ff91d
|
self.config.get("view/window-position-y") |
105 |
c9d89f3ff91d
|
) |
106 |
c9d89f3ff91d
|
|
107 |
c9d89f3ff91d
|
self.hpaned.set_position( |
108 |
c9d89f3ff91d
|
self.config.get("view/pane-position") |
109 |
c9d89f3ff91d
|
) |
110 |
c9d89f3ff91d
|
|
111 |
c9d89f3ff91d
|
# bind ui widgets to config keys |
112 |
c9d89f3ff91d
|
bind = { |
113 |
c9d89f3ff91d
|
"view/passwords" : "/menubar/menu-view/view-passwords", |
114 |
c9d89f3ff91d
|
"view/searchbar" : "/menubar/menu-view/view-searchbar", |
115 |
c9d89f3ff91d
|
"view/statusbar" : "/menubar/menu-view/view-statusbar", |
116 |
c9d89f3ff91d
|
"view/toolbar" : "/menubar/menu-view/view-toolbar" |
117 |
c9d89f3ff91d
|
} |
118 |
c9d89f3ff91d
|
|
119 |
c9d89f3ff91d
|
for key, path in bind.items(): |
120 |
c9d89f3ff91d
|
ui.config_bind(self.config, key, self.uimanager.get_widget(path)) |
121 |
c9d89f3ff91d
|
|
122 |
c9d89f3ff91d
|
|
123 |
c9d89f3ff91d
|
self.entryview.display_info() |
124 |
c9d89f3ff91d
|
self.show_all() |
125 |
c9d89f3ff91d
|
|
126 |
c9d89f3ff91d
|
# set some variables |
127 |
c9d89f3ff91d
|
self.entrysearch.string = "" |
128 |
c9d89f3ff91d
|
self.entrysearch.type = None |
129 |
c9d89f3ff91d
|
|
130 |
c9d89f3ff91d
|
# set ui widget states |
131 |
c9d89f3ff91d
|
self.__state_clipboard(self.entryclipboard.has_contents()) |
132 |
c9d89f3ff91d
|
self.__state_entry([]) |
133 |
c9d89f3ff91d
|
self.__state_file(None) |
134 |
c9d89f3ff91d
|
self.__state_find(self.entrysearch.string) |
135 |
c9d89f3ff91d
|
self.__state_undo(None, None) |
136 |
c9d89f3ff91d
|
|
137 |
c9d89f3ff91d
|
# set states from config |
138 |
c9d89f3ff91d
|
self.config.monitor("view/searchbar", self.__cb_config_toolbar, self.searchbar) |
139 |
c9d89f3ff91d
|
self.config.monitor("view/statusbar", self.__cb_config_toolbar, self.statusbar) |
140 |
c9d89f3ff91d
|
self.config.monitor("view/toolbar", self.__cb_config_toolbar, self.toolbar) |
141 |
c9d89f3ff91d
|
|
142 |
c9d89f3ff91d
|
|
143 |
086abbb6fc3c
|
def __init_ui(self): |
144 |
c9d89f3ff91d
|
"Sets up the UI" |
145 |
a207757f8451
|
|
146 |
c9d89f3ff91d
|
# set window icons |
147 |
3dcf95408d8d
|
gtk.window_set_default_icon_list( |
148 |
3dcf95408d8d
|
self.items.load_icon("revelation", 48), |
149 |
620759d3032d
|
self.items.load_icon("revelation", 32), |
150 |
620759d3032d
|
self.items.load_icon("revelation", 24), |
151 |
3dcf95408d8d
|
self.items.load_icon("revelation", 16) |
152 |
3dcf95408d8d
|
) |
153 |
3dcf95408d8d
|
|
154 |
c9d89f3ff91d
|
# load UI definitions |
155 |
c9d89f3ff91d
|
self.uimanager.add_actions_from_file(config.DIR_UI + "/actions.xml") |
156 |
c9d89f3ff91d
|
self.uimanager.add_ui_from_file(config.DIR_UI + "/menubar.xml") |
157 |
c9d89f3ff91d
|
self.uimanager.add_ui_from_file(config.DIR_UI + "/popup-tree.xml") |
158 |
c9d89f3ff91d
|
self.uimanager.add_ui_from_file(config.DIR_UI + "/toolbar.xml") |
159 |
a207757f8451
|
|
160 |
c9d89f3ff91d
|
# set up callbacks for actions |
161 |
c9d89f3ff91d
|
callbacks = { |
162 |
4674492151c3
|
"clip-chain" : lambda w: self.clip_chain(self.entrystore.get_entry(self.tree.get_active())), |
163 |
4674492151c3
|
"clip-copy" : lambda w: self.clip_copy(self.tree.get_selected()), |
164 |
4674492151c3
|
"clip-cut" : lambda w: self.clip_cut(self.tree.get_selected()), |
165 |
4674492151c3
|
"clip-paste" : lambda w: self.clip_paste(self.entryclipboard.get(), self.tree.get_active()), |
166 |
4674492151c3
|
"entry-add" : lambda w: self.entry_add(None, self.tree.get_active()), |
167 |
4674492151c3
|
"entry-edit" : lambda w: self.entry_edit(self.tree.get_active()), |
168 |
095f4008dc7f
|
"entry-goto" : lambda w: self.entry_goto(self.tree.get_selected()), |
169 |
4674492151c3
|
"entry-remove" : lambda w: self.entry_remove(self.tree.get_selected()), |
170 |
c9d89f3ff91d
|
"file-change-password" : lambda w: self.file_change_password(), |
171 |
c9d89f3ff91d
|
"file-close" : lambda w: self.quit(), |
172 |
c9d89f3ff91d
|
"file-export" : lambda w: self.file_export(), |
173 |
c9d89f3ff91d
|
"file-import" : lambda w: self.file_import(), |
174 |
c9d89f3ff91d
|
"file-lock" : lambda w: self.file_lock(), |
175 |
c9d89f3ff91d
|
"file-new" : lambda w: self.file_new(), |
176 |
c9d89f3ff91d
|
"file-open" : lambda w: self.file_open(), |
177 |
c9d89f3ff91d
|
"file-save" : lambda w: self.file_save(self.datafile.get_file(), self.datafile.get_password()), |
178 |
c9d89f3ff91d
|
"file-save-as" : lambda w: self.file_save(None, None), |
179 |
c9d89f3ff91d
|
"find" : lambda w: self.entry_find(), |
180 |
c9d89f3ff91d
|
"find-next" : lambda w: self.__entry_find(self, self.entrysearch.string, self.entrysearch.type, data.SEARCH_NEXT), |
181 |
c9d89f3ff91d
|
"find-previous" : lambda w: self.__entry_find(self, self.entrysearch.string, self.entrysearch.type, data.SEARCH_PREVIOUS), |
182 |
c9d89f3ff91d
|
"help-about" : lambda w: dialog.About(self).run(), |
183 |
c9d89f3ff91d
|
"help-homepage" : lambda w: gnome.url_show(config.URL), |
184 |
c9d89f3ff91d
|
"prefs" : lambda w: dialog.Preferences(self, self.config).run(), |
185 |
c9d89f3ff91d
|
"pwgenerator" : lambda w: dialog.PasswordGenerator(self, self.config).run(), |
186 |
c9d89f3ff91d
|
"quit" : lambda w: self.quit(), |
187 |
c9d89f3ff91d
|
"redo" : lambda w: self.redo(), |
188 |
c9d89f3ff91d
|
"select-all" : lambda w: self.tree.select_all(), |
189 |
c9d89f3ff91d
|
"select-none" : lambda w: self.tree.unselect_all(), |
190 |
c9d89f3ff91d
|
"undo" : lambda w: self.undo() |
191 |
c9d89f3ff91d
|
} |
192 |
a207757f8451
|
|
193 |
c9d89f3ff91d
|
for id, callback in callbacks.items(): |
194 |
c9d89f3ff91d
|
action = self.uimanager.get_action(id) |
195 |
c9d89f3ff91d
|
action.connect("activate", callback) |
196 |
a207757f8451
|
|
197 |
c9d89f3ff91d
|
# set up toolbar and menus |
198 |
c9d89f3ff91d
|
self.set_menus(self.uimanager.get_widget("/menubar")) |
199 |
a207757f8451
|
|
200 |
c9d89f3ff91d
|
self.toolbar = self.uimanager.get_widget("/toolbar") |
201 |
c9d89f3ff91d
|
self.set_toolbar(self.toolbar) |
202 |
a207757f8451
|
|
203 |
47caac1545f2
|
try: |
204 |
47caac1545f2
|
detachable = self.config.get("/desktop/gnome/interface/toolbar_detachable") |
205 |
47caac1545f2
|
|
206 |
47caac1545f2
|
except config.ConfigError: |
207 |
47caac1545f2
|
detachable = False |
208 |
47caac1545f2
|
|
209 |
c9d89f3ff91d
|
self.searchbar = ui.Searchbar() |
210 |
47caac1545f2
|
self.add_toolbar(self.searchbar, "searchbar", 2, detachable) |
211 |
a207757f8451
|
|
212 |
c9d89f3ff91d
|
# set up main application widgets |
213 |
c9d89f3ff91d
|
self.tree = ui.EntryTree(self.entrystore) |
214 |
c9d89f3ff91d
|
self.scrolledwindow = ui.ScrolledWindow(self.tree) |
215 |
a207757f8451
|
|
216 |
c9d89f3ff91d
|
self.entryview = ui.EntryView(self.config) |
217 |
c9d89f3ff91d
|
alignment = gtk.Alignment(0.5, 0.4, 0, 0) |
218 |
c9d89f3ff91d
|
alignment.add(self.entryview) |
219 |
a7c01d3b4b22
|
|
220 |
c9d89f3ff91d
|
self.hpaned = ui.HPaned(self.scrolledwindow, alignment) |
221 |
c9d89f3ff91d
|
self.set_contents(self.hpaned) |
222 |
c9d89f3ff91d
|
|
223 |
8077271f872d
|
# set up drag-and-drop |
224 |
f5f9aadf2e19
|
self.drag_dest_set(gtk.DEST_DEFAULT_ALL, ( ( "text/uri-list", 0, 0 ), ), gtk.gdk.ACTION_COPY | gtk.gdk.ACTION_MOVE | gtk.gdk.ACTION_LINK ) |
225 |
f5f9aadf2e19
|
self.connect("drag_data_received", self.__cb_drag_dest) |
226 |
f5f9aadf2e19
|
|
227 |
8077271f872d
|
self.tree.enable_model_drag_source(gtk.gdk.BUTTON1_MASK, ( ( "revelation/treerow", gtk.TARGET_SAME_APP | gtk.TARGET_SAME_WIDGET, 0), ), gtk.gdk.ACTION_MOVE) |
228 |
8077271f872d
|
self.tree.enable_model_drag_dest(( ( "revelation/treerow", gtk.TARGET_SAME_APP | gtk.TARGET_SAME_WIDGET, 0), ), gtk.gdk.ACTION_MOVE) |
229 |
8077271f872d
|
self.tree.connect("drag_data_received", self.__cb_tree_drag_received) |
230 |
8077271f872d
|
|
231 |
c9d89f3ff91d
|
# set up callbacks |
232 |
c9d89f3ff91d
|
self.searchbar.button.connect("clicked", lambda w: self.__entry_find(self, self.searchbar.entry.get_text(), None)) |
233 |
c9d89f3ff91d
|
|
234 |
c9d89f3ff91d
|
self.tree.connect("popup", lambda w,d: self.popup(self.uimanager.get_widget("/popup-tree"), d.button, d.time)) |
235 |
6cf6849eed72
|
self.tree.connect("doubleclick", self.__cb_tree_doubleclick) |
236 |
c9d89f3ff91d
|
self.tree.connect("key-press-event", self.__cb_tree_keypress) |
237 |
c9d89f3ff91d
|
self.tree.selection.connect("changed", lambda w: self.entryview.display_entry(self.entrystore.get_entry(self.tree.get_active()))) |
238 |
c9d89f3ff91d
|
self.tree.selection.connect("changed", lambda w: self.__state_entry(self.tree.get_selected())) |
239 |
c9d89f3ff91d
|
|
240 |
c9d89f3ff91d
|
|
241 |
c9d89f3ff91d
|
|
242 |
c9d89f3ff91d
|
##### STATE HANDLERS ##### |
243 |
c9d89f3ff91d
|
|
244 |
59be49691b29
|
def __save_state(self): |
245 |
59be49691b29
|
"Saves the current application state" |
246 |
59be49691b29
|
|
247 |
59be49691b29
|
width, height = self.get_size() |
248 |
59be49691b29
|
self.config.set("view/window-width", width) |
249 |
59be49691b29
|
self.config.set("view/window-height", height) |
250 |
59be49691b29
|
|
251 |
59be49691b29
|
x, y = self.get_position() |
252 |
59be49691b29
|
self.config.set("view/window-position-x", x) |
253 |
59be49691b29
|
self.config.set("view/window-position-y", y) |
254 |
59be49691b29
|
|
255 |
59be49691b29
|
self.config.set("view/pane-position", self.hpaned.get_position()) |
256 |
59be49691b29
|
|
257 |
59be49691b29
|
|
258 |
c9d89f3ff91d
|
def __state_clipboard(self, has_contents): |
259 |
c9d89f3ff91d
|
"Sets states based on the clipboard contents" |
260 |
c9d89f3ff91d
|
|
261 |
c9d89f3ff91d
|
self.uimanager.get_action("clip-paste").set_property("sensitive", has_contents) |
262 |
c9d89f3ff91d
|
|
263 |
c9d89f3ff91d
|
|
264 |
c9d89f3ff91d
|
def __state_entry(self, iters): |
265 |
c9d89f3ff91d
|
"Sets states for entry-dependant ui items" |
266 |
c9d89f3ff91d
|
|
267 |
c9d89f3ff91d
|
# widget sensitivity based on number of entries |
268 |
c9d89f3ff91d
|
self.uimanager.get_action_group("entry-multiple").set_sensitive(len(iters) > 0) |
269 |
c9d89f3ff91d
|
self.uimanager.get_action_group("entry-single").set_sensitive(len(iters) == 1) |
270 |
c9d89f3ff91d
|
self.uimanager.get_action_group("entry-optional").set_sensitive(len(iters) < 2) |
271 |
c9d89f3ff91d
|
|
272 |
095f4008dc7f
|
# goto sensitivity |
273 |
c9d89f3ff91d
|
try: |
274 |
c9d89f3ff91d
|
for iter in iters: |
275 |
c9d89f3ff91d
|
e = self.entrystore.get_entry(iter) |
276 |
c9d89f3ff91d
|
|
277 |
c9d89f3ff91d
|
if self.config.get("launcher/%s" % e.id) not in ( "", None ): |
278 |
c9d89f3ff91d
|
s = True |
279 |
c9d89f3ff91d
|
break |
280 |
c9d89f3ff91d
|
|
281 |
c9d89f3ff91d
|
else: |
282 |
c9d89f3ff91d
|
s = False |
283 |
c9d89f3ff91d
|
|
284 |
c9d89f3ff91d
|
except config.ConfigError: |
285 |
c9d89f3ff91d
|
s = False |
286 |
c9d89f3ff91d
|
|
287 |
095f4008dc7f
|
self.uimanager.get_action_group("entry-goto").set_sensitive(s) |
288 |
dad9b7b301d1
|
|
289 |
dad9b7b301d1
|
# dynamic menu items for field copying |
290 |
dad9b7b301d1
|
for menupath in ( "/popup-tree", "/menubar/menu-edit"): |
291 |
dad9b7b301d1
|
menu = self.uimanager.get_widget(menupath) |
292 |
dad9b7b301d1
|
|
293 |
dad9b7b301d1
|
if menupath == "/menubar/menu-edit": |
294 |
dad9b7b301d1
|
menu = menu.get_submenu() |
295 |
dad9b7b301d1
|
|
296 |
dad9b7b301d1
|
# scan for copy action, and remove dynamic items |
297 |
dad9b7b301d1
|
children = menu.get_children() |
298 |
b57efdd083f5
|
index_chain = children.index(self.uimanager.get_widget("%s/clip-chain" % menupath)) |
299 |
dad9b7b301d1
|
index_paste = children.index(self.uimanager.get_widget("%s/clip-paste" % menupath)) |
300 |
dad9b7b301d1
|
|
301 |
b57efdd083f5
|
for i in range(index_chain + 1, index_paste): |
302 |
dad9b7b301d1
|
children[i].destroy() |
303 |
dad9b7b301d1
|
|
304 |
b57efdd083f5
|
# fetch entry data |
305 |
dad9b7b301d1
|
if len(iters) != 1: |
306 |
dad9b7b301d1
|
continue |
307 |
dad9b7b301d1
|
|
308 |
dad9b7b301d1
|
e = self.entrystore.get_entry(iters[0]) |
309 |
b57efdd083f5
|
secretfields = [ field for field in e.fields if (type(field) == entry.UsernameField or field.datatype == entry.DATATYPE_PASSWORD) and field.value != "" ] |
310 |
dad9b7b301d1
|
|
311 |
b57efdd083f5
|
# create menuitems |
312 |
b57efdd083f5
|
i = index_chain + 1 |
313 |
b57efdd083f5
|
for field in secretfields: |
314 |
dad9b7b301d1
|
menuitem = ui.ImageMenuItem(gtk.STOCK_COPY, "Copy %s" % field.name) |
315 |
dad9b7b301d1
|
menuitem.tooltip = "Copy the %s to the clipboard" % field.name.lower() |
316 |
dad9b7b301d1
|
menuitem.connect("activate", self.__cb_clip_set, field.value) |
317 |
dad9b7b301d1
|
menuitem.connect("select", self.cb_menudesc, True) |
318 |
dad9b7b301d1
|
menuitem.connect("deselect", self.cb_menudesc, False) |
319 |
dad9b7b301d1
|
menu.insert(menuitem, i) |
320 |
dad9b7b301d1
|
|
321 |
dad9b7b301d1
|
i += 1 |
322 |
dad9b7b301d1
|
|
323 |
dad9b7b301d1
|
menu.show_all() |
324 |
dad9b7b301d1
|
|
325 |
a207757f8451
|
|
326 |
c9d89f3ff91d
|
def __state_file(self, file): |
327 |
c9d89f3ff91d
|
"Sets states based on file" |
328 |
a207757f8451
|
|
329 |
c9d89f3ff91d
|
self.uimanager.get_action_group("file-exists").set_sensitive(file is not None) |
330 |
a207757f8451
|
|
331 |
c9d89f3ff91d
|
if file is not None: |
332 |
c9d89f3ff91d
|
self.set_title(os.path.basename(file)) |
333 |
a207757f8451
|
|
334 |
c9d89f3ff91d
|
if io.file_is_local(file): |
335 |
c9d89f3ff91d
|
os.chdir(os.path.dirname(file)) |
336 |
a7c01d3b4b22
|
|
337 |
c9d89f3ff91d
|
else: |
338 |
c9d89f3ff91d
|
self.set_title("[New file]") |
339 |
a207757f8451
|
|
340 |
086abbb6fc3c
|
|
341 |
c9d89f3ff91d
|
def __state_find(self, string): |
342 |
c9d89f3ff91d
|
"Sets states based on the current search string" |
343 |
086abbb6fc3c
|
|
344 |
c9d89f3ff91d
|
self.uimanager.get_action_group("find").set_sensitive(string != "") |
345 |
086abbb6fc3c
|
|
346 |
086abbb6fc3c
|
|
347 |
c9d89f3ff91d
|
def __state_undo(self, undoaction, redoaction): |
348 |
c9d89f3ff91d
|
"Sets states based on undoqueue actions" |
349 |
086abbb6fc3c
|
|
350 |
c9d89f3ff91d
|
if undoaction is None: |
351 |
c9d89f3ff91d
|
s, l = False, "_Undo" |
352 |
086abbb6fc3c
|
|
353 |
c9d89f3ff91d
|
else: |
354 |
c9d89f3ff91d
|
s, l = True, "_Undo %s" % undoaction[1].lower() |
355 |
086abbb6fc3c
|
|
356 |
c9d89f3ff91d
|
action = self.uimanager.get_action("undo") |
357 |
c9d89f3ff91d
|
action.set_property("sensitive", s) |
358 |
c9d89f3ff91d
|
action.set_property("label", l) |
359 |
a207757f8451
|
|
360 |
a207757f8451
|
|
361 |
c9d89f3ff91d
|
if redoaction is None: |
362 |
c9d89f3ff91d
|
s, l = False, "_Redo" |
363 |
a207757f8451
|
|
364 |
c9d89f3ff91d
|
else: |
365 |
c9d89f3ff91d
|
s, l = True, "_Redo %s" % redoaction[1].lower() |
366 |
a207757f8451
|
|
367 |
c9d89f3ff91d
|
action = self.uimanager.get_action("redo") |
368 |
c9d89f3ff91d
|
action.set_property("sensitive", s) |
369 |
c9d89f3ff91d
|
action.set_property("label", l) |
370 |
a207757f8451
|
|
371 |
a207757f8451
|
|
372 |
a207757f8451
|
|
373 |
a207757f8451
|
|
374 |
c9d89f3ff91d
|
##### MISC CALLBACKS ##### |
375 |
086abbb6fc3c
|
|
376 |
dad9b7b301d1
|
def __cb_clip_set(self, widget, data): |
377 |
dad9b7b301d1
|
"Sets data on the clipboars" |
378 |
dad9b7b301d1
|
|
379 |
dad9b7b301d1
|
self.clipboard.set(data) |
380 |
dad9b7b301d1
|
|
381 |
dad9b7b301d1
|
|
382 |
f5f9aadf2e19
|
def __cb_drag_dest(self, widget, context, x, y, seldata, info, time, userdata = None): |
383 |
f5f9aadf2e19
|
"Handles file drops" |
384 |
f5f9aadf2e19
|
|
385 |
f5f9aadf2e19
|
if seldata.data is None: |
386 |
f5f9aadf2e19
|
return |
387 |
f5f9aadf2e19
|
|
388 |
f5f9aadf2e19
|
files = [ file.strip() for file in seldata.data.split("\n") if file.strip() != "" ] |
389 |
f5f9aadf2e19
|
|
390 |
b57efdd083f5
|
if len(files) > 0: |
391 |
b57efdd083f5
|
self.file_open(files[0]) |
392 |
f5f9aadf2e19
|
|
393 |
f5f9aadf2e19
|
|
394 |
a207757f8451
|
def __cb_exception(self, type, value, trace): |
395 |
a207757f8451
|
"Callback for unhandled exceptions" |
396 |
a207757f8451
|
|
397 |
9e161d94bffc
|
if type == KeyboardInterrupt: |
398 |
9e161d94bffc
|
sys.exit(1) |
399 |
9e161d94bffc
|
|
400 |
c9d89f3ff91d
|
traceback = util.trace_exception(type, value, trace) |
401 |
a207757f8451
|
sys.stderr.write(traceback) |
402 |
4db37324fd05
|
|
403 |
c9d89f3ff91d
|
if dialog.Exception(self, traceback).run() == True: |
404 |
c9d89f3ff91d
|
gtk.main() |
405 |
4db37324fd05
|
|
406 |
4db37324fd05
|
else: |
407 |
4db37324fd05
|
sys.exit(1) |
408 |
a207757f8451
|
|
409 |
a207757f8451
|
|
410 |
59be49691b29
|
def __cb_session_save(self, widget, phase, what, end, interaction, fast, data = None): |
411 |
59be49691b29
|
"Handles session saves" |
412 |
59be49691b29
|
|
413 |
59be49691b29
|
self.sessionclient.set_current_directory(os.getcwd()) |
414 |
59be49691b29
|
self.sessionclient.set_clone_command(sys.argv[0]) |
415 |
59be49691b29
|
|
416 |
59be49691b29
|
if self.datafile.get_file() == None: |
417 |
59be49691b29
|
self.sessionclient.set_restart_command(1, [ sys.argv[0] ]) |
418 |
59be49691b29
|
|
419 |
59be49691b29
|
else: |
420 |
59be49691b29
|
self.sessionclient.set_restart_command(2, [ sys.argv[0], self.datafile.get_file() ] ) |
421 |
59be49691b29
|
|
422 |
59be49691b29
|
self.__save_state() |
423 |
59be49691b29
|
|
424 |
59be49691b29
|
return True |
425 |
59be49691b29
|
|
426 |
59be49691b29
|
|
427 |
6cf6849eed72
|
def __cb_tree_doubleclick(self, widget, data = None): |
428 |
6cf6849eed72
|
"Handles doubleclicks on the tree" |
429 |
6cf6849eed72
|
|
430 |
095f4008dc7f
|
self.entry_goto(self.tree.get_selected()) |
431 |
6cf6849eed72
|
|
432 |
6cf6849eed72
|
|
433 |
8077271f872d
|
def __cb_tree_drag_received(self, tree, context, x, y, seldata, info, time): |
434 |
8077271f872d
|
"Callback for drag drops on the treeview" |
435 |
8077271f872d
|
|
436 |
8077271f872d
|
# get source and destination data |
437 |
8077271f872d
|
sourceiters = self.entrystore.filter_parents(self.tree.get_selected()) |
438 |
8077271f872d
|
destrow = self.tree.get_dest_row_at_pos(x, y) |
439 |
8077271f872d
|
|
440 |
8077271f872d
|
if destrow is None: |
441 |
8077271f872d
|
destpath = ( self.entrystore.iter_n_children(None) - 1, ) |
442 |
8077271f872d
|
pos = gtk.TREE_VIEW_DROP_AFTER |
443 |
8077271f872d
|
|
444 |
8077271f872d
|
else: |
445 |
8077271f872d
|
destpath, pos = destrow |
446 |
8077271f872d
|
|
447 |
8077271f872d
|
destiter = self.entrystore.get_iter(destpath) |
448 |
9e1db933e6ed
|
destpath = self.entrystore.get_path(destiter) |
449 |
8077271f872d
|
|
450 |
9e1db933e6ed
|
# avoid drops to current iter or descentants |
451 |
8077271f872d
|
for sourceiter in sourceiters: |
452 |
9e1db933e6ed
|
sourcepath = self.entrystore.get_path(sourceiter) |
453 |
9e1db933e6ed
|
|
454 |
9e1db933e6ed
|
if self.entrystore.is_ancestor(sourceiter, destiter) == True or sourcepath == destpath: |
455 |
8077271f872d
|
context.finish(False, False, time) |
456 |
8077271f872d
|
return |
457 |
8077271f872d
|
|
458 |
9e1db933e6ed
|
elif pos in ( gtk.TREE_VIEW_DROP_INTO_OR_BEFORE, gtk.TREE_VIEW_DROP_BEFORE ) and sourcepath[:-1] == destpath[:-1] and sourcepath[-1] == destpath[-1] - 1: |
459 |
9e1db933e6ed
|
context.finish(False, False, time) |
460 |
9e1db933e6ed
|
return |
461 |
9e1db933e6ed
|
|
462 |
9e1db933e6ed
|
elif pos in ( gtk.TREE_VIEW_DROP_INTO_OR_AFTER, gtk.TREE_VIEW_DROP_AFTER ) and sourcepath[:-1] == destpath[:-1] and sourcepath[-1] == destpath[-1] + 1: |
463 |
9e1db933e6ed
|
context.finish(False, False, time) |
464 |
9e1db933e6ed
|
return |
465 |
9e1db933e6ed
|
|
466 |
9e1db933e6ed
|
|
467 |
8077271f872d
|
# move the entries |
468 |
8077271f872d
|
if pos in ( gtk.TREE_VIEW_DROP_INTO_OR_BEFORE, gtk.TREE_VIEW_DROP_INTO_OR_AFTER): |
469 |
8077271f872d
|
parent = destiter |
470 |
8077271f872d
|
sibling = None |
471 |
8077271f872d
|
|
472 |
8077271f872d
|
elif pos == gtk.TREE_VIEW_DROP_BEFORE: |
473 |
8077271f872d
|
parent = self.entrystore.iter_parent(destiter) |
474 |
8077271f872d
|
sibling = destiter |
475 |
8077271f872d
|
|
476 |
8077271f872d
|
elif pos == gtk.TREE_VIEW_DROP_AFTER: |
477 |
8077271f872d
|
parent = self.entrystore.iter_parent(destiter) |
478 |
8077271f872d
|
|
479 |
8077271f872d
|
sibpath = list(destpath) |
480 |
8077271f872d
|
sibpath[-1] += 1 |
481 |
8077271f872d
|
sibling = self.entrystore.get_iter(sibpath) |
482 |
8077271f872d
|
|
483 |
8077271f872d
|
self.entry_move(sourceiters, parent, sibling) |
484 |
8077271f872d
|
context.finish(True, True, time) |
485 |
8077271f872d
|
|
486 |
8077271f872d
|
|
487 |
c9d89f3ff91d
|
def __cb_tree_keypress(self, widget, data = None): |
488 |
a207757f8451
|
"Handles key presses for the tree" |
489 |
a207757f8451
|
|
490 |
a207757f8451
|
# return |
491 |
a207757f8451
|
if data.keyval == 65293: |
492 |
a207757f8451
|
self.entry_edit() |
493 |
a207757f8451
|
|
494 |
a207757f8451
|
# insert |
495 |
a207757f8451
|
elif data.keyval == 65379: |
496 |
a207757f8451
|
self.entry_add() |
497 |
a207757f8451
|
|
498 |
a207757f8451
|
# delete |
499 |
a207757f8451
|
elif data.keyval == 65535: |
500 |
a207757f8451
|
self.entry_remove() |
501 |
a207757f8451
|
|
502 |
a207757f8451
|
|
503 |
a207757f8451
|
|
504 |
c9d89f3ff91d
|
##### CONFIG CALLBACKS ##### |
505 |
c9d89f3ff91d
|
|
506 |
c9d89f3ff91d
|
def __cb_config_toolbar(self, config, value, toolbar): |
507 |
c9d89f3ff91d
|
"Config callback for showing toolbars" |
508 |
c9d89f3ff91d
|
|
509 |
c9d89f3ff91d
|
if value == True: |
510 |
c9d89f3ff91d
|
toolbar.show() |
511 |
c9d89f3ff91d
|
|
512 |
c9d89f3ff91d
|
else: |
513 |
c9d89f3ff91d
|
toolbar.hide() |
514 |
c9d89f3ff91d
|
|
515 |
c9d89f3ff91d
|
|
516 |
c9d89f3ff91d
|
|
517 |
c9d89f3ff91d
|
#### UNDO / REDO CALLBACKS ##### |
518 |
c9d89f3ff91d
|
|
519 |
c9d89f3ff91d
|
def __cb_redo_add(self, name, actiondata): |
520 |
c9d89f3ff91d
|
"Redoes an add action" |
521 |
c9d89f3ff91d
|
|
522 |
c9d89f3ff91d
|
path, e = actiondata |
523 |
c9d89f3ff91d
|
parent = self.entrystore.get_iter(path[:-1]) |
524 |
c9d89f3ff91d
|
sibling = self.entrystore.get_iter(path) |
525 |
c9d89f3ff91d
|
|
526 |
c9d89f3ff91d
|
iter = self.entrystore.add_entry(e, parent, sibling) |
527 |
c9d89f3ff91d
|
self.tree.select(iter) |
528 |
c9d89f3ff91d
|
|
529 |
c9d89f3ff91d
|
|
530 |
c9d89f3ff91d
|
def __cb_redo_edit(self, name, actiondata): |
531 |
c9d89f3ff91d
|
"Redoes an edit action" |
532 |
c9d89f3ff91d
|
|
533 |
c9d89f3ff91d
|
path, preentry, postentry = actiondata |
534 |
c9d89f3ff91d
|
iter = self.entrystore.get_iter(path) |
535 |
c9d89f3ff91d
|
|
536 |
c9d89f3ff91d
|
self.entrystore.update_entry(iter, postdata) |
537 |
c9d89f3ff91d
|
self.tree.select(iter) |
538 |
c9d89f3ff91d
|
|
539 |
c9d89f3ff91d
|
|
540 |
c9d89f3ff91d
|
def __cb_redo_import(self, name, actiondata): |
541 |
c9d89f3ff91d
|
"Redoes an import action" |
542 |
c9d89f3ff91d
|
|
543 |
c9d89f3ff91d
|
paths, entrystore = actiondata |
544 |
c9d89f3ff91d
|
self.entrystore.import_entry(entrystore, None) |
545 |
c9d89f3ff91d
|
|
546 |
c9d89f3ff91d
|
|
547 |
8077271f872d
|
def __cb_redo_move(self, name, actiondata): |
548 |
8077271f872d
|
"Redoes a move action" |
549 |
8077271f872d
|
|
550 |
8077271f872d
|
newiters = [] |
551 |
8077271f872d
|
|
552 |
8077271f872d
|
for prepath, postpath in actiondata: |
553 |
8077271f872d
|
prepath, postpath = list(prepath), list(postpath) |
554 |
8077271f872d
|
|
555 |
8077271f872d
|
# adjust path if necessary |
556 |
8077271f872d
|
if postpath[:len(prepath) - 1] == prepath[:-1]: |
557 |
8077271f872d
|
if prepath[-1] <= postpath[len(prepath) - 1]: |
558 |
8077271f872d
|
postpath[len(prepath) - 1] += 1 |
559 |
8077271f872d
|
|
560 |
8077271f872d
|
newiter = self.entrystore.move_entry( |
561 |
8077271f872d
|
self.entrystore.get_iter(prepath), |
562 |
8077271f872d
|
self.entrystore.get_iter(postpath[:-1]), |
563 |
8077271f872d
|
self.entrystore.get_iter(postpath) |
564 |
8077271f872d
|
) |
565 |
8077271f872d
|
|
566 |
8077271f872d
|
newiters.append(newiter) |
567 |
8077271f872d
|
|
568 |
8077271f872d
|
if len(newiters) > 0: |
569 |
8077271f872d
|
self.tree.select(newiters[0]) |
570 |
8077271f872d
|
|
571 |
8077271f872d
|
|
572 |
c9d89f3ff91d
|
def __cb_redo_paste(self, name, actiondata): |
573 |
c9d89f3ff91d
|
"Redoes a paste action" |
574 |
c9d89f3ff91d
|
|
575 |
c9d89f3ff91d
|
entrystore, parentpath, paths = actiondata |
576 |
c9d89f3ff91d
|
iters = self.entrystore.import_entry(entrystore, None, self.entrystore.get_iter(parentpath)) |
577 |
c9d89f3ff91d
|
|
578 |
c9d89f3ff91d
|
if len(iters) > 0: |
579 |
c9d89f3ff91d
|
self.tree.select(iters[0]) |
580 |
c9d89f3ff91d
|
|
581 |
c9d89f3ff91d
|
|
582 |
c9d89f3ff91d
|
def __cb_redo_remove(self, name, actiondata): |
583 |
c9d89f3ff91d
|
"Redoes a remove action" |
584 |
c9d89f3ff91d
|
|
585 |
c9d89f3ff91d
|
iters = [] |
586 |
c9d89f3ff91d
|
for path, entrystore in actiondata: |
587 |
c9d89f3ff91d
|
iters.append(self.entrystore.get_iter(path)) |
588 |
c9d89f3ff91d
|
|
589 |
c9d89f3ff91d
|
for iter in iters: |
590 |
c9d89f3ff91d
|
self.entrystore.remove_entry(iter) |
591 |
c9d89f3ff91d
|
|
592 |
c9d89f3ff91d
|
self.tree.unselect_all() |
593 |
c9d89f3ff91d
|
|
594 |
c9d89f3ff91d
|
|
595 |
c9d89f3ff91d
|
def __cb_undo_add(self, name, actiondata): |
596 |
c9d89f3ff91d
|
"Undoes an add action" |
597 |
c9d89f3ff91d
|
|
598 |
c9d89f3ff91d
|
path, e = actiondata |
599 |
c9d89f3ff91d
|
|
600 |
c9d89f3ff91d
|
self.entrystore.remove_entry(self.entrystore.get_iter(path)) |
601 |
c9d89f3ff91d
|
self.tree.unselect_all() |
602 |
c9d89f3ff91d
|
|
603 |
c9d89f3ff91d
|
|
604 |
c9d89f3ff91d
|
def __cb_undo_edit(self, name, actiondata): |
605 |
c9d89f3ff91d
|
"Undoes an edit action" |
606 |
c9d89f3ff91d
|
|
607 |
c9d89f3ff91d
|
path, preentry, postentry = actiondata |
608 |
c9d89f3ff91d
|
iter = self.entrystore.get_iter(path) |
609 |
c9d89f3ff91d
|
|
610 |
c9d89f3ff91d
|
self.entrystore.update_entry(iter, postentry) |
611 |
c9d89f3ff91d
|
self.tree.select(iter) |
612 |
c9d89f3ff91d
|
|
613 |
c9d89f3ff91d
|
|
614 |
c9d89f3ff91d
|
def __cb_undo_import(self, name, actiondata): |
615 |
c9d89f3ff91d
|
"Undoes an import action" |
616 |
c9d89f3ff91d
|
|
617 |
c9d89f3ff91d
|
paths, entrystore = actiondata |
618 |
c9d89f3ff91d
|
iters = [ self.entrystore.get_iter(path) for path in paths ] |
619 |
c9d89f3ff91d
|
|
620 |
c9d89f3ff91d
|
for iter in iters: |
621 |
c9d89f3ff91d
|
self.entrystore.remove_entry(iter) |
622 |
c9d89f3ff91d
|
|
623 |
c9d89f3ff91d
|
self.tree.unselect_all() |
624 |
c9d89f3ff91d
|
|
625 |
c9d89f3ff91d
|
|
626 |
8077271f872d
|
def __cb_undo_move(self, name, actiondata): |
627 |
8077271f872d
|
"Undoes a move action" |
628 |
8077271f872d
|
|
629 |
6bb7821fddaf
|
actiondata = actiondata[:] |
630 |
6bb7821fddaf
|
actiondata.reverse() |
631 |
6bb7821fddaf
|
|
632 |
8077271f872d
|
newiters = [] |
633 |
8077271f872d
|
|
634 |
8077271f872d
|
for prepath, postpath in actiondata: |
635 |
8077271f872d
|
prepath, postpath = list(prepath), list(postpath) |
636 |
8077271f872d
|
|
637 |
8077271f872d
|
# adjust path if necessary |
638 |
8077271f872d
|
if postpath[:-1] == prepath[:-1]: |
639 |
8077271f872d
|
if prepath[-1] > postpath[len(prepath) - 1]: |
640 |
8077271f872d
|
prepath[-1] += 1 |
641 |
8077271f872d
|
|
642 |
8077271f872d
|
newiter = self.entrystore.move_entry( |
643 |
8077271f872d
|
self.entrystore.get_iter(postpath), |
644 |
8077271f872d
|
self.entrystore.get_iter(prepath[:-1]), |
645 |
8077271f872d
|
self.entrystore.get_iter(prepath) |
646 |
8077271f872d
|
) |
647 |
8077271f872d
|
|
648 |
8077271f872d
|
newiters.append(newiter) |
649 |
8077271f872d
|
|
650 |
8077271f872d
|
if len(newiters) > 0: |
651 |
6bb7821fddaf
|
self.tree.select(newiters[-1]) |
652 |
8077271f872d
|
|
653 |
8077271f872d
|
|
654 |
c9d89f3ff91d
|
def __cb_undo_paste(self, name, actiondata): |
655 |
c9d89f3ff91d
|
"Undoes a paste action" |
656 |
c9d89f3ff91d
|
|
657 |
c9d89f3ff91d
|
entrystore, parentpath, paths = actiondata |
658 |
c9d89f3ff91d
|
iters = [ self.entrystore.get_iter(path) for path in paths ] |
659 |
c9d89f3ff91d
|
|
660 |
c9d89f3ff91d
|
for iter in iters: |
661 |
c9d89f3ff91d
|
self.entrystore.remove_entry(iter) |
662 |
c9d89f3ff91d
|
|
663 |
c9d89f3ff91d
|
self.tree.unselect_all() |
664 |
c9d89f3ff91d
|
|
665 |
c9d89f3ff91d
|
|
666 |
c9d89f3ff91d
|
def __cb_undo_remove(self, name, actiondata): |
667 |
c9d89f3ff91d
|
"Undoes a remove action" |
668 |
c9d89f3ff91d
|
|
669 |
c9d89f3ff91d
|
iters = [] |
670 |
c9d89f3ff91d
|
for path, entrystore in actiondata: |
671 |
c9d89f3ff91d
|
parent = self.entrystore.get_iter(path[:-1]) |
672 |
c9d89f3ff91d
|
sibling = self.entrystore.get_iter(path) |
673 |
c9d89f3ff91d
|
|
674 |
c9d89f3ff91d
|
iter = self.entrystore.import_entry(entrystore, entrystore.iter_nth_child(None, 0), parent, sibling) |
675 |
c9d89f3ff91d
|
iters.append(iter) |
676 |
c9d89f3ff91d
|
|
677 |
c9d89f3ff91d
|
self.tree.select(iters[0]) |
678 |
c9d89f3ff91d
|
|
679 |
c9d89f3ff91d
|
|
680 |
c9d89f3ff91d
|
|
681 |
c9d89f3ff91d
|
##### PRIVATE METHODS ##### |
682 |
c9d89f3ff91d
|
|
683 |
c9d89f3ff91d
|
def __entry_find(self, parent, string, entrytype, direction = data.SEARCH_NEXT): |
684 |
a207757f8451
|
"Searches for an entry" |
685 |
a207757f8451
|
|
686 |
c9d89f3ff91d
|
match = self.entrysearch.find(string, entrytype, self.tree.get_active(), direction) |
687 |
a207757f8451
|
|
688 |
c9d89f3ff91d
|
self.entrysearch.string = string |
689 |
c9d89f3ff91d
|
self.entrysearch.type = entrytype |
690 |
5ff135ae2b70
|
|
691 |
c9d89f3ff91d
|
self.__state_find(string) |
692 |
a207757f8451
|
|
693 |
c9d89f3ff91d
|
if match is not None: |
694 |
c9d89f3ff91d
|
self.tree.select(match) |
695 |
a207757f8451
|
|
696 |
a207757f8451
|
else: |
697 |
c9d89f3ff91d
|
dialog.Error(parent, "No match found", "The string you searched for did not match any entries. Try searching for a different phrase.").run() |
698 |
a207757f8451
|
|
699 |
a207757f8451
|
|
700 |
a207757f8451
|
def __file_autosave(self): |
701 |
c9d89f3ff91d
|
"Autosaves the current file if needed" |
702 |
a207757f8451
|
|
703 |
c9d89f3ff91d
|
if self.datafile.get_file() is None or self.datafile.get_password() is None: |
704 |
a207757f8451
|
return |
705 |
a207757f8451
|
|
706 |
c9d89f3ff91d
|
if self.config.get("file/autosave") == False: |
707 |
a207757f8451
|
return |
708 |
a207757f8451
|
|
709 |
c9d89f3ff91d
|
self.__file_save(self.datafile.get_file(), self.datafile.get_password()) |
710 |
c9d89f3ff91d
|
self.entrystore.changed = False |
711 |
a207757f8451
|
|
712 |
a207757f8451
|
|
713 |
9e161d94bffc
|
def __file_load(self, file, password, datafile = None): |
714 |
c9d89f3ff91d
|
"Loads data from a data file into an entrystore" |
715 |
9e161d94bffc
|
|
716 |
a207757f8451
|
try: |
717 |
c9d89f3ff91d
|
if datafile is None: |
718 |
c9d89f3ff91d
|
datafile = self.datafile |
719 |
c9d89f3ff91d
|
|
720 |
9e161d94bffc
|
while 1: |
721 |
9e161d94bffc
|
try: |
722 |
c9d89f3ff91d
|
return datafile.load(file, password, lambda: dialog.PasswordOpen(self, os.path.basename(file)).run()) |
723 |
a207757f8451
|
|
724 |
c9d89f3ff91d
|
except datahandler.PasswordError: |
725 |
c9d89f3ff91d
|
dialog.Error(self, "Incorrect password", "The password you entered for the file'%s' was not correct." % file).run() |
726 |
a207757f8451
|
|
727 |
c9d89f3ff91d
|
except datahandler.FormatError: |
728 |
c9d89f3ff91d
|
self.statusbar.set_status("Open failed") |
729 |
c9d89f3ff91d
|
dialog.Error(self, "Invalid file format", "The file '%s' contains invalid data." % file).run() |
730 |
a207757f8451
|
|
731 |
c9d89f3ff91d
|
except ( entry.EntryTypeError, entry.EntryFieldError ): |
732 |
c9d89f3ff91d
|
self.statusbar.set_status("Open failed") |
733 |
c9d89f3ff91d
|
dialog.Error(self, "Unknown data", "The file '%s' contains unknown data. It may have been created by a more recent version of Revelation.." % file).run() |
734 |
a207757f8451
|
|
735 |
c9d89f3ff91d
|
except datahandler.VersionError: |
736 |
c9d89f3ff91d
|
self.statusbar.set_status("Open failed") |
737 |
f5f9aadf2e19
|
dialog.Error(self, "Unknown data version", "The file '%s' has a future version number, please upgrade Revelation to open it." % file).run() |
738 |
a207757f8451
|
|
739 |
c9d89f3ff91d
|
except datahandler.DetectError: |
740 |
a207757f8451
|
self.statusbar.set_status("Open failed") |
741 |
f5f9aadf2e19
|
dialog.Error(self, "Unable to detect filetype", "The file type of the file '%s' could not be automatically detected. Try specifying the file type manually." % file).run() |
742 |
a207757f8451
|
|
743 |
a207757f8451
|
except IOError: |
744 |
a207757f8451
|
self.statusbar.set_status("Open failed") |
745 |
f5f9aadf2e19
|
dialog.Error(self, "Unable to open file", "The file '%s' could not be opened. Make sure that the file exists, and that you have permissions to open it." % file).run() |
746 |
a207757f8451
|
|
747 |
a207757f8451
|
|
748 |
9e161d94bffc
|
def __file_save(self, file, password, datafile = None): |
749 |
a207757f8451
|
"Saves data to a file" |
750 |
a207757f8451
|
|
751 |
c9d89f3ff91d
|
try: |
752 |
c9d89f3ff91d
|
if datafile is None: |
753 |
c9d89f3ff91d
|
datafile = self.datafile |
754 |
9e161d94bffc
|
|
755 |
c9d89f3ff91d
|
if io.file_normpath(str(datafile)) != io.file_normpath(file) and io.file_exists(file): |
756 |
c9d89f3ff91d
|
dialog.FileOverwrite(self, file).run() |
757 |
a207757f8451
|
|
758 |
c9d89f3ff91d
|
if datafile.get_handler().encryption == True: |
759 |
9e161d94bffc
|
if password is None: |
760 |
c9d89f3ff91d
|
password = dialog.PasswordSave(self, file).run() |
761 |
9e161d94bffc
|
|
762 |
9e161d94bffc
|
else: |
763 |
c9d89f3ff91d
|
dialog.FileSaveInsecure(self).run() |
764 |
a207757f8451
|
|
765 |
c9d89f3ff91d
|
datafile.save(self.entrystore, file, password) |
766 |
a207757f8451
|
|
767 |
a207757f8451
|
except IOError: |
768 |
c9d89f3ff91d
|
revelation.dialog.Error(self, "Unable to write to file", "The file '%s' could not be opened for writing. Make sure that you have the proper permissions to write to it." % file).run() |
769 |
a207757f8451
|
self.statusbar.set_status("Save failed") |
770 |
a207757f8451
|
|
771 |
a207757f8451
|
|
772 |
c9d89f3ff91d
|
def __get_common_usernames(self, e = None): |
773 |
c9d89f3ff91d
|
"Returns a list of possibly relevant usernames" |
774 |
a207757f8451
|
|
775 |
c9d89f3ff91d
|
list = [] |
776 |
c9d89f3ff91d
|
|
777 |
c9d89f3ff91d
|
if e is not None and e.has_field(entry.UsernameField): |
778 |
c9d89f3ff91d
|
list.append(e[entry.UsernameField]) |
779 |
c9d89f3ff91d
|
|
780 |
c9d89f3ff91d
|
list.append(pwd.getpwuid(os.getuid())[0]) |
781 |
c9d89f3ff91d
|
list.extend(self.entrystore.get_popular_values(entry.UsernameField, 3)) |
782 |
c9d89f3ff91d
|
|
783 |
c9d89f3ff91d
|
list = {}.fromkeys(list).keys() |
784 |
c9d89f3ff91d
|
list.sort() |
785 |
c9d89f3ff91d
|
|
786 |
c9d89f3ff91d
|
return list |
787 |
c9d89f3ff91d
|
|
788 |
c9d89f3ff91d
|
|
789 |
c9d89f3ff91d
|
def __save_changes(self, d): |
790 |
a207757f8451
|
"Asks the user if she wants to save her changes" |
791 |
a207757f8451
|
|
792 |
c9d89f3ff91d
|
if self.entrystore.changed == True and d(self).run() == True: |
793 |
c9d89f3ff91d
|
if self.file_save(self.datafile.get_file(), self.datafile.get_password()) == False: |
794 |
c9d89f3ff91d
|
raise dialog.CancelError |
795 |
a207757f8451
|
|
796 |
a207757f8451
|
|
797 |
a207757f8451
|
|
798 |
c9d89f3ff91d
|
##### PUBLIC METHODS ##### |
799 |
a207757f8451
|
|
800 |
4674492151c3
|
def clip_chain(self, e): |
801 |
b57efdd083f5
|
"Copies any usernames and passwords from an entry as a chain" |
802 |
b57efdd083f5
|
|
803 |
4674492151c3
|
if e == None: |
804 |
b57efdd083f5
|
return |
805 |
b57efdd083f5
|
|
806 |
b57efdd083f5
|
secrets = [ field.value for field in e.fields if field.datatype == entry.DATATYPE_PASSWORD and field.value != "" ] |
807 |
b57efdd083f5
|
|
808 |
b57efdd083f5
|
if e.has_field(entry.UsernameField) == True and e[entry.UsernameField] != "": |
809 |
b57efdd083f5
|
secrets.insert(0, e[entry.UsernameField]) |
810 |
b57efdd083f5
|
|
811 |
b57efdd083f5
|
self.clipboard.set(secrets) |
812 |
4674492151c3
|
self.statusbar.set_status("Password chain copied to clipboard") |
813 |
b57efdd083f5
|
|
814 |
b57efdd083f5
|
|
815 |
4674492151c3
|
def clip_copy(self, iters): |
816 |
c9d89f3ff91d
|
"Copies entries to the clipboard" |
817 |
a207757f8451
|
|
818 |
4674492151c3
|
self.entryclipboard.set(self.entrystore, iters) |
819 |
c9d89f3ff91d
|
self.statusbar.set_status("Entries copied") |
820 |
a207757f8451
|
|
821 |
a207757f8451
|
|
822 |
4674492151c3
|
def clip_cut(self, iters): |
823 |
c9d89f3ff91d
|
"Cuts entries to the clipboard" |
824 |
a207757f8451
|
|
825 |
c9d89f3ff91d
|
iters = self.entrystore.filter_parents(iters) |
826 |
c9d89f3ff91d
|
self.entryclipboard.set(self.entrystore, iters) |
827 |
c9d89f3ff91d
|
|
828 |
c9d89f3ff91d
|
# store undo data (need paths) |
829 |
c9d89f3ff91d
|
undoactions = [] |
830 |
c9d89f3ff91d
|
for iter in iters: |
831 |
c9d89f3ff91d
|
undostore = data.EntryStore() |
832 |
c9d89f3ff91d
|
undostore.import_entry(self.entrystore, iter) |
833 |
c9d89f3ff91d
|
path = self.entrystore.get_path(iter) |
834 |
c9d89f3ff91d
|
undoactions.append( ( path, undostore ) ) |
835 |
c9d89f3ff91d
|
|
836 |
c9d89f3ff91d
|
# remove data |
837 |
c9d89f3ff91d
|
for iter in iters: |
838 |
c9d89f3ff91d
|
self.entrystore.remove_entry(iter) |
839 |
c9d89f3ff91d
|
|
840 |
c9d89f3ff91d
|
self.undoqueue.add_action( |
841 |
c9d89f3ff91d
|
"Cut entries", self.__cb_undo_remove, self.__cb_redo_remove, |
842 |
c9d89f3ff91d
|
undoactions |
843 |
c9d89f3ff91d
|
) |
844 |
c9d89f3ff91d
|
|
845 |
a207757f8451
|
self.__file_autosave() |
846 |
c9d89f3ff91d
|
|
847 |
a207757f8451
|
self.tree.unselect_all() |
848 |
c9d89f3ff91d
|
self.statusbar.set_status("Entries cut") |
849 |
a207757f8451
|
|
850 |
a207757f8451
|
|
851 |
4674492151c3
|
def clip_paste(self, entrystore, parent): |
852 |
a207757f8451
|
"Pastes entries from the clipboard" |
853 |
a207757f8451
|
|
854 |
4674492151c3
|
if entrystore == None: |
855 |
a207757f8451
|
return |
856 |
a207757f8451
|
|
857 |
c9d89f3ff91d
|
parent = self.tree.get_active() |
858 |
c9d89f3ff91d
|
iters = self.entrystore.import_entry(entrystore, None, parent) |
859 |
c9d89f3ff91d
|
|
860 |
c9d89f3ff91d
|
paths = [ self.entrystore.get_path(iter) for iter in iters ] |
861 |
c9d89f3ff91d
|
|
862 |
c9d89f3ff91d
|
self.undoqueue.add_action( |
863 |
c9d89f3ff91d
|
"Paste entries", self.__cb_undo_paste, self.__cb_redo_paste, |
864 |
c9d89f3ff91d
|
( entrystore, self.entrystore.get_path(parent), paths ) |
865 |
c9d89f3ff91d
|
) |
866 |
c9d89f3ff91d
|
|
867 |
c9d89f3ff91d
|
if len(iters) > 0: |
868 |
c9d89f3ff91d
|
self.tree.select(iters[0]) |
869 |
c9d89f3ff91d
|
|
870 |
c9d89f3ff91d
|
self.statusbar.set_status("Entries pasted") |
871 |
a207757f8451
|
|
872 |
a207757f8451
|
|
873 |
4674492151c3
|
def entry_add(self, e = None, parent = None, sibling = None): |
874 |
a207757f8451
|
"Adds an entry" |
875 |
a207757f8451
|
|
876 |
a207757f8451
|
try: |
877 |
4674492151c3
|
if e == None: |
878 |
4674492151c3
|
d = dialog.EntryEdit(self, self.config, "Add entry") |
879 |
4674492151c3
|
d.set_fieldwidget_data(entry.UsernameField, self.__get_common_usernames()) |
880 |
4674492151c3
|
e = d.run() |
881 |
a207757f8451
|
|
882 |
4674492151c3
|
iter = self.entrystore.add_entry(e, parent, sibling) |
883 |
c9d89f3ff91d
|
|
884 |
c9d89f3ff91d
|
self.undoqueue.add_action( |
885 |
c9d89f3ff91d
|
"Add entry", self.__cb_undo_add, self.__cb_redo_add, |
886 |
c9d89f3ff91d
|
( self.entrystore.get_path(iter), e.copy() ) |
887 |
c9d89f3ff91d
|
) |
888 |
c9d89f3ff91d
|
|
889 |
a207757f8451
|
self.__file_autosave() |
890 |
4674492151c3
|
self.tree.select(iter) |
891 |
c9d89f3ff91d
|
self.statusbar.set_status("Entry added") |
892 |
a207757f8451
|
|
893 |
c9d89f3ff91d
|
except dialog.CancelError: |
894 |
a207757f8451
|
self.statusbar.set_status("Add entry cancelled") |
895 |
a207757f8451
|
|
896 |
a207757f8451
|
|
897 |
4674492151c3
|
def entry_edit(self, iter): |
898 |
a207757f8451
|
"Edits an entry" |
899 |
a207757f8451
|
|
900 |
c9d89f3ff91d
|
try: |
901 |
4674492151c3
|
if iter == None: |
902 |
4674492151c3
|
return |
903 |
4674492151c3
|
|
904 |
c9d89f3ff91d
|
e = self.entrystore.get_entry(iter) |
905 |
c9d89f3ff91d
|
d = dialog.EntryEdit(self, self.config, "Edit entry", e) |
906 |
c9d89f3ff91d
|
d.set_fieldwidget_data(entry.UsernameField, self.__get_common_usernames(e)) |
907 |
a207757f8451
|
|
908 |
c9d89f3ff91d
|
if type(e) == entry.FolderEntry and self.entrystore.iter_n_children(iter) > 0: |
909 |
c9d89f3ff91d
|
d.allow_typechange(False) |
910 |
a207757f8451
|
|
911 |
c9d89f3ff91d
|
n = d.run() |
912 |
c9d89f3ff91d
|
self.entrystore.update_entry(iter, n) |
913 |
c9d89f3ff91d
|
self.tree.select(iter) |
914 |
a207757f8451
|
|
915 |
c9d89f3ff91d
|
self.undoqueue.add_action( |
916 |
c9d89f3ff91d
|
"Update entry", self.__cb_undo_edit, self.__cb_redo_edit, |
917 |
c9d89f3ff91d
|
( self.entrystore.get_path(iter), e.copy(), n.copy() ) |
918 |
c9d89f3ff91d
|
) |
919 |
a207757f8451
|
|
920 |
a207757f8451
|
self.__file_autosave() |
921 |
c9d89f3ff91d
|
self.statusbar.set_status("Entry updated") |
922 |
c9d89f3ff91d
|
|
923 |
c9d89f3ff91d
|
except dialog.CancelError: |
924 |
c9d89f3ff91d
|
self.statusbar.set_status("Edit entry cancelled") |
925 |
a207757f8451
|
|
926 |
a207757f8451
|
|
927 |
a207757f8451
|
def entry_find(self): |
928 |
a207757f8451
|
"Searches for an entry" |
929 |
a207757f8451
|
|
930 |
c9d89f3ff91d
|
d = dialog.Find(self, self.config) |
931 |
c9d89f3ff91d
|
d.entry_string.set_text(self.entrysearch.string) |
932 |
c9d89f3ff91d
|
d.dropdown.set_active_type(self.entrysearch.type) |
933 |
a207757f8451
|
|
934 |
a207757f8451
|
while 1: |
935 |
c9d89f3ff91d
|
response = d.run() |
936 |
c9d89f3ff91d
|
string = d.entry_string.get_text() |
937 |
c9d89f3ff91d
|
entrytype = d.dropdown.get_active_type() |
938 |
a207757f8451
|
|
939 |
c9d89f3ff91d
|
if response == dialog.RESPONSE_NEXT: |
940 |
c9d89f3ff91d
|
self.__entry_find(d, string, entrytype, data.SEARCH_NEXT) |
941 |
a207757f8451
|
|
942 |
c9d89f3ff91d
|
elif response == dialog.RESPONSE_PREVIOUS: |
943 |
c9d89f3ff91d
|
self.__entry_find(d, string, entrytype, data.SEARCH_PREVIOUS) |
944 |
a207757f8451
|
|
945 |
a207757f8451
|
else: |
946 |
c9d89f3ff91d
|
d.destroy() |
947 |
a207757f8451
|
break |
948 |
a207757f8451
|
|
949 |
a207757f8451
|
|
950 |
095f4008dc7f
|
def entry_goto(self, iters): |
951 |
095f4008dc7f
|
"Goes to an entry" |
952 |
a207757f8451
|
|
953 |
a207757f8451
|
for iter in iters: |
954 |
a207757f8451
|
try: |
955 |
e5330daca1cd
|
|
956 |
095f4008dc7f
|
# get goto data for entry |
957 |
c9d89f3ff91d
|
e = self.entrystore.get_entry(iter) |
958 |
6cf6849eed72
|
command = self.config.get("launcher/%s" % e.id) |
959 |
a207757f8451
|
|
960 |
c9d89f3ff91d
|
if command in ( "", None ): |
961 |
095f4008dc7f
|
self.statusbar.set_status("No goto command found for " + e.typename + " entries") |
962 |
c9d89f3ff91d
|
return |
963 |
c371b70d69d8
|
|
964 |
c9d89f3ff91d
|
subst = {} |
965 |
c9d89f3ff91d
|
for field in e.fields: |
966 |
c9d89f3ff91d
|
subst[field.symbol] = field.value |
967 |
c371b70d69d8
|
|
968 |
095f4008dc7f
|
# copy passwords to clipboar |
969 |
095f4008dc7f
|
chain = [] |
970 |
e5330daca1cd
|
|
971 |
095f4008dc7f
|
if e.has_field(entry.UsernameField) == True and e[entry.UsernameField] != "" and "%" + entry.UsernameField.symbol not in command: |
972 |
095f4008dc7f
|
chain.append(e[entry.UsernameField]) |
973 |
095f4008dc7f
|
|
974 |
095f4008dc7f
|
for field in e.fields: |
975 |
095f4008dc7f
|
if field.datatype == entry.DATATYPE_PASSWORD and field.value != "": |
976 |
095f4008dc7f
|
chain.append(field.value) |
977 |
095f4008dc7f
|
|
978 |
095f4008dc7f
|
self.clipboard.set(chain) |
979 |
095f4008dc7f
|
|
980 |
095f4008dc7f
|
# generate and run goto command |
981 |
c9d89f3ff91d
|
command = util.parse_subst(command, subst) |
982 |
c9d89f3ff91d
|
util.execute_child(command) |
983 |
a207757f8451
|
|
984 |
095f4008dc7f
|
self.statusbar.set_status("Entry opened") |
985 |
e5330daca1cd
|
|
986 |
c9d89f3ff91d
|
except ( util.SubstFormatError, config.ConfigError ): |
987 |
095f4008dc7f
|
dialog.Error(self, "Invalid goto command format", "The goto command for '" + e.typename + "' entries is invalid, please correct this in the preferences.").run() |
988 |
a207757f8451
|
|
989 |
c9d89f3ff91d
|
except util.SubstValueError: |
990 |
095f4008dc7f
|
dialog.Error(self, "Missing entry data", "The entry '" + e.name + "' does not have all the data required to go to it.").run() |
991 |
8077271f872d
|
|
992 |
8077271f872d
|
|
993 |
8077271f872d
|
def entry_move(self, sourceiters, parent = None, sibling = None): |
994 |
8077271f872d
|
"Moves a set of entries" |
995 |
8077271f872d
|
|
996 |
8077271f872d
|
if type(sourceiters) != list: |
997 |
8077271f872d
|
sourceiters = [ sourceiters ] |
998 |
8077271f872d
|
|
999 |
8077271f872d
|
newiters = [] |
1000 |
8077271f872d
|
undoactions = [] |
1001 |
8077271f872d
|
|
1002 |
8077271f872d
|
for sourceiter in sourceiters: |
1003 |
8077271f872d
|
sourcepath = self.entrystore.get_path(sourceiter) |
1004 |
8077271f872d
|
newiter = self.entrystore.move_entry(sourceiter, parent, sibling) |
1005 |
8077271f872d
|
newpath = self.entrystore.get_path(newiter) |
1006 |
8077271f872d
|
|
1007 |
8077271f872d
|
undoactions.append( ( sourcepath, newpath ) ) |
1008 |
8077271f872d
|
newiters.append(newiter) |
1009 |
8077271f872d
|
|
1010 |
8077271f872d
|
self.undoqueue.add_action( |
1011 |
8077271f872d
|
"Move entry", self.__cb_undo_move, self.__cb_redo_move, |
1012 |
8077271f872d
|
undoactions |
1013 |
8077271f872d
|
) |
1014 |
8077271f872d
|
|
1015 |
8077271f872d
|
if len(newiters) > 0: |
1016 |
8077271f872d
|
self.tree.select(newiters[0]) |
1017 |
8077271f872d
|
|
1018 |
8077271f872d
|
self.statusbar.set_status("Entries moved") |
1019 |
a207757f8451
|
|
1020 |
a207757f8451
|
|
1021 |
4674492151c3
|
def entry_remove(self, iters): |
1022 |
c9d89f3ff91d
|
"Removes the selected entries" |
1023 |
a207757f8451
|
|
1024 |
a207757f8451
|
try: |
1025 |
c9d89f3ff91d
|
if len(iters) == 0: |
1026 |
c9d89f3ff91d
|
return |
1027 |
a207757f8451
|
|
1028 |
c9d89f3ff91d
|
entries = [ self.entrystore.get_entry(iter) for iter in iters ] |
1029 |
c9d89f3ff91d
|
dialog.EntryRemove(self, entries).run() |
1030 |
c9d89f3ff91d
|
iters = self.entrystore.filter_parents(iters) |
1031 |
a207757f8451
|
|
1032 |
c9d89f3ff91d
|
# store undo data (need paths) |
1033 |
c9d89f3ff91d
|
undoactions = [] |
1034 |
c9d89f3ff91d
|
for iter in iters: |
1035 |
c9d89f3ff91d
|
undostore = data.EntryStore() |
1036 |
c9d89f3ff91d
|
undostore.import_entry(self.entrystore, iter) |
1037 |
c9d89f3ff91d
|
path = self.entrystore.get_path(iter) |
1038 |
c9d89f3ff91d
|
undoactions.append( ( path, undostore ) ) |
1039 |
a207757f8451
|
|
1040 |
c9d89f3ff91d
|
# remove data |
1041 |
a207757f8451
|
for iter in iters: |
1042 |
c9d89f3ff91d
|
self.entrystore.remove_entry(iter) |
1043 |
c9d89f3ff91d
|
|
1044 |
c9d89f3ff91d
|
self.undoqueue.add_action( |
1045 |
c9d89f3ff91d
|
"Remove entry", self.__cb_undo_remove, self.__cb_redo_remove, |
1046 |
c9d89f3ff91d
|
undoactions |
1047 |
c9d89f3ff91d
|
) |
1048 |
a207757f8451
|
|
1049 |
4674492151c3
|
self.tree.unselect_all() |
1050 |
a207757f8451
|
self.__file_autosave() |
1051 |
c9d89f3ff91d
|
self.statusbar.set_status("Entries removed") |
1052 |
a207757f8451
|
|
1053 |
c9d89f3ff91d
|
except dialog.CancelError: |
1054 |
c9d89f3ff91d
|
self.statusbar.set_status("Entry removal cancelled") |
1055 |
c9d89f3ff91d
|
|
1056 |
c9d89f3ff91d
|
|
1057 |
4674492151c3
|
def file_change_password(self, password = None): |
1058 |
c9d89f3ff91d
|
"Changes the password of the current data file" |
1059 |
c9d89f3ff91d
|
|
1060 |
c9d89f3ff91d
|
try: |
1061 |
4674492151c3
|
if password == None: |
1062 |
4674492151c3
|
password = dialog.PasswordChange(self, self.datafile.get_password()).run() |
1063 |
4674492151c3
|
|
1064 |
c9d89f3ff91d
|
self.datafile.set_password(password) |
1065 |
c9d89f3ff91d
|
self.entrystore.changed = True |
1066 |
c9d89f3ff91d
|
|
1067 |
c9d89f3ff91d
|
self.__file_autosave() |
1068 |
c9d89f3ff91d
|
self.statusbar.set_status("Password changed") |
1069 |
c9d89f3ff91d
|
|
1070 |
c9d89f3ff91d
|
except dialog.CancelError: |
1071 |
c9d89f3ff91d
|
self.statusbar.set_status("Password change cancelled") |
1072 |
a207757f8451
|
|
1073 |
a207757f8451
|
|
1074 |
a207757f8451
|
def file_export(self): |
1075 |
a207757f8451
|
"Exports data to a foreign file format" |
1076 |
a207757f8451
|
|
1077 |
a207757f8451
|
try: |
1078 |
c9d89f3ff91d
|
file, handler = dialog.ExportFileSelector(self).run() |
1079 |
c9d89f3ff91d
|
datafile = io.DataFile(handler) |
1080 |
9e161d94bffc
|
self.__file_save(file, None, datafile) |
1081 |
a207757f8451
|
|
1082 |
c9d89f3ff91d
|
self.statusbar.set_status("Data exported to %s" % datafile.get_file()) |
1083 |
c9d89f3ff91d
|
|
1084 |
c9d89f3ff91d
|
except dialog.CancelError: |
1085 |
a207757f8451
|
self.statusbar.set_status("Export cancelled") |
1086 |
a207757f8451
|
|
1087 |
a207757f8451
|
|
1088 |
a207757f8451
|
def file_import(self): |
1089 |
a207757f8451
|
"Imports data from a foreign file" |
1090 |
a207757f8451
|
|
1091 |
a207757f8451
|
try: |
1092 |
c9d89f3ff91d
|
file, handler = dialog.ImportFileSelector(self).run() |
1093 |
c9d89f3ff91d
|
datafile = io.DataFile(handler) |
1094 |
9e161d94bffc
|
entrystore = self.__file_load(file, None, datafile) |
1095 |
a207757f8451
|
|
1096 |
c9d89f3ff91d
|
if entrystore is not None: |
1097 |
c9d89f3ff91d
|
newiters = self.entrystore.import_entry(entrystore, None) |
1098 |
c9d89f3ff91d
|
paths = [ self.entrystore.get_path(iter) for iter in newiters ] |
1099 |
a207757f8451
|
|
1100 |
c9d89f3ff91d
|
self.undoqueue.add_action( |
1101 |
c9d89f3ff91d
|
"Import data", self.__cb_undo_import, self.__cb_redo_import, |
1102 |
c9d89f3ff91d
|
( paths, entrystore ) |
1103 |
c9d89f3ff91d
|
) |
1104 |
c9d89f3ff91d
|
|
1105 |
c9d89f3ff91d
|
self.statusbar.set_status("Data imported from %s" % datafile.get_file()) |
1106 |
c9d89f3ff91d
|
|
1107 |
a207757f8451
|
self.__file_autosave() |
1108 |
a207757f8451
|
|
1109 |
c9d89f3ff91d
|
except dialog.CancelError: |
1110 |
a207757f8451
|
self.statusbar.set_status("Import cancelled") |
1111 |
a207757f8451
|
|
1112 |
a207757f8451
|
|
1113 |
a207757f8451
|
def file_lock(self): |
1114 |
c9d89f3ff91d
|
"Locks the current file" |
1115 |
a207757f8451
|
|
1116 |
c9d89f3ff91d
|
password = self.datafile.get_password() |
1117 |
c9d89f3ff91d
|
|
1118 |
c9d89f3ff91d
|
if password is None: |
1119 |
a207757f8451
|
return |
1120 |
a207757f8451
|
|
1121 |
c9d89f3ff91d
|
# store current state |
1122 |
c9d89f3ff91d
|
activeiter = self.tree.get_active() |
1123 |
c9d89f3ff91d
|
oldtitle = self.get_title() |
1124 |
c9d89f3ff91d
|
|
1125 |
c9d89f3ff91d
|
# lock the file |
1126 |
a207757f8451
|
self.tree.set_model(None) |
1127 |
c9d89f3ff91d
|
self.entryview.clear() |
1128 |
c9d89f3ff91d
|
self.set_title("[Locked]") |
1129 |
a207757f8451
|
self.statusbar.set_status("File locked") |
1130 |
a207757f8451
|
|
1131 |
c9d89f3ff91d
|
dialog.PasswordLock(self, password).run() |
1132 |
a207757f8451
|
|
1133 |
c9d89f3ff91d
|
# unlock the file and restore state |
1134 |
c9d89f3ff91d
|
self.tree.set_model(self.entrystore) |
1135 |
c9d89f3ff91d
|
self.tree.select(activeiter) |
1136 |
c9d89f3ff91d
|
self.set_title(oldtitle) |
1137 |
a207757f8451
|
self.statusbar.set_status("File unlocked") |
1138 |
a207757f8451
|
|
1139 |
a207757f8451
|
|
1140 |
a207757f8451
|
def file_new(self): |
1141 |
a207757f8451
|
"Opens a new file" |
1142 |
a207757f8451
|
|
1143 |
c9d89f3ff91d
|
try: |
1144 |
c9d89f3ff91d
|
self.__save_changes(dialog.FileChangedNew) |
1145 |
c9d89f3ff91d
|
|
1146 |
c9d89f3ff91d
|
self.entrystore.clear() |
1147 |
c9d89f3ff91d
|
self.datafile.close() |
1148 |
8eba4e20d67b
|
self.undoqueue.clear() |
1149 |
c9d89f3ff91d
|
self.statusbar.set_status("New file created") |
1150 |
c9d89f3ff91d
|
|
1151 |
c9d89f3ff91d
|
except dialog.CancelError: |
1152 |
a207757f8451
|
self.statusbar.set_status("New file cancelled") |
1153 |
a207757f8451
|
|
1154 |
a207757f8451
|
|
1155 |
a207757f8451
|
def file_open(self, file = None, password = None): |
1156 |
a207757f8451
|
"Opens a data file" |
1157 |
a207757f8451
|
|
1158 |
a207757f8451
|
try: |
1159 |
c9d89f3ff91d
|
self.__save_changes(dialog.FileChangedOpen) |
1160 |
a207757f8451
|
|
1161 |
a207757f8451
|
if file is None: |
1162 |
c9d89f3ff91d
|
file = dialog.OpenFileSelector(self).run() |
1163 |
a207757f8451
|
|
1164 |
9e161d94bffc
|
entrystore = self.__file_load(file, password) |
1165 |
a207757f8451
|
|
1166 |
a207757f8451
|
if entrystore is None: |
1167 |
a207757f8451
|
return |
1168 |
a207757f8451
|
|
1169 |
c9d89f3ff91d
|
self.entrystore.clear() |
1170 |
c9d89f3ff91d
|
self.entrystore.import_entry(entrystore, None) |
1171 |
c9d89f3ff91d
|
self.entrystore.changed = False |
1172 |
8eba4e20d67b
|
self.undoqueue.clear() |
1173 |
a207757f8451
|
|
1174 |
f5f9aadf2e19
|
self.statusbar.set_status("Opened file %s" % self.datafile.get_file()) |
1175 |
a207757f8451
|
|
1176 |
c9d89f3ff91d
|
except dialog.CancelError: |
1177 |
a207757f8451
|
self.statusbar.set_status("Open cancelled") |
1178 |
a207757f8451
|
|
1179 |
a207757f8451
|
|
1180 |
a207757f8451
|
def file_save(self, file = None, password = None): |
1181 |
c9d89f3ff91d
|
"Saves data to a file" |
1182 |
a207757f8451
|
|
1183 |
a207757f8451
|
try: |
1184 |
a207757f8451
|
if file is None: |
1185 |
c9d89f3ff91d
|
file = dialog.SaveFileSelector(self).run() |
1186 |
a207757f8451
|
|
1187 |
c9d89f3ff91d
|
self.__file_save(file, password) |
1188 |
c9d89f3ff91d
|
self.entrystore.changed = False |
1189 |
c9d89f3ff91d
|
self.statusbar.set_status("Data saved to file %s" % file) |
1190 |
a207757f8451
|
|
1191 |
c9d89f3ff91d
|
return True |
1192 |
c9d89f3ff91d
|
|
1193 |
c9d89f3ff91d
|
except dialog.CancelError: |
1194 |
a207757f8451
|
self.statusbar.set_status("Save cancelled") |
1195 |
9e161d94bffc
|
return False |
1196 |
a207757f8451
|
|
1197 |
a207757f8451
|
|
1198 |
a207757f8451
|
def quit(self): |
1199 |
a207757f8451
|
"Quits the application" |
1200 |
a207757f8451
|
|
1201 |
c9d89f3ff91d
|
try: |
1202 |
c9d89f3ff91d
|
self.__save_changes(dialog.FileChangedQuit) |
1203 |
c9d89f3ff91d
|
|
1204 |
c9d89f3ff91d
|
self.clipboard.clear() |
1205 |
c9d89f3ff91d
|
self.entryclipboard.clear() |
1206 |
c9d89f3ff91d
|
|
1207 |
59be49691b29
|
self.__save_state() |
1208 |
c9d89f3ff91d
|
|
1209 |
c9d89f3ff91d
|
gtk.main_quit() |
1210 |
c9d89f3ff91d
|
sys.exit(0) |
1211 |
c9d89f3ff91d
|
|
1212 |
c9d89f3ff91d
|
except dialog.CancelError: |
1213 |
a207757f8451
|
self.statusbar.set_status("Quit cancelled") |
1214 |
c9d89f3ff91d
|
return False |
1215 |
a207757f8451
|
|
1216 |
a207757f8451
|
|
1217 |
a207757f8451
|
def redo(self): |
1218 |
c9d89f3ff91d
|
"Redoes the previous action" |
1219 |
a207757f8451
|
|
1220 |
c9d89f3ff91d
|
action = self.undoqueue.get_redo_action() |
1221 |
c9d89f3ff91d
|
|
1222 |
c9d89f3ff91d
|
if action is None: |
1223 |
a207757f8451
|
return |
1224 |
a207757f8451
|
|
1225 |
c9d89f3ff91d
|
self.undoqueue.redo() |
1226 |
c9d89f3ff91d
|
self.statusbar.set_status("%s redone" % action[1]) |
1227 |
c9d89f3ff91d
|
self.__file_autosave() |
1228 |
a207757f8451
|
|
1229 |
c9d89f3ff91d
|
|
1230 |
c9d89f3ff91d
|
def run(self): |
1231 |
c9d89f3ff91d
|
"Runs the application" |
1232 |
c9d89f3ff91d
|
|
1233 |
59be49691b29
|
args, argdict = self.program.get_popt_args() |
1234 |
59be49691b29
|
|
1235 |
59be49691b29
|
if len(args) > 0: |
1236 |
59be49691b29
|
file = args[0] |
1237 |
c9d89f3ff91d
|
|
1238 |
c9d89f3ff91d
|
elif self.config.get("file/autoload") == True: |
1239 |
c9d89f3ff91d
|
file = self.config.get("file/autoload_file") |
1240 |
a207757f8451
|
|
1241 |
a207757f8451
|
else: |
1242 |
c9d89f3ff91d
|
file = "" |
1243 |
a207757f8451
|
|
1244 |
a207757f8451
|
|
1245 |
c9d89f3ff91d
|
if file != "": |
1246 |
c9d89f3ff91d
|
self.file_open(io.file_normpath(file)) |
1247 |
a207757f8451
|
|
1248 |
a207757f8451
|
gtk.main() |
1249 |
a207757f8451
|
|
1250 |
a207757f8451
|
|
1251 |
a207757f8451
|
def undo(self): |
1252 |
c9d89f3ff91d
|
"Undoes the previous action" |
1253 |
a207757f8451
|
|
1254 |
c9d89f3ff91d
|
action = self.undoqueue.get_undo_action() |
1255 |
c9d89f3ff91d
|
|
1256 |
c9d89f3ff91d
|
if action is None: |
1257 |
a207757f8451
|
return |
1258 |
a207757f8451
|
|
1259 |
c9d89f3ff91d
|
self.undoqueue.undo() |
1260 |
c9d89f3ff91d
|
self.statusbar.set_status("%s undone" % action[1]) |
1261 |
a207757f8451
|
self.__file_autosave() |
1262 |
a207757f8451
|
|
1263 |
a207757f8451
|
|
1264 |
a207757f8451
|
|
1265 |
a207757f8451
|
if __name__ == "__main__": |
1266 |
c9d89f3ff91d
|
app = Revelation() |
1267 |
c9d89f3ff91d
|
app.run() |
1268 |
a207757f8451
|