Compare commits
10
Commits
49f0af4ae1
...
b3a9ff8d02
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3a9ff8d02 | ||
|
|
33a8d69d03 | ||
|
|
671edf1234 | ||
|
|
eb0adeeed1 | ||
|
|
4c2c6bb893 | ||
|
|
da4a4cc52b | ||
|
|
2aef1f78fb | ||
|
|
c033c19e5d | ||
|
|
8c80a528cc | ||
|
|
4991f28aef |
No files matched your search
@@ -0,0 +1,6 @@
|
|||||||
|
# Contributions to linuxmint/cinnamon
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
This repository holds only my commits to the upstream project, with their original author, date and diff.
|
||||||
|
Each of my commits is preceded by a snapshot of the upstream files it touched, so every diff shows exactly what I changed.
|
||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,499 @@
|
|||||||
|
#!/usr/bin/python3
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pam
|
||||||
|
print("Using pam module (python3-pampy)")
|
||||||
|
except:
|
||||||
|
import PAM
|
||||||
|
print("Using PAM module (python3-pam)")
|
||||||
|
pam = None
|
||||||
|
import pexpect
|
||||||
|
import time
|
||||||
|
from random import randint
|
||||||
|
import shutil
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
from PIL import Image
|
||||||
|
import gi
|
||||||
|
gi.require_version('AccountsService', '1.0')
|
||||||
|
from gi.repository import AccountsService, GLib, GdkPixbuf, XApp
|
||||||
|
|
||||||
|
from bin.SettingsWidgets import SidePage
|
||||||
|
from bin.ChooserButtonWidgets import PictureChooserButton
|
||||||
|
from xapp.GSettingsWidgets import *
|
||||||
|
|
||||||
|
class PasswordError(Exception):
|
||||||
|
"""Exception raised when an incorrect password is supplied."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class Module:
|
||||||
|
name = "user"
|
||||||
|
category = "prefs"
|
||||||
|
comment = _("Change your user preferences and password")
|
||||||
|
|
||||||
|
def __init__(self, content_box):
|
||||||
|
keywords = _("user, account, information, details, password")
|
||||||
|
sidePage = SidePage(_("Account Details"), "cs-user", keywords, content_box, module=self)
|
||||||
|
self.sidePage = sidePage
|
||||||
|
self.window = None
|
||||||
|
|
||||||
|
def _setParentRef(self, window):
|
||||||
|
self.window = window
|
||||||
|
|
||||||
|
def on_module_selected(self):
|
||||||
|
if not self.loaded:
|
||||||
|
print("Loading User module")
|
||||||
|
|
||||||
|
page = SettingsPage()
|
||||||
|
self.sidePage.add_widget(page)
|
||||||
|
|
||||||
|
settings = page.add_section(_("Account Details"))
|
||||||
|
|
||||||
|
self.scale = self.window.get_scale_factor()
|
||||||
|
|
||||||
|
self.face_button = PictureChooserButton(num_cols=4, button_picture_width=64, menu_picture_width=64*self.scale, keep_square=True)
|
||||||
|
self.face_button.set_alignment(0.0, 0.5)
|
||||||
|
self.face_button.set_tooltip_text(_("Click to change your picture"))
|
||||||
|
|
||||||
|
self.face_photo_menuitem = Gtk.MenuItem.new_with_label(_("Take a photo..."))
|
||||||
|
self.face_photo_menuitem.connect('activate', self._on_face_photo_menuitem_activated)
|
||||||
|
|
||||||
|
self.face_browse_menuitem = Gtk.MenuItem.new_with_label(_("Browse for more pictures..."))
|
||||||
|
self.face_browse_menuitem.connect('activate', self._on_face_browse_menuitem_activated)
|
||||||
|
|
||||||
|
face_dirs = ["/usr/share/cinnamon/faces"]
|
||||||
|
for face_dir in face_dirs:
|
||||||
|
if os.path.exists(face_dir):
|
||||||
|
pictures = sorted(os.listdir(face_dir))
|
||||||
|
for picture in pictures:
|
||||||
|
path = os.path.join(face_dir, picture)
|
||||||
|
self.face_button.add_picture(path, self._on_face_menuitem_activated)
|
||||||
|
|
||||||
|
widget = SettingsWidget()
|
||||||
|
label = Gtk.Label.new(_("Picture"))
|
||||||
|
widget.pack_start(label, False, False, 0)
|
||||||
|
widget.pack_end(self.face_button, False, False, 0)
|
||||||
|
settings.add_row(widget)
|
||||||
|
|
||||||
|
size_group = Gtk.SizeGroup.new(Gtk.SizeGroupMode.HORIZONTAL)
|
||||||
|
|
||||||
|
widget = SettingsWidget()
|
||||||
|
label = Gtk.Label.new(_("Name"))
|
||||||
|
widget.pack_start(label, False, False, 0)
|
||||||
|
self.realname_entry = EditableEntry()
|
||||||
|
size_group.add_widget(self.realname_entry)
|
||||||
|
self.realname_entry.connect("changed", self._on_realname_changed)
|
||||||
|
self.realname_entry.set_tooltip_text(_("Click to change your name"))
|
||||||
|
widget.pack_end(self.realname_entry, False, False, 0)
|
||||||
|
settings.add_row(widget)
|
||||||
|
|
||||||
|
widget = SettingsWidget()
|
||||||
|
label = Gtk.Label.new(_("Password"))
|
||||||
|
widget.pack_start(label, False, False, 0)
|
||||||
|
password_mask = Gtk.Label.new('\u2022\u2022\u2022\u2022\u2022\u2022')
|
||||||
|
password_mask.set_alignment(0.9, 0.5)
|
||||||
|
self.password_button = Gtk.Button()
|
||||||
|
size_group.add_widget(self.password_button)
|
||||||
|
self.password_button.add(password_mask)
|
||||||
|
self.password_button.set_relief(Gtk.ReliefStyle.NONE)
|
||||||
|
self.password_button.set_tooltip_text(_("Click to change your password"))
|
||||||
|
self.password_button.connect('activate', self._on_password_button_clicked)
|
||||||
|
self.password_button.connect('released', self._on_password_button_clicked)
|
||||||
|
widget.pack_end(self.password_button, False, False, 0)
|
||||||
|
settings.add_row(widget)
|
||||||
|
|
||||||
|
current_user = GLib.get_user_name()
|
||||||
|
self.accountService = AccountsService.UserManager.get_default().get_user(current_user)
|
||||||
|
self.accountService.connect('notify::is-loaded', self.load_user_info)
|
||||||
|
|
||||||
|
self.face_button.add_separator()
|
||||||
|
|
||||||
|
# Video devices assumed to be webcams
|
||||||
|
import glob
|
||||||
|
webcam_detected = len(glob.glob("/dev/video*")) > 0
|
||||||
|
|
||||||
|
if webcam_detected:
|
||||||
|
self.face_button.add_menuitem(self.face_photo_menuitem)
|
||||||
|
|
||||||
|
self.face_button.add_menuitem(self.face_browse_menuitem)
|
||||||
|
|
||||||
|
def update_preview_cb (self, dialog, preview):
|
||||||
|
filename = dialog.get_preview_filename()
|
||||||
|
if filename is not None:
|
||||||
|
if os.path.isfile(filename):
|
||||||
|
try:
|
||||||
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(filename, 128, 128)
|
||||||
|
if pixbuf is not None:
|
||||||
|
preview.set_from_pixbuf(pixbuf)
|
||||||
|
self.frame.show()
|
||||||
|
return
|
||||||
|
except GLib.Error as e:
|
||||||
|
print(f"Unable to generate preview for file '{filename}' - {e.message}\n")
|
||||||
|
|
||||||
|
preview.clear()
|
||||||
|
self.frame.hide()
|
||||||
|
|
||||||
|
def _on_face_photo_menuitem_activated(self, menuitem):
|
||||||
|
|
||||||
|
# streamer takes -t photos, uses /dev/video0
|
||||||
|
if 0 != subprocess.call(["streamer", "-j90", "-t8", "-s800x600", "-o", "/tmp/temp-account-pic00.jpeg"]):
|
||||||
|
print("Error: Webcam not available")
|
||||||
|
return
|
||||||
|
|
||||||
|
# Use the 8th frame (the webcam takes a few frames to "lighten up")
|
||||||
|
path = "/tmp/temp-account-pic07.jpeg"
|
||||||
|
|
||||||
|
# Crop the image to thumbnail size
|
||||||
|
image = Image.open(path)
|
||||||
|
width, height = image.size
|
||||||
|
|
||||||
|
if width > height:
|
||||||
|
new_width = height
|
||||||
|
new_height = height
|
||||||
|
elif height > width:
|
||||||
|
new_width = width
|
||||||
|
new_height = width
|
||||||
|
else:
|
||||||
|
new_width = width
|
||||||
|
new_height = height
|
||||||
|
|
||||||
|
left = (width - new_width) / 2
|
||||||
|
top = (height - new_height) / 2
|
||||||
|
right = (width + new_width) / 2
|
||||||
|
bottom = (height + new_height) / 2
|
||||||
|
|
||||||
|
image = image.crop((left, top, right, bottom))
|
||||||
|
image.thumbnail((255, 255), Image.LANCZOS)
|
||||||
|
|
||||||
|
face_path = os.path.join(self.accountService.get_home_dir(), ".face")
|
||||||
|
|
||||||
|
image.save(face_path, "png")
|
||||||
|
self.accountService.set_icon_file(face_path)
|
||||||
|
self.face_button.set_picture_from_file(face_path)
|
||||||
|
|
||||||
|
|
||||||
|
def _on_face_browse_menuitem_activated(self, menuitem):
|
||||||
|
dialog = Gtk.FileChooserDialog(None, None, Gtk.FileChooserAction.OPEN, (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
|
||||||
|
dialog.set_current_folder(self.accountService.get_home_dir())
|
||||||
|
filter = Gtk.FileFilter()
|
||||||
|
filter.set_name(_("Images"))
|
||||||
|
filter.add_mime_type("image/*")
|
||||||
|
dialog.add_filter(filter)
|
||||||
|
|
||||||
|
box = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
||||||
|
self.frame = Gtk.Frame(visible=False, no_show_all=True)
|
||||||
|
preview = Gtk.Image(visible=True)
|
||||||
|
|
||||||
|
box.pack_start(self.frame, False, False, 0)
|
||||||
|
self.frame.add(preview)
|
||||||
|
dialog.set_preview_widget(box)
|
||||||
|
dialog.set_preview_widget_active(True)
|
||||||
|
dialog.set_use_preview_label(False)
|
||||||
|
|
||||||
|
box.set_margin_end(12)
|
||||||
|
box.set_margin_top(12)
|
||||||
|
box.set_size_request(128, -1)
|
||||||
|
|
||||||
|
dialog.connect("update-preview", self.update_preview_cb, preview)
|
||||||
|
|
||||||
|
response = dialog.run()
|
||||||
|
|
||||||
|
if response == Gtk.ResponseType.OK:
|
||||||
|
string = dialog.get_filename()
|
||||||
|
print(string)
|
||||||
|
if string.startswith("/"):
|
||||||
|
path = string
|
||||||
|
else:
|
||||||
|
theme = Gtk.IconTheme.get_default()
|
||||||
|
icon_info = theme.lookup_icon_for_scale(string, 256, dialog.get_scale_factor(), Gtk.IconLookupFlags.FORCE_SIZE)
|
||||||
|
path = icon_info.get_filename() if icon_info else None
|
||||||
|
|
||||||
|
face_path = os.path.join(self.accountService.get_home_dir(), ".face")
|
||||||
|
|
||||||
|
pixbuf = GdkPixbuf.Pixbuf.new_from_file_at_size(path, 255, -1)
|
||||||
|
pixbuf.savev(face_path, "png")
|
||||||
|
self.accountService.set_icon_file(face_path)
|
||||||
|
self.face_button.set_picture_from_file(face_path)
|
||||||
|
|
||||||
|
dialog.destroy()
|
||||||
|
|
||||||
|
def _on_face_menuitem_activated(self, path):
|
||||||
|
if os.path.exists(path):
|
||||||
|
self.accountService.set_icon_file(path)
|
||||||
|
shutil.copy(path, os.path.join(self.accountService.get_home_dir(), ".face"))
|
||||||
|
return True
|
||||||
|
|
||||||
|
def load_user_info(self, user, param):
|
||||||
|
self.realname_entry.set_text(user.get_real_name())
|
||||||
|
for path in [os.path.join(self.accountService.get_home_dir(), ".face"), user.get_icon_file(), "/usr/share/cinnamon/faces/user-generic.png"]:
|
||||||
|
if os.path.exists(path):
|
||||||
|
self.face_button.set_picture_from_file(path)
|
||||||
|
break
|
||||||
|
|
||||||
|
def _on_realname_changed(self, widget, text):
|
||||||
|
self.accountService.set_real_name(text)
|
||||||
|
|
||||||
|
def _on_password_button_clicked(self, widget):
|
||||||
|
dialog = PasswordDialog()
|
||||||
|
response = dialog.run()
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordDialog(Gtk.Dialog):
|
||||||
|
|
||||||
|
def __init__ (self):
|
||||||
|
super(PasswordDialog, self).__init__()
|
||||||
|
|
||||||
|
self.correct_current_password = False # Flag to remember if the current password is correct or not
|
||||||
|
|
||||||
|
self.set_modal(True)
|
||||||
|
self.set_skip_taskbar_hint(True)
|
||||||
|
self.set_skip_pager_hint(True)
|
||||||
|
self.set_title(_("Change Password"))
|
||||||
|
|
||||||
|
table = Gtk.Table(6, 3)
|
||||||
|
table.set_border_width(6)
|
||||||
|
table.set_row_spacings(8)
|
||||||
|
table.set_col_spacings(15)
|
||||||
|
|
||||||
|
label = Gtk.Label.new(_("Current password"))
|
||||||
|
label.set_alignment(1, 0.5)
|
||||||
|
table.attach(label, 0, 1, 0, 1)
|
||||||
|
|
||||||
|
label = Gtk.Label.new(_("New password"))
|
||||||
|
label.set_alignment(1, 0.5)
|
||||||
|
table.attach(label, 0, 1, 1, 2)
|
||||||
|
|
||||||
|
label = Gtk.Label.new(_("Confirm password"))
|
||||||
|
label.set_alignment(1, 0.5)
|
||||||
|
table.attach(label, 0, 1, 3, 4)
|
||||||
|
|
||||||
|
self.current_password = Gtk.Entry()
|
||||||
|
self.current_password.set_visibility(False)
|
||||||
|
self.current_password.connect("focus-out-event", self._on_current_password_changed)
|
||||||
|
table.attach(self.current_password, 1, 3, 0, 1)
|
||||||
|
|
||||||
|
self.new_password = Gtk.Entry()
|
||||||
|
self.new_password.set_icon_from_icon_name(Gtk.EntryIconPosition.SECONDARY, "view-refresh")
|
||||||
|
self.new_password.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, _("Generate a password"))
|
||||||
|
self.new_password.set_tooltip_text(_("Generate a password"))
|
||||||
|
self.new_password.connect("icon-release", self._on_new_password_icon_released)
|
||||||
|
self.new_password.connect("changed", self._on_passwords_changed)
|
||||||
|
table.attach(self.new_password, 1, 3, 1, 2)
|
||||||
|
|
||||||
|
self.strengh_indicator = Gtk.ProgressBar()
|
||||||
|
self.strengh_indicator.set_tooltip_text(_("Your new password needs to be at least 8 characters long"))
|
||||||
|
self.strengh_indicator.set_fraction(0.0)
|
||||||
|
table.attach(self.strengh_indicator, 1, 2, 2, 3, xoptions=Gtk.AttachOptions.EXPAND|Gtk.AttachOptions.FILL)
|
||||||
|
self.strengh_indicator.set_size_request(-1, 1)
|
||||||
|
|
||||||
|
self.strengh_label = Gtk.Label()
|
||||||
|
self.strengh_label.set_tooltip_text(_("Your new password needs to be at least 8 characters long"))
|
||||||
|
self.strengh_label.set_alignment(1, 0.5)
|
||||||
|
table.attach(self.strengh_label, 2, 3, 2, 3)
|
||||||
|
|
||||||
|
self.confirm_password = Gtk.Entry()
|
||||||
|
self.confirm_password.connect("changed", self._on_passwords_changed)
|
||||||
|
table.attach(self.confirm_password, 1, 3, 3, 4)
|
||||||
|
|
||||||
|
self.show_password = Gtk.CheckButton(_("Show password"))
|
||||||
|
self.show_password.connect('toggled', self._on_show_password_toggled)
|
||||||
|
table.attach(self.show_password, 1, 3, 4, 5)
|
||||||
|
|
||||||
|
self.set_border_width(6)
|
||||||
|
|
||||||
|
box = self.get_content_area()
|
||||||
|
box.add(table)
|
||||||
|
self.show_all()
|
||||||
|
|
||||||
|
self.infobar = Gtk.InfoBar()
|
||||||
|
self.infobar.set_message_type(Gtk.MessageType.ERROR)
|
||||||
|
label = Gtk.Label.new(_("An error occurred. Your password was not changed."))
|
||||||
|
content = self.infobar.get_content_area()
|
||||||
|
content.add(label)
|
||||||
|
table.attach(self.infobar, 0, 3, 5, 6)
|
||||||
|
|
||||||
|
self.add_buttons(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, _("Change"), Gtk.ResponseType.OK, )
|
||||||
|
|
||||||
|
self.set_passwords_visibility()
|
||||||
|
self.set_response_sensitive(Gtk.ResponseType.OK, False)
|
||||||
|
self.infobar.hide()
|
||||||
|
|
||||||
|
self.connect("response", self._on_response)
|
||||||
|
|
||||||
|
def _on_response(self, dialog, response_id):
|
||||||
|
if response_id == Gtk.ResponseType.OK:
|
||||||
|
self.change_password()
|
||||||
|
else:
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
def change_password(self):
|
||||||
|
oldpass = self.current_password.get_text()
|
||||||
|
newpass = self.new_password.get_text()
|
||||||
|
passwd = pexpect.spawn("/usr/bin/passwd")
|
||||||
|
# passwd only asks for the old password when there already is one set.
|
||||||
|
if oldpass == "":
|
||||||
|
time.sleep(0.5)
|
||||||
|
passwd.sendline(newpass)
|
||||||
|
time.sleep(0.5)
|
||||||
|
passwd.sendline(newpass)
|
||||||
|
else:
|
||||||
|
time.sleep(0.5)
|
||||||
|
passwd.sendline(oldpass)
|
||||||
|
time.sleep(0.5)
|
||||||
|
passwd.sendline(newpass)
|
||||||
|
time.sleep(0.5)
|
||||||
|
passwd.sendline(newpass)
|
||||||
|
time.sleep(0.5)
|
||||||
|
passwd.close()
|
||||||
|
|
||||||
|
if passwd.exitstatus is None or passwd.exitstatus > 0:
|
||||||
|
self.infobar.show_all()
|
||||||
|
else:
|
||||||
|
self.destroy()
|
||||||
|
|
||||||
|
def set_passwords_visibility(self):
|
||||||
|
visible = self.show_password.get_active()
|
||||||
|
self.new_password.set_visibility(visible)
|
||||||
|
self.confirm_password.set_visibility(visible)
|
||||||
|
|
||||||
|
def _on_new_password_icon_released(self, widget, icon_pos, event):
|
||||||
|
self.infobar.hide()
|
||||||
|
self.show_password.set_active(True)
|
||||||
|
characters = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_-"
|
||||||
|
newpass = ""
|
||||||
|
for i in range (8):
|
||||||
|
index = randint(0, len(characters) -1)
|
||||||
|
newpass = newpass + characters[index]
|
||||||
|
|
||||||
|
self.new_password.set_text(newpass)
|
||||||
|
self.confirm_password.set_text(newpass)
|
||||||
|
self.check_passwords()
|
||||||
|
|
||||||
|
def _on_show_password_toggled(self, widget):
|
||||||
|
self.set_passwords_visibility()
|
||||||
|
|
||||||
|
def _get_pam_service(self):
|
||||||
|
import os
|
||||||
|
if os.path.exists('/etc/pam.d/system-auth'):
|
||||||
|
return 'system-auth'
|
||||||
|
elif os.path.exists('/etc/pam.d/common-auth'):
|
||||||
|
return 'common-auth'
|
||||||
|
else:
|
||||||
|
return 'login'
|
||||||
|
|
||||||
|
def auth_pam(self):
|
||||||
|
service = self._get_pam_service()
|
||||||
|
if not pam.pam().authenticate(GLib.get_user_name(), self.current_password.get_text(), service):
|
||||||
|
raise PasswordError("Invalid password")
|
||||||
|
|
||||||
|
def auth_PyPAM(self):
|
||||||
|
auth = PAM.pam()
|
||||||
|
auth.start(self._get_pam_service())
|
||||||
|
auth.set_item(PAM.PAM_USER, GLib.get_user_name())
|
||||||
|
auth.set_item(PAM.PAM_CONV, self.pam_conv)
|
||||||
|
try:
|
||||||
|
auth.authenticate()
|
||||||
|
auth.acct_mgmt()
|
||||||
|
return True
|
||||||
|
except PAM.error as resp:
|
||||||
|
raise PasswordError("Invalid password")
|
||||||
|
|
||||||
|
def _on_current_password_changed(self, widget, event):
|
||||||
|
self.infobar.hide()
|
||||||
|
try:
|
||||||
|
self.auth_pam() if pam else self.auth_PyPAM()
|
||||||
|
except PasswordError:
|
||||||
|
self.current_password.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, Gtk.STOCK_DIALOG_WARNING)
|
||||||
|
self.current_password.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, _("Wrong password"))
|
||||||
|
self.current_password.set_tooltip_text(_("Wrong password"))
|
||||||
|
self.correct_current_password = False
|
||||||
|
except:
|
||||||
|
self.current_password.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, Gtk.STOCK_DIALOG_WARNING)
|
||||||
|
self.current_password.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, _("Internal Error"))
|
||||||
|
self.current_password.set_tooltip_text(_("Internal Error"))
|
||||||
|
self.correct_current_password = False
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
self.current_password.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, None)
|
||||||
|
self.current_password.set_tooltip_text("")
|
||||||
|
self.correct_current_password = True
|
||||||
|
self.check_passwords()
|
||||||
|
|
||||||
|
# Based on setPasswordStrength() in Mozilla Seamonkey, which is tri-licensed under MPL 1.1, GPL 2.0, and LGPL 2.1.
|
||||||
|
# Forked from Ubiquity validation.py
|
||||||
|
def password_strength(self, password):
|
||||||
|
upper = lower = digit = symbol = 0
|
||||||
|
for char in password:
|
||||||
|
if char.isdigit():
|
||||||
|
digit += 1
|
||||||
|
elif char.islower():
|
||||||
|
lower += 1
|
||||||
|
elif char.isupper():
|
||||||
|
upper += 1
|
||||||
|
else:
|
||||||
|
symbol += 1
|
||||||
|
length = len(password)
|
||||||
|
|
||||||
|
length = min(length,4)
|
||||||
|
digit = min(digit,3)
|
||||||
|
upper = min(upper,3)
|
||||||
|
symbol = min(symbol,3)
|
||||||
|
strength = (
|
||||||
|
((length * 0.1) - 0.2) +
|
||||||
|
(digit * 0.1) +
|
||||||
|
(symbol * 0.15) +
|
||||||
|
(upper * 0.1))
|
||||||
|
if strength > 1:
|
||||||
|
strength = 1
|
||||||
|
if strength < 0:
|
||||||
|
strength = 0
|
||||||
|
return strength
|
||||||
|
|
||||||
|
def _on_passwords_changed(self, widget):
|
||||||
|
self.infobar.hide()
|
||||||
|
new_password = self.new_password.get_text()
|
||||||
|
confirm_password = self.confirm_password.get_text()
|
||||||
|
strength = self.password_strength(new_password)
|
||||||
|
if new_password != confirm_password:
|
||||||
|
self.confirm_password.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, Gtk.STOCK_DIALOG_WARNING)
|
||||||
|
self.confirm_password.set_icon_tooltip_text(Gtk.EntryIconPosition.SECONDARY, _("Passwords do not match"))
|
||||||
|
self.confirm_password.set_tooltip_text(_("Passwords do not match"))
|
||||||
|
else:
|
||||||
|
self.confirm_password.set_icon_from_stock(Gtk.EntryIconPosition.SECONDARY, None)
|
||||||
|
self.confirm_password.set_tooltip_text("")
|
||||||
|
if len(new_password) < 8:
|
||||||
|
self.strengh_label.set_text(_("Too short"))
|
||||||
|
self.strengh_indicator.set_fraction(0.0)
|
||||||
|
elif strength < 0.6:
|
||||||
|
self.strengh_label.set_text(_("Weak"))
|
||||||
|
self.strengh_indicator.set_fraction(0.2)
|
||||||
|
elif strength < 0.75:
|
||||||
|
self.strengh_label.set_text(_("Fair"))
|
||||||
|
self.strengh_indicator.set_fraction(0.4)
|
||||||
|
elif strength < 0.9:
|
||||||
|
self.strengh_label.set_text(_("Good"))
|
||||||
|
self.strengh_indicator.set_fraction(0.6)
|
||||||
|
else:
|
||||||
|
self.strengh_label.set_text(_("Strong"))
|
||||||
|
self.strengh_indicator.set_fraction(1.0)
|
||||||
|
|
||||||
|
self.check_passwords()
|
||||||
|
|
||||||
|
def check_passwords(self):
|
||||||
|
if self.correct_current_password:
|
||||||
|
new_password = self.new_password.get_text()
|
||||||
|
confirm_password = self.confirm_password.get_text()
|
||||||
|
if len(new_password) >= 8 and new_password == confirm_password:
|
||||||
|
self.set_response_sensitive(Gtk.ResponseType.OK, True)
|
||||||
|
else:
|
||||||
|
self.set_response_sensitive(Gtk.ResponseType.OK, False)
|
||||||
|
|
||||||
|
def pam_conv(self, auth, query_list, userData):
|
||||||
|
resp = []
|
||||||
|
for i in range(len(query_list)):
|
||||||
|
query, type = query_list[i]
|
||||||
|
val = self.current_password.get_text()
|
||||||
|
resp.append((val, 0))
|
||||||
|
return resp
|
||||||
@@ -394,6 +394,10 @@ KeybindingManager.prototype = {
|
|||||||
|
|
||||||
let custom_path = CUSTOM_KEYS_BASENAME + "/" + list[i] + "/";
|
let custom_path = CUSTOM_KEYS_BASENAME + "/" + list[i] + "/";
|
||||||
let schema = Gio.Settings.new_with_path(CUSTOM_KEYS_SCHEMA, custom_path);
|
let schema = Gio.Settings.new_with_path(CUSTOM_KEYS_SCHEMA, custom_path);
|
||||||
|
if (!schema) {
|
||||||
|
global.logWarning(`Ignoring invalid custom keybinding entry '${list[i]}'`);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
let command = schema.get_string("command");
|
let command = schema.get_string("command");
|
||||||
let binding = schema.get_strv("binding");
|
let binding = schema.get_strv("binding");
|
||||||
let name = list[i];
|
let name = list[i];
|
||||||
|
|||||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,867 @@
|
|||||||
|
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||||
|
/*
|
||||||
|
* st-button.c: Plain button actor
|
||||||
|
*
|
||||||
|
* Copyright 2007 OpenedHand
|
||||||
|
* Copyright 2008, 2009 Intel Corporation.
|
||||||
|
* Copyright 2009, 2010 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it
|
||||||
|
* under the terms and conditions of the GNU Lesser General Public License,
|
||||||
|
* version 2.1, as published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
||||||
|
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SECTION:st-button
|
||||||
|
* @short_description: Button widget
|
||||||
|
*
|
||||||
|
* A button widget with support for either a text label or icon, toggle mode
|
||||||
|
* and transitions effects between states.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#include <glib.h>
|
||||||
|
|
||||||
|
#include <clutter/clutter.h>
|
||||||
|
|
||||||
|
#include "st-button.h"
|
||||||
|
|
||||||
|
#include "st-icon.h"
|
||||||
|
#include "st-enum-types.h"
|
||||||
|
#include "st-texture-cache.h"
|
||||||
|
#include "st-private.h"
|
||||||
|
|
||||||
|
#include "st-widget-accessible.h"
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PROP_0,
|
||||||
|
|
||||||
|
PROP_LABEL,
|
||||||
|
PROP_ICON_NAME,
|
||||||
|
PROP_BUTTON_MASK,
|
||||||
|
PROP_TOGGLE_MODE,
|
||||||
|
PROP_CHECKED,
|
||||||
|
PROP_PRESSED
|
||||||
|
};
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
CLICKED,
|
||||||
|
|
||||||
|
LAST_SIGNAL
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _StButtonPrivate
|
||||||
|
{
|
||||||
|
ClutterInputDevice *device;
|
||||||
|
gchar *text;
|
||||||
|
|
||||||
|
guint button_mask : 3;
|
||||||
|
guint is_toggle : 1;
|
||||||
|
|
||||||
|
guint pressed : 3;
|
||||||
|
guint grabbed : 3;
|
||||||
|
guint is_checked : 1;
|
||||||
|
|
||||||
|
gint spacing;
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint button_signals[LAST_SIGNAL] = { 0, };
|
||||||
|
|
||||||
|
G_DEFINE_TYPE_WITH_PRIVATE (StButton, st_button, ST_TYPE_BIN);
|
||||||
|
|
||||||
|
static GType st_button_accessible_get_type (void) G_GNUC_CONST;
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_update_label_style (StButton *button)
|
||||||
|
{
|
||||||
|
ClutterActor *label;
|
||||||
|
|
||||||
|
label = st_bin_get_child (ST_BIN (button));
|
||||||
|
|
||||||
|
/* check the child is really a label */
|
||||||
|
if (!CLUTTER_IS_TEXT (label))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_st_set_text_from_style (CLUTTER_TEXT (label), st_widget_get_theme_node (ST_WIDGET (button)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_style_changed (StWidget *widget)
|
||||||
|
{
|
||||||
|
StButton *button = ST_BUTTON (widget);
|
||||||
|
StButtonPrivate *priv = button->priv;
|
||||||
|
StButtonClass *button_class = ST_BUTTON_GET_CLASS (button);
|
||||||
|
StThemeNode *theme_node = st_widget_get_theme_node (ST_WIDGET (button));
|
||||||
|
double spacing;
|
||||||
|
|
||||||
|
ST_WIDGET_CLASS (st_button_parent_class)->style_changed (widget);
|
||||||
|
|
||||||
|
spacing = 6;
|
||||||
|
st_theme_node_lookup_length (theme_node, "border-spacing", FALSE, &spacing);
|
||||||
|
priv->spacing = (int)(0.5 + spacing);
|
||||||
|
|
||||||
|
/* update the label styling */
|
||||||
|
st_button_update_label_style (button);
|
||||||
|
|
||||||
|
/* run a transition if applicable */
|
||||||
|
if (button_class->transition)
|
||||||
|
{
|
||||||
|
button_class->transition (button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_press (StButton *button,
|
||||||
|
StButtonMask mask)
|
||||||
|
{
|
||||||
|
if (button->priv->pressed == 0)
|
||||||
|
st_widget_add_style_pseudo_class (ST_WIDGET (button), "active");
|
||||||
|
|
||||||
|
button->priv->pressed |= mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_release (StButton *button,
|
||||||
|
StButtonMask mask,
|
||||||
|
int clicked_button)
|
||||||
|
{
|
||||||
|
button->priv->pressed &= ~mask;
|
||||||
|
if (button->priv->pressed != 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
st_widget_remove_style_pseudo_class (ST_WIDGET (button), "active");
|
||||||
|
|
||||||
|
if (clicked_button)
|
||||||
|
{
|
||||||
|
if (button->priv->is_toggle)
|
||||||
|
st_button_set_checked (button, !button->priv->is_checked);
|
||||||
|
|
||||||
|
g_signal_emit (button, button_signals[CLICKED], 0, clicked_button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
st_button_button_press (ClutterActor *actor,
|
||||||
|
ClutterButtonEvent *event)
|
||||||
|
{
|
||||||
|
StButton *button = ST_BUTTON (actor);
|
||||||
|
StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (event->button);
|
||||||
|
ClutterInputDevice *device = clutter_event_get_device ((ClutterEvent*) event);
|
||||||
|
|
||||||
|
button->priv->device = device;
|
||||||
|
|
||||||
|
if (button->priv->button_mask & mask)
|
||||||
|
{
|
||||||
|
if (button->priv->grabbed == 0)
|
||||||
|
clutter_input_device_grab (device, actor);
|
||||||
|
|
||||||
|
button->priv->grabbed |= mask;
|
||||||
|
st_button_press (button, mask);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
st_button_button_release (ClutterActor *actor,
|
||||||
|
ClutterButtonEvent *event)
|
||||||
|
{
|
||||||
|
StButton *button = ST_BUTTON (actor);
|
||||||
|
StButtonMask mask = ST_BUTTON_MASK_FROM_BUTTON (event->button);
|
||||||
|
ClutterInputDevice *device = clutter_event_get_device ((ClutterEvent*) event);
|
||||||
|
|
||||||
|
if (button->priv->button_mask & mask)
|
||||||
|
{
|
||||||
|
gboolean is_click;
|
||||||
|
|
||||||
|
is_click = button->priv->grabbed && clutter_actor_contains (actor, event->source);
|
||||||
|
st_button_release (button, mask, is_click ? event->button : 0);
|
||||||
|
|
||||||
|
button->priv->grabbed &= ~mask;
|
||||||
|
if (button->priv->grabbed == 0)
|
||||||
|
clutter_input_device_ungrab (device);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
st_button_key_press (ClutterActor *actor,
|
||||||
|
ClutterKeyEvent *event)
|
||||||
|
{
|
||||||
|
StButton *button = ST_BUTTON (actor);
|
||||||
|
|
||||||
|
if (button->priv->button_mask & ST_BUTTON_ONE)
|
||||||
|
{
|
||||||
|
if (event->keyval == CLUTTER_KEY_space ||
|
||||||
|
event->keyval == CLUTTER_KEY_Return ||
|
||||||
|
event->keyval == CLUTTER_KEY_KP_Enter)
|
||||||
|
{
|
||||||
|
st_button_press (button, ST_BUTTON_ONE);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return CLUTTER_ACTOR_CLASS (st_button_parent_class)->key_press_event (actor, event);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
st_button_key_release (ClutterActor *actor,
|
||||||
|
ClutterKeyEvent *event)
|
||||||
|
{
|
||||||
|
StButton *button = ST_BUTTON (actor);
|
||||||
|
|
||||||
|
if (button->priv->button_mask & ST_BUTTON_ONE)
|
||||||
|
{
|
||||||
|
if (event->keyval == CLUTTER_KEY_space ||
|
||||||
|
event->keyval == CLUTTER_KEY_Return ||
|
||||||
|
event->keyval == CLUTTER_KEY_KP_Enter)
|
||||||
|
{
|
||||||
|
gboolean is_click;
|
||||||
|
|
||||||
|
is_click = (button->priv->pressed & ST_BUTTON_ONE);
|
||||||
|
st_button_release (button, ST_BUTTON_ONE, is_click ? 1 : 0);
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_key_focus_out (ClutterActor *actor)
|
||||||
|
{
|
||||||
|
StButton *button = ST_BUTTON (actor);
|
||||||
|
|
||||||
|
/* If we lose focus between a key press and release, undo the press */
|
||||||
|
if ((button->priv->pressed & ST_BUTTON_ONE) &&
|
||||||
|
!(button->priv->grabbed & ST_BUTTON_ONE))
|
||||||
|
st_button_release (button, ST_BUTTON_ONE, 0);
|
||||||
|
|
||||||
|
CLUTTER_ACTOR_CLASS (st_button_parent_class)->key_focus_out (actor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
st_button_enter (ClutterActor *actor,
|
||||||
|
ClutterCrossingEvent *event)
|
||||||
|
{
|
||||||
|
StButton *button = ST_BUTTON (actor);
|
||||||
|
gboolean ret;
|
||||||
|
|
||||||
|
ret = CLUTTER_ACTOR_CLASS (st_button_parent_class)->enter_event (actor, event);
|
||||||
|
|
||||||
|
if (button->priv->grabbed)
|
||||||
|
{
|
||||||
|
if (st_widget_get_hover (ST_WIDGET (button)))
|
||||||
|
st_button_press (button, button->priv->grabbed);
|
||||||
|
else
|
||||||
|
st_button_release (button, button->priv->grabbed, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
st_widget_add_accessible_state (ST_WIDGET (button), ATK_STATE_FOCUSED);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
st_button_leave (ClutterActor *actor,
|
||||||
|
ClutterCrossingEvent *event)
|
||||||
|
{
|
||||||
|
StButton *button = ST_BUTTON (actor);
|
||||||
|
gboolean ret;
|
||||||
|
|
||||||
|
ret = CLUTTER_ACTOR_CLASS (st_button_parent_class)->leave_event (actor, event);
|
||||||
|
|
||||||
|
if (button->priv->grabbed)
|
||||||
|
{
|
||||||
|
if (st_widget_get_hover (ST_WIDGET (button)))
|
||||||
|
st_button_press (button, button->priv->grabbed);
|
||||||
|
else
|
||||||
|
st_button_release (button, button->priv->grabbed, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
st_widget_remove_accessible_state (ST_WIDGET (button), ATK_STATE_FOCUSED);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_set_property (GObject *gobject,
|
||||||
|
guint prop_id,
|
||||||
|
const GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
StButton *button = ST_BUTTON (gobject);
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_LABEL:
|
||||||
|
st_button_set_label (button, g_value_get_string (value));
|
||||||
|
break;
|
||||||
|
case PROP_ICON_NAME:
|
||||||
|
st_button_set_icon_name (button, g_value_get_string (value));
|
||||||
|
break;
|
||||||
|
case PROP_BUTTON_MASK:
|
||||||
|
st_button_set_button_mask (button, g_value_get_flags (value));
|
||||||
|
break;
|
||||||
|
case PROP_TOGGLE_MODE:
|
||||||
|
st_button_set_toggle_mode (button, g_value_get_boolean (value));
|
||||||
|
break;
|
||||||
|
case PROP_CHECKED:
|
||||||
|
st_button_set_checked (button, g_value_get_boolean (value));
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_get_property (GObject *gobject,
|
||||||
|
guint prop_id,
|
||||||
|
GValue *value,
|
||||||
|
GParamSpec *pspec)
|
||||||
|
{
|
||||||
|
StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
|
||||||
|
|
||||||
|
switch (prop_id)
|
||||||
|
{
|
||||||
|
case PROP_LABEL:
|
||||||
|
g_value_set_string (value, priv->text);
|
||||||
|
break;
|
||||||
|
case PROP_ICON_NAME:
|
||||||
|
g_value_set_string (value, st_button_get_icon_name (ST_BUTTON (gobject)));
|
||||||
|
break;
|
||||||
|
case PROP_BUTTON_MASK:
|
||||||
|
g_value_set_flags (value, priv->button_mask);
|
||||||
|
break;
|
||||||
|
case PROP_TOGGLE_MODE:
|
||||||
|
g_value_set_boolean (value, priv->is_toggle);
|
||||||
|
break;
|
||||||
|
case PROP_CHECKED:
|
||||||
|
g_value_set_boolean (value, priv->is_checked);
|
||||||
|
break;
|
||||||
|
case PROP_PRESSED:
|
||||||
|
g_value_set_boolean (value, priv->pressed != 0);
|
||||||
|
break;
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_finalize (GObject *gobject)
|
||||||
|
{
|
||||||
|
StButtonPrivate *priv = ST_BUTTON (gobject)->priv;
|
||||||
|
|
||||||
|
g_free (priv->text);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS (st_button_parent_class)->finalize (gobject);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_class_init (StButtonClass *klass)
|
||||||
|
{
|
||||||
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
||||||
|
ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass);
|
||||||
|
StWidgetClass *widget_class = ST_WIDGET_CLASS (klass);
|
||||||
|
GParamSpec *pspec;
|
||||||
|
|
||||||
|
gobject_class->set_property = st_button_set_property;
|
||||||
|
gobject_class->get_property = st_button_get_property;
|
||||||
|
gobject_class->finalize = st_button_finalize;
|
||||||
|
|
||||||
|
actor_class->button_press_event = st_button_button_press;
|
||||||
|
actor_class->button_release_event = st_button_button_release;
|
||||||
|
actor_class->key_press_event = st_button_key_press;
|
||||||
|
actor_class->key_release_event = st_button_key_release;
|
||||||
|
actor_class->key_focus_out = st_button_key_focus_out;
|
||||||
|
actor_class->enter_event = st_button_enter;
|
||||||
|
actor_class->leave_event = st_button_leave;
|
||||||
|
|
||||||
|
widget_class->style_changed = st_button_style_changed;
|
||||||
|
widget_class->get_accessible_type = st_button_accessible_get_type;
|
||||||
|
|
||||||
|
pspec = g_param_spec_string ("label",
|
||||||
|
"Label",
|
||||||
|
"Label of the button",
|
||||||
|
NULL, G_PARAM_READWRITE);
|
||||||
|
g_object_class_install_property (gobject_class, PROP_LABEL, pspec);
|
||||||
|
|
||||||
|
pspec = g_param_spec_string ("icon-name",
|
||||||
|
"Icon name",
|
||||||
|
"Icon name of the button",
|
||||||
|
NULL, G_PARAM_READWRITE);
|
||||||
|
g_object_class_install_property (gobject_class, PROP_ICON_NAME, pspec);
|
||||||
|
|
||||||
|
pspec = g_param_spec_flags ("button-mask",
|
||||||
|
"Button mask",
|
||||||
|
"Which buttons trigger the 'clicked' signal",
|
||||||
|
ST_TYPE_BUTTON_MASK, ST_BUTTON_ONE,
|
||||||
|
G_PARAM_READWRITE);
|
||||||
|
g_object_class_install_property (gobject_class, PROP_BUTTON_MASK, pspec);
|
||||||
|
|
||||||
|
pspec = g_param_spec_boolean ("toggle-mode",
|
||||||
|
"Toggle Mode",
|
||||||
|
"Enable or disable toggling",
|
||||||
|
FALSE, G_PARAM_READWRITE);
|
||||||
|
g_object_class_install_property (gobject_class, PROP_TOGGLE_MODE, pspec);
|
||||||
|
|
||||||
|
pspec = g_param_spec_boolean ("checked",
|
||||||
|
"Checked",
|
||||||
|
"Indicates if a toggle button is \"on\""
|
||||||
|
" or \"off\"",
|
||||||
|
FALSE, G_PARAM_READWRITE);
|
||||||
|
g_object_class_install_property (gobject_class, PROP_CHECKED, pspec);
|
||||||
|
|
||||||
|
pspec = g_param_spec_boolean ("pressed",
|
||||||
|
"Pressed",
|
||||||
|
"Indicates if the button is pressed in",
|
||||||
|
FALSE, G_PARAM_READABLE);
|
||||||
|
g_object_class_install_property (gobject_class, PROP_PRESSED, pspec);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StButton::clicked:
|
||||||
|
* @button: the object that received the signal
|
||||||
|
* @clicked_button: the mouse button that was used
|
||||||
|
*
|
||||||
|
* Emitted when the user activates the button, either with a mouse press and
|
||||||
|
* release or with the keyboard.
|
||||||
|
*/
|
||||||
|
button_signals[CLICKED] =
|
||||||
|
g_signal_new ("clicked",
|
||||||
|
G_TYPE_FROM_CLASS (klass),
|
||||||
|
G_SIGNAL_RUN_LAST,
|
||||||
|
G_STRUCT_OFFSET (StButtonClass, clicked),
|
||||||
|
NULL, NULL, NULL,
|
||||||
|
G_TYPE_NONE, 1,
|
||||||
|
G_TYPE_INT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_init (StButton *button)
|
||||||
|
{
|
||||||
|
button->priv = st_button_get_instance_private (button);
|
||||||
|
button->priv->spacing = 6;
|
||||||
|
button->priv->button_mask = ST_BUTTON_ONE;
|
||||||
|
|
||||||
|
clutter_actor_set_reactive (CLUTTER_ACTOR (button), TRUE);
|
||||||
|
st_widget_set_track_hover (ST_WIDGET (button), TRUE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_new:
|
||||||
|
*
|
||||||
|
* Create a new button
|
||||||
|
*
|
||||||
|
* Returns: a new #StButton
|
||||||
|
*/
|
||||||
|
StWidget *
|
||||||
|
st_button_new (void)
|
||||||
|
{
|
||||||
|
return g_object_new (ST_TYPE_BUTTON, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_new_with_label:
|
||||||
|
* @text: text to set the label to
|
||||||
|
*
|
||||||
|
* Create a new #StButton with the specified label
|
||||||
|
*
|
||||||
|
* Returns: a new #StButton
|
||||||
|
*/
|
||||||
|
StWidget *
|
||||||
|
st_button_new_with_label (const gchar *text)
|
||||||
|
{
|
||||||
|
return g_object_new (ST_TYPE_BUTTON, "label", text, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_get_label:
|
||||||
|
* @button: a #StButton
|
||||||
|
*
|
||||||
|
* Get the text displayed on the button
|
||||||
|
*
|
||||||
|
* Returns: the text for the button. This must not be freed by the application
|
||||||
|
*/
|
||||||
|
const gchar *
|
||||||
|
st_button_get_label (StButton *button)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (ST_IS_BUTTON (button), NULL);
|
||||||
|
|
||||||
|
return button->priv->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_set_label:
|
||||||
|
* @button: a #Stbutton
|
||||||
|
* @text: text to set the label to
|
||||||
|
*
|
||||||
|
* Sets the text displayed on the button
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
st_button_set_label (StButton *button,
|
||||||
|
const gchar *text)
|
||||||
|
{
|
||||||
|
StButtonPrivate *priv;
|
||||||
|
ClutterActor *label;
|
||||||
|
|
||||||
|
g_return_if_fail (ST_IS_BUTTON (button));
|
||||||
|
|
||||||
|
priv = button->priv;
|
||||||
|
|
||||||
|
g_free (priv->text);
|
||||||
|
|
||||||
|
if (text)
|
||||||
|
priv->text = g_strdup (text);
|
||||||
|
else
|
||||||
|
priv->text = g_strdup ("");
|
||||||
|
|
||||||
|
label = st_bin_get_child (ST_BIN (button));
|
||||||
|
|
||||||
|
if (label && CLUTTER_IS_TEXT (label))
|
||||||
|
{
|
||||||
|
clutter_text_set_text (CLUTTER_TEXT (label), priv->text);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
label = g_object_new (CLUTTER_TYPE_TEXT,
|
||||||
|
"text", priv->text,
|
||||||
|
"line-alignment", PANGO_ALIGN_CENTER,
|
||||||
|
"ellipsize", PANGO_ELLIPSIZE_END,
|
||||||
|
"use-markup", TRUE,
|
||||||
|
NULL);
|
||||||
|
st_bin_set_child (ST_BIN (button), label);
|
||||||
|
st_widget_set_accessible_name (ST_WIDGET (button), text);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Fake a style change so that we reset the style properties on the label */
|
||||||
|
st_widget_style_changed (ST_WIDGET (button));
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (button), "label");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_get_icon_name:
|
||||||
|
* @button: a #StButton
|
||||||
|
*
|
||||||
|
* Get the icon name of the button. If the button isn't showing an icon,
|
||||||
|
* the return value will be %NULL.
|
||||||
|
*
|
||||||
|
* Returns: (transfer none) (nullable): the icon name of the button
|
||||||
|
*/
|
||||||
|
const char *
|
||||||
|
st_button_get_icon_name (StButton *button)
|
||||||
|
{
|
||||||
|
ClutterActor *icon;
|
||||||
|
|
||||||
|
g_return_val_if_fail (ST_IS_BUTTON (button), NULL);
|
||||||
|
|
||||||
|
icon = st_bin_get_child (ST_BIN (button));
|
||||||
|
if (ST_IS_ICON (icon))
|
||||||
|
return st_icon_get_icon_name (ST_ICON (icon));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_set_icon_name:
|
||||||
|
* @button: a #Stbutton
|
||||||
|
* @icon_name: an icon name
|
||||||
|
*
|
||||||
|
* Adds an `StIcon` with the given icon name as a child.
|
||||||
|
*
|
||||||
|
* If @button already contains a child actor, that child will
|
||||||
|
* be removed and replaced with the icon.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
st_button_set_icon_name (StButton *button,
|
||||||
|
const char *icon_name)
|
||||||
|
{
|
||||||
|
ClutterActor *icon;
|
||||||
|
|
||||||
|
g_return_if_fail (ST_IS_BUTTON (button));
|
||||||
|
g_return_if_fail (icon_name != NULL);
|
||||||
|
|
||||||
|
icon = st_bin_get_child (ST_BIN (button));
|
||||||
|
|
||||||
|
if (ST_IS_ICON (icon))
|
||||||
|
{
|
||||||
|
st_icon_set_icon_name (ST_ICON (icon), icon_name);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
icon = g_object_new (ST_TYPE_ICON,
|
||||||
|
"icon-name", icon_name,
|
||||||
|
"x-align", CLUTTER_ACTOR_ALIGN_CENTER,
|
||||||
|
"y-align", CLUTTER_ACTOR_ALIGN_CENTER,
|
||||||
|
NULL);
|
||||||
|
st_bin_set_child (ST_BIN (button), icon);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (button), "icon-name");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_get_button_mask:
|
||||||
|
* @button: a #StButton
|
||||||
|
*
|
||||||
|
* Gets the mask of mouse buttons that @button emits the
|
||||||
|
* #StButton::clicked signal for.
|
||||||
|
*
|
||||||
|
* Returns: the mask of mouse buttons that @button emits the
|
||||||
|
* #StButton::clicked signal for.
|
||||||
|
*/
|
||||||
|
StButtonMask
|
||||||
|
st_button_get_button_mask (StButton *button)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (ST_IS_BUTTON (button), 0);
|
||||||
|
|
||||||
|
return button->priv->button_mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_set_button_mask:
|
||||||
|
* @button: a #Stbutton
|
||||||
|
* @mask: the mask of mouse buttons that @button responds to
|
||||||
|
*
|
||||||
|
* Sets which mouse buttons @button emits #StButton::clicked for.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
st_button_set_button_mask (StButton *button,
|
||||||
|
StButtonMask mask)
|
||||||
|
{
|
||||||
|
g_return_if_fail (ST_IS_BUTTON (button));
|
||||||
|
|
||||||
|
button->priv->button_mask = mask;
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (button), "button-mask");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_get_toggle_mode:
|
||||||
|
* @button: a #StButton
|
||||||
|
*
|
||||||
|
* Get the toggle mode status of the button.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if toggle mode is set, otherwise %FALSE
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
st_button_get_toggle_mode (StButton *button)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
|
||||||
|
|
||||||
|
return button->priv->is_toggle;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_set_toggle_mode:
|
||||||
|
* @button: a #Stbutton
|
||||||
|
* @toggle: %TRUE or %FALSE
|
||||||
|
*
|
||||||
|
* Enables or disables toggle mode for the button. In toggle mode, the active
|
||||||
|
* state will be "toggled" when the user clicks the button.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
st_button_set_toggle_mode (StButton *button,
|
||||||
|
gboolean toggle)
|
||||||
|
{
|
||||||
|
g_return_if_fail (ST_IS_BUTTON (button));
|
||||||
|
|
||||||
|
button->priv->is_toggle = toggle;
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (button), "toggle-mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_get_checked:
|
||||||
|
* @button: a #StButton
|
||||||
|
*
|
||||||
|
* Get the state of the button that is in toggle mode.
|
||||||
|
*
|
||||||
|
* Returns: %TRUE if the button is checked, or %FALSE if not
|
||||||
|
*/
|
||||||
|
gboolean
|
||||||
|
st_button_get_checked (StButton *button)
|
||||||
|
{
|
||||||
|
g_return_val_if_fail (ST_IS_BUTTON (button), FALSE);
|
||||||
|
|
||||||
|
return button->priv->is_checked;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_set_checked:
|
||||||
|
* @button: a #Stbutton
|
||||||
|
* @checked: %TRUE or %FALSE
|
||||||
|
*
|
||||||
|
* Sets the pressed state of the button. This is only really useful if the
|
||||||
|
* button has #toggle-mode mode set to %TRUE.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
st_button_set_checked (StButton *button,
|
||||||
|
gboolean checked)
|
||||||
|
{
|
||||||
|
g_return_if_fail (ST_IS_BUTTON (button));
|
||||||
|
|
||||||
|
if (button->priv->is_checked != checked)
|
||||||
|
{
|
||||||
|
button->priv->is_checked = checked;
|
||||||
|
|
||||||
|
st_widget_change_style_pseudo_class (ST_WIDGET (button), "checked", checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
g_object_notify (G_OBJECT (button), "checked");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* st_button_fake_release:
|
||||||
|
* @button: an #StButton
|
||||||
|
*
|
||||||
|
* If this widget is holding a pointer grab, this function will
|
||||||
|
* will ungrab it, and reset the pressed state. The effect is
|
||||||
|
* similar to if the user had released the mouse button, but without
|
||||||
|
* emitting the clicked signal.
|
||||||
|
*
|
||||||
|
* This function is useful if for example you want to do something
|
||||||
|
* after the user is holding the mouse button for a given period of
|
||||||
|
* time, breaking the grab.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
st_button_fake_release (StButton *button)
|
||||||
|
{
|
||||||
|
if (button->priv->pressed)
|
||||||
|
st_button_release (button, button->priv->pressed, 0);
|
||||||
|
|
||||||
|
if (button->priv->grabbed)
|
||||||
|
{
|
||||||
|
button->priv->grabbed = 0;
|
||||||
|
if (button->priv->device != NULL)
|
||||||
|
{
|
||||||
|
clutter_input_device_ungrab (button->priv->device);
|
||||||
|
button->priv->device = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
/*************************** ACCESSIBILITY SUPPORT ****************************/
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
#define ST_TYPE_BUTTON_ACCESSIBLE st_button_accessible_get_type ()
|
||||||
|
|
||||||
|
#define ST_BUTTON_ACCESSIBLE(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_CAST ((obj), \
|
||||||
|
ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessible))
|
||||||
|
|
||||||
|
#define ST_IS_BUTTON_ACCESSIBLE(obj) \
|
||||||
|
(G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
|
||||||
|
ST_TYPE_BUTTON_ACCESSIBLE))
|
||||||
|
|
||||||
|
#define ST_BUTTON_ACCESSIBLE_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_CAST ((klass), \
|
||||||
|
ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessibleClass))
|
||||||
|
|
||||||
|
#define ST_IS_BUTTON_ACCESSIBLE_CLASS(klass) \
|
||||||
|
(G_TYPE_CHECK_CLASS_TYPE ((klass), \
|
||||||
|
ST_TYPE_BUTTON_ACCESSIBLE))
|
||||||
|
|
||||||
|
#define ST_BUTTON_ACCESSIBLE_GET_CLASS(obj) \
|
||||||
|
(G_TYPE_INSTANCE_GET_CLASS ((obj), \
|
||||||
|
ST_TYPE_BUTTON_ACCESSIBLE, StButtonAccessibleClass))
|
||||||
|
|
||||||
|
typedef struct _StButtonAccessible StButtonAccessible;
|
||||||
|
typedef struct _StButtonAccessibleClass StButtonAccessibleClass;
|
||||||
|
|
||||||
|
struct _StButtonAccessible
|
||||||
|
{
|
||||||
|
StWidgetAccessible parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct _StButtonAccessibleClass
|
||||||
|
{
|
||||||
|
StWidgetAccessibleClass parent_class;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* AtkObject */
|
||||||
|
static void st_button_accessible_initialize (AtkObject *obj,
|
||||||
|
gpointer data);
|
||||||
|
|
||||||
|
G_DEFINE_TYPE (StButtonAccessible, st_button_accessible, ST_TYPE_WIDGET_ACCESSIBLE)
|
||||||
|
|
||||||
|
static const gchar *
|
||||||
|
st_button_accessible_get_name (AtkObject *obj)
|
||||||
|
{
|
||||||
|
StButton *button = NULL;
|
||||||
|
const gchar *name = NULL;
|
||||||
|
|
||||||
|
button = ST_BUTTON (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (obj)));
|
||||||
|
|
||||||
|
if (button == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
name = ATK_OBJECT_CLASS (st_button_accessible_parent_class)->get_name (obj);
|
||||||
|
if (name != NULL)
|
||||||
|
return name;
|
||||||
|
|
||||||
|
return button->priv->text;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_accessible_class_init (StButtonAccessibleClass *klass)
|
||||||
|
{
|
||||||
|
AtkObjectClass *atk_class = ATK_OBJECT_CLASS (klass);
|
||||||
|
|
||||||
|
atk_class->initialize = st_button_accessible_initialize;
|
||||||
|
atk_class->get_name = st_button_accessible_get_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_accessible_init (StButtonAccessible *self)
|
||||||
|
{
|
||||||
|
/* initialization done on AtkObject->initialize */
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_accessible_notify_label_cb (StButton *button,
|
||||||
|
GParamSpec *psec,
|
||||||
|
AtkObject *accessible)
|
||||||
|
{
|
||||||
|
g_object_notify (G_OBJECT (accessible), "accessible-name");
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
st_button_accessible_initialize (AtkObject *obj,
|
||||||
|
gpointer data)
|
||||||
|
{
|
||||||
|
ATK_OBJECT_CLASS (st_button_accessible_parent_class)->initialize (obj, data);
|
||||||
|
|
||||||
|
obj->role = ATK_ROLE_PUSH_BUTTON;
|
||||||
|
|
||||||
|
g_signal_connect (data, "notify::label",
|
||||||
|
G_CALLBACK (st_button_accessible_notify_label_cb), obj);
|
||||||
|
}
|
||||||
Reference in new issue
Block a user