merge 'staging'

This commit is contained in:
Vinicius Moreira
2019-08-12 17:29:33 -03:00
parent 7af8e89325
commit 1951a545c8
48 changed files with 119 additions and 119 deletions

5
bauh/__init__.py Normal file
View File

@@ -0,0 +1,5 @@
__version__ = '1.0.0'
__app_name__ = 'bauh'
import os
ROOT_DIR = os.path.dirname(os.path.abspath(__file__))

63
bauh/app.py Executable file
View File

@@ -0,0 +1,63 @@
import sys
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QApplication
from bauh_api.util.cache import Cache
from bauh_api.util.disk import DiskCacheLoaderFactory
from bauh_api.util.http import HttpClient
from bauh import __version__, __app_name__, app_args
from bauh.core import resource, extensions
from bauh.core.controller import GenericApplicationManager
from bauh.util import util
from bauh.util.memory import CacheCleaner
from bauh.view.qt.systray import TrayIcon
from bauh.view.qt.window import ManageWindow
args = app_args.read()
locale_keys = util.get_locale_keys(args.locale)
http_client = HttpClient()
caches, cache_map = [], {}
managers = extensions.load_managers(caches=caches,
cache_map=cache_map,
locale_keys=locale_keys,
http_client=http_client,
app_args=args)
icon_cache = Cache(expiration_time=args.icon_exp)
caches.append(icon_cache)
disk_loader_factory = DiskCacheLoaderFactory(disk_cache=args.disk_cache, cache_map=cache_map)
manager = GenericApplicationManager(managers, disk_loader_factory=disk_loader_factory, app_args=args, locale_keys=locale_keys)
manager.prepare()
app = QApplication(sys.argv)
app.setApplicationName(__app_name__)
app.setApplicationVersion(__version__)
app.setWindowIcon(QIcon(resource.get_path('img/logo.svg')))
manage_window = ManageWindow(locale_keys=locale_keys,
manager=manager,
icon_cache=icon_cache,
disk_cache=args.disk_cache,
download_icons=bool(args.download_icons),
screen_size=app.primaryScreen().size(),
suggestions=args.sugs)
if args.tray:
trayIcon = TrayIcon(locale_keys=locale_keys,
manager=manager,
manage_window=manage_window,
check_interval=args.check_interval,
update_notification=bool(args.update_notification))
manage_window.tray_icon = trayIcon
trayIcon.show()
else:
manage_window.refresh_apps()
manage_window.show()
CacheCleaner(caches).start()
sys.exit(app.exec_())

73
bauh/app_args.py Normal file
View File

@@ -0,0 +1,73 @@
import argparse
import os
from argparse import Namespace
from colorama import Fore
from bauh import __app_name__, __version__
def log_msg(msg: str, color: int = None):
if color is None:
print('[{}] {}'.format(__app_name__, msg))
else:
print('{}[{}] {}{}'.format(color, __app_name__, msg, Fore.RESET))
def read() -> Namespace:
parser = argparse.ArgumentParser(prog=__app_name__, description="GUI for Flatpak applications management")
parser.add_argument('-v', '--version', action='version', version='%(prog)s {}'.format(__version__))
parser.add_argument('-e', '--cache-exp', action="store",
default=int(os.getenv('BAUH_CACHE_EXPIRATION', 60 * 60)), type=int,
help='cached API data expiration time in SECONDS. Default: %(default)s')
parser.add_argument('-ie', '--icon-exp', action="store", default=int(os.getenv('BAUH_ICON_EXPIRATION', 60 * 5)),
type=int, help='cached icons expiration time in SECONDS. Default: %(default)s')
parser.add_argument('-l', '--locale', action="store", default=os.getenv('BAUH_LOCALE', 'en'),
help='Translation key. Default: %(default)s')
parser.add_argument('-i', '--check-interval', action="store", default=int(os.getenv('BAUH_CHECK_INTERVAL', 60)),
type=int, help='Updates check interval in SECONDS. Default: %(default)s')
parser.add_argument('-n', '--update-notification', action="store", choices=[0, 1],
default=os.getenv('BAUH_UPDATE_NOTIFICATION', 1), type=int,
help='Enables / disables system notifications for new updates. Default: %(default)s')
parser.add_argument('-dc', '--disk-cache', action="store", choices=[0, 1],
default=os.getenv('BAUH_DISK_CACHE', 1), type=int,
help='Enables / disables disk cache. When disk cache is enabled, the installed applications data are loaded faster. Default: %(default)s')
parser.add_argument('-di', '--download-icons', action="store", choices=[0, 1],
default=os.getenv('BAUH_DOWNLOAD_ICONS', 1), type=int,
help='Enables / disables app icons download. It may improve the application speed, depending of how applications data are retrieved by their extensions.')
parser.add_argument('-co', '--check-packaging-once', action="store",
default=os.getenv('BAUH_CHECK_PACKAGING_ONCE', 0), choices=[0, 1], type=int,
help='If the available supported packaging types should be checked ONLY once. It improves the application speed if enabled, but can generate errors if you uninstall any packaging technology while using it, and every time a supported packaging type is installed it will only be available after a restart. Default: %(default)s')
parser.add_argument('--tray', action="store", default=os.getenv('BAUH_TRAY', 0), choices=[0, 1], type=int,
help='If the tray icon and update-check daemon should be created. Default: %(default)s')
parser.add_argument('--sugs', action="store", default=os.getenv('BAUH_SUGGESTIONS', 1), choices=[0, 1], type=int, help='If app suggestions should be displayed if no app is installed (runtimes do not count as apps). Default: %(default)s')
args = parser.parse_args()
if args.cache_exp < 0:
log_msg("'cache-exp' set to '{}': cache will not expire.".format(args.cache_exp), Fore.YELLOW)
if args.icon_exp < 0:
log_msg("'icon-exp' set to '{}': cache will not expire.".format(args.cache_exp), Fore.YELLOW)
if not args.locale.strip():
log_msg("'locale' set as '{}'. You must provide a valid one. Aborting...".format(args.locale), Fore.RED)
exit(1)
if args.check_interval <= 0:
log_msg("'check-interval' set as '{}'. It must be >= 0. Aborting...".format(args.check_interval), Fore.RED)
exit(1)
if args.update_notification == 0:
log_msg('updates notifications are disabled', Fore.YELLOW)
if args.download_icons == 0:
log_msg("'download-icons' is disabled", Fore.YELLOW)
if args.check_packaging_once == 1:
log_msg("'check-packaging-once' is enabled", Fore.YELLOW)
if args.sugs == 0:
log_msg("suggestions are disabled", Fore.YELLOW)
return args

1
bauh/core/__init__.py Executable file
View File

@@ -0,0 +1 @@

259
bauh/core/controller.py Executable file
View File

@@ -0,0 +1,259 @@
from argparse import Namespace
from threading import Thread
from typing import List, Dict
from bauh_api.abstract.controller import ApplicationManager
from bauh_api.abstract.model import Application, ApplicationUpdate
from bauh_api.util.disk import DiskCacheLoader
from bauh_api.util.disk import DiskCacheLoaderFactory
from bauh_api.util.system import SystemProcess
from bauh import ROOT_DIR
SUGGESTIONS_LIMIT = 6
class GenericApplicationManager(ApplicationManager):
def __init__(self, managers: List[ApplicationManager], disk_loader_factory: DiskCacheLoaderFactory, app_args: Namespace, locale_keys: dict):
super(GenericApplicationManager, self).__init__(app_args=app_args, app_cache=None, locale_keys=locale_keys, fpakman_root_dir=ROOT_DIR, http_client=None)
self.managers = managers
self.map = {m.get_app_type(): m for m in self.managers}
self.disk_loader_factory = disk_loader_factory
self._enabled_map = {} if app_args.check_packaging_once else None
self.thread_prepare = None
def _sort(self, apps: List[Application], word: str) -> List[Application]:
exact_name_matches, contains_name_matches, others = [], [], []
for app in apps:
lower_name = app.base_data.name.lower()
if word == lower_name:
exact_name_matches.append(app)
elif word in lower_name:
contains_name_matches.append(app)
else:
others.append(app)
res = []
for app_list in (exact_name_matches, contains_name_matches, others):
app_list.sort(key=lambda a: a.base_data.name.lower())
res.extend(app_list)
return res
def _is_enabled(self, man: ApplicationManager):
if self._enabled_map is not None:
enabled = self._enabled_map.get(man.get_app_type())
if enabled is None:
enabled = man.is_enabled()
self._enabled_map[man.get_app_type()] = enabled
return enabled
else:
return man.is_enabled()
def _search(self, word: str, man: ApplicationManager, disk_loader, res: dict):
if self._is_enabled(man):
apps_found = man.search(words=word, disk_loader=disk_loader)
res['installed'].extend(apps_found['installed'])
res['new'].extend(apps_found['new'])
def search(self, word: str, disk_loader: DiskCacheLoader = None) -> Dict[str, List[Application]]:
self._wait_to_be_ready()
res = {'installed': [], 'new': []}
norm_word = word.strip().lower()
disk_loader = self.disk_loader_factory.new()
disk_loader.start()
threads = []
for man in self.managers:
t = Thread(target=self._search, args=(norm_word, man, disk_loader, res))
t.start()
threads.append(t)
for t in threads:
t.join()
if disk_loader:
disk_loader.stop = True
disk_loader.join()
for key in res:
res[key] = self._sort(res[key], norm_word)
return res
def _wait_to_be_ready(self):
if self.thread_prepare:
self.thread_prepare.join()
self.thread_prepare = None
def read_installed(self, disk_loader: DiskCacheLoader = None) -> List[Application]:
self._wait_to_be_ready()
installed = []
disk_loader = None
for man in self.managers:
if self._is_enabled(man):
if not disk_loader:
disk_loader = self.disk_loader_factory.new()
disk_loader.start()
installed.extend(man.read_installed(disk_loader=disk_loader))
if disk_loader:
disk_loader.stop = True
disk_loader.join()
installed.sort(key=lambda a: a.base_data.name.lower())
return installed
def can_downgrade(self):
return True
def downgrade_app(self, app: Application, root_password: str) -> SystemProcess:
man = self._get_manager_for(app)
if man and app.can_be_downgraded():
return man.downgrade_app(app, root_password)
else:
raise Exception("downgrade is not possible for {}".format(app.__class__.__name__))
def clean_cache_for(self, app: Application):
man = self._get_manager_for(app)
if man:
return man.clean_cache_for(app)
def update(self, app: Application, root_password: str) -> SystemProcess:
man = self._get_manager_for(app)
if man:
return man.update(app, root_password)
def uninstall(self, app: Application, root_password: str) -> SystemProcess:
man = self._get_manager_for(app)
if man:
return man.uninstall(app, root_password)
def install(self, app: Application, root_password: str) -> SystemProcess:
man = self._get_manager_for(app)
if man:
return man.install(app, root_password)
def get_info(self, app: Application):
man = self._get_manager_for(app)
if man:
return man.get_info(app)
def get_history(self, app: Application):
man = self._get_manager_for(app)
if man:
return man.get_history(app)
def get_app_type(self):
return None
def is_enabled(self):
return True
def _get_manager_for(self, app: Application) -> ApplicationManager:
man = self.map[app.__class__]
return man if man and self._is_enabled(man) else None
def cache_to_disk(self, app: Application, icon_bytes: bytes, only_icon: bool):
if self.disk_loader_factory.disk_cache and app.supports_disk_cache():
man = self._get_manager_for(app)
if man:
return man.cache_to_disk(app, icon_bytes=icon_bytes, only_icon=only_icon)
def requires_root(self, action: str, app: Application):
man = self._get_manager_for(app)
if man:
return man.requires_root(action, app)
def refresh(self, app: Application, root_password: str) -> SystemProcess:
self._wait_to_be_ready()
man = self._get_manager_for(app)
if man:
return man.refresh(app, root_password)
def _prepare(self):
if self.managers:
for man in self.managers:
if self._is_enabled(man):
man.prepare()
def prepare(self):
self.thread_prepare = Thread(target=self._prepare)
self.thread_prepare.start()
def list_updates(self) -> List[ApplicationUpdate]:
self._wait_to_be_ready()
updates = []
if self.managers:
for man in self.managers:
if self._is_enabled(man):
man_updates = man.list_updates()
if man_updates:
updates.extend(man_updates)
return updates
def list_warnings(self) -> List[str]:
warnings = []
if self.managers:
for man in self.managers:
man_warnings = man.list_warnings()
if man_warnings:
if warnings is None:
warnings = []
warnings.extend(man_warnings)
else:
warnings.append(self.locale_keys['warning.no_managers'])
return warnings
def _fill_suggestions(self, suggestions: list, man: ApplicationManager, limit: int):
if self._is_enabled(man):
man_sugs = man.list_suggestions(limit)
if man_sugs:
suggestions.extend(man_sugs)
def list_suggestions(self, limit: int) -> List[Application]:
if self.managers:
suggestions, threads = [], []
for man in self.managers:
t = Thread(target=self._fill_suggestions, args=(suggestions, man, SUGGESTIONS_LIMIT))
t.start()
threads.append(t)
for t in threads:
t.join()
return suggestions

52
bauh/core/extensions.py Normal file
View File

@@ -0,0 +1,52 @@
import inspect
import os
import pkgutil
from argparse import Namespace
from typing import List, Dict
from bauh_api.abstract.controller import ApplicationManager
from bauh_api.util.cache import Cache
from bauh_api.util.http import HttpClient
from bauh import ROOT_DIR
from bauh.util import util
ignore_modules = {'fpakman_api'}
def find_manager(member):
if inspect.isclass(member) and inspect.getmro(member)[1].__name__ == 'ApplicationManager':
return member
elif inspect.ismodule(member) and member.__name__ not in ignore_modules:
for name, mod in inspect.getmembers(member):
manager_found = find_manager(mod)
if manager_found:
return manager_found
def load_managers(caches: List[Cache], cache_map: Dict[type, Cache], locale_keys: Dict[str, str], app_args: Namespace, http_client: HttpClient) -> List[ApplicationManager]:
managers = []
for m in pkgutil.iter_modules():
if m.ispkg and m.name and m.name not in ignore_modules and m.name.startswith('fpakman_'):
module = pkgutil.find_loader(m.name).load_module()
manager_class = find_manager(module)
if manager_class:
locale_path = '{}/resources/locale'.format(module.__path__[0])
if os.path.exists(locale_path):
locale_keys.update(util.get_locale_keys(app_args.locale, locale_path))
app_cache = Cache(expiration_time=app_args.cache_exp)
man = manager_class(app_args=app_args,
fpakman_root_dir=ROOT_DIR,
http_client=http_client,
locale_keys=locale_keys,
app_cache=app_cache)
cache_map[man.get_app_type()] = app_cache
caches.append(app_cache)
managers.append(man)
return managers

5
bauh/core/resource.py Executable file
View File

@@ -0,0 +1,5 @@
from bauh import ROOT_DIR
def get_path(resource_path):
return ROOT_DIR + '/resources/' + resource_path

142
bauh/resources/img/about.svg Executable file
View File

@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
width="23.99629"
height="24.016451"
viewBox="0 0 23.99629 24.016451"
xml:space="preserve"
sodipodi:docname="about.svg"
inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata
id="metadata933"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs931"><linearGradient
inkscape:collect="always"
id="linearGradient1618"><stop
style="stop-color:#0088aa;stop-opacity:1;"
offset="0"
id="stop1614" /><stop
style="stop-color:#0088aa;stop-opacity:0;"
offset="1"
id="stop1616" /></linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient1618"
id="linearGradient1620"
x1="-22.25"
y1="-4.4919429"
x2="-28.25"
y2="9.7580566"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="739"
id="namedview929"
showgrid="false"
showborder="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="0.50000001"
inkscape:cx="-197.75436"
inkscape:cy="-20.194417"
inkscape:window-x="0"
inkscape:window-y="432"
inkscape:window-maximized="1"
inkscape:current-layer="Capa_1" />
<g
id="g898"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g900"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g902"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g904"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g906"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g908"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g910"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g912"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g914"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g916"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g918"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g920"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g922"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g924"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g926"
transform="translate(239.5083,-10.347359)">
</g>
<g
id="g1624"
transform="matrix(1.4998882,0,0,1.5002743,1.245458e-5,-1.4011516e-5)"><ellipse
ry="7.9742656"
rx="7.969605"
cy="8.0040293"
cx="7.999351"
id="path939"
style="fill:url(#linearGradient1620);fill-opacity:1;stroke:#4d4d4d;stroke-width:0.05950872" /><path
d="m 7.8854557,3.7948064 c 1.4552891,0 2.8880403,0.6706192 2.8880403,2.2746822 0,1.479228 -1.6940807,2.0480998 -2.057903,2.5826085 C 8.442458,9.04969 8.5336818,9.6083603 7.7832313,9.6083603 c -0.4888527,0 -0.7276446,-0.3978613 -0.7276446,-0.7618964 0,-1.3546614 1.9892172,-1.6612455 1.9892172,-2.7767069 0,-0.6139735 -0.408361,-0.9780086 -1.0909303,-0.9780086 -1.4552891,0 -0.887018,1.5012419 -1.9892171,1.5012419 -0.3978971,0 -0.73945,-0.2389316 -0.73945,-0.6937069 -2.683e-4,-1.1157299 1.2728414,-2.104477 2.6602492,-2.104477 z M 7.8288432,10.3469 c 0.5108538,0 0.9323617,0.420413 0.9323617,0.933176 0,0.512763 -0.420703,0.933175 -0.9323617,0.933175 -0.5116586,0 -0.9326299,-0.419875 -0.9326299,-0.933175 0,-0.512495 0.4209713,-0.933176 0.9326299,-0.933176 z"
id="path894"
inkscape:connector-curvature="0"
style="fill:#ffffff;stroke-width:0.26838395" /></g></svg>

After

Width:  |  Height:  |  Size: 4.3 KiB

