spiegel-keyman/linux/keyman-config/keyman_config/keyboard_details.py
Eberhard Beilharz 9b7070b8a6
fix(linux): Remove tags from package description
This is a temporary workaround - see discussion on #8057. While
this is not the perfect solution it should improve things in most
cases.

Fixes #4769.
2023-07-05 11:43:32 +02:00

224 lines
8.8 KiB
Python

#!/usr/bin/python3
# Keyboard details window
import json
import logging
import re
import tempfile
from os import path
import gi
import qrcode
import qrcode.constants
gi.require_version('Gtk', '3.0')
from gi.repository import GLib, Gtk
from keyman_config import KeymanComUrl, _, secure_lookup
from keyman_config.accelerators import init_accel
from keyman_config.kmpmetadata import parsemetadata
# basics: keyboard name, package version, description
# other things: filename (of kmx), ,
# OSK availability, documentation availability, package copyright
# also: supported languages, fonts
# from kmx?: keyboard version, encoding, layout type
# there is data in kmp.inf/kmp.json
# there is possibly data in kbid.json (downloaded from api)
class KeyboardDetailsView(Gtk.Dialog):
# TODO Display all the information that is available
# especially what is displayed for Keyman on Windows
# TODO clean up file once have what we want
def __init__(self, parent, kmp):
self.kmp = kmp
# kmp has name, version, packageID, area
if "keyboard" in self.kmp["name"].lower():
wintitle = self.kmp["name"]
else:
wintitle = _("{name} keyboard").format(name=self.kmp["name"])
super().__init__(wintitle, parent=parent)
init_accel(self)
self.set_border_width(6)
self.packageDir = path.join(self.kmp['areapath'], self.kmp['packageID'])
kmp_json = path.join(self.packageDir, "kmp.json")
self.info, system, options, keyboards, files = parsemetadata(kmp_json)
if self.info is None:
self._display_invalid_metadata()
return
kbdata = None
jsonfile = path.join(self.packageDir, self.kmp['packageID'] + ".json")
if path.isfile(jsonfile):
try:
with open(jsonfile, "r") as read_file:
kbdata = json.load(read_file)
except Exception as e:
logging.warning('Exception %s reading %s %s', type(e), jsonfile, e.args)
self.grid = Gtk.Grid()
scrolledWindow = Gtk.ScrolledWindow()
scrolledWindow.add(self.grid)
# self.grid.set_column_homogeneous(True)
# kbdatapath = path.join("/usr/local/share/keyman", self.kmp["id"], self.kmp["id"] + ".json")
# Package info
prevlabel = self._add_label_with_content(
_("Package name: "), secure_lookup(self.info, 'name', 'description'), None)
prevlabel = self._add_label_with_content(
_("Package id: "), secure_lookup(self.kmp, 'packageID'), prevlabel)
prevlabel = self._add_label_with_content(
_("Package version: "), secure_lookup(self.info, 'version', 'description'), prevlabel)
prevlabel = self._add_label_with_content(
_("Package description: "), secure_lookup(kbdata, 'description'), prevlabel,
alwaysAdd=False)
prevlabel = self._add_label_with_content(
_("Package author: "), secure_lookup(self.info, 'author', 'description'), prevlabel,
alwaysAdd=False)
prevlabel = self._add_label_with_content(
_("Package copyright: "), secure_lookup(self.info, 'copyright', 'description'), prevlabel,
alwaysAdd=False)
prevlabel = self._add_horizontal_divider(self._add_padding(prevlabel))
# Keyboard info for each keyboard
prevlabel = self._add_info_for_all_keyboards(keyboards, prevlabel)
# Add an entire row of padding
prevlabel = self._add_padding(prevlabel)
prevlabel = self._add_qr_code(prevlabel)
self.add_button(_("_Close"), Gtk.ResponseType.CLOSE)
self.get_content_area().pack_start(scrolledWindow, True, True, 12)
self.resize(800, 450)
scrolledWindow.set_vadjustment(None)
self.show_all()
def _add_info_for_all_keyboards(self, keyboards, prevlabel) -> Gtk.Widget:
if keyboards:
for kbd in keyboards:
prevlabel = self._add_info_for_keyboard(kbd, prevlabel)
return prevlabel
def _add_info_for_keyboard(self, kbd, prevlabel: Gtk.Widget) -> Gtk.Widget:
kbdata = None
jsonfile = path.join(self.packageDir, kbd['id'] + ".json")
if path.isfile(jsonfile):
try:
with open(jsonfile, "r") as read_file:
kbdata = json.load(read_file)
except Exception as e:
logging.warning('Exception %s reading %s %s', type(e), jsonfile, e.args)
# start with padding
prevlabel = self._add_padding(prevlabel)
# show the icon somewhere
prevlabel = self._add_label_with_content(
_("Keyboard filename: "), path.join(self.packageDir, kbd['id'] + ".kmx"), prevlabel)
if kbdata and secure_lookup(kbdata, 'id') != secure_lookup(self.kmp, 'packageID'):
prevlabel = self._add_keyboard_details(kbdata, prevlabel)
return prevlabel
def _add_keyboard_details(self, kbdata, prevlabel: Gtk.Widget) -> Gtk.Widget:
prevlabel = self._add_label_with_content(_("Keyboard name: "), secure_lookup(kbdata, 'name'), prevlabel)
prevlabel = self._add_label_with_content(_("Keyboard id: "), secure_lookup(kbdata, 'id'), prevlabel)
prevlabel = self._add_label_with_content(_("Keyboard version: "), secure_lookup(kbdata, 'version'), prevlabel)
prevlabel = self._add_label_with_content(
_("Keyboard author: "), secure_lookup(kbdata, 'authorName'), prevlabel,
alwaysAdd=False)
prevlabel = self._add_label_with_content(
_("Keyboard license: "), secure_lookup(kbdata, 'license'), prevlabel)
prevlabel = self._add_label_with_content(
_("Keyboard description: "), secure_lookup(kbdata, 'description'), prevlabel)
prevlabel = self._add_horizontal_divider(self._add_padding(prevlabel))
return prevlabel
def _add_label(self, text: str, prevlabel) -> Gtk.Label:
label = Gtk.Label()
label.set_text(text)
label.set_halign(Gtk.Align.END)
label.set_valign(Gtk.Align.START)
if prevlabel:
self.grid.attach_next_to(label, prevlabel, Gtk.PositionType.BOTTOM, 1, 1)
else:
self.grid.add(label)
return label
def _add_label_with_content(self, text: str, content, prevlabel, alwaysAdd: bool = True):
if not alwaysAdd and not content:
return prevlabel
label = self._add_label(text, prevlabel)
contentLabel = Gtk.Label()
if content:
contentLabel.set_text(re.sub(r'( )?</?[^>]*>', ' ', content))
contentLabel.set_halign(Gtk.Align.START)
contentLabel.set_selectable(True)
contentLabel.set_line_wrap(True)
self.grid.attach_next_to(contentLabel, label, Gtk.PositionType.RIGHT, 1, 1)
return label
def _add_padding(self, prevlabel) -> Gtk.Label:
return self._add_label("", prevlabel)
def _add_horizontal_divider(self, prevlabel) -> Gtk.HSeparator:
divider = Gtk.HSeparator()
self.grid.attach_next_to(divider, prevlabel, Gtk.PositionType.BOTTOM, 2, 1)
return divider
def _display_invalid_metadata(self):
self.add_button(_("_Close"), Gtk.ResponseType.CLOSE)
self.grid = Gtk.Grid()
self.get_content_area().pack_start(self.grid, True, True, 12)
lbl_invalid_metadata = Gtk.Label()
lbl_invalid_metadata.set_text(
_("ERROR: Keyboard metadata is damaged.\nPlease \"Uninstall\" and then \"Install\" the keyboard."))
lbl_invalid_metadata.set_halign(Gtk.Align.END)
self.grid.add(lbl_invalid_metadata)
self.resize(700, 200)
self.show_all()
def _add_qr_code(self, prevlabel):
# If it doesn't exist, generate QR code to share keyboard package
path_qr = path.join(tempfile.gettempdir(), self.kmp['packageID'] + '_qrcode.png')
url = f"{KeymanComUrl}/go/keyboard/{self.kmp['packageID']}/share"
if not path.isfile(path_qr):
qr = qrcode.QRCode(
version=1,
error_correction=qrcode.constants.ERROR_CORRECT_H,
box_size=4,
border=4)
qr.add_data(url)
qr.make(fit=True)
img = qr.make_image()
img.save(path_qr)
# Display QR Code, spanning 2 columns so it will be centered
image = Gtk.Image()
image.set_from_file(path_qr)
self.grid.attach_next_to(image, prevlabel, Gtk.PositionType.BOTTOM, 2, 1)
lbl_share_kbd = Gtk.Label()
lbl_share_kbd.set_markup(
_("Scan this code to load this keyboard\non another device or <a href='{uri}'>share online</a>")
.format(uri=url))
lbl_share_kbd.set_halign(Gtk.Align.CENTER)
lbl_share_kbd.set_line_wrap(True)
self.grid.attach_next_to(lbl_share_kbd, image, Gtk.PositionType.BOTTOM, 2, 1)
return lbl_share_kbd