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