126
bauh/resources/img/app_info.svg Executable file
View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 10.048382 24.046875"
xml:space="preserve"
sodipodi:docname="info3.svg"
width="10.048382"
height="24.046875"
inkscape:version="0.92.4 5da689c313, 2019-01-14"><metadata
id="metadata856"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs854" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="739"
id="namedview852"
showgrid="false"
showborder="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="4.2578125"
inkscape:cx="72.471146"
inkscape:cy="14.340367"
inkscape:window-x="0"
inkscape:window-y="432"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<g
id="g819"
transform="matrix(0.04838117,0,0,0.046875,-7.3613885,0.0234375)"
style="fill:#ffffff;stroke:#333333">
<circle
style="fill:#ffffff;stroke:#333333"
cx="255.99899"
cy="75.469002"
r="75.469002"
id="circle815" />
<path
style="fill:#ffffff;stroke:#333333"
d="M 359.345,230.952 V 185.078 H 152.654 v 45.874 c 15.395,0 27.874,12.479 27.874,27.873 v 179.426 c 0,15.394 -12.48,27.874 -27.874,27.874 V 512 h 206.692 v -45.873 c -15.395,0 -27.874,-12.48 -27.874,-27.874 V 258.825 c -10e-4,-15.394 12.479,-27.873 27.873,-27.873 z"
id="path817"
inkscape:connector-curvature="0" />
</g>
<g
id="g821"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g823"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g825"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g827"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g829"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g831"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g833"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g835"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g837"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g839"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g841"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g843"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g845"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g847"
transform="translate(-152.62981,-487.97656)">
</g>
<g
id="g849"
transform="translate(-152.62981,-487.97656)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

View File

@@ -0,0 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="svg1069"
width="23.995125"
height="24.004246"
viewBox="0 0 23.995125 24.004246"
sodipodi:docname="settings_3svg.svg"
inkscape:version="0.92.4 5da689c313, 2019-01-14">
<metadata
id="metadata1075">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs1073" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="739"
id="namedview1071"
showgrid="false"
showborder="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="3.6875"
inkscape:cx="-9.476931"
inkscape:cy="-2.2075679"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
inkscape:current-layer="svg1069" />
<path
style="opacity:0.95;fill:#ffffff;fill-opacity:1;stroke:#333333;stroke-width:0.18215948;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
d="m 12.037667,0.09769312 c -0.72043,0.0637 -1.845694,-0.17097 -2.124115,0.73052 -0.08137,0.50962998 0.01515,1.11913998 -0.387221,1.48686998 -0.15736,0.21026 -0.218698,0.60361 -0.5623,0.63599 -0.231156,0.1825 -0.482289,0.4504 -0.805519,0.37943 -0.33519,0.2264 -0.769547,0.14283 -1.104717,-0.0131 -0.637006,0.0746 -1.03032,-0.57076 -1.534985,-0.83449 -0.885643,-0.28862 -1.407803,0.72336 -2.066783,1.10551 -0.25976,0.24133 -0.32452,0.57765 -0.62658,0.79417 -0.43543,0.45528 -0.40057,1.2532 0.17952,1.56982 0.43169,0.5212 0.62511,1.299 0.50516,1.9344 -0.16048,0.28253 -0.0479,0.67728 -0.35903,0.91801 -0.26574,0.43697 -0.71175,0.56803 -1.12615,0.76234 -0.49764,0.39004 -1.38447003,0.0415 -1.77627003,0.5985699 -0.25861,0.96381 -0.12396,2.0252 -0.10444,3.02506 0.0509,0.34442 0.16638,0.76134 0.54628,0.8123 0.39988003,0.23671 0.91536003,0.015 1.32806003,0.27835 1.07706,0.33161 1.66943,1.62912 1.41067,2.66993 -0.16334,0.19954 -0.0658,0.46544 -0.29147,0.64159 -0.17812,0.26318 -0.30377,0.55768 -0.54204,0.80265 -0.47979,0.64849 0.33827,1.30054 0.7343,1.74015 0.54098,0.48923 1.03633,1.14605 1.70793,1.42206 0.23833,-0.12983 0.561824,-0.003 0.682754,-0.29128 0.406567,-0.31367 0.790035,-0.72877 1.343883,-0.71643 0.670515,-0.32887 1.506075,0.0717 2.058292,0.51022 0.156853,0.14124 0.119739,0.39965 0.359038,0.4728 0.497527,0.61075 0.0069,1.60837 0.705143,2.08371 0.874823,0.43838 1.919873,0.26337 2.864969,0.24421 0.659557,0.0141 1.164474,-0.57496 1.060705,-1.22434 0.136064,-0.91849 0.774338,-1.93921 1.77666,-2.0214 0.363739,-0.22429 0.812774,-0.0589 1.166487,0.0473 0.759153,0.0202 1.164413,1.0138 1.994591,0.87519 0.4616,-0.16783 0.782731,-0.57119 1.168225,-0.87538 0.50432,-0.48329 1.01909,-0.97579 1.28636,-1.62094 0.144599,-0.71118 -0.730744,-1.06353 -0.890067,-1.70137 -0.100989,-0.22757 -0.0049,-0.49343 -0.179905,-0.69117 -0.04673,-0.84613 0.412368,-1.65862 1.11958,-2.10125 0.305264,0.0339 0.411929,-0.37354 0.763246,-0.3152 0.591382,0.0245 1.440793,-0.10369 1.514329,-0.83661 0.05748,-1.0154 0.169703,-2.08796 -0.111765,-3.07367 -0.246291,-0.5252799 -0.897473,-0.31832 -1.285587,-0.5717499 -0.232806,0.0157 -0.388499,-0.16382 -0.634686,-0.13137 -0.42064,-0.20941 -0.754834,-0.56503 -0.952802,-0.96218 -0.424334,-0.53524 -0.424871,-1.41725 -0.12354,-1.9942 0.231967,-0.27004 0.338997,-0.6292 0.631791,-0.88078 0.499512,-0.69929 -0.151216,-1.47883 -0.657434,-1.94804 -0.499489,-0.48119 -0.952777,-1.06089 -1.592345,-1.33975 -0.659633,-0.16168 -0.98362,0.57179 -1.534211,0.74575 -0.125378,0.13168 -0.358502,0.0285 -0.45652,0.20544 -0.334935,-0.0571 -0.579653,0.23864 -0.92597,0.071 -0.681696,-0.0739 -1.347245,-0.54339 -1.630535,-1.13561 -0.102377,-0.27247 -0.348146,-0.49327 -0.331433,-0.83892 0.03397,-0.5178 -0.09363,-1.33088998 -0.748962,-1.35222998 -0.449202,-0.1603 -0.970143,-0.0669 -1.440591,-0.0918 z m 7.358542,2.90314998 c 0.09152,-0.13657 0.180927,-0.13633 0,0 z m -7.380163,5.3821 c 0.391366,-0.0266 0.744155,0.13023 1.08889,0.2282 0.620271,0.0714 1.143014,0.59122 1.609108,0.96643 0.332946,0.16294 0.165699,0.5772199 0.500916,0.7067799 0.221257,0.23884 0.09768,0.58093 0.319466,0.79533 0.07607,1.0594 0.150917,2.20776 -0.611329,3.06518 -0.47404,0.62985 -1.178494,1.1764 -1.950002,1.33815 -0.35881,3e-5 -0.689909,0.20436 -1.078465,0.1302 -0.307061,0.0394 -0.57734,-0.14573 -0.847792,-0.16049 -0.201682,-0.11921 -0.435966,-0.0458 -0.596853,-0.26311 -0.569432,-0.0889 -0.972318,-0.61227 -1.328054,-1.03395 -0.01774,-0.31189 -0.432129,-0.39167 -0.438374,-0.75366 -0.338183,-0.85301 -0.406908,-1.899 0.01158,-2.72529 0.246346,-0.36263 0.317564,-0.8099699 0.679663,-1.1234499 0.674731,-0.73941 1.661428,-1.11848 2.641244,-1.17032 z m -1.846149,6.8379199 c 6.6e-4,-0.004 0.01304,-0.006 0,0 z m -0.08223,0.13213 c -0.0079,-0.0194 0.04552,-0.0747 0,0 z m 1.435381,0.35725 c 0.0025,0.0119 -0.0022,-0.007 0,0 z m -8.415191,2.52892 c -0.003,0.003 -0.0212,-0.009 0,0 z m 17.973515,1.20852 c 0.08741,0.13502 -0.194278,-0.16337 0,0 z m -5.019582,0.98456 c -0.01137,0.0188 0.01702,-0.0484 0,0 z"
id="path1626"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 5.8 KiB

View File

@@ -0,0 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
viewBox="0 0 48.000008 47.999886"
enable-background="new 0 0 26 26"
id="svg8"
sodipodi:docname="update_white.svg"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
width="48.000008"
height="47.999886">
<metadata
id="metadata14">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs12" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="739"
id="namedview10"
showgrid="false"
showborder="false"
inkscape:zoom="3.2091769"
inkscape:cx="11.492348"
inkscape:cy="-1.3206439"
inkscape:window-x="0"
inkscape:window-y="432"
inkscape:window-maximized="1"
inkscape:current-layer="svg8"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<g
id="g6"
transform="matrix(1.8424663,0,0,1.9956679,0.04794249,-1.943739)"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.05204165">
<path
d="m 25,17 h -2 c -0.6,0 -1,0.4 -1,1 v 2.5 C 22,20.8 21.8,21 21.5,21 H 4.5 C 4.2,21 4,20.8 4,20.5 V 18 C 4,17.4 3.6,17 3,17 H 1 c -0.6,0 -1,0.4 -1,1 v 6 c 0,0.6 0.4,1 1,1 h 24 c 0.6,0 1,-0.4 1,-1 v -6 c 0,-0.6 -0.4,-1 -1,-1 z"
id="path2"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.05204165" />
<path
d="m 12.3,16.7 c 0.2,0.2 0.5,0.3 0.7,0.3 0.2,0 0.5,-0.1 0.7,-0.3 l 6,-6 C 19.9,10.5 20,10.3 20,10 20,9.7 19.9,9.5 19.7,9.3 L 18.3,7.9 C 18.1,7.7 17.9,7.6 17.6,7.6 c -0.3,0 -0.5,0.1 -0.7,0.3 l -1,1 C 15.6,9.2 15,9 15,8.5 V 2 C 15,1.4 14.6,1 14,1 H 12 C 11.4,1 11,1.4 11,2 V 8.6 C 11,9 10.5,9.3 10.1,9 L 9.1,8 C 8.9,7.8 8.7,7.7 8.4,7.7 8.1,7.7 7.9,7.8 7.7,8 L 6.3,9.4 C 6.1,9.6 6,9.8 6,10.1 c 0,0.3 0.1,0.5 0.3,0.7 z"
id="path4"
inkscape:connector-curvature="0"
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0.05204165" />
</g>
<g
transform="matrix(3.7795276,0,0,3.7795276,668.44512,-480.86859)"
id="layer1"
inkscape:label="Layer 1" />
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

62
bauh/resources/img/checked.svg Executable file
View File

@@ -0,0 +1,62 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
viewBox="0 0 11.999997 12"
enable-background="new 0 0 26 26"
id="svg817"
sodipodi:docname="checked.svg"
width="11.999997"
height="12"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata823">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs821" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="703"
id="namedview819"
showgrid="false"
showborder="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="4.7692308"
inkscape:cx="-42.8741"
inkscape:cy="25.96043"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
inkscape:current-layer="svg817" />
<path
d="M 0.13846154,6.6 C 0.04615385,6.48 0,6.3 0,6.18 0,6.06 0.04615385,5.88 0.13846154,5.76 L 0.78461536,4.92 c 0.1846154,-0.24 0.46153844,-0.24 0.64615384,0 l 0.046153,0.06 2.5384614,3.54 c 0.092308,0.12 0.2307693,0.12 0.323077,0 L 10.523077,0.18 h 0.04615 v 0 c 0.184616,-0.24 0.461539,-0.24 0.646154,0 l 0.646154,0.84 c 0.184616,0.24 0.184616,0.6 0,0.84 v 0 L 4.4769229,11.82 C 4.3846154,11.94 4.2923076,12 4.1538462,12 4.0153844,12 3.923077,11.94 3.8307691,11.82 L 0.23076922,6.78 Z"
id="path815"
inkscape:connector-curvature="0"
style="fill:#00ff00;stroke-width:0.52623481" />
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@@ -0,0 +1,72 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
viewBox="0 0 24 24"
enable-background="new 0 0 26 26"
id="svg8"
sodipodi:docname="downgrade.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
width="24"
height="24">
<metadata
id="metadata14">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs12" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="703"
id="namedview10"
showgrid="false"
showborder="false"
inkscape:zoom="4.5384616"
inkscape:cx="-20.270304"
inkscape:cy="12.13426"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
inkscape:current-layer="svg8"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<g
id="g6"
transform="matrix(0.92307692,0,0,1,0,-1)"
style="fill:#ff0000">
<path
d="m 25,17 h -2 c -0.6,0 -1,0.4 -1,1 v 2.5 C 22,20.8 21.8,21 21.5,21 H 4.5 C 4.2,21 4,20.8 4,20.5 V 18 C 4,17.4 3.6,17 3,17 H 1 c -0.6,0 -1,0.4 -1,1 v 6 c 0,0.6 0.4,1 1,1 h 24 c 0.6,0 1,-0.4 1,-1 v -6 c 0,-0.6 -0.4,-1 -1,-1 z"
id="path2"
inkscape:connector-curvature="0"
style="fill:#ff0000" />
<path
d="m 12.3,16.7 c 0.2,0.2 0.5,0.3 0.7,0.3 0.2,0 0.5,-0.1 0.7,-0.3 l 6,-6 C 19.9,10.5 20,10.3 20,10 20,9.7 19.9,9.5 19.7,9.3 L 18.3,7.9 C 18.1,7.7 17.9,7.6 17.6,7.6 c -0.3,0 -0.5,0.1 -0.7,0.3 l -1,1 C 15.6,9.2 15,9 15,8.5 V 2 C 15,1.4 14.6,1 14,1 H 12 C 11.4,1 11,1.4 11,2 V 8.6 C 11,9 10.5,9.3 10.1,9 L 9.1,8 C 8.9,7.8 8.7,7.7 8.4,7.7 8.1,7.7 7.9,7.8 7.7,8 L 6.3,9.4 C 6.1,9.6 6,9.8 6,10.1 c 0,0.3 0.1,0.5 0.3,0.7 z"
id="path4"
inkscape:connector-curvature="0"
style="fill:#ff0000" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -0,0 +1,144 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
viewBox="0 0 24 24"
xml:space="preserve"
sodipodi:docname="exclamation2.svg"
width="24"
height="24"
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
id="metadata949"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs947"><linearGradient
inkscape:collect="always"
id="linearGradient955"><stop
style="stop-color:#d4aa00;stop-opacity:1;"
offset="0"
id="stop951" /><stop
style="stop-color:#d4aa00;stop-opacity:0;"
offset="1"
id="stop953" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient955"
id="linearGradient957"
x1="-5.0336413"
y1="-1.8872991"
x2="-22.291842"
y2="17.528175"
gradientUnits="userSpaceOnUse" /><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient955"
id="linearGradient1210"
gradientUnits="userSpaceOnUse"
x1="-5.0336413"
y1="-1.8872991"
x2="-22.291842"
y2="17.528175" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="739"
id="namedview945"
showgrid="false"
showborder="false"
inkscape:zoom="16"
inkscape:cx="3.6611245"
inkscape:cy="14.576667"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
inkscape:current-layer="Capa_1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<g
id="g914">
</g>
<g
id="g916">
</g>
<g
id="g918">
</g>
<g
id="g920">
</g>
<g
id="g922">
</g>
<g
id="g924">
</g>
<g
id="g926">
</g>
<g
id="g928">
</g>
<g
id="g930">
</g>
<g
id="g932">
</g>
<g
id="g934">
</g>
<g
id="g936">
</g>
<g
id="g938">
</g>
<g
id="g940">
</g>
<g
id="g942">
</g>
<g
id="g1217"
transform="translate(10.429681,16.353516)"><g
transform="translate(-10.429681,-16.353516)"
style="fill:url(#linearGradient957);fill-opacity:1"
id="g912">
<path
inkscape:connector-curvature="0"
id="path910"
d="M 12,0 C 5.373,0 0,5.373 0,12 0,18.627 5.373,24 12,24 18.627,24 24,18.627 24,12 24,5.373 18.627,0 12,0 Z m 0,19.66 c -0.938,0 -1.58,-0.723 -1.58,-1.66 0,-0.964 0.669,-1.66 1.58,-1.66 0.963,0 1.58,0.696 1.58,1.66 0,0.938 -0.617,1.66 -1.58,1.66 z m 0.622,-6.339 c -0.239,0.815 -0.992,0.829 -1.243,0 -0.289,-0.956 -1.316,-4.585 -1.316,-6.942 0,-3.11 3.891,-3.125 3.891,0 -0.001,2.371 -1.083,6.094 -1.332,6.942 z"
style="fill:url(#linearGradient1210);fill-opacity:1" />
</g><path
inkscape:connector-curvature="0"
id="path1198"
d="m 1.3648508,-2.489113 c -0.250891,-0.124189 -0.327144,-0.271469 -0.55064598,-1.063559 -0.664465,-2.354862 -1.04293,-4.2896531 -1.147198,-5.8647013 -0.0767,-1.1585747 0.09689,-1.7887227 0.643137,-2.3346987 0.4799,-0.479662 1.07478698,-0.648108 1.71268798,-0.48496 0.564701,0.144426 0.973861,0.498107 1.24114,1.072852 0.194111,0.41741 0.235782,0.660945 0.231159,1.3509478 -0.0094,1.4036771 -0.421836,3.6124492 -1.172557,6.2796362 -0.174505,0.619988 -0.250011,0.795473 -0.406612,0.94501 -0.186151,0.177754 -0.337702,0.205108 -0.551111,0.09947 z"
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.03125" /><path
inkscape:connector-curvature="0"
id="path1200"
d="m 1.5625068,-12.296875 c -0.133701,-4.9e-5 -0.257218,0.01288 -0.375,0.03711 -0.01713,0.0034 -0.04774,0.0019 -0.0625,0.0059 -0.04019,0.01083 -0.07595,0.03503 -0.115235,0.04883 -0.07496,0.02544 -0.14937098,0.05778 -0.22070298,0.0957 -0.0693,0.03572 -0.136178,0.07245 -0.201172,0.117187 -0.395912,0.272524 -0.712194,0.695841 -0.861328,1.210938 -0.06573,0.22717 -0.07016,0.309243 -0.06836,0.9374996 2.45e-4,0.086621 0.01357,0.1776107 0.01563,0.2636719 0.0078,0.2791513 0.0217,0.567286 0.05469,0.8886719 0.02567,0.2619407 0.07197,0.5785227 0.117188,0.8730468 0.02898,0.1904904 0.0596,0.3838721 0.0957,0.5859376 0.02975,0.1646911 0.04058,0.2865674 0.07617,0.4667968 0.02253,0.1140684 0.06671,0.2792068 0.09375,0.40625 0.08083,0.3833354 0.176637,0.7912044 0.279296,1.2089844 0.246077,1.01458 0.501767,1.987896 0.61914098,2.261719 0.03892,0.0908 0.09578,0.1787 0.160156,0.253906 0.0136,0.01632 0.0326,0.02725 0.04687,0.04102 0.04125,0.0418 0.08219,0.08717 0.125,0.113281 0.06319,0.03853 0.132973,0.05138 0.205078,0.05273 0.05796,0.0011 0.116036,-0.008 0.173829,-0.0293 0.0075,-0.0027 0.01406,-0.0086 0.02148,-0.01172 0.05163,-0.02164 0.101553,-0.04945 0.148437,-0.08789 0.0063,-0.0052 0.01142,-0.01207 0.01758,-0.01758 0.03276,-0.02895 0.06728,-0.05485 0.0957,-0.0918 0.0047,-0.0061 0.01259,-0.03336 0.01758,-0.04102 0.0052,-0.0076 0.01256,-0.01166 0.01758,-0.01953 0.0081,-0.01277 0.02602,-0.07759 0.03516,-0.0957 0.06526,-0.134622 0.143353,-0.353611 0.236328,-0.652344 0.0061,-0.02016 0.01142,-0.03393 0.01758,-0.05469 0.08547,-0.278648 0.179338,-0.659826 0.277344,-1.03711 0.07187,-0.280521 0.135865,-0.545447 0.207032,-0.847656 0.07372,-0.310588 0.144002,-0.546795 0.216796,-0.882812 0.07915,-0.3653757 0.116018,-0.6348283 0.173828,-0.9511722 0.04874,-0.2610293 0.108943,-0.5530308 0.140626,-0.7675782 10e-4,-0.00687 9.46e-4,-0.012662 0.002,-0.019531 0.07245,-0.4988018 0.120186,-0.9630243 0.136718,-1.3847656 0.004,-0.069734 0.01867,-0.1529162 0.02149,-0.2207032 0.01305,-0.3147671 -0.006,-0.5821898 -0.04102,-0.8300778 -0.0308,-0.279799 -0.08807,-0.50458 -0.222656,-0.773438 -0.01126,-0.02248 -0.0271,-0.0406 -0.03906,-0.0625 -0.02477,-0.04414 -0.05401,-0.08682 -0.08203,-0.128906 -0.06991,-0.10808 -0.147597,-0.206648 -0.234375,-0.296875 -0.0099,-0.01034 -0.01524,-0.02297 -0.02539,-0.0332 -0.0097,-0.0098 -0.02143,-0.01589 -0.03125,-0.02539 -0.06451,-0.06161 -0.13129,-0.118543 -0.203124,-0.169922 -0.03372,-0.02443 -0.06852,-0.0466 -0.103516,-0.06836 -0.06898,-0.04263 -0.138762,-0.08215 -0.212891,-0.115234 -0.03375,-0.01482 -0.06876,-0.02643 -0.103515,-0.03906 -0.05821,-0.02181 -0.113151,-0.0506 -0.173828,-0.06641 -0.01977,-0.0052 -0.057,-0.0035 -0.08008,-0.0078 -0.124433,-0.02492 -0.253549,-0.03901 -0.388671,-0.03906 z"
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.03125" /><path
inkscape:connector-curvature="0"
id="path1205"
d="m 1.5800848,0.015625 c -0.0178,0 -0.03152,0.0036 -0.04883,0.0039 -0.08654,0.002 -0.170637,0.0024 -0.25586,0.01758 -0.13064,0.01899 -0.248148,0.05582 -0.35742198,0.105469 -0.01495,0.0066 -0.03215,0.0066 -0.04687,0.01367 -0.01917,0.0092 -0.03902,0.03191 -0.05859,0.04297 -0.08499,0.04925 -0.168809,0.110204 -0.25,0.183594 -0.07098,0.06223 -0.123346,0.120203 -0.183594,0.191406 -0.04461,0.05354 -0.08918,0.107632 -0.125,0.164063 -0.01932,0.0304 -0.05333,0.06019 -0.06836,0.08984 -0.02046,0.04038 -0.03184,0.09021 -0.04883,0.134765 -0.007,0.01713 -0.01317,0.03525 -0.01953,0.05273 -0.02632,0.07746 -0.04829,0.155268 -0.06445,0.240234 -0.0059,0.03085 -0.0093,0.06368 -0.01367,0.0957 -0.0137,0.09626 -0.02276,0.191001 -0.02344,0.289063 -1.3e-5,0.0034 0,0.0063 0,0.0098 0,0.0038 0.0019,0.0078 0.002,0.01172 4.07e-4,0.136442 0.01441,0.266964 0.04101,0.392578 0.0035,0.01807 0.0021,0.04995 0.0059,0.06445 0.0034,0.01335 0.01187,0.02392 0.01563,0.03711 0.0062,0.0218 0.0066,0.04736 0.01367,0.06836 0.02672,0.07959 0.06612,0.147112 0.101562,0.21875 0.01461,0.02886 0.02478,0.06019 0.04102,0.08789 0.202316,0.355898 0.515811,0.606046 0.92578098,0.71875 0.114715,0.03154 0.285952,0.03886 0.457031,0.03516 0.03918,-0.0012 0.07647,-0.0058 0.115234,-0.0098 0.06575,-0.005 0.140201,-0.0026 0.19336,-0.01367 0.06811,-0.0142 0.134222,-0.04299 0.201172,-0.06836 0.333189,-0.126241 0.641516,-0.374039 0.800781,-0.669922 0.02144,-0.03983 0.0249,-0.08731 0.04297,-0.128906 0.0434,-0.09308 0.07854,-0.19749 0.103515,-0.304688 0.01416,-0.06169 0.02721,-0.12056 0.03516,-0.183593 0.02001,-0.144005 0.02205,-0.286726 0.0098,-0.427735 -0.0047,-0.05246 -0.01464,-0.100691 -0.02344,-0.152343 -0.0201,-0.117318 -0.05021,-0.229314 -0.0918,-0.337891 -0.02117,-0.05719 -0.03975,-0.112112 -0.06641,-0.166016 -0.04827,-0.0948 -0.11061,-0.177917 -0.175782,-0.259765 -0.03204,-0.04093 -0.04978,-0.09412 -0.08594,-0.13086 -0.02601,-0.02643 -0.06342,-0.04035 -0.0918,-0.06445 -0.08406,-0.07533 -0.172768,-0.138308 -0.265625,-0.1875 -0.01534,-0.0078 -0.02927,-0.01619 -0.04492,-0.02344 -0.118257,-0.0564 -0.246131,-0.09834 -0.390625,-0.119141 -0.08008,-0.01479 -0.160229,-0.01418 -0.242187,-0.01758 -0.02205,-5.53e-4 -0.03973,-0.0039 -0.0625,-0.0039 z"
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.03125" /></g></svg>

