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
- ba434a17e651
- parent
- e6bf198935fe
- branch
- default
- tags
- revelation-0.4.6
set release dates
1 |
e5681e842641
|
#!/usr/bin/env python |
2 |
e5681e842641
|
|
3 |
e5681e842641
|
# |
4 |
8bcba268295a
|
# Revelation 0.4.6 - 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 |
3083465f003a
|
self.file_open(self.datafile.get_file(), self.datafile.get_password()) |
268 |
3083465f003a
|
|
269 |
3083465f003a
|
except datahandler.PasswordError: |
270 |
3083465f003a
|
self.file_close() |
271 |
3083465f003a
|
|
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 |
6994b7f39b79
|
def __flash_entry(self, color = "#ffbaba", duration = 500): |
345 |
6994b7f39b79
|
"Flashes the entry with a color" |
346 |
e5681e842641
|
|
347 |
6994b7f39b79
|
color_normal = ui.Entry().rc_get_style().base[gtk.STATE_NORMAL] |
348 |
6994b7f39b79
|
color_new = gtk.gdk.color_parse(color) |
349 |
e5681e842641
|
|
350 |
6994b7f39b79
|
self.entry.modify_base(gtk.STATE_NORMAL, color_new) |
351 |
6994b7f39b79
|
gobject.timeout_add(duration, lambda: self.entry.modify_base(gtk.STATE_NORMAL, color_normal)) |
352 |
e5681e842641
|
|
353 |
e5681e842641
|
|
354 |
6994b7f39b79
|
def __focus_entry(self): |
355 |
6994b7f39b79
|
"Gives focus to the entry" |
356 |
e5681e842641
|
|
357 |
ca2af4e02c4c
|
if hasattr(gnomemisc, "panel_applet_request_focus") == True: |
358 |
ca2af4e02c4c
|
gnomemisc.panel_applet_request_focus(self.applet, long(0)) |
359 |
e5681e842641
|
|
360 |
e5681e842641
|
|
361 |
6994b7f39b79
|
def __generate_entrymenu(self, entrystore, parent = None): |
362 |
6994b7f39b79
|
"Generates an entry menu tree" |
363 |
e5681e842641
|
|
364 |
6994b7f39b79
|
menu = gtk.Menu() |
365 |
6994b7f39b79
|
|
366 |
6994b7f39b79
|
for i in range(entrystore.iter_n_children(parent)): |
367 |
6994b7f39b79
|
iter = entrystore.iter_nth_child(parent, i) |
368 |
6994b7f39b79
|
|
369 |
6994b7f39b79
|
e = entrystore.get_entry(iter) |
370 |
6994b7f39b79
|
item = ui.ImageMenuItem(type(e) == entry.FolderEntry and ui.STOCK_FOLDER or e.icon, e.name) |
371 |
6994b7f39b79
|
item.connect("select", lambda w,d=None: self.locktimer.reset()) |
372 |
6994b7f39b79
|
|
373 |
6994b7f39b79
|
if type(e) == entry.FolderEntry: |
374 |
6994b7f39b79
|
item.set_submenu(self.__generate_entrymenu(entrystore, iter)) |
375 |
6994b7f39b79
|
|
376 |
6994b7f39b79
|
else: |
377 |
6994b7f39b79
|
item.connect("activate", self.__cb_popup_activate, e) |
378 |
6994b7f39b79
|
|
379 |
6994b7f39b79
|
menu.append(item) |
380 |
6994b7f39b79
|
|
381 |
6994b7f39b79
|
return menu |
382 |
6994b7f39b79
|
|
383 |
6994b7f39b79
|
|
384 |
6994b7f39b79
|
def __get_launcher(self, e): |
385 |
e5681e842641
|
"Returns a launcher command for an entry, if possible" |
386 |
e5681e842641
|
|
387 |
e5681e842641
|
command = self.config.get("/apps/revelation/launcher/%s" % e.id) |
388 |
e5681e842641
|
|
389 |
e5681e842641
|
if command in ( "", None ): |
390 |
e5681e842641
|
return None |
391 |
e5681e842641
|
|
392 |
e5681e842641
|
subst = {} |
393 |
e5681e842641
|
for field in e.fields: |
394 |
e5681e842641
|
subst[field.symbol] = field.value |
395 |
e5681e842641
|
|
396 |
e5681e842641
|
command = util.parse_subst(command, subst) |
397 |
e5681e842641
|
|
398 |
e5681e842641
|
return command |
399 |
e5681e842641
|
|
400 |
e5681e842641
|
|
401 |
6994b7f39b79
|
def __get_popup_offset(self, popup): |
402 |
e5681e842641
|
"Returns a tuple of x and y offset coords for popups" |
403 |
e5681e842641
|
|
404 |
e5681e842641
|
screen = self.applet.get_screen() |
405 |
e5681e842641
|
a = self.applet.get_allocation() |
406 |
e5681e842641
|
rw, rh = popup.size_request() |
407 |
e5681e842641
|
|
408 |
e5681e842641
|
x, y = self.applet.window.get_origin() |
409 |
e5681e842641
|
x += a.x |
410 |
e5681e842641
|
y += a.y |
411 |
e5681e842641
|
|
412 |
e5681e842641
|
|
413 |
e5681e842641
|
# TODO use constants ORIENT_UP etc here, if available |
414 |
e5681e842641
|
if self.applet.get_orient() in ( 0, 1 ): |
415 |
e5681e842641
|
x = min(x, screen.get_width() - rw) |
416 |
e5681e842641
|
|
417 |
e5681e842641
|
if (y > screen.get_height() / 2): |
418 |
e5681e842641
|
y -= rh |
419 |
e5681e842641
|
|
420 |
e5681e842641
|
else: |
421 |
e5681e842641
|
y += a.height |
422 |
e5681e842641
|
|
423 |
e5681e842641
|
else: |
424 |
e5681e842641
|
y = min(y, screen.get_height() - rh) |
425 |
e5681e842641
|
|
426 |
e5681e842641
|
if (x > screen.get_width() / 2): |
427 |
e5681e842641
|
x -= rw |
428 |
e5681e842641
|
|
429 |
e5681e842641
|
else: |
430 |
e5681e842641
|
x += a.width |
431 |
e5681e842641
|
|
432 |
e5681e842641
|
return x, y |
433 |
e5681e842641
|
|
434 |
e5681e842641
|
|
435 |
6994b7f39b79
|
def __launcher_valid(self, e): |
436 |
6994b7f39b79
|
"Checks if a launcher is valid" |
437 |
e5681e842641
|
|
438 |
6994b7f39b79
|
try: |
439 |
6994b7f39b79
|
command = self.__get_launcher(e) |
440 |
6994b7f39b79
|
|
441 |
6994b7f39b79
|
return command != None |
442 |
6994b7f39b79
|
|
443 |
6994b7f39b79
|
except ( util.SubstFormatError ): |
444 |
6994b7f39b79
|
return True |
445 |
6994b7f39b79
|
|
446 |
6994b7f39b79
|
except ( util.SubstValueError, config.ConfigError ): |
447 |
6994b7f39b79
|
return False |
448 |
6994b7f39b79
|
|
449 |
6994b7f39b79
|
|
450 |
6994b7f39b79
|
def __require_file(self): |
451 |
6994b7f39b79
|
"Checks if a datafile is loaded, or alerts the user" |
452 |
6994b7f39b79
|
|
453 |
6994b7f39b79
|
if self.datafile.get_file() != None: |
454 |
6994b7f39b79
|
return True |
455 |
6994b7f39b79
|
|
456 |
6994b7f39b79
|
if self.config.get("file") != "": |
457 |
6994b7f39b79
|
return self.file_open(self.config.get("file")) |
458 |
6994b7f39b79
|
|
459 |
6994b7f39b79
|
d = dialog.Info( |
460 |
6994b7f39b79
|
None, "File not selected", |
461 |
6994b7f39b79
|
"You must select a Revelation data file to use - this can be done in the applet preferences.", |
462 |
6994b7f39b79
|
( ( gtk.STOCK_PREFERENCES, gtk.RESPONSE_ACCEPT ), ( gtk.STOCK_OK, gtk.RESPONSE_OK ) ) |
463 |
6994b7f39b79
|
) |
464 |
6994b7f39b79
|
|
465 |
6994b7f39b79
|
|
466 |
6994b7f39b79
|
if d.run() == gtk.RESPONSE_ACCEPT: |
467 |
6994b7f39b79
|
self.prefs() |
468 |
6994b7f39b79
|
|
469 |
6994b7f39b79
|
return False |
470 |
6994b7f39b79
|
|
471 |
6994b7f39b79
|
|
472 |
6994b7f39b79
|
##### PUBLIC METHODS ##### |
473 |
6994b7f39b79
|
|
474 |
6994b7f39b79
|
def about(self): |
475 |
6994b7f39b79
|
"Displays an about dialog" |
476 |
6994b7f39b79
|
|
477 |
dce7200431f0
|
dialog.run_unique(About, self.applet) |
478 |
6994b7f39b79
|
|
479 |
6994b7f39b79
|
|
480 |
6994b7f39b79
|
def entry_copychain(self, e, launcher = ""): |
481 |
6994b7f39b79
|
"Copies all passwords from an entry as a chain" |
482 |
6994b7f39b79
|
|
483 |
6994b7f39b79
|
if e == None: |
484 |
6994b7f39b79
|
return |
485 |
6994b7f39b79
|
|
486 |
6994b7f39b79
|
secrets = [ field.value for field in e.fields if field.datatype == entry.DATATYPE_PASSWORD and field.value != "" ] |
487 |
6994b7f39b79
|
|
488 |
6994b7f39b79
|
if self.config.get("chain_username") == True and len(secrets) > 0: |
489 |
6994b7f39b79
|
if e.has_field(entry.UsernameField) and e[entry.UsernameField] != "": |
490 |
6994b7f39b79
|
if "%" + entry.UsernameField.symbol not in launcher: |
491 |
6994b7f39b79
|
secrets.insert(0, e[entry.UsernameField]) |
492 |
6994b7f39b79
|
|
493 |
6994b7f39b79
|
self.clipboard.set(secrets) |
494 |
6994b7f39b79
|
|
495 |
6994b7f39b79
|
|
496 |
6994b7f39b79
|
def entry_goto(self, e): |
497 |
6994b7f39b79
|
"Goes to an entry" |
498 |
6994b7f39b79
|
|
499 |
6994b7f39b79
|
try: |
500 |
6994b7f39b79
|
command = self.__get_launcher(e) |
501 |
6994b7f39b79
|
|
502 |
6994b7f39b79
|
if command == None: |
503 |
6994b7f39b79
|
return |
504 |
6994b7f39b79
|
|
505 |
6994b7f39b79
|
self.entry_copychain(e) |
506 |
6994b7f39b79
|
|
507 |
6994b7f39b79
|
util.execute_child(command) |
508 |
6994b7f39b79
|
|
509 |
6994b7f39b79
|
except ( util.SubstFormatError, config.ConfigError ): |
510 |
6994b7f39b79
|
dialog.Error(None, "Invalid goto command format", "The goto command for '" + e.typename + "' entries is invalid, please correct this in the preferences.").run() |
511 |
6994b7f39b79
|
|
512 |
6994b7f39b79
|
except util.SubstValueError: |
513 |
6994b7f39b79
|
self.entry_show(e) |
514 |
6994b7f39b79
|
|
515 |
6994b7f39b79
|
|
516 |
6994b7f39b79
|
def entry_menu(self, time = None): |
517 |
6994b7f39b79
|
"Displays the entry menu" |
518 |
6994b7f39b79
|
|
519 |
6994b7f39b79
|
self.__close_popups() |
520 |
6994b7f39b79
|
|
521 |
6994b7f39b79
|
if self.__require_file() == False: |
522 |
6994b7f39b79
|
return |
523 |
6994b7f39b79
|
|
524 |
6994b7f39b79
|
if self.entrymenu == None: |
525 |
6994b7f39b79
|
return |
526 |
6994b7f39b79
|
|
527 |
6994b7f39b79
|
x, y = self.__get_popup_offset(self.entrymenu) |
528 |
6994b7f39b79
|
|
529 |
6994b7f39b79
|
self.entrymenu.show_all() |
530 |
6994b7f39b79
|
self.entrymenu.popup(None, None, lambda d: (x, y, False), 1, time) |
531 |
6994b7f39b79
|
|
532 |
6994b7f39b79
|
|
533 |
55c3f554d446
|
def entry_search(self, term, focusafter = False): |
534 |
6994b7f39b79
|
"Searches for an entry" |
535 |
6994b7f39b79
|
|
536 |
6994b7f39b79
|
self.__close_popups() |
537 |
6994b7f39b79
|
|
538 |
6994b7f39b79
|
if term.strip() == "": |
539 |
6994b7f39b79
|
return |
540 |
6994b7f39b79
|
|
541 |
6994b7f39b79
|
if self.__require_file() == False: |
542 |
6994b7f39b79
|
return |
543 |
6994b7f39b79
|
|
544 |
6994b7f39b79
|
|
545 |
6994b7f39b79
|
matches = [ self.entrystore.get_entry(iter) for iter in self.entrysearch.find_all(term) ] |
546 |
6994b7f39b79
|
|
547 |
6994b7f39b79
|
if len(matches) == 0: |
548 |
13ec1163cf9f
|
self.__focus_entry() |
549 |
6994b7f39b79
|
self.__flash_entry() |
550 |
5a07753dbd3c
|
self.entry.select_region(0, -1) |
551 |
6994b7f39b79
|
|
552 |
6994b7f39b79
|
elif len(matches) == 1: |
553 |
55c3f554d446
|
self.entry_show(matches[0], True) |
554 |
6994b7f39b79
|
|
555 |
6994b7f39b79
|
else: |
556 |
6994b7f39b79
|
self.popup_entrylist = EntryListPopup(matches) |
557 |
55c3f554d446
|
self.popup_entrylist.connect("entry-chosen", lambda w,e: self.entry_show(e, focusafter)) |
558 |
55c3f554d446
|
|
559 |
55c3f554d446
|
if focusafter == True: |
560 |
55c3f554d446
|
self.popup_entrylist.connect("closed", lambda w: self.__focus_entry()) |
561 |
6994b7f39b79
|
|
562 |
6994b7f39b79
|
self.popup_entrylist.realize() |
563 |
6994b7f39b79
|
x, y = self.__get_popup_offset(self.popup_entrylist) |
564 |
6994b7f39b79
|
self.popup_entrylist.show(x, y) |
565 |
6994b7f39b79
|
|
566 |
6994b7f39b79
|
|
567 |
55c3f554d446
|
def entry_show(self, e, focusafter = False): |
568 |
6994b7f39b79
|
"Shows an entry" |
569 |
6994b7f39b79
|
|
570 |
6994b7f39b79
|
self.__close_popups() |
571 |
6994b7f39b79
|
|
572 |
6994b7f39b79
|
self.popup_entryview = EntryViewPopup(e, self.config, self.clipboard) |
573 |
55c3f554d446
|
|
574 |
55c3f554d446
|
if focusafter == True: |
575 |
55c3f554d446
|
self.popup_entryview.connect("closed", lambda w: self.__focus_entry()) |
576 |
6994b7f39b79
|
|
577 |
6994b7f39b79
|
def cb_goto(widget): |
578 |
6994b7f39b79
|
if self.__launcher_valid(e): |
579 |
6994b7f39b79
|
self.entry_goto(e) |
580 |
6994b7f39b79
|
|
581 |
bd0aa2a05f0f
|
self.popup_entryview.close() |
582 |
6994b7f39b79
|
|
583 |
6994b7f39b79
|
self.popup_entryview.button_goto.connect("clicked", cb_goto) |
584 |
6994b7f39b79
|
self.popup_entryview.button_goto.set_sensitive(self.__launcher_valid(e)) |
585 |
6994b7f39b79
|
|
586 |
6994b7f39b79
|
self.popup_entryview.realize() |
587 |
6994b7f39b79
|
x, y = self.__get_popup_offset(self.popup_entryview) |
588 |
6994b7f39b79
|
self.popup_entryview.show(x, y) |
589 |
6994b7f39b79
|
|
590 |
6994b7f39b79
|
|
591 |
6994b7f39b79
|
def file_close(self): |
592 |
6994b7f39b79
|
"Closes the current data file" |
593 |
6994b7f39b79
|
|
594 |
6994b7f39b79
|
self.__close_popups() |
595 |
6994b7f39b79
|
self.locktimer.stop() |
596 |
6994b7f39b79
|
|
597 |
6994b7f39b79
|
self.datafile.close() |
598 |
6994b7f39b79
|
self.entrystore.clear() |
599 |
6994b7f39b79
|
self.entrymenu = None |
600 |
6994b7f39b79
|
|
601 |
6994b7f39b79
|
|
602 |
6994b7f39b79
|
def file_open(self, file, password = None): |
603 |
6994b7f39b79
|
"Opens a data file" |
604 |
6994b7f39b79
|
|
605 |
6994b7f39b79
|
try: |
606 |
6994b7f39b79
|
if file in ( "", None ): |
607 |
6994b7f39b79
|
return False |
608 |
6994b7f39b79
|
|
609 |
dce7200431f0
|
if dialog.present_unique(dialog.PasswordOpen) == True: |
610 |
6994b7f39b79
|
return False |
611 |
6994b7f39b79
|
|
612 |
dce7200431f0
|
entrystore = self.datafile.load(file, password, lambda: dialog.run_unique(dialog.PasswordOpen, None, os.path.basename(file))) |
613 |
6994b7f39b79
|
|
614 |
6994b7f39b79
|
self.entrystore.clear() |
615 |
6994b7f39b79
|
self.entrystore.import_entry(entrystore, None) |
616 |
6994b7f39b79
|
|
617 |
6994b7f39b79
|
self.entrymenu = self.__generate_entrymenu(self.entrystore) |
618 |
6994b7f39b79
|
self.locktimer.start(self.config.get("autolock_timeout") * 60) |
619 |
6994b7f39b79
|
|
620 |
6994b7f39b79
|
self.__close_popups() |
621 |
6994b7f39b79
|
|
622 |
6994b7f39b79
|
return True |
623 |
6994b7f39b79
|
|
624 |
6994b7f39b79
|
except dialog.CancelError: |
625 |
6994b7f39b79
|
pass |
626 |
6994b7f39b79
|
|
627 |
6994b7f39b79
|
except datahandler.FormatError: |
628 |
6994b7f39b79
|
dialog.Error(None, "Invalid file format", "The file '%s' contains invalid data." % file).run() |
629 |
6994b7f39b79
|
|
630 |
6994b7f39b79
|
except ( datahandler.DataError, entry.EntryTypeError, entry.EntryFieldError ): |
631 |
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() |
632 |
6994b7f39b79
|
|
633 |
6994b7f39b79
|
except datahandler.PasswordError: |
634 |
6994b7f39b79
|
dialog.Error(None, "Incorrect password", "You entered an incorrect password for the file '%s', please try again." % file).run() |
635 |
6994b7f39b79
|
self.file_open(file, None) |
636 |
6994b7f39b79
|
|
637 |
6994b7f39b79
|
except datahandler.VersionError: |
638 |
6994b7f39b79
|
dialog.Error(None, "Unknown data version", "The file '%s' has a future version number, please upgrade Revelation to open it." % file).run() |
639 |
6994b7f39b79
|
|
640 |
6994b7f39b79
|
except IOError: |
641 |
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() |
642 |
6994b7f39b79
|
|
643 |
6994b7f39b79
|
return False |
644 |
6994b7f39b79
|
|
645 |
6994b7f39b79
|
|
646 |
6994b7f39b79
|
def file_reload(self): |
647 |
6994b7f39b79
|
"Reloads the current data file" |
648 |
6994b7f39b79
|
|
649 |
6994b7f39b79
|
if self.datafile.get_file() == None: |
650 |
6994b7f39b79
|
return |
651 |
6994b7f39b79
|
|
652 |
6994b7f39b79
|
self.file_open(self.datafile.get_file(), self.datafile.get_password()) |
653 |
e5681e842641
|
|
654 |
e5681e842641
|
|
655 |
e5681e842641
|
def prefs(self): |
656 |
e5681e842641
|
"Displays the preference dialog" |
657 |
e5681e842641
|
|
658 |
dce7200431f0
|
dialog.run_unique(Preferences, None, self.config) |
659 |
e5681e842641
|
|
660 |
e5681e842641
|
|
661 |
e5681e842641
|
|
662 |
e5681e842641
|
class About(gnome.ui.About): |
663 |
e5681e842641
|
"About dialog" |
664 |
e5681e842641
|
|
665 |
e5681e842641
|
def __init__(self, parent): |
666 |
e5681e842641
|
gnome.ui.About.__init__( |
667 |
9295ddb2c689
|
self, "Revelation Account Search", config.VERSION, config.COPYRIGHT, |
668 |
9295ddb2c689
|
""""%s"\n\nAn applet for searching and browsing a Revelation account database""" % config.RELNAME, |
669 |
e5681e842641
|
( config.AUTHOR, ), None, "", |
670 |
e5681e842641
|
parent.render_icon(ui.STOCK_REVELATION, gtk.ICON_SIZE_DIALOG) |
671 |
e5681e842641
|
) |
672 |
e5681e842641
|
|
673 |
e5681e842641
|
|
674 |
e5681e842641
|
def run(self): |
675 |
e5681e842641
|
"Displays the dialog" |
676 |
e5681e842641
|
|
677 |
e5681e842641
|
self.show_all() |
678 |
e5681e842641
|
|
679 |
e5681e842641
|
|
680 |
e5681e842641
|
|
681 |
3083465f003a
|
class EntryListPopup(dialog.Popup): |
682 |
3083465f003a
|
"A popup for displaying a list of entries" |
683 |
3083465f003a
|
|
684 |
3083465f003a
|
def __init__(self, entries): |
685 |
3083465f003a
|
dialog.Popup.__init__(self) |
686 |
167384ef2e8d
|
self.set_default_size(225, 200) |
687 |
3083465f003a
|
|
688 |
3083465f003a
|
self.entrystore = data.EntryStore() |
689 |
3083465f003a
|
self.entrystore.set_sort_column_id(0, gtk.SORT_ASCENDING) |
690 |
3083465f003a
|
|
691 |
3083465f003a
|
for e in entries: |
692 |
3083465f003a
|
self.entrystore.add_entry(e) |
693 |
3083465f003a
|
|
694 |
3083465f003a
|
self.treeview = ui.EntryTree(self.entrystore) |
695 |
3083465f003a
|
self.treeview.set_cursor((0,)) |
696 |
6994b7f39b79
|
self.treeview.connect("row-activated", self.__cb_row_activated) |
697 |
3083465f003a
|
|
698 |
3083465f003a
|
self.scrolledwindow = ui.ScrolledWindow(self.treeview) |
699 |
167384ef2e8d
|
self.scrolledwindow.set_policy(gtk.POLICY_NEVER, gtk.POLICY_AUTOMATIC) |
700 |
3083465f003a
|
self.add(self.scrolledwindow) |
701 |
3083465f003a
|
|
702 |
3083465f003a
|
|
703 |
6994b7f39b79
|
def __cb_row_activated(self, widget, path, data = None): |
704 |
6994b7f39b79
|
"Callback for tree row activation" |
705 |
6994b7f39b79
|
|
706 |
6994b7f39b79
|
iter = self.entrystore.get_iter(path) |
707 |
6994b7f39b79
|
e = self.entrystore.get_entry(iter) |
708 |
6994b7f39b79
|
|
709 |
6994b7f39b79
|
self.emit("entry-chosen", e) |
710 |
bd0aa2a05f0f
|
self.close() |
711 |
6994b7f39b79
|
|
712 |
167384ef2e8d
|
|
713 |
6994b7f39b79
|
gobject.signal_new("entry-chosen", EntryListPopup, gobject.SIGNAL_ACTION, gobject.TYPE_BOOLEAN, ( gobject.TYPE_PYOBJECT, )) |
714 |
6994b7f39b79
|
|
715 |
6994b7f39b79
|
|
716 |
3083465f003a
|
|
717 |
3083465f003a
|
class EntryViewPopup(dialog.Popup): |
718 |
3083465f003a
|
"A popup for displaying an entry" |
719 |
3083465f003a
|
|
720 |
3083465f003a
|
def __init__(self, e, cfg = None, clipboard = None): |
721 |
3083465f003a
|
dialog.Popup.__init__(self) |
722 |
3083465f003a
|
self.set_title(e.name) |
723 |
3083465f003a
|
|
724 |
3083465f003a
|
self.entryview = ui.EntryView(cfg, clipboard) |
725 |
3083465f003a
|
self.entryview.set_border_width(0) |
726 |
3083465f003a
|
self.entryview.display_entry(e) |
727 |
3083465f003a
|
|
728 |
bd0aa2a05f0f
|
self.button_close = ui.Button(gtk.STOCK_CLOSE, lambda w: self.close()) |
729 |
3083465f003a
|
self.button_goto = ui.Button(ui.STOCK_GOTO) |
730 |
3083465f003a
|
self.buttonbox = ui.HButtonBox(self.button_goto, self.button_close) |
731 |
3083465f003a
|
|
732 |
3083465f003a
|
self.vbox = ui.VBox(self.entryview, self.buttonbox) |
733 |
3083465f003a
|
self.vbox.set_border_width(12) |
734 |
3083465f003a
|
self.vbox.set_spacing(15) |
735 |
3083465f003a
|
|
736 |
3083465f003a
|
self.add(self.vbox) |
737 |
3083465f003a
|
|
738 |
3083465f003a
|
self.connect("show", lambda w: self.button_close.grab_focus()) |
739 |
3083465f003a
|
|
740 |
3083465f003a
|
|
741 |
3083465f003a
|
|
742 |
e5681e842641
|
class Preferences(dialog.Utility): |
743 |
e5681e842641
|
"A preference dialog" |
744 |
e5681e842641
|
|
745 |
e5681e842641
|
def __init__(self, parent, cfg): |
746 |
e5681e842641
|
dialog.Utility.__init__(self, parent, "Preferences") |
747 |
e5681e842641
|
self.config = cfg |
748 |
e5681e842641
|
self.set_modal(False) |
749 |
e5681e842641
|
|
750 |
e5681e842641
|
self.notebook = ui.Notebook() |
751 |
e5681e842641
|
self.vbox.pack_start(self.notebook) |
752 |
e5681e842641
|
|
753 |
e5681e842641
|
self.page_general = self.notebook.create_page("General") |
754 |
e5681e842641
|
self.__init_section_file(self.page_general) |
755 |
e5681e842641
|
self.__init_section_menuaction(self.page_general) |
756 |
e5681e842641
|
self.__init_section_misc(self.page_general) |
757 |
e5681e842641
|
|
758 |
e5681e842641
|
self.page_goto = self.notebook.create_page("Goto Commands") |
759 |
e5681e842641
|
self.__init_section_gotocmd(self.page_goto) |
760 |
e5681e842641
|
|
761 |
e5681e842641
|
self.connect("response", lambda w,d: self.destroy()) |
762 |
e5681e842641
|
|
763 |
e5681e842641
|
|
764 |
e5681e842641
|
def __init_section_file(self, page): |
765 |
e5681e842641
|
"Sets up a file section in a page" |
766 |
e5681e842641
|
|
767 |
e5681e842641
|
self.section_file = page.add_section("File Handling") |
768 |
e5681e842641
|
|
769 |
e5681e842641
|
# entry for file |
770 |
e5681e842641
|
self.entry_file = ui.FileEntry("Select File to Use") |
771 |
e5681e842641
|
ui.config_bind(self.config, "file", self.entry_file) |
772 |
167384ef2e8d
|
|
773 |
167384ef2e8d
|
eventbox = ui.EventBox(self.entry_file) |
774 |
167384ef2e8d
|
self.tooltips.set_tip(eventbox, "The data file to search for accounts in") |
775 |
167384ef2e8d
|
self.section_file.append_widget("File to use", eventbox) |
776 |
e5681e842641
|
|
777 |
e5681e842641
|
# check-button for autolock |
778 |
e5681e842641
|
self.check_autolock = ui.CheckButton("Lock file when inactive for") |
779 |
e5681e842641
|
ui.config_bind(self.config, "autolock", self.check_autolock) |
780 |
e5681e842641
|
self.check_autolock.connect("toggled", lambda w: self.spin_autolock_timeout.set_sensitive(w.get_active())) |
781 |
e5681e842641
|
self.tooltips.set_tip(self.check_autolock, "Automatically lock the file after a period of inactivity") |
782 |
e5681e842641
|
|
783 |
e5681e842641
|
# spin-entry for autolock-timeout |
784 |
e5681e842641
|
self.spin_autolock_timeout = ui.SpinEntry() |
785 |
e5681e842641
|
self.spin_autolock_timeout.set_range(1, 120) |
786 |
e5681e842641
|
self.spin_autolock_timeout.set_sensitive(self.check_autolock.get_active()) |
787 |
e5681e842641
|
ui.config_bind(self.config, "autolock_timeout", self.spin_autolock_timeout) |
788 |
e5681e842641
|
self.tooltips.set_tip(self.spin_autolock_timeout, "The period of inactivity before locking the file, in minutes") |
789 |
e5681e842641
|
|
790 |
e5681e842641
|
# container for autolock-widgets |
791 |
e5681e842641
|
hbox = ui.HBox() |
792 |
e5681e842641
|
hbox.set_spacing(3) |
793 |
e5681e842641
|
hbox.pack_start(self.check_autolock) |
794 |
e5681e842641
|
hbox.pack_start(self.spin_autolock_timeout) |
795 |
e5681e842641
|
hbox.pack_start(ui.Label("minutes")) |
796 |
e5681e842641
|
self.section_file.append_widget(None, hbox) |
797 |
e5681e842641
|
|
798 |
e5681e842641
|
|
799 |
e5681e842641
|
def __init_section_gotocmd(self, page): |
800 |
e5681e842641
|
"Sets up the goto command section" |
801 |
e5681e842641
|
|
802 |
e5681e842641
|
self.section_goto = page.add_section("Goto Commands") |
803 |
e5681e842641
|
|
804 |
e5681e842641
|
for entrytype in entry.ENTRYLIST: |
805 |
e5681e842641
|
if entrytype == entry.FolderEntry: |
806 |
e5681e842641
|
continue |
807 |
e5681e842641
|
|
808 |
e5681e842641
|
e = entrytype() |
809 |
e5681e842641
|
|
810 |
e5681e842641
|
widget = ui.Entry() |
811 |
e5681e842641
|
ui.config_bind(self.config, "/apps/revelation/launcher/%s" % e.id, widget) |
812 |
e5681e842641
|
|
813 |
e5681e842641
|
tooltip = "Goto command for %s accounts. The following expansion variables can be used:\n\n" % e.typename |
814 |
e5681e842641
|
|
815 |
e5681e842641
|
for field in e.fields: |
816 |
e5681e842641
|
tooltip += "%%%s: %s\n" % ( field.symbol, field.name ) |
817 |
e5681e842641
|
|
818 |
e5681e842641
|
tooltip += "\n" |
819 |
e5681e842641
|
tooltip += "%%: a % sign\n" |
820 |
e5681e842641
|
tooltip += "%?x: optional expansion variable\n" |
821 |
e5681e842641
|
tooltip += "%(...%): optional substring expansion" |
822 |
e5681e842641
|
|
823 |
e5681e842641
|
self.tooltips.set_tip(widget, tooltip) |
824 |
e5681e842641
|
self.section_goto.append_widget(e.typename, widget) |
825 |
e5681e842641
|
|
826 |
e5681e842641
|
|
827 |
e5681e842641
|
def __init_section_menuaction(self, page): |
828 |
e5681e842641
|
"Sets up a menuaction section in a page" |
829 |
e5681e842641
|
|
830 |
e5681e842641
|
self.section_menuaction = page.add_section("Menu Action") |
831 |
e5681e842641
|
|
832 |
e5681e842641
|
# radio-button for show |
833 |
1ea461278a52
|
self.radio_show = ui.RadioButton(None, "Display account info") |
834 |
e5681e842641
|
ui.config_bind(self.config, "menuaction", self.radio_show, "show") |
835 |
e5681e842641
|
|
836 |
e5681e842641
|
self.tooltips.set_tip(self.radio_show, "Display the account information") |
837 |
e5681e842641
|
self.section_menuaction.append_widget(None, self.radio_show) |
838 |
e5681e842641
|
|
839 |
e5681e842641
|
# radio-button for goto |
840 |
e5681e842641
|
self.radio_goto = ui.RadioButton(self.radio_show, "Go to account, if possible") |
841 |
e5681e842641
|
ui.config_bind(self.config, "menuaction", self.radio_goto, "goto") |
842 |
e5681e842641
|
|
843 |
e5681e842641
|
self.tooltips.set_tip(self.radio_goto, "Open the account in an external application if possible, otherwise display it") |
844 |
e5681e842641
|
self.section_menuaction.append_widget(None, self.radio_goto) |
845 |
e5681e842641
|
|
846 |
e5681e842641
|
# radio-button for copy username/password |
847 |
e5681e842641
|
self.radio_copy = ui.RadioButton(self.radio_show, "Copy password to clipboard") |
848 |
e5681e842641
|
ui.config_bind(self.config, "menuaction", self.radio_copy, "copy") |
849 |
e5681e842641
|
|
850 |
1ea461278a52
|
self.tooltips.set_tip(self.radio_copy, "Copy the account password to the clipboard") |
851 |
e5681e842641
|
self.section_menuaction.append_widget(None, self.radio_copy) |
852 |
e5681e842641
|
|
853 |
e5681e842641
|
|
854 |
e5681e842641
|
def __init_section_misc(self, page): |
855 |
e5681e842641
|
"Sets up the misc section" |
856 |
e5681e842641
|
|
857 |
e5681e842641
|
self.section_misc = page.add_section("Miscellaneous") |
858 |
e5681e842641
|
|
859 |
693abd3fbd04
|
# show searchentry checkbutton |
860 |
693abd3fbd04
|
self.check_show_searchentry = ui.CheckButton("Show search entry") |
861 |
693abd3fbd04
|
ui.config_bind(self.config, "show_searchentry", self.check_show_searchentry) |
862 |
693abd3fbd04
|
|
863 |
693abd3fbd04
|
self.tooltips.set_tip(self.check_show_searchentry, "Display an entry box in the applet for searching") |
864 |
693abd3fbd04
|
self.section_misc.append_widget(None, self.check_show_searchentry) |
865 |
693abd3fbd04
|
|
866 |
c6e069377f71
|
# show passwords checkbutton |
867 |
c6e069377f71
|
self.check_show_passwords = ui.CheckButton("Show passwords and other secrets") |
868 |
c6e069377f71
|
ui.config_bind(self.config, "show_passwords", self.check_show_passwords) |
869 |
c6e069377f71
|
|
870 |
c6e069377f71
|
self.tooltips.set_tip(self.check_show_passwords, "Display passwords and other secrets, such as PIN codes (otherwise, hide with ******)") |
871 |
c6e069377f71
|
self.section_misc.append_widget(None, self.check_show_passwords) |
872 |
c6e069377f71
|
|
873 |
e5681e842641
|
# check-button for username |
874 |
e5681e842641
|
self.check_chain_username = ui.CheckButton("Also copy username when copying password") |
875 |
e5681e842641
|
ui.config_bind(self.config, "chain_username", self.check_chain_username) |
876 |
e5681e842641
|
|
877 |
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\"") |
878 |
e5681e842641
|
self.section_misc.append_widget(None, self.check_chain_username) |
879 |
e5681e842641
|
|
880 |
e5681e842641
|
|
881 |
e5681e842641
|
def run(self): |
882 |
e5681e842641
|
"Runs the dialog" |
883 |
e5681e842641
|
|
884 |
e5681e842641
|
self.show_all() |
885 |
e5681e842641
|
|
886 |
e5681e842641
|
|
887 |
e5681e842641
|
|
888 |
e5681e842641
|
def factory(applet, iid): |
889 |
e5681e842641
|
"Applet factory function" |
890 |
e5681e842641
|
|
891 |
e5681e842641
|
RevelationApplet(applet, iid) |
892 |
e5681e842641
|
|
893 |
e5681e842641
|
return True |
894 |
e5681e842641
|
|
895 |
e5681e842641
|
|
896 |
e5681e842641
|
|
897 |
e5681e842641
|
if __name__ == "__main__": |
898 |
e5681e842641
|
gnome.init(config.APPNAME, config.VERSION) |
899 |
e5681e842641
|
|
900 |
0f8c7965fdf3
|
gnomeapplet.bonobo_factory( |
901 |
e5681e842641
|
"OAFIID:GNOME_RevelationApplet_Factory", |
902 |
0f8c7965fdf3
|
gnomeapplet.Applet.__gtype__, |
903 |
e5681e842641
|
config.APPNAME, config.VERSION, factory |
904 |
e5681e842641
|
) |
905 |
e5681e842641
|