mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-06 08:55:34 +00:00
This changes re-creates the `<package>.bmp.png` as well as the `<package>.json` files if they are missing when `km-config` starts. - `<package.bmp.png` gets created from a `.bmp` or `.ico` file included in the package, similar to what happens when installing the keyboard. - `<package>.json` gets created from downloaded metadata, similar to the installation. If the files are missing or we can't write to the directory, e.g. if it's the shared area, we skip the re-creation. Fixes: #9396
271 lines
10 KiB
Python
Executable file
271 lines
10 KiB
Python
Executable file
#!/usr/bin/python3
|
|
|
|
import logging
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
import gi
|
|
|
|
|
|
gi.require_version('Gtk', '3.0')
|
|
gi.require_version('Gdk', '3.0')
|
|
|
|
from gi.overrides.GLib import GError
|
|
from gi.repository import GdkPixbuf, Gtk
|
|
|
|
from keyman_config import _
|
|
from keyman_config.accelerators import bind_accelerator, init_accel
|
|
from keyman_config.convertico import checkandsaveico
|
|
from keyman_config.dbus_util import get_keyman_config_service
|
|
from keyman_config.downloadkeyboard import DownloadKmpWindow
|
|
from keyman_config.fcitx_util import is_fcitx_running
|
|
from keyman_config.get_kmp import (InstallLocation, get_keyboard_dir,
|
|
get_install_area_string, get_keyman_dir)
|
|
from keyman_config.ibus_util import IbusDaemon, verify_ibus_daemon
|
|
from keyman_config.install_kmp import process_keyboard_data
|
|
from keyman_config.install_window import InstallKmpWindow, find_keyman_image
|
|
from keyman_config.keyboard_layouts_model import create_kbd_layouts_model
|
|
from keyman_config.keyboard_layouts_widget import KeyboardLayoutsWidget
|
|
from keyman_config.list_installed_kmp import get_installed_kmp
|
|
from keyman_config.options_widget import OptionsWidget
|
|
from keyman_config.sentry_handling import SentryErrorHandling
|
|
|
|
|
|
class ViewInstalledWindowBase(Gtk.Window):
|
|
def __init__(self):
|
|
self.accelerators = None
|
|
super().__init__(title=_("Keyman Configuration"))
|
|
init_accel(self)
|
|
self._config_service = get_keyman_config_service(self.refresh_installed_kmp)
|
|
|
|
def refresh_installed_kmp(self):
|
|
pass
|
|
|
|
def on_close_clicked(self, button):
|
|
logging.debug("Close application clicked")
|
|
self.close()
|
|
|
|
def on_refresh_clicked(self, button):
|
|
logging.debug("Refresh application clicked")
|
|
self.refresh_installed_kmp()
|
|
|
|
def on_download_clicked(self, button):
|
|
logging.debug("Download clicked")
|
|
downloadDlg = DownloadKmpWindow(self)
|
|
response = downloadDlg.run()
|
|
if response != Gtk.ResponseType.OK:
|
|
downloadDlg.destroy()
|
|
return
|
|
|
|
file = downloadDlg.downloadfile
|
|
language = downloadDlg.language
|
|
downloadDlg.destroy()
|
|
self.restart(self.install_file(file, language))
|
|
|
|
def on_installfile_clicked(self, button):
|
|
logging.debug("Install from file clicked")
|
|
dlg = Gtk.FileChooserDialog(
|
|
_("Choose a kmp file..."), self, Gtk.FileChooserAction.OPEN,
|
|
(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK))
|
|
dlg.resize(640, 480)
|
|
filter_text = Gtk.FileFilter()
|
|
# i18n: file type in file selection dialog
|
|
filter_text.set_name(_("KMP files"))
|
|
filter_text.add_pattern("*.kmp")
|
|
dlg.add_filter(filter_text)
|
|
response = dlg.run()
|
|
if response != Gtk.ResponseType.OK:
|
|
dlg.destroy()
|
|
return
|
|
|
|
file = dlg.get_filename()
|
|
dlg.destroy()
|
|
self.restart(self.install_file(file))
|
|
|
|
def install_file(self, kmpfile, language=None):
|
|
installDlg = InstallKmpWindow(kmpfile, viewkmp=self, language=language)
|
|
if installDlg.is_error:
|
|
return Gtk.ResponseType.CANCEL
|
|
result = installDlg.run()
|
|
installDlg.destroy()
|
|
return result
|
|
|
|
def restart(self, response=Gtk.ResponseType.OK):
|
|
if response != Gtk.ResponseType.CANCEL:
|
|
subprocess.Popen(sys.argv)
|
|
self.close()
|
|
|
|
def run(self):
|
|
self.resize(776, 424)
|
|
self.connect("destroy", Gtk.main_quit)
|
|
self.show_all()
|
|
|
|
if (not is_fcitx_running()) and (verify_ibus_daemon(False) != IbusDaemon.RUNNING):
|
|
dialog = Gtk.MessageDialog(
|
|
self, 0, Gtk.MessageType.WARNING,
|
|
Gtk.ButtonsType.YES_NO, _("ibus-daemon is not running. Try to start ibus-daemon now?"))
|
|
dialog.format_secondary_text(
|
|
_("If you just recently installed Keyman you might have to reboot or logout and login again."))
|
|
response = dialog.run()
|
|
dialog.destroy()
|
|
if response == Gtk.ResponseType.YES:
|
|
if verify_ibus_daemon(True) != IbusDaemon.RUNNING:
|
|
dialog = Gtk.MessageDialog(
|
|
self, 0, Gtk.MessageType.WARNING,
|
|
Gtk.ButtonsType.OK,
|
|
_("Unable to start ibus-daemon. Please reboot or logout and login again."))
|
|
dialog.run()
|
|
dialog.destroy()
|
|
self.close()
|
|
else:
|
|
self.close()
|
|
Gtk.main()
|
|
|
|
|
|
class ViewInstalledWindow(ViewInstalledWindowBase):
|
|
def __init__(self):
|
|
super().__init__()
|
|
|
|
self.sentry = SentryErrorHandling()
|
|
|
|
outmostVBox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL)
|
|
outmostVBox.pack_start(self._add_stack_sidebar(), True, True, 0)
|
|
outmostVBox.pack_end(self._add_app_buttons(), False, True, 12)
|
|
self.add(outmostVBox)
|
|
|
|
def _add_stack_sidebar(self):
|
|
outerHbox = Gtk.HBox()
|
|
sidebar = Gtk.StackSidebar()
|
|
stack = Gtk.Stack()
|
|
|
|
outerHbox.pack_start(sidebar, False, False, 0)
|
|
outerHbox.pack_end(Gtk.Separator(), False, False, 0)
|
|
outerHbox.pack_end(stack, True, True, 0)
|
|
stack.set_hexpand(True)
|
|
stack.set_vexpand(True)
|
|
sidebar.set_stack(stack)
|
|
|
|
self.store = create_kbd_layouts_model()
|
|
self.refresh_installed_kmp()
|
|
stack.add_titled(KeyboardLayoutsWidget(self, self.store, self.restart), "KeyboardLayouts", _("Keyboard Layouts"))
|
|
stack.add_titled(OptionsWidget(self.sentry), "Options", _("Options"))
|
|
return outerHbox
|
|
|
|
def _add_app_buttons(self):
|
|
bbox_bottom = Gtk.ButtonBox(spacing=12, orientation=Gtk.Orientation.HORIZONTAL)
|
|
bbox_bottom.set_layout(Gtk.ButtonBoxStyle.START)
|
|
|
|
button = Gtk.Button.new_with_mnemonic(_("_Install keyboard..."))
|
|
button.set_tooltip_text(_("Install a keyboard from a file"))
|
|
button.connect("clicked", self.on_installfile_clicked)
|
|
bbox_bottom.add(button)
|
|
|
|
button = Gtk.Button.new_with_mnemonic(_("_Download keyboard..."))
|
|
button.set_tooltip_text(_("Download and install a keyboard from the Keyman website"))
|
|
button.connect("clicked", self.on_download_clicked)
|
|
bbox_bottom.add(button)
|
|
|
|
button = Gtk.Button.new_with_mnemonic(_("_Refresh"))
|
|
button.set_tooltip_text(_("Refresh keyboard list"))
|
|
button.connect("clicked", self.on_refresh_clicked)
|
|
bbox_bottom.add(button)
|
|
|
|
button = Gtk.Button.new_with_mnemonic(_("_Close"))
|
|
button.set_tooltip_text(_("Close window"))
|
|
button.connect("clicked", self.on_close_clicked)
|
|
bind_accelerator(self.accelerators, button, '<Control>q')
|
|
bind_accelerator(self.accelerators, button, '<Control>w')
|
|
bbox_bottom.add(button)
|
|
|
|
bbox_hbox = Gtk.HBox(spacing=12)
|
|
bbox_hbox.pack_start(bbox_bottom, True, True, 12)
|
|
return bbox_hbox
|
|
|
|
def _try_restore_icon_file(self, path, kmpdata):
|
|
if not os.access(path, os.X_OK | os.W_OK):
|
|
return ''
|
|
|
|
file_name = os.path.join(path, kmpdata['packageID'] + '.bmp')
|
|
if not os.path.isfile(file_name):
|
|
file_name = os.path.join(path, kmpdata['packageID'] + '.ico')
|
|
if not os.path.isfile(file_name):
|
|
file_name = os.path.join(path, kmpdata['keyboardID'] + '.bmp')
|
|
if not os.path.isfile(file_name):
|
|
file_name = os.path.join(path, kmpdata['keyboardID'] + '.ico')
|
|
if not os.path.isfile(file_name):
|
|
return ''
|
|
checkandsaveico(file_name)
|
|
root, ext = os.path.splitext(file_name)
|
|
return root + '.bmp.png'
|
|
|
|
def _addlistitems(self, installed_kmp, store, install_area):
|
|
bmppng = ".bmp.png" # Icon file extension
|
|
|
|
for kmp in sorted(installed_kmp):
|
|
kmpdata = installed_kmp[kmp]
|
|
path = get_keyboard_dir(install_area, kmpdata['packageID'])
|
|
|
|
welcome_file = os.path.join(path, "welcome.htm")
|
|
options_file = os.path.join(path, "options.htm")
|
|
icofile_name = os.path.join(path, kmpdata['packageID'] + bmppng)
|
|
|
|
if not os.path.isfile(welcome_file):
|
|
welcome_file = None
|
|
if not os.path.isfile(options_file):
|
|
options_file = None
|
|
if not os.path.isfile(icofile_name):
|
|
icofile_name = os.path.join(path, kmpdata['keyboardID'] + bmppng)
|
|
if not os.path.isfile(icofile_name):
|
|
icofile_name = self._try_restore_icon_file(path, kmpdata)
|
|
if icofile_name == '':
|
|
icofile_name = find_keyman_image('icon_kmp.png')
|
|
|
|
try:
|
|
icofile = GdkPixbuf.Pixbuf.new_from_file_at_size(icofile_name, 16, 16)
|
|
except GError:
|
|
_, value, _ = sys.exc_info()
|
|
logging.info(f"Error reading icon file {icofile_name}: {value.message}")
|
|
icofile = None
|
|
|
|
store.append([
|
|
icofile,
|
|
kmpdata['name'],
|
|
kmpdata['kmpversion'],
|
|
kmpdata['packageID'],
|
|
install_area,
|
|
get_install_area_string(install_area),
|
|
path.replace(os.path.expanduser('~'), '~'),
|
|
welcome_file,
|
|
options_file])
|
|
|
|
def _try_restore_package_json(self, packageId, area):
|
|
packageDir = get_keyman_dir(area)
|
|
if not os.access(packageDir, os.X_OK | os.W_OK):
|
|
# we don't have write access to packageDir, so we can't restore the .json file
|
|
return False
|
|
return process_keyboard_data(packageId, os.path.join(packageDir, packageId))
|
|
|
|
def _add_keyboards_from_area(self, area):
|
|
kmps = get_installed_kmp(area)
|
|
for kmp in sorted(kmps):
|
|
kmpdata = kmps[kmp]
|
|
if not kmpdata['has_kbjson'] and not self._try_restore_package_json(kmpdata['packageID'], area):
|
|
self.incomplete_kmp.append(kmpdata)
|
|
self._addlistitems(kmps, self.store, area)
|
|
|
|
def refresh_installed_kmp(self):
|
|
logging.debug("Refreshing listview")
|
|
self.store.clear()
|
|
self.incomplete_kmp = []
|
|
self._add_keyboards_from_area(InstallLocation.User)
|
|
self._add_keyboards_from_area(InstallLocation.Shared)
|
|
self._add_keyboards_from_area(InstallLocation.OS)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
w = ViewInstalledWindow()
|
|
w.connect("destroy", Gtk.main_quit)
|
|
w.resize(576, 324)
|
|
w.show_all()
|
|
Gtk.main()
|