After

Width:  |  Height:  |  Size: 9.2 KiB

179
bauh/resources/img/flathub.svg Executable file
View File

@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="47.919052"
height="48.110374"
viewBox="0 0 12.678582 12.729203"
version="1.1"
id="svg3871"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
sodipodi:docname="flathub.svg">
<defs
id="defs3865" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="0.35"
inkscape:cx="8.4698372"
inkscape:cy="118.04149"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
showborder="false"
inkscape:window-width="1366"
inkscape:window-height="703"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
fit-margin-bottom="-0.2"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
units="px" />
<metadata
id="metadata3868">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-12.122116,-167.33593)">
<g
transform="matrix(0.08875425,0,0,0.11490723,-57.513376,72.576168)"
id="g10674"
style="stroke-width:0.77710575">
<path
sodipodi:nodetypes="ccccccccccc"
inkscape:connector-curvature="0"
id="path10642"
d="m 856.01397,830.22631 -66.76365,43.44891 v 34.93973 l 33.38182,21.72315 33.37926,-21.72315 v -0.005 l 0.003,0.003 33.38183,21.72574 33.37925,-21.72315 v -34.93979 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:9.3252697;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<g
style="stroke-width:0.58728963"
transform="matrix(0.50221552,0,0,0.50636711,429.63205,432.90461)"
id="g10652">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect10644"
width="79.110413"
height="79.110413"
x="1289.9076"
y="279.42584"
rx="0"
ry="0"
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 915.46811,824.92983 -5e-5,68.99997 -66.46803,42.90055 5e-5,-68.99997 z"
id="path10648"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path10650"
d="m 783.00003,824.92983 5e-5,68.99997 66.46803,42.90055 -5e-5,-68.99997 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#000000;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)"
ry="0"
rx="0"
y="215.8064"
x="1226.2882"
height="79.110413"
width="79.110413"
id="rect10646"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<g
style="stroke-width:0.58728963"
transform="matrix(0.50221552,0,0,0.50636711,463.01368,454.62849)"
id="g10662">
<rect
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)"
ry="0"
rx="0"
y="279.42584"
x="1289.9076"
height="79.110413"
width="79.110413"
id="rect10654"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect10656"
width="79.110413"
height="79.110413"
x="1226.2882"
y="215.8064"
rx="0"
ry="0"
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)" />
<path
inkscape:connector-curvature="0"
id="path10658"
d="m 915.46811,824.92983 -5e-5,68.99997 -66.46803,42.90055 5e-5,-68.99997 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 783.00003,826.54445 5e-5,68.99997 66.46803,42.90055 -5e-5,-68.99997 z"
id="path10660"
inkscape:connector-curvature="0" />
</g>
<g
style="stroke-width:0.58728963"
id="g10672"
transform="matrix(0.50221552,0,0,0.50636711,396.25042,454.62849)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect10664"
width="79.110413"
height="79.110413"
x="1289.9076"
y="279.42584"
rx="0"
ry="0"
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)" />
<rect
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)"
ry="0"
rx="0"
y="215.8064"
x="1226.2882"
height="79.110413"
width="79.110413"
id="rect10666"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#204a87;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 915.46811,824.92983 -5e-5,68.99997 -66.46803,42.90055 5e-5,-68.99997 z"
id="path10668"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path10670"
d="m 783.00003,824.92983 5e-5,68.99997 66.46803,42.90055 -5e-5,-68.99997 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:#204a87;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

220
bauh/resources/img/history.svg Executable file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 40 KiB

120
bauh/resources/img/install.svg Executable file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
width="12"
height="12"
viewBox="0 0 12 12"
xml:space="preserve"
sodipodi:docname="install.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
id="metadata135"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs133" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="703"
id="namedview131"
showgrid="false"
showborder="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="1.9124627"
inkscape:cx="58.262905"
inkscape:cy="42.640588"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
inkscape:current-layer="Capa_1" />
<g
id="g98"
transform="scale(0.034381)"
style="fill:#00ff00">
<path
d="m 349.03,141.226 v 66.579 c 0,5.012 -4.061,9.079 -9.079,9.079 H 216.884 v 123.067 c 0,5.019 -4.067,9.079 -9.079,9.079 h -66.579 c -5.009,0 -9.079,-4.061 -9.079,-9.079 V 216.884 H 9.079 C 4.063,216.884 0,212.817 0,207.805 v -66.579 c 0,-5.013 4.063,-9.079 9.079,-9.079 H 132.147 V 9.079 C 132.147,4.061 136.216,0 141.226,0 h 66.579 c 5.012,0 9.079,4.061 9.079,9.079 v 123.068 h 123.067 c 5.019,0 9.079,4.066 9.079,9.079 z"
id="path96"
inkscape:connector-curvature="0"
style="fill:#00ff00" />
</g>
<g
id="g100"
transform="translate(0,-337.03)">
</g>
<g
id="g102"
transform="translate(0,-337.03)">
</g>
<g
id="g104"
transform="translate(0,-337.03)">
</g>
<g
id="g106"
transform="translate(0,-337.03)">
</g>
<g
id="g108"
transform="translate(0,-337.03)">
</g>
<g
id="g110"
transform="translate(0,-337.03)">
</g>
<g
id="g112"
transform="translate(0,-337.03)">
</g>
<g
id="g114"
transform="translate(0,-337.03)">
</g>
<g
id="g116"
transform="translate(0,-337.03)">
</g>
<g
id="g118"
transform="translate(0,-337.03)">
</g>
<g
id="g120"
transform="translate(0,-337.03)">
</g>
<g
id="g122"
transform="translate(0,-337.03)">
</g>
<g
id="g124"
transform="translate(0,-337.03)">
</g>
<g
id="g126"
transform="translate(0,-337.03)">
</g>
<g
id="g128"
transform="translate(0,-337.03)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
bauh/resources/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

477
bauh/resources/img/logo.svg Executable file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 74 KiB

115
bauh/resources/img/red_cross.svg Executable file
View File

@@ -0,0 +1,115 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 19.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Layer_1"
x="0px"
y="0px"
viewBox="0 0 12 12"
xml:space="preserve"
sodipodi:docname="cross_red.svg"
width="12"
height="12"
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
id="metadata1018"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs1016" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="703"
id="namedview1014"
showgrid="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
showborder="false"
inkscape:zoom="1.7430481"
inkscape:cx="39.29923"
inkscape:cy="73.435604"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
inkscape:current-layer="Layer_1" />
<path
id="XMLID_90_"
d="M 0.18530763,10.025545 1.9744745,11.814702 C 2.093058,11.933327 2.2540227,12 2.4217347,12 2.5894888,12 2.7504115,11.93333 2.8689948,11.814702 L 5.9999843,8.6837266 9.1309735,11.814702 C 9.2495992,11.933327 9.4105217,12 9.5782337,12 c 0.167754,0 0.3286768,-0.06667 0.4473023,-0.185298 l 1.789167,-1.789157 c 0.247034,-0.2470354 0.247034,-0.6475284 0,-0.8945604 L 8.6837558,6.0000066 11.814703,2.8689866 C 11.933371,2.7503616 12,2.5894836 12,2.4217286 c 0,-0.167795 -0.06663,-0.328675 -0.185297,-0.4473 L 10.025578,0.18526863 c -0.2469922,-0.247032 -0.6475697,-0.247032 -0.8945622,4.3e-5 L 6.0000263,3.3162466 2.8689948,0.18531163 c -0.2469925,-0.247035 -0.6475699,-0.247035 -0.8945624,0 L 0.18530763,1.9744286 C 0.0666399,2.0930536 1.054262e-5,2.2539336 1.054262e-5,2.4216866 c 0,0.167755 0.06662935738,0.328635 0.18529708738,0.44726 l 3.13094717,3.131018 -3.13094717,3.13102 c -0.24707684,0.247032 -0.24707684,0.647525 0,0.8945604 z"
inkscape:connector-curvature="0"
style="fill:#ff0000;stroke-width:0.0421704" />
<g
id="g983"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g985"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g987"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g989"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g991"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g993"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g995"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g997"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g999"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g1001"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g1003"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g1005"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g1007"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g1009"
transform="translate(2.5e-4,-272.56001)">
</g>
<g
id="g1011"
transform="translate(2.5e-4,-272.56001)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.6 KiB

146
bauh/resources/img/refresh.svg Executable file
View File

@@ -0,0 +1,146 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
width="48"
height="48"
viewBox="0 0 47.999999 47.999999"
xml:space="preserve"
sodipodi:docname="refresh_2.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"><metadata
id="metadata45"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs43"><linearGradient
inkscape:collect="always"
id="linearGradient540"><stop
style="stop-color:#0088aa;stop-opacity:1;"
offset="0"
id="stop536" /><stop
style="stop-color:#0088aa;stop-opacity:0;"
offset="1"
id="stop538" /></linearGradient><linearGradient
inkscape:collect="always"
xlink:href="#linearGradient540"
id="linearGradient542"
x1="-130.74721"
y1="-214.80638"
x2="-556.49438"
y2="110.01643"
gradientUnits="userSpaceOnUse" /></defs><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="703"
id="namedview41"
showgrid="false"
showborder="false"
inkscape:zoom="1.9374833"
inkscape:cx="165.58246"
inkscape:cy="-10.288149"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
inkscape:current-layer="Capa_1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<g
id="g8"
transform="matrix(0.10547014,0,0,0.0985161,-1.6940777,0)"
style="fill:url(#linearGradient542);fill-opacity:1">
<g
id="g6"
style="fill:url(#linearGradient542);fill-opacity:1">
<path
d="m 55.323,203.641 c 15.664,0 29.813,-9.405 35.872,-23.854 25.017,-59.604 83.842,-101.61 152.42,-101.61 37.797,0 72.449,12.955 100.23,34.442 l -21.775,3.371 c -7.438,1.153 -13.224,7.054 -14.232,14.512 -1.01,7.454 3.008,14.686 9.867,17.768 l 119.746,53.872 c 5.249,2.357 11.33,1.904 16.168,-1.205 4.83,-3.114 7.764,-8.458 7.796,-14.208 l 0.621,-131.943 c 0.042,-7.506 -4.851,-14.144 -12.024,-16.332 -7.185,-2.188 -14.947,0.589 -19.104,6.837 L 414.403,70.096 C 370.398,26.778 310.1,0 243.615,0 142.806,0 56.133,61.562 19.167,149.06 c -5.134,12.128 -3.84,26.015 3.429,36.987 7.269,10.976 19.556,17.594 32.727,17.594 z"
id="path2"
inkscape:connector-curvature="0"
style="fill:url(#linearGradient542);fill-opacity:1" />
<path
d="m 464.635,301.184 c -7.27,-10.977 -19.558,-17.594 -32.728,-17.594 -15.664,0 -29.813,9.405 -35.872,23.854 -25.018,59.604 -83.843,101.61 -152.42,101.61 -37.798,0 -72.45,-12.955 -100.232,-34.442 l 21.776,-3.369 c 7.437,-1.153 13.223,-7.055 14.233,-14.514 1.009,-7.453 -3.008,-14.686 -9.867,-17.768 L 49.779,285.089 c -5.25,-2.356 -11.33,-1.905 -16.169,1.205 -4.829,3.114 -7.764,8.458 -7.795,14.207 l -0.622,131.943 c -0.042,7.506 4.85,14.144 12.024,16.332 7.185,2.188 14.948,-0.59 19.104,-6.839 l 16.505,-24.805 c 44.004,43.32 104.303,70.098 170.788,70.098 100.811,0 187.481,-61.561 224.446,-149.059 5.137,-12.128 3.843,-26.014 -3.425,-36.987 z"
id="path4"
inkscape:connector-curvature="0"
style="fill:url(#linearGradient542);fill-opacity:1" />
</g>
</g>
<g
id="g10"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g12"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g14"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g16"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g18"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g20"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g22"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g24"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g26"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g28"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g30"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g32"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g34"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g36"
transform="translate(-16.062155,-439.23)">
</g>
<g
id="g38"
transform="translate(-16.062155,-439.23)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.8 KiB

