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