mirror of
https://gitlab.gnome.org/World/Authenticator.git
synced 2025-03-04 08:44:40 +01:00
i'm on fire, a lot of updates
This commit is contained in:
parent
e0abfa1a16
commit
79150f2221
15 changed files with 862 additions and 608 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -8,6 +8,7 @@ __pycache__/
|
||||||
|
|
||||||
# Distribution / packaging
|
# Distribution / packaging
|
||||||
.Python
|
.Python
|
||||||
|
.idea/
|
||||||
env/
|
env/
|
||||||
build/
|
build/
|
||||||
develop-eggs/
|
develop-eggs/
|
||||||
|
|
|
@ -8,18 +8,21 @@ from TwoFactorAuth.widgets.settings import SettingsWindow
|
||||||
from TwoFactorAuth.models.settings import SettingsReader
|
from TwoFactorAuth.models.settings import SettingsReader
|
||||||
import logging
|
import logging
|
||||||
import signal
|
import signal
|
||||||
|
from gettext import gettext as _
|
||||||
|
|
||||||
|
|
||||||
class Application(Gtk.Application):
|
class Application(Gtk.Application):
|
||||||
win = None
|
win = None
|
||||||
alive = True
|
alive = True
|
||||||
locked = False
|
locked = False
|
||||||
|
menu = Gio.Menu()
|
||||||
|
auth = Authenticator()
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
for key in kwargs:
|
for key in kwargs:
|
||||||
setattr(self, key, kwargs[key])
|
setattr(self, key, kwargs[key])
|
||||||
Gtk.Application.__init__(self,
|
Gtk.Application.__init__(self,
|
||||||
application_id='org.gnome.twofactorauth',
|
application_id='org.gnome.TwoFactorAuth',
|
||||||
flags=Gio.ApplicationFlags.FLAGS_NONE)
|
flags=Gio.ApplicationFlags.FLAGS_NONE)
|
||||||
GLib.set_application_name("TwoFactorAuth")
|
GLib.set_application_name("TwoFactorAuth")
|
||||||
GLib.set_prgname(self.package)
|
GLib.set_prgname(self.package)
|
||||||
|
@ -41,36 +44,68 @@ class Application(Gtk.Application):
|
||||||
|
|
||||||
def do_startup(self):
|
def do_startup(self):
|
||||||
Gtk.Application.do_startup(self)
|
Gtk.Application.do_startup(self)
|
||||||
|
if self.locked:
|
||||||
|
self.menu.append(_("Unlock the Application"), "app.lock")
|
||||||
|
else:
|
||||||
|
self.menu.append(_("Lock the Application"), "app.lock")
|
||||||
|
|
||||||
|
self.menu.append(_("Settings"), "app.settings")
|
||||||
|
if Gtk.get_major_version() >= 3 and Gtk.get_minor_version() >= 20:
|
||||||
|
self.menu.append(_("Shortcuts"), "app.shortcuts")
|
||||||
|
self.menu.append(_("About"), "app.about")
|
||||||
|
self.menu.append(_("Quit"), "app.quit")
|
||||||
|
self.set_app_menu(self.menu)
|
||||||
|
|
||||||
action = Gio.SimpleAction.new("settings", None)
|
action = Gio.SimpleAction.new("settings", None)
|
||||||
action.connect("activate", self.on_settings)
|
action.connect("activate", self.on_settings)
|
||||||
self.add_action(action)
|
self.add_action(action)
|
||||||
|
|
||||||
action = Gio.SimpleAction.new("shortcuts", None)
|
if Gtk.get_major_version() >= 3 and Gtk.get_minor_version() >= 20:
|
||||||
action.connect("activate", self.on_shortcuts)
|
action = Gio.SimpleAction.new("shortcuts", None)
|
||||||
self.add_action(action)
|
action.connect("activate", self.on_shortcuts)
|
||||||
|
self.add_action(action)
|
||||||
|
|
||||||
action = Gio.SimpleAction.new("about", None)
|
action = Gio.SimpleAction.new("about", None)
|
||||||
action.connect("activate", self.on_about)
|
action.connect("activate", self.on_about)
|
||||||
self.add_action(action)
|
self.add_action(action)
|
||||||
|
|
||||||
|
action = Gio.SimpleAction.new("lock", None)
|
||||||
|
action.connect("activate", self.on_toggle_lock)
|
||||||
|
self.add_action(action)
|
||||||
|
|
||||||
action = Gio.SimpleAction.new("quit", None)
|
action = Gio.SimpleAction.new("quit", None)
|
||||||
action.connect("activate", self.on_quit)
|
action.connect("activate", self.on_quit)
|
||||||
self.add_action(action)
|
self.add_action(action)
|
||||||
|
|
||||||
builder = Gtk.Builder()
|
self.toggle_settings_menu()
|
||||||
builder.add_from_file(self.pkgdatadir + "/data/menu.ui")
|
|
||||||
self.app_menu = builder.get_object("app-menu")
|
|
||||||
|
|
||||||
logging.debug("Adding gnome shell menu")
|
logging.debug("Adding gnome shell menu")
|
||||||
self.set_app_menu(self.app_menu)
|
|
||||||
|
|
||||||
def do_activate(self, *args):
|
def do_activate(self, *args):
|
||||||
self.auth = Authenticator()
|
|
||||||
self.win = Window(self)
|
self.win = Window(self)
|
||||||
self.win.show_all()
|
self.win.show_all()
|
||||||
self.add_window(self.win)
|
self.add_window(self.win)
|
||||||
|
|
||||||
|
def toggle_settings_menu(self):
|
||||||
|
if self.locked:
|
||||||
|
self.menu.remove(1)
|
||||||
|
else:
|
||||||
|
self.menu.insert(1, _("Settings"), "app.settings")
|
||||||
|
|
||||||
|
def on_toggle_lock(self, *args):
|
||||||
|
if not self.locked:
|
||||||
|
self.locked = not self.locked
|
||||||
|
self.toggle_app_lock_menu()
|
||||||
|
self.toggle_settings_menu()
|
||||||
|
self.win.refresh_window()
|
||||||
|
|
||||||
|
def toggle_app_lock_menu(self):
|
||||||
|
if self.locked:
|
||||||
|
label = _("Unlock the Application")
|
||||||
|
else:
|
||||||
|
label = _("Lock the Application")
|
||||||
|
self.menu.remove(0)
|
||||||
|
self.menu.insert(0, label, "app.lock")
|
||||||
|
|
||||||
def on_shortcuts(self, *args):
|
def on_shortcuts(self, *args):
|
||||||
self.win.show_shortcuts()
|
self.win.show_shortcuts()
|
||||||
|
|
||||||
|
@ -78,8 +113,10 @@ class Application(Gtk.Application):
|
||||||
self.win.show_about()
|
self.win.show_about()
|
||||||
|
|
||||||
def on_settings(self, *args):
|
def on_settings(self, *args):
|
||||||
if not self.locked:
|
"""
|
||||||
SettingsWindow(self.win)
|
Shows settings window
|
||||||
|
"""
|
||||||
|
SettingsWindow(self.win)
|
||||||
|
|
||||||
def on_quit(self, *args):
|
def on_quit(self, *args):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -9,6 +9,7 @@ class Authenticator:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
home = path.expanduser("~")
|
home = path.expanduser("~")
|
||||||
database_file = home + '/.config/TwoFactorAuth/database.db'
|
database_file = home + '/.config/TwoFactorAuth/database.db'
|
||||||
|
# Create missing folders
|
||||||
if not (path.isfile(database_file) and path.exists(database_file)):
|
if not (path.isfile(database_file) and path.exists(database_file)):
|
||||||
dirs = database_file.split("/")
|
dirs = database_file.split("/")
|
||||||
i = 0
|
i = 0
|
||||||
|
@ -18,57 +19,81 @@ class Authenticator:
|
||||||
makedirs(directory)
|
makedirs(directory)
|
||||||
logging.debug("Creating directory %s " % directory)
|
logging.debug("Creating directory %s " % directory)
|
||||||
i += 1
|
i += 1
|
||||||
|
# create database file
|
||||||
mknod(database_file)
|
mknod(database_file)
|
||||||
logging.debug("Creating database file %s " % database_file)
|
logging.debug("Creating database file %s " % database_file)
|
||||||
|
# Connect to database
|
||||||
self.conn = sqlite3.connect(database_file)
|
self.conn = sqlite3.connect(database_file)
|
||||||
if not self.is_table_exists():
|
if not self.is_table_exists():
|
||||||
logging.debug(
|
logging.debug("SQL: Table 'applciations' does not exists, creating it now...")
|
||||||
"Table 'applciations' does not exists, creating it now...")
|
|
||||||
self.create_table()
|
self.create_table()
|
||||||
logging.debug("Table 'applications' created successfully")
|
logging.debug("SQL: Table 'applications' created successfully")
|
||||||
|
|
||||||
def add_application(self, name, secret_code, image):
|
def add_application(self, name, secret_code, image):
|
||||||
|
"""
|
||||||
|
Add an application to applications table
|
||||||
|
:param name: (str) Application name
|
||||||
|
:param secret_code: (str) ASCII Secret code
|
||||||
|
:param image: image path or icon name
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
t = (name, secret_code, image,)
|
t = (name, secret_code, image,)
|
||||||
query = "INSERT INTO applications (name, secret_code, image) VALUES (?, ?, ?)"
|
query = "INSERT INTO applications (name, secret_code, image) VALUES (?, ?, ?)"
|
||||||
try:
|
try:
|
||||||
self.conn.execute(query, t)
|
self.conn.execute(query, t)
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Couldn't add a new application : %s ", str(e))
|
logging.error("SQL: Couldn't add a new application : %s ", str(e))
|
||||||
|
|
||||||
def remove_by_id(self, id):
|
def remove_by_uid(self, uid):
|
||||||
query = "DELETE FROM applications WHERE id=?"
|
"""
|
||||||
|
Remove an application by uid
|
||||||
|
:param uid: (int) application uid
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
query = "DELETE FROM applications WHERE uid=?"
|
||||||
try:
|
try:
|
||||||
self.conn.execute(query, (id,))
|
self.conn.execute(query, (uid,))
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(
|
logging.error("SQL: Couldn't remove application by uid : %s with error : %s" % (uid, str(e)))
|
||||||
"Couldn't remove application by id : %s with error : %s" % (id, str(e)))
|
|
||||||
|
|
||||||
def count(self):
|
def count(self):
|
||||||
|
"""
|
||||||
|
Count number of applications
|
||||||
|
:return: (int) count
|
||||||
|
"""
|
||||||
c = self.conn.cursor()
|
c = self.conn.cursor()
|
||||||
query = "SELECT COUNT(id) AS count FROM applications"
|
query = "SELECT COUNT(uid) AS count FROM applications"
|
||||||
try:
|
try:
|
||||||
data = c.execute(query)
|
data = c.execute(query)
|
||||||
return data.fetchone()[0]
|
return data.fetchone()[0]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Couldn't count applications list : %s " % str(e))
|
logging.error("SQL: Couldn't count applications list : %s " % str(e))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def fetch_apps(self):
|
def fetch_apps(self):
|
||||||
|
"""
|
||||||
|
Fetch list of applications
|
||||||
|
:return: (tuple) list of applications
|
||||||
|
"""
|
||||||
c = self.conn.cursor()
|
c = self.conn.cursor()
|
||||||
query = "SELECT * FROM applications"
|
query = "SELECT * FROM applications"
|
||||||
try:
|
try:
|
||||||
data = c.execute(query)
|
data = c.execute(query)
|
||||||
return data.fetchall()
|
return data.fetchall()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(query)
|
logging.error("SQL: Couldn't fetch applications list %s" % str(e))
|
||||||
logging.error("Couldn't fetch applications list")
|
|
||||||
logging.error(str(e))
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_auth_icon(image, pkgdatadir):
|
def get_auth_icon(image, pkgdatadir):
|
||||||
|
"""
|
||||||
|
Generate a GdkPixbuf image
|
||||||
|
:param image: icon name or image path
|
||||||
|
:param pkgdatadir: default installation dir of apps
|
||||||
|
:return: GdkPixbux Image
|
||||||
|
"""
|
||||||
directory = pkgdatadir + "/data/logos/"
|
directory = pkgdatadir + "/data/logos/"
|
||||||
theme = Gtk.IconTheme.get_default()
|
theme = Gtk.IconTheme.get_default()
|
||||||
if path.isfile(directory + image) and path.exists(directory + image):
|
if path.isfile(directory + image) and path.exists(directory + image):
|
||||||
|
@ -79,24 +104,31 @@ class Authenticator:
|
||||||
icon = theme.load_icon(path.splitext(image)[0], 48, 0)
|
icon = theme.load_icon(path.splitext(image)[0], 48, 0)
|
||||||
else:
|
else:
|
||||||
icon = theme.load_icon("image-missing", 48, 0)
|
icon = theme.load_icon("image-missing", 48, 0)
|
||||||
if icon.get_width() != 48 or icon.get_height() != 48:
|
if icon.get_wuidth() != 48 or icon.get_height() != 48:
|
||||||
icon = icon.scale_simple(48, 48,
|
icon = icon.scale_simple(48, 48,
|
||||||
GdkPixbuf.InterpType.BILINEAR)
|
GdkPixbuf.InterpType.BILINEAR)
|
||||||
return icon
|
return icon
|
||||||
|
|
||||||
def get_latest_id(self):
|
def get_latest_uid(self):
|
||||||
|
"""
|
||||||
|
Get the latest uid on applications table
|
||||||
|
:return: (int) latest uid
|
||||||
|
"""
|
||||||
c = self.conn.cursor()
|
c = self.conn.cursor()
|
||||||
query = "SELECT id FROM applications ORDER BY id DESC LIMIT 1;"
|
query = "SELECT uid FROM applications ORDER BY uid DESC LIMIT 1;"
|
||||||
try:
|
try:
|
||||||
data = c.execute(query)
|
data = c.execute(query)
|
||||||
return data.fetchone()[0]
|
return data.fetchone()[0]
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Couldn't fetch the latest id %s" % str(e))
|
logging.error("SQL: Couldn't fetch the latest uid %s" % str(e))
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def create_table(self):
|
def create_table(self):
|
||||||
|
"""
|
||||||
|
Create applications table
|
||||||
|
"""
|
||||||
query = '''CREATE TABLE "applications" (
|
query = '''CREATE TABLE "applications" (
|
||||||
"id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE ,
|
"uid" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL UNIQUE ,
|
||||||
"name" VARCHAR NOT NULL ,
|
"name" VARCHAR NOT NULL ,
|
||||||
"secret_code" VARCHAR NOT NULL UNIQUE ,
|
"secret_code" VARCHAR NOT NULL UNIQUE ,
|
||||||
"image" TEXT NOT NULL
|
"image" TEXT NOT NULL
|
||||||
|
@ -105,11 +137,14 @@ class Authenticator:
|
||||||
self.conn.execute(query)
|
self.conn.execute(query)
|
||||||
self.conn.commit()
|
self.conn.commit()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error(
|
logging.error( "SQL: imossibile to create table 'applications' %s " % str(e))
|
||||||
"SQL : imossibile to create table 'applications' %s " % str(err))
|
|
||||||
|
|
||||||
def is_table_exists(self):
|
def is_table_exists(self):
|
||||||
query = "SELECT id from applications LIMIT 1"
|
"""
|
||||||
|
Check if applications table exists
|
||||||
|
:return: (bool)
|
||||||
|
"""
|
||||||
|
query = "SELECT uid from applications LIMIT 1"
|
||||||
c = self.conn.cursor()
|
c = self.conn.cursor()
|
||||||
try:
|
try:
|
||||||
data = c.execute(query)
|
data = c.execute(query)
|
||||||
|
|
|
@ -7,12 +7,19 @@ class SettingsReader:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
try:
|
try:
|
||||||
|
# Check if the gsettings path exists
|
||||||
self.source = Gio.SettingsSchemaSource.get_default()
|
self.source = Gio.SettingsSchemaSource.get_default()
|
||||||
self.source.lookup(self.path, True)
|
self.source.lookup(self.path, True)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.critical("Couldn't load gsettings source %s " % str(e))
|
logging.critical("Couldn't load gsettings source %s " % str(e))
|
||||||
|
|
||||||
def read(self, key, path):
|
def read(self, key, path):
|
||||||
|
"""
|
||||||
|
Read a 'key' from org.gnome.TwoFactorAuth.'path'
|
||||||
|
:param key: (str) key to read
|
||||||
|
:param path: login/user
|
||||||
|
:return: value
|
||||||
|
"""
|
||||||
gsettings = Gio.Settings.new(self.path + "." + path)
|
gsettings = Gio.Settings.new(self.path + "." + path)
|
||||||
value = gsettings.get_value(key)
|
value = gsettings.get_value(key)
|
||||||
value_type = value.get_type_string()
|
value_type = value.get_type_string()
|
||||||
|
@ -25,6 +32,13 @@ class SettingsReader:
|
||||||
return value
|
return value
|
||||||
|
|
||||||
def update(self, key, value, path):
|
def update(self, key, value, path):
|
||||||
|
"""
|
||||||
|
Update 'key' value to 'value' from org.gnome.TwoFactorAuth.'path'
|
||||||
|
:param key: (str) key to read
|
||||||
|
:param value: updated value
|
||||||
|
:param path: login/user
|
||||||
|
:return: value
|
||||||
|
"""
|
||||||
gsettings = Gio.Settings.new(self.path + "." + path)
|
gsettings = Gio.Settings.new(self.path + "." + path)
|
||||||
if type(value) is int:
|
if type(value) is int:
|
||||||
gsettings.set_int(key, value)
|
gsettings.set_int(key, value)
|
||||||
|
|
|
@ -11,28 +11,23 @@ from gettext import gettext as _
|
||||||
|
|
||||||
class AddAuthenticator(Gtk.Window):
|
class AddAuthenticator(Gtk.Window):
|
||||||
selected_image = None
|
selected_image = None
|
||||||
|
hb = Gtk.HeaderBar()
|
||||||
|
popover = Gtk.PopoverMenu.new()
|
||||||
|
logo_image = Gtk.Image(xalign=0)
|
||||||
|
secret_code = Gtk.Entry()
|
||||||
|
name_entry = Gtk.Entry()
|
||||||
|
|
||||||
def __init__(self, window):
|
def __init__(self, window):
|
||||||
self.parent = window
|
self.parent = window
|
||||||
self.generate_window()
|
self.generate_window()
|
||||||
self.generate_compenents()
|
self.generate_components()
|
||||||
self.generate_headerbar()
|
self.generate_headerbar()
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
def update_logo(self, image):
|
|
||||||
# TODO : add the possiblity to use external icons or icon names
|
|
||||||
directory = self.parent.app.pkgdatadir + "/data/logos/"
|
|
||||||
self.selected_image = image
|
|
||||||
auth_icon = Authenticator.get_auth_icon(
|
|
||||||
image, self.parent.app.pkgdatadir)
|
|
||||||
img_box = self.get_children()[0].get_children()[0].get_children()
|
|
||||||
img_box[0].get_children()[0].clear()
|
|
||||||
img_box[0].get_children()[0].set_from_pixbuf(auth_icon)
|
|
||||||
|
|
||||||
def generate_window(self):
|
def generate_window(self):
|
||||||
Gtk.Window.__init__(self, title=_("Add a new application"), modal=True,
|
Gtk.Window.__init__(self, title=_("Add a new application"),
|
||||||
destroy_with_parent=True)
|
modal=True, destroy_with_parent=True)
|
||||||
self.connect("delete-event", lambda x, y: self.destroy())
|
self.connect("delete-event", self.close_window)
|
||||||
self.resize(300, 100)
|
self.resize(300, 100)
|
||||||
self.set_border_width(12)
|
self.set_border_width(12)
|
||||||
self.set_size_request(300, 100)
|
self.set_size_request(300, 100)
|
||||||
|
@ -41,11 +36,27 @@ class AddAuthenticator(Gtk.Window):
|
||||||
self.set_transient_for(self.parent)
|
self.set_transient_for(self.parent)
|
||||||
self.connect("key_press_event", self.on_key_press)
|
self.connect("key_press_event", self.on_key_press)
|
||||||
|
|
||||||
def on_key_press(self, key, keyevent):
|
def on_key_press(self, key, key_event):
|
||||||
if Gdk.keyval_name(keyevent.keyval) == "Escape":
|
"""
|
||||||
|
Keyboard Listener handler
|
||||||
|
"""
|
||||||
|
if Gdk.keyval_name(key_event.keyval) == "Escape":
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
def select_logo(self, eventbox, event_button):
|
def update_logo(self, image):
|
||||||
|
"""
|
||||||
|
Update image logo
|
||||||
|
"""
|
||||||
|
directory = self.parent.app.pkgdatadir + "/data/logos/"
|
||||||
|
self.selected_image = image
|
||||||
|
auth_icon = Authenticator.get_auth_icon(image, self.parent.app.pkgdatadir)
|
||||||
|
self.logo_image.clear()
|
||||||
|
self.logo_image.set_from_pixbuf(auth_icon)
|
||||||
|
|
||||||
|
def select_logo(self, event_box, event_button):
|
||||||
|
"""
|
||||||
|
Application logo selection, right & left mouse click event handling
|
||||||
|
"""
|
||||||
# Right click handling
|
# Right click handling
|
||||||
if event_button.button == 3:
|
if event_button.button == 3:
|
||||||
if self.popover.get_visible():
|
if self.popover.get_visible():
|
||||||
|
@ -56,55 +67,54 @@ class AddAuthenticator(Gtk.Window):
|
||||||
AuthenticatorLogoChooser(self)
|
AuthenticatorLogoChooser(self)
|
||||||
|
|
||||||
def add_application(self, *args):
|
def add_application(self, *args):
|
||||||
entries_box = self.get_children()[0].get_children()[1].get_children()
|
"""
|
||||||
name_entry = entries_box[0].get_children()[1].get_text()
|
Add a new application to the database
|
||||||
secret_entry = entries_box[1].get_children()[1].get_text()
|
"""
|
||||||
|
name_entry = self.name_entry.get_text()
|
||||||
|
secret_entry = self.secret_code.get_text()
|
||||||
image_entry = self.selected_image if self.selected_image else "image-missing"
|
image_entry = self.selected_image if self.selected_image else "image-missing"
|
||||||
try:
|
try:
|
||||||
self.parent.app.auth.add_application(name_entry, secret_entry,
|
self.parent.app.auth.add_application(name_entry, secret_entry,
|
||||||
image_entry)
|
image_entry)
|
||||||
id = self.parent.app.auth.get_latest_id()
|
id = self.parent.app.auth.get_latest_id()
|
||||||
self.parent.update_list(id, name_entry, secret_entry, image_entry)
|
self.parent.append(id, name_entry, secret_entry, image_entry)
|
||||||
self.parent.refresh_window()
|
self.parent.refresh_window()
|
||||||
self.close_window()
|
self.close_window()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logging.error("Error in adding a new application")
|
logging.error("Error in adding a new application")
|
||||||
logging.error(str(e))
|
logging.error(str(e))
|
||||||
|
|
||||||
def generate_compenents(self):
|
def generate_components(self):
|
||||||
mainbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
"""
|
||||||
|
Generate window components
|
||||||
|
"""
|
||||||
|
main_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||||
logo_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
|
logo_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=6)
|
||||||
hbox_title = Gtk.Box(
|
hbox_title = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
||||||
orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
|
||||||
title_label = Gtk.Label()
|
title_label = Gtk.Label()
|
||||||
title_label.set_text(_("Application name : "))
|
title_label.set_text(_("Application Name"))
|
||||||
title_entry = Gtk.Entry()
|
|
||||||
hbox_title.pack_start(title_label, False, True, 0)
|
|
||||||
hbox_title.pack_end(title_entry, False, True, 0)
|
|
||||||
|
|
||||||
hbox_two_factor = Gtk.Box(
|
hbox_title.pack_start(title_label, False, True, 0)
|
||||||
orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
hbox_title.pack_end(self.name_entry, False, True, 0)
|
||||||
|
|
||||||
|
hbox_two_factor = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
||||||
two_factor_label = Gtk.Label()
|
two_factor_label = Gtk.Label()
|
||||||
two_factor_label.set_text(_("Two-factor secret : "))
|
two_factor_label.set_text(_("Secret Code"))
|
||||||
two_factor_entry = Gtk.Entry()
|
self.secret_code.connect("changed", self.validate_ascii_code)
|
||||||
two_factor_entry.connect("changed", self.validate_ascii_code)
|
|
||||||
hbox_two_factor.pack_start(two_factor_label, False, True, 0)
|
hbox_two_factor.pack_start(two_factor_label, False, True, 0)
|
||||||
hbox_two_factor.pack_end(two_factor_entry, False, True, 0)
|
hbox_two_factor.pack_end(self.secret_code, False, True, 0)
|
||||||
|
|
||||||
logo_event = Gtk.EventBox()
|
logo_event = Gtk.EventBox()
|
||||||
auth_icon = Authenticator.get_auth_icon("image-missing",
|
auth_icon = Authenticator.get_auth_icon("image-missing", self.parent.app.pkgdatadir)
|
||||||
self.parent.app.pkgdatadir)
|
self.logo_image.set_from_pixbuf(auth_icon)
|
||||||
logo_image = Gtk.Image(xalign=0)
|
self.logo_image.get_style_context().add_class("application-logo-add")
|
||||||
logo_image.set_from_pixbuf(auth_icon)
|
logo_event.add(self.logo_image)
|
||||||
logo_image.get_style_context().add_class("application-logo-add")
|
|
||||||
logo_event.add(logo_image)
|
|
||||||
logo_event.connect("button-press-event", self.select_logo)
|
logo_event.connect("button-press-event", self.select_logo)
|
||||||
logo_box.pack_start(logo_event, False, False, 6)
|
logo_box.pack_start(logo_event, False, False, 6)
|
||||||
|
|
||||||
self.popover = Gtk.PopoverMenu.new()
|
|
||||||
self.popover.get_style_context().add_class("choose-popover")
|
self.popover.get_style_context().add_class("choose-popover")
|
||||||
self.popover.set_relative_to(logo_image)
|
self.popover.set_relative_to(self.logo_image)
|
||||||
|
|
||||||
pbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
pbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
self.popover.add(pbox)
|
self.popover.add(pbox)
|
||||||
|
@ -126,30 +136,36 @@ class AddAuthenticator(Gtk.Window):
|
||||||
|
|
||||||
vbox.add(hbox_title)
|
vbox.add(hbox_title)
|
||||||
vbox.add(hbox_two_factor)
|
vbox.add(hbox_two_factor)
|
||||||
mainbox.pack_start(logo_box, False, True, 6)
|
main_box.pack_start(logo_box, False, True, 6)
|
||||||
mainbox.pack_start(vbox, False, True, 6)
|
main_box.pack_start(vbox, False, True, 6)
|
||||||
self.add(mainbox)
|
self.add(main_box)
|
||||||
|
|
||||||
def validate_ascii_code(self, entry):
|
def validate_ascii_code(self, entry):
|
||||||
|
"""
|
||||||
|
Validate if the typed secret code is a valid ascii one
|
||||||
|
"""
|
||||||
ascii_code = entry.get_text().strip()
|
ascii_code = entry.get_text().strip()
|
||||||
is_valid = Code.is_valid(ascii_code)
|
is_valid = Code.is_valid(ascii_code)
|
||||||
self.hb.get_children()[1].get_children()[0].set_sensitive(is_valid)
|
self.apply_button.set_sensitive(is_valid)
|
||||||
if not is_valid and len(ascii_code) != 0:
|
if not is_valid and len(ascii_code) != 0:
|
||||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
|
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "dialog-error-symbolic")
|
||||||
"dialog-error-symbolic")
|
|
||||||
else:
|
else:
|
||||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
|
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
|
||||||
"")
|
|
||||||
|
|
||||||
def on_provided_click(self, *args):
|
def on_provided_click(self, *args):
|
||||||
|
"""
|
||||||
|
Select an icon from provided ones
|
||||||
|
"""
|
||||||
AuthenticatorLogoChooser(self)
|
AuthenticatorLogoChooser(self)
|
||||||
|
|
||||||
def on_file_clicked(self, *args):
|
def on_file_clicked(self, *args):
|
||||||
|
""""
|
||||||
|
Select an icon by filename
|
||||||
|
"""
|
||||||
dialog = Gtk.FileChooserDialog(_("Please choose a file"), self,
|
dialog = Gtk.FileChooserDialog(_("Please choose a file"), self,
|
||||||
Gtk.FileChooserAction.OPEN,
|
Gtk.FileChooserAction.OPEN,
|
||||||
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
||||||
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
|
Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
|
||||||
|
|
||||||
self.add_filters(dialog)
|
self.add_filters(dialog)
|
||||||
|
|
||||||
response = dialog.run()
|
response = dialog.run()
|
||||||
|
@ -158,9 +174,16 @@ class AddAuthenticator(Gtk.Window):
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
|
|
||||||
def on_icon_clicked(self, *args):
|
def on_icon_clicked(self, *args):
|
||||||
|
"""
|
||||||
|
Shows icon finder window
|
||||||
|
select icon by icon name
|
||||||
|
"""
|
||||||
IconFinderWindow(self)
|
IconFinderWindow(self)
|
||||||
|
|
||||||
def add_filters(self, dialog):
|
def add_filters(self, dialog):
|
||||||
|
"""
|
||||||
|
Add file filters to GtkFileChooser
|
||||||
|
"""
|
||||||
filter_png = Gtk.FileFilter()
|
filter_png = Gtk.FileFilter()
|
||||||
filter_png.set_name("PNG")
|
filter_png.set_name("PNG")
|
||||||
filter_png.add_mime_type("image/png")
|
filter_png.add_mime_type("image/png")
|
||||||
|
@ -171,9 +194,10 @@ class AddAuthenticator(Gtk.Window):
|
||||||
filter_svg.add_mime_type("image/svg+xml")
|
filter_svg.add_mime_type("image/svg+xml")
|
||||||
dialog.add_filter(filter_svg)
|
dialog.add_filter(filter_svg)
|
||||||
|
|
||||||
def generate_headerbar(self):
|
def generate_header_bar(self):
|
||||||
self.hb = Gtk.HeaderBar()
|
"""
|
||||||
|
Generate the header bar box
|
||||||
|
"""
|
||||||
left_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
left_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
right_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
right_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
|
|
||||||
|
@ -182,15 +206,18 @@ class AddAuthenticator(Gtk.Window):
|
||||||
cancel_button.get_style_context().add_class("destructive-action")
|
cancel_button.get_style_context().add_class("destructive-action")
|
||||||
left_box.add(cancel_button)
|
left_box.add(cancel_button)
|
||||||
|
|
||||||
apply_button = Gtk.Button.new_with_label(_("Add"))
|
self.apply_button = Gtk.Button.new_with_label(_("Add"))
|
||||||
apply_button.get_style_context().add_class("suggested-action")
|
self.apply_button.get_style_context().add_class("suggested-action")
|
||||||
apply_button.connect("clicked", self.add_application)
|
self.apply_button.connect("clicked", self.add_application)
|
||||||
apply_button.set_sensitive(False)
|
self.apply_button.set_sensitive(False)
|
||||||
right_box.add(apply_button)
|
right_box.add(self.apply_button)
|
||||||
|
|
||||||
self.hb.pack_start(left_box)
|
self.hb.pack_start(left_box)
|
||||||
self.hb.pack_end(right_box)
|
self.hb.pack_end(right_box)
|
||||||
self.set_titlebar(self.hb)
|
self.set_titlebar(self.hb)
|
||||||
|
|
||||||
def close_window(self, *args):
|
def close_window(self, *args):
|
||||||
|
"""
|
||||||
|
Close the window
|
||||||
|
"""
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
|
@ -21,9 +21,8 @@ class AuthenticatorLogoChooser(Gtk.Window):
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
def generate_window(self):
|
def generate_window(self):
|
||||||
Gtk.Window.__init__(self, modal=True,
|
Gtk.Window.__init__(self, modal=True, destroy_with_parent=True)
|
||||||
destroy_with_parent=True)
|
self.connect("delete-event", self.close_window)
|
||||||
self.connect("delete-event", lambda x, y: self.destroy())
|
|
||||||
self.resize(350, 400)
|
self.resize(350, 400)
|
||||||
self.set_size_request(350, 400)
|
self.set_size_request(350, 400)
|
||||||
self.set_position(Gtk.WindowPosition.CENTER)
|
self.set_position(Gtk.WindowPosition.CENTER)
|
||||||
|
|
|
@ -8,19 +8,23 @@ from TwoFactorAuth.models.settings import SettingsReader
|
||||||
|
|
||||||
|
|
||||||
class PasswordWindow(Gtk.Window):
|
class PasswordWindow(Gtk.Window):
|
||||||
|
hb = Gtk.HeaderBar()
|
||||||
|
apply_button = Gtk.Button.new_with_label(_("Modifier"))
|
||||||
|
new_entry = Gtk.Entry()
|
||||||
|
new2_entry = Gtk.Entry()
|
||||||
|
old_entry = Gtk.Entry()
|
||||||
|
|
||||||
def __init__(self, window):
|
def __init__(self, window):
|
||||||
self.parent = window
|
self.parent = window
|
||||||
self.cfg = SettingsReader()
|
self.cfg = SettingsReader()
|
||||||
self.generate_window()
|
self.generate_window()
|
||||||
self.generate_compenents()
|
self.generate_components()
|
||||||
self.generate_headerbar()
|
self.generate_header_bar()
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
def generate_window(self):
|
def generate_window(self):
|
||||||
Gtk.Window.__init__(self, modal=True,
|
Gtk.Window.__init__(self, modal=True, destroy_with_parent=True)
|
||||||
destroy_with_parent=True)
|
self.connect("delete-event", self.close_window)
|
||||||
self.connect("delete-event", lambda x, y: self.destroy())
|
|
||||||
self.resize(300, 100)
|
self.resize(300, 100)
|
||||||
self.set_border_width(12)
|
self.set_border_width(12)
|
||||||
self.set_size_request(300, 100)
|
self.set_size_request(300, 100)
|
||||||
|
@ -29,60 +33,70 @@ class PasswordWindow(Gtk.Window):
|
||||||
self.set_transient_for(self.parent)
|
self.set_transient_for(self.parent)
|
||||||
self.connect("key_press_event", self.on_key_press)
|
self.connect("key_press_event", self.on_key_press)
|
||||||
|
|
||||||
def on_key_press(self, key, keyevent):
|
def on_key_press(self, key, key_event):
|
||||||
if Gdk.keyval_name(keyevent.keyval) == "Escape":
|
"""
|
||||||
|
Keyboard listener handler
|
||||||
|
"""
|
||||||
|
if Gdk.keyval_name(key_event.keyval) == "Escape":
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
def generate_compenents(self):
|
def generate_components(self):
|
||||||
mainbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
"""
|
||||||
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
Generate window components
|
||||||
|
"""
|
||||||
|
main_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||||
|
|
||||||
if len(self.cfg.read("password", "login")) != 0:
|
if len(self.cfg.read("password", "login")) != 0:
|
||||||
hbox_old = Gtk.Box(
|
box_old = Gtk.Box(
|
||||||
orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
||||||
old_label = Gtk.Label()
|
old_label = Gtk.Label()
|
||||||
old_label.set_text(_("Old password : "))
|
old_label.set_text(_("Old password"))
|
||||||
self.old_entry = Gtk.Entry()
|
|
||||||
self.old_entry.connect("changed", self.on_type_password)
|
self.old_entry.connect("changed", self.on_type_password)
|
||||||
self.old_entry.set_visibility(False)
|
self.old_entry.set_visibility(False)
|
||||||
hbox_old.pack_start(old_label, False, True, 0)
|
box_old.pack_start(old_label, False, True, 0)
|
||||||
hbox_old.pack_end(self.old_entry, False, True, 0)
|
box_old.pack_end(self.old_entry, False, True, 0)
|
||||||
vbox.add(hbox_old)
|
box.add(box_old)
|
||||||
|
|
||||||
hbox_new = Gtk.Box(
|
box_new = Gtk.Box(
|
||||||
orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
||||||
new_label = Gtk.Label()
|
new_label = Gtk.Label()
|
||||||
new_label.set_text(_("New password : "))
|
new_label.set_text(_("New password"))
|
||||||
self.new_entry = Gtk.Entry()
|
|
||||||
self.new_entry.connect("changed", self.on_type_password)
|
self.new_entry.connect("changed", self.on_type_password)
|
||||||
self.new_entry.set_visibility(False)
|
self.new_entry.set_visibility(False)
|
||||||
hbox_new.pack_start(new_label, False, True, 0)
|
box_new.pack_start(new_label, False, True, 0)
|
||||||
hbox_new.pack_end(self.new_entry, False, True, 0)
|
box_new.pack_end(self.new_entry, False, True, 0)
|
||||||
|
|
||||||
hbox_new2 = Gtk.Box(
|
box_new2 = Gtk.Box(
|
||||||
orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
orientation=Gtk.Orientation.HORIZONTAL, spacing=18)
|
||||||
new2_label = Gtk.Label()
|
new2_label = Gtk.Label()
|
||||||
new2_label.set_text(_("Repeat new password : "))
|
new2_label.set_text(_("Repeat new password"))
|
||||||
self.new2_entry = Gtk.Entry()
|
|
||||||
self.new2_entry.connect("changed", self.on_type_password)
|
self.new2_entry.connect("changed", self.on_type_password)
|
||||||
self.new2_entry.set_visibility(False)
|
self.new2_entry.set_visibility(False)
|
||||||
hbox_new2.pack_start(new2_label, False, True, 0)
|
box_new2.pack_start(new2_label, False, True, 0)
|
||||||
hbox_new2.pack_end(self.new2_entry, False, True, 0)
|
box_new2.pack_end(self.new2_entry, False, True, 0)
|
||||||
|
|
||||||
vbox.add(hbox_new)
|
box.add(box_new)
|
||||||
vbox.add(hbox_new2)
|
box.add(box_new2)
|
||||||
|
|
||||||
mainbox.pack_start(vbox, False, True, 6)
|
main_box.pack_start(box, False, True, 6)
|
||||||
self.add(mainbox)
|
self.add(main_box)
|
||||||
|
|
||||||
def update_password(self, *args):
|
def update_password(self, *args):
|
||||||
|
"""
|
||||||
|
Update user password
|
||||||
|
"""
|
||||||
password = md5(self.new_entry.get_text().encode("utf-8")).hexdigest()
|
password = md5(self.new_entry.get_text().encode("utf-8")).hexdigest()
|
||||||
self.cfg.update("password", password, "login")
|
self.cfg.update("password", password, "login")
|
||||||
|
logging.debug("Password changed successfully")
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
def on_type_password(self, entry):
|
def on_type_password(self, entry):
|
||||||
|
"""
|
||||||
|
Validate the old & new password
|
||||||
|
"""
|
||||||
pwd = self.cfg.read("password", "login")
|
pwd = self.cfg.read("password", "login")
|
||||||
are_diff = False
|
|
||||||
old_is_ok = True
|
old_is_ok = True
|
||||||
if self.new_entry.get_text() != self.new2_entry.get_text():
|
if self.new_entry.get_text() != self.new2_entry.get_text():
|
||||||
self.new_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
|
self.new_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
|
||||||
|
@ -113,9 +127,10 @@ class PasswordWindow(Gtk.Window):
|
||||||
Gtk.EntryIconPosition.SECONDARY, "")
|
Gtk.EntryIconPosition.SECONDARY, "")
|
||||||
self.apply_button.set_sensitive(not are_diff and old_is_ok)
|
self.apply_button.set_sensitive(not are_diff and old_is_ok)
|
||||||
|
|
||||||
def generate_headerbar(self):
|
def generate_header_bar(self):
|
||||||
self.hb = Gtk.HeaderBar()
|
"""
|
||||||
|
Generate header bar box
|
||||||
|
"""
|
||||||
left_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
left_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
right_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
right_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
|
|
||||||
|
@ -124,7 +139,6 @@ class PasswordWindow(Gtk.Window):
|
||||||
cancel_button.get_style_context().add_class("destructive-action")
|
cancel_button.get_style_context().add_class("destructive-action")
|
||||||
left_box.add(cancel_button)
|
left_box.add(cancel_button)
|
||||||
|
|
||||||
self.apply_button = Gtk.Button.new_with_label(_("Modifier"))
|
|
||||||
self.apply_button.get_style_context().add_class("suggested-action")
|
self.apply_button.get_style_context().add_class("suggested-action")
|
||||||
self.apply_button.connect("clicked", self.update_password)
|
self.apply_button.connect("clicked", self.update_password)
|
||||||
self.apply_button.set_sensitive(False)
|
self.apply_button.set_sensitive(False)
|
||||||
|
@ -135,4 +149,7 @@ class PasswordWindow(Gtk.Window):
|
||||||
self.set_titlebar(self.hb)
|
self.set_titlebar(self.hb)
|
||||||
|
|
||||||
def close_window(self, *args):
|
def close_window(self, *args):
|
||||||
|
"""
|
||||||
|
Close the window
|
||||||
|
"""
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
|
@ -5,6 +5,7 @@ import logging
|
||||||
|
|
||||||
|
|
||||||
class ConfirmationMessage(Gtk.Window):
|
class ConfirmationMessage(Gtk.Window):
|
||||||
|
result = None
|
||||||
|
|
||||||
def __init__(self, parent, message):
|
def __init__(self, parent, message):
|
||||||
try:
|
try:
|
||||||
|
@ -18,6 +19,9 @@ class ConfirmationMessage(Gtk.Window):
|
||||||
logging.error(str(e))
|
logging.error(str(e))
|
||||||
|
|
||||||
def show(self):
|
def show(self):
|
||||||
|
"""
|
||||||
|
Show confirmation dialog
|
||||||
|
"""
|
||||||
try:
|
try:
|
||||||
self.result = self.dialog.run()
|
self.result = self.dialog.run()
|
||||||
self.dialog.hide()
|
self.dialog.hide()
|
||||||
|
@ -26,7 +30,13 @@ class ConfirmationMessage(Gtk.Window):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_confirmation(self):
|
def get_confirmation(self):
|
||||||
|
"""
|
||||||
|
get confirmation
|
||||||
|
"""
|
||||||
return self.result == Gtk.ResponseType.OK
|
return self.result == Gtk.ResponseType.OK
|
||||||
|
|
||||||
def destroy(self):
|
def destroy(self):
|
||||||
|
"""
|
||||||
|
Destroy the message
|
||||||
|
"""
|
||||||
self.dialog.destroy()
|
self.dialog.destroy()
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
from gi import require_version
|
from gi import require_version
|
||||||
require_version("Gtk", "3.0")
|
require_version("Gtk", "3.0")
|
||||||
from gi.repository import Gtk, Gdk
|
from gi.repository import Gtk, Gdk
|
||||||
import logging
|
|
||||||
from TwoFactorAuth.widgets.authenticator_logo import AuthenticatorLogoChooser
|
|
||||||
from TwoFactorAuth.models.code import Code
|
|
||||||
from TwoFactorAuth.models.authenticator import Authenticator
|
from TwoFactorAuth.models.authenticator import Authenticator
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
|
|
||||||
class IconFinderWindow(Gtk.Window):
|
class IconFinderWindow(Gtk.Window):
|
||||||
selected_image = None
|
selected_image = None
|
||||||
|
hb = Gtk.HeaderBar()
|
||||||
|
apply_button = Gtk.Button.new_with_label(_("Select"))
|
||||||
|
logo_image = Gtk.Image(xalign=0)
|
||||||
|
icon_entry = Gtk.Entry()
|
||||||
|
|
||||||
def __init__(self, window):
|
def __init__(self, window):
|
||||||
self.parent = window
|
self.parent = window
|
||||||
self.generate_window()
|
self.generate_window()
|
||||||
self.generate_compenents()
|
self.generate_components()
|
||||||
self.generate_headerbar()
|
self.generate_header_bar()
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
def generate_window(self):
|
def generate_window(self):
|
||||||
Gtk.Window.__init__(self, modal=True,
|
Gtk.Window.__init__(self, modal=True, destroy_with_parent=True)
|
||||||
destroy_with_parent=True)
|
self.connect("delete-event", self.close_window)
|
||||||
self.connect("delete-event", lambda x, y: self.destroy())
|
|
||||||
self.resize(200, 100)
|
self.resize(200, 100)
|
||||||
self.set_border_width(12)
|
self.set_border_width(12)
|
||||||
self.set_size_request(200, 100)
|
self.set_size_request(200, 100)
|
||||||
|
@ -29,29 +29,33 @@ class IconFinderWindow(Gtk.Window):
|
||||||
self.set_resizable(False)
|
self.set_resizable(False)
|
||||||
self.set_transient_for(self.parent)
|
self.set_transient_for(self.parent)
|
||||||
|
|
||||||
def generate_compenents(self):
|
def generate_components(self):
|
||||||
mainbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
"""
|
||||||
|
Generate all the components
|
||||||
|
"""
|
||||||
|
main_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
|
|
||||||
logo_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
logo_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
auth_icon = Authenticator.get_auth_icon("image-missing",
|
auth_icon = Authenticator.get_auth_icon("image-missing",
|
||||||
self.parent.parent.app.pkgdatadir)
|
self.parent.parent.app.pkgdatadir)
|
||||||
logo_image = Gtk.Image(xalign=0)
|
self.logo_image.set_from_pixbuf(auth_icon)
|
||||||
logo_image.set_from_pixbuf(auth_icon)
|
self.logo_image.get_style_context().add_class("application-logo-add")
|
||||||
logo_image.get_style_context().add_class("application-logo-add")
|
logo_box.pack_start(self.logo_image, False, False, 0)
|
||||||
logo_box.pack_start(logo_image, False, False, 0)
|
|
||||||
|
|
||||||
box_entry = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
box_entry = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
icon_entry = Gtk.Entry()
|
self.icon_entry.set_margin_top(10)
|
||||||
icon_entry.set_margin_top(10)
|
self.icon_entry.connect("changed", self.update_icon)
|
||||||
icon_entry.connect("changed", self.update_icon)
|
box_entry.pack_start(self.icon_entry, False, False, 0)
|
||||||
box_entry.pack_start(icon_entry, False, False, 0)
|
|
||||||
|
|
||||||
mainbox.pack_start(logo_box, False, True, 6)
|
main_box.pack_start(logo_box, False, True, 6)
|
||||||
mainbox.pack_end(box_entry, False, True, 6)
|
main_box.pack_end(box_entry, False, True, 6)
|
||||||
|
|
||||||
self.add(mainbox)
|
self.add(main_box)
|
||||||
|
|
||||||
def update_icon(self, entry):
|
def update_icon(self, entry):
|
||||||
|
"""
|
||||||
|
Update icon image on changed event
|
||||||
|
"""
|
||||||
icon_name = entry.get_text()
|
icon_name = entry.get_text()
|
||||||
theme = Gtk.IconTheme.get_default()
|
theme = Gtk.IconTheme.get_default()
|
||||||
apply_button = self.hb.get_children()[1].get_children()[0]
|
apply_button = self.hb.get_children()[1].get_children()[0]
|
||||||
|
@ -61,18 +65,20 @@ class IconFinderWindow(Gtk.Window):
|
||||||
else:
|
else:
|
||||||
icon = theme.load_icon("image-missing", 48, 0)
|
icon = theme.load_icon("image-missing", 48, 0)
|
||||||
apply_button.set_sensitive(False)
|
apply_button.set_sensitive(False)
|
||||||
icon_image = self.get_children()[0].get_children()[0].get_children()[0]
|
self.logo_image.clear()
|
||||||
icon_image.clear()
|
self.logo_image.set_from_pixbuf(icon)
|
||||||
icon_image.set_from_pixbuf(icon)
|
|
||||||
|
|
||||||
def update_logo(self, *args):
|
def update_logo(self, *args):
|
||||||
img_entry = self.get_children()[0].get_children()[1].get_children()[0]
|
"""
|
||||||
self.parent.update_logo(img_entry.get_text().strip())
|
Update application logo and close the window
|
||||||
|
"""
|
||||||
|
self.parent.update_logo(self.icon_entry.get_text().strip())
|
||||||
self.close_window()
|
self.close_window()
|
||||||
|
|
||||||
def generate_headerbar(self):
|
def generate_header_bar(self):
|
||||||
self.hb = Gtk.HeaderBar()
|
"""
|
||||||
|
Generate header bar box
|
||||||
|
"""
|
||||||
left_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
left_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
right_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
right_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
|
|
||||||
|
@ -81,15 +87,17 @@ class IconFinderWindow(Gtk.Window):
|
||||||
cancel_button.get_style_context().add_class("destructive-action")
|
cancel_button.get_style_context().add_class("destructive-action")
|
||||||
left_box.add(cancel_button)
|
left_box.add(cancel_button)
|
||||||
|
|
||||||
apply_button = Gtk.Button.new_with_label(_("Select"))
|
self.apply_button.get_style_context().add_class("suggested-action")
|
||||||
apply_button.get_style_context().add_class("suggested-action")
|
self.apply_button.connect("clicked", self.update_logo)
|
||||||
apply_button.connect("clicked", self.update_logo)
|
self.apply_button.set_sensitive(False)
|
||||||
apply_button.set_sensitive(False)
|
right_box.add(self.apply_button)
|
||||||
right_box.add(apply_button)
|
|
||||||
|
|
||||||
self.hb.pack_start(left_box)
|
self.hb.pack_start(left_box)
|
||||||
self.hb.pack_end(right_box)
|
self.hb.pack_end(right_box)
|
||||||
self.set_titlebar(self.hb)
|
self.set_titlebar(self.hb)
|
||||||
|
|
||||||
def close_window(self, *args):
|
def close_window(self, *args):
|
||||||
|
"""
|
||||||
|
Close the window
|
||||||
|
"""
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
from gi import require_version
|
from gi import require_version
|
||||||
require_version("Gtk", "3.0")
|
require_version("Gtk", "3.0")
|
||||||
from gi.repository import Gtk, GLib, Gio, Gdk, GObject
|
from gi.repository import Gtk, GObject
|
||||||
from TwoFactorAuth.models.code import Code
|
from TwoFactorAuth.models.code import Code
|
||||||
from TwoFactorAuth.models.authenticator import Authenticator
|
from TwoFactorAuth.models.authenticator import Authenticator
|
||||||
from TwoFactorAuth.models.settings import SettingsReader
|
from TwoFactorAuth.models.settings import SettingsReader
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import time
|
from time import sleep
|
||||||
import logging
|
import logging
|
||||||
from math import pi
|
from math import pi
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
@ -18,8 +18,13 @@ class ListBoxRow(Thread):
|
||||||
code = None
|
code = None
|
||||||
code_generated = True
|
code_generated = True
|
||||||
|
|
||||||
|
row = Gtk.ListBoxRow()
|
||||||
|
drawing_area = Gtk.DrawingArea()
|
||||||
|
code_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
|
|
||||||
def __init__(self, parent, id, name, secret_code, logo):
|
def __init__(self, parent, id, name, secret_code, logo):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
|
# Read default values
|
||||||
cfg = SettingsReader()
|
cfg = SettingsReader()
|
||||||
self.counter_max = cfg.read("refresh-time", "preferences")
|
self.counter_max = cfg.read("refresh-time", "preferences")
|
||||||
self.counter = self.counter_max
|
self.counter = self.counter_max
|
||||||
|
@ -33,16 +38,60 @@ class ListBoxRow(Thread):
|
||||||
self.start()
|
self.start()
|
||||||
GObject.timeout_add_seconds(1, self.refresh_listbox)
|
GObject.timeout_add_seconds(1, self.refresh_listbox)
|
||||||
|
|
||||||
def toggle_code_box(self, *args):
|
@staticmethod
|
||||||
code_box = self.row.get_children()[0].get_children()[1]
|
def get_id(row):
|
||||||
is_visible = code_box.get_visible()
|
"""
|
||||||
code_box.set_visible(not is_visible)
|
Get the application id
|
||||||
code_box.set_no_show_all(is_visible)
|
:param row: ListBoxRow
|
||||||
code_box.show_all()
|
:return: (int): row id
|
||||||
|
"""
|
||||||
|
label_id = row.get_children()[0].get_children()[2]
|
||||||
|
label_id = int(label_id.get_text())
|
||||||
|
return label_id
|
||||||
|
|
||||||
def copy_code(self, eventbox, box):
|
@staticmethod
|
||||||
|
def get_label(row):
|
||||||
|
"""
|
||||||
|
Get the application label
|
||||||
|
:param row: ListBoxRow
|
||||||
|
:return: (str): application label
|
||||||
|
"""
|
||||||
|
label = row.get_children()[0].get_children()[0].get_children()[2].get_children()[0].get_text()
|
||||||
|
return label.strip()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_checkbox(row):
|
||||||
|
"""
|
||||||
|
Get ListBowRow's checkbox
|
||||||
|
:param row: ListBoxRow
|
||||||
|
:return: (Gtk.Checkbox)
|
||||||
|
"""
|
||||||
|
return row.get_children()[0].get_children()[0].get_children()[0]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_code_box(row):
|
||||||
|
"""
|
||||||
|
Get code's box
|
||||||
|
:param row: ListBoxRow
|
||||||
|
:return: (Gtk.Box)
|
||||||
|
"""
|
||||||
|
return row.get_children()[0].get_children()[1]
|
||||||
|
|
||||||
|
def toggle_code_box(self, *args):
|
||||||
|
"""
|
||||||
|
Toggle code box
|
||||||
|
"""
|
||||||
|
is_visible = self.code_box.get_visible()
|
||||||
|
self.code_box.set_visible(not is_visible)
|
||||||
|
self.code_box.set_no_show_all(is_visible)
|
||||||
|
self.code_box.show_all()
|
||||||
|
|
||||||
|
def copy_code(self, event_box, box):
|
||||||
|
"""
|
||||||
|
Copy code shows the code box for a while (10s by default)
|
||||||
|
"""
|
||||||
self.timer = 0
|
self.timer = 0
|
||||||
self.parent.copy_code(eventbox)
|
self.parent.copy_code(event_box)
|
||||||
code_box = self.row.get_children()[0].get_children()[1]
|
code_box = self.row.get_children()[0].get_children()[1]
|
||||||
code_box.set_visible(True)
|
code_box.set_visible(True)
|
||||||
code_box.set_no_show_all(False)
|
code_box.set_no_show_all(False)
|
||||||
|
@ -50,6 +99,9 @@ class ListBoxRow(Thread):
|
||||||
GObject.timeout_add_seconds(1, self.update_timer)
|
GObject.timeout_add_seconds(1, self.update_timer)
|
||||||
|
|
||||||
def update_timer(self, *args):
|
def update_timer(self, *args):
|
||||||
|
"""
|
||||||
|
Update timer
|
||||||
|
"""
|
||||||
self.timer += 1
|
self.timer += 1
|
||||||
if self.timer > 10:
|
if self.timer > 10:
|
||||||
code_box = self.row.get_children()[0].get_children()[1]
|
code_box = self.row.get_children()[0].get_children()[1]
|
||||||
|
@ -58,36 +110,36 @@ class ListBoxRow(Thread):
|
||||||
return self.timer <= 10
|
return self.timer <= 10
|
||||||
|
|
||||||
def create_row(self):
|
def create_row(self):
|
||||||
self.row = Gtk.ListBoxRow()
|
"""
|
||||||
|
Create ListBoxRow
|
||||||
|
"""
|
||||||
self.row.get_style_context().add_class("application-list-row")
|
self.row.get_style_context().add_class("application-list-row")
|
||||||
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
h_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
pass_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
self.code_box.set_visible(False)
|
||||||
pass_box.set_visible(False)
|
box.pack_start(h_box, True, True, 6)
|
||||||
vbox.pack_start(hbox, True, True, 6)
|
box.pack_start(self.code_box, True, True, 6)
|
||||||
vbox.pack_start(pass_box, True, True, 6)
|
|
||||||
|
|
||||||
# ID
|
# ID
|
||||||
label_id = Gtk.Label()
|
label_id = Gtk.Label()
|
||||||
label_id.set_text(str(self.id))
|
label_id.set_text(str(self.id))
|
||||||
label_id.set_visible(False)
|
label_id.set_visible(False)
|
||||||
label_id.set_no_show_all(True)
|
label_id.set_no_show_all(True)
|
||||||
vbox.pack_end(label_id, False, False, 0)
|
box.pack_end(label_id, False, False, 0)
|
||||||
|
|
||||||
# Checkbox
|
# Checkbox
|
||||||
checkbox = Gtk.CheckButton()
|
checkbox = Gtk.CheckButton()
|
||||||
checkbox.set_visible(False)
|
checkbox.set_visible(False)
|
||||||
checkbox.set_no_show_all(True)
|
checkbox.set_no_show_all(True)
|
||||||
checkbox.connect("toggled", self.parent.select_application)
|
checkbox.connect("toggled", self.parent.select_application)
|
||||||
hbox.pack_start(checkbox, False, True, 6)
|
h_box.pack_start(checkbox, False, True, 6)
|
||||||
|
|
||||||
# Application logo
|
# Application logo
|
||||||
auth_icon = Authenticator.get_auth_icon(self.logo,
|
auth_icon = Authenticator.get_auth_icon(self.logo, self.parent.app.pkgdatadir)
|
||||||
self.parent.app.pkgdatadir)
|
|
||||||
auth_logo = Gtk.Image(xalign=0)
|
auth_logo = Gtk.Image(xalign=0)
|
||||||
auth_logo.set_from_pixbuf(auth_icon)
|
auth_logo.set_from_pixbuf(auth_icon)
|
||||||
hbox.pack_start(auth_logo, False, True, 6)
|
h_box.pack_start(auth_logo, False, True, 6)
|
||||||
|
|
||||||
# Application name
|
# Application name
|
||||||
name_event = Gtk.EventBox()
|
name_event = Gtk.EventBox()
|
||||||
|
@ -96,40 +148,36 @@ class ListBoxRow(Thread):
|
||||||
application_name.set_text(self.name)
|
application_name.set_text(self.name)
|
||||||
name_event.connect("button-press-event", self.toggle_code_box)
|
name_event.connect("button-press-event", self.toggle_code_box)
|
||||||
name_event.add(application_name)
|
name_event.add(application_name)
|
||||||
hbox.pack_start(name_event, True, True, 6)
|
h_box.pack_start(name_event, True, True, 6)
|
||||||
# Copy button
|
# Copy button
|
||||||
copy_event = Gtk.EventBox()
|
copy_event = Gtk.EventBox()
|
||||||
copy_button = Gtk.Image(xalign=0)
|
copy_button = Gtk.Image(xalign=0)
|
||||||
copy_button.set_from_icon_name("edit-copy-symbolic",
|
copy_button.set_from_icon_name("edit-copy-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
|
||||||
Gtk.IconSize.SMALL_TOOLBAR)
|
|
||||||
copy_button.set_tooltip_text(_("Copy the generated code.."))
|
copy_button.set_tooltip_text(_("Copy the generated code.."))
|
||||||
copy_event.connect("button-press-event", self.copy_code)
|
copy_event.connect("button-press-event", self.copy_code)
|
||||||
copy_event.add(copy_button)
|
copy_event.add(copy_button)
|
||||||
hbox.pack_end(copy_event, False, True, 6)
|
h_box.pack_end(copy_event, False, True, 6)
|
||||||
|
|
||||||
# Remove button
|
# Remove button
|
||||||
remove_event = Gtk.EventBox()
|
remove_event = Gtk.EventBox()
|
||||||
remove_button = Gtk.Image(xalign=0)
|
remove_button = Gtk.Image(xalign=0)
|
||||||
remove_button.set_from_icon_name("user-trash-symbolic",
|
remove_button.set_from_icon_name("user-trash-symbolic", Gtk.IconSize.SMALL_TOOLBAR)
|
||||||
Gtk.IconSize.SMALL_TOOLBAR)
|
|
||||||
remove_button.set_tooltip_text(_("Remove the source.."))
|
remove_button.set_tooltip_text(_("Remove the source.."))
|
||||||
remove_event.add(remove_button)
|
remove_event.add(remove_button)
|
||||||
remove_event.connect("button-press-event",
|
remove_event.connect("button-press-event", self.parent.remove_application)
|
||||||
self.parent.remove_application)
|
h_box.pack_end(remove_event, False, True, 6)
|
||||||
hbox.pack_end(remove_event, False, True, 6)
|
|
||||||
|
|
||||||
self.darea = Gtk.DrawingArea()
|
self.drawing_area.set_size_request(24, 24)
|
||||||
self.darea.set_size_request(24, 24)
|
|
||||||
|
|
||||||
code_label = Gtk.Label(xalign=0)
|
code_label = Gtk.Label(xalign=0)
|
||||||
code_label.get_style_context().add_class("application-secret-code")
|
code_label.get_style_context().add_class("application-secret-code")
|
||||||
# TODO : show the real secret code
|
# TODO : show the real secret code
|
||||||
self.update_code(code_label)
|
self.update_code(code_label)
|
||||||
pass_box.set_no_show_all(True)
|
self.code_box.set_no_show_all(True)
|
||||||
pass_box.pack_end(self.darea, False, True, 6)
|
self.code_box.pack_end(self.drawing_area, False, True, 6)
|
||||||
pass_box.pack_start(code_label, False, True, 6)
|
self.code_box.pack_start(code_label, False, True, 6)
|
||||||
|
|
||||||
self.row.add(vbox)
|
self.row.add(box)
|
||||||
|
|
||||||
def get_counter(self):
|
def get_counter(self):
|
||||||
return self.counter
|
return self.counter
|
||||||
|
@ -142,16 +190,16 @@ class ListBoxRow(Thread):
|
||||||
self.regenerate_code()
|
self.regenerate_code()
|
||||||
if self.timer != 0:
|
if self.timer != 0:
|
||||||
self.timer = 0
|
self.timer = 0
|
||||||
self.darea.connect("draw", self.expose)
|
self.drawing_area.connect("draw", self.expose)
|
||||||
self.row.changed()
|
self.row.changed()
|
||||||
time.sleep(1)
|
sleep(1)
|
||||||
|
|
||||||
def get_listrow(self):
|
def get_list_row(self):
|
||||||
return self.row
|
return self.row
|
||||||
|
|
||||||
def refresh_listbox(self):
|
def refresh_listbox(self):
|
||||||
self.parent.listbox.hide()
|
self.parent.list_box.hide()
|
||||||
self.parent.listbox.show_all()
|
self.parent.list_box.show_all()
|
||||||
return self.code_generated
|
return self.code_generated
|
||||||
|
|
||||||
def regenerate_code(self):
|
def regenerate_code(self):
|
||||||
|
@ -163,7 +211,7 @@ class ListBoxRow(Thread):
|
||||||
def update_code(self, label):
|
def update_code(self, label):
|
||||||
try:
|
try:
|
||||||
code = self.code.get_secret_code()
|
code = self.code.get_secret_code()
|
||||||
if code != None:
|
if code:
|
||||||
label.set_text(code)
|
label.set_text(code)
|
||||||
else:
|
else:
|
||||||
raise TypeError
|
raise TypeError
|
||||||
|
@ -172,13 +220,13 @@ class ListBoxRow(Thread):
|
||||||
label.set_text(_("Couldn't generate the secret code"))
|
label.set_text(_("Couldn't generate the secret code"))
|
||||||
self.code_generated = False
|
self.code_generated = False
|
||||||
|
|
||||||
def expose(self, darea, cairo):
|
def expose(self, drawing_area, cairo):
|
||||||
try:
|
try:
|
||||||
if self.code_generated:
|
if self.code_generated:
|
||||||
cairo.arc(12, 12, 12, 0, (self.counter *
|
cairo.arc(12, 12, 12, 0, (self.counter * 2 * pi / self.counter_max))
|
||||||
2 * pi / self.counter_max))
|
|
||||||
cairo.set_source_rgba(0, 0, 0, 0.4)
|
cairo.set_source_rgba(0, 0, 0, 0.4)
|
||||||
cairo.fill_preserve()
|
cairo.fill_preserve()
|
||||||
|
# TODO : get colors from default theme
|
||||||
if self.counter < self.counter_max / 2:
|
if self.counter < self.counter_max / 2:
|
||||||
cairo.set_source_rgb(0, 0, 0)
|
cairo.set_source_rgb(0, 0, 0)
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -1,36 +1,39 @@
|
||||||
from gi import require_version
|
from gi import require_version
|
||||||
require_version("Gtk", "3.0")
|
require_version("Gtk", "3.0")
|
||||||
from gi.repository import Gtk, GLib, Gio, Gdk, GObject
|
from gi.repository import Gtk
|
||||||
import logging
|
|
||||||
from TwoFactorAuth.models.settings import SettingsReader
|
from TwoFactorAuth.models.settings import SettingsReader
|
||||||
from TwoFactorAuth.widgets.change_password import PasswordWindow
|
from TwoFactorAuth.widgets.change_password import PasswordWindow
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from hashlib import md5
|
|
||||||
|
|
||||||
|
|
||||||
class SettingsWindow(Gtk.Window):
|
class SettingsWindow(Gtk.Window):
|
||||||
|
notebook = Gtk.Notebook()
|
||||||
|
time_spin_button = Gtk.SpinButton()
|
||||||
|
enable_switch = Gtk.Switch()
|
||||||
|
password_button = Gtk.Button()
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
self.parent = parent
|
self.parent = parent
|
||||||
self.cfg = SettingsReader()
|
self.cfg = SettingsReader()
|
||||||
self.generate_window()
|
self.generate_window()
|
||||||
self.generate_compenents()
|
self.generate_components()
|
||||||
self.show_all()
|
self.show_all()
|
||||||
|
|
||||||
def generate_window(self):
|
def generate_window(self):
|
||||||
Gtk.Window.__init__(self, title=_("Settings"), modal=True,
|
Gtk.Window.__init__(self, title=_("Settings"), modal=True,
|
||||||
destroy_with_parent=True)
|
destroy_with_parent=True)
|
||||||
self.connect("delete-event", lambda x, y: self.destroy())
|
self.connect("delete-event", self.close_window)
|
||||||
self.resize(300, 300)
|
self.resize(300, 300)
|
||||||
self.set_size_request(300, 300)
|
self.set_size_request(300, 300)
|
||||||
self.set_resizable(False)
|
self.set_resizable(False)
|
||||||
self.set_position(Gtk.WindowPosition.CENTER)
|
self.set_position(Gtk.WindowPosition.CENTER)
|
||||||
self.set_transient_for(self.parent)
|
self.set_transient_for(self.parent)
|
||||||
|
|
||||||
def generate_compenents(self):
|
def generate_components(self):
|
||||||
self.notebook = Gtk.Notebook()
|
"""
|
||||||
|
Generate all the components
|
||||||
|
"""
|
||||||
self.add(self.notebook)
|
self.add(self.notebook)
|
||||||
|
|
||||||
user_settings = self.generate_user_settings()
|
user_settings = self.generate_user_settings()
|
||||||
user_settings.set_border_width(10)
|
user_settings.set_border_width(10)
|
||||||
self.notebook.append_page(user_settings, Gtk.Label(_('Preferences')))
|
self.notebook.append_page(user_settings, Gtk.Label(_('Preferences')))
|
||||||
|
@ -40,34 +43,40 @@ class SettingsWindow(Gtk.Window):
|
||||||
self.notebook.append_page(login_settings, Gtk.Label(_('Account')))
|
self.notebook.append_page(login_settings, Gtk.Label(_('Account')))
|
||||||
|
|
||||||
def generate_login_settings(self):
|
def generate_login_settings(self):
|
||||||
mainbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
"""
|
||||||
|
Create a box with login settings components
|
||||||
|
:return (Gtk.Box): Box contains all the components
|
||||||
|
"""
|
||||||
|
main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
enable_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
enable_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
enable_label = Gtk.Label(_("Enable password: "))
|
enable_label = Gtk.Label(_("Enable password: "))
|
||||||
enable_switch = Gtk.Switch()
|
self.enable_switch.set_active(bool(self.cfg.read("state", "login")))
|
||||||
enable_switch.set_active(bool(self.cfg.read("state", "login")))
|
self.enable_switch.connect("notify::active", self.on_switch_activated)
|
||||||
enable_switch.connect("notify::active", self.on_switch_activated)
|
|
||||||
|
|
||||||
enable_box.pack_start(enable_label, False, True, 0)
|
enable_box.pack_start(enable_label, False, True, 0)
|
||||||
enable_box.pack_end(enable_switch, False, True, 0)
|
enable_box.pack_end(self.enable_switch, False, True, 0)
|
||||||
|
|
||||||
password_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
password_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
password_label = Gtk.Label(_("Password: "))
|
password_label = Gtk.Label(_("Password: "))
|
||||||
password_button = Gtk.Button()
|
self.password_button.get_style_context().add_class("flat")
|
||||||
password_button.get_style_context().add_class("flat")
|
self.password_button.get_style_context().add_class("text-button")
|
||||||
password_button.get_style_context().add_class("text-button")
|
self.password_button.set_label("******")
|
||||||
password_button.set_label("******")
|
self.password_button.connect("clicked", self.new_password_window)
|
||||||
password_button.connect("clicked", self.new_password_window)
|
|
||||||
|
|
||||||
password_box.pack_start(password_label, False, True, 0)
|
password_box.pack_start(password_label, False, True, 0)
|
||||||
password_box.pack_end(password_button, False, True, 0)
|
password_box.pack_end(self.password_button, False, True, 0)
|
||||||
|
|
||||||
mainbox.pack_start(enable_box, False, True, 6)
|
main_box.pack_start(enable_box, False, True, 6)
|
||||||
mainbox.pack_start(password_box, False, True, 6)
|
main_box.pack_start(password_box, False, True, 6)
|
||||||
return mainbox
|
return main_box
|
||||||
|
|
||||||
def generate_user_settings(self):
|
def generate_user_settings(self):
|
||||||
mainbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
"""
|
||||||
|
Create a box with user settings components
|
||||||
|
:return (Gtk.Box): Box contains all the components
|
||||||
|
"""
|
||||||
|
main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
time_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
time_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
time_label = Gtk.Label(_("Password generation time (s): "))
|
time_label = Gtk.Label(_("Password generation time (s): "))
|
||||||
|
@ -75,31 +84,39 @@ class SettingsWindow(Gtk.Window):
|
||||||
if default_value < 30 or default_value > 120:
|
if default_value < 30 or default_value > 120:
|
||||||
default_value = 30
|
default_value = 30
|
||||||
adjustment = Gtk.Adjustment(default_value, 10, 120, 1, 10, 0)
|
adjustment = Gtk.Adjustment(default_value, 10, 120, 1, 10, 0)
|
||||||
time_spinbutton = Gtk.SpinButton()
|
self.time_spin_button.connect("value-changed", self.on_time_changed)
|
||||||
time_spinbutton.connect("value-changed", self.on_time_changed)
|
self.time_spin_button.set_adjustment(adjustment)
|
||||||
time_spinbutton.set_adjustment(adjustment)
|
self.time_spin_button.set_value(default_value)
|
||||||
time_spinbutton.set_value(default_value)
|
|
||||||
|
|
||||||
time_box.pack_start(time_label, False, True, 0)
|
time_box.pack_start(time_label, False, True, 0)
|
||||||
time_box.pack_end(time_spinbutton, False, True, 0)
|
time_box.pack_end(self.time_spin_button, False, True, 0)
|
||||||
|
|
||||||
mainbox.pack_start(time_box, False, True, 6)
|
main_box.pack_start(time_box, False, True, 6)
|
||||||
return mainbox
|
return main_box
|
||||||
|
|
||||||
def new_password_window(self, *args):
|
def new_password_window(self, *args):
|
||||||
|
"""
|
||||||
|
Show a new password window
|
||||||
|
"""
|
||||||
PasswordWindow(self)
|
PasswordWindow(self)
|
||||||
|
|
||||||
def on_time_changed(self, spinbutton):
|
def on_time_changed(self, spin_button):
|
||||||
self.cfg.update("refresh-time", spinbutton.get_value_as_int(),
|
"""
|
||||||
|
Update time tog generate a new secret code
|
||||||
|
"""
|
||||||
|
self.cfg.update("refresh-time", spin_button.get_value_as_int(),
|
||||||
"preferences")
|
"preferences")
|
||||||
|
|
||||||
""" def on_password_changed(self, entry):
|
def on_switch_activated(self, switch, *args):
|
||||||
password = md5(entry.get_text().encode('utf-8')).hexdigest()
|
"""
|
||||||
self.cfg.update("password", password, "login")
|
Update password state : enabled/disabled
|
||||||
"""
|
"""
|
||||||
|
self.password_button.set_sensitive(switch.get_active())
|
||||||
def on_switch_activated(self, switch, gparam):
|
|
||||||
password_box = switch.get_parent().get_parent().get_children()[1]
|
|
||||||
password_entry = password_box.get_children()[1]
|
|
||||||
password_entry.set_sensitive(switch.get_active())
|
|
||||||
self.cfg.update("state", switch.get_active(), "login")
|
self.cfg.update("state", switch.get_active(), "login")
|
||||||
|
|
||||||
|
|
||||||
|
def close_window(self, *args):
|
||||||
|
"""
|
||||||
|
Close the window
|
||||||
|
"""
|
||||||
|
self.destroy()
|
|
@ -1,31 +1,50 @@
|
||||||
from gi import require_version
|
from gi import require_version
|
||||||
require_version("Gtk", "3.0")
|
require_version("Gtk", "3.0")
|
||||||
from gi.repository import Gtk, GLib, Gio, Gdk, GObject
|
from gi.repository import Gtk, Gio, Gdk
|
||||||
from TwoFactorAuth.widgets.add_authenticator import AddAuthenticator
|
from TwoFactorAuth.widgets.add_authenticator import AddAuthenticator
|
||||||
from TwoFactorAuth.widgets.confirmation import ConfirmationMessage
|
from TwoFactorAuth.widgets.confirmation import ConfirmationMessage
|
||||||
from TwoFactorAuth.widgets.listrow import ListBoxRow
|
from TwoFactorAuth.widgets.listrow import ListBoxRow
|
||||||
from threading import Thread
|
|
||||||
import logging
|
import logging
|
||||||
from hashlib import md5
|
from hashlib import md5
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
|
|
||||||
class Window(Gtk.ApplicationWindow):
|
class Window(Gtk.ApplicationWindow):
|
||||||
app = None
|
app = None
|
||||||
selected_app_idx = None
|
selected_app_idx = None
|
||||||
selected_count = 0
|
selected_count = 0
|
||||||
|
|
||||||
|
hb = Gtk.HeaderBar()
|
||||||
|
main_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
search_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
login_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
|
no_apps_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||||
|
apps_list_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
||||||
|
apps_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
|
||||||
|
list_box = Gtk.ListBox()
|
||||||
|
search_button = Gtk.ToggleButton()
|
||||||
|
add_button = Gtk.Button()
|
||||||
|
remove_button = Gtk.Button()
|
||||||
|
cancel_button = Gtk.Button()
|
||||||
|
select_button = Gtk.Button()
|
||||||
|
|
||||||
|
password_entry = Gtk.Entry()
|
||||||
|
search_entry = Gtk.Entry()
|
||||||
|
|
||||||
def __init__(self, application):
|
def __init__(self, application):
|
||||||
self.app = application
|
self.app = application
|
||||||
self.generate_window()
|
self.generate_window()
|
||||||
self.generate_headerbar()
|
self.generate_header_bar()
|
||||||
self.generate_searchbar()
|
self.generate_search_bar()
|
||||||
self.generate_applications_list()
|
self.generate_applications_list()
|
||||||
if self.app.locked:
|
self.generate_no_apps_box()
|
||||||
self.generate_login_form()
|
self.generate_login_form()
|
||||||
self.get_children()[0].get_children()[0].set_visible(False)
|
self.refresh_window()
|
||||||
|
|
||||||
def generate_window(self, *args):
|
def generate_window(self, *args):
|
||||||
|
"""
|
||||||
|
Generate application window (Gtk.Window)
|
||||||
|
"""
|
||||||
Gtk.ApplicationWindow.__init__(self, Gtk.WindowType.TOPLEVEL,
|
Gtk.ApplicationWindow.__init__(self, Gtk.WindowType.TOPLEVEL,
|
||||||
application=self.app)
|
application=self.app)
|
||||||
self.set_position(Gtk.WindowPosition.CENTER)
|
self.set_position(Gtk.WindowPosition.CENTER)
|
||||||
|
@ -35,319 +54,332 @@ class Window(Gtk.ApplicationWindow):
|
||||||
self.set_resizable(False)
|
self.set_resizable(False)
|
||||||
self.connect("key_press_event", self.on_key_press)
|
self.connect("key_press_event", self.on_key_press)
|
||||||
self.connect("delete-event", lambda x, y: self.app.on_quit())
|
self.connect("delete-event", lambda x, y: self.app.on_quit())
|
||||||
self.add(Gtk.Box(orientation=Gtk.Orientation.VERTICAL))
|
self.add(self.main_box)
|
||||||
|
|
||||||
def on_key_press(self, app, keyevent):
|
def on_key_press(self, app, key_event):
|
||||||
keypressed = Gdk.keyval_name(keyevent.keyval).lower()
|
"""
|
||||||
|
Keyboard Listener handling
|
||||||
|
"""
|
||||||
|
keypress = Gdk.keyval_name(key_event.keyval).lower()
|
||||||
if not self.app.locked:
|
if not self.app.locked:
|
||||||
CONTROL_MASK = Gdk.ModifierType.CONTROL_MASK
|
control_mask = Gdk.ModifierType.CONTROL_MASK
|
||||||
search_box = self.get_children()[0].get_children()[
|
|
||||||
0].get_children()[0]
|
|
||||||
count = self.app.auth.count()
|
count = self.app.auth.count()
|
||||||
if keypressed == "c":
|
if keypress == "c":
|
||||||
if keyevent.state == CONTROL_MASK:
|
if key_event.state == control_mask:
|
||||||
self.copy_code()
|
self.copy_code()
|
||||||
elif keypressed == "f":
|
elif keypress == "f":
|
||||||
if keyevent.state == CONTROL_MASK:
|
if key_event.state == control_mask:
|
||||||
self.toggle_searchobox()
|
self.toggle_search_box()
|
||||||
elif keypressed == "s":
|
elif keypress == "s":
|
||||||
if keyevent.state == CONTROL_MASK:
|
if key_event.state == control_mask:
|
||||||
self.toggle_select()
|
self.toggle_select()
|
||||||
elif keypressed == "n":
|
elif keypress == "n":
|
||||||
if keyevent.state == CONTROL_MASK:
|
if key_event.state == control_mask:
|
||||||
self.add_application()
|
self.add_application()
|
||||||
elif keypressed == "delete" and not search_box.get_visible():
|
elif keypress == "delete" and not self.search_box.get_visible():
|
||||||
self.remove_application()
|
self.remove_application()
|
||||||
elif keypressed == "return":
|
elif keypress == "return":
|
||||||
if count > 0:
|
if count > 0:
|
||||||
if self.listbox.get_selected_row():
|
if self.list_box.get_selected_row():
|
||||||
index = self.listbox.get_selected_row().get_index()
|
index = self.list_box.get_selected_row().get_index()
|
||||||
else:
|
else:
|
||||||
index = 0
|
index = 0
|
||||||
listrow_box = self.listbox.get_row_at_index(index)
|
ListBoxRow.toggle_code_box(self.list_box.get_row_at_index(index))
|
||||||
listbox = listrow_box.get_children()[0]
|
elif keypress == "backspace":
|
||||||
code_box = listbox.get_children()[1]
|
if len(self.search_entry.get_text()) == 0:
|
||||||
is_visible = code_box.get_no_show_all()
|
self.hide_searchbar()
|
||||||
code_box.set_no_show_all(not is_visible)
|
|
||||||
code_box.set_visible(is_visible)
|
|
||||||
code_box.show_all()
|
|
||||||
elif keypressed == "backspace":
|
|
||||||
search_box = self.get_children()[0].get_children()[
|
|
||||||
0].get_children()[0]
|
|
||||||
search_entry = search_box.get_children()[0]
|
|
||||||
if len(search_entry.get_text()) == 0:
|
|
||||||
search_box.set_visible(False)
|
|
||||||
self.listbox.set_filter_func(lambda x, y, z: True, None,
|
|
||||||
False)
|
|
||||||
else:
|
else:
|
||||||
if keypressed == "return":
|
if keypress == "return":
|
||||||
self.on_login_clicked()
|
self.on_unlock_clicked()
|
||||||
|
|
||||||
def filter_applications(self, entry):
|
def filter_applications(self, entry):
|
||||||
data = entry.get_text()
|
data = entry.get_text()
|
||||||
if len(data) != 0:
|
if len(data) != 0:
|
||||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
|
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "edit-clear-symbolic")
|
||||||
"edit-clear-symbolic")
|
|
||||||
else:
|
else:
|
||||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
|
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
|
||||||
None)
|
self.list_box.set_filter_func(self.filter_func, data, False)
|
||||||
self.listbox.set_filter_func(self.filter_func, data, False)
|
|
||||||
|
|
||||||
def generate_searchbar(self):
|
def hide_search_bar(self):
|
||||||
hbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
"""
|
||||||
search_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
Hide search bar
|
||||||
search_entry = Gtk.Entry()
|
"""
|
||||||
search_box.set_margin_left(60)
|
self.search_box.set_visible(False)
|
||||||
search_entry.set_width_chars(21)
|
self.list_box.set_filter_func(lambda x, y, z: True, None, False)
|
||||||
search_entry.connect("changed", self.filter_applications)
|
|
||||||
search_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY,
|
|
||||||
"system-search-symbolic")
|
|
||||||
|
|
||||||
search_box.pack_start(search_entry, False, True, 0)
|
def generate_search_bar(self):
|
||||||
hbox.pack_start(search_box, False, True, 6)
|
"""
|
||||||
self.get_children()[0].pack_start(hbox, True, True, 0)
|
Generate search bar box and entry
|
||||||
search_box.set_no_show_all(True)
|
"""
|
||||||
|
box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
|
box.set_margin_left(60)
|
||||||
|
|
||||||
|
self.search_entry.set_width_chars(22)
|
||||||
|
self.search_entry.connect("changed", self.filter_applications)
|
||||||
|
self.search_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.PRIMARY, "system-search-symbolic")
|
||||||
|
|
||||||
|
box.pack_start(self.search_entry, False, True, 0)
|
||||||
|
self.search_box.pack_start(box, False, True, 6)
|
||||||
|
self.search_box.set_no_show_all(True)
|
||||||
|
self.apps_box.pack_start(self.search_box, False, True, 0)
|
||||||
|
self.main_box.pack_start(self.apps_box, True, True, 0)
|
||||||
|
|
||||||
def remove_selected(self, *args):
|
def remove_selected(self, *args):
|
||||||
message = _("Do you really want to remove this application?")
|
"""
|
||||||
|
Remove selected applications
|
||||||
|
"""
|
||||||
|
message = _("Do you really want to remove selected applications?")
|
||||||
confirmation = ConfirmationMessage(self, message)
|
confirmation = ConfirmationMessage(self, message)
|
||||||
confirmation.show()
|
confirmation.show()
|
||||||
if confirmation.get_confirmation():
|
if confirmation.get_confirmation():
|
||||||
for row in self.listbox.get_children():
|
for row in self.list_box.get_children():
|
||||||
checkbox = self.get_checkbox_from_row(row)
|
checkbox = ListBoxRow.get_checkbox(row)
|
||||||
if checkbox.get_active():
|
if checkbox.get_active():
|
||||||
label_id = row.get_children()[0].get_children()[2]
|
label_id = ListBoxRow.get_id(row)
|
||||||
label_id = int(label_id.get_text())
|
|
||||||
self.app.auth.remove_by_id(label_id)
|
self.app.auth.remove_by_id(label_id)
|
||||||
self.listbox.remove(row)
|
self.list_box.remove(row)
|
||||||
self.listbox.unselect_all()
|
self.list_box.unselect_all()
|
||||||
confirmation.destroy()
|
confirmation.destroy()
|
||||||
self.refresh_window()
|
self.refresh_window()
|
||||||
|
|
||||||
def generate_login_form(self):
|
def generate_login_form(self):
|
||||||
mainbox = self.get_children()[0]
|
"""
|
||||||
|
Generate login form
|
||||||
login_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
"""
|
||||||
password_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
password_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
password_entry = Gtk.Entry()
|
|
||||||
password_entry.set_visibility(False)
|
|
||||||
password_entry.set_placeholder_text(_("Enter your password"))
|
|
||||||
password_box.pack_start(password_entry, False, False, 6)
|
|
||||||
|
|
||||||
login_button = Gtk.Button()
|
self.password_entry.set_visibility(False)
|
||||||
login_button.set_label(_("Login"))
|
self.password_entry.set_placeholder_text(_("Enter your password"))
|
||||||
login_button.connect("clicked", self.on_login_clicked)
|
password_box.pack_start(self.password_entry, False, False, 6)
|
||||||
password_box.pack_start(login_button, False, False, 6)
|
|
||||||
login_box.pack_start(password_box, True, False, 6)
|
|
||||||
|
|
||||||
mainbox.pack_start(login_box, True, False, 0)
|
unlock_button = Gtk.Button()
|
||||||
mainbox.get_children()[0].set_no_show_all(self.app.locked)
|
unlock_button.set_label(_("Unlock"))
|
||||||
mainbox.get_children()[1].set_no_show_all(self.app.locked)
|
unlock_button.connect("clicked", self.on_unlock_clicked)
|
||||||
mainbox.get_children()[2].set_no_show_all(not self.app.locked)
|
|
||||||
self.hide_headerbar()
|
|
||||||
|
|
||||||
def on_login_clicked(self, *args):
|
password_box.pack_start(unlock_button, False, False, 6)
|
||||||
login_box = self.get_children()[0].get_children()[2]
|
self.login_box.pack_start(password_box, True, False, 6)
|
||||||
entry = login_box.get_children()[0].get_children()[0]
|
|
||||||
password = md5(entry.get_text().encode("utf-8")).hexdigest()
|
self.main_box.pack_start(self.login_box, True, False, 0)
|
||||||
|
|
||||||
|
def on_unlock_clicked(self, *args):
|
||||||
|
"""
|
||||||
|
Password check and unlock
|
||||||
|
"""
|
||||||
|
password = self.password_entry.get_text()
|
||||||
|
password = md5(password.encode("utf-8")).hexdigest()
|
||||||
if password == self.app.cfg.read("password", "login"):
|
if password == self.app.cfg.read("password", "login"):
|
||||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "")
|
self.password_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, None)
|
||||||
self.toggle_app_lock()
|
self.toggle_app_lock()
|
||||||
|
self.app.locked = False
|
||||||
|
self.app.toggle_settings_menu()
|
||||||
|
self.password_entry.set_text("")
|
||||||
|
self.app.toggle_app_lock_menu()
|
||||||
else:
|
else:
|
||||||
entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY,
|
self.password_entry.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "dialog-error-symbolic")
|
||||||
"dialog-error-symbolic")
|
|
||||||
|
|
||||||
def toggle_app_lock(self):
|
def toggle_app_lock(self):
|
||||||
|
"""
|
||||||
|
Lock/unlock the application
|
||||||
|
"""
|
||||||
self.app.locked = not self.app.locked
|
self.app.locked = not self.app.locked
|
||||||
mainbox = self.get_children()[0]
|
self.app.toggle_app_lock_menu()
|
||||||
mainbox.get_children()[2].set_visible(self.app.locked)
|
|
||||||
mainbox.get_children()[2].set_no_show_all(not self.app.locked)
|
|
||||||
self.refresh_window()
|
self.refresh_window()
|
||||||
|
|
||||||
def hide_headerbar(self):
|
def toggle_boxes(self, apps_box, no_apps_box, login_box):
|
||||||
hb = self.get_children()[1]
|
"""
|
||||||
hb.get_children()[0].get_children()[0].set_no_show_all(True)
|
Change the status of all the boxes in one time
|
||||||
hb.get_children()[0].get_children()[1].set_no_show_all(True)
|
:param apps_box: bool
|
||||||
hb.get_children()[1].get_children()[0].set_no_show_all(True)
|
:param no_apps_box: bool
|
||||||
hb.get_children()[1].get_children()[1].set_no_show_all(True)
|
:param login_box: bool
|
||||||
hb.get_children()[1].get_children()[2].set_no_show_all(True)
|
:return:
|
||||||
|
"""
|
||||||
|
self.login_box.set_visible(login_box)
|
||||||
|
self.login_box.set_no_show_all(not login_box)
|
||||||
|
self.apps_box.set_visible(apps_box)
|
||||||
|
self.apps_box.set_no_show_all(not apps_box)
|
||||||
|
self.no_apps_box.set_visible(no_apps_box)
|
||||||
|
self.no_apps_box.set_no_show_all(not no_apps_box)
|
||||||
|
|
||||||
def generate_headerbar(self):
|
def hide_header_bar(self):
|
||||||
hb = Gtk.HeaderBar()
|
"""
|
||||||
hb.set_show_close_button(True)
|
Hide all buttons on the header bar
|
||||||
|
"""
|
||||||
|
self.toggle_hb_buttons(False, False, False, False, False)
|
||||||
|
|
||||||
|
def generate_header_bar(self):
|
||||||
|
"""
|
||||||
|
Generate a header bar box
|
||||||
|
"""
|
||||||
|
self.hb.set_show_close_button(True)
|
||||||
|
|
||||||
left_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
left_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
right_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
right_box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
||||||
Gtk.StyleContext.add_class(left_box.get_style_context(), "linked")
|
|
||||||
Gtk.StyleContext.add_class(right_box.get_style_context(), "linked")
|
|
||||||
self.remove_button = Gtk.Button()
|
|
||||||
remove_icon = Gio.ThemedIcon(name="user-trash-symbolic")
|
remove_icon = Gio.ThemedIcon(name="user-trash-symbolic")
|
||||||
remove_image = Gtk.Image.new_from_gicon(remove_icon,
|
remove_image = Gtk.Image.new_from_gicon(remove_icon, Gtk.IconSize.BUTTON)
|
||||||
Gtk.IconSize.BUTTON)
|
self.remove_button.set_tooltip_text(_("Remove selected two factor auth sources"))
|
||||||
self.remove_button.set_tooltip_text(_("Remove selected two factor auth "
|
|
||||||
"sources"))
|
|
||||||
self.remove_button.set_sensitive(False)
|
|
||||||
self.remove_button.set_image(remove_image)
|
self.remove_button.set_image(remove_image)
|
||||||
|
self.remove_button.set_sensitive(False)
|
||||||
self.remove_button.set_no_show_all(True)
|
self.remove_button.set_no_show_all(True)
|
||||||
self.remove_button.connect("clicked", self.remove_selected)
|
self.remove_button.connect("clicked", self.remove_selected)
|
||||||
left_box.add(self.remove_button)
|
|
||||||
|
|
||||||
add_button = Gtk.Button()
|
|
||||||
add_icon = Gio.ThemedIcon(name="list-add-symbolic")
|
add_icon = Gio.ThemedIcon(name="list-add-symbolic")
|
||||||
add_image = Gtk.Image.new_from_gicon(add_icon,
|
add_image = Gtk.Image.new_from_gicon(add_icon, Gtk.IconSize.BUTTON)
|
||||||
Gtk.IconSize.BUTTON)
|
self.add_button.set_tooltip_text(_("Add a new application"))
|
||||||
add_button.set_tooltip_text(_("Add a new application"))
|
self.add_button.set_image(add_image)
|
||||||
add_button.set_image(add_image)
|
self.add_button.connect("clicked", self.add_application)
|
||||||
add_button.connect("clicked", self.add_application)
|
|
||||||
left_box.add(add_button)
|
left_box.add(self.remove_button)
|
||||||
|
left_box.add(self.add_button)
|
||||||
|
|
||||||
select_button = Gtk.Button()
|
|
||||||
select_icon = Gio.ThemedIcon(name="object-select-symbolic")
|
select_icon = Gio.ThemedIcon(name="object-select-symbolic")
|
||||||
select_image = Gtk.Image.new_from_gicon(select_icon,
|
select_image = Gtk.Image.new_from_gicon(select_icon, Gtk.IconSize.BUTTON)
|
||||||
Gtk.IconSize.BUTTON)
|
self.select_button.set_tooltip_text(_("Select mode"))
|
||||||
select_button.set_tooltip_text(_("Select mode"))
|
self.select_button.set_image(select_image)
|
||||||
select_button.set_image(select_image)
|
self.select_button.connect("clicked", self.toggle_select)
|
||||||
select_button.connect("clicked", self.toggle_select)
|
self.select_button.set_no_show_all(not self.app.auth.count() > 0)
|
||||||
select_button.set_no_show_all(not self.app.auth.count() > 0)
|
|
||||||
|
|
||||||
search_button = Gtk.ToggleButton()
|
|
||||||
search_icon = Gio.ThemedIcon(name="system-search-symbolic")
|
search_icon = Gio.ThemedIcon(name="system-search-symbolic")
|
||||||
search_image = Gtk.Image.new_from_gicon(search_icon,
|
search_image = Gtk.Image.new_from_gicon(search_icon, Gtk.IconSize.BUTTON)
|
||||||
Gtk.IconSize.BUTTON)
|
self.search_button.set_tooltip_text(_("Search"))
|
||||||
search_button.set_tooltip_text(_("Search"))
|
self.search_button.set_image(search_image)
|
||||||
search_button.set_image(search_image)
|
self.search_button.connect("clicked", self.toggle_search_box)
|
||||||
search_button.connect("clicked", self.toggle_searchobox)
|
self.search_button.set_no_show_all(not self.app.auth.count() > 0)
|
||||||
search_button.set_no_show_all(not self.app.auth.count() > 0)
|
|
||||||
|
|
||||||
cancel_buton = Gtk.Button()
|
self.cancel_button.set_label(_("Cancel"))
|
||||||
cancel_buton.set_label(_("Cancel"))
|
self.cancel_button.connect("clicked", self.toggle_select)
|
||||||
cancel_buton.connect("clicked", self.toggle_select)
|
self.cancel_button.set_no_show_all(True)
|
||||||
cancel_buton.set_no_show_all(True)
|
|
||||||
|
|
||||||
right_box.add(search_button)
|
right_box.add(self.search_button)
|
||||||
right_box.add(select_button)
|
right_box.add(self.select_button)
|
||||||
right_box.add(cancel_buton)
|
right_box.add(self.cancel_button)
|
||||||
|
|
||||||
hb.pack_start(left_box)
|
self.hb.pack_start(left_box)
|
||||||
hb.pack_end(right_box)
|
self.hb.pack_end(right_box)
|
||||||
self.set_titlebar(hb)
|
self.set_titlebar(self.hb)
|
||||||
|
|
||||||
def add_application(self, *args):
|
def add_application(self, *args):
|
||||||
|
"""
|
||||||
|
Create add application window
|
||||||
|
"""
|
||||||
AddAuthenticator(self)
|
AddAuthenticator(self)
|
||||||
|
|
||||||
def toggle_searchobox(self, *args):
|
def toggle_search_box(self, *args):
|
||||||
|
"""
|
||||||
|
Toggle search box, only if there's an application
|
||||||
|
"""
|
||||||
if self.app.auth.count() > 0:
|
if self.app.auth.count() > 0:
|
||||||
search_box = self.get_children()[0].get_children()[
|
is_visible = self.search_box.get_no_show_all()
|
||||||
0].get_children()[0]
|
|
||||||
is_visible = search_box.get_no_show_all()
|
self.search_box.set_no_show_all(not is_visible)
|
||||||
|
self.search_box.set_visible(is_visible)
|
||||||
|
self.search_box.show_all()
|
||||||
|
|
||||||
headerbar = self.get_children()[1]
|
|
||||||
search_button = headerbar.get_children()[1].get_children()[0]
|
|
||||||
search_box.set_no_show_all(not is_visible)
|
|
||||||
search_box.set_visible(is_visible)
|
|
||||||
search_box.show_all()
|
|
||||||
if is_visible:
|
if is_visible:
|
||||||
search_button.get_style_context().add_class("toggle")
|
self.search_button.get_style_context().add_class("toggle")
|
||||||
search_box.get_children()[0].grab_focus_without_selecting()
|
self.search_entry.grab_focus_without_selecting()
|
||||||
else:
|
else:
|
||||||
search_button.get_style_context().remove_class("toggle")
|
self.search_button.get_style_context().remove_class("toggle")
|
||||||
self.listbox.set_filter_func(lambda x, y, z: True, None, False)
|
self.list_box.set_filter_func(lambda x, y, z: True, None, False)
|
||||||
|
|
||||||
def toggle_select(self, *args):
|
def toggle_select(self, *args):
|
||||||
i = 0
|
"""
|
||||||
button_visible = self.remove_button.get_visible()
|
Toggle select mode
|
||||||
headerbar = self.get_children()[1]
|
"""
|
||||||
headerbar.set_show_close_button(button_visible)
|
is_visible = self.remove_button.get_visible()
|
||||||
headerbar.get_children()[0].get_children()[
|
|
||||||
1].set_visible(button_visible)
|
|
||||||
headerbar.get_children()[1].get_children()[
|
|
||||||
1].set_visible(button_visible)
|
|
||||||
headerbar.get_children()[1].get_children()[
|
|
||||||
2].set_visible(not button_visible)
|
|
||||||
self.remove_button.set_visible(not button_visible)
|
|
||||||
self.remove_button.set_no_show_all(button_visible)
|
|
||||||
|
|
||||||
if not button_visible:
|
self.remove_button.set_visible(not is_visible)
|
||||||
self.listbox.set_selection_mode(Gtk.SelectionMode.MULTIPLE)
|
self.remove_button.set_no_show_all(is_visible)
|
||||||
headerbar.get_style_context().add_class("selection-mode")
|
|
||||||
|
self.hb.set_show_close_button(is_visible)
|
||||||
|
self.cancel_button.set_visible(not is_visible)
|
||||||
|
self.remove_button.set_visible(not is_visible)
|
||||||
|
self.add_button.set_visible(is_visible)
|
||||||
|
self.select_button.set_visible(is_visible)
|
||||||
|
|
||||||
|
if not is_visible:
|
||||||
|
self.list_box.set_selection_mode(Gtk.SelectionMode.MULTIPLE)
|
||||||
|
self.hb.get_style_context().add_class("selection-mode")
|
||||||
else:
|
else:
|
||||||
self.listbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
self.list_box.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
||||||
headerbar.get_style_context().remove_class("selection-mode")
|
self.hb.get_style_context().remove_class("selection-mode")
|
||||||
|
|
||||||
if self.selected_app_idx:
|
if self.selected_app_idx:
|
||||||
index = self.selected_app_idx
|
index = self.selected_app_idx
|
||||||
else:
|
else:
|
||||||
index = 0
|
index = 0
|
||||||
listrow_box = self.listbox.get_row_at_index(index)
|
list_row_box = self.list_box.get_row_at_index(index)
|
||||||
self.listbox.select_row(listrow_box)
|
self.list_box.select_row(list_row_box)
|
||||||
|
|
||||||
for row in self.listbox.get_children():
|
for row in self.list_box.get_children():
|
||||||
checkbox = self.get_checkbox_from_row(row)
|
checkbox = ListBoxRow.get_checkbox(row)
|
||||||
visible = checkbox.get_visible()
|
visible = checkbox.get_visible()
|
||||||
selected = checkbox.get_active()
|
selected = checkbox.get_active()
|
||||||
if not button_visible:
|
if not is_visible:
|
||||||
self.select_application(checkbox)
|
self.select_application(checkbox)
|
||||||
checkbox.set_visible(not visible)
|
checkbox.set_visible(not visible)
|
||||||
checkbox.set_no_show_all(visible)
|
checkbox.set_no_show_all(visible)
|
||||||
|
|
||||||
def select_application(self, checkbutton):
|
def select_application(self, checkbutton):
|
||||||
|
"""
|
||||||
|
Select an application in the application ListBox
|
||||||
|
:param checkbutton:
|
||||||
|
"""
|
||||||
is_active = checkbutton.get_active()
|
is_active = checkbutton.get_active()
|
||||||
|
is_visible = checkbutton.get_visible()
|
||||||
listbox_row = checkbutton.get_parent().get_parent().get_parent()
|
listbox_row = checkbutton.get_parent().get_parent().get_parent()
|
||||||
if is_active:
|
if is_active:
|
||||||
self.listbox.select_row(listbox_row)
|
self.list_box.select_row(listbox_row)
|
||||||
if is_visible:
|
if is_visible:
|
||||||
self.selected_count += 1
|
self.selected_count += 1
|
||||||
else:
|
else:
|
||||||
self.listbox.unselect_row(listbox_row)
|
self.list_box.unselect_row(listbox_row)
|
||||||
if is_visible:
|
if is_visible:
|
||||||
self.selected_count -= 1
|
self.selected_count -= 1
|
||||||
self.remove_button.set_sensitive(self.selected_count > 0)
|
self.remove_button.set_sensitive(self.selected_count > 0)
|
||||||
|
|
||||||
def filter_func(self, row, data, notify_destroy):
|
def filter_func(self, row, data, notify_destroy):
|
||||||
app_label = row.get_children()[0].get_children()[0].get_children()
|
"""
|
||||||
|
Filter function, used to check if the entered data exists on the application ListBox
|
||||||
|
"""
|
||||||
|
app_label = ListBoxRow.get_label(row)
|
||||||
data = data.strip()
|
data = data.strip()
|
||||||
if len(data) > 0:
|
if len(data) > 0:
|
||||||
return data in app_label[2].get_children()[0].get_text().lower()
|
return data in app_label
|
||||||
else:
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def get_checkbox_from_row(self, row):
|
def select_row(self, list_box, listbox_row):
|
||||||
if row:
|
"""
|
||||||
return row.get_children()[0].get_children()[0].get_children()[0]
|
Select row @override the clicked event by default for ListBoxRow
|
||||||
else:
|
"""
|
||||||
return None
|
|
||||||
|
|
||||||
def select_row(self, listbox, listbox_row):
|
|
||||||
index = listbox_row.get_index()
|
index = listbox_row.get_index()
|
||||||
button_visible = self.remove_button.get_visible()
|
button_visible = self.remove_button.get_visible()
|
||||||
checkbox = self.get_checkbox_from_row(listbox_row)
|
checkbox = ListBoxRow.get_checkbox(listbox_row)
|
||||||
if button_visible:
|
if button_visible:
|
||||||
checkbox.set_active(not checkbox.get_active())
|
checkbox.set_active(not checkbox.get_active())
|
||||||
else:
|
else:
|
||||||
if self.selected_app_idx:
|
if self.selected_app_idx:
|
||||||
listrow_box = self.listbox.get_row_at_index(
|
self.list_box.unselect_row(self.list_box.get_row_at_index(self.selected_app_idx))
|
||||||
self.selected_app_idx)
|
|
||||||
self.listbox.unselect_row(listbox_row)
|
|
||||||
self.selected_app_idx = index
|
self.selected_app_idx = index
|
||||||
listrow_box = self.listbox.get_row_at_index(index)
|
self.list_box.select_row(self.list_box.get_row_at_index(index))
|
||||||
self.listbox.select_row(listbox_row)
|
|
||||||
|
|
||||||
# TODO : show a nice message when no application is added
|
|
||||||
def generate_applications_list(self):
|
def generate_applications_list(self):
|
||||||
list_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
"""
|
||||||
|
Generate an application ListBox inside of a ScrolledWindow
|
||||||
|
"""
|
||||||
count = self.app.auth.count()
|
count = self.app.auth.count()
|
||||||
|
|
||||||
# Create a ScrolledWindow for installed applications
|
# Create a ScrolledWindow for installed applications
|
||||||
self.listbox = Gtk.ListBox()
|
self.list_box.get_style_context().add_class("applications-list")
|
||||||
self.listbox.get_style_context().add_class("applications-list")
|
self.list_box.set_adjustment()
|
||||||
self.listbox.set_adjustment()
|
self.list_box.connect("row_activated", self.select_row)
|
||||||
self.listbox.connect("row_activated", self.select_row)
|
self.list_box.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
||||||
self.listbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
self.apps_list_box.pack_start(self.list_box, True, True, 0)
|
||||||
list_box.pack_start(self.listbox, True, True, 0)
|
|
||||||
|
|
||||||
scrolled_win = Gtk.ScrolledWindow()
|
scrolled_win = Gtk.ScrolledWindow()
|
||||||
scrolled_win.add_with_viewport(list_box)
|
scrolled_win.add_with_viewport(self.apps_list_box)
|
||||||
self.get_children()[0].get_children()[0].pack_start(
|
self.apps_box.pack_start(scrolled_win, True, True, 0)
|
||||||
scrolled_win, True, True, 0)
|
|
||||||
|
|
||||||
apps = self.app.auth.fetch_apps()
|
apps = self.app.auth.fetch_apps()
|
||||||
i = 0
|
i = 0
|
||||||
|
@ -355,39 +387,46 @@ class Window(Gtk.ApplicationWindow):
|
||||||
while i < count:
|
while i < count:
|
||||||
row = ListBoxRow(self, apps[i][0], apps[i][1], apps[i][2],
|
row = ListBoxRow(self, apps[i][0], apps[i][1], apps[i][2],
|
||||||
apps[i][3])
|
apps[i][3])
|
||||||
self.listbox.add(row.get_listrow())
|
self.list_box.add(row.get_list_row())
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
nolist_box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=6)
|
def generate_no_apps_box(self):
|
||||||
vbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
"""
|
||||||
|
Generate a box with no applications message
|
||||||
|
"""
|
||||||
logo_image = Gtk.Image()
|
logo_image = Gtk.Image()
|
||||||
logo_image.set_from_icon_name("dialog-information-symbolic",
|
logo_image.set_from_icon_name("dialog-information-symbolic",
|
||||||
Gtk.IconSize.DIALOG)
|
Gtk.IconSize.DIALOG)
|
||||||
vbox.pack_start(logo_image, False, False, 6)
|
|
||||||
|
|
||||||
no_proivders_label = Gtk.Label()
|
no_apps_label = Gtk.Label()
|
||||||
no_proivders_label.set_text(_("There's no application at the moment"))
|
no_apps_label.set_text(_("There's no application at the moment"))
|
||||||
vbox.pack_start(no_proivders_label, False, False, 6)
|
|
||||||
|
|
||||||
nolist_box.pack_start(vbox, True, True, 0)
|
self.no_apps_box.pack_start(logo_image, False, False, 6)
|
||||||
self.get_children()[0].pack_start(nolist_box, True, True, 0)
|
self.no_apps_box.pack_start(no_apps_label, False, False, 6)
|
||||||
self.get_children()[0].get_children()[0].set_no_show_all(count == 0)
|
self.main_box.pack_start(self.no_apps_box, True, False, 0)
|
||||||
self.get_children()[0].get_children()[
|
|
||||||
1].set_no_show_all(not count == 0)
|
|
||||||
|
|
||||||
def update_list(self, id, name, secret_code, image):
|
def append(self, uid, name, secret_code, image):
|
||||||
row = ListBoxRow(self, id, name, secret_code, image)
|
"""
|
||||||
self.listbox.add(row.get_listrow())
|
Add an element to the ListBox
|
||||||
self.listbox.show_all()
|
:param uid: application id
|
||||||
|
:param name: application name
|
||||||
|
:param secret_code: application secret code
|
||||||
|
:param image: application image path or icon name
|
||||||
|
"""
|
||||||
|
row = ListBoxRow(self, uid, name, secret_code, image)
|
||||||
|
self.list_box.add(row.get_list_row())
|
||||||
|
self.list_box.show_all()
|
||||||
|
|
||||||
def copy_code(self, *args):
|
def copy_code(self, *args):
|
||||||
|
"""
|
||||||
|
Copy the secret code to clipboard
|
||||||
|
"""
|
||||||
if len(args) > 0:
|
if len(args) > 0:
|
||||||
|
# if the code is called by clicking on copy button, select the right ListBowRow
|
||||||
row = args[0].get_parent().get_parent().get_parent()
|
row = args[0].get_parent().get_parent().get_parent()
|
||||||
self.listbox.select_row(row)
|
self.list_box.select_row(row)
|
||||||
selected_row = self.listbox.get_selected_row()
|
selected_row = self.list_box.get_selected_row()
|
||||||
label = selected_row.get_children()[0].get_children()[1].get_children()
|
code = ListBoxRow.get_label(selected_row)
|
||||||
code = label[0].get_text()
|
|
||||||
try:
|
try:
|
||||||
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD)
|
||||||
clipboard.clear()
|
clipboard.clear()
|
||||||
|
@ -397,56 +436,70 @@ class Window(Gtk.ApplicationWindow):
|
||||||
logging.error(str(e))
|
logging.error(str(e))
|
||||||
|
|
||||||
def refresh_window(self):
|
def refresh_window(self):
|
||||||
mainbox = self.get_children()[0]
|
"""
|
||||||
|
Refresh windows components
|
||||||
|
"""
|
||||||
count = self.app.auth.count()
|
count = self.app.auth.count()
|
||||||
headerbar = self.get_children()[1]
|
is_locked = self.app.locked
|
||||||
if count == 0:
|
|
||||||
mainbox.get_children()[0].set_visible(False)
|
if is_locked:
|
||||||
mainbox.get_children()[1].set_visible(True)
|
self.toggle_boxes(False, False, True)
|
||||||
mainbox.get_children()[1].set_no_show_all(False)
|
self.hide_header_bar()
|
||||||
mainbox.get_children()[1].show_all()
|
|
||||||
headerbar.get_children()[0].get_children()[1].set_visible(True)
|
|
||||||
headerbar.get_children()[1].get_children()[1].set_visible(False)
|
|
||||||
headerbar.get_children()[1].get_children()[2].set_visible(False)
|
|
||||||
headerbar.set_show_close_button(True)
|
|
||||||
headerbar.get_style_context().remove_class("selection-mode")
|
|
||||||
else:
|
else:
|
||||||
self.get_children()[0].get_children()[0].set_no_show_all(False)
|
if count == 0:
|
||||||
self.get_children()[0].get_children()[0].set_visible(True)
|
self.toggle_boxes(False, True, False)
|
||||||
self.get_children()[0].get_children()[0].show_all()
|
self.toggle_hb_buttons(False, True, False, False, False)
|
||||||
headerbar.get_children()[0].get_children()[0].set_visible(False)
|
else:
|
||||||
headerbar.get_children()[1].get_children()[0].set_visible(True)
|
self.toggle_boxes(True, False, False)
|
||||||
headerbar.get_children()[1].get_children()[1].set_visible(True)
|
self.toggle_hb_buttons(False, True, True, True, False)
|
||||||
mainbox.get_children()[1].set_visible(False)
|
|
||||||
|
|
||||||
headerbar.get_children()[0].get_children()[1].set_no_show_all(False)
|
self.main_box.show_all()
|
||||||
headerbar.get_children()[0].get_children()[1].set_visible(True)
|
self.list_box.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
||||||
|
|
||||||
headerbar = self.get_children()[1]
|
def toggle_hb_buttons(self, remove, add, search, select, cancel):
|
||||||
left_box = headerbar.get_children()[0]
|
"""
|
||||||
right_box = headerbar.get_children()[1]
|
Toggle header bar buttons visibilty
|
||||||
right_box.get_children()[0].set_visible(count > 0)
|
:param remove: (bool)
|
||||||
self.listbox.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
:param add: (bool)
|
||||||
left_box.get_children()[0].set_visible(False)
|
:param search: (bool)
|
||||||
|
:param select: (bool)
|
||||||
|
:param cancel: (bool)
|
||||||
|
"""
|
||||||
|
self.add_button.set_visible(add)
|
||||||
|
self.add_button.set_no_show_all(not add)
|
||||||
|
self.remove_button.set_visible(remove)
|
||||||
|
self.remove_button.set_no_show_all(not remove)
|
||||||
|
self.cancel_button.set_visible(cancel)
|
||||||
|
self.cancel_button.set_no_show_all(not cancel)
|
||||||
|
self.select_button.set_visible(select)
|
||||||
|
self.select_button.set_no_show_all(not select)
|
||||||
|
self.search_button.set_visible(search)
|
||||||
|
self.search_button.set_no_show_all(not search)
|
||||||
|
|
||||||
def remove_application(self, *args):
|
def remove_application(self, *args):
|
||||||
|
"""
|
||||||
|
Remove an application
|
||||||
|
"""
|
||||||
if len(args) > 0:
|
if len(args) > 0:
|
||||||
row = args[0].get_parent().get_parent().get_parent()
|
row = args[0].get_parent().get_parent().get_parent()
|
||||||
self.listbox.select_row(row)
|
self.list_box.select_row(row)
|
||||||
|
|
||||||
message = _("Do you really want to remove the application?")
|
message = _("Do you really want to remove the application?")
|
||||||
confirmation = ConfirmationMessage(self, message)
|
confirmation = ConfirmationMessage(self, message)
|
||||||
confirmation.show()
|
confirmation.show()
|
||||||
if confirmation.get_confirmation():
|
if confirmation.get_confirmation():
|
||||||
if self.listbox.get_selected_row():
|
if self.list_box.get_selected_row():
|
||||||
selected_row = self.listbox.get_selected_row()
|
selected_row = self.list_box.get_selected_row()
|
||||||
self.listbox.remove(selected_row)
|
self.list_box.remove(selected_row)
|
||||||
label_id = selected_row.get_children()[0].get_children()[2]
|
app_id = ListBoxRow.get_id(selected_row)
|
||||||
self.app.auth.remove_by_id(int(label_id.get_text()))
|
self.app.auth.remove_by_id(app_id)
|
||||||
confirmation.destroy()
|
confirmation.destroy()
|
||||||
self.refresh_window()
|
self.refresh_window()
|
||||||
|
|
||||||
def show_about(self, *args):
|
def show_about(self, *args):
|
||||||
|
"""
|
||||||
|
Shows about dialog
|
||||||
|
"""
|
||||||
builder = Gtk.Builder()
|
builder = Gtk.Builder()
|
||||||
builder.add_from_file(self.app.pkgdatadir + "/data/about.ui")
|
builder.add_from_file(self.app.pkgdatadir + "/data/about.ui")
|
||||||
|
|
||||||
|
@ -456,9 +509,13 @@ class Window(Gtk.ApplicationWindow):
|
||||||
dialog.destroy()
|
dialog.destroy()
|
||||||
|
|
||||||
def show_shortcuts(self, *args):
|
def show_shortcuts(self, *args):
|
||||||
builder = Gtk.Builder()
|
"""
|
||||||
builder.add_from_file(self.app.pkgdatadir + "/data/shortcuts.ui")
|
Shows keyboard shortcuts
|
||||||
|
"""
|
||||||
|
if Gtk.get_major_version() >= 3 and Gtk.get_minor_version() >= 20:
|
||||||
|
builder = Gtk.Builder()
|
||||||
|
builder.add_from_file(self.app.pkgdatadir + "/data/shortcuts.ui")
|
||||||
|
|
||||||
shortcuts = builder.get_object("shortcuts")
|
shortcuts = builder.get_object("shortcuts")
|
||||||
shortcuts.set_transient_for(self)
|
shortcuts.set_transient_for(self)
|
||||||
shortcuts.show()
|
shortcuts.show()
|
||||||
|
|
|
@ -6,7 +6,6 @@ desktop_DATA = twofactorauth.desktop
|
||||||
uidir = $(pkgdatadir)/data
|
uidir = $(pkgdatadir)/data
|
||||||
ui_DATA = about.ui \
|
ui_DATA = about.ui \
|
||||||
shortcuts.ui \
|
shortcuts.ui \
|
||||||
menu.ui \
|
|
||||||
style.css
|
style.css
|
||||||
|
|
||||||
appdatadir = $(pkgdatadir)/appdata
|
appdatadir = $(pkgdatadir)/appdata
|
||||||
|
|
28
data/menu.ui
28
data/menu.ui
|
@ -1,28 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<interface>
|
|
||||||
<menu id="app-menu">
|
|
||||||
<section>
|
|
||||||
<item>
|
|
||||||
<attribute name="action">app.settings</attribute>
|
|
||||||
<attribute name="label" translatable="yes">Settings</attribute>
|
|
||||||
</item>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<item>
|
|
||||||
<attribute name="action">app.shortcuts</attribute>
|
|
||||||
<attribute name="label" translatable="yes">Keyboard shortcuts</attribute>
|
|
||||||
</item>
|
|
||||||
</section>
|
|
||||||
<section>
|
|
||||||
<item>
|
|
||||||
<attribute name="action">app.about</attribute>
|
|
||||||
<attribute name="label" translatable="yes">About</attribute>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<attribute name="action">app.quit</attribute>
|
|
||||||
<attribute name="label" translatable="yes">Quit</attribute>
|
|
||||||
<attribute name="accel"><Primary>q</attribute>
|
|
||||||
</item>
|
|
||||||
</section>
|
|
||||||
</menu>
|
|
||||||
</interface>
|
|
|
@ -8,7 +8,7 @@ msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Project-Id-Version: PACKAGE VERSION\n"
|
"Project-Id-Version: PACKAGE VERSION\n"
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2016-05-29 22:16+0200\n"
|
"POT-Creation-Date: 2016-05-30 05:45+0200\n"
|
||||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
|
||||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
|
||||||
"Language-Team: LANGUAGE <LL@li.org>\n"
|
"Language-Team: LANGUAGE <LL@li.org>\n"
|
||||||
|
@ -17,7 +17,8 @@ msgstr ""
|
||||||
"Content-Type: text/plain; charset=CHARSET\n"
|
"Content-Type: text/plain; charset=CHARSET\n"
|
||||||
"Content-Transfer-Encoding: 8bit\n"
|
"Content-Transfer-Encoding: 8bit\n"
|
||||||
|
|
||||||
#: data/menu.ui:7 TwoFactorAuth/widgets/settings.py:21
|
#: data/menu.ui:7 TwoFactorAuth/widgets/settings.py:23
|
||||||
|
#: TwoFactorAuth/application.py:52 TwoFactorAuth/application.py:92
|
||||||
msgid "Settings"
|
msgid "Settings"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -25,11 +26,11 @@ msgstr ""
|
||||||
msgid "Keyboard shortcuts"
|
msgid "Keyboard shortcuts"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/menu.ui:19
|
#: data/menu.ui:19 TwoFactorAuth/application.py:55
|
||||||
msgid "About"
|
msgid "About"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: data/menu.ui:23
|
#: data/menu.ui:23 TwoFactorAuth/application.py:56
|
||||||
msgid "Quit"
|
msgid "Quit"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
@ -84,131 +85,143 @@ msgid ""
|
||||||
"later</a> for details."
|
"later</a> for details."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/window.py:111
|
#: TwoFactorAuth/widgets/window.py:131
|
||||||
msgid "Do you really want to remove this application?"
|
msgid "Do you really want to remove selected applications?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/window.py:133
|
#: TwoFactorAuth/widgets/window.py:152
|
||||||
msgid "Enter your password"
|
msgid "Enter your password"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/window.py:137
|
#: TwoFactorAuth/widgets/window.py:156
|
||||||
msgid "Login"
|
msgid "Unlock"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/window.py:186
|
#: TwoFactorAuth/widgets/window.py:220
|
||||||
msgid "Remove selected two factor auth sources"
|
msgid "Remove selected two factor auth sources"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/window.py:198
|
#: TwoFactorAuth/widgets/window.py:228
|
||||||
#: TwoFactorAuth/widgets/add_authenticator.py:33
|
#: TwoFactorAuth/widgets/add_authenticator.py:28
|
||||||
msgid "Add a new application"
|
msgid "Add a new application"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/window.py:207
|
#: TwoFactorAuth/widgets/window.py:237
|
||||||
msgid "Select mode"
|
msgid "Select mode"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/window.py:216
|
#: TwoFactorAuth/widgets/window.py:244
|
||||||
msgid "Search"
|
msgid "Search"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/window.py:222
|
#: TwoFactorAuth/widgets/window.py:249
|
||||||
#: TwoFactorAuth/widgets/change_password.py:122
|
#: TwoFactorAuth/widgets/change_password.py:137
|
||||||
#: TwoFactorAuth/widgets/icon_finder.py:79
|
#: TwoFactorAuth/widgets/icon_finder.py:85
|
||||||
#: TwoFactorAuth/widgets/authenticator_logo.py:140
|
#: TwoFactorAuth/widgets/authenticator_logo.py:139
|
||||||
#: TwoFactorAuth/widgets/add_authenticator.py:180
|
#: TwoFactorAuth/widgets/add_authenticator.py:204
|
||||||
msgid "Cancel"
|
msgid "Cancel"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/window.py:364
|
#: TwoFactorAuth/widgets/window.py:402
|
||||||
msgid "There's no application at the moment"
|
msgid "There's no application at the moment"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/window.py:431
|
#: TwoFactorAuth/widgets/window.py:487
|
||||||
msgid "Do you really want to remove the application?"
|
msgid "Do you really want to remove the application?"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/change_password.py:44
|
#: TwoFactorAuth/widgets/change_password.py:12
|
||||||
msgid "Old password : "
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/change_password.py:55
|
|
||||||
msgid "New password : "
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/change_password.py:65
|
|
||||||
msgid "Repeat new password : "
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/change_password.py:127
|
|
||||||
msgid "Modifier"
|
msgid "Modifier"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/icon_finder.py:84
|
#: TwoFactorAuth/widgets/change_password.py:54
|
||||||
|
msgid "Old password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: TwoFactorAuth/widgets/change_password.py:64
|
||||||
|
msgid "New password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: TwoFactorAuth/widgets/change_password.py:74
|
||||||
|
msgid "Repeat new password"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: TwoFactorAuth/widgets/icon_finder.py:11
|
||||||
msgid "Select"
|
msgid "Select"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/authenticator_logo.py:145
|
#: TwoFactorAuth/widgets/authenticator_logo.py:144
|
||||||
msgid "Choose"
|
msgid "Choose"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/settings.py:36
|
#: TwoFactorAuth/widgets/settings.py:39
|
||||||
msgid "Preferences"
|
msgid "Preferences"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/settings.py:40
|
#: TwoFactorAuth/widgets/settings.py:43
|
||||||
msgid "Account"
|
msgid "Account"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/settings.py:46
|
#: TwoFactorAuth/widgets/settings.py:53
|
||||||
msgid "Enable password: "
|
msgid "Enable password: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/settings.py:55
|
#: TwoFactorAuth/widgets/settings.py:61
|
||||||
msgid "Password: "
|
msgid "Password: "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/settings.py:73
|
#: TwoFactorAuth/widgets/settings.py:82
|
||||||
msgid "Password generation time (s): "
|
msgid "Password generation time (s): "
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/listrow.py:105
|
#: TwoFactorAuth/widgets/listrow.py:156
|
||||||
msgid "Copy the generated code.."
|
msgid "Copy the generated code.."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/listrow.py:115
|
#: TwoFactorAuth/widgets/listrow.py:165
|
||||||
msgid "Remove the source.."
|
msgid "Remove the source.."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/listrow.py:172
|
#: TwoFactorAuth/widgets/listrow.py:220
|
||||||
msgid "Couldn't generate the secret code"
|
msgid "Couldn't generate the secret code"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/add_authenticator.py:81
|
#: TwoFactorAuth/widgets/add_authenticator.py:96
|
||||||
msgid "Application name : "
|
msgid "Application Name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/add_authenticator.py:89
|
#: TwoFactorAuth/widgets/add_authenticator.py:103
|
||||||
msgid "Two-factor secret : "
|
msgid "Secret Code"
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/add_authenticator.py:113
|
|
||||||
msgid "Select from provided icons"
|
|
||||||
msgstr ""
|
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/add_authenticator.py:118
|
|
||||||
msgid "Select a file"
|
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/add_authenticator.py:123
|
#: TwoFactorAuth/widgets/add_authenticator.py:123
|
||||||
|
msgid "Select from provided icons"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: TwoFactorAuth/widgets/add_authenticator.py:128
|
||||||
|
msgid "Select a file"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: TwoFactorAuth/widgets/add_authenticator.py:133
|
||||||
msgid "Select an icon name"
|
msgid "Select an icon name"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/add_authenticator.py:148
|
#: TwoFactorAuth/widgets/add_authenticator.py:165
|
||||||
msgid "Please choose a file"
|
msgid "Please choose a file"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: TwoFactorAuth/widgets/add_authenticator.py:185
|
#: TwoFactorAuth/widgets/add_authenticator.py:209
|
||||||
msgid "Add"
|
msgid "Add"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
#: TwoFactorAuth/application.py:48 TwoFactorAuth/application.py:103
|
||||||
|
msgid "Unlock the Application"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: TwoFactorAuth/application.py:50 TwoFactorAuth/application.py:105
|
||||||
|
msgid "Lock the Application"
|
||||||
|
msgstr ""
|
||||||
|
|
||||||
|
#: TwoFactorAuth/application.py:54
|
||||||
|
msgid "Shortcuts"
|
||||||
|
msgstr ""
|
||||||
|
|
Loading…
Add table
Reference in a new issue