120
bauh/resources/img/search.svg Executable file
View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
id="Capa_1"
x="0px"
y="0px"
viewBox="0 0 12 12"
xml:space="preserve"
sodipodi:docname="search.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
width="12"
height="12"><metadata
id="metadata126"><rdf:RDF><cc:Work
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /><dc:title></dc:title></cc:Work></rdf:RDF></metadata><defs
id="defs124" /><sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="739"
id="namedview122"
showgrid="false"
showborder="false"
inkscape:zoom="0.53133476"
inkscape:cx="834.65593"
inkscape:cy="-101.88082"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
inkscape:current-layer="Capa_1"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<g
id="Search"
transform="scale(0.04794017)"
style="fill:#999999">
<path
style="clip-rule:evenodd;fill:#999999;fill-rule:evenodd"
d="m 244.186,214.604 -54.379,-54.378 c -0.289,-0.289 -0.628,-0.491 -0.93,-0.76 10.7,-16.231 16.945,-35.66 16.945,-56.554 C 205.822,46.075 159.747,0 102.911,0 46.075,0 0,46.075 0,102.911 c 0,56.835 46.074,102.911 102.91,102.911 20.895,0 40.323,-6.245 56.554,-16.945 0.269,0.301 0.47,0.64 0.759,0.929 l 54.38,54.38 c 8.169,8.168 21.413,8.168 29.583,0 8.168,-8.169 8.168,-21.413 0,-29.582 z M 102.911,170.146 c -37.134,0 -67.236,-30.102 -67.236,-67.235 0,-37.134 30.103,-67.236 67.236,-67.236 37.132,0 67.235,30.103 67.235,67.236 0,37.133 -30.103,67.235 -67.235,67.235 z"
id="path88"
inkscape:connector-curvature="0" />
</g>
<g
id="g91"
transform="translate(0,-238.312)">
</g>
<g
id="g93"
transform="translate(0,-238.312)">
</g>
<g
id="g95"
transform="translate(0,-238.312)">
</g>
<g
id="g97"
transform="translate(0,-238.312)">
</g>
<g
id="g99"
transform="translate(0,-238.312)">
</g>
<g
id="g101"
transform="translate(0,-238.312)">
</g>
<g
id="g103"
transform="translate(0,-238.312)">
</g>
<g
id="g105"
transform="translate(0,-238.312)">
</g>
<g
id="g107"
transform="translate(0,-238.312)">
</g>
<g
id="g109"
transform="translate(0,-238.312)">
</g>
<g
id="g111"
transform="translate(0,-238.312)">
</g>
<g
id="g113"
transform="translate(0,-238.312)">
</g>
<g
id="g115"
transform="translate(0,-238.312)">
</g>
<g
id="g117"
transform="translate(0,-238.312)">
</g>
<g
id="g119"
transform="translate(0,-238.312)">
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

@@ -0,0 +1,96 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
height="24"
viewBox="-40 0 18 18.000056"
width="24"
version="1.1"
id="svg72"
sodipodi:docname="uninstall.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata78">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs76" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="703"
id="namedview74"
showgrid="false"
showborder="false"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:zoom="5.7611241"
inkscape:cx="40.648821"
inkscape:cy="17.781204"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
inkscape:current-layer="svg72"
units="px" />
<g
id="g5907"
transform="matrix(0.50000001,0,0,0.49999997,-20.000045,0)">
<g
transform="matrix(0.10380557,0,0,0.08430914,-35.847729,0)"
id="g84">
<path
d="m 192.40098,154.70443 c -5.52344,0 -10,4.47657 -10,10 v 189 c 0,5.51954 4.47656,10 10,10 5.52343,0 10,-4.48046 10,-10 v -189 c 0,-5.52343 -4.47657,-10 -10,-10 z m 0,0"
id="path64"
inkscape:connector-curvature="0" />
<path
d="m 74.400978,154.70443 c -5.52344,0 -10,4.47657 -10,10 v 189 c 0,5.51954 4.47656,10 10,10 5.52344,0 10,-4.48046 10,-10 v -189 c 0,-5.52343 -4.47656,-10 -10,-10 z m 0,0"
id="path66"
inkscape:connector-curvature="0" />
<path
d="m -11.599024,127.1224 v 246.37891 c 0,14.5625 5.3398433,28.23828 14.6679683,38.05078 9.2851557,9.83984 22.2070317,15.42578 35.7304687,15.44922 H 228.00254 c 13.52734,-0.0234 26.44922,-5.60938 35.73047,-15.44922 9.32812,-9.8125 14.66797,-23.48828 14.66797,-38.05078 V 127.1224 C 296.94395,122.20053 308.95957,104.28647 306.4791,85.259126 303.99473,66.235689 287.7877,52.00522 268.6002,52.001314 h -51.19922 v -12.5 c 0.0586,-10.511719 -4.09766,-20.605469 -11.53907,-28.03125 -7.4414,-7.4218751 -17.55078,-11.55468758 -28.0625,-11.46875008 H 89.002538 C 78.490818,-0.08462358 68.381448,4.0481889 60.940038,11.470064 53.498632,18.895845 49.342382,28.989595 49.400976,39.501314 v 12.5 H -1.7982427 c -19.1875003,0.0039 -35.3945313,14.234375 -37.8789073,33.257812 -2.480468,19.027344 9.535157,36.941404 28.078126,41.863274 z M 228.00254,407.00131 H 38.799413 c -17.097656,0 -30.3984367,-14.6875 -30.3984367,-33.5 v -245.5 H 258.40098 v 245.5 c 0,18.8125 -13.30078,33.5 -30.39844,33.5 z M 69.400978,39.501314 c -0.0664,-5.207031 1.98047,-10.21875 5.67578,-13.894531 3.69141,-3.675781 8.71484,-5.695313 13.92578,-5.605469 h 88.796872 c 5.21094,-0.08984 10.23438,1.929688 13.92579,5.605469 3.69531,3.671875 5.74218,8.6875 5.67578,13.894531 v 12.5 H 69.400978 Z m -71.1992207,32.5 H 268.6002 c 9.9414,0 18,8.058594 18,18 0,9.941406 -8.0586,17.999996 -18,17.999996 H -1.7982427 c -9.9414073,0 -18.0000003,-8.05859 -18.0000003,-17.999996 0,-9.941406 8.058593,-18 18.0000003,-18 z m 0,0"
id="path68"
inkscape:connector-curvature="0" />
<path
d="m 133.40098,154.70443 c -5.52344,0 -10,4.47657 -10,10 v 189 c 0,5.51954 4.47656,10 10,10 5.52343,0 10,-4.48046 10,-10 v -189 c 0,-5.52343 -4.47657,-10 -10,-10 z m 0,0"
id="path70"
inkscape:connector-curvature="0" />
</g>
<path
inkscape:connector-curvature="0"
id="path3791"
d="m -32.347465,34.215765 c -1.136967,-0.263564 -1.85228,-0.770702 -2.285878,-1.620623 -0.244025,-0.478328 -0.252697,-0.85963 -0.252697,-11.110455 V 10.869559 h 12.888149 12.8881497 l -0.0028,10.577396 c -0.0027,10.017853 -0.01562,10.604942 -0.243951,11.098129 -0.295923,0.639162 -0.8228787,1.147628 -1.5106647,1.457656 -0.486498,0.219295 -1.201157,0.235997 -10.870308,0.254055 -5.692267,0.01062 -10.46674,-0.0078 -10.609942,-0.04103 z m 4.983091,-3.760996 0.289334,-0.202658 v -8.369266 c 0,-8.31107 -0.0019,-8.370969 -0.270765,-8.614305 -0.358608,-0.324535 -1.182796,-0.324535 -1.541404,0 -0.268882,0.243336 -0.270765,0.303235 -0.270765,8.614305 v 8.369266 l 0.289334,0.202658 c 0.159133,0.11146 0.497593,0.202656 0.752133,0.202656 0.25454,0 0.593,-0.0912 0.752133,-0.202656 z m 6.118617,0 0.289333,-0.202658 v -8.369266 c 0,-8.31107 -0.0019,-8.370969 -0.270764,-8.614305 -0.358608,-0.324535 -1.182797,-0.324535 -1.541404,0 -0.268883,0.243336 -0.270765,0.303235 -0.270765,8.614305 v 8.369266 l 0.289333,0.202658 c 0.159134,0.11146 0.497593,0.202656 0.752133,0.202656 0.25454,0 0.593001,-0.0912 0.752134,-0.202656 z m 6.118616,0 0.289334,-0.202658 v -8.369266 c 0,-8.31107 -0.0019,-8.370969 -0.270765,-8.614305 -0.358608,-0.324535 -1.182796,-0.324535 -1.541404,0 -0.268882,0.243336 -0.270765,0.303235 -0.270765,8.614305 v 8.369266 l 0.289334,0.202658 c 0.159132,0.11146 0.497593,0.202656 0.752133,0.202656 0.254539,0 0.593,-0.0912 0.752133,-0.202656 z"
style="fill:#b3b3b3;stroke-width:0.13018332" />
<path
inkscape:connector-curvature="0"
id="path3793"
d="m -36.877845,8.8644087 c -0.540224,-0.2384603 -0.844092,-0.6148864 -0.931583,-1.1540278 -0.06403,-0.3945707 -0.01083,-0.5268359 0.386094,-0.9599757 l 0.460354,-0.5023539 14.753284,-0.034506 c 10.894353,-0.025481 14.8480787,0.00467 15.1156987,0.115259 0.791461,0.3270622 1.121594,1.3085049 0.671312,1.9957216 -0.490508,0.7486088 0.06973,0.7229272 -15.6494407,0.7173712 -11.671083,-0.00413 -14.489515,-0.037913 -14.805719,-0.1774884 z"
style="fill:#b3b3b3;stroke-width:0.13018332" />
<path
inkscape:connector-curvature="0"
id="path3795"
d="m -28.586127,3.581076 c 0.04302,-0.5947544 0.131512,-0.8607897 0.373651,-1.1233362 0.63458,-0.6880588 0.750066,-0.7010134 6.249312,-0.7010134 4.435032,0 5.151954,0.027404 5.540514,0.2117885 0.718185,0.3408012 0.959962,0.7509159 0.959962,1.6283345 V 4.360393 h -6.589905 -6.589904 z"
style="fill:#b3b3b3;stroke-width:0.13018332" />
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@@ -0,0 +1,117 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
version="1.1"
viewBox="0 0 48 48"
enable-background="new 0 0 26 26"
id="svg8"
sodipodi:docname="update_green_2.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)"
width="48"
height="48">
<metadata
id="metadata14">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs12">
<linearGradient
inkscape:collect="always"
id="linearGradient309">
<stop
style="stop-color:#55d400;stop-opacity:1;"
offset="0"
id="stop305" />
<stop
style="stop-color:#55d400;stop-opacity:0;"
offset="1"
id="stop307" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient309"
id="linearGradient311"
x1="-12.490223"
y1="5.5246015"
x2="-20.59199"
y2="18.300463"
gradientUnits="userSpaceOnUse" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient309"
id="linearGradient313"
gradientUnits="userSpaceOnUse"
x1="-12.490223"
y1="5.5246015"
x2="-20.59199"
y2="18.300463" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient309"
id="linearGradient315"
gradientUnits="userSpaceOnUse"
x1="-12.490223"
y1="5.5246015"
x2="-20.59199"
y2="18.300463" />
</defs>
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1366"
inkscape:window-height="703"
id="namedview10"
showgrid="false"
showborder="false"
inkscape:zoom="3.2091769"
inkscape:cx="11.132699"
inkscape:cy="36.020085"
inkscape:window-x="0"
inkscape:window-y="260"
inkscape:window-maximized="1"
inkscape:current-layer="svg8"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0" />
<g
id="g6"
transform="matrix(1.8461538,0,0,2,0,-2)"
style="fill:url(#linearGradient311);fill-opacity:1">
<path
d="m 25,17 h -2 c -0.6,0 -1,0.4 -1,1 v 2.5 C 22,20.8 21.8,21 21.5,21 H 4.5 C 4.2,21 4,20.8 4,20.5 V 18 C 4,17.4 3.6,17 3,17 H 1 c -0.6,0 -1,0.4 -1,1 v 6 c 0,0.6 0.4,1 1,1 h 24 c 0.6,0 1,-0.4 1,-1 v -6 c 0,-0.6 -0.4,-1 -1,-1 z"
id="path2"
inkscape:connector-curvature="0"
style="fill:url(#linearGradient313);fill-opacity:1" />
<path
d="m 12.3,16.7 c 0.2,0.2 0.5,0.3 0.7,0.3 0.2,0 0.5,-0.1 0.7,-0.3 l 6,-6 C 19.9,10.5 20,10.3 20,10 20,9.7 19.9,9.5 19.7,9.3 L 18.3,7.9 C 18.1,7.7 17.9,7.6 17.6,7.6 c -0.3,0 -0.5,0.1 -0.7,0.3 l -1,1 C 15.6,9.2 15,9 15,8.5 V 2 C 15,1.4 14.6,1 14,1 H 12 C 11.4,1 11,1.4 11,2 V 8.6 C 11,9 10.5,9.3 10.1,9 L 9.1,8 C 8.9,7.8 8.7,7.7 8.4,7.7 8.1,7.7 7.9,7.8 7.7,8 L 6.3,9.4 C 6.1,9.6 6,9.8 6,10.1 c 0,0.3 0.1,0.5 0.3,0.7 z"
id="path4"
inkscape:connector-curvature="0"
style="fill:url(#linearGradient315);fill-opacity:1" />
</g>
<g
transform="matrix(3.7795276,0,0,3.7795276,668.39708,-480.81644)"
id="layer1"
inkscape:label="Layer 1" />
</svg>

After

Width:  |  Height:  |  Size: 3.9 KiB

View File

@@ -0,0 +1,179 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="43.449291"
height="41.737198"
viewBox="0 0 11.495958 11.042967"
version="1.1"
id="svg3871"
inkscape:version="0.92.4 5da689c313, 2019-01-14"
sodipodi:docname="update_logo.svg">
<defs
id="defs3865" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="1.4"
inkscape:cx="301.87613"
inkscape:cy="-17.251705"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
showborder="false"
inkscape:window-width="1360"
inkscape:window-height="707"
inkscape:window-x="0"
inkscape:window-y="0"
inkscape:window-maximized="1"
fit-margin-bottom="-0.2"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
units="px" />
<metadata
id="metadata3868">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-12.111406,-169.00616)">
<g
transform="matrix(0.08047549,0,0,0.0997485,-51.028672,86.747244)"
id="g10674"
style="fill:#ff2a2a;stroke:#800000;stroke-width:0.77710575">
<path
sodipodi:nodetypes="ccccccccccc"
inkscape:connector-curvature="0"
id="path10642"
d="m 856.01397,830.22631 -66.76365,43.44891 v 34.93973 l 33.38182,21.72315 33.37926,-21.72315 v -0.005 l 0.003,0.003 33.38183,21.72574 33.37925,-21.72315 v -34.93979 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:9.3252697;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<g
style="fill:#ff2a2a;stroke:#800000;stroke-width:0.58728963"
transform="matrix(0.50221552,0,0,0.50636711,429.63205,432.90461)"
id="g10652">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect10644"
width="79.110413"
height="79.110413"
x="1289.9076"
y="279.42584"
rx="0"
ry="0"
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 915.46811,824.92983 -5e-5,68.99997 -66.46803,42.90055 5e-5,-68.99997 z"
id="path10648"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path10650"
d="m 783.00003,824.92983 5e-5,68.99997 66.46803,42.90055 -5e-5,-68.99997 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)"
ry="0"
rx="0"
y="215.8064"
x="1226.2882"
height="79.110413"
width="79.110413"
id="rect10646"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
<g
style="fill:#ff2a2a;stroke:#800000;stroke-width:0.58728963"
transform="matrix(0.50221552,0,0,0.50636711,463.01368,454.62849)"
id="g10662">
<rect
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)"
ry="0"
rx="0"
y="279.42584"
x="1289.9076"
height="79.110413"
width="79.110413"
id="rect10654"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect10656"
width="79.110413"
height="79.110413"
x="1226.2882"
y="215.8064"
rx="0"
ry="0"
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)" />
<path
inkscape:connector-curvature="0"
id="path10658"
d="m 915.46811,824.92983 -5e-5,68.99997 -66.46803,42.90055 5e-5,-68.99997 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 783.00003,826.54445 5e-5,68.99997 66.46803,42.90055 -5e-5,-68.99997 z"
id="path10660"
inkscape:connector-curvature="0" />
</g>
<g
style="fill:#ff2a2a;stroke:#800000;stroke-width:0.58728963"
id="g10672"
transform="matrix(0.50221552,0,0,0.50636711,396.25042,454.62849)">
<rect
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
id="rect10664"
width="79.110413"
height="79.110413"
x="1289.9076"
y="279.42584"
rx="0"
ry="0"
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)" />
<rect
transform="matrix(0.84019328,0.54228706,-0.84019328,0.54228706,0,0)"
ry="0"
rx="0"
y="215.8064"
x="1226.2882"
height="79.110413"
width="79.110413"
id="rect10666"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:2.4608953;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
<path
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
d="m 915.46811,824.92983 -5e-5,68.99997 -66.46803,42.90055 5e-5,-68.99997 z"
id="path10668"
inkscape:connector-curvature="0" />
<path
inkscape:connector-curvature="0"
id="path10670"
d="m 783.00003,824.92983 5e-5,68.99997 66.46803,42.90055 -5e-5,-68.99997 z"
style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ff2a2a;fill-opacity:1;fill-rule:nonzero;stroke:#800000;stroke-width:2.34915853;stroke-linecap:square;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" />
</g>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 14 KiB

86
bauh/resources/locale/en Normal file
View File

