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-applet.in
- commit
- e92f2103325c
- parent
- e7337e848a4c
- branch
- default
- tags
- revelation-0.4.7
set release date
1 |
e5681e842641
|
#!/usr/bin/env python |
2 |
e5681e842641
|
|
3 |
e5681e842641
|
# |
4 |
f5e91f3eef93
|
# Revelation 0.4.7 - a password manager for GNOME 2 |
5 |
e5681e842641
|
# http://oss.codepoet.no/revelation/ |
6 |
e5681e842641
|
# $Id$ |
7 |
e5681e842641
|
# |
8 |
e5681e842641
|
# Applet for account lookup |
9 |
e5681e842641
|
# |
10 |
e5681e842641
|
# |
11 |
d230b54e5239
|
# Copyright (c) 2003-2006 Erik Grinaker |
12 |
e5681e842641
|
# |
13 |
e5681e842641
|
# This program is free software; you can redistribute it and/or |
14 |
e5681e842641
|
# modify it under the terms of the GNU General Public License |
15 |
e5681e842641
|
# as published by the Free Software Foundation; either version 2 |
16 |
e5681e842641
|
# of the License, or (at your option) any later version. |
17 |
e5681e842641
|
# |
18 |
e5681e842641
|
# This program is distributed in the hope that it will be useful, |
19 |
e5681e842641
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
20 |
e5681e842641
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
21 |
e5681e842641
|
# GNU General Public License for more details. |
22 |
e5681e842641
|
# |
23 |
e5681e842641
|
# You should have received a copy of the GNU General Public License |
24 |
e5681e842641
|
# along with this program; if not, write to the Free Software |
25 |
e5681e842641
|
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
26 |
e5681e842641
|
# |
27 |
e5681e842641
|
|
28 |
0f8c7965fdf3
|
import gnome, gobject, gtk, os, sys |
29 |
0f8c7965fdf3
|
|
30 |
0f8c7965fdf3
|
try: |
31 |
0f8c7965fdf3
|
import gnomeapplet |
32 |
0f8c7965fdf3
|
|
33 |
0f8c7965fdf3
|
except ImportError: |
34 |
bd0aa2a05f0f
|
import gnome.applet as gnomeapplet |
35 |
0f8c7965fdf3
|
|
36 |
e5681e842641
|
|
37 |
9c0451b7071b
|
if "@pyexecdir@" not in sys.path: |
38 |
9c0451b7071b
|
sys.path.insert(0, "@pyexecdir@") |
39 |
e5681e842641
|
|
40 |
ca2af4e02c4c
|
from revelation import config, data, datahandler, dialog, entry, gnomemisc, io, ui, util |
41 |
e5681e842641
|
|
42 |
e5681e842641
|
|
43 |
bd0aa2a05f0f
|
|
44 |
e5681e842641
|
class RevelationApplet(object): |
45 |
e5681e842641
|
"Revelation applet" |
46 |
e5681e842641
|
|
47 |
e5681e842641
|
def __init__(self, applet, iid): |
48 |
e5681e842641
|
self.applet = applet |
49 |
e5681e842641
|
self.iid = iid |
50 |
e5681e842641
|
|
51 |
e5681e842641
|
sys.excepthook = self.__cb_exception |
52 |
e5681e842641
|
|
53 |
e5681e842641
|
try: |
54 |
3083465f003a
|
self.__init_config() |
55 |
e5681e842641
|
self.__init_facilities() |
56 |
e5681e842641
|
self.__init_ui() |
57 |
e5681e842641
|
self.__init_states() |
58 |
e5681e842641
|
|
59 |
e5681e842641
|
except config.ConfigError: |
60 |
e5681e842641
|
dialog.Error(None, "Missing configuration data", "The applet could not find its configuration data, please reinstall Revelation.").run() |
61 |
e5681e842641
|
sys.exit(1) |
62 |
e5681e842641
|
|
63 |
e5681e842641
|
|
64 |
3083465f003a
|
def __init_config(self): |
65 |
3083465f003a
|
"Sets up configuration" |
66 |
e5681e842641
|
|
67 |
e5681e842641
|
self.applet.add_preferences("/schemas/apps/revelation-applet/prefs") |
68 |
3083465f003a
|
self.config = config.Config(self.applet.get_preferences_key()) |
69 |
e5681e842641
|
|
70 |
3083465f003a
|
# set up defaults |
71 |
e5681e842641
|
# TODO this shouldn't really be necessary, the schema should |
72 |
3083465f003a
|
# be used for defaults - is this even possible with the current |
73 |
e5681e842641
|
# applet api? |
74 |
e5681e842641
|
defaults = { |
75 |
e5681e842641
|
"autolock" : True, |
76 |
f48ca1b4146d
|
"autolock_timeout" : 10, |
77 |
e5681e842641
|
"chain_username" : False, |
78 |
e5681e842641
|
"file" : "", |
79 |
e5681e842641
|
"menuaction" : "show", |
80 |
693abd3fbd04
|
"show_passwords" : True, |
81 |
693abd3fbd04
|
"show_searchentry" : True |
82 |
e5681e842641
|
} |
83 |
e5681e842641
|
|
84 |
e5681e842641
|
for key, value in defaults.items(): |
85 |
e5681e842641
|
try: |
86 |
e5681e842641
|
self.config.get(key) |
87 |
e5681e842641
|
|
88 |
e5681e842641
|
except config.ConfigError: |
89 |
e5681e842641
|
self.config.set_force(key, value) |
90 |
e5681e842641
|
|
91 |
e5681e842641
|
# make sure the launchers have been set up, otherwise |
92 |
e5681e842641
|
# install the Revelation schema |
93 |
e5681e842641
|
def check_launchers(): |
94 |
e5681e842641
|
try: |
95 |
3083465f003a
|
for entrytype in [ et for et in entry.ENTRYLIST if et != entry.FolderEntry ]: |
96 |
e5681e842641
|
self.config.get("/apps/revelation/launcher/%s" % entrytype.id) |
97 |
e5681e842641
|
|
98 |
e5681e842641
|
except config.ConfigError: |
99 |
e5681e842641
|
return False |
100 |
e5681e842641
|
|
101 |
3083465f003a
|
else: |
102 |
3083465f003a
|
return True |
103 |
3083465f003a
|
|
104 |
3083465f003a
|
|
105 |
e5681e842641
|
if check_launchers() == False: |
106 |
e5681e842641
|
if config.install_schema("%s/revelation.schemas" % config.DIR_GCONFSCHEMAS) == False: |
107 |
e5681e842641
|
raise config.ConfigError |
108 |
e5681e842641
|
|
109 |
e5681e842641
|
if check_launchers() == False: |
110 |
e5681e842641
|
raise config.ConfigError |
111 |
e5681e842641
|
|
112 |
e5681e842641
|
|
113 |
3083465f003a
|
def __init_facilities(self): |
114 |
3083465f003a
|
"Sets up facilities" |
115 |
3083465f003a
|
|
116 |
3083465f003a
|
self.clipboard = data.Clipboard() |
117 |
3083465f003a
|
self.datafile = io.DataFile(datahandler.Revelation) |
118 |
3083465f003a
|
self.entrystore = data.EntryStore() |
119 |
3083465f003a
|
self.entrysearch = data.EntrySearch(self.entrystore) |
120 |
3083465f003a
|
self.items = ui.ItemFactory(self.applet) |
121 |
3083465f003a
|
self.locktimer = data.Timer() |
122 |
3083465f003a
|
|
123 |
3083465f003a
|
self.config.monitor("autolock_timeout", lambda k,v,d: self.locktimer.start(v * 60)) |
124 |
3083465f003a
|
self.config.monitor("file", self.__cb_config_file) |
125 |
3083465f003a
|
|
126 |
3083465f003a
|
self.datafile.connect("changed", self.__cb_file_changed) |
127 |
3083465f003a
|
self.datafile.connect("content-changed", self.__cb_file_content_changed) |
128 |
3083465f003a
|
self.locktimer.connect("ring", self.__cb_file_autolock) |
129 |
3083465f003a
|
|
130 |
3083465f003a
|
self.entrysearch.folders = False |
131 |
3083465f003a
|
|
132 |
3083465f003a
|
|
133 |
3083465f003a
|
def __init_states(self): |
134 |
3083465f003a
|
"Sets up the initial states" |
135 |
3083465f003a
|
|
136 |
3083465f003a
|
self.datafile.emit("changed", self.datafile.get_file()) |
137 |
3083465f003a
|
os.chdir(os.path.expanduser("~/")) |
138 |
3083465f003a
|
|
139 |
693abd3fbd04
|
self.config.monitor("show_searchentry", self.__cb_config_show_searchentry) |
140 |
693abd3fbd04
|
|
141 |
3083465f003a
|
|
142 |
3083465f003a
|
def __init_ui(self): |
143 |
3083465f003a
|
"Sets up the main ui" |
144 |
3083465f003a
|
|
145 |
3083465f003a
|
# set up applet |
146 |
0f8c7965fdf3
|
self.applet.set_flags(gnomeapplet.EXPAND_MINOR) |
147 |
3083465f003a
|
|
148 |
3083465f003a
|
# set up window icons |
149 |
3083465f003a
|
pixbufs = [ self.items.load_icon("revelation", size) for size in ( 48, 32, 24, 16) ] |
150 |
3083465f003a
|
pixbufs = [ pixbuf for pixbuf in pixbufs if pixbuf != None ] |
151 |
3083465f003a
|
|
152 |
3083465f003a
|
if len(pixbufs) > 0: |
153 |
3083465f003a
|
gtk.window_set_default_icon_list(*pixbufs) |
154 |
3083465f003a
|
|
155 |
3083465f003a
|
# set up popup menu |
156 |
3083465f003a
|
self.applet.setup_menu(""" |
157 |
3083465f003a
|
<popup name="button3"> |
158 |
3083465f003a
|
<menuitem name="file-unlock" verb="file-unlock" label="Unlock File" pixtype="stock" pixname="revelation-unlock" /> |
159 |
3083465f003a
|
<menuitem name="file-lock" verb="file-lock" label="Lock File" pixtype="stock" pixname="revelation-lock" /> |
160 |
3083465f003a
|
<menuitem name="file-reload" verb="file-reload" label="Reload File" pixtype="stock" pixname="revelation-reload" /> |
161 |
3083465f003a
|
<separator /> |
162 |
3083465f003a
|
<menuitem name="revelation" verb="revelation" label="Start Revelation" pixtype="stock" pixname="revelation-revelation" /> |
163 |
3083465f003a
|
<menuitem name="prefs" verb="prefs" label="Preferences" pixtype="stock" pixname="gtk-properties" /> |
164 |
3083465f003a
|
<menuitem name="about" verb="about" label="About" pixtype="stock" pixname="gnome-stock-about" /> |
165 |
3083465f003a
|
</popup> |
166 |
3083465f003a
|
""", ( |
167 |
3083465f003a
|
( "about", lambda w,d=None: self.about() ), |
168 |
3083465f003a
|
( "file-lock", lambda w,d=None: self.file_close() ), |
169 |
3083465f003a
|
( "file-reload", lambda w,d=None: self.file_reload() ), |
170 |
6994b7f39b79
|
( "file-unlock", lambda w,d=None: self.file_open(self.config.get("file")) ), |
171 |
3083465f003a
|
( "prefs", lambda w,d=None: self.prefs() ), |
172 |
e1e69f9f5cf7
|
( "revelation", lambda w,d=None: util.execute_child("@bindir@/revelation") ), |
173 |
3083465f003a
|
), None) |
174 |
3083465f003a
|
|
175 |
3083465f003a
|
# set up ui items |
176 |
3083465f003a
|
self.entry = ui.Entry() |
177 |
064a0c169fe8
|
self.entry.set_width_chars(14) |
178 |
3083465f003a
|
self.entry.connect("activate", self.__cb_entry_activate) |
179 |
3083465f003a
|
self.entry.connect("button_press_event", self.__cb_entry_buttonpress) |
180 |
3083465f003a
|
self.entry.connect("key_press_event", lambda w,d=None: self.locktimer.reset()) |
181 |
3083465f003a
|
|
182 |
96a507350f4e
|
self.icon = ui.Image() |
183 |
3083465f003a
|
self.eventbox = ui.EventBox(self.icon) |
184 |
3083465f003a
|
self.eventbox.connect("button_press_event", self.__cb_icon_buttonpress) |
185 |
3083465f003a
|
|
186 |
3083465f003a
|
self.hbox = ui.HBox(self.eventbox, self.entry) |
187 |
3083465f003a
|
self.applet.add(self.hbox) |
188 |
3083465f003a
|
|
189 |
693abd3fbd04
|
self.applet.show_all() |
190 |
693abd3fbd04
|
|
191 |
6994b7f39b79
|
# set up various ui element holders |
192 |
6994b7f39b79
|
self.popup_entryview = None |
193 |
6994b7f39b79
|
self.popup_entrylist = None |
194 |
6994b7f39b79
|
|
195 |
3083465f003a
|
self.entrymenu = None |
196 |
3083465f003a
|
|
197 |
3083465f003a
|
|
198 |
3083465f003a
|
|
199 |
6994b7f39b79
|
##### CALLBACKS ##### |
200 |
3083465f003a
|
|
201 |
3083465f003a
|
def __cb_config_file(self, key, value, data): |
202 |
6994b7f39b79
|
"Config callback for file key changes" |
203 |
3083465f003a
|
|
204 |
3083465f003a
|
self.file_close() |
205 |
6994b7f39b79
|
self.applet.get_popup_component().set_prop("/commands/file-unlock", "sensitive", self.config.get("file") != "" and "1" or "0") |
206 |
3083465f003a
|
|
207 |
3083465f003a
|
|
208 |
693abd3fbd04
|
def __cb_config_show_searchentry(self, key, value, data): |
209 |
693abd3fbd04
|
"Config callback for show searchentry setting" |
210 |
693abd3fbd04
|
|
211 |
693abd3fbd04
|
if value == True: |
212 |
693abd3fbd04
|
self.entry.show() |
213 |
693abd3fbd04
|
|
214 |
693abd3fbd04
|
else: |
215 |
693abd3fbd04
|
self.entry.hide() |
216 |
693abd3fbd04
|
|
217 |
693abd3fbd04
|
|
218 |
3083465f003a
|
def __cb_exception(self, type, value, trace): |
219 |
3083465f003a
|
"Callback for unhandled exceptions" |
220 |
3083465f003a
|
|
221 |
3083465f003a
|
if type == KeyboardInterrupt: |
222 |
3083465f003a
|
sys.exit(1) |
223 |
3083465f003a
|
|
224 |
3083465f003a
|
traceback = util.trace_exception(type, value, trace) |
225 |
3083465f003a
|
sys.stderr.write(traceback) |
226 |
3083465f003a
|
|
227 |
3083465f003a
|
if dialog.Exception(None, traceback).run() == True: |
228 |
3083465f003a
|
gtk.main() |
229 |
3083465f003a
|
|
230 |
3083465f003a
|
else: |
231 |
3083465f003a
|
sys.exit(1) |
232 |
3083465f003a
|
|
233 |
3083465f003a
|
|
234 |
3083465f003a
|
def __cb_entry_activate(self, widget, data = None): |
235 |
3083465f003a
|
"Callback for entry activation (pressing enter etc)" |
236 |
3083465f003a
|
|
237 |
55c3f554d446
|
self.entry_search(self.entry.get_text(), True) |
238 |
3083465f003a
|
|
239 |
3083465f003a
|
|
240 |
3083465f003a
|
def __cb_entry_buttonpress(self, widget, data = None): |
241 |
6994b7f39b79
|
"Callback for entry button presses" |
242 |
3083465f003a
|
|
243 |
3083465f003a
|
self.locktimer.reset() |
244 |
3083465f003a
|
|
245 |
3083465f003a
|
# from gnome 2.10, the panel doesn't receive focus when clicked |
246 |
fe71b6d3fc2a
|
# (the OverflowError exception is a workaround for what I suspect |
247 |
fe71b6d3fc2a
|
# is a pygtk bug - see pygtk bug #300012) |
248 |
ac1d7b7ffc91
|
try: |
249 |
ac1d7b7ffc91
|
if data.button == 1 and hasattr(gnomemisc, "panel_applet_request_focus") == True: |
250 |
fe71b6d3fc2a
|
gnomemisc.panel_applet_request_focus(self.applet, long(data.time)) |
251 |
fe71b6d3fc2a
|
|
252 |
ac1d7b7ffc91
|
except OverflowError: |
253 |
ac1d7b7ffc91
|
gnomemisc.panel_applet_request_focus(self.applet, 0L) |
254 |
3083465f003a
|
|
255 |
3083465f003a
|
|
256 |
3083465f003a
|
def __cb_file_autolock(self, widget, data = None): |
257 |
3083465f003a
|
"Callback for autolocking the file" |
258 |
3083465f003a
|
|
259 |
3083465f003a
|
if self.config.get("autolock") == True: |
260 |
3083465f003a
|
self.file_close() |
261 |
3083465f003a
|
|
262 |
3083465f003a
|
|
263 |
3083465f003a
|
def __cb_file_content_changed(self, widget, data = None): |
264 |
3083465f003a
|
"Callback for changed file content" |
265 |
3083465f003a
|
|
266 |
3083465f003a
|
try: |
267 |
db3c0183fd5c
|
self.__file_load(self.datafile.get_file(), self.datafile.get_password()) |
268 |
db3c0183fd5c
|
|
269 |
db3c0183fd5c
|
except dialog.CancelError: |
270 |
db3c0183fd5c
|
pass |
271 |
3083465f003a
|
|
272 |
3083465f003a
|
except datahandler.PasswordError: |
273 |
3083465f003a
|
self.file_close() |
274 |
3083465f003a
|
|
275 |
db3c0183fd5c
|
except datahandler.Error: |
276 |
db3c0183fd5c
|
pass |
277 |
db3c0183fd5c
|
|
278 |
3083465f003a
|
|
279 |
3083465f003a
|
def __cb_file_changed(self, widget, data = None): |
280 |
3083465f003a
|
"Callback for changed data file" |
281 |
3083465f003a
|
|
282 |
3083465f003a
|
popup = self.applet.get_popup_component() |
283 |
3083465f003a
|
|
284 |
3083465f003a
|
if self.datafile.get_file() == None: |
285 |
3083465f003a
|
self.entry.set_text("") |
286 |
3083465f003a
|
|
287 |
6994b7f39b79
|
popup.set_prop("/commands/file-unlock", "sensitive", self.config.get("file") != "" and "1" or "0") |
288 |
3083465f003a
|
popup.set_prop("/commands/file-lock", "sensitive", "0") |
289 |
3083465f003a
|
popup.set_prop("/commands/file-reload", "sensitive", "0") |
290 |
3083465f003a
|
|
291 |
96a507350f4e
|
self.icon.set_from_stock(ui.STOCK_REVELATION_LOCKED, ui.ICON_SIZE_APPLET) |
292 |
96a507350f4e
|
|
293 |
3083465f003a
|
else: |
294 |
3083465f003a
|
popup.set_prop("/commands/file-unlock", "sensitive", "0") |
295 |
3083465f003a
|
popup.set_prop("/commands/file-lock", "sensitive", "1") |
296 |
3083465f003a
|
popup.set_prop("/commands/file-reload", "sensitive", "1") |
297 |
3083465f003a
|
|
298 |
96a507350f4e
|
self.icon.set_from_stock(ui.STOCK_REVELATION, ui.ICON_SIZE_APPLET) |
299 |
96a507350f4e
|
|
300 |
3083465f003a
|
|
301 |
6994b7f39b79
|
def __cb_icon_buttonpress(self, widget, data = None): |
302 |
6994b7f39b79
|
"Callback for buttonpress on button" |
303 |
6994b7f39b79
|
|
304 |
6994b7f39b79
|
if data.button != 1: |
305 |
6994b7f39b79
|
return False |
306 |
6994b7f39b79
|
|
307 |
6994b7f39b79
|
self.entry_menu(data.time) |
308 |
6994b7f39b79
|
|
309 |
6994b7f39b79
|
return True |
310 |
6994b7f39b79
|
|
311 |
6994b7f39b79
|
|
312 |
6994b7f39b79
|
def __cb_popup_activate(self, widget, data = None): |
313 |
3083465f003a
|
"Takes appropriate action when a menu item is activated" |
314 |
3083465f003a
|
|
315 |
3083465f003a
|
self.locktimer.reset() |
316 |
3083465f003a
|
|
317 |
3083465f003a
|
action = self.config.get("menuaction") |
318 |
3083465f003a
|
|
319 |
3083465f003a
|
if action == "show": |
320 |
3083465f003a
|
self.entry_show(data) |
321 |
3083465f003a
|
|
322 |
3083465f003a
|
elif action == "copy": |
323 |
3083465f003a
|
self.entry_copychain(data) |
324 |
3083465f003a
|
|
325 |
6994b7f39b79
|
elif self.__launcher_valid(data): |
326 |
3083465f003a
|
self.entry_goto(data) |
327 |
3083465f003a
|
|
328 |
3083465f003a
|
else: |
329 |
3083465f003a
|
self.entry_show(data) |
330 |
3083465f003a
|
|
331 |
3083465f003a
|
|
332 |
e5681e842641
|
|
333 |
6994b7f39b79
|
##### PRIVATE METHODS ##### |
334 |
e5681e842641
|
|
335 |
6994b7f39b79
|
def __close_popups(self): |
336 |
6994b7f39b79
|
"Closes any open popups" |
337 |
e5681e842641
|
|
338 |
6994b7f39b79
|
self.locktimer.reset() |
339 |
e5681e842641
|
|
340 |
6994b7f39b79
|
if hasattr(self, "popup_entryview") == True and self.popup_entryview != None: |
341 |
6994b7f39b79
|
self.popup_entryview.destroy() |
342 |
e5681e842641
|
|
343 |
6994b7f39b79
|
if hasattr(self, "popup_entrylist") == True and self.popup_entrylist != None: |
344 |
6994b7f39b79
|
self.popup_entrylist.destroy() |
345 |
e5681e842641
|
|
346 |
6994b7f39b79
|
if hasattr(self, "entrymenu") == True and self.entrymenu != None: |
347 |
6994b7f39b79
|
self.entrymenu.hide() |
348 |
e5681e842641
|
|
349 |
e5681e842641
|
|
350 |
db3c0183fd5c
|
def __file_load(self, file, password = None): |
351 |
db3c0183fd5c
|
"Loads a data file" |
352 |
db3c0183fd5c
|
|
353 |
db3c0183fd5c
|
if file in ( "", None ): |
354 |
db3c0183fd5c
|
return False |
355 |
db3c0183fd5c
|
|
356 |
db3c0183fd5c
|
if dialog.present_unique(dialog.PasswordOpen) == True: |
357 |
db3c0183fd5c
|
return False |
358 |
db3c0183fd5c
|
|
359 |
db3c0183fd5c
|
entrystore = self.datafile.load(file, password, lambda: dialog.run_unique(dialog.PasswordOpen, None, os.path.basename(file))) |
360 |
db3c0183fd5c
|
|
361 |
db3c0183fd5c
|
self.entrystore.clear() |
362 |
db3c0183fd5c
|
self.entrystore.import_entry(entrystore, None) |
363 |
db3c0183fd5c
|
|
364 |
db3c0183fd5c
|
self.entrymenu = self.__generate_entrymenu(self.entrystore) |
365 |
db3c0183fd5c
|
self.locktimer.start(self.config.get("autolock_timeout") * 60) |
366 |
db3c0183fd5c
|
|
367 |
db3c0183fd5c
|
self.__close_popups() |
368 |
db3c0183fd5c
|
|
369 |
db3c0183fd5c
|
return True |
370 |
db3c0183fd5c
|
|
371 |
db3c0183fd5c
|
|
372 |
6994b7f39b79
|
def __flash_entry(self, color = "#ffbaba", duration = 500): |
373 |
6994b7f39b79
|
"Flashes the entry with a color" |
374 |
e5681e842641
|
|
375 |
6994b7f39b79
|
color_normal = ui.Entry().rc_get_style().base[gtk.STATE_NORMAL] |
376 |
6994b7f39b79
|
color_new = gtk.gdk.color_parse(color) |
377 |
e5681e842641
|
|
378 |
6994b7f39b79
|
self.entry.modify_base(gtk.STATE_NORMAL, color_new) |
379 |
6994b7f39b79
|
gobject.timeout_add(duration, lambda: self.entry.modify_base(gtk.STATE_NORMAL, color_normal)) |
380 |
e5681e842641
|
|
381 |
e5681e842641
|
|
382 |
6994b7f39b79
|
def __focus_entry(self): |
383 |
6994b7f39b79
|
"Gives focus to the entry" |
384 |
e5681e842641
|
|
385 |
ca2af4e02c4c
|
if hasattr(gnomemisc, "panel_applet_request_focus") == True: |
386 |
ca2af4e02c4c
|
gnomemisc.panel_applet_request_focus(self.applet, long(0)) |
387 |
e5681e842641
|
|
388 |
e5681e842641
|
|
389 |
6994b7f39b79
|
def __generate_entrymenu(self, entrystore, parent = None): |
390 |
6994b7f39b79
|
"Generates an entry menu tree" |
391 |
e5681e842641
|
|
392 |
6994b7f39b79
|
menu = gtk.Menu() |
393 |
6994b7f39b79
|
|
394 |
6994b7f39b79
|
for i in range(entrystore.iter_n_children(parent)): |
395 |
6994b7f39b79
|
iter = entrystore.iter_nth_child(parent, i) |
396 |
6994b7f39b79
|
|
397 |
6994b7f39b79
|
e = entrystore.get_entry(iter) |
398 |
6994b7f39b79
|
item = ui.ImageMenuItem(type(e) == entry.FolderEntry and ui.STOCK_FOLDER or e.icon, e.name) |
399 |
6994b7f39b79
|
item.connect("select", lambda w,d=None: self.locktimer.reset()) |
400 |
6994b7f39b79
|
|
401 |
6994b7f39b79
|
if type(e) == entry.FolderEntry: |
402 |
6994b7f39b79
|
item.set_submenu(self.__generate_entrymenu(entrystore, iter)) |
403 |
6994b7f39b79
|
|
404 |
6994b7f39b79
|
else: |
405 |
6994b7f39b79
|
item.connect("activate", self.__cb_popup_activate, e) |
406 |
6994b7f39b79
|
|
407 |
6994b7f39b79
|
menu.append(item) |
408 |
6994b7f39b79
|
|
409 |
6994b7f39b79
|
return menu |
410 |
6994b7f39b79
|
|
411 |
6994b7f39b79
|
|
412 |
6994b7f39b79
|
def __get_launcher(self, e): |
413 |
e5681e842641
|
"Returns a launcher command for an entry, if possible" |
414 |
e5681e842641
|
|
415 |
e5681e842641
|
command = self.config.get("/apps/revelation/launcher/%s" % e.id) |
416 |
e5681e842641
|
|
417 |
e5681e842641
|
if command in ( "", None ): |
418 |
e5681e842641
|
return None |
419 |
e5681e842641
|
|
420 |
e5681e842641
|
subst = {} |
421 |
e5681e842641
|
for field in e.fields: |
422 |
e5681e842641
|
subst[field.symbol] = field.value |
423 |
e5681e842641
|
|
424 |
e5681e842641
|
command = util.parse_subst(command, subst) |
425 |
e5681e842641
|
|
426 |
e5681e842641
|
return command |
427 |
e5681e842641
|
|
428 |
e5681e842641
|
|
429 |
6994b7f39b79
|
def __get_popup_offset(self, popup): |
430 |
e5681e842641
|
"Returns a tuple of x and y offset coords for popups" |
431 |
e5681e842641
|
|
432 |
e5681e842641
|
screen = self.applet.get_screen() |
433 |
e5681e842641
|
a = self.applet.get_allocation() |
434 |
e5681e842641
|
rw, rh = popup.size_request() |
435 |
e5681e842641
|
|
436 |
e5681e842641
|
x, y = self.applet.window.get_origin() |
437 |
e5681e842641
|
x += a.x |
438 |
e5681e842641
|
y += a.y |
439 |
e5681e842641
|
|
440 |
e5681e842641
|
|
441 |
e5681e842641
|
# TODO use constants ORIENT_UP etc here, if available |
442 |
e5681e842641
|
if self.applet.get_orient() in ( 0, 1 ): |
443 |
e5681e842641
|
x = min(x, screen.get_width() - rw) |
444 |
e5681e842641
|
|
445 |
e5681e842641
|
if (y > screen.get_height() / 2): |
446 |
e5681e842641
|
y -= rh |
447 |
e5681e842641
|
|
448 |
e5681e842641
|
else: |
449 |
e5681e842641
|
y += a.height |
450 |
e5681e842641
|
|
451 |
e5681e842641
|
else: |
452 |
e5681e842641
|
y = min(y, screen.get_height() - rh) |
453 |
e5681e842641
|
|
454 |
e5681e842641
|
if (x > screen.get_width() / 2): |
455 |
e5681e842641
|
x -= rw |
456 |
e5681e842641
|
|
457 |
e5681e842641
|
else: |
458 |
e5681e842641
|
x += a.width |
459 |
e5681e842641
|
|
460 |
e5681e842641
|
return x, y |
461 |
e5681e842641
|
|
462 |
e5681e842641
|
|
463 |
6994b7f39b79
|
def __launcher_valid(self, e): |
464 |
6994b7f39b79
|
"Checks if a launcher is valid" |
465 |
e5681e842641
|
|
466 |
6994b7f39b79
|
try: |
467 |
6994b7f39b79
|
command = self.__get_launcher(e) |
468 |
6994b7f39b79
|
|
469 |
6994b7f39b79
|
return command != None |
470 |
6994b7f39b79
|
|
471 |
6994b7f39b79
|
except ( util.SubstFormatError ): |
472 |
6994b7f39b79
|
return True |
473 |
6994b7f39b79
|
|
474 |
6994b7f39b79
|
except ( util.SubstValueError, config.ConfigError ): |
475 |
6994b7f39b79
|
return False |
476 |
6994b7f39b79
|
|
477 |
6994b7f39b79
|
|
478 |
6994b7f39b79
|
def __require_file(self): |
479 |
6994b7f39b79
|
"Checks if a datafile is loaded, or alerts the user" |
480 |
6994b7f39b79
|
|
481 |
6994b7f39b79
|
if self.datafile.get_file() != None: |
482 |
6994b7f39b79
|
return True |
483 |
6994b7f39b79
|
|
484 |
6994b7f39b79
|
if self.config.get("file") != "": |
485 |
6994b7f39b79
|
return self.file_open(self.config.get("file")) |
486 |
6994b7f39b79
|
|
487 |
6994b7f39b79
|
d = dialog.Info( |
488 |
6994b7f39b79
|
None, "File not selected", |
489 |
6994b7f39b79
|
"You must select a Revelation data file to use - this can be done in the applet preferences.", |
490 |
6994b7f39b79
|
( ( gtk.STOCK_PREFERENCES, gtk.RESPONSE_ACCEPT ), ( gtk.STOCK_OK, gtk.RESPONSE_OK ) ) |
491 |
6994b7f39b79
|
) |
492 |
6994b7f39b79
|
|
493 |
6994b7f39b79
|
|
494 |
6994b7f39b79
|
if d.run() == gtk.RESPONSE_ACCEPT: |
495 |
6994b7f39b79
|
self.prefs() |
496 |
6994b7f39b79
|
|
497 |
6994b7f39b79
|
return False |
498 |
6994b7f39b79
|
|
499 |
6994b7f39b79
|
|
500 |
6994b7f39b79
|
##### PUBLIC METHODS ##### |
501 |
6994b7f39b79
|
|
502 |
6994b7f39b79
|
def about(self): |
503 |
6994b7f39b79
|
"Displays an about dialog" |
504 |
6994b7f39b79
|
|
505 |
dce7200431f0
|
dialog.run_unique(About, self.applet) |
506 |
6994b7f39b79
|
|
507 |
6994b7f39b79
|
|
508 |
6994b7f39b79
|
def entry_copychain(self, e, launcher = ""): |
509 |
6994b7f39b79
|
"Copies all passwords from an entry as a chain" |
510 |
6994b7f39b79
|
|
511 |
6994b7f39b79
|
if e == None: |
512 |
6994b7f39b79
|
return |
513 |
6994b7f39b79
|
|
514 |
6994b7f39b79
|
secrets = [ field.value for field in e.fields if field.datatype == entry.DATATYPE_PASSWORD and field.value != "" ] |
515 |
6994b7f39b79
|
|
516 |
6994b7f39b79
|
if self.config.get("chain_username") == True and len(secrets) > 0: |
517 |
6994b7f39b79
|
if e.has_field(entry.UsernameField) and e[entry.UsernameField] != "": |
518 |
6994b7f39b79
|
if "%" + entry.UsernameField.symbol not in launcher: |
519 |
6994b7f39b79
|
secrets.insert(0, e[entry.UsernameField]) |
520 |
6994b7f39b79
|
|
521 |
6994b7f39b79
|
self.clipboard.set(secrets) |
522 |
6994b7f39b79
|
|
523 |
6994b7f39b79
|
|
524 |
6994b7f39b79
|
def entry_goto(self, e): |
525 |
6994b7f39b79
|
"Goes to an entry" |
526 |
6994b7f39b79
|
|
527 |
6994b7f39b79
|
try: |
528 |
6994b7f39b79
|
command = self.__get_launcher(e) |
529 |
6994b7f39b79
|
|
530 |
6994b7f39b79
|
if command == None: |
531 |
6994b7f39b79
|
return |
532 |
6994b7f39b79
|
|
533 |
6994b7f39b79
|
self.entry_copychain(e) |
534 |
6994b7f39b79
|
|
535 |
6994b7f39b79
|
util.execute_child(command) |
536 |
6994b7f39b79
|
|
537 |
6994b7f39b79
|
except ( util.SubstFormatError, config.ConfigError ): |
538 |
6994b7f39b79
|
dialog.Error(None, "Invalid goto command format", "The goto command for '" + e.typename + "' entries is invalid, please correct this in the preferences.").run() |
539 |
6994b7f39b79
|
|
540 |
6994b7f39b79
|
except util.SubstValueError: |
541 |
6994b7f39b79
|
self.entry_show(e) |
542 |
6994b7f39b79
|
|
543 |
6994b7f39b79
|
|
544 |
6994b7f39b79
|
def entry_menu(self, time = None): |
545 |
6994b7f39b79
|
"Displays the entry menu" |
546 |
6994b7f39b79
|
|
547 |
6994b7f39b79
|
self.__close_popups() |
548 |
6994b7f39b79
|
|
549 |
6994b7f39b79
|
if self.__require_file() == False: |
550 |
6994b7f39b79
|
return |
551 |
6994b7f39b79
|
|
552 |
6994b7f39b79
|
if self.entrymenu == None: |
553 |
6994b7f39b79
|
return |
554 |
6994b7f39b79
|
|
555 |
6994b7f39b79
|
x, y = self.__get_popup_offset(self.entrymenu) |
556 |
6994b7f39b79
|
|
557 |
6994b7f39b79
|
self.entrymenu.show_all() |
558 |
6994b7f39b79
|
self.entrymenu.popup(None, None, lambda d: (x, y, False), 1, time) |
559 |
6994b7f39b79
|
|
560 |
6994b7f39b79
|
|
561 |
55c3f554d446
|
def entry_search(self, term, focusafter = False): |
562 |
6994b7f39b79
|
"Searches for an entry" |
563 |
6994b7f39b79
|
|
564 |
6994b7f39b79
|
self.__close_popups() |
565 |
6994b7f39b79
|
|
566 |
6994b7f39b79
|
if term.strip() == "": |
567 |
6994b7f39b79
|
return |
568 |
6994b7f39b79
|
|
569 |
6994b7f39b79
|
if self.__require_file() == False: |
570 |
6994b7f39b79
|
return |
571 |
6994b7f39b79
|
|
572 |
6994b7f39b79
|
|
573 |
6994b7f39b79
|
matches = [ self.entrystore.get_entry(iter) for iter in self.entrysearch.find_all(term) ] |
574 |
6994b7f39b79
|
|
575 |
6994b7f39b79
|
if len(matches) == 0: |
576 |
13ec1163cf9f
|
self.__focus_entry() |
577 |
6994b7f39b79
|
self.__flash_entry() |
578 |
5a07753dbd3c
|
self.entry.select_region(0, -1) |
579 |
6994b7f39b79
|
|
580 |
6994b7f39b79
|
elif len(matches) == 1: |
581 |
55c3f554d446
|
self.entry_show(matches[0], True) |
582 |
6994b7f39b79
|
|
583 |
6994b7f39b79
|
else: |
584 |
6994b7f39b79
|
self.popup_entrylist = EntryListPopup(matches) |
585 |
55c3f554d446
|
self.popup_entrylist.connect("entry-chosen", lambda w,e: self.entry_show(e, focusafter)) |
586 |
55c3f554d446
|
|
587 |
55c3f554d446
|
if focusafter == True: |
588 |
55c3f554d446
|
self.popup_entrylist.connect("closed", lambda w: self.__focus_entry()) |
589 |
6994b7f39b79
|
|
590 |
6994b7f39b79
|
self.popup_entrylist.realize() |
591 |
6994b7f39b79
|
x, y = self.__get_popup_offset(self.popup_entrylist) |
592 |
6994b7f39b79
|
self.popup_entrylist.show(x, y) |
593 |
6994b7f39b79
|
|
594 |
6994b7f39b79
|
|
595 |
55c3f554d446
|
def entry_show(self, e, focusafter = False): |
596 |
6994b7f39b79
|
"Shows an entry" |
597 |
6994b7f39b79
|
|
598 |
6994b7f39b79
|
self.__close_popups() |
599 |
6994b7f39b79
|
|
600 |
6994b7f39b79
|
self.popup_entryview = EntryViewPopup(e, self.config, self.clipboard) |
601 |
55c3f554d446
|
|
602 |
55c3f554d446
|
if focusafter == True: |
603 |
55c3f554d446
|
self.popup_entryview.connect("closed", lambda w: self.__focus_entry()) |
604 |
6994b7f39b79
|
|
605 |
6994b7f39b79
|
def cb_goto(widget): |
606 |
6994b7f39b79
|
if self.__launcher_valid(e): |
607 |
6994b7f39b79
|
self.entry_goto(e) |
608 |
6994b7f39b79
|
|
609 |
bd0aa2a05f0f
|
self.popup_entryview.close() |
610 |
6994b7f39b79
|
|
611 |
6994b7f39b79
|
self.popup_entryview.button_goto.connect("clicked", cb_goto) |
612 |
6994b7f39b79
|
self.popup_entryview.button_goto.set_sensitive(self.__launcher_valid(e)) |
613 |
6994b7f39b79
|
|
614 |
6994b7f39b79
|
self.popup_entryview.realize() |
615 |
6994b7f39b79
|
x, y = self.__get_popup_offset(self.popup_entryview) |
616 |
6994b7f39b79
|
self.popup_entryview.show(x, y) |
617 |
6994b7f39b79
|
|
618 |
6994b7f39b79
|
|
619 |
6994b7f39b79
|
def file_close(self): |
620 |
6994b7f39b79
|
"Closes the current data file" |
621 |
6994b7f39b79
|
|
622 |
6994b7f39b79
|
self.__close_popups() |
623 |
6994b7f39b79
|
self.locktimer.stop() |
624 |
6994b7f39b79
|
|
625 |
6994b7f39b79
|
self.datafile.close() |
626 |
6994b7f39b79
|
self.entrystore.clear() |
627 |
6994b7f39b79
|
self.entrymenu = None |
628 |
6994b7f39b79
|
|
629 |
6994b7f39b79
|
|
630 |
6994b7f39b79
|
def file_open(self, file, password = None): |
631 |
6994b7f39b79
|
"Opens a data file" |
632 |
6994b7f39b79
|
|
633 |
6994b7f39b79
|
try: |
634 |
db3c0183fd5c
|
return self.__file_load(file, password) |
635 |
6994b7f39b79
|
|
636 |
6994b7f39b79
|
except dialog.CancelError: |
637 |
6994b7f39b79
|
pass |
638 |
6994b7f39b79
|
|
639 |
6994b7f39b79
|
except datahandler.FormatError: |
640 |
6994b7f39b79
|
dialog.Error(None, "Invalid file format", "The file '%s' contains invalid data." % file).run() |
641 |
6994b7f39b79
|
|
642 |
6994b7f39b79
|
except ( datahandler.DataError, entry.EntryTypeError, entry.EntryFieldError ): |
643 |
6994b7f39b79
|
dialog.Error(None, "Unknown data", "The file '%s' contains unknown data. It may have been created by a more recent version of Revelation.." % file).run() |
644 |
6994b7f39b79
|
|
645 |
6994b7f39b79
|
except datahandler.PasswordError: |
646 |
6994b7f39b79
|
dialog.Error(None, "Incorrect password", "You entered an incorrect password for the file '%s', please try again." % file).run() |
647 |
6994b7f39b79
|
self.file_open(file, None) |
648 |
6994b7f39b79
|
|
649 |
6994b7f39b79
|
except datahandler.VersionError: |
650 |
6994b7f39b79
|
dialog.Error(None, "Unknown data version", "The file '%s' has a future version number, please upgrade Revelation to open it." % file).run() |
651 |
6994b7f39b79
|
|
652 |
6994b7f39b79
|
except IOError: |
653 |
6994b7f39b79
|
dialog.Error(None, "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() |
654 |
6994b7f39b79
|
|
655 |
6994b7f39b79
|
return False |
656 |
6994b7f39b79
|
|
657 |
6994b7f39b79
|
|
658 |
6994b7f39b79
|
def file_reload(self): |
659 |
6994b7f39b79
|
"Reloads the current data file" |
660 |
6994b7f39b79
|
|
661 |
6994b7f39b79
|
if self.datafile.get_file() == None: |
662 |
6994b7f39b79
|
return |
663 |
6994b7f39b79
|
|
664 |
6994b7f39b79
|
self.file_open(self.datafile.get_file(), self.datafile.get_password()) |
665 |
e5681e842641
|
|
666 |
e5681e842641
|
|
667 |
e5681e842641
|
def prefs(self): |
668 |
e5681e842641
|
"Displays the preference dialog" |
669 |
e5681e842641
|
|
670 |
dce7200431f0
|
dialog.run_unique(Preferences, None, self.config) |
671 |
e5681e842641
|
|
672 |
e5681e842641
|
|
673 |
e5681e842641
|
|
674 |
e5681e842641
|
class About(gnome.ui.About): |
675 |
e5681e842641
|
"About dialog" |
676 |
e5681e842641
|
|
677 |
e5681e842641
|
def __init__(self, parent): |
678 |
e5681e842641
|
gnome.ui.About.__init__( |
679 |
9295ddb2c689
|
self, "Revelation Account Search", config.VERSION, config.COPYRIGHT, |
680 |
9295ddb2c689
|
""""%s"\n\nAn applet for searching and browsing a Revelation account database""" % config.RELNAME, |
681 |
e5681e842641
|
( config.AUTHOR, ), None, "", |
682 |
e5681e842641
|
parent.render_icon(ui.STOCK_REVELATION, gtk.ICON_SIZE_DIALOG) |
683 |
e5681e842641
|
) |
684 |
e5681e842641
|
|
685 |
e5681e842641
|
|
686 |
e5681e842641
|
def run(self): |
687 |
e5681e842641
|
"Displays the dialog" |
688 |
e5681e842641
|
|
689 |
e5681e842641
|
self.show_all() |
690 |
e5681e842641
|
|
691 |
e5681e842641
|
|
692 |
e5681e842641
|
|
693 |
3083465f003a
|
class EntryListPopup(dialog.Popup): |
694 |
3083465f003a
|
"A popup for displaying a list of entries" |
695 |
3083465f003a
|
|
696 |
3083465f003a
|
def __init__(self, entries): |
697 |
3083465f003a
|
dialog.Popup.__init__(self) |
698 |
167384ef2e8d
|
self.set_default_size(225, 200) |
699 |
3083465f003a
|
|
700 |
3083465f003a
|
self.entrystore = data.EntryStore() |
701 |
3083465f003a
|
self.entrystore.set_sort_column_id(0, gtk.SORT_ASCENDING) |
702 |
3083465f003a
|
|
703 |
3083465f003a
|
for e in entries: |
704 |
3083465f003a
|
self.entrystore.add_entry(e) |
705 |
3083465f003a
|
|
706 |
3083465f003a
|
self.treeview = ui.EntryTree(self.entrystore) |
707 |
3083465f003a
|
self.treeview.set_cursor((0,)) |
708 |
6994b7f39b79
|
self.treeview.connect("row-activated", self.__cb_row_activated) |
709 |
3083465f003a
|
|
710 |
3083465f003a
|
self.scrolledwindow = ui.ScrolledWindow(self.treeview) |
711 |
167384ef2e8d
|
self.scrolledwindow.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) |
712 |
3083465f003a
|
self.add(self.scrolledwindow) |
713 |
3083465f003a
|
|
714 |
3083465f003a
|
|
715 |
6994b7f39b79
|
def __cb_row_activated(self, widget, path, data = None): |
716 |
6994b7f39b79
|
"Callback for tree row activation" |
717 |
6994b7f39b79
|
|
718 |
6994b7f39b79
|
iter = self.entrystore.get_iter(path) |
719 |
6994b7f39b79
|
e = self.entrystore.get_entry(iter) |
720 |
6994b7f39b79
|
|
721 |
6994b7f39b79
|
self.emit("entry-chosen", e) |
722 |
bd0aa2a05f0f
|
self.close() |
723 |
6994b7f39b79
|
|
724 |
167384ef2e8d
|
|
725 |
6994b7f39b79
|
gobject.signal_new("entry-chosen", EntryListPopup, gobject.SIGNAL_ACTION, gobject.TYPE_BOOLEAN, ( gobject.TYPE_PYOBJECT, )) |
726 |
6994b7f39b79
|
|
727 |
6994b7f39b79
|
|
728 |
3083465f003a
|
|
729 |
3083465f003a
|
class EntryViewPopup(dialog.Popup): |
730 |
3083465f003a
|
"A popup for displaying an entry" |
731 |
3083465f003a
|
|
732 |
3083465f003a
|
def __init__(self, e, cfg = None, clipboard = None): |
733 |
3083465f003a
|
dialog.Popup.__init__(self) |
734 |
3083465f003a
|
self.set_title(e.name) |
735 |
3083465f003a
|
|
736 |
3083465f003a
|
self.entryview = ui.EntryView(cfg, clipboard) |
737 |
3083465f003a
|
self.entryview.set_border_width(0) |
738 |
3083465f003a
|
self.entryview.display_entry(e) |
739 |
3083465f003a
|
|
740 |
bd0aa2a05f0f
|
self.button_close = ui.Button(gtk.STOCK_CLOSE, lambda w: self.close()) |
741 |
3083465f003a
|
self.button_goto = ui.Button(ui.STOCK_GOTO) |
742 |
3083465f003a
|
self.buttonbox = ui.HButtonBox(self.button_goto, self.button_close) |
743 |
3083465f003a
|
|
744 |
3083465f003a
|
self.vbox = ui.VBox(self.entryview, self.buttonbox) |
745 |
3083465f003a
|
self.vbox.set_border_width(12) |
746 |
3083465f003a
|
self.vbox.set_spacing(15) |
747 |
3083465f003a
|
|
748 |
3083465f003a
|
self.add(self.vbox) |
749 |
3083465f003a
|
|
750 |
3083465f003a
|
self.connect("show", lambda w: self.button_close.grab_focus()) |
751 |
3083465f003a
|
|
752 |
3083465f003a
|
|
753 |
3083465f003a
|
|
754 |
e5681e842641
|
class Preferences(dialog.Utility): |
755 |
e5681e842641
|
"A preference dialog" |
756 |
e5681e842641
|
|
757 |
e5681e842641
|
def __init__(self, parent, cfg): |
758 |
e5681e842641
|
dialog.Utility.__init__(self, parent, "Preferences") |
759 |
e5681e842641
|
self.config = cfg |
760 |
e5681e842641
|
self.set_modal(False) |
761 |
e5681e842641
|
|
762 |
e5681e842641
|
self.notebook = ui.Notebook() |
763 |
e5681e842641
|
self.vbox.pack_start(self.notebook) |
764 |
e5681e842641
|
|
765 |
e5681e842641
|
self.page_general = self.notebook.create_page("General") |
766 |
e5681e842641
|
self.__init_section_file(self.page_general) |
767 |
e5681e842641
|
self.__init_section_menuaction(self.page_general) |
768 |
e5681e842641
|
self.__init_section_misc(self.page_general) |
769 |
e5681e842641
|
|
770 |
e5681e842641
|
self.page_goto = self.notebook.create_page("Goto Commands") |
771 |
e5681e842641
|
self.__init_section_gotocmd(self.page_goto) |
772 |
e5681e842641
|
|
773 |
e5681e842641
|
self.connect("response", lambda w,d: self.destroy()) |
774 |
e5681e842641
|
|
775 |
e5681e842641
|
|
776 |
e5681e842641
|
def __init_section_file(self, page): |
777 |
e5681e842641
|
"Sets up a file section in a page" |
778 |
e5681e842641
|
|
779 |
e5681e842641
|
self.section_file = page.add_section("File Handling") |
780 |
e5681e842641
|
|
781 |
e5681e842641
|
# entry for file |
782 |
e5681e842641
|
self.entry_file = ui.FileEntry("Select File to Use") |
783 |
e5681e842641
|
ui.config_bind(self.config, "file", self.entry_file) |
784 |
167384ef2e8d
|
|
785 |
167384ef2e8d
|
eventbox = ui.EventBox(self.entry_file) |
786 |
167384ef2e8d
|
self.tooltips.set_tip(eventbox, "The data file to search for accounts in") |
787 |
167384ef2e8d
|
self.section_file.append_widget("File to use", eventbox) |
788 |
e5681e842641
|
|
789 |
e5681e842641
|
# check-button for autolock |
790 |
e5681e842641
|
self.check_autolock = ui.CheckButton("Lock file when inactive for") |
791 |
e5681e842641
|
ui.config_bind(self.config, "autolock", self.check_autolock) |
792 |
e5681e842641
|
self.check_autolock.connect("toggled", lambda w: self.spin_autolock_timeout.set_sensitive(w.get_active())) |
793 |
e5681e842641
|
self.tooltips.set_tip(self.check_autolock, "Automatically lock the file after a period of inactivity") |
794 |
e5681e842641
|
|
795 |
e5681e842641
|
# spin-entry for autolock-timeout |
796 |
e5681e842641
|
self.spin_autolock_timeout = ui.SpinEntry() |
797 |
e5681e842641
|
self.spin_autolock_timeout.set_range(1, 120) |
798 |
e5681e842641
|
self.spin_autolock_timeout.set_sensitive(self.check_autolock.get_active()) |
799 |
e5681e842641
|
ui.config_bind(self.config, "autolock_timeout", self.spin_autolock_timeout) |
800 |
e5681e842641
|
self.tooltips.set_tip(self.spin_autolock_timeout, "The period of inactivity before locking the file, in minutes") |
801 |
e5681e842641
|
|
802 |
e5681e842641
|
# container for autolock-widgets |
803 |
e5681e842641
|
hbox = ui.HBox() |
804 |
e5681e842641
|
hbox.set_spacing(3) |
805 |
e5681e842641
|
hbox.pack_start(self.check_autolock) |
806 |
e5681e842641
|
hbox.pack_start(self.spin_autolock_timeout) |
807 |
e5681e842641
|
hbox.pack_start(ui.Label("minutes")) |
808 |
e5681e842641
|
self.section_file.append_widget(None, hbox) |
809 |
e5681e842641
|
|
810 |
e5681e842641
|
|
811 |
e5681e842641
|
def __init_section_gotocmd(self, page): |
812 |
e5681e842641
|
"Sets up the goto command section" |
813 |
e5681e842641
|
|
814 |
e5681e842641
|
self.section_goto = page.add_section("Goto Commands") |
815 |
e5681e842641
|
|
816 |
e5681e842641
|
for entrytype in entry.ENTRYLIST: |
817 |
e5681e842641
|
if entrytype == entry.FolderEntry: |
818 |
e5681e842641
|
continue |
819 |
e5681e842641
|
|
820 |
e5681e842641
|
e = entrytype() |
821 |
e5681e842641
|
|
822 |
e5681e842641
|
widget = ui.Entry() |
823 |
e5681e842641
|
ui.config_bind(self.config, "/apps/revelation/launcher/%s" % e.id, widget) |
824 |
e5681e842641
|
|
825 |
e5681e842641
|
tooltip = "Goto command for %s accounts. The following expansion variables can be used:\n\n" % e.typename |
826 |
e5681e842641
|
|
827 |
e5681e842641
|
for field in e.fields: |
828 |
e5681e842641
|
tooltip += "%%%s: %s\n" % ( field.symbol, field.name ) |
829 |
e5681e842641
|
|
830 |
e5681e842641
|
tooltip += "\n" |
831 |
e5681e842641
|
tooltip += "%%: a % sign\n" |
832 |
e5681e842641
|
tooltip += "%?x: optional expansion variable\n" |
833 |
e5681e842641
|
tooltip += "%(...%): optional substring expansion" |
834 |
e5681e842641
|
|
835 |
e5681e842641
|
self.tooltips.set_tip(widget, tooltip) |
836 |
e5681e842641
|
self.section_goto.append_widget(e.typename, widget) |
837 |
e5681e842641
|
|
838 |
e5681e842641
|
|
839 |
e5681e842641
|
def __init_section_menuaction(self, page): |
840 |
e5681e842641
|
"Sets up a menuaction section in a page" |
841 |
e5681e842641
|
|
842 |
e5681e842641
|
self.section_menuaction = page.add_section("Menu Action") |
843 |
e5681e842641
|
|
844 |
e5681e842641
|
# radio-button for show |
845 |
1ea461278a52
|
self.radio_show = ui.RadioButton(None, "Display account info") |
846 |
e5681e842641
|
ui.config_bind(self.config, "menuaction", self.radio_show, "show") |
847 |
e5681e842641
|
|
848 |
e5681e842641
|
self.tooltips.set_tip(self.radio_show, "Display the account information") |
849 |
e5681e842641
|
self.section_menuaction.append_widget(None, self.radio_show) |
850 |
e5681e842641
|
|
851 |
e5681e842641
|
# radio-button for goto |
852 |
e5681e842641
|
self.radio_goto = ui.RadioButton(self.radio_show, "Go to account, if possible") |
853 |
e5681e842641
|
ui.config_bind(self.config, "menuaction", self.radio_goto, "goto") |
854 |
e5681e842641
|
|
855 |
e5681e842641
|
self.tooltips.set_tip(self.radio_goto, "Open the account in an external application if possible, otherwise display it") |
856 |
e5681e842641
|
self.section_menuaction.append_widget(None, self.radio_goto) |
857 |
e5681e842641
|
|
858 |
e5681e842641
|
# radio-button for copy username/password |
859 |
e5681e842641
|
self.radio_copy = ui.RadioButton(self.radio_show, "Copy password to clipboard") |
860 |
e5681e842641
|
ui.config_bind(self.config, "menuaction", self.radio_copy, "copy") |
861 |
e5681e842641
|
|
862 |
1ea461278a52
|
self.tooltips.set_tip(self.radio_copy, "Copy the account password to the clipboard") |
863 |
e5681e842641
|
self.section_menuaction.append_widget(None, self.radio_copy) |
864 |
e5681e842641
|
|
865 |
e5681e842641
|
|
866 |
e5681e842641
|
def __init_section_misc(self, page): |
867 |
e5681e842641
|
"Sets up the misc section" |
868 |
e5681e842641
|
|
869 |
e5681e842641
|
self.section_misc = page.add_section("Miscellaneous") |
870 |
e5681e842641
|
|
871 |
693abd3fbd04
|
# show searchentry checkbutton |
872 |
693abd3fbd04
|
self.check_show_searchentry = ui.CheckButton("Show search entry") |
873 |
693abd3fbd04
|
ui.config_bind(self.config, "show_searchentry", self.check_show_searchentry) |
874 |
693abd3fbd04
|
|
875 |
693abd3fbd04
|
self.tooltips.set_tip(self.check_show_searchentry, "Display an entry box in the applet for searching") |
876 |
693abd3fbd04
|
self.section_misc.append_widget(None, self.check_show_searchentry) |
877 |
693abd3fbd04
|
|
878 |
c6e069377f71
|
# show passwords checkbutton |
879 |
c6e069377f71
|
self.check_show_passwords = ui.CheckButton("Show passwords and other secrets") |
880 |
c6e069377f71
|
ui.config_bind(self.config, "show_passwords", self.check_show_passwords) |
881 |
c6e069377f71
|
|
882 |
c6e069377f71
|
self.tooltips.set_tip(self.check_show_passwords, "Display passwords and other secrets, such as PIN codes (otherwise, hide with ******)") |
883 |
c6e069377f71
|
self.section_misc.append_widget(None, self.check_show_passwords) |
884 |
c6e069377f71
|
|
885 |
e5681e842641
|
# check-button for username |
886 |
e5681e842641
|
self.check_chain_username = ui.CheckButton("Also copy username when copying password") |
887 |
e5681e842641
|
ui.config_bind(self.config, "chain_username", self.check_chain_username) |
888 |
e5681e842641
|
|
889 |
e5681e842641
|
self.tooltips.set_tip(self.check_chain_username, "When the password is copied to clipboard, put the username before the password as a clipboard \"chain\"") |
890 |
e5681e842641
|
self.section_misc.append_widget(None, self.check_chain_username) |
891 |
e5681e842641
|
|
892 |
e5681e842641
|
|
893 |
e5681e842641
|
def run(self): |
894 |
e5681e842641
|
"Runs the dialog" |
895 |
e5681e842641
|
|
896 |
e5681e842641
|
self.show_all() |
897 |
e5681e842641
|
|
898 |
e5681e842641
|
|
899 |
e5681e842641
|
|
900 |
e5681e842641
|
def factory(applet, iid): |
901 |
e5681e842641
|
"Applet factory function" |
902 |
e5681e842641
|
|
903 |
e5681e842641
|
RevelationApplet(applet, iid) |
904 |
e5681e842641
|
|
905 |
e5681e842641
|
return True |
906 |
e5681e842641
|
|
907 |
e5681e842641
|
|
908 |
e5681e842641
|
|
909 |
e5681e842641
|
if __name__ == "__main__": |
910 |
e5681e842641
|
gnome.init(config.APPNAME, config.VERSION) |
911 |
e5681e842641
|
|
912 |
0f8c7965fdf3
|
gnomeapplet.bonobo_factory( |
913 |
e5681e842641
|
"OAFIID:GNOME_RevelationApplet_Factory", |
914 |
0f8c7965fdf3
|
gnomeapplet.Applet.__gtype__, |
915 |
e5681e842641
|
config.APPNAME, config.VERSION, factory |
916 |
e5681e842641
|
) |
917 |
e5681e842641
|