mirror of
https://github.com/keymanapp/keyman.git
synced 2026-08-07 01:15:33 +00:00
387 lines
16 KiB
Python
Executable file
387 lines
16 KiB
Python
Executable file
#!/usr/bin/python3
|
|
|
|
import logging
|
|
import os
|
|
import pathlib
|
|
import subprocess
|
|
import sys
|
|
|
|
import gi
|
|
|
|
gi.require_version('Gtk', '3.0')
|
|
gi.require_version('Gdk', '3.0')
|
|
|
|
from gi.repository import Gtk, GdkPixbuf
|
|
|
|
from keyman_config import _
|
|
from keyman_config.list_installed_kmp import get_installed_kmp
|
|
from keyman_config.welcome import WelcomeView
|
|
from keyman_config.options import OptionsView
|
|
from keyman_config.keyboard_details import KeyboardDetailsView
|
|
from keyman_config.downloadkeyboard import DownloadKmpWindow
|
|
from keyman_config.install_window import InstallKmpWindow, find_keyman_image
|
|
from keyman_config.uninstall_kmp import uninstall_kmp
|
|
from keyman_config.accelerators import bind_accelerator, init_accel
|
|
from keyman_config.get_kmp import InstallLocation, get_keyboard_dir, get_keyman_dir
|
|
from gi.overrides.GLib import GError
|
|
|
|
|
|
class ViewInstalledWindowBase(Gtk.Window):
|
|
def __init__(self):
|
|
self.accelerators = None
|
|
Gtk.Window.__init__(self, title=_("Keyman Configuration"))
|
|
init_accel(self)
|
|
|
|
def refresh_installed_kmp(self):
|
|
pass
|
|
|
|
def on_close_clicked(self, button):
|
|
logging.debug("Close application clicked")
|
|
Gtk.main_quit()
|
|
|
|
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)
|
|
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(576, 324)
|
|
self.connect("destroy", Gtk.main_quit)
|
|
self.show_all()
|
|
Gtk.main()
|
|
|
|
|
|
class ViewInstalledWindow(ViewInstalledWindowBase):
|
|
def __init__(self):
|
|
ViewInstalledWindowBase.__init__(self)
|
|
|
|
# window is split left/right hbox
|
|
# right is ButtonBox
|
|
# possibly 2 ButtonBox in a vbox
|
|
# top one with _Remove, _About, ?_Welcome? or ?Read_Me?, _Options
|
|
# bottom one with _Download, _Install, Re_fresh, _Close
|
|
# left is GtkTreeView - does it need to be inside anything else apart from the hbox?
|
|
# with liststore which defines columns
|
|
# GdkPixbuf icon
|
|
# gchararray name
|
|
# gchararray version
|
|
# gchararray packageID (hidden)
|
|
# enum? area (user, shared, system) (icon or hidden?)
|
|
# gchararray welcomefile (hidden) (or just use area and packageID?)
|
|
# changing selected item in treeview changes what buttons are activated
|
|
# on selected_item_changed signal set the data that the buttons will use in their callbacks
|
|
# see https://developer.gnome.org/gtk3/stable/TreeWidget.html#TreeWidget
|
|
|
|
hbox = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL)
|
|
s = Gtk.ScrolledWindow()
|
|
hbox.pack_start(s, True, True, 0)
|
|
|
|
self.store = Gtk.ListStore(
|
|
GdkPixbuf.Pixbuf, # icon
|
|
str, # name
|
|
str, # version
|
|
str, # packageID
|
|
int, # enum InstallLocation (KmpArea is GObject version)
|
|
str, # path to welcome file if it exists or None
|
|
str) # path to options file if it exists or None
|
|
|
|
# add installed keyboards to the the store e.g.
|
|
# treeiter = store.append([GdkPixbuf.Pixbuf.new_from_file_at_size(
|
|
# "/usr/local/share/keyman/libtralo/libtralo.ico.png", 16, 16), \
|
|
# "LIBTRALO", "1.6.1", \
|
|
# "libtralo", KmpArea.SHARED, True])
|
|
|
|
self.refresh_installed_kmp()
|
|
|
|
self.tree = Gtk.TreeView(self.store)
|
|
|
|
renderer = Gtk.CellRendererPixbuf()
|
|
# i18n: column header in table displaying installed keyboards
|
|
column = Gtk.TreeViewColumn(_("Icon"), renderer, pixbuf=0)
|
|
self.tree.append_column(column)
|
|
renderer = Gtk.CellRendererText()
|
|
# i18n: column header in table displaying installed keyboards
|
|
column = Gtk.TreeViewColumn(_("Name"), renderer, text=1)
|
|
self.tree.append_column(column)
|
|
# i18n: column header in table displaying installed keyboards
|
|
column = Gtk.TreeViewColumn(_("Version"), renderer, text=2)
|
|
self.tree.append_column(column)
|
|
|
|
select = self.tree.get_selection()
|
|
select.connect("changed", self.on_tree_selection_changed)
|
|
|
|
s.add(self.tree)
|
|
|
|
vbox = Gtk.Box(orientation=Gtk.Orientation.VERTICAL, spacing=12)
|
|
|
|
bbox_top = Gtk.ButtonBox(spacing=12, orientation=Gtk.Orientation.VERTICAL)
|
|
bbox_top.set_layout(Gtk.ButtonBoxStyle.START)
|
|
|
|
self.uninstall_button = Gtk.Button.new_with_mnemonic(_("_Uninstall"))
|
|
self.uninstall_button.set_tooltip_text(_("Uninstall keyboard"))
|
|
self.uninstall_button.connect("clicked", self.on_uninstall_clicked)
|
|
self.uninstall_button.set_sensitive(False)
|
|
bbox_top.add(self.uninstall_button)
|
|
|
|
self.about_button = Gtk.Button.new_with_mnemonic(_("_About"))
|
|
self.about_button.set_tooltip_text(_("About keyboard"))
|
|
self.about_button.connect("clicked", self.on_about_clicked)
|
|
self.about_button.set_sensitive(False)
|
|
bbox_top.add(self.about_button)
|
|
|
|
self.help_button = Gtk.Button.new_with_mnemonic(_("_Help"))
|
|
self.help_button.set_tooltip_text(_("Help for keyboard"))
|
|
self.help_button.connect("clicked", self.on_help_clicked)
|
|
self.help_button.set_sensitive(False)
|
|
bbox_top.add(self.help_button)
|
|
|
|
self.options_button = Gtk.Button.new_with_mnemonic(_("_Options"))
|
|
self.options_button.set_tooltip_text(_("Settings for keyboard"))
|
|
self.options_button.connect("clicked", self.on_options_clicked)
|
|
self.options_button.set_sensitive(False)
|
|
bbox_top.add(self.options_button)
|
|
|
|
vbox.pack_start(bbox_top, False, False, 12)
|
|
|
|
bbox_bottom = Gtk.ButtonBox(spacing=12, orientation=Gtk.Orientation.VERTICAL)
|
|
bbox_bottom.set_layout(Gtk.ButtonBoxStyle.END)
|
|
|
|
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(_("_Download"))
|
|
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(_("_Install"))
|
|
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(_("_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)
|
|
|
|
vbox.pack_end(bbox_bottom, False, False, 12)
|
|
|
|
hbox.pack_start(vbox, False, False, 12)
|
|
self.add(hbox)
|
|
|
|
def addlistitems(self, installed_kmp, store, install_area):
|
|
for kmp in sorted(installed_kmp):
|
|
kmpdata = installed_kmp[kmp]
|
|
bmppng = ".bmp.png" # Icon file extension
|
|
|
|
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 = 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("Error reading icon file %s: %s" % (icofile_name, value.message))
|
|
icofile = None
|
|
|
|
store.append([
|
|
icofile,
|
|
kmpdata['name'],
|
|
kmpdata['version'],
|
|
kmpdata['packageID'],
|
|
install_area,
|
|
welcome_file,
|
|
options_file])
|
|
|
|
def refresh_installed_kmp(self):
|
|
logging.debug("Refreshing listview")
|
|
self.store.clear()
|
|
self.incomplete_kmp = []
|
|
user_kmp = get_installed_kmp(InstallLocation.User)
|
|
for kmp in sorted(user_kmp):
|
|
kmpdata = user_kmp[kmp]
|
|
if kmpdata["has_kbjson"] is False:
|
|
self.incomplete_kmp.append(kmpdata)
|
|
self.addlistitems(user_kmp, self.store, InstallLocation.User)
|
|
shared_kmp = get_installed_kmp(InstallLocation.Shared)
|
|
for kmp in sorted(shared_kmp):
|
|
kmpdata = shared_kmp[kmp]
|
|
if kmpdata["has_kbjson"] is False:
|
|
self.incomplete_kmp.append(kmpdata)
|
|
self.addlistitems(shared_kmp, self.store, InstallLocation.Shared)
|
|
os_kmp = get_installed_kmp(InstallLocation.OS)
|
|
for kmp in sorted(os_kmp):
|
|
kmpdata = os_kmp[kmp]
|
|
if kmpdata["has_kbjson"] is False:
|
|
self.incomplete_kmp.append(kmpdata)
|
|
self.addlistitems(os_kmp, self.store, InstallLocation.OS)
|
|
|
|
def on_tree_selection_changed(self, selection):
|
|
model, treeiter = selection.get_selected()
|
|
if treeiter is not None:
|
|
self.uninstall_button.set_tooltip_text(
|
|
_("Uninstall keyboard {package}").format(package=model[treeiter][1]))
|
|
self.help_button.set_tooltip_text(
|
|
_("Help for keyboard {package}").format(package=model[treeiter][1]))
|
|
self.about_button.set_tooltip_text(
|
|
_("About keyboard {package}").format(package=model[treeiter][1]))
|
|
self.options_button.set_tooltip_text(
|
|
_("Settings for keyboard {package}").format(package=model[treeiter][1]))
|
|
logging.debug("You selected %s version %s", model[treeiter][1], model[treeiter][2])
|
|
self.about_button.set_sensitive(True)
|
|
if model[treeiter][4] == InstallLocation.User:
|
|
logging.debug("Enabling uninstall button for %s in %s", model[treeiter][3], model[treeiter][4])
|
|
self.uninstall_button.set_sensitive(True)
|
|
else:
|
|
self.uninstall_button.set_sensitive(False)
|
|
logging.debug("Disabling uninstall button for %s in %s", model[treeiter][3], model[treeiter][4])
|
|
# welcome file if it exists
|
|
if model[treeiter][5]:
|
|
self.help_button.set_sensitive(True)
|
|
else:
|
|
self.help_button.set_sensitive(False)
|
|
# options file if it exists
|
|
if model[treeiter][6]:
|
|
self.options_button.set_sensitive(True)
|
|
else:
|
|
self.options_button.set_sensitive(False)
|
|
else:
|
|
self.uninstall_button.set_tooltip_text(_("Uninstall keyboard"))
|
|
self.help_button.set_tooltip_text(_("Help for keyboard"))
|
|
self.about_button.set_tooltip_text(_("About keyboard"))
|
|
self.options_button.set_tooltip_text(_("Settings for keyboard"))
|
|
self.uninstall_button.set_sensitive(False)
|
|
self.about_button.set_sensitive(False)
|
|
self.help_button.set_sensitive(False)
|
|
self.options_button.set_sensitive(False)
|
|
|
|
def on_help_clicked(self, button):
|
|
model, treeiter = self.tree.get_selection().get_selected()
|
|
if treeiter is not None:
|
|
logging.info("Open welcome.htm for %s if available", model[treeiter][1])
|
|
welcome_file = model[treeiter][5]
|
|
if welcome_file and os.path.isfile(welcome_file):
|
|
uri_path = pathlib.Path(welcome_file).as_uri()
|
|
logging.info("opening " + uri_path)
|
|
w = WelcomeView(self, uri_path, model[treeiter][3])
|
|
w.run()
|
|
w.destroy()
|
|
else:
|
|
logging.info("welcome.htm not available")
|
|
|
|
def on_options_clicked(self, button):
|
|
model, treeiter = self.tree.get_selection().get_selected()
|
|
if treeiter is not None:
|
|
logging.info("Open options.htm for %s if available", model[treeiter][1])
|
|
options_file = model[treeiter][6]
|
|
if options_file and os.path.isfile(options_file):
|
|
uri_path = pathlib.Path(options_file).as_uri()
|
|
logging.info("opening " + uri_path)
|
|
# TODO: Determine keyboardID
|
|
info = {"optionurl": uri_path, "packageID": model[treeiter][3], "keyboardID": model[treeiter][3]}
|
|
w = OptionsView(info)
|
|
w.resize(800, 600)
|
|
w.show_all()
|
|
else:
|
|
logging.info("options.htm not available")
|
|
|
|
def on_uninstall_clicked(self, button):
|
|
model, treeiter = self.tree.get_selection().get_selected()
|
|
if treeiter is not None:
|
|
logging.info("Uninstall keyboard " + model[treeiter][3] + "?")
|
|
dialog = Gtk.MessageDialog(
|
|
self, 0, Gtk.MessageType.QUESTION,
|
|
Gtk.ButtonsType.YES_NO, _("Uninstall keyboard package?"))
|
|
dialog.format_secondary_text(
|
|
_("Are you sure that you want to uninstall the {keyboard} keyboard and its fonts?")
|
|
.format(keyboard=model[treeiter][1]))
|
|
response = dialog.run()
|
|
dialog.destroy()
|
|
if response == Gtk.ResponseType.YES:
|
|
logging.info("Uninstalling keyboard" + model[treeiter][1])
|
|
# can only uninstall with the gui from user area
|
|
uninstall_kmp(model[treeiter][3])
|
|
logging.info("need to restart window after uninstalling a keyboard")
|
|
self.restart()
|
|
elif response == Gtk.ResponseType.NO:
|
|
logging.info("Not uninstalling keyboard " + model[treeiter][1])
|
|
|
|
def on_about_clicked(self, button):
|
|
model, treeiter = self.tree.get_selection().get_selected()
|
|
if treeiter is not None and model[treeiter] is not None:
|
|
logging.info("Show keyboard details of " + model[treeiter][1])
|
|
areapath = get_keyman_dir(model[treeiter][4])
|
|
kmp = {
|
|
"name": model[treeiter][1], "version": model[treeiter][2],
|
|
"packageID": model[treeiter][3], "areapath": areapath
|
|
}
|
|
w = KeyboardDetailsView(self, kmp)
|
|
w.run()
|
|
w.destroy()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
w = ViewInstalledWindow()
|
|
w.connect("destroy", Gtk.main_quit)
|
|
w.resize(576, 324)
|
|
w.show_all()
|
|
Gtk.main()
|