@@ -0,0 +1,86 @@
manage_window.title=Applications Manager
manage_window.columns.latest_version=Latest Version
manage_window.columns.update=Upgrade ?
manage_window.apps_table.row.actions.info=Information
manage_window.apps_table.row.actions.history=History
manage_window.apps_table.row.actions.uninstall.popup.title=Uninstall
manage_window.apps_table.row.actions.uninstall.popup.body=Remove {} from your computer ?
manage_window.apps_table.row.actions.install.popup.title=Installation
manage_window.apps_table.row.actions.install.popup.body=Install {} on your computer ?
manage_window.apps_table.row.actions.downgrade=Downgrade
manage_window.apps_table.row.actions.downgrade.popup.body=Do you really want to downgrade {} ?
manage_window.apps_table.row.actions.install=Install
manage_window.apps_table.row.actions.refresh=Refresh
manage_window.apps_table.upgrade_toggle.tooltip=There is an update for this application. Click here to check or uncheck the update
manage_window.checkbox.only_apps=Apps
window_manage.input_search.placeholder=Search
window_manage.input_search.tooltip=Type and press ENTER to search for applications
manage_window.label.updates=Updates
manage_window.status.refreshing=Refreshing
manage_window.status.upgrading=Upgrading
manage_window.status.uninstalling=Uninstalling
manage_window.status.downgrading=Downgrading
manage_window.status.info=Retrieving information
manage_window.status.history=Retrieving history
manage_window.status.searching=Searching
manage_window.status.installing=Installing
manage_window.status.refreshing_app=Refreshing
manage_window.bt.refresh.tooltip=Reload the data about installed applications
manage_window.bt.upgrade.tooltip=Upgrade all checked applications
manage_window.checkbox.show_details=Show details
popup.root.title=Requires root privileges
popup.root.password=Password
popup.root.bad_password.title=Error
popup.root.bad_password.body=Wrong password
popup.downgrade.impossible.title=Error
popup.downgrade.impossible.body=Impossible to downgrade: the app is in its first version
popup.history.title=History
popup.history.selected.tooltip=Current version
popup.button.yes=Yes
popup.button.no=No
popup.button.cancel=Cancel
tray.action.manage=Manage applications
tray.action.exit=Exit
tray.action.about=About
tray.action.refreshing=Refreshing
notification.new_updates=Updates
notification.update_selected.success=app(s) updated successfully
notification.update_selected.failed=Update failed
notification.install.failed=installation failed
notification.uninstall.failed=uninstallation failed
notification.downgrade.failed=Failed to downgrade
about.info.desc=Non-official Flatpak / Snap application management graphical interface
about.info.link=More information at
about.info.license=Free license
about.info.rate=If this tool is useful for you, give it a star on Github to keep it up
yes=yes
no=no
version.updated=updated
version.outdated=outdated
name=name
version=version
latest_version=latest version
description=description
type=type
installed=installed
uninstalled=uninstalled
not_installed=not installed
downgraded=downgraded
others=others
internet.required=Internet connection is required
license.unset=unset
updates=updates
version.installed=installed version
version.latest=latest version
version.installed_outdated=the installed version is outdated
version.unknown=not informed
app.name=application name
warning=warning
install=install
uninstall=uninstall
bt.app_upgrade=Upgrade
bt.app_not_upgrade=Don't upgrade
manage_window.upgrade_all.popup.title=Upgrade
manage_window.upgrade_all.popup.body=Upgrade all selected applications ?
manage_window.bt_about.tooltip=About
warning.no_managers=No hay extensiones instaladas. No será posible instalar y administrar aplicaciones.

88
bauh/resources/locale/es Normal file
View File

@@ -0,0 +1,88 @@
manage_window.title=Administrador de Aplicativos
manage_window.columns.latest_version=Ultima Versión
manage_window.columns.update=Actualizar ?
manage_window.columns.installed=Instalado
manage_window.apps_table.row.actions.info=Información
manage_window.apps_table.row.actions.history=Historia
manage_window.apps_table.row.actions.uninstall=Desinstalar
manage_window.apps_table.row.actions.uninstall.popup.title=Desinstalación
manage_window.apps_table.row.actions.uninstall.popup.body=¿Eliminar {} de tu ordenador?
manage_window.apps_table.row.actions.install.popup.title=Instalación
manage_window.apps_table.row.actions.install.popup.body=¿Instalar {} en tu ordenador?
manage_window.apps_table.row.actions.downgrade=Revertir versión
manage_window.apps_table.row.actions.downgrade.popup.body=¿Realmente quieres revertir la versión actual de {}?
manage_window.apps_table.row.actions.install=Instalar
manage_window.apps_table.row.actions.refresh=Actualizar
manage_window.apps_table.upgrade_toggle.tooltip=Haces clic aqui para marcar o desmarcar la actualización
manage_window.checkbox.only_apps=Aplicativos
window_manage.input_search.placeholder=Buscar
window_manage.input_search.tooltip=Escriba y oprima ENTER para buscar aplicativos
manage_window.label.updates=Actualizaciones
manage_window.status.refreshing=Recargando
manage_window.status.upgrading=Actualizando
manage_window.status.uninstalling=Desinstalando
manage_window.status.downgrading=Revirtiendo
manage_window.status.info=Obteniendo información
manage_window.status.history=Obteniendo la historia
manage_window.status.searching=Buscando
manage_window.status.installing=Instalando
manage_window.status.refreshing_app=Actualizando
manage_window.bt.refresh.tooltip=Recarga los datos sobre los aplicativos instalados
manage_window.bt.upgrade.tooltip=Actualiza todos los aplicativos marcados
manage_window.checkbox.show_details=Mostrar detalles
popup.root.title=Requiere privilegios de root
popup.root.password=Contraseña
popup.root.bad_password.title=Error
popup.root.bad_password.body=Contraseña incorrecta
popup.downgrade.impossible.title=Error
popup.downgrade.impossible.body=Imposible revertir la versión: el aplicativo está en su primera versión
popup.history.title=Historia
popup.history.selected.tooltip=Versión actual
popup.button.yes=Sí
popup.button.no=No
popup.button.cancel=Cancelar
tray.action.manage=Administrar aplicativos
tray.action.exit=Salir
tray.action.about=Sobre
tray.action.refreshing=Recargando
notification.new_updates=Actualizaciones
notification.update_selected.success=aplicativo(s) actualizado(s) con éxito
notification.update_selected.failed=La actualización falló
notification.install.failed=la instalación ha fallado
notification.uninstall.failed=la desinstalación ha fallado
notification.downgrade.failed=Error al revertir la versión
about.info.desc=Interfaz grafica no oficial para administración de aplicativos Flatpak / Snap
about.info.link=Mas informaciones en
about.info.license=Licencia gratuita
about.info.rate=Si esta herramienta es útil para ti, dale una estrella en Github para mantenerla
yes=sí
no=no
version.updated=actualizada
version.outdated=desactualizada
name=nombre
version=versión
latest_version=ultima versión
description=descripción
type=tipo
installed=instalado
uninstalled=desinstalado
not_installed=no instalado
downgraded=versión revertida
others=otros
internet.required=Se requiere conexión a internet
license.unset=No está definida
updates=actualizaciones
version.installed=versión instalada
version.latest=versión más reciente
version.installed_outdated=la versión instalada está desactualizada
version.unknown=versión no informada
app.name=nombre del aplicativo
warning=aviso
install=instalar
uninstall=desinstalar
bt.app_upgrade=Actualizar
bt.app_not_upgrade=No actualizar
manage_window.upgrade_all.popup.title=Actualizar
manage_window.upgrade_all.popup.body=¿Actualizar todos los aplicativos seleccionados?
manage_window.bt_about.tooltip=Sobre
warning.no_managers=Não há nenhuma extensão instalada. Não será possível instalar e gerenciar aplicações.

88
bauh/resources/locale/pt Normal file
View File

@@ -0,0 +1,88 @@
manage_window.title=Gerenciador de Aplicativos
manage_window.columns.latest_version=Última Versão
manage_window.columns.update=Atualizar ?
manage_window.columns.installed=Instalado
manage_window.apps_table.row.actions.info=Informação
manage_window.apps_table.row.actions.history=Histórico
manage_window.apps_table.row.actions.uninstall=Desinstalar
manage_window.apps_table.row.actions.uninstall.popup.title=Desinstalação
manage_window.apps_table.row.actions.uninstall.popup.body=Remover {} do seu computador ?
manage_window.apps_table.row.actions.install.popup.title=Instalação
manage_window.apps_table.row.actions.install.popup.body=Instalar {} no seu computador ?
manage_window.apps_table.row.actions.downgrade=Reverter versão
manage_window.apps_table.row.actions.downgrade.popup.body=Você realmente quer reverter a versão atual de {} ?
manage_window.apps_table.row.actions.install=Instalar
manage_window.apps_table.row.actions.refresh=Atualizar
manage_window.apps_table.upgrade_toggle.tooltip=Existe um atualização para esse aplicativo. Clique aqui para marcar ou desmarcar a atualização
manage_window.checkbox.only_apps=Aplicativos
window_manage.input_search.placeholder=Buscar
window_manage.input_search.tooltip=Digite e pressione ENTER para buscar aplicativos
manage_window.label.updates=Atualizações
manage_window.status.refreshing=Recarregando
manage_window.status.upgrading=Atualizando
manage_window.status.uninstalling=Desinstalando
manage_window.status.downgrading=Revertendo
manage_window.status.info=Obtendo informação
manage_window.status.history=Obtendo histórico
manage_window.status.searching=Buscando
manage_window.status.installing=Instalando
manage_window.status.refreshing_app=Atualizando
manage_window.bt.refresh.tooltip=Recarrega os dados sobre os aplicativos instalados
manage_window.bt.upgrade.tooltip=Atualiza todos os aplicativos marcados
manage_window.checkbox.show_details=Mostrar detalhes
popup.root.title=Requer privilégios de root
popup.root.password=Senha
popup.root.bad_password.title=Erro
popup.root.bad_password.body=Senha incorreta
popup.downgrade.impossible.title=Erro
popup.downgrade.impossible.body=Impossível reverter a versão: o aplicativo está na sua primeira versão
popup.history.title=Histórico
popup.history.selected.tooltip=Versão atual
popup.button.yes=Sim
popup.button.no=Não
popup.button.cancel=Cancelar
tray.action.manage=Gerenciar aplicativos
tray.action.exit=Sair
tray.action.about=Sobre
tray.action.refreshing=Recarregando
notification.new_updates=Atualizações
notification.update_selected.success=aplicativo(s) atualizado(s) com sucesso
notification.update_selected.failed=Erro ao atualizar
notification.install.failed=instalação falhou
notification.uninstall.failed=desinstalação falhou
notification.downgrade.failed=Erro ao reverter versão
about.info.desc=Interface gráfica não oficial para gerenciamento de aplicativos Flatpak / Snap
about.info.link=Mais informações em
about.info.license=Licença gratuita
about.info.rate=Se essa ferramenta é útil para você, dê uma estrela no Github para mantê-la de pé
yes=sim
no=não
version.updated=atualizada
version.outdated=desatualizada
name=nome
version=versão
latest_version=última versão
description=descrição
type=tipo
installed=instalado
uninstalled=desinstalado
not_installed=não instalado
downgraded=versão revertida
others=outros
internet.required=É necessário estar conectado a Internet
license.unset=Não definida
updates=atualizações
version.installed=versão instalada
version.latest=versão mais recente
version.installed_outdated=a versão instalada está desatualizada
version.unknown=versão não informada
app.name=nome do aplicativo
warning=aviso
install=instalar
uninstall=desinstalar
bt.app_upgrade=Atualizar
bt.app_not_upgrade=Não atualizar
manage_window.upgrade_all.popup.title=Atualizar
manage_window.upgrade_all.popup.body=Atualizar todos os aplicativos selecionados ?
manage_window.bt_about.tooltip=Sobre
warning.no_managers=Não há nenhuma extensão instalada. Não será possível instalar e gerenciar aplicações.

0
bauh/util/__init__.py Normal file
View File

23
bauh/util/memory.py Normal file
View File

@@ -0,0 +1,23 @@
import time
from threading import Thread
from typing import List
from bauh_api.util.cache import Cache
class CacheCleaner(Thread):
def __init__(self, caches: List[Cache], check_interval: int = 15):
super(CacheCleaner, self).__init__(daemon=True)
self.caches = [c for c in caches if c.is_enabled()]
self.check_interval = check_interval
def run(self):
if self.caches:
while True:
for cache in self.caches:
cache.clean_expired()
time.sleep(self.check_interval)

53
bauh/util/util.py Normal file
View File

@@ -0,0 +1,53 @@
import glob
import locale
import re
from bauh_api.util import system
from bauh_api.util.resource import get_path
from bauh import ROOT_DIR, __app_name__
from bauh.core import resource
HTML_RE = re.compile(r'<[^>]+>')
def get_locale_keys(key: str = None, locale_dir: str = resource.get_path('locale')):
locale_path = None
if key is None:
current_locale = locale.getdefaultlocale()
else:
current_locale = [key.strip().lower()]
if current_locale:
current_locale = current_locale[0]
for locale_file in glob.glob(locale_dir + '/*'):
name = locale_file.split('/')[-1]
if current_locale == name or current_locale.startswith(name + '_'):
locale_path = locale_file
break
if not locale_path:
locale_path = resource.get_path('locale/en')
with open(locale_path, 'r') as f:
locale_keys = f.readlines()
locale_obj = {}
for line in locale_keys:
if line:
keyval = line.strip().split('=')
locale_obj[keyval[0].strip()] = keyval[1].strip()
return locale_obj
def strip_html(string: str):
return HTML_RE.sub('', string)
def notify_user(msg: str, icon_path: str = get_path('img/logo.svg', ROOT_DIR)):
system.notify_user(msg=msg, app_name=__app_name__, icon_path=icon_path)

0
bauh/view/__init__.py Executable file
View File

0
bauh/view/qt/__init__.py Executable file
View File

71
bauh/view/qt/about.py Normal file
View File

@@ -0,0 +1,71 @@
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QPixmap
from PyQt5.QtWidgets import QVBoxLayout, QDialog, QLabel
from bauh import __version__, __app_name__
from bauh.core import resource
PROJECT_URL = 'https://github.com/vinifmor/fpakman'
LICENSE_URL = 'https://raw.githubusercontent.com/vinifmor/fpakman/master/LICENSE'
class AboutDialog(QDialog):
def __init__(self, locale_keys: dict):
super(AboutDialog, self).__init__()
self.setWindowTitle(locale_keys['tray.action.about'])
layout = QVBoxLayout()
self.setLayout(layout)
label_logo = QLabel(self)
label_logo.setPixmap(QPixmap(resource.get_path('img/logo.svg')))
label_logo.setAlignment(Qt.AlignCenter)
layout.addWidget(label_logo)
label_name = QLabel('{} ( {} {} )'.format(__app_name__, locale_keys['flatpak.info.version'].lower(), __version__))
label_name.setStyleSheet('font-weight: bold;')
label_name.setAlignment(Qt.AlignCenter)
layout.addWidget(label_name)
layout.addWidget(QLabel(''))
line_desc = QLabel(self)
line_desc.setStyleSheet('font-size: 10px; font-weight: bold;')
line_desc.setText(locale_keys['about.info.desc'])
line_desc.setAlignment(Qt.AlignCenter)
line_desc.setMinimumWidth(400)
layout.addWidget(line_desc)
layout.addWidget(QLabel(''))
label_more_info = QLabel()
label_more_info.setStyleSheet('font-size: 9px;')
label_more_info.setText(locale_keys['about.info.link'] + ": <a href='{url}'>{url}</a>".format(url=PROJECT_URL))
label_more_info.setOpenExternalLinks(True)
label_more_info.setAlignment(Qt.AlignCenter)
layout.addWidget(label_more_info)
label_license = QLabel()
label_license.setStyleSheet('font-size: 9px;')
label_license.setText("<a href='{}'>{}</a>".format(LICENSE_URL, locale_keys['about.info.license']))
label_license.setOpenExternalLinks(True)
label_license.setAlignment(Qt.AlignCenter)
layout.addWidget(label_license)
layout.addWidget(QLabel(''))
label_rate = QLabel()
label_rate.setStyleSheet('font-size: 9px; font-weight: bold;')
label_rate.setText(locale_keys['about.info.rate'] + ' :)')
label_rate.setOpenExternalLinks(True)
label_rate.setAlignment(Qt.AlignCenter)
layout.addWidget(label_rate)
layout.addWidget(QLabel(''))
self.adjustSize()
self.setFixedSize(self.size())
def closeEvent(self, event):
event.ignore()
self.hide()

390
bauh/view/qt/apps_table.py Normal file
View File

