mirror of
https://github.com/DYefremov/DemonEditor.git
synced 2025-12-21 16:09:41 +01:00
401 lines
10 KiB
Python
401 lines
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
#
|
|
# The MIT License (MIT)
|
|
#
|
|
# Copyright (c) 2018-2021 Dmitriy Yefremov
|
|
#
|
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
# of this software and associated documentation files (the "Software"), to deal
|
|
# in the Software without restriction, including without limitation the rights
|
|
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
# copies of the Software, and to permit persons to whom the Software is
|
|
# furnished to do so, subject to the following conditions:
|
|
#
|
|
# The above copyright notice and this permission notice shall be included in
|
|
# all copies or substantial portions of the Software.
|
|
#
|
|
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
# THE SOFTWARE.
|
|
#
|
|
# Author: Dmitriy Yefremov
|
|
#
|
|
|
|
|
|
import locale
|
|
import os
|
|
from enum import Enum, IntEnum
|
|
from functools import lru_cache
|
|
|
|
import gi
|
|
|
|
gi.require_version("Gtk", "3.0")
|
|
gi.require_version("Gdk", "3.0")
|
|
from gi.repository import Gtk, Gdk, GLib
|
|
|
|
from app.settings import Settings, SettingsException, IS_DARWIN, GTK_PATH, IS_LINUX
|
|
|
|
# Setting mod mask for keyboard depending on platform
|
|
MOD_MASK = Gdk.ModifierType.MOD2_MASK if IS_DARWIN else Gdk.ModifierType.CONTROL_MASK
|
|
# Paths.
|
|
BASE_PATH = "app/ui/"
|
|
EX_PATH = "/usr/share/demoneditor/app/ui/" if IS_LINUX else "ui/"
|
|
# Path to *.glade files.
|
|
UI_RESOURCES_PATH = BASE_PATH if os.path.exists(BASE_PATH) else EX_PATH
|
|
# Translation.
|
|
LANG_PATH = UI_RESOURCES_PATH + "lang"
|
|
TEXT_DOMAIN = "demon-editor"
|
|
|
|
NOTIFY_IS_INIT = False
|
|
APP_FONT = None
|
|
IS_GNOME_SESSION = int(bool(os.environ.get("GNOME_DESKTOP_SESSION_ID")))
|
|
|
|
try:
|
|
settings = Settings.get_instance()
|
|
except SettingsException:
|
|
pass
|
|
else:
|
|
os.environ["LANGUAGE"] = settings.language
|
|
st = Gtk.Settings().get_default()
|
|
APP_FONT = st.get_property("gtk-font-name")
|
|
st.set_property("gtk-application-prefer-dark-theme", settings.dark_mode)
|
|
|
|
if settings.is_themes_support:
|
|
st.set_property("gtk-theme-name", settings.theme)
|
|
st.set_property("gtk-icon-theme-name", settings.icon_theme)
|
|
else:
|
|
if not IS_LINUX:
|
|
if IS_DARWIN:
|
|
s_path = f"{GTK_PATH + '/' + UI_RESOURCES_PATH if GTK_PATH else UI_RESOURCES_PATH}mac_style.css"
|
|
else:
|
|
s_path = f"{UI_RESOURCES_PATH}win_style.css"
|
|
|
|
style_provider = Gtk.CssProvider()
|
|
style_provider.load_from_path(s_path)
|
|
Gtk.StyleContext.add_provider_for_screen(Gdk.Screen.get_default(), style_provider,
|
|
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION)
|
|
|
|
if IS_LINUX:
|
|
if UI_RESOURCES_PATH == BASE_PATH:
|
|
locale.bindtextdomain(TEXT_DOMAIN, LANG_PATH)
|
|
# Init notify
|
|
try:
|
|
gi.require_version("Notify", "0.7")
|
|
from gi.repository import Notify
|
|
except ImportError:
|
|
pass
|
|
else:
|
|
NOTIFY_IS_INIT = Notify.init("DemonEditor")
|
|
elif IS_DARWIN:
|
|
import gettext
|
|
|
|
if GTK_PATH:
|
|
LANG_PATH = GTK_PATH + "/share/locale"
|
|
gettext.bindtextdomain(TEXT_DOMAIN, LANG_PATH)
|
|
# For launching from the bundle.
|
|
if os.getcwd() == "/" and GTK_PATH:
|
|
os.chdir(GTK_PATH)
|
|
else:
|
|
locale.setlocale(locale.LC_NUMERIC, "C")
|
|
|
|
# Icons.
|
|
theme = Gtk.IconTheme.get_default()
|
|
theme.append_search_path(UI_RESOURCES_PATH + "icons")
|
|
|
|
|
|
def get_icon(name, size, default=None):
|
|
try:
|
|
return theme.load_icon(name, size, 0) if theme.lookup_icon(name, size, 0) else default
|
|
except GLib.Error:
|
|
return default
|
|
|
|
|
|
_IMAGE_MISSING = get_icon("image-missing", 16)
|
|
CODED_ICON = get_icon("emblem-readonly", 16, _IMAGE_MISSING)
|
|
LOCKED_ICON = get_icon("changes-prevent-symbolic", 16, _IMAGE_MISSING)
|
|
HIDE_ICON = get_icon("go-jump", 16, _IMAGE_MISSING)
|
|
TV_ICON = get_icon("tv-symbolic", 16, _IMAGE_MISSING)
|
|
IPTV_ICON = get_icon("emblem-shared", 16, _IMAGE_MISSING)
|
|
EPG_ICON = get_icon("gtk-index", 16, _IMAGE_MISSING)
|
|
DEFAULT_ICON = get_icon("emblem-default", 16, _IMAGE_MISSING)
|
|
|
|
|
|
@lru_cache(maxsize=1)
|
|
def get_yt_icon(icon_name, size=24):
|
|
""" Getting YouTube icon.
|
|
|
|
If the icon is not found in the icon themes, the "Info" icon is returned by default!
|
|
"""
|
|
default_theme = Gtk.IconTheme.get_default()
|
|
if default_theme.has_icon(icon_name):
|
|
return default_theme.load_icon(icon_name, size, 0)
|
|
|
|
n_theme = Gtk.IconTheme.new()
|
|
import glob
|
|
|
|
for theme_name in map(os.path.basename, filter(os.path.isdir, glob.glob("/usr/share/icons/*"))):
|
|
n_theme.set_custom_theme(theme_name)
|
|
if n_theme.has_icon(icon_name):
|
|
return n_theme.load_icon(icon_name, size, 0)
|
|
|
|
return default_theme.load_icon("emblem-important-symbolic", size, 0)
|
|
|
|
|
|
def show_notification(message, timeout=10000, urgency=1):
|
|
""" Shows notification.
|
|
|
|
@param message: text to display
|
|
@param timeout: milliseconds
|
|
@param urgency: 0 - low, 1 - normal, 2 - critical
|
|
"""
|
|
if IS_DARWIN:
|
|
# Since NSUserNotification has been deprecated, osascript will be used.
|
|
os.system("""osascript -e 'display notification "{}" with title "DemonEditor"'""".format(message))
|
|
elif NOTIFY_IS_INIT:
|
|
notify = Notify.Notification.new("DemonEditor", message, "demon-editor")
|
|
notify.set_urgency(urgency)
|
|
notify.set_timeout(timeout)
|
|
notify.show()
|
|
|
|
|
|
class Page(Enum):
|
|
""" Main stack widget page. """
|
|
INFO = "info"
|
|
SERVICES = "services"
|
|
SATELLITE = "satellite"
|
|
PICONS = "picons"
|
|
EPG = "epg"
|
|
TIMERS = "timers"
|
|
RECORDINGS = "recordings"
|
|
FTP = "ftp"
|
|
CONTROL = "control"
|
|
|
|
|
|
class FavClickMode(IntEnum):
|
|
""" Double click mode on the service in the bouquet(FAV) list. """
|
|
DISABLED = 0
|
|
STREAM = 1
|
|
PLAY = 2
|
|
ZAP = 3
|
|
ZAP_PLAY = 4
|
|
|
|
|
|
class ViewTarget(Enum):
|
|
""" Used for set target view. """
|
|
BOUQUET = 0
|
|
FAV = 1
|
|
SERVICES = 2
|
|
|
|
|
|
class BqGenType(Enum):
|
|
""" Bouquet generation type. """
|
|
SAT = 0
|
|
EACH_SAT = 1
|
|
PACKAGE = 2
|
|
EACH_PACKAGE = 3
|
|
TYPE = 4
|
|
EACH_TYPE = 5
|
|
|
|
|
|
class Column(IntEnum):
|
|
""" Column nums in the views """
|
|
# Main view
|
|
SRV_CAS_FLAGS = 0
|
|
SRV_STANDARD = 1
|
|
SRV_CODED = 2
|
|
SRV_SERVICE = 3
|
|
SRV_LOCKED = 4
|
|
SRV_HIDE = 5
|
|
SRV_PACKAGE = 6
|
|
SRV_TYPE = 7
|
|
SRV_PICON = 8
|
|
SRV_PICON_ID = 9
|
|
SRV_SSID = 10
|
|
SRV_FREQ = 11
|
|
SRV_RATE = 12
|
|
SRV_POL = 13
|
|
SRV_FEC = 14
|
|
SRV_SYSTEM = 15
|
|
SRV_POS = 16
|
|
SRV_DATA_ID = 17
|
|
SRV_FAV_ID = 18
|
|
SRV_TRANSPONDER = 19
|
|
SRV_TOOLTIP = 20
|
|
SRV_BACKGROUND = 21
|
|
# FAV view
|
|
FAV_NUM = 0
|
|
FAV_CODED = 1
|
|
FAV_SERVICE = 2
|
|
FAV_LOCKED = 3
|
|
FAV_HIDE = 4
|
|
FAV_TYPE = 5
|
|
FAV_POS = 6
|
|
FAV_ID = 7
|
|
FAV_PICON = 8
|
|
FAV_TOOLTIP = 9
|
|
FAV_BACKGROUND = 10
|
|
# Bouquets view
|
|
BQ_NAME = 0
|
|
BQ_LOCKED = 1
|
|
BQ_HIDDEN = 2
|
|
BQ_TYPE = 3
|
|
# Alternatives view
|
|
ALT_NUM = 0
|
|
ALT_PICON = 1
|
|
ALT_SERVICE = 2
|
|
ALT_TYPE = 3
|
|
ALT_POS = 4
|
|
ALT_FAV_ID = 5
|
|
ALT_ID = 6
|
|
ALT_ITER = 7
|
|
# Recordings view
|
|
REC_SERVICE = 0
|
|
REC_TITLE = 1
|
|
REC_TIME = 2
|
|
REC_LEN = 3
|
|
REC_FILE = 4
|
|
REC_DESC = 5
|
|
|
|
def __index__(self):
|
|
""" Overridden to get the index in slices directly """
|
|
return self.value
|
|
|
|
|
|
# *************** Keyboard keys *************** #
|
|
|
|
class BaseKeyboardKey(Enum):
|
|
@classmethod
|
|
def value_exist(cls, value):
|
|
return value in (val.value for val in cls.__members__.values())
|
|
|
|
|
|
if IS_LINUX:
|
|
class KeyboardKey(BaseKeyboardKey):
|
|
""" The raw(hardware) codes [Linux] of the keyboard keys. """
|
|
E = 26
|
|
R = 27
|
|
T = 28
|
|
P = 33
|
|
S = 39
|
|
F = 41
|
|
X = 53
|
|
C = 54
|
|
V = 55
|
|
W = 25
|
|
Z = 52
|
|
INSERT = 118
|
|
HOME = 110
|
|
END = 115
|
|
UP = 111
|
|
DOWN = 116
|
|
PAGE_UP = 112
|
|
PAGE_DOWN = 117
|
|
LEFT = 113
|
|
RIGHT = 114
|
|
F2 = 68
|
|
F5 = 71
|
|
F7 = 73
|
|
SPACE = 65
|
|
DELETE = 119
|
|
BACK_SPACE = 22
|
|
RETURN = 36
|
|
CTRL_L = 37
|
|
CTRL_R = 105
|
|
# Laptop codes
|
|
HOME_KP = 79
|
|
END_KP = 87
|
|
PAGE_UP_KP = 81
|
|
PAGE_DOWN_KP = 89
|
|
|
|
elif IS_DARWIN:
|
|
class KeyboardKey(BaseKeyboardKey):
|
|
""" The raw(hardware) codes [macOS] of the keyboard keys. """
|
|
F = 3
|
|
E = 14
|
|
R = 15
|
|
T = 17
|
|
P = 35
|
|
S = 1
|
|
H = 4
|
|
L = 37
|
|
X = 7
|
|
C = 8
|
|
V = 9
|
|
W = 13
|
|
Z = 6
|
|
INSERT = -1
|
|
HOME = -1
|
|
END = -1
|
|
UP = 126
|
|
DOWN = 125
|
|
PAGE_UP = -1
|
|
PAGE_DOWN = -1
|
|
LEFT = 123
|
|
RIGHT = 123
|
|
F2 = 120
|
|
F5 = 96
|
|
F7 = 98
|
|
SPACE = 49
|
|
DELETE = 51
|
|
BACK_SPACE = 76
|
|
RETURN = 36
|
|
CTRL_L = 55
|
|
CTRL_R = 55
|
|
# Laptop codes.
|
|
HOME_KP = -1
|
|
END_KP = -1
|
|
PAGE_UP_KP = -1
|
|
PAGE_DOWN_KP = -1
|
|
|
|
else:
|
|
class KeyboardKey(BaseKeyboardKey):
|
|
""" The raw(hardware) codes [Windows] of the keyboard keys. """
|
|
E = 69
|
|
R = 82
|
|
T = 84
|
|
P = 80
|
|
S = 83
|
|
F = 70
|
|
X = 88
|
|
C = 67
|
|
V = 86
|
|
W = 87
|
|
Z = 90
|
|
INSERT = 45
|
|
HOME = 36
|
|
END = 35
|
|
UP = 38
|
|
DOWN = 40
|
|
PAGE_UP = 33
|
|
PAGE_DOWN = 34
|
|
LEFT = 37
|
|
RIGHT = 39
|
|
F2 = 113
|
|
F5 = 116
|
|
F7 = 118
|
|
SPACE = 32
|
|
DELETE = 46
|
|
BACK_SPACE = 8
|
|
RETURN = 13
|
|
CTRL_L = 17
|
|
CTRL_R = 163
|
|
# Laptop codes.
|
|
HOME_KP = -1
|
|
END_KP = -1
|
|
PAGE_UP_KP = -1
|
|
PAGE_DOWN_KP = -1
|
|
|
|
# Keys for move in lists. KEY_KP_(NAME) for laptop!
|
|
MOVE_KEYS = {KeyboardKey.UP, KeyboardKey.PAGE_UP,
|
|
KeyboardKey.DOWN, KeyboardKey.PAGE_DOWN,
|
|
KeyboardKey.HOME, KeyboardKey.END,
|
|
KeyboardKey.HOME_KP, KeyboardKey.END_KP,
|
|
KeyboardKey.PAGE_UP_KP, KeyboardKey.PAGE_DOWN_KP}
|
|
|
|
if __name__ == "__main__":
|
|
pass
|