@@ -0,0 +1,390 @@
import os
from threading import Lock
from typing import List
from PyQt5.QtCore import Qt, QUrl
from PyQt5.QtGui import QPixmap, QIcon, QCursor
from PyQt5.QtNetwork import QNetworkAccessManager, QNetworkRequest
from PyQt5.QtWidgets import QTableWidget, QTableView, QMenu, QAction, QTableWidgetItem, QToolButton, QWidget, \
QHeaderView, QLabel, QHBoxLayout, QPushButton, QToolBar
from bauh_api.abstract.model import ApplicationStatus
from bauh.core import resource
from bauh_api.util.cache import Cache
from bauh.util import util
from bauh.view.qt import dialog
from bauh.view.qt.view_model import ApplicationView, ApplicationViewStatus
INSTALL_BT_STYLE = 'background: {back}; color: white; font-size: 10px; font-weight: bold'
class IconButton(QWidget):
def __init__(self, icon_path: str, action, background: str = None, align: int = Qt.AlignCenter, tooltip: str = None):
super(IconButton, self).__init__()
self.bt = QToolButton()
self.bt.setIcon(QIcon(icon_path))
self.bt.clicked.connect(action)
if background:
self.bt.setStyleSheet('QToolButton { color: white; background: ' + background + '}')
if tooltip:
self.bt.setToolTip(tooltip)
layout = QHBoxLayout()
layout.setContentsMargins(0, 0, 0, 0)
layout.setAlignment(align)
layout.addWidget(self.bt)
self.setLayout(layout)
class UpdateToggleButton(QWidget):
def __init__(self, app_view: ApplicationView, root: QWidget, locale_keys: dict, checked: bool = True):
super(UpdateToggleButton, self).__init__()
self.app_view = app_view
self.root = root
layout = QHBoxLayout()
layout.setContentsMargins(2, 2, 2, 0)
layout.setAlignment(Qt.AlignCenter)
self.setLayout(layout)
self.bt = QToolButton()
self.bt.setCheckable(True)
self.bt.clicked.connect(self.change_state)
self.bt.setIcon(QIcon(resource.get_path('img/app_update.svg')))
self.bt.setStyleSheet('QToolButton { background: #4EC306 } ' +
'QToolButton:checked { background: gray }')
layout.addWidget(self.bt)
self.setToolTip(locale_keys['manage_window.apps_table.upgrade_toggle.tooltip'])
if not checked:
self.bt.click()
def change_state(self, not_checked: bool):
self.app_view.update_checked = not not_checked
self.root.change_update_state(change_filters=False)
class AppsTable(QTableWidget):
def __init__(self, parent: QWidget, icon_cache: Cache, disk_cache: bool, download_icons: bool):
super(AppsTable, self).__init__()
self.setParent(parent)
self.window = parent
self.disk_cache = disk_cache
self.download_icons = download_icons
self.column_names = ['' for _ in range(7)]
self.setColumnCount(len(self.column_names))
self.setFocusPolicy(Qt.NoFocus)
self.setShowGrid(False)
self.verticalHeader().setVisible(False)
self.horizontalHeader().setVisible(False)
self.setSelectionBehavior(QTableView.SelectRows)
self.setHorizontalHeaderLabels(self.column_names)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.icon_flathub = QIcon(resource.get_path('img/flathub.svg'))
self.icon_logo = QIcon(resource.get_path('img/logo.svg'))
self.network_man = QNetworkAccessManager()
self.network_man.finished.connect(self._load_icon_and_cache)
self.icon_cache = icon_cache
self.lock_async_data = Lock()
def has_any_settings(self, app_v: ApplicationView):
return app_v.model.can_be_refreshed() or \
app_v.model.has_history() or \
app_v.model.can_be_downgraded()
def show_app_settings(self, app: ApplicationView):
menu_row = QMenu()
if app.model.installed:
if app.model.can_be_refreshed():
action_history = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.refresh"])
action_history.setIcon(QIcon(resource.get_path('img/refresh.svg')))
def refresh():
self.window.refresh(app)
action_history.triggered.connect(refresh)
menu_row.addAction(action_history)
if app.model.has_history():
action_history = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.history"])
action_history.setIcon(QIcon(resource.get_path('img/history.svg')))
def show_history():
self.window.get_app_history(app)
action_history.triggered.connect(show_history)
menu_row.addAction(action_history)
if app.model.can_be_downgraded():
action_downgrade = QAction(self.window.locale_keys["manage_window.apps_table.row.actions.downgrade"])
def downgrade():
if dialog.ask_confirmation(
title=self.window.locale_keys['manage_window.apps_table.row.actions.downgrade'],
body=self.window.locale_keys['manage_window.apps_table.row.actions.downgrade.popup.body'].format(app.model.base_data.name),
locale_keys=self.window.locale_keys):
self.window.downgrade_app(app)
action_downgrade.triggered.connect(downgrade)
action_downgrade.setIcon(QIcon(resource.get_path('img/downgrade.svg')))
menu_row.addAction(action_downgrade)
menu_row.adjustSize()
menu_row.popup(QCursor.pos())
menu_row.exec_()
def fill_async_data(self):
if self.window.apps:
for idx, app_v in enumerate(self.window.apps):
if app_v.visible and app_v.status == ApplicationViewStatus.LOADING and app_v.model.status == ApplicationStatus.READY:
if self.download_icons:
self.network_man.get(QNetworkRequest(QUrl(app_v.model.base_data.icon_url)))
app_name = self.item(idx, 0).text()
if not app_name or app_name == '...':
self.item(idx, 0).setText(app_v.model.base_data.name)
self._set_col_version(idx, app_v)
self._set_col_description(idx, app_v)
app_v.status = ApplicationViewStatus.READY
self.window.resize_and_center()
def get_selected_app(self) -> ApplicationView:
return self.window.apps[self.currentRow()]
def get_selected_app_icon(self) -> QIcon:
return self.item(self.currentRow(), 0).icon()
def _uninstall_app(self, app_v: ApplicationView):
if dialog.ask_confirmation(title=self.window.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.title'],
body=self.window.locale_keys['manage_window.apps_table.row.actions.uninstall.popup.body'].format(app_v),
locale_keys=self.window.locale_keys):
self.window.uninstall_app(app_v)
def _downgrade_app(self):
selected_app = self.get_selected_app()
if dialog.ask_confirmation(title=self.window.locale_keys['manage_window.apps_table.row.actions.downgrade'],
body=self.window.locale_keys['manage_window.apps_table.row.actions.downgrade.popup.body'].format(selected_app.model.base_data.name),
locale_keys=self.window.locale_keys):
self.window.downgrade_app(selected_app)
def _refresh_app(self):
self.window.refresh(self.get_selected_app())
def _get_app_info(self):
self.window.get_app_info(self.get_selected_app())
def _get_app_history(self):
self.window.get_app_history(self.get_selected_app())
def _install_app(self, app_v: ApplicationView):
if dialog.ask_confirmation(
title=self.window.locale_keys['manage_window.apps_table.row.actions.install.popup.title'],
body=self.window.locale_keys['manage_window.apps_table.row.actions.install.popup.body'].format(app_v),
locale_keys=self.window.locale_keys):
self.window.install_app(app_v)
def _load_icon_and_cache(self, http_response):
icon_url = http_response.url().toString()
icon_data = self.icon_cache.get(icon_url)
icon_was_cached = True
if not icon_data:
icon_bytes = http_response.readAll()
if not icon_bytes:
return
icon_was_cached = False
pixmap = QPixmap()
pixmap.loadFromData(icon_bytes)
icon = QIcon(pixmap)
icon_data = {'icon': icon, 'bytes': icon_bytes}
self.icon_cache.add(icon_url, icon_data)
for idx, app in enumerate(self.window.apps):
if app.model.base_data.icon_url == icon_url:
col_name = self.item(idx, 0)
col_name.setIcon(icon_data['icon'])
if self.disk_cache and app.model.supports_disk_cache():
if not icon_was_cached or not os.path.exists(app.model.get_disk_icon_path()):
self.window.manager.cache_to_disk(app=app.model, icon_bytes=icon_data['bytes'], only_icon=True)
def update_apps(self, app_views: List[ApplicationView], update_check_enabled: bool = True):
self.setRowCount(len(app_views) if app_views else 0)
self.setEnabled(True)
if app_views:
for idx, app_v in enumerate(app_views):
self._set_col_name(idx, app_v)
self._set_col_version(idx, app_v)
self._set_col_description(idx, app_v)
self._set_col_type(idx, app_v)
self._set_col_installed(idx, app_v)
self._set_col_settings(idx, app_v)
col_update = None
if update_check_enabled and app_v.model.update:
col_update = UpdateToggleButton(app_v, self.window, self.window.locale_keys, app_v.model.update)
self.setCellWidget(idx, 6, col_update)
def _gen_row_button(self, text: str, style: str, callback) -> QWidget:
col = QWidget()
col_bt = QPushButton()
col_bt.setText(text)
col_bt.setStyleSheet('QPushButton { ' + style + '}')
col_bt.clicked.connect(callback)
layout = QHBoxLayout()
layout.setContentsMargins(2, 2, 2, 0)
layout.setAlignment(Qt.AlignCenter)
layout.addWidget(col_bt)
col.setLayout(layout)
return col
def _set_col_installed(self, idx: int, app_v: ApplicationView):
if app_v.model.installed:
if app_v.model.can_be_uninstalled():
def uninstall():
self._uninstall_app(app_v)
col = self._gen_row_button(self.window.locale_keys['uninstall'].capitalize(), INSTALL_BT_STYLE.format(back='#cc0000'), uninstall)
else:
col = QLabel()
col.setPixmap((QPixmap(resource.get_path('img/checked.svg'))))
col.setAlignment(Qt.AlignCenter)
col.setToolTip(self.window.locale_keys['installed'])
elif app_v.model.can_be_installed():
def install():
self._install_app(app_v)
col = self._gen_row_button(self.window.locale_keys['install'].capitalize(), INSTALL_BT_STYLE.format(back='#088A08'), install)
else:
col = None
self.setCellWidget(idx, 4, col)
def _set_col_type(self, idx: int, app_v: ApplicationView):
col_type = QLabel()
pixmap = QPixmap(app_v.model.get_default_icon_path())
col_type.setPixmap(pixmap.scaled(16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation))
col_type.setAlignment(Qt.AlignCenter)
col_type.setToolTip('{}: {}'.format(self.window.locale_keys['type'], app_v.model.get_type()))
self.setCellWidget(idx, 3, col_type)
def _set_col_version(self, idx: int, app_v: ApplicationView):
label_version = QLabel(app_v.model.base_data.version if app_v.model.base_data.version else '?')
label_version.setAlignment(Qt.AlignCenter)
col_version = QWidget()
col_version.setLayout(QHBoxLayout())
col_version.layout().addWidget(label_version)
if app_v.model.base_data.version:
tooltip = self.window.locale_keys['version.installed'] if app_v.model.installed else self.window.locale_keys['version']
else:
tooltip = self.window.locale_keys['version.unknown']
if app_v.model.update:
label_version.setStyleSheet("color: #4EC306; font-weight: bold")
tooltip = self.window.locale_keys['version.installed_outdated']
if app_v.model.installed and app_v.model.base_data.version and app_v.model.base_data.latest_version and app_v.model.base_data.version < app_v.model.base_data.latest_version:
tooltip = '{}. {}: {}'.format(tooltip, self.window.locale_keys['version.latest'], app_v.model.base_data.latest_version)
label_version.setText(label_version.text() + ' > {}'.format(app_v.model.base_data.latest_version))
col_version.setToolTip(tooltip)
self.setCellWidget(idx, 1, col_version)
def _set_col_name(self, idx: int, app_v: ApplicationView):
col = QTableWidgetItem()
col.setText(app_v.model.base_data.name if app_v.model.base_data.name else '...')
col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
col.setToolTip(self.window.locale_keys['app.name'].lower())
if self.disk_cache and app_v.model.supports_disk_cache() and os.path.exists(app_v.model.get_disk_icon_path()):
with open(app_v.model.get_disk_icon_path(), 'rb') as f:
icon_bytes = f.read()
pixmap = QPixmap()
pixmap.loadFromData(icon_bytes)
icon = QIcon(pixmap)
self.icon_cache.add_non_existing(app_v.model.base_data.icon_url, {'icon': icon, 'bytes': icon_bytes})
elif not app_v.model.base_data.icon_url:
icon = QIcon(app_v.model.get_default_icon_path())
else:
icon_data = self.icon_cache.get(app_v.model.base_data.icon_url)
icon = icon_data['icon'] if icon_data else QIcon(app_v.model.get_default_icon_path())
col.setIcon(icon)
self.setItem(idx, 0, col)
def _set_col_description(self, idx: int, app_v: ApplicationView):
col = QTableWidgetItem()
col.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
if app_v.model.base_data.description is not None or app_v.model.is_library() or app_v.model.status == ApplicationStatus.READY:
desc = app_v.model.base_data.description
else:
desc = '...'
if desc and desc != '...':
desc = util.strip_html(desc[0:25]) + '...'
col.setText(desc)
if app_v.model.base_data.description:
col.setToolTip(app_v.model.base_data.description)
self.setItem(idx, 2, col)
def _set_col_settings(self, idx: int, app_v: ApplicationView):
tb = QToolBar()
if app_v.model.has_info():
def get_info():
self.window.get_app_info(app_v)
tb.addWidget(IconButton(icon_path=resource.get_path('img/app_info.svg'), action=get_info, background='#2E68D3'))
def handle_click():
self.show_app_settings(app_v)
if self.has_any_settings(app_v):
bt = IconButton(icon_path=resource.get_path('img/app_settings.svg'), action=handle_click, background='#12ABAB')
tb.addWidget(bt)
self.setCellWidget(idx, 5, tb)
def change_headers_policy(self, policy: QHeaderView = QHeaderView.ResizeToContents):
header_horizontal = self.horizontalHeader()
for i in range(self.columnCount()):
header_horizontal.setSectionResizeMode(i, policy)

46
bauh/view/qt/dialog.py Normal file
View File

@@ -0,0 +1,46 @@
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QMessageBox
from bauh.core import resource
def show_error(title: str, body: str, icon: QIcon = QIcon(resource.get_path('img/logo.svg'))):
error_msg = QMessageBox()
error_msg.setIcon(QMessageBox.Critical)
error_msg.setWindowTitle(title)
error_msg.setText(body)
if icon:
error_msg.setWindowIcon(icon)
error_msg.exec_()
def show_warning(title: str, body: str, icon: QIcon = QIcon(resource.get_path('img/logo.svg'))):
msg = QMessageBox()
msg.setIcon(QMessageBox.Warning)
msg.setWindowTitle(title)
msg.setText(body)
if icon:
msg.setWindowIcon(icon)
msg.exec_()
def ask_confirmation(title: str, body: str, locale_keys: dict, icon: QIcon = QIcon(resource.get_path('img/logo.svg'))):
dialog_confirmation = QMessageBox()
dialog_confirmation.setIcon(QMessageBox.Question)
dialog_confirmation.setWindowTitle(title)
dialog_confirmation.setText(body)
dialog_confirmation.setStyleSheet('QLabel { margin-right: 25px; }')
bt_yes = dialog_confirmation.addButton(locale_keys['popup.button.yes'], QMessageBox.YesRole)
dialog_confirmation.addButton(locale_keys['popup.button.no'], QMessageBox.NoRole)
if icon:
dialog_confirmation.setWindowIcon(icon)
dialog_confirmation.exec_()
return dialog_confirmation.clickedButton() == bt_yes

59
bauh/view/qt/history.py Normal file
View File

@@ -0,0 +1,59 @@
import operator
from functools import reduce
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QTableWidget, QTableWidgetItem, QHeaderView
from bauh_api.util.cache import Cache
class HistoryDialog(QDialog):
def __init__(self, app: dict, icon_cache: Cache, locale_keys: dict):
super(HistoryDialog, self).__init__()
self.setWindowTitle('{} - {} '.format(locale_keys['popup.history.title'], app['model'].base_data.name))
layout = QVBoxLayout()
self.setLayout(layout)
table_history = QTableWidget()
table_history.setFocusPolicy(Qt.NoFocus)
table_history.setShowGrid(False)
table_history.verticalHeader().setVisible(False)
table_history.setAlternatingRowColors(True)
table_history.setColumnCount(len(app['history'][0]))
table_history.setRowCount(len(app['history']))
table_history.setHorizontalHeaderLabels([locale_keys['flatpak.info.' + key].capitalize() for key in sorted(app['history'][0].keys())])
for row, commit in enumerate(app['history']):
current_app_commit = app['model'].commit == commit['commit']
for col, key in enumerate(sorted(commit.keys())):
item = QTableWidgetItem()
item.setText(commit[key])
item.setFlags(Qt.ItemIsSelectable | Qt.ItemIsEnabled)
if current_app_commit:
item.setBackground(QColor('#ffbf00' if row != 0 else '#32CD32'))
tip = '{}. {}.'.format(locale_keys['popup.history.selected.tooltip'], locale_keys['version.{}'.format('updated'if row == 0 else 'outdated')].capitalize())
item.setToolTip(tip)
table_history.setItem(row, col, item)
layout.addWidget(table_history)
header_horizontal = table_history.horizontalHeader()
for i in range(0, table_history.columnCount()):
header_horizontal.setSectionResizeMode(i, QHeaderView.Stretch)
new_width = reduce(operator.add, [table_history.columnWidth(i) for i in range(table_history.columnCount())])
self.resize(new_width, table_history.height())
icon_data = icon_cache.get(app['model'].base_data.icon_url)
if icon_data and icon_data.get('icon'):
self.setWindowIcon(icon_data.get('icon'))

58
bauh/view/qt/info.py Normal file
View File

@@ -0,0 +1,58 @@
from PyQt5.QtCore import QSize
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QDialog, QVBoxLayout, QFormLayout, QGroupBox, \
QLineEdit, QLabel
from bauh_api.util.cache import Cache
from bauh.util import util
IGNORED_ATTRS = {'name', '__app__'}
class InfoDialog(QDialog):
def __init__(self, app: dict, icon_cache: Cache, locale_keys: dict, screen_size: QSize()):
super(InfoDialog, self).__init__()
self.setWindowTitle(app['__app__'].model.base_data.name)
self.screen_size = screen_size
layout = QVBoxLayout()
self.setLayout(layout)
gbox_info = QGroupBox()
gbox_info.setMaximumHeight(self.screen_size.height() - self.screen_size.height() * 0.1)
gbox_info_layout = QFormLayout()
gbox_info.setLayout(gbox_info_layout)
layout.addWidget(gbox_info)
icon_data = icon_cache.get(app['__app__'].model.base_data.icon_url)
if icon_data and icon_data.get('icon'):
self.setWindowIcon(icon_data.get('icon'))
else:
self.setWindowIcon(QIcon(app['__app__'].model.get_type_icon_path()))
for attr in sorted(app.keys()):
if attr not in IGNORED_ATTRS and app[attr]:
val = app[attr]
text = QLineEdit()
text.setToolTip(val)
if attr == 'license' and val.strip() == 'unset':
val = locale_keys['license.unset']
if attr == 'description':
val = util.strip_html(val)
val = val[0:40] + '...'
text.setText(val)
text.setCursorPosition(0)
text.setStyleSheet("width: 400px")
text.setReadOnly(True)
label = QLabel("{}: ".format(locale_keys.get(app['__app__'].model.get_type() + '.info.' + attr, attr)).capitalize())
label.setStyleSheet("font-weight: bold")
gbox_info_layout.addRow(label, text)
self.adjustSize()

45
bauh/view/qt/root.py Normal file
View File

@@ -0,0 +1,45 @@
import io
import os
import subprocess
from PyQt5.QtWidgets import QInputDialog, QLineEdit
from bauh.view.qt.dialog import show_error
def is_root():
return os.getuid() == 0
def ask_root_password(locale_keys: dict):
dialog_pwd = QInputDialog()
dialog_pwd.setInputMode(QInputDialog.TextInput)
dialog_pwd.setTextEchoMode(QLineEdit.Password)
dialog_pwd.setWindowTitle(locale_keys['popup.root.title'])
dialog_pwd.setLabelText(locale_keys['popup.root.password'] + ':')
dialog_pwd.setCancelButtonText(locale_keys['popup.button.cancel'])
dialog_pwd.resize(400, 200)
ok = dialog_pwd.exec_()
if ok:
if not validate_password(dialog_pwd.textValue()):
show_error(title=locale_keys['popup.root.bad_password.title'],
body=locale_keys['popup.root.bad_password.body'])
ok = False
return dialog_pwd.textValue(), ok
def validate_password(password: str) -> bool:
proc = subprocess.Popen('sudo -k && echo {} | sudo -S whoami'.format(password),
shell=True,
stdout=subprocess.PIPE,
stderr=subprocess.DEVNULL,
bufsize=-1)
stream = os._wrap_close(io.TextIOWrapper(proc.stdout), proc)
res = stream.read()
stream.close()
return bool(res.strip())

129
bauh/view/qt/systray.py Executable file
View File

@@ -0,0 +1,129 @@
import time
from threading import Lock, Thread
from typing import List
from PyQt5.QtCore import QThread, pyqtSignal, QCoreApplication, Qt
from PyQt5.QtGui import QIcon
from PyQt5.QtWidgets import QSystemTrayIcon, QMenu
from bauh_api.abstract.model import ApplicationUpdate
from bauh import __app_name__
from bauh.core import resource
from bauh_api.abstract.controller import ApplicationManager
from bauh.util import util
from bauh.view.qt.about import AboutDialog
from bauh.view.qt.window import ManageWindow
class UpdateCheck(QThread):
signal = pyqtSignal(list)
def __init__(self, manager: ApplicationManager, check_interval: int, parent=None):
super(UpdateCheck, self).__init__(parent)
self.check_interval = check_interval
self.manager = manager
def run(self):
while True:
updates = self.manager.list_updates()
self.signal.emit(updates)
time.sleep(self.check_interval)
class TrayIcon(QSystemTrayIcon):
def __init__(self, locale_keys: dict, manager: ApplicationManager, manage_window: ManageWindow, check_interval: int = 60, update_notification: bool = True):
super(TrayIcon, self).__init__()
self.locale_keys = locale_keys
self.manager = manager
self.icon_default = QIcon(resource.get_path('img/logo.png'))
self.icon_update = QIcon(resource.get_path('img/logo_update.png'))
self.setIcon(self.icon_default)
self.menu = QMenu()
self.action_manage = self.menu.addAction(self.locale_keys['tray.action.manage'])
self.action_manage.triggered.connect(self.show_manage_window)
self.action_about = self.menu.addAction(self.locale_keys['tray.action.about'])
self.action_about.triggered.connect(self.show_about)
self.action_exit = self.menu.addAction(self.locale_keys['tray.action.exit'])
self.action_exit.triggered.connect(lambda: QCoreApplication.exit())
self.setContextMenu(self.menu)
self.manage_window = None
self.dialog_about = None
self.check_thread = UpdateCheck(check_interval=check_interval, manager=self.manager)
self.check_thread.signal.connect(self.notify_updates)
self.check_thread.start()
self.last_updates = set()
self.update_notification = update_notification
self.lock_notify = Lock()
self.activated.connect(self.handle_click)
self.set_default_tooltip()
self.manage_window = manage_window
def set_default_tooltip(self):
self.setToolTip('{} ({})'.format(self.locale_keys['manage_window.title'], __app_name__).lower())
def handle_click(self, reason):
if reason == self.Trigger:
self.show_manage_window()
def verify_updates(self, notify_user: bool = True):
Thread(target=self._verify_updates, args=(notify_user,)).start()
def _verify_updates(self, notify_user: bool):
self.notify_updates(self.manager.list_updates(), notify_user=notify_user)
def notify_updates(self, updates: List[ApplicationUpdate], notify_user: bool = True):
self.lock_notify.acquire()
try:
if len(updates) > 0:
update_keys = {'{}:{}:{}'.format(up.type, up.id, up.version) for up in updates}
new_icon = self.icon_update
if update_keys.difference(self.last_updates):
self.last_updates = update_keys
msg = '{}: {}'.format(self.locale_keys['notification.new_updates'], len(updates))
self.setToolTip(msg)
if self.update_notification and notify_user:
util.notify_user(msg=msg)
else:
self.last_updates.clear()
new_icon = self.icon_default
self.set_default_tooltip()
if self.icon().cacheKey() != new_icon.cacheKey(): # changes the icon if needed
self.setIcon(new_icon)
finally:
self.lock_notify.release()
def show_manage_window(self):
if self.manage_window.isMinimized():
self.manage_window.setWindowState(Qt.WindowNoState)
elif not self.manage_window.isVisible():
self.manage_window.refresh_apps()
self.manage_window.show()
def show_about(self):
if self.dialog_about is None:
self.dialog_about = AboutDialog(self.locale_keys)
if self.dialog_about.isHidden():
self.dialog_about.show()

364
bauh/view/qt/thread.py Normal file
View File

@@ -0,0 +1,364 @@
import time
from datetime import datetime, timedelta
from typing import List
import requests
from PyQt5.QtCore import QThread, pyqtSignal
from bauh_api.abstract.model import ApplicationStatus
from bauh_api.exception import NoInternetException
from bauh_api.util.cache import Cache
from bauh_api.util.system import SystemProcess
from bauh_api.abstract.controller import ApplicationManager
from bauh.view.qt import dialog
from bauh.view.qt.view_model import ApplicationView
class AsyncAction(QThread):
def notify_subproc_outputs(self, proc: SystemProcess, signal) -> bool:
"""
:param signal:
:param proc:
:return: if the subprocess succeeded
"""
signal.emit(' '.join(proc.subproc.args) + '\n')
success, already_succeeded = True, False
for output in proc.subproc.stdout:
line = output.decode().strip()
if line:
signal.emit(line)
if proc.success_phrase and proc.success_phrase in line:
already_succeeded = True
if already_succeeded:
return True
for output in proc.subproc.stderr:
line = output.decode().strip()
if line:
if proc.wrong_error_phrase and proc.wrong_error_phrase in line:
continue
else:
success = False
signal.emit(line)
return success
class UpdateSelectedApps(AsyncAction):
signal_finished = pyqtSignal(bool, int)
signal_status = pyqtSignal(str)
signal_output = pyqtSignal(str)
def __init__(self, manager: ApplicationManager, apps_to_update: List[ApplicationView] = None):
super(UpdateSelectedApps, self).__init__()
self.apps_to_update = apps_to_update
self.manager = manager
self.root_password = None
def run(self):
success = False
for app in self.apps_to_update:
self.signal_status.emit(app.model.base_data.name)
process = self.manager.update(app.model, self.root_password)
success = self.notify_subproc_outputs(process, self.signal_output)
if not success:
break
else:
self.signal_output.emit('\n')
self.signal_finished.emit(success, len(self.apps_to_update))
self.apps_to_update = None
class RefreshApps(QThread):
signal = pyqtSignal(list)
def __init__(self, manager: ApplicationManager):
super(RefreshApps, self).__init__()
self.manager = manager
def run(self):
self.signal.emit(self.manager.read_installed())
class UninstallApp(AsyncAction):
signal_finished = pyqtSignal(object)
signal_output = pyqtSignal(str)
def __init__(self, manager: ApplicationManager, icon_cache: Cache, app: ApplicationView = None):
super(UninstallApp, self).__init__()
self.app = app
self.manager = manager
self.icon_cache = icon_cache
self.root_password = None
def run(self):
if self.app:
process = self.manager.uninstall(self.app.model, self.root_password)
success = self.notify_subproc_outputs(process, self.signal_output)
if success:
self.icon_cache.delete(self.app.model.base_data.icon_url)
self.manager.clean_cache_for(self.app.model)
self.signal_finished.emit(self.app if success else None)
self.app = None
self.root_password = None
class DowngradeApp(AsyncAction):
signal_finished = pyqtSignal(bool)
signal_output = pyqtSignal(str)
def __init__(self, manager: ApplicationManager, locale_keys: dict, app: ApplicationView = None):
super(DowngradeApp, self).__init__()
self.manager = manager
self.app = app
self.locale_keys = locale_keys
self.root_password = None
def run(self):
if self.app:
success = False
try:
process = self.manager.downgrade_app(self.app.model, self.root_password)
if process is None:
dialog.show_error(title=self.locale_keys['popup.downgrade.impossible.title'],
body=self.locale_keys['popup.downgrade.impossible.body'])
else:
success = self.notify_subproc_outputs(process, self.signal_output)
except (requests.exceptions.ConnectionError, NoInternetException):
success = False
self.signal_output.emit(self.locale_keys['internet.required'])
finally:
self.app = None
self.root_password = None
self.signal_finished.emit(success)
class GetAppInfo(QThread):
signal_finished = pyqtSignal(dict)
def __init__(self, manager: ApplicationManager, app: ApplicationView = None):
super(GetAppInfo, self).__init__()
self.app = app
self.manager = manager
def run(self):
if self.app:
info = {'__app__': self.app}
info.update(self.manager.get_info(self.app.model))
self.signal_finished.emit(info)
self.app = None
class GetAppHistory(QThread):
signal_finished = pyqtSignal(dict)
def __init__(self, manager: ApplicationManager, locale_keys: dict, app: ApplicationView = None):
super(GetAppHistory, self).__init__()
self.app = app
self.manager = manager
self.locale_keys = locale_keys
def run(self):
if self.app:
try:
res = {'model': self.app.model, 'history': self.manager.get_history(self.app.model)}
self.signal_finished.emit(res)
except (requests.exceptions.ConnectionError, NoInternetException):
self.signal_finished.emit({'error': self.locale_keys['internet.required']})
finally:
self.app = None
class SearchApps(QThread):
signal_finished = pyqtSignal(list)
def __init__(self, manager: ApplicationManager):
super(SearchApps, self).__init__()
self.word = None
self.manager = manager
def run(self):
apps_found = []
if self.word:
res = self.manager.search(self.word)
apps_found.extend(res['installed'])
apps_found.extend(res['new'])
self.signal_finished.emit(apps_found)
self.word = None
class InstallApp(AsyncAction):
signal_finished = pyqtSignal(object)
signal_output = pyqtSignal(str)
def __init__(self, manager: ApplicationManager, disk_cache: bool, icon_cache: Cache, locale_keys: dict, app: ApplicationView = None):
super(InstallApp, self).__init__()
self.app = app
self.manager = manager
self.icon_cache = icon_cache
self.disk_cache = disk_cache
self.locale_keys = locale_keys
self.root_password = None
def run(self):
if self.app:
success = False
try:
process = self.manager.install(self.app.model, self.root_password)
success = self.notify_subproc_outputs(process, self.signal_output)
if success and self.disk_cache:
self.app.model.installed = True
if self.app.model.supports_disk_cache():
icon_data = self.icon_cache.get(self.app.model.base_data.icon_url)
self.manager.cache_to_disk(app=self.app.model,
icon_bytes=icon_data.get('bytes') if icon_data else None,
only_icon=False)
except (requests.exceptions.ConnectionError, NoInternetException):
success = False
self.signal_output.emit(self.locale_keys['internet.required'])
finally:
self.signal_finished.emit(self.app if success else None)
self.app = None
class AnimateProgress(QThread):
signal_change = pyqtSignal(int)
def __init__(self):
super(AnimateProgress, self).__init__()
self.progress_value = 0
self.increment = 5
self.stop = False
def run(self):
current_increment = self.increment
while not self.stop:
self.signal_change.emit(self.progress_value)
if self.progress_value == 100:
current_increment = -current_increment
if self.progress_value == 0:
current_increment = self.increment
self.progress_value += current_increment
time.sleep(0.05)
self.progress_value = 0
class VerifyModels(QThread):
signal_updates = pyqtSignal()
def __init__(self, apps: List[ApplicationView] = None):
super(VerifyModels, self).__init__()
self.apps = apps
def run(self):
if self.apps:
stop_at = datetime.utcnow() + timedelta(seconds=30)
last_ready = 0
while True:
current_ready = 0
for app in self.apps:
current_ready += 1 if app.model.status == ApplicationStatus.READY else 0
if current_ready > last_ready:
last_ready = current_ready
self.signal_updates.emit()
if current_ready == len(self.apps):
self.signal_updates.emit()
break
if stop_at <= datetime.utcnow():
break
time.sleep(0.1)
self.apps = None
class RefreshApp(AsyncAction):
signal_finished = pyqtSignal(bool)
signal_output = pyqtSignal(str)
def __init__(self, manager: ApplicationManager, app: ApplicationView = None):
super(RefreshApp, self).__init__()
self.app = app
self.manager = manager
self.root_password = None
def run(self):
if self.app:
success = False
try:
process = self.manager.refresh(self.app.model, self.root_password)
success = self.notify_subproc_outputs(process, self.signal_output)
except (requests.exceptions.ConnectionError, NoInternetException):
success = False
self.signal_output.emit(self.locale_keys['internet.required'])
finally:
self.app = None
self.signal_finished.emit(success)
class FindSuggestions(AsyncAction):
signal_finished = pyqtSignal(list)
def __init__(self, man: ApplicationManager):
super(FindSuggestions, self).__init__()
self.man = man
def run(self):
sugs = self.man.list_suggestions(limit=-1)
self.signal_finished.emit(sugs if sugs is not None else [])
class ListWarnings(QThread):
signal_warnings = pyqtSignal(list)
def __init__(self, man: ApplicationManager, locale_keys: dict):
super(QThread, self).__init__()
self.locale_keys = locale_keys
self.man = man
def run(self):
warnings = self.man.list_warnings()
if warnings:
self.signal_warnings.emit(warnings)

View File

@@ -0,0 +1,20 @@
from enum import Enum
from bauh_api.abstract.model import Application
class ApplicationViewStatus(Enum):
LOADING = 0
READY = 1
class ApplicationView:
def __init__(self, model: Application, visible: bool = True):
self.model = model
self.update_checked = model.update
self.visible = visible
self.status = ApplicationViewStatus.LOADING
def __repr__(self):
return '{} ( {} )'.format(self.model.base_data.name, self.model.get_type())

722
bauh/view/qt/window.py Executable file
View File

@@ -0,0 +1,722 @@
import operator
from functools import reduce
from typing import List, Set
from PyQt5.QtCore import QEvent, Qt, QSize
from PyQt5.QtGui import QIcon, QWindowStateChangeEvent, QPixmap
from PyQt5.QtWidgets import QWidget, QVBoxLayout, QApplication, QCheckBox, QHeaderView, QToolButton, QToolBar, \
QSizePolicy, QLabel, QPlainTextEdit, QLineEdit, QProgressBar, QHBoxLayout
from bauh_api.abstract.controller import ApplicationManager
from bauh_api.abstract.model import Application
from bauh_api.util.cache import Cache
from bauh.core import resource
from bauh.util import util
from bauh.view.qt import dialog
from bauh.view.qt.about import AboutDialog
from bauh.view.qt.apps_table import AppsTable
from bauh.view.qt.history import HistoryDialog
from bauh.view.qt.info import InfoDialog
from bauh.view.qt.root import is_root, ask_root_password
from bauh.view.qt.thread import UpdateSelectedApps, RefreshApps, UninstallApp, DowngradeApp, GetAppInfo, \
GetAppHistory, SearchApps, InstallApp, AnimateProgress, VerifyModels, RefreshApp, FindSuggestions, ListWarnings
from bauh.view.qt.view_model import ApplicationView
DARK_ORANGE = '#FF4500'
class ManageWindow(QWidget):
__BASE_HEIGHT__ = 400
def __init__(self, locale_keys: dict, icon_cache: Cache, manager: ApplicationManager, disk_cache: bool, download_icons: bool, screen_size, suggestions: bool, tray_icon=None):
super(ManageWindow, self).__init__()
self.locale_keys = locale_keys
self.manager = manager
self.tray_icon = tray_icon
self.working = False # restrict the number of threaded actions
self.apps = []
self.label_flatpak = None
self.icon_cache = icon_cache
self.disk_cache = disk_cache
self.download_icons = download_icons
self.screen_size = screen_size
self.icon_flathub = QIcon(resource.get_path('img/logo.svg'))
self.resize(ManageWindow.__BASE_HEIGHT__, ManageWindow.__BASE_HEIGHT__)
self.setWindowIcon(self.icon_flathub)
self.layout = QVBoxLayout()
self.setLayout(self.layout)
self.toolbar_top = QToolBar()
self.toolbar_top.addWidget(self._new_spacer())
self.label_status = QLabel()
self.label_status.setText('')
self.label_status.setStyleSheet("font-weight: bold")
self.toolbar_top.addWidget(self.label_status)
self.toolbar_search = QToolBar()
self.toolbar_search.setStyleSheet("spacing: 0px;")
self.toolbar_search.setContentsMargins(0, 0, 0, 0)
label_pre_search = QLabel()
label_pre_search.setStyleSheet("background: white; border-top-left-radius: 5px; border-bottom-left-radius: 5px;")
self.toolbar_search.addWidget(label_pre_search)
self.input_search = QLineEdit()
self.input_search.setMaxLength(20)
self.input_search.setFrame(False)
self.input_search.setPlaceholderText(self.locale_keys['window_manage.input_search.placeholder'] + "...")
self.input_search.setToolTip(self.locale_keys['window_manage.input_search.tooltip'])
self.input_search.setStyleSheet("QLineEdit { background-color: white; color: gray; spacing: 0;}")
self.input_search.returnPressed.connect(self.search)
self.toolbar_search.addWidget(self.input_search)
label_pos_search = QLabel()
label_pos_search.setPixmap(QPixmap(resource.get_path('img/search.svg')))
label_pos_search.setStyleSheet("background: white; padding-right: 10px; border-top-right-radius: 5px; border-bottom-right-radius: 5px;")
self.toolbar_search.addWidget(label_pos_search)
self.ref_toolbar_search = self.toolbar_top.addWidget(self.toolbar_search)
self.toolbar_top.addWidget(self._new_spacer())
self.layout.addWidget(self.toolbar_top)
toolbar = QToolBar()
self.checkbox_updates = QCheckBox()
self.checkbox_updates.setText(self.locale_keys['updates'].capitalize())
self.checkbox_updates.stateChanged.connect(self._handle_updates_filter)
self.ref_checkbox_updates = toolbar.addWidget(self.checkbox_updates)
self.checkbox_only_apps = QCheckBox()
self.checkbox_only_apps.setText(self.locale_keys['manage_window.checkbox.only_apps'])
self.checkbox_only_apps.setChecked(True)
self.checkbox_only_apps.stateChanged.connect(self._handle_filter_only_apps)
self.ref_checkbox_only_apps = toolbar.addWidget(self.checkbox_only_apps)
self.extra_filters = QWidget()
self.extra_filters.setLayout(QHBoxLayout())
toolbar.addWidget(self.extra_filters)
toolbar.addWidget(self._new_spacer())
self.bt_refresh = QToolButton()
self.bt_refresh.setToolTip(locale_keys['manage_window.bt.refresh.tooltip'])
self.bt_refresh.setIcon(QIcon(resource.get_path('img/refresh.svg')))
self.bt_refresh.clicked.connect(lambda: self.refresh_apps(keep_console=False))
toolbar.addWidget(self.bt_refresh)
self.bt_upgrade = QToolButton()
self.bt_upgrade.setToolTip(locale_keys['manage_window.bt.upgrade.tooltip'])
self.bt_upgrade.setIcon(QIcon(resource.get_path('img/update_green.svg')))
self.bt_upgrade.setEnabled(False)
self.bt_upgrade.clicked.connect(self.update_selected)
self.ref_bt_upgrade = toolbar.addWidget(self.bt_upgrade)
self.layout.addWidget(toolbar)
self.table_apps = AppsTable(self, self.icon_cache, disk_cache=self.disk_cache, download_icons=self.download_icons)
self.table_apps.change_headers_policy()
self.layout.addWidget(self.table_apps)
toolbar_console = QToolBar()
self.checkbox_console = QCheckBox()
self.checkbox_console.setText(self.locale_keys['manage_window.checkbox.show_details'])
self.checkbox_console.stateChanged.connect(self._handle_console)
self.checkbox_console.setVisible(False)
self.ref_checkbox_console = toolbar_console.addWidget(self.checkbox_console)
toolbar_console.addWidget(self._new_spacer())
self.layout.addWidget(toolbar_console)
self.textarea_output = QPlainTextEdit(self)
self.textarea_output.resize(self.table_apps.size())
self.textarea_output.setStyleSheet("background: black; color: white;")
self.layout.addWidget(self.textarea_output)
self.textarea_output.setVisible(False)
self.textarea_output.setReadOnly(True)
self.thread_update = UpdateSelectedApps(self.manager)
self.thread_update.signal_output.connect(self._update_action_output)
self.thread_update.signal_finished.connect(self._finish_update_selected)
self.thread_update.signal_status.connect(self._change_updating_app_status)
self.thread_refresh = RefreshApps(self.manager)
self.thread_refresh.signal.connect(self._finish_refresh_apps)
self.thread_uninstall = UninstallApp(self.manager, self.icon_cache)
self.thread_uninstall.signal_output.connect(self._update_action_output)
self.thread_uninstall.signal_finished.connect(self._finish_uninstall)
self.thread_downgrade = DowngradeApp(self.manager, self.locale_keys)
self.thread_downgrade.signal_output.connect(self._update_action_output)
self.thread_downgrade.signal_finished.connect(self._finish_downgrade)
self.thread_get_info = GetAppInfo(self.manager)
self.thread_get_info.signal_finished.connect(self._finish_get_info)
self.thread_get_history = GetAppHistory(self.manager, self.locale_keys)
self.thread_get_history.signal_finished.connect(self._finish_get_history)
self.thread_search = SearchApps(self.manager)
self.thread_search.signal_finished.connect(self._finish_search)
self.thread_install = InstallApp(manager=self.manager, disk_cache=self.disk_cache, icon_cache=self.icon_cache, locale_keys=self.locale_keys)
self.thread_install.signal_output.connect(self._update_action_output)
self.thread_install.signal_finished.connect(self._finish_install)
self.thread_animate_progress = AnimateProgress()
self.thread_animate_progress.signal_change.connect(self._update_progress)
self.thread_verify_models = VerifyModels()
self.thread_verify_models.signal_updates.connect(self._notify_model_data_change)
self.thread_refresh_app = RefreshApp(manager=self.manager)
self.thread_refresh_app.signal_finished.connect(self._finish_refresh)
self.thread_refresh_app.signal_output.connect(self._update_action_output)
self.thread_suggestions = FindSuggestions(man=self.manager)
self.thread_suggestions.signal_finished.connect(self._finish_search)
self.toolbar_bottom = QToolBar()
self.toolbar_bottom.setIconSize(QSize(16, 16))
self.label_updates = QLabel()
self.ref_label_updates = self.toolbar_bottom.addWidget(self.label_updates)
self.toolbar_bottom.addWidget(self._new_spacer())
self.progress_bar = QProgressBar()
self.progress_bar.setTextVisible(False)
self.ref_progress_bar = self.toolbar_bottom.addWidget(self.progress_bar)
self.toolbar_bottom.addWidget(self._new_spacer())
bt_about = QToolButton()
bt_about.setStyleSheet('QToolButton { border: 0px; }')
bt_about.setIcon(QIcon(resource.get_path('img/about.svg')))
bt_about.clicked.connect(self._show_about)
bt_about.setToolTip(self.locale_keys['manage_window.bt_about.tooltip'])
self.ref_bt_about = self.toolbar_bottom.addWidget(bt_about)
self.layout.addWidget(self.toolbar_bottom)
self.centralize()
self.filter_only_apps = True
self.filter_types = set()
self.filter_updates = False
self._maximized = False
self.dialog_about = None
self.first_refresh = suggestions
self.thread_warnings = ListWarnings(man=manager, locale_keys=locale_keys)
self.thread_warnings.signal_warnings.connect(self._show_warnings)
def _show_warnings(self, warnings: List[str]):
if warnings:
for warning in warnings:
dialog.show_warning(title=self.locale_keys['warning'].capitalize(), body=warning)
def show(self):
super(ManageWindow, self).show()
if not self.thread_warnings.isFinished():
self.thread_warnings.start()
def _show_about(self):
if self.dialog_about is None:
self.dialog_about = AboutDialog(self.locale_keys)
self.dialog_about.show()
def _handle_updates_filter(self, status: int):
self.filter_updates = status == 2
self.apply_filters()
def _handle_filter_only_apps(self, status: int):
self.filter_only_apps = status == 2
self.apply_filters()
def _handle_type_filter(self, status: int, app_type: str):
if status == 2:
self.filter_types.add(app_type)
elif app_type in self.filter_types:
self.filter_types.remove(app_type)
self.apply_filters()
def _notify_model_data_change(self):
self.table_apps.fill_async_data()
def _new_spacer(self):
spacer = QWidget()
spacer.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
return spacer
def changeEvent(self, e: QEvent):
if isinstance(e, QWindowStateChangeEvent):
self._maximized = self.isMaximized()
policy = QHeaderView.Stretch if self._maximized else QHeaderView.ResizeToContents
self.table_apps.change_headers_policy(policy)
def closeEvent(self, event):
if self.tray_icon:
event.ignore()
self.hide()
self._handle_console_option(False)
def _handle_console(self, checked: bool):
if checked:
self.textarea_output.show()
else:
self.textarea_output.hide()
def _handle_console_option(self, enable: bool):
if enable:
self.textarea_output.clear()
self.ref_checkbox_console.setVisible(enable)
self.checkbox_console.setChecked(False)
self.textarea_output.hide()
def refresh_apps(self, keep_console: bool = True):
self.filter_types.clear()
self.input_search.clear()
if not keep_console:
self._handle_console_option(False)
self.ref_checkbox_updates.setVisible(False)
self.ref_checkbox_only_apps.setVisible(False)
self._begin_action(self.locale_keys['manage_window.status.refreshing'], clear_filters=True)
self.thread_refresh.start()
def _finish_refresh_apps(self, apps: List[Application]):
self.finish_action()
self.ref_checkbox_only_apps.setVisible(True)
self.ref_bt_upgrade.setVisible(True)
self.update_apps(apps)
self.first_refresh = False
def uninstall_app(self, app: ApplicationView):
pwd = None
requires_root = self.manager.requires_root('uninstall', app.model)
if not is_root() and requires_root:
pwd, ok = ask_root_password(self.locale_keys)
if not ok:
return
self._handle_console_option(True)
self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.uninstalling'], app.model.base_data.name))
self.thread_uninstall.app = app
self.thread_uninstall.root_password = pwd
self.thread_uninstall.start()
def refresh(self, app: ApplicationView):
pwd = None
requires_root = self.manager.requires_root('refresh', app.model)
if not is_root() and requires_root:
pwd, ok = ask_root_password(self.locale_keys)
if not ok:
return
self._handle_console_option(True)
self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.refreshing_app'], app.model.base_data.name))
self.thread_refresh_app.app = app
self.thread_refresh_app.root_password = pwd
self.thread_refresh_app.start()
def _finish_uninstall(self, app: ApplicationView):
self.finish_action()
if app:
if self._can_notify_user():
util.notify_user('{} ({}) {}'.format(app.model.base_data.name, app.model.get_type(), self.locale_keys['uninstalled']))
self.refresh_apps()
else:
if self._can_notify_user():
util.notify_user('{}: {}'.format(app.model.base_data.name, self.locale_keys['notification.uninstall.failed']))
self.checkbox_console.setChecked(True)
def _can_notify_user(self):
return self.isHidden() or self.isMinimized()
def _finish_downgrade(self, success: bool):
self.finish_action()
if success:
if self._can_notify_user():
app = self.table_apps.get_selected_app()
util.notify_user('{} ({}) {}'.format(app.model.base_data.name, app.model.get_type(), self.locale_keys['downgraded']))
self.refresh_apps()
if self.tray_icon:
self.tray_icon.verify_updates(notify_user=False)
else:
if self._can_notify_user():
util.notify_user(self.locale_keys['notification.downgrade.failed'])
self.checkbox_console.setChecked(True)
def _finish_refresh(self, success: bool):
self.finish_action()
if success:
self.refresh_apps()
else:
self.checkbox_console.setChecked(True)
def _change_updating_app_status(self, app_name: str):
self.label_status.setText('{} {}...'.format(self.locale_keys['manage_window.status.upgrading'], app_name))
def apply_filters(self):
if self.apps:
visible_apps = len(self.apps)
for idx, app_v in enumerate(self.apps):
hidden = self.filter_only_apps and app_v.model.is_library()
if not hidden and self.filter_types is not None:
hidden = app_v.model.get_type() not in self.filter_types
if not hidden and self.filter_updates:
hidden = not app_v.model.update
self.table_apps.setRowHidden(idx, hidden)
app_v.visible = not hidden
visible_apps -= 1 if hidden else 0
self.change_update_state(change_filters=False)
if not self._maximized:
self.table_apps.change_headers_policy(QHeaderView.Stretch)
self.table_apps.change_headers_policy()
self.resize_and_center(accept_lower_width=visible_apps > 0)
def change_update_state(self, change_filters: bool = True):
enable_bt_update = False
app_updates, library_updates, not_installed = 0, 0, 0
for app_v in self.apps:
if app_v.model.update:
if app_v.model.runtime:
library_updates += 1
else:
app_updates += 1
if not app_v.model.installed:
not_installed += 1
for app_v in self.apps:
if not_installed == 0 and app_v.visible and app_v.update_checked:
enable_bt_update = True
break
self.bt_upgrade.setEnabled(enable_bt_update)
total_updates = app_updates + library_updates
if total_updates > 0:
self.label_updates.setPixmap(QPixmap(resource.get_path('img/exclamation.svg')).scaled(16, 16, Qt.KeepAspectRatio, Qt.SmoothTransformation))
self.label_updates.setToolTip('{}: {} ( {} {} | {} {} )'.format(self.locale_keys['manage_window.label.updates'],
total_updates,
app_updates,
self.locale_keys['manage_window.checkbox.only_apps'].lower(),
library_updates,
self.locale_keys['others'].lower()))
if not_installed == 0:
if not self.ref_checkbox_updates.isVisible():
self.ref_checkbox_updates.setVisible(True)
if change_filters and not self.checkbox_updates.isChecked():
self.checkbox_updates.setChecked(True)
if change_filters and library_updates > 0 and self.checkbox_only_apps.isChecked():
self.checkbox_only_apps.setChecked(False)
else:
self.checkbox_updates.setChecked(False)
self.ref_checkbox_updates.setVisible(False)
self.label_updates.setPixmap(QPixmap())
def centralize(self):
geo = self.frameGeometry()
screen = QApplication.desktop().screenNumber(QApplication.desktop().cursor().pos())
center_point = QApplication.desktop().screenGeometry(screen).center()
geo.moveCenter(center_point)
self.move(geo.topLeft())
def update_apps(self, apps: List[Application], update_check_enabled: bool = True):
self.apps = []
napps = 0 # number of apps (not libraries)
available_types = set()
if apps:
for app in apps:
app_model = ApplicationView(model=app, visible=(not app.is_library()) or not self.checkbox_only_apps.isChecked())
available_types.add(app.get_type())
napps += 1 if not app.is_library() else 0
self.apps.append(app_model)
if napps == 0:
if self.first_refresh:
self._begin_search('')
self.thread_suggestions.start()
return
else:
self.checkbox_only_apps.setChecked(False)
self.checkbox_only_apps.setCheckable(False)
else:
self.checkbox_only_apps.setCheckable(True)
self.checkbox_only_apps.setChecked(True)
self._update_type_filters(available_types)
self.table_apps.update_apps(self.apps, update_check_enabled=update_check_enabled)
self.apply_filters()
self.change_update_state()
self.resize_and_center()
self.thread_verify_models.apps = self.apps
self.thread_verify_models.start()
def _update_type_filters(self, available_types: Set[str]):
self.filter_types = available_types
filters_layout = self.extra_filters.layout()
for i in reversed(range(filters_layout.count())):
filters_layout.itemAt(i).widget().setParent(None)
if available_types:
for app_type in sorted(list(available_types)):
checkbox_app_type = QCheckBox()
checkbox_app_type.setChecked(True)
checkbox_app_type.setText(app_type.capitalize())
def handle_click(status: int, filter_type: str = app_type):
self._handle_type_filter(status, filter_type)
checkbox_app_type.stateChanged.connect(handle_click)
filters_layout.addWidget(checkbox_app_type)
def resize_and_center(self, accept_lower_width: bool = True):
new_width = reduce(operator.add, [self.table_apps.columnWidth(i) for i in range(len(self.table_apps.column_names))]) * 1.05
if accept_lower_width or new_width > self.width():
self.resize(new_width, self.height())
self.centralize()
def update_selected(self):
if self.apps:
requires_root = False
to_update = []
for app_v in self.apps:
if app_v.visible and app_v.update_checked:
to_update.append(app_v)
if self.manager.requires_root('update', app_v.model):
requires_root = True
if to_update and dialog.ask_confirmation(title=self.locale_keys['manage_window.upgrade_all.popup.title'],
body=self.locale_keys['manage_window.upgrade_all.popup.body'],
locale_keys=self.locale_keys):
pwd = None
if not is_root() and requires_root:
pwd, ok = ask_root_password(self.locale_keys)
if not ok:
return
self._handle_console_option(True)
self._begin_action(self.locale_keys['manage_window.status.upgrading'])
self.thread_update.apps_to_update = to_update
self.thread_update.root_password = pwd
self.thread_update.start()
def _finish_update_selected(self, success: bool, updated: int):
self.finish_action()
if success:
if self._can_notify_user():
util.notify_user('{} {}'.format(updated, self.locale_keys['notification.update_selected.success']))
self.refresh_apps()
if self.tray_icon:
self.tray_icon.verify_updates()
else:
if self._can_notify_user():
util.notify_user(self.locale_keys['notification.update_selected.failed'])
self.bt_upgrade.setEnabled(True)
self.checkbox_console.setChecked(True)
def _update_action_output(self, output: str):
self.textarea_output.appendPlainText(output)
def _begin_action(self, action_label: str, keep_search: bool = False, clear_filters: bool = False):
self.ref_bt_about.setVisible(False)
self.ref_label_updates.setVisible(False)
self.thread_animate_progress.stop = False
self.thread_animate_progress.start()
self.ref_progress_bar.setVisible(True)
self.label_status.setText(action_label + "...")
self.bt_upgrade.setEnabled(False)
self.bt_refresh.setEnabled(False)
self.checkbox_only_apps.setEnabled(False)
self.table_apps.setEnabled(False)
self.checkbox_updates.setEnabled(False)
if keep_search:
self.ref_toolbar_search.setVisible(True)
else:
self.ref_toolbar_search.setVisible(False)
if clear_filters:
self._update_type_filters(set())
else:
self.extra_filters.setEnabled(False)
def finish_action(self):
self.ref_bt_about.setVisible(True)
self.ref_progress_bar.setVisible(False)
self.ref_label_updates.setVisible(True)
self.thread_animate_progress.stop = True
self.progress_bar.setValue(0)
self.bt_refresh.setEnabled(True)
self.checkbox_only_apps.setEnabled(True)
self.table_apps.setEnabled(True)
self.input_search.setEnabled(True)
self.label_status.setText('')
self.ref_toolbar_search.setVisible(True)
self.ref_toolbar_search.setEnabled(True)
self.extra_filters.setEnabled(True)
self.checkbox_updates.setEnabled(True)
def downgrade_app(self, app: ApplicationView):
pwd = None
requires_root = self.manager.requires_root('downgrade', self.table_apps.get_selected_app().model)
if not is_root() and requires_root:
pwd, ok = ask_root_password(self.locale_keys)
if not ok:
return
self._handle_console_option(True)
self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.downgrading'], app.model.base_data.name))
self.thread_downgrade.app = app
self.thread_downgrade.root_password = pwd
self.thread_downgrade.start()
def get_app_info(self, app: dict):
self._handle_console_option(False)
self._begin_action(self.locale_keys['manage_window.status.info'])
self.thread_get_info.app = app
self.thread_get_info.start()
def get_app_history(self, app: dict):
self._handle_console_option(False)
self._begin_action(self.locale_keys['manage_window.status.history'])
self.thread_get_history.app = app
self.thread_get_history.start()
def _finish_get_info(self, app_info: dict):
self.finish_action()
self.change_update_state(change_filters=False)
dialog_info = InfoDialog(app=app_info, icon_cache=self.icon_cache, locale_keys=self.locale_keys, screen_size=self.screen_size)
dialog_info.exec_()
def _finish_get_history(self, app: dict):
self.finish_action()
self.change_update_state(change_filters=False)
if app.get('error'):
self._handle_console_option(True)
self.textarea_output.appendPlainText(app['error'])
self.checkbox_console.setChecked(True)
else:
dialog_history = HistoryDialog(app, self.icon_cache, self.locale_keys)
dialog_history.exec_()
def _begin_search(self, word):
self._handle_console_option(False)
self.ref_checkbox_only_apps.setVisible(False)
self.ref_checkbox_updates.setVisible(False)
self.filter_updates = False
self._begin_action('{}{}'.format(self.locale_keys['manage_window.status.searching'], '"{}"'.format(word) if word else ''), clear_filters=True)
def search(self):
word = self.input_search.text().strip()
if word:
self._begin_search(word)
self.thread_search.word = word
self.thread_search.start()
def _finish_search(self, apps_found: List[Application]):
self.finish_action()
self.ref_bt_upgrade.setVisible(False)
self.update_apps(apps_found, update_check_enabled=False)
def install_app(self, app: ApplicationView):
pwd = None
requires_root = self.manager.requires_root('install', app.model)
if not is_root() and requires_root:
pwd, ok = ask_root_password(self.locale_keys)
if not ok:
return
self._handle_console_option(True)
self._begin_action('{} {}'.format(self.locale_keys['manage_window.status.installing'], app.model.base_data.name))
self.thread_install.app = app
self.thread_install.root_password = pwd
self.thread_install.start()
def _finish_install(self, app: ApplicationView):
self.input_search.setText('')
self.finish_action()
if app:
if self._can_notify_user():
util.notify_user(msg='{} ({}) {}'.format(app.model.base_data.name, app.model.get_type(), self.locale_keys['installed']))
self.refresh_apps()
else:
if self._can_notify_user():
util.notify_user('{}: {}'.format(app.model.base_data.name, self.locale_keys['notification.install.failed']))
self.checkbox_console.setChecked(True)
def _update_progress(self, value: int):
self.progress_bar.setValue(value)