This commit is contained in:
parent
3c849568b4
commit
9b2322f63c
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,2 +0,0 @@
|
||||
/target
|
||||
/.idea
|
@ -1,12 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="EMPTY_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/bin/apt" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="EMPTY_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/bin/gui" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="EMPTY_MODULE" version="4">
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/lib" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/target" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/bin-gui.iml" filepath="$PROJECT_DIR$/.idea/bin-gui.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/bin-apt.iml" filepath="$PROJECT_DIR$/.idea/bin-apt.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/lib.iml" filepath="$PROJECT_DIR$/.idea/lib.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
@ -1,6 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@ -1,198 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="AutoImportSettings">
|
||||
<option name="autoReloadType" value="ALL" />
|
||||
</component>
|
||||
<component name="CargoProjects">
|
||||
<cargoProject FILE="$PROJECT_DIR$/Cargo.toml" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="df2ca9e1-e07d-43f4-bc68-0a6113fc1fa2" name="Changes" comment="Fix scrolling">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/src/bin/gui/apt_update_page/process.rs" beforeDir="false" afterPath="$PROJECT_DIR$/src/bin/gui/apt_update_page/process.rs" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
||||
</component>
|
||||
<component name="FileTemplateManagerImpl">
|
||||
<option name="RECENT_TEMPLATES">
|
||||
<list>
|
||||
<option value="Rust File" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="Git.Settings">
|
||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
||||
</component>
|
||||
<component name="MacroExpansionManager">
|
||||
<option name="directoryName" value="rqc3l2bj" />
|
||||
</component>
|
||||
<component name="ProjectColorInfo">{
|
||||
"associatedIndex": 8
|
||||
}</component>
|
||||
<component name="ProjectId" id="2iTBzsng9KVBd9K88mZWsKzuiQ1" />
|
||||
<component name="ProjectLevelVcsManager">
|
||||
<ConfirmationsSetting value="2" id="Add" />
|
||||
</component>
|
||||
<component name="ProjectViewState">
|
||||
<option name="hideEmptyMiddlePackages" value="true" />
|
||||
<option name="showLibraryContents" value="true" />
|
||||
</component>
|
||||
<component name="PropertiesComponent">{
|
||||
"keyToString": {
|
||||
"ASKED_ADD_EXTERNAL_FILES": "true",
|
||||
"Cargo.Build all.executor": "Run",
|
||||
"Cargo.Run apt_update.executor": "Run",
|
||||
"Cargo.Run pikman-update-manager.executor": "Run",
|
||||
"Cargo.Test pikman-update-manager.executor": "Run",
|
||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
||||
"RunOnceActivity.rust.reset.selective.auto.import": "true",
|
||||
"git-widget-placeholder": "main",
|
||||
"last_opened_file_path": "/home/ward/RustroverProjects/fedora-kernel-manager",
|
||||
"node.js.detected.package.eslint": "true",
|
||||
"node.js.selected.package.eslint": "(autodetect)",
|
||||
"nodejs_package_manager_path": "npm",
|
||||
"org.rust.cargo.project.model.PROJECT_DISCOVERY": "true",
|
||||
"org.rust.cargo.project.model.impl.CargoExternalSystemProjectAware.subscribe.first.balloon": "",
|
||||
"org.rust.disableDetachedFileInspection/home/ward/RustroverProjects/pkg-pikman-update-manager/src/apt_update_progress_socket/lib.rs": "true",
|
||||
"org.rust.first.attach.projects": "true"
|
||||
}
|
||||
}</component>
|
||||
<component name="RecentsManager">
|
||||
<key name="CopyFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/src/lib" />
|
||||
<recent name="$PROJECT_DIR$/src" />
|
||||
</key>
|
||||
<key name="MoveFile.RECENT_KEYS">
|
||||
<recent name="$PROJECT_DIR$/src/lib" />
|
||||
<recent name="$PROJECT_DIR$/src/bin/apt" />
|
||||
<recent name="$PROJECT_DIR$/src/bin/gui" />
|
||||
<recent name="$PROJECT_DIR$/src" />
|
||||
<recent name="$PROJECT_DIR$" />
|
||||
</key>
|
||||
</component>
|
||||
<component name="RunManager" selected="Cargo.Run pikman-update-manager">
|
||||
<configuration name="Build all" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="build" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<envs />
|
||||
<option name="emulateTerminal" value="true" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
<configuration name="Run pikman-update-manager" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="run --package pikman-update-manager --bin pikman-update-manager" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<envs />
|
||||
<option name="emulateTerminal" value="true" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
<configuration name="Test pikman-update-manager" type="CargoCommandRunConfiguration" factoryName="Cargo Command">
|
||||
<option name="command" value="test --workspace" />
|
||||
<option name="workingDirectory" value="file://$PROJECT_DIR$" />
|
||||
<envs />
|
||||
<option name="emulateTerminal" value="true" />
|
||||
<option name="channel" value="DEFAULT" />
|
||||
<option name="requiredFeatures" value="true" />
|
||||
<option name="allFeatures" value="false" />
|
||||
<option name="withSudo" value="false" />
|
||||
<option name="buildTarget" value="REMOTE" />
|
||||
<option name="backtrace" value="SHORT" />
|
||||
<option name="isRedirectInput" value="false" />
|
||||
<option name="redirectInputPath" value="" />
|
||||
<method v="2">
|
||||
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
|
||||
</method>
|
||||
</configuration>
|
||||
<list>
|
||||
<item itemvalue="Cargo.Build all" />
|
||||
<item itemvalue="Cargo.Run pikman-update-manager" />
|
||||
<item itemvalue="Cargo.Test pikman-update-manager" />
|
||||
</list>
|
||||
</component>
|
||||
<component name="RustProjectSettings">
|
||||
<option name="toolchainHomeDirectory" value="$USER_HOME$/.cargo/bin" />
|
||||
</component>
|
||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="application-level" UseSingleDictionary="true" transferred="true" />
|
||||
<component name="TaskManager">
|
||||
<task active="true" id="Default" summary="Default task">
|
||||
<changelist id="df2ca9e1-e07d-43f4-bc68-0a6113fc1fa2" name="Changes" comment="" />
|
||||
<created>1719504951223</created>
|
||||
<option name="number" value="Default" />
|
||||
<option name="presentableId" value="Default" />
|
||||
<updated>1719504951223</updated>
|
||||
<workItem from="1719504952240" duration="1363000" />
|
||||
<workItem from="1719510804090" duration="7000" />
|
||||
<workItem from="1719609702948" duration="9756000" />
|
||||
<workItem from="1719635307639" duration="18000" />
|
||||
<workItem from="1719797505365" duration="10000" />
|
||||
<workItem from="1719797522707" duration="698000" />
|
||||
<workItem from="1719847278228" duration="8841000" />
|
||||
<workItem from="1719872581190" duration="4000" />
|
||||
<workItem from="1720090281244" duration="5582000" />
|
||||
<workItem from="1720289925469" duration="7265000" />
|
||||
<workItem from="1720301553869" duration="12211000" />
|
||||
<workItem from="1720423242486" duration="3396000" />
|
||||
<workItem from="1720476457389" duration="7123000" />
|
||||
<workItem from="1720502207843" duration="3142000" />
|
||||
<workItem from="1720556059466" duration="12649000" />
|
||||
<workItem from="1720594302708" duration="52000" />
|
||||
<workItem from="1720597532937" duration="7050000" />
|
||||
<workItem from="1720648284536" duration="9271000" />
|
||||
<workItem from="1720657757166" duration="11187000" />
|
||||
<workItem from="1720668957580" duration="53000" />
|
||||
<workItem from="1720669130008" duration="560000" />
|
||||
<workItem from="1721340242546" duration="6495000" />
|
||||
<workItem from="1721388775654" duration="8802000" />
|
||||
<workItem from="1721415142159" duration="10501000" />
|
||||
<workItem from="1721723508115" duration="1009000" />
|
||||
<workItem from="1721726491372" duration="189000" />
|
||||
<workItem from="1721726689334" duration="811000" />
|
||||
<workItem from="1721763208718" duration="1000" />
|
||||
<workItem from="1721937337342" duration="20000" />
|
||||
<workItem from="1723284635393" duration="2171000" />
|
||||
<workItem from="1723389702463" duration="2000" />
|
||||
<workItem from="1724431821562" duration="113000" />
|
||||
<workItem from="1724477728278" duration="2367000" />
|
||||
</task>
|
||||
<task id="LOCAL-00001" summary="Fix scrolling">
|
||||
<option name="closed" value="true" />
|
||||
<created>1723286179091</created>
|
||||
<option name="number" value="00001" />
|
||||
<option name="presentableId" value="LOCAL-00001" />
|
||||
<option name="project" value="LOCAL" />
|
||||
<updated>1723286179091</updated>
|
||||
</task>
|
||||
<option name="localTasksCounter" value="2" />
|
||||
<servers />
|
||||
</component>
|
||||
<component name="TypeScriptGeneratedFilesManager">
|
||||
<option name="version" value="3" />
|
||||
</component>
|
||||
<component name="VcsManagerConfiguration">
|
||||
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
|
||||
<MESSAGE value="Fix scrolling" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value="Fix scrolling" />
|
||||
</component>
|
||||
</project>
|
2299
Cargo.lock
generated
2299
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
40
Cargo.toml
40
Cargo.toml
@ -1,40 +0,0 @@
|
||||
[package]
|
||||
name = "pikman-update-manager"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
[lib]
|
||||
name = "pika_unixsocket_tools"
|
||||
path = "src/lib/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "pikman-update-manager"
|
||||
path = "src/bin/gui/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "apt_update"
|
||||
path = "src/bin/apt/apt_update/main.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "apt_full_upgrade"
|
||||
path = "src/bin/apt/apt_full_upgrade/main.rs"
|
||||
|
||||
[dependencies]
|
||||
adw = { version = "0.7.0", package = "libadwaita", features = ["v1_4"] }
|
||||
gtk = { version = "0.9.0", package = "gtk4", features = ["v4_12"] }
|
||||
async-channel = "2.1.1"
|
||||
rust-i18n = "3.0.1"
|
||||
rust-apt = { git = "https://gitlab.com/volian/rust-apt" }
|
||||
tokio = { version = "1", features = ["full"] }
|
||||
tokio-uds = "0.2"
|
||||
serde = { version = "1.0.203", features = ["derive"] }
|
||||
serde_json = "1.0.118"
|
||||
async-trait = "0.1.80"
|
||||
futures = "0.3.30"
|
||||
pretty-bytes = "0.2.2"
|
||||
crossbeam-utils = "0.8.20"
|
||||
chrono = "0.4.38"
|
||||
lock_api = "0.4.2"
|
||||
libflatpak = { version = "0.5.0", package = "libflatpak", features = ["v1_11_1"] }
|
||||
|
@ -1,24 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<schemalist>
|
||||
<schema id="com.github.pikaos-linux.pikmanupdatemanager" path="/com/github/pikaos-linux/pikmanupdatemanager/">
|
||||
<key name="window-width" type="i">
|
||||
<default>1400</default>
|
||||
<summary>Default window width</summary>
|
||||
</key>
|
||||
<key name="window-height" type="i">
|
||||
<default>700</default>
|
||||
<summary>Default window height</summary>
|
||||
</key>
|
||||
<key name="is-maximized" type="b">
|
||||
<default>false</default>
|
||||
<summary>Default window maximized behaviour</summary>
|
||||
</key>
|
||||
<key type="b" name="startup-show">
|
||||
<default>true</default>
|
||||
<summary>Show PikaOS Welcome on startup.</summary>
|
||||
<description>
|
||||
Show PikaOS Welcome on startup.
|
||||
</description>
|
||||
</key>
|
||||
</schema>
|
||||
</schemalist>
|
@ -1,91 +0,0 @@
|
||||
#! /bin/python3
|
||||
|
||||
import socket
|
||||
import os
|
||||
|
||||
import sys
|
||||
import time
|
||||
|
||||
import apt_pkg
|
||||
|
||||
import apt
|
||||
import apt.progress.base
|
||||
|
||||
def get_change(current, total):
|
||||
if current == total:
|
||||
return 100.0
|
||||
try:
|
||||
return float("{:.1f}".format(((current * 100) / total)))
|
||||
except ZeroDivisionError:
|
||||
return 0.0
|
||||
|
||||
class UpdateProgressSocket(apt.progress.base.AcquireProgress):
|
||||
# Init
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
# Start
|
||||
def start(self):
|
||||
self.current_bytes = 0
|
||||
self.total_bytes = 0
|
||||
print("Starting APT Cache Update.")
|
||||
return super().start()
|
||||
|
||||
# Stop
|
||||
def stop(self):
|
||||
print("\nAPT Cache Update Complete!")
|
||||
return super().stop()
|
||||
|
||||
# Progrss pulse
|
||||
def pulse(self, owner):
|
||||
# Calculate current progress percentage
|
||||
progress_percent = get_change(self.current_bytes, self.total_bytes)
|
||||
|
||||
# apt_update_progress ipc sock
|
||||
socket_path = "/tmp/pika_apt_update.sock"
|
||||
|
||||
# Create a Unix domain socket
|
||||
with socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) as client:
|
||||
client.connect(socket_path)
|
||||
# Send percentage to socket as UTF-8
|
||||
client.sendall(str(progress_percent).encode('utf-8'))
|
||||
|
||||
#response = client.recv(1024)
|
||||
#print(f"Received: {response.decode('utf-8')}")
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def fail(self, item):
|
||||
print("Failure at: %s %s" % (item.uri, item.shortdesc))
|
||||
|
||||
def fetch(self, item):
|
||||
print("Fetch: %s %s" % (item.uri, item.shortdesc))
|
||||
|
||||
def ims_hit(self, item):
|
||||
print("Download: %s %s" % (item.uri, item.shortdesc))
|
||||
|
||||
def media_change(self, medium, drive):
|
||||
print(f"Please insert medium {medium} in drive {drive}")
|
||||
sys.stdin.readline()
|
||||
# return False
|
||||
|
||||
def update_cache():
|
||||
# First of all, open the cache
|
||||
cache = apt.Cache()
|
||||
# Now, lets update the package list
|
||||
cache.update(UpdateProgressSocket())
|
||||
# We need to re-open the cache because it needs to read the package list
|
||||
cache.open(None)
|
||||
# We need to re-open the cache because it needs to read the package list
|
||||
for pkg in cache:
|
||||
if pkg.is_upgradable:
|
||||
print(f"{pkg.name} ({pkg.installed.version} -> {pkg.candidate.version})")
|
||||
|
||||
def process(data):
|
||||
# Echo the input data
|
||||
return data
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
update_cache()
|
@ -1,84 +0,0 @@
|
||||
{
|
||||
"application_name": "Pikman Update Manager",
|
||||
"developer_name": "Cosmo",
|
||||
"installed_version_badge_text": "Installed",
|
||||
"candidate_version_badge_text": "Upgradable",
|
||||
"arch_label_label": "Arch",
|
||||
"mark_for_update": "Mark This Package for Upgrade",
|
||||
"description_button_label": "Description",
|
||||
"extra_info_page_button_label": "Additional Info",
|
||||
"uris_page_button_label": "Download URIs",
|
||||
"changelog_page_button_label": "Changelog (todo)",
|
||||
"extra_info_maintainer": "Maintainer",
|
||||
"extra_info_download_size": "Download Size",
|
||||
"extra_info_installed_size": "Size on Disk",
|
||||
"update_status_error_perms": "Unknown Error! (Likely Permission Denied)",
|
||||
"apt_update_dialog_heading": "APT Cache Update",
|
||||
"apt_update_dialog_retry_label": "Retry",
|
||||
"select_button_deselect_all": "De-Select All",
|
||||
"select_button_select_all": "Select All",
|
||||
"update_button_label": "Commit Upgrade",
|
||||
"installed_version_to_be_installed": "Not Installed Yet",
|
||||
"apt_pkg_property_unknown": "Unknown",
|
||||
"apt_update_dialog_status_failed": "APT Cache Update: Failed!",
|
||||
"excluded_updates_alert_dialog_heading": "Alert: Partial Upgrade",
|
||||
"excluded_updates_alert_dialog_body": "It seems you have deselected some upgrades.\nThis is generally not recommended, and APT partial update security systems might not respect your input.",
|
||||
"excluded_updates_alert_dialog_cancel_label": "Cancel",
|
||||
"excluded_updates_alert_continue_label": "Continue",
|
||||
"gui_changes_emu_msg_0": "The following opreations will be sent to the root process:",
|
||||
"gui_changes_emu_msg_upgrading": "Upgrading",
|
||||
"gui_changes_emu_msg_installing": "Installing",
|
||||
"gui_changes_emu_msg_downgrading": "Downgrading",
|
||||
"gui_changes_emu_msg_removing": "Uninstalling",
|
||||
"package_count_upgrade_badge_label": "Packages to Upgrade",
|
||||
"package_count_install_badge_label": "New Packages to Install",
|
||||
"package_count_downgrade_badge_label": "Packages to Downgrade",
|
||||
"package_count_remove_badge_label": "Packages to Uninstall",
|
||||
"total_download_size_badge_label": "Total Download Size",
|
||||
"total_installed_size_badge_label": "Total Size on Disk",
|
||||
"apt_confirm_dialog_heading": "APT Upgrade Transaction: Please Review",
|
||||
"apt_confirm_dialog_body": "The following changes are going to be made:",
|
||||
"apt_confirm_dialog_cancel_label": "Decline",
|
||||
"apt_confirm_dialog_confirm_label": "Confirm & Accept",
|
||||
"apt_remove_confirm_dialog_heading": "APT Removal Confirmation: Please Review",
|
||||
"apt_remove_confirm_dialog_body": "The Upgrade wants to remove the following packages: (This Might be extremely Dangerous!)",
|
||||
"apt_remove_confirm_dialog_cancel_label": "Cancel",
|
||||
"apt_remove_confirm_dialog_confirm_label": "Allow & Confirm",
|
||||
"upgrade_status_error_perms": "Unknown Error! (Likely Permission Denied)",
|
||||
"apt_upgrade_dialog_heading": "APT Upgrade Transaction: Hang Tight!",
|
||||
"apt_upgrade_dialog_ok_label": "OK",
|
||||
"apt_upgrade_dialog_open_log_file_label": "Open Apt Log File",
|
||||
"apt_upgrade_dialog_status_successful": "APT Upgrade Transaction Successful!",
|
||||
"apt_upgrade_dialog_status_failed": "APT Upgrade Transaction Failed!",
|
||||
"banner_text_no_internet": "Warning: No Internet Connection!",
|
||||
"refresh_button_tooltip_text": "Refresh Opened Page",
|
||||
"apt_update_page_title": "Native Updates (APT)",
|
||||
"flatpak_update_page_title" : "Flatpak Updates",
|
||||
"apt_packages_no_viewport_page_title": "All Native APT Packages are Up to date!",
|
||||
"summary_button_label": "Summary",
|
||||
"flatpak_extra_info_ref_name": "Ref Codename",
|
||||
"flatpak_extra_info_download_size": "Download Size",
|
||||
"flatpak_extra_info_installed_size": "Size on Disk",
|
||||
"remote_label_label": "Remote",
|
||||
"flatpak_update_dialog_heading": "Flatpak Appstream Sync",
|
||||
"flatpak_update_dialog_retry_label": "Retry",
|
||||
"flatpak_update_dialog_status_failed": "Flatpak Appstream Sync: Failed",
|
||||
"flatpak_packages_no_viewport_page_title": "All Flatpak Packages are Up to date!",
|
||||
"flatpak_type_user": "User",
|
||||
"flatpak_type_system": "System",
|
||||
"flatpak_ref": "Flatpak Ref",
|
||||
"flatpak_status": "Status",
|
||||
"flatpak_transaction_bytes_transferred": "Downloaded",
|
||||
"flatpak_transaction_installed_size": "Saved to Disk",
|
||||
"flatpak_confirm_dialog_body": "The following changes are going to be made:",
|
||||
"system_flatref_count_badge_label": "System Flatpaks to Update",
|
||||
"user_flatref_count_badge_label": "User Flatpaks to Update",
|
||||
"flatpak_confirm_dialog_heading": "Flatpak Update Transaction: Please Review",
|
||||
"flatpak_confirm_dialog_cancel_label": "Decline",
|
||||
"flatpak_confirm_dialog_confirm_label": "Confirm & Accept",
|
||||
"flatpak_transaction_dialog_heading": "Flatpak Update Transaction: Hang Tight!",
|
||||
"flatpak_transaction_dialog_ok_label": "OK",
|
||||
"flatpak_transaction_dialog_open_log_file_label": "Open Flatpak Log File",
|
||||
"flatpak_transaction_dialog_status_successful": "Flatpak Update Transaction: Successful!",
|
||||
"flatpak_transaction_dialog_status_failed": "Flatpak Update Transaction: Failed!"
|
||||
}
|
@ -1,89 +0,0 @@
|
||||
use pika_unixsocket_tools::apt_install_progress_socket::AptInstallProgressSocket;
|
||||
use pika_unixsocket_tools::apt_update_progress_socket::AptUpdateProgressSocket;
|
||||
use pika_unixsocket_tools::pika_unixsocket_tools::*;
|
||||
use rust_apt::cache::Upgrade;
|
||||
use rust_apt::new_cache;
|
||||
use rust_apt::progress::{AcquireProgress, InstallProgress};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
fn main() {
|
||||
let percent_socket_path = "/tmp/pika_apt_upgrade_percent.sock";
|
||||
let status_socket_path = "/tmp/pika_apt_upgrade_status.sock";
|
||||
let json_file_path = "/tmp/pika-apt-exclusions.json";
|
||||
let mut excluded_updates_vec: Vec<String> = Vec::new();
|
||||
|
||||
if std::path::Path::new(json_file_path).exists() {
|
||||
let data = std::fs::read_to_string(json_file_path).expect("Unable to read file");
|
||||
let json: serde_json::Value =
|
||||
serde_json::from_str(&data).expect("JSON was not well-formatted");
|
||||
|
||||
if let serde_json::Value::Array(exclusions) = &json["exclusions"] {
|
||||
for exclusion in exclusions {
|
||||
match exclusion["package"].as_str() {
|
||||
Some(v) => {
|
||||
excluded_updates_vec.push(v.to_owned());
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let apt_cache = new_cache!().unwrap();
|
||||
|
||||
apt_cache.upgrade(Upgrade::FullUpgrade).unwrap();
|
||||
|
||||
let apt_upgrade_cache = if excluded_updates_vec.is_empty() {
|
||||
apt_cache
|
||||
} else {
|
||||
let apt_upgrade_cache = new_cache!().unwrap();
|
||||
for change in apt_cache.get_changes(false) {
|
||||
if !excluded_updates_vec
|
||||
.iter()
|
||||
.any(|e| change.name().contains(e))
|
||||
{
|
||||
let pkg = apt_upgrade_cache.get(change.name()).unwrap();
|
||||
if change.marked_upgrade() || change.marked_install() || change.marked_downgrade() {
|
||||
pkg.mark_install(true, false);
|
||||
} else if change.marked_delete() {
|
||||
pkg.mark_delete(false);
|
||||
}
|
||||
pkg.protect();
|
||||
}
|
||||
}
|
||||
apt_upgrade_cache
|
||||
};
|
||||
|
||||
apt_upgrade_cache.resolve(true).unwrap();
|
||||
|
||||
let mut acquire_progress = AcquireProgress::new(AptUpdateProgressSocket::new(
|
||||
percent_socket_path,
|
||||
status_socket_path,
|
||||
));
|
||||
let mut install_progress = InstallProgress::new(AptInstallProgressSocket::new(
|
||||
percent_socket_path,
|
||||
status_socket_path,
|
||||
));
|
||||
|
||||
apt_upgrade_cache.resolve(true).unwrap();
|
||||
|
||||
match apt_upgrade_cache.get_archives(&mut acquire_progress) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
panic!("{}", e.to_string())
|
||||
}
|
||||
};
|
||||
|
||||
match apt_upgrade_cache.do_install(&mut install_progress) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_failed_to_socket(percent_socket_path));
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_failed_to_socket(status_socket_path));
|
||||
panic!("{}", e.to_string())
|
||||
}
|
||||
};
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
use pika_unixsocket_tools::apt_update_progress_socket::AptUpdateProgressSocket;
|
||||
use pika_unixsocket_tools::pika_unixsocket_tools::*;
|
||||
use rust_apt::new_cache;
|
||||
use rust_apt::progress::AcquireProgress;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
fn main() {
|
||||
let update_cache = new_cache!().unwrap();
|
||||
let percent_socket_path = "/tmp/pika_apt_update_percent.sock";
|
||||
let status_socket_path = "/tmp/pika_apt_update_status.sock";
|
||||
match update_cache.update(&mut AcquireProgress::new(AptUpdateProgressSocket::new(
|
||||
percent_socket_path,
|
||||
status_socket_path,
|
||||
))) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_failed_to_socket(percent_socket_path));
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_failed_to_socket(status_socket_path));
|
||||
panic!("{}", e.to_string())
|
||||
}
|
||||
};
|
||||
}
|
@ -1,597 +0,0 @@
|
||||
use std::{cell::RefCell, sync::OnceLock};
|
||||
|
||||
use adw::*;
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use glib::{clone, subclass::Signal, Properties};
|
||||
use gtk::*;
|
||||
use pretty_bytes::converter::convert;
|
||||
use std::env;
|
||||
|
||||
// ANCHOR: custom_button
|
||||
// Object holding the state
|
||||
#[derive(Properties, Default)]
|
||||
#[properties(wrapper_type = super::AptPackageRow)]
|
||||
pub struct AptPackageRow {
|
||||
#[property(get, set)]
|
||||
package_name: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
package_arch: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
package_installed_version: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
package_candidate_version: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
package_description: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
package_source_uri: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
package_maintainer: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
package_size: RefCell<u64>,
|
||||
#[property(get, set)]
|
||||
package_installed_size: RefCell<u64>,
|
||||
#[property(get, set)]
|
||||
package_marked: RefCell<bool>,
|
||||
}
|
||||
// ANCHOR_END: custom_button
|
||||
|
||||
// The central trait for subclassing a GObject
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for AptPackageRow {
|
||||
const NAME: &'static str = "AptPackageRow";
|
||||
type Type = super::AptPackageRow;
|
||||
type ParentType = ExpanderRow;
|
||||
}
|
||||
|
||||
// ANCHOR: object_impl
|
||||
// Trait shared by all GObjects
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for AptPackageRow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
|
||||
SIGNALS.get_or_init(|| {
|
||||
vec![
|
||||
Signal::builder("checkbutton-toggled").build(),
|
||||
Signal::builder("checkbutton-untoggled").build(),
|
||||
]
|
||||
})
|
||||
}
|
||||
fn constructed(&self) {
|
||||
let current_locale = match env::var_os("LANG") {
|
||||
Some(v) => v
|
||||
.into_string()
|
||||
.unwrap()
|
||||
.chars()
|
||||
.take_while(|&ch| ch != '.')
|
||||
.collect::<String>(),
|
||||
None => panic!("$LANG is not set"),
|
||||
};
|
||||
rust_i18n::set_locale(¤t_locale);
|
||||
|
||||
self.parent_constructed();
|
||||
|
||||
// Bind label to number
|
||||
// `SYNC_CREATE` ensures that the label will be immediately set
|
||||
let obj = self.obj();
|
||||
|
||||
let prefix_box = Box::new(Orientation::Vertical, 0);
|
||||
|
||||
let expandable_box = Box::new(Orientation::Vertical, 0);
|
||||
|
||||
obj.connect_package_name_notify(clone!(
|
||||
#[weak]
|
||||
prefix_box,
|
||||
#[weak]
|
||||
expandable_box,
|
||||
#[strong]
|
||||
obj,
|
||||
move |_| {
|
||||
remove_all_children_from_box(&prefix_box);
|
||||
remove_all_children_from_box(&expandable_box);
|
||||
//
|
||||
let package_name = obj.package_name();
|
||||
let package_arch = obj.package_arch();
|
||||
let package_installed_version = obj.package_installed_version();
|
||||
let package_candidate_version = obj.package_candidate_version();
|
||||
let package_description = obj.package_description();
|
||||
let package_source_uri = obj.package_source_uri();
|
||||
let package_maintainer = obj.package_maintainer();
|
||||
let package_size = obj.package_size();
|
||||
let package_installed_size = obj.package_installed_size();
|
||||
//
|
||||
create_prefix_content(
|
||||
&prefix_box,
|
||||
&package_name,
|
||||
&package_arch,
|
||||
&package_installed_version,
|
||||
&package_candidate_version,
|
||||
);
|
||||
//
|
||||
create_expandable_content(
|
||||
&obj,
|
||||
&expandable_box,
|
||||
package_description,
|
||||
package_source_uri,
|
||||
package_maintainer,
|
||||
package_size,
|
||||
package_installed_size,
|
||||
);
|
||||
}
|
||||
));
|
||||
|
||||
obj.add_prefix(&prefix_box);
|
||||
obj.add_row(&expandable_box);
|
||||
|
||||
let suffix_toggle = CheckButton::builder()
|
||||
.tooltip_text(t!("mark_for_update"))
|
||||
.halign(Align::Center)
|
||||
.valign(Align::Center)
|
||||
.hexpand(false)
|
||||
.vexpand(false)
|
||||
.build();
|
||||
|
||||
suffix_toggle.connect_toggled(clone!(
|
||||
#[weak]
|
||||
obj,
|
||||
#[weak]
|
||||
suffix_toggle,
|
||||
move |_| {
|
||||
if suffix_toggle.is_active() {
|
||||
obj.emit_by_name::<()>("checkbutton-toggled", &[]);
|
||||
} else {
|
||||
obj.emit_by_name::<()>("checkbutton-untoggled", &[]);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
obj.add_suffix(&suffix_toggle);
|
||||
|
||||
let obj = self.obj();
|
||||
obj.bind_property("package-marked", &suffix_toggle, "active")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.build();
|
||||
|
||||
// turn on by default
|
||||
obj.set_property("package-marked", true)
|
||||
}
|
||||
}
|
||||
// Trait shared by all widgets
|
||||
impl WidgetImpl for AptPackageRow {}
|
||||
|
||||
// Trait shared by all buttons
|
||||
// Trait shared by all buttons
|
||||
|
||||
impl ListBoxRowImpl for AptPackageRow {}
|
||||
impl PreferencesRowImpl for AptPackageRow {}
|
||||
impl ExpanderRowImpl for AptPackageRow {}
|
||||
|
||||
fn create_version_badge(installed_version: &str, candidate_version: &str) -> ListBox {
|
||||
let (base_version, installed_diff, candidate_diff) =
|
||||
get_diff_by_prefix(installed_version, candidate_version);
|
||||
|
||||
let badge_box = Box::builder()
|
||||
.halign(Align::Start)
|
||||
.hexpand(false)
|
||||
.orientation(Orientation::Horizontal)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(5)
|
||||
.margin_top(5)
|
||||
.build();
|
||||
|
||||
let installed_version_box = Box::builder()
|
||||
.halign(Align::Start)
|
||||
.hexpand(false)
|
||||
.orientation(Orientation::Horizontal)
|
||||
.tooltip_text(t!("installed_version_badge_text"))
|
||||
.build();
|
||||
|
||||
let installed_version_base_version_label = Label::builder()
|
||||
.label(format!(
|
||||
"{}: {}",
|
||||
t!("installed_version_badge_text"),
|
||||
&base_version
|
||||
))
|
||||
.valign(Align::Center)
|
||||
.halign(Align::Start)
|
||||
.hexpand(false)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
|
||||
let installed_diff_label = Label::builder()
|
||||
.label(installed_diff)
|
||||
.valign(Align::Center)
|
||||
.halign(Align::Start)
|
||||
.hexpand(false)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
installed_diff_label.add_css_class("destructive-color-text");
|
||||
|
||||
installed_version_box.append(&installed_version_base_version_label.clone());
|
||||
installed_version_box.append(&installed_diff_label);
|
||||
|
||||
let label_separator = Separator::builder().margin_start(5).margin_end(5).build();
|
||||
|
||||
let candidate_version_box = Box::builder()
|
||||
.halign(Align::Start)
|
||||
.hexpand(false)
|
||||
.orientation(Orientation::Horizontal)
|
||||
.tooltip_text(t!("candidate_version_badge_text"))
|
||||
.build();
|
||||
|
||||
let candidate_version_base_version_label = Label::builder()
|
||||
.label(format!(
|
||||
"{}: {}",
|
||||
t!("candidate_version_badge_text"),
|
||||
&base_version
|
||||
))
|
||||
.valign(Align::Center)
|
||||
.halign(Align::Start)
|
||||
.hexpand(false)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
|
||||
let candidate_diff_label = Label::builder()
|
||||
.label(candidate_diff)
|
||||
.valign(Align::Center)
|
||||
.halign(Align::Start)
|
||||
.hexpand(false)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
candidate_diff_label.add_css_class("success-color-text");
|
||||
|
||||
candidate_version_box.append(&candidate_version_base_version_label);
|
||||
candidate_version_box.append(&candidate_diff_label);
|
||||
|
||||
badge_box.append(&installed_version_box);
|
||||
badge_box.append(&label_separator);
|
||||
badge_box.append(&candidate_version_box);
|
||||
|
||||
let boxedlist = ListBox::builder()
|
||||
.selection_mode(SelectionMode::None)
|
||||
.halign(Align::Start)
|
||||
.valign(Align::End)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(10)
|
||||
.build();
|
||||
|
||||
boxedlist.add_css_class("boxed-list");
|
||||
boxedlist.append(&badge_box);
|
||||
boxedlist
|
||||
}
|
||||
|
||||
fn create_arch_badge(arch: &str) -> ListBox {
|
||||
let arch_label = Label::builder()
|
||||
.halign(Align::Start)
|
||||
.hexpand(false)
|
||||
.label(format!("{}: {}", t!("arch_label_label"), arch))
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(5)
|
||||
.margin_top(5)
|
||||
.build();
|
||||
|
||||
let boxedlist = ListBox::builder()
|
||||
.selection_mode(SelectionMode::None)
|
||||
.halign(Align::Start)
|
||||
.valign(Align::End)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(10)
|
||||
.build();
|
||||
|
||||
boxedlist.add_css_class("boxed-list");
|
||||
boxedlist.append(&arch_label);
|
||||
boxedlist
|
||||
}
|
||||
|
||||
fn remove_all_children_from_box(parent: >k::Box) {
|
||||
while let Some(child) = parent.last_child() {
|
||||
parent.remove(&child);
|
||||
}
|
||||
}
|
||||
|
||||
fn create_prefix_content(
|
||||
prefix_box: >k::Box,
|
||||
package_name: &str,
|
||||
package_arch: &str,
|
||||
package_installed_version: &str,
|
||||
package_candidate_version: &str,
|
||||
) {
|
||||
let package_label = Label::builder()
|
||||
.halign(Align::Start)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(5)
|
||||
.margin_top(5)
|
||||
.label(package_name)
|
||||
.build();
|
||||
package_label.add_css_class("size-20-bold-text");
|
||||
let version_box = Box::new(Orientation::Horizontal, 0);
|
||||
version_box.append(&create_version_badge(
|
||||
package_installed_version,
|
||||
package_candidate_version,
|
||||
));
|
||||
version_box.append(&create_arch_badge(package_arch));
|
||||
prefix_box.append(&package_label);
|
||||
prefix_box.append(&version_box);
|
||||
}
|
||||
|
||||
fn create_expandable_content(
|
||||
apt_package_row: &impl IsA<ExpanderRow>,
|
||||
expandable_box: >k::Box,
|
||||
package_description: String,
|
||||
package_source_uri: String,
|
||||
package_maintainer: String,
|
||||
package_size: u64,
|
||||
package_installed_size: u64,
|
||||
) {
|
||||
let expandable_page_selection_box = Box::builder()
|
||||
.orientation(Orientation::Horizontal)
|
||||
.hexpand(false)
|
||||
.vexpand(false)
|
||||
.halign(Align::Start)
|
||||
.valign(Align::Start)
|
||||
.margin_bottom(10)
|
||||
.margin_top(10)
|
||||
.margin_start(10)
|
||||
.margin_end(10)
|
||||
.build();
|
||||
expandable_page_selection_box.add_css_class("linked");
|
||||
//
|
||||
let description_page_button = ToggleButton::builder()
|
||||
.label(t!("description_button_label"))
|
||||
.active(true)
|
||||
.build();
|
||||
let extra_info_page_button = ToggleButton::builder()
|
||||
.label(t!("extra_info_page_button_label"))
|
||||
.group(&description_page_button)
|
||||
.build();
|
||||
let uris_page_button = ToggleButton::builder()
|
||||
.label(t!("uris_page_button_label"))
|
||||
.group(&description_page_button)
|
||||
.build();
|
||||
let changelog_page_button = ToggleButton::builder()
|
||||
.label(t!("changelog_page_button_label"))
|
||||
// till we find a way to implement
|
||||
.sensitive(false)
|
||||
.group(&description_page_button)
|
||||
.build();
|
||||
expandable_page_selection_box.append(&description_page_button);
|
||||
expandable_page_selection_box.append(&extra_info_page_button);
|
||||
expandable_page_selection_box.append(&uris_page_button);
|
||||
expandable_page_selection_box.append(&changelog_page_button);
|
||||
//
|
||||
expandable_box.append(&expandable_page_selection_box);
|
||||
//
|
||||
let expandable_bin = Bin::builder().hexpand(true).vexpand(true).build();
|
||||
//
|
||||
description_page_button.connect_clicked(clone!(
|
||||
#[strong]
|
||||
expandable_bin,
|
||||
#[strong]
|
||||
description_page_button,
|
||||
move |_| {
|
||||
if description_page_button.is_active() {
|
||||
expandable_bin.set_child(Some(&description_stack_page(&package_description)));
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
extra_info_page_button.connect_clicked(clone!(
|
||||
#[strong]
|
||||
expandable_bin,
|
||||
#[strong]
|
||||
extra_info_page_button,
|
||||
move |_| {
|
||||
if extra_info_page_button.is_active() {
|
||||
expandable_bin.set_child(Some(&extra_info_stack_page(
|
||||
&package_maintainer,
|
||||
package_size,
|
||||
package_installed_size,
|
||||
)));
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
uris_page_button.connect_clicked(clone!(
|
||||
#[strong]
|
||||
expandable_bin,
|
||||
#[strong]
|
||||
uris_page_button,
|
||||
move |_| {
|
||||
if uris_page_button.is_active() {
|
||||
expandable_bin.set_child(Some(&uris_stack_page(&package_source_uri)));
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
apt_package_row.connect_expanded_notify(clone!(
|
||||
#[strong]
|
||||
expandable_bin,
|
||||
#[strong]
|
||||
expandable_box,
|
||||
#[strong]
|
||||
apt_package_row,
|
||||
#[strong]
|
||||
description_page_button,
|
||||
move |_| {
|
||||
if apt_package_row.property("expanded") {
|
||||
description_page_button.set_active(true);
|
||||
description_page_button.emit_by_name::<()>("clicked", &[]);
|
||||
expandable_box.append(&expandable_bin)
|
||||
} else {
|
||||
expandable_box.remove(&expandable_bin)
|
||||
}
|
||||
}
|
||||
));
|
||||
//expandable_bin.add_named(&extra_info_stack_page(package_maintainer, package_size, package_installed_size), Some("extra_info_page"));
|
||||
//
|
||||
}
|
||||
|
||||
fn uris_stack_page(package_source_uri: &str) -> gtk::Box {
|
||||
let uris_content_box = Box::builder()
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.orientation(Orientation::Vertical)
|
||||
.build();
|
||||
let uris_text_buffer = TextBuffer::builder()
|
||||
.text(package_source_uri.to_owned() + "\n")
|
||||
.build();
|
||||
let uris_text_view = TextView::builder()
|
||||
.buffer(&uris_text_buffer)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.margin_top(15)
|
||||
.margin_bottom(15)
|
||||
.margin_start(15)
|
||||
.margin_end(15)
|
||||
.editable(false)
|
||||
.buffer(&uris_text_buffer)
|
||||
.build();
|
||||
uris_content_box.append(&uris_text_view);
|
||||
uris_content_box
|
||||
}
|
||||
|
||||
fn description_stack_page(package_description: &str) -> gtk::Box {
|
||||
let description_content_box = Box::builder()
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.orientation(Orientation::Vertical)
|
||||
.build();
|
||||
let description_text_buffer = TextBuffer::builder()
|
||||
.text(package_description.to_owned() + "\n")
|
||||
.build();
|
||||
let description_text_view = TextView::builder()
|
||||
.buffer(&description_text_buffer)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.margin_top(0)
|
||||
.margin_bottom(10)
|
||||
.margin_start(15)
|
||||
.margin_end(15)
|
||||
.editable(false)
|
||||
.build();
|
||||
description_content_box.append(&description_text_view);
|
||||
description_content_box
|
||||
}
|
||||
|
||||
fn extra_info_stack_page(
|
||||
package_maintainer: &str,
|
||||
package_size: u64,
|
||||
package_installed_size: u64,
|
||||
) -> gtk::Box {
|
||||
let extra_info_badges_content_box = Box::builder()
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.orientation(Orientation::Vertical)
|
||||
.build();
|
||||
let extra_info_badges_size_group = SizeGroup::new(SizeGroupMode::Both);
|
||||
let extra_info_badges_size_group0 = SizeGroup::new(SizeGroupMode::Both);
|
||||
let extra_info_badges_size_group1 = SizeGroup::new(SizeGroupMode::Both);
|
||||
let package_size = package_size as f64;
|
||||
let package_installed_size = package_installed_size as f64;
|
||||
extra_info_badges_content_box.append(&create_color_badge(
|
||||
&t!("extra_info_maintainer").to_string(),
|
||||
package_maintainer,
|
||||
"background-accent-bg",
|
||||
&extra_info_badges_size_group,
|
||||
&extra_info_badges_size_group0,
|
||||
&extra_info_badges_size_group1,
|
||||
));
|
||||
extra_info_badges_content_box.append(&create_color_badge(
|
||||
&t!("extra_info_download_size").to_string(),
|
||||
&convert(package_size),
|
||||
"background-accent-bg",
|
||||
&extra_info_badges_size_group,
|
||||
&extra_info_badges_size_group0,
|
||||
&extra_info_badges_size_group1,
|
||||
));
|
||||
extra_info_badges_content_box.append(&create_color_badge(
|
||||
&t!("extra_info_installed_size").to_string(),
|
||||
&convert(package_installed_size),
|
||||
"background-accent-bg",
|
||||
&extra_info_badges_size_group,
|
||||
&extra_info_badges_size_group0,
|
||||
&extra_info_badges_size_group1,
|
||||
));
|
||||
extra_info_badges_content_box
|
||||
}
|
||||
fn create_color_badge(
|
||||
label0_text: &str,
|
||||
label1_text: &str,
|
||||
css_style: &str,
|
||||
group_size: &SizeGroup,
|
||||
group_size0: &SizeGroup,
|
||||
group_size1: &SizeGroup,
|
||||
) -> ListBox {
|
||||
let badge_box = Box::builder().build();
|
||||
|
||||
let label0 = Label::builder()
|
||||
.label(label0_text)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(1)
|
||||
.margin_top(1)
|
||||
.valign(Align::Center)
|
||||
.halign(Align::Center)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
group_size0.add_widget(&label0);
|
||||
|
||||
let label_separator = Separator::builder().build();
|
||||
|
||||
let label1 = Label::builder()
|
||||
.label(label1_text)
|
||||
.margin_start(3)
|
||||
.margin_end(0)
|
||||
.margin_bottom(1)
|
||||
.margin_top(1)
|
||||
.valign(Align::Center)
|
||||
.halign(Align::Center)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
group_size1.add_widget(&label1);
|
||||
|
||||
label1.add_css_class(css_style);
|
||||
|
||||
badge_box.append(&label0);
|
||||
badge_box.append(&label_separator);
|
||||
badge_box.append(&label1);
|
||||
|
||||
let boxedlist = ListBox::builder()
|
||||
.selection_mode(SelectionMode::None)
|
||||
.halign(Align::Start)
|
||||
.valign(Align::Start)
|
||||
.margin_start(10)
|
||||
.margin_end(10)
|
||||
.margin_bottom(10)
|
||||
.margin_top(10)
|
||||
.build();
|
||||
|
||||
boxedlist.add_css_class("boxed-list");
|
||||
boxedlist.append(&badge_box);
|
||||
group_size.add_widget(&boxedlist);
|
||||
boxedlist
|
||||
}
|
||||
|
||||
pub fn get_diff_by_prefix(xs: &str, ys: &str) -> (String, String, String) {
|
||||
let mut count = String::new();
|
||||
for (x, y) in xs.chars().zip(ys.chars()) {
|
||||
if x == y {
|
||||
count.push(x)
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let count_clone0 = count.clone();
|
||||
return (
|
||||
count_clone0,
|
||||
xs.trim_start_matches(&count.as_str()).to_string(),
|
||||
ys.trim_start_matches(&count.as_str()).to_string(),
|
||||
);
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
mod imp;
|
||||
|
||||
use crate::apt_update_page::AptPackageSocket;
|
||||
use glib::Object;
|
||||
use gtk::glib;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct AptPackageRow(ObjectSubclass<imp::AptPackageRow>)
|
||||
@extends adw::ExpanderRow, gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow,
|
||||
@implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget;
|
||||
}
|
||||
|
||||
impl AptPackageRow {
|
||||
pub fn new(package: AptPackageSocket) -> Self {
|
||||
Object::builder()
|
||||
.property("package-name", package.name)
|
||||
.property("package-arch", package.arch)
|
||||
.property("package-installed-version", package.installed_version)
|
||||
.property("package-candidate-version", package.candidate_version)
|
||||
.property("package-description", package.description)
|
||||
.property("package-source-uri", package.source_uri)
|
||||
.property("package-maintainer", package.maintainer)
|
||||
.property("package-size", package.size)
|
||||
.property("package-installed-size", package.installed_size)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: mod
|
||||
|
||||
impl Default for AptPackageRow {
|
||||
fn default() -> Self {
|
||||
Self::new(AptPackageSocket {
|
||||
name: "name".to_string(),
|
||||
arch: "arch".to_string(),
|
||||
installed_version: "0.0".to_string(),
|
||||
candidate_version: "0.0".to_string(),
|
||||
description: "??".to_string(),
|
||||
source_uri: "??".to_string(),
|
||||
maintainer: "??".to_string(),
|
||||
size: 0,
|
||||
installed_size: 0,
|
||||
is_last: false,
|
||||
})
|
||||
}
|
||||
}
|
@ -1,492 +0,0 @@
|
||||
mod process;
|
||||
|
||||
use crate::apt_package_row::AptPackageRow;
|
||||
use adw::gio::SimpleAction;
|
||||
use adw::prelude::*;
|
||||
use gtk::glib::*;
|
||||
use gtk::*;
|
||||
use pika_unixsocket_tools::pika_unixsocket_tools::*;
|
||||
use rust_apt::cache::*;
|
||||
use rust_apt::new_cache;
|
||||
use rust_apt::records::RecordField;
|
||||
use std::cell::RefCell;
|
||||
use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
use std::thread;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct AptPackageSocket {
|
||||
pub name: String,
|
||||
pub arch: String,
|
||||
pub installed_version: String,
|
||||
pub candidate_version: String,
|
||||
pub description: String,
|
||||
pub source_uri: String,
|
||||
pub maintainer: String,
|
||||
pub size: u64,
|
||||
pub installed_size: u64,
|
||||
pub is_last: bool,
|
||||
}
|
||||
pub fn apt_update_page(
|
||||
window: adw::ApplicationWindow,
|
||||
retry_signal_action: &SimpleAction,
|
||||
flatpak_retry_signal_action: &SimpleAction,
|
||||
flatpak_ran_once: Rc<RefCell<bool>>
|
||||
) -> gtk::Box {
|
||||
let (update_percent_sender, update_percent_receiver) = async_channel::unbounded::<String>();
|
||||
let update_percent_sender = update_percent_sender.clone();
|
||||
let (update_status_sender, update_status_receiver) = async_channel::unbounded::<String>();
|
||||
let update_status_sender = update_status_sender.clone();
|
||||
let update_status_sender_clone0 = update_status_sender.clone();
|
||||
let (get_upgradable_sender, get_upgradable_receiver) = async_channel::unbounded();
|
||||
let get_upgradable_sender = get_upgradable_sender.clone();
|
||||
|
||||
let excluded_updates_vec: Rc<RefCell<Vec<String>>> = Rc::new(RefCell::new(Vec::new()));
|
||||
|
||||
thread::spawn(move || {
|
||||
Runtime::new().unwrap().block_on(start_socket_server_no_log(
|
||||
update_percent_sender,
|
||||
"/tmp/pika_apt_update_percent.sock",
|
||||
));
|
||||
});
|
||||
|
||||
thread::spawn(move || {
|
||||
Runtime::new().unwrap().block_on(start_socket_server(
|
||||
update_status_sender,
|
||||
"/tmp/pika_apt_update_status.sock",
|
||||
"/tmp/pika-apt-update.log",
|
||||
));
|
||||
});
|
||||
|
||||
thread::spawn(move || {
|
||||
let apt_update_command = Command::new("pkexec")
|
||||
.args([
|
||||
"/home/ward/RustroverProjects/pkg-pikman-update-manager/target/debug/apt_update",
|
||||
])
|
||||
.status()
|
||||
.unwrap();
|
||||
match apt_update_command.code().unwrap() {
|
||||
0 => update_status_sender_clone0
|
||||
.send_blocking("FN_OVERRIDE_SUCCESSFUL".to_owned())
|
||||
.unwrap(),
|
||||
53 => {}
|
||||
_ => {
|
||||
update_status_sender_clone0
|
||||
.send_blocking(t!("update_status_error_perms").to_string())
|
||||
.unwrap();
|
||||
update_status_sender_clone0
|
||||
.send_blocking("FN_OVERRIDE_FAILED".to_owned())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let main_box = Box::builder()
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.orientation(Orientation::Vertical)
|
||||
.build();
|
||||
|
||||
let searchbar = SearchEntry::builder()
|
||||
.search_delay(500)
|
||||
.margin_top(15)
|
||||
.margin_bottom(15)
|
||||
.margin_end(30)
|
||||
.margin_start(30)
|
||||
.build();
|
||||
searchbar.add_css_class("rounded-all-25");
|
||||
|
||||
let packages_boxedlist = ListBox::builder()
|
||||
.selection_mode(SelectionMode::None)
|
||||
.sensitive(false)
|
||||
.build();
|
||||
packages_boxedlist.add_css_class("boxed-list");
|
||||
packages_boxedlist.add_css_class("round-all-scroll");
|
||||
|
||||
let packages_viewport = ScrolledWindow::builder()
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.has_frame(true)
|
||||
.margin_bottom(15)
|
||||
.margin_top(15)
|
||||
.margin_end(15)
|
||||
.margin_start(15)
|
||||
.height_request(390)
|
||||
.child(&packages_boxedlist)
|
||||
.build();
|
||||
packages_viewport.add_css_class("round-all-scroll");
|
||||
|
||||
let packages_no_viewport_page = adw::StatusPage::builder()
|
||||
.icon_name("emblem-default-symbolic")
|
||||
.title(t!("apt_packages_no_viewport_page_title"))
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
|
||||
let viewport_bin = adw::Bin::builder()
|
||||
.child(&packages_no_viewport_page)
|
||||
.build();
|
||||
|
||||
let apt_update_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build();
|
||||
|
||||
let apt_update_dialog_progress_bar =
|
||||
ProgressBar::builder().show_text(true).hexpand(true).build();
|
||||
|
||||
let apt_update_dialog_spinner = Spinner::builder()
|
||||
.hexpand(true)
|
||||
.valign(Align::Start)
|
||||
.halign(Align::Center)
|
||||
.spinning(true)
|
||||
.height_request(128)
|
||||
.width_request(128)
|
||||
.build();
|
||||
|
||||
apt_update_dialog_child_box.append(&apt_update_dialog_spinner);
|
||||
apt_update_dialog_child_box.append(&apt_update_dialog_progress_bar);
|
||||
|
||||
let apt_update_dialog = adw::MessageDialog::builder()
|
||||
.transient_for(&window)
|
||||
.extra_child(&apt_update_dialog_child_box)
|
||||
.heading(t!("apt_update_dialog_heading"))
|
||||
.width_request(500)
|
||||
.build();
|
||||
|
||||
apt_update_dialog.add_response(
|
||||
"apt_update_dialog_retry",
|
||||
&t!("apt_update_dialog_retry_label").to_string(),
|
||||
);
|
||||
|
||||
apt_update_dialog.set_response_appearance(
|
||||
"apt_update_dialog_retry",
|
||||
adw::ResponseAppearance::Suggested,
|
||||
);
|
||||
|
||||
apt_update_dialog.set_response_enabled("apt_update_dialog_retry", false);
|
||||
|
||||
let retry_signal_action0 = retry_signal_action.clone();
|
||||
|
||||
apt_update_dialog
|
||||
.clone()
|
||||
.choose(None::<&gio::Cancellable>, move |choice| {
|
||||
if choice == "apt_update_dialog_retry" {
|
||||
retry_signal_action0.activate(None);
|
||||
}
|
||||
});
|
||||
|
||||
let bottom_bar = Box::builder().valign(Align::End).build();
|
||||
|
||||
let select_button = Button::builder()
|
||||
.halign(Align::End)
|
||||
.valign(Align::Center)
|
||||
.hexpand(true)
|
||||
.margin_start(10)
|
||||
.margin_end(10)
|
||||
.margin_bottom(15)
|
||||
.label(t!("select_button_deselect_all"))
|
||||
.build();
|
||||
|
||||
select_button.connect_clicked(clone!(
|
||||
#[weak]
|
||||
select_button,
|
||||
#[weak]
|
||||
packages_boxedlist,
|
||||
move |_| {
|
||||
let select_button_label = select_button.label().unwrap();
|
||||
let value_to_mark = if select_button_label == t!("select_button_select_all").to_string()
|
||||
{
|
||||
true
|
||||
} else if select_button_label == t!("select_button_deselect_all").to_string() {
|
||||
false
|
||||
} else {
|
||||
panic!("Unexpected label on selection button")
|
||||
};
|
||||
set_all_apt_row_marks_to(&packages_boxedlist, value_to_mark)
|
||||
}
|
||||
));
|
||||
|
||||
let update_button = Button::builder()
|
||||
.halign(Align::End)
|
||||
.valign(Align::Center)
|
||||
.hexpand(false)
|
||||
.margin_start(10)
|
||||
.margin_end(30)
|
||||
.margin_bottom(15)
|
||||
.label(t!("update_button_label"))
|
||||
.build();
|
||||
update_button.add_css_class("destructive-action");
|
||||
|
||||
update_button.connect_clicked(clone!(
|
||||
#[weak]
|
||||
window,
|
||||
#[weak]
|
||||
retry_signal_action,
|
||||
#[strong]
|
||||
excluded_updates_vec,
|
||||
move |_| {
|
||||
process::apt_process_update(
|
||||
&excluded_updates_vec.borrow(),
|
||||
window,
|
||||
&retry_signal_action,
|
||||
);
|
||||
}
|
||||
));
|
||||
|
||||
bottom_bar.append(&select_button);
|
||||
bottom_bar.append(&update_button);
|
||||
|
||||
let update_percent_server_context = MainContext::default();
|
||||
// The main loop executes the asynchronous block
|
||||
update_percent_server_context.spawn_local(clone!(
|
||||
#[weak]
|
||||
apt_update_dialog_progress_bar,
|
||||
async move {
|
||||
while let Ok(state) = update_percent_receiver.recv().await {
|
||||
match state.parse::<f64>() {
|
||||
Ok(p) => apt_update_dialog_progress_bar.set_fraction(p / 100.0),
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
let update_status_server_context = MainContext::default();
|
||||
// The main loop executes the asynchronous block
|
||||
update_status_server_context.spawn_local(clone!(
|
||||
#[weak]
|
||||
apt_update_dialog,
|
||||
#[weak]
|
||||
apt_update_dialog_child_box,
|
||||
#[weak]
|
||||
flatpak_retry_signal_action,
|
||||
async move {
|
||||
while let Ok(state) = update_status_receiver.recv().await {
|
||||
match state.as_ref() {
|
||||
"FN_OVERRIDE_SUCCESSFUL" => {
|
||||
let get_upgradable_sender = get_upgradable_sender.clone();
|
||||
thread::spawn(move || {
|
||||
// Create upgradable list cache
|
||||
let upgradable_cache = new_cache!().unwrap();
|
||||
//
|
||||
upgradable_cache.upgrade(Upgrade::FullUpgrade).unwrap();
|
||||
|
||||
upgradable_cache.resolve(true).unwrap();
|
||||
|
||||
let mut upgradeable_iter =
|
||||
upgradable_cache.get_changes(false).peekable();
|
||||
while let Some(pkg) = upgradeable_iter.next() {
|
||||
if !pkg.marked_delete() {
|
||||
let candidate_version_pkg = pkg.candidate().unwrap();
|
||||
let package_struct = AptPackageSocket {
|
||||
name: pkg.name().to_string(),
|
||||
arch: pkg.arch().to_string(),
|
||||
installed_version: match pkg.installed() {
|
||||
Some(t) => t.version().to_string(),
|
||||
_ => {
|
||||
t!("installed_version_to_be_installed").to_string()
|
||||
}
|
||||
},
|
||||
candidate_version: candidate_version_pkg
|
||||
.version()
|
||||
.to_string(),
|
||||
description: match candidate_version_pkg.description() {
|
||||
Some(s) => s,
|
||||
_ => t!("apt_pkg_property_unknown").to_string(),
|
||||
},
|
||||
source_uri: candidate_version_pkg
|
||||
.uris()
|
||||
.collect::<Vec<String>>()
|
||||
.join("\n"),
|
||||
maintainer: match candidate_version_pkg
|
||||
.get_record(RecordField::Maintainer)
|
||||
{
|
||||
Some(s) => s,
|
||||
_ => t!("apt_pkg_property_unknown").to_string(),
|
||||
},
|
||||
size: candidate_version_pkg.size(),
|
||||
installed_size: candidate_version_pkg.installed_size(),
|
||||
is_last: upgradeable_iter.peek().is_none(),
|
||||
};
|
||||
get_upgradable_sender.send_blocking(package_struct).unwrap()
|
||||
}
|
||||
}
|
||||
});
|
||||
apt_update_dialog.close();
|
||||
let mut flatpak_ran_once_borrow = flatpak_ran_once.borrow_mut();
|
||||
if *flatpak_ran_once_borrow != true {
|
||||
flatpak_retry_signal_action.activate(None);
|
||||
*flatpak_ran_once_borrow = true;
|
||||
}
|
||||
}
|
||||
"FN_OVERRIDE_FAILED" => {
|
||||
apt_update_dialog_child_box.set_visible(false);
|
||||
apt_update_dialog.set_extra_child(Some(
|
||||
&Image::builder()
|
||||
.pixel_size(128)
|
||||
.icon_name("dialog-error-symbolic")
|
||||
.halign(Align::Center)
|
||||
.build(),
|
||||
));
|
||||
apt_update_dialog
|
||||
.set_title(Some(&t!("apt_update_dialog_status_failed").to_string()));
|
||||
apt_update_dialog.set_response_enabled("apt_update_dialog_retry", true);
|
||||
}
|
||||
_ => apt_update_dialog.set_body(&state),
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
let get_upgradable_server_context = MainContext::default();
|
||||
// The main loop executes the asynchronous block
|
||||
get_upgradable_server_context.spawn_local(clone!(
|
||||
#[strong]
|
||||
select_button,
|
||||
#[strong]
|
||||
update_button,
|
||||
#[strong]
|
||||
packages_boxedlist,
|
||||
#[strong]
|
||||
packages_viewport,
|
||||
#[strong]
|
||||
viewport_bin,
|
||||
#[strong]
|
||||
excluded_updates_vec,
|
||||
async move {
|
||||
while let Ok(state) = get_upgradable_receiver.recv().await {
|
||||
viewport_bin.set_child(Some(&packages_viewport));
|
||||
let apt_row = AptPackageRow::new(state.clone());
|
||||
apt_row.connect_closure(
|
||||
"checkbutton-toggled",
|
||||
false,
|
||||
closure_local!(
|
||||
#[strong]
|
||||
select_button,
|
||||
#[strong]
|
||||
update_button,
|
||||
#[strong]
|
||||
packages_boxedlist,
|
||||
#[strong]
|
||||
excluded_updates_vec,
|
||||
move |apt_row: AptPackageRow| {
|
||||
if is_widget_select_all_ready(&packages_boxedlist) {
|
||||
select_button
|
||||
.set_label(&t!("select_button_select_all").to_string());
|
||||
} else {
|
||||
select_button
|
||||
.set_label(&t!("select_button_deselect_all").to_string());
|
||||
}
|
||||
update_button
|
||||
.set_sensitive(!is_all_children_unmarked(&packages_boxedlist));
|
||||
excluded_updates_vec
|
||||
.borrow_mut()
|
||||
.retain(|x| x != &apt_row.package_name());
|
||||
}
|
||||
),
|
||||
);
|
||||
apt_row.connect_closure(
|
||||
"checkbutton-untoggled",
|
||||
false,
|
||||
closure_local!(
|
||||
#[strong]
|
||||
select_button,
|
||||
#[strong]
|
||||
update_button,
|
||||
#[strong]
|
||||
packages_boxedlist,
|
||||
#[strong]
|
||||
excluded_updates_vec,
|
||||
move |apt_row: AptPackageRow| {
|
||||
select_button.set_label(&t!("select_button_select_all").to_string());
|
||||
update_button
|
||||
.set_sensitive(!is_all_children_unmarked(&packages_boxedlist));
|
||||
excluded_updates_vec
|
||||
.borrow_mut()
|
||||
.push(apt_row.package_name())
|
||||
}
|
||||
),
|
||||
);
|
||||
packages_boxedlist.append(&apt_row);
|
||||
if state.is_last {
|
||||
packages_boxedlist.set_sensitive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
searchbar.connect_search_changed(clone!(
|
||||
#[weak]
|
||||
searchbar,
|
||||
#[weak]
|
||||
packages_boxedlist,
|
||||
move |_| {
|
||||
let mut counter = packages_boxedlist.first_child();
|
||||
while let Some(row) = counter {
|
||||
if row.widget_name() == "AptPackageRow" {
|
||||
if !searchbar.text().is_empty() {
|
||||
if row
|
||||
.property::<String>("package-name")
|
||||
.to_lowercase()
|
||||
.contains(&searchbar.text().to_string().to_lowercase())
|
||||
{
|
||||
row.set_property("visible", true);
|
||||
searchbar.grab_focus();
|
||||
} else {
|
||||
row.set_property("visible", false);
|
||||
}
|
||||
} else {
|
||||
row.set_property("visible", true);
|
||||
}
|
||||
}
|
||||
counter = row.next_sibling();
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
main_box.append(&searchbar);
|
||||
main_box.append(&viewport_bin);
|
||||
main_box.append(&bottom_bar);
|
||||
|
||||
apt_update_dialog.present();
|
||||
main_box
|
||||
}
|
||||
|
||||
fn is_widget_select_all_ready(parent_listbox: &impl IsA<ListBox>) -> bool {
|
||||
let mut is_ready = false;
|
||||
let mut child_counter = parent_listbox.borrow().first_child();
|
||||
while let Some(child) = child_counter {
|
||||
let next_child = child.next_sibling();
|
||||
let downcast = child.downcast::<AptPackageRow>().unwrap();
|
||||
if !downcast.package_marked() {
|
||||
is_ready = true;
|
||||
break;
|
||||
}
|
||||
child_counter = next_child
|
||||
}
|
||||
is_ready
|
||||
}
|
||||
|
||||
fn is_all_children_unmarked(parent_listbox: &impl IsA<ListBox>) -> bool {
|
||||
let mut is_all_unmarked = true;
|
||||
let mut child_counter = parent_listbox.borrow().first_child();
|
||||
while let Some(child) = child_counter {
|
||||
let next_child = child.next_sibling();
|
||||
let downcast = child.downcast::<AptPackageRow>().unwrap();
|
||||
if downcast.package_marked() {
|
||||
is_all_unmarked = false;
|
||||
break;
|
||||
}
|
||||
child_counter = next_child
|
||||
}
|
||||
is_all_unmarked
|
||||
}
|
||||
|
||||
fn set_all_apt_row_marks_to(parent_listbox: &impl IsA<ListBox>, value: bool) {
|
||||
let mut child_counter = parent_listbox.borrow().first_child();
|
||||
while let Some(child) = child_counter {
|
||||
let next_child = child.next_sibling();
|
||||
let downcast = child.downcast::<AptPackageRow>().unwrap();
|
||||
downcast.set_package_marked(value);
|
||||
child_counter = next_child
|
||||
}
|
||||
}
|
@ -1,622 +0,0 @@
|
||||
use adw::gio::SimpleAction;
|
||||
use adw::prelude::*;
|
||||
use gtk::glib::*;
|
||||
use gtk::*;
|
||||
use pika_unixsocket_tools::pika_unixsocket_tools::{
|
||||
start_socket_server, start_socket_server_no_log,
|
||||
};
|
||||
use pretty_bytes::converter::convert;
|
||||
use rust_apt::cache::Upgrade;
|
||||
use rust_apt::new_cache;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use std::cell::RefCell;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
use std::thread;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
struct AptChangesInfo {
|
||||
package_count_upgrade: u64,
|
||||
package_count_install: u64,
|
||||
package_count_downgrade: u64,
|
||||
package_count_remove: u64,
|
||||
total_download_size: u64,
|
||||
total_installed_size: i64,
|
||||
}
|
||||
#[derive(Serialize)]
|
||||
struct Exclusions {
|
||||
exclusions: Vec<Value>,
|
||||
}
|
||||
|
||||
impl AptChangesInfo {
|
||||
fn add_upgrade(&mut self) {
|
||||
self.package_count_upgrade += 1;
|
||||
}
|
||||
fn add_install(&mut self) {
|
||||
self.package_count_install += 1;
|
||||
}
|
||||
fn add_downgrade(&mut self) {
|
||||
self.package_count_downgrade += 1;
|
||||
}
|
||||
fn add_remove(&mut self) {
|
||||
self.package_count_remove += 1;
|
||||
}
|
||||
|
||||
fn increase_total_download_size_by(&mut self, value: u64) {
|
||||
self.total_download_size += value;
|
||||
}
|
||||
|
||||
fn increase_total_installed_size_by(&mut self, value: u64) {
|
||||
self.total_installed_size += value as i64;
|
||||
}
|
||||
|
||||
fn decrease_total_installed_size_by(&mut self, value: u64) {
|
||||
self.total_installed_size -= value as i64;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn apt_process_update(
|
||||
excluded_updates_vec: &Vec<String>,
|
||||
window: adw::ApplicationWindow,
|
||||
retry_signal_action: &SimpleAction,
|
||||
) {
|
||||
let excluded_updates_alert_dialog = adw::MessageDialog::builder()
|
||||
.transient_for(&window)
|
||||
.heading(t!("excluded_updates_alert_dialog_heading"))
|
||||
.body(t!("excluded_updates_alert_dialog_body"))
|
||||
.build();
|
||||
|
||||
excluded_updates_alert_dialog.add_response(
|
||||
"excluded_updates_alert_dialog_cancel",
|
||||
&t!("excluded_updates_alert_dialog_cancel_label").to_string(),
|
||||
);
|
||||
|
||||
excluded_updates_alert_dialog.add_response(
|
||||
"excluded_updates_alert_continue",
|
||||
&t!("excluded_updates_alert_continue_label").to_string(),
|
||||
);
|
||||
|
||||
excluded_updates_alert_dialog.set_response_appearance(
|
||||
"excluded_updates_alert_continue",
|
||||
adw::ResponseAppearance::Destructive,
|
||||
);
|
||||
|
||||
excluded_updates_alert_dialog.set_default_response(Some("excluded_updates_alert_continue"));
|
||||
|
||||
let excluded_updates_alert_dialog_action =
|
||||
SimpleAction::new("excluded_updates_alert_dialog_action", None);
|
||||
|
||||
excluded_updates_alert_dialog_action.connect_activate(clone!(
|
||||
#[weak]
|
||||
window,
|
||||
#[weak]
|
||||
retry_signal_action,
|
||||
#[strong]
|
||||
excluded_updates_vec,
|
||||
move |_, _| apt_confirm_window(&excluded_updates_vec, window, &retry_signal_action)
|
||||
));
|
||||
|
||||
if excluded_updates_vec.is_empty() {
|
||||
excluded_updates_alert_dialog_action.activate(None);
|
||||
} else {
|
||||
excluded_updates_alert_dialog.choose(None::<&gio::Cancellable>, move |choice| {
|
||||
if choice == "excluded_updates_alert_continue" {
|
||||
excluded_updates_alert_dialog_action.activate(None);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn apt_confirm_window(
|
||||
excluded_updates_vec: &Vec<String>,
|
||||
window: adw::ApplicationWindow,
|
||||
retry_signal_action: &SimpleAction,
|
||||
) {
|
||||
let to_be_removed_packages_vec: Rc<RefCell<Vec<String>>> = Rc::new(RefCell::new(Vec::new()));
|
||||
// Emulate Apt Full Upgrade to get transaction info
|
||||
let mut apt_changes_struct = AptChangesInfo {
|
||||
package_count_upgrade: 0,
|
||||
package_count_install: 0,
|
||||
package_count_downgrade: 0,
|
||||
package_count_remove: 0,
|
||||
total_download_size: 0,
|
||||
total_installed_size: 0,
|
||||
};
|
||||
|
||||
let apt_cache = new_cache!().unwrap();
|
||||
let apt_upgrade_cache = new_cache!().unwrap();
|
||||
|
||||
apt_cache.upgrade(Upgrade::FullUpgrade).unwrap();
|
||||
|
||||
for change in apt_cache.get_changes(false) {
|
||||
if !excluded_updates_vec
|
||||
.iter()
|
||||
.any(|e| change.name().contains(e))
|
||||
{
|
||||
let pkg = apt_upgrade_cache.get(change.name()).unwrap();
|
||||
if change.marked_upgrade() || change.marked_install() || change.marked_downgrade() {
|
||||
pkg.mark_install(true, false);
|
||||
} else if change.marked_delete() {
|
||||
pkg.mark_delete(false);
|
||||
to_be_removed_packages_vec
|
||||
.borrow_mut()
|
||||
.push(pkg.name().to_owned());
|
||||
}
|
||||
pkg.protect();
|
||||
}
|
||||
}
|
||||
|
||||
apt_upgrade_cache.resolve(true).unwrap();
|
||||
|
||||
println!("{}", t!("gui_changes_emu_msg_0"));
|
||||
for change in apt_upgrade_cache.get_changes(false) {
|
||||
if change.is_installed() {
|
||||
apt_changes_struct
|
||||
.decrease_total_installed_size_by(change.installed().unwrap().installed_size());
|
||||
}
|
||||
if change.marked_upgrade() && change.is_installed() {
|
||||
println!("{}: {}", t!("gui_changes_emu_msg_upgrading"), change.name());
|
||||
apt_changes_struct.add_upgrade();
|
||||
apt_changes_struct.increase_total_download_size_by(change.candidate().unwrap().size());
|
||||
apt_changes_struct
|
||||
.increase_total_installed_size_by(change.candidate().unwrap().installed_size());
|
||||
} else if change.marked_install() || change.marked_upgrade() && !change.is_installed() {
|
||||
println!(
|
||||
"{}: {}",
|
||||
t!("gui_changes_emu_msg_installing"),
|
||||
change.name()
|
||||
);
|
||||
apt_changes_struct.add_install();
|
||||
apt_changes_struct.increase_total_download_size_by(change.candidate().unwrap().size());
|
||||
apt_changes_struct
|
||||
.increase_total_installed_size_by(change.candidate().unwrap().installed_size());
|
||||
} else if change.marked_downgrade() {
|
||||
println!(
|
||||
"{}: {}",
|
||||
t!("gui_changes_emu_msg_downgrading"),
|
||||
change.name()
|
||||
);
|
||||
apt_changes_struct.add_downgrade();
|
||||
apt_changes_struct.increase_total_download_size_by(change.candidate().unwrap().size());
|
||||
apt_changes_struct
|
||||
.increase_total_installed_size_by(change.candidate().unwrap().installed_size());
|
||||
} else if change.marked_delete() {
|
||||
println!("{}: {}", t!("gui_changes_emu_msg_removing"), change.name());
|
||||
apt_changes_struct.add_remove();
|
||||
}
|
||||
}
|
||||
|
||||
let apt_confirm_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build();
|
||||
|
||||
let apt_update_dialog_badges_size_group = SizeGroup::new(SizeGroupMode::Both);
|
||||
let apt_update_dialog_badges_size_group0 = SizeGroup::new(SizeGroupMode::Both);
|
||||
let apt_update_dialog_badges_size_group1 = SizeGroup::new(SizeGroupMode::Both);
|
||||
|
||||
apt_confirm_dialog_child_box.append(&create_color_badge(
|
||||
&t!("package_count_upgrade_badge_label"),
|
||||
&apt_changes_struct.package_count_upgrade.to_string(),
|
||||
"background-accent-bg",
|
||||
&apt_update_dialog_badges_size_group,
|
||||
&apt_update_dialog_badges_size_group0,
|
||||
&apt_update_dialog_badges_size_group1,
|
||||
));
|
||||
|
||||
apt_confirm_dialog_child_box.append(&create_color_badge(
|
||||
&t!("package_count_install_badge_label"),
|
||||
&apt_changes_struct.package_count_install.to_string(),
|
||||
"background-accent-bg",
|
||||
&apt_update_dialog_badges_size_group,
|
||||
&apt_update_dialog_badges_size_group0,
|
||||
&apt_update_dialog_badges_size_group1,
|
||||
));
|
||||
|
||||
apt_confirm_dialog_child_box.append(&create_color_badge(
|
||||
&t!("package_count_downgrade_badge_label"),
|
||||
&apt_changes_struct.package_count_downgrade.to_string(),
|
||||
"background-accent-bg",
|
||||
&apt_update_dialog_badges_size_group,
|
||||
&apt_update_dialog_badges_size_group0,
|
||||
&apt_update_dialog_badges_size_group1,
|
||||
));
|
||||
|
||||
apt_confirm_dialog_child_box.append(&create_color_badge(
|
||||
&t!("package_count_remove_badge_label"),
|
||||
&apt_changes_struct.package_count_remove.to_string(),
|
||||
"background-accent-bg",
|
||||
&apt_update_dialog_badges_size_group,
|
||||
&apt_update_dialog_badges_size_group0,
|
||||
&apt_update_dialog_badges_size_group1,
|
||||
));
|
||||
|
||||
apt_confirm_dialog_child_box.append(&create_color_badge(
|
||||
&t!("total_download_size_badge_label"),
|
||||
&convert(apt_changes_struct.total_download_size as f64),
|
||||
"background-accent-bg",
|
||||
&apt_update_dialog_badges_size_group,
|
||||
&apt_update_dialog_badges_size_group0,
|
||||
&apt_update_dialog_badges_size_group1,
|
||||
));
|
||||
|
||||
apt_confirm_dialog_child_box.append(&create_color_badge(
|
||||
&t!("total_installed_size_badge_label"),
|
||||
&convert(apt_changes_struct.total_installed_size as f64),
|
||||
"background-accent-bg",
|
||||
&apt_update_dialog_badges_size_group,
|
||||
&apt_update_dialog_badges_size_group0,
|
||||
&apt_update_dialog_badges_size_group1,
|
||||
));
|
||||
|
||||
let apt_confirm_dialog = adw::MessageDialog::builder()
|
||||
.transient_for(&window)
|
||||
.heading(t!("apt_confirm_dialog_heading"))
|
||||
.body(t!("apt_confirm_dialog_body"))
|
||||
.extra_child(&apt_confirm_dialog_child_box)
|
||||
.build();
|
||||
|
||||
apt_confirm_dialog.add_response(
|
||||
"apt_confirm_dialog_cancel",
|
||||
&t!("apt_confirm_dialog_cancel_label").to_string(),
|
||||
);
|
||||
|
||||
apt_confirm_dialog.add_response(
|
||||
"apt_confirm_dialog_confirm",
|
||||
&t!("apt_confirm_dialog_confirm_label").to_string(),
|
||||
);
|
||||
|
||||
apt_confirm_dialog.set_response_appearance(
|
||||
"apt_confirm_dialog_confirm",
|
||||
adw::ResponseAppearance::Destructive,
|
||||
);
|
||||
|
||||
apt_confirm_dialog.set_default_response(Some("apt_confirm_dialog_confirm"));
|
||||
apt_confirm_dialog.set_close_response("apt_confirm_dialog_cancel");
|
||||
|
||||
let json_file_path = "/tmp/pika-apt-exclusions.json";
|
||||
|
||||
if Path::new(json_file_path).exists() {
|
||||
std::fs::remove_file(json_file_path).expect("Failed to remove old json file");
|
||||
}
|
||||
|
||||
if !excluded_updates_vec.is_empty() {
|
||||
let exclusions_array = Exclusions {
|
||||
exclusions: excluded_updates_vec
|
||||
.into_iter()
|
||||
.map(|i| serde_json::from_str(format!("{{\"package\":\"{}\"}}", i).as_str()))
|
||||
.collect::<Result<Vec<Value>, _>>()
|
||||
.unwrap(),
|
||||
};
|
||||
|
||||
std::fs::write(
|
||||
json_file_path,
|
||||
serde_json::to_string_pretty(&exclusions_array).unwrap(),
|
||||
)
|
||||
.expect("Failed to write to json file");
|
||||
}
|
||||
|
||||
let apt_confirm_start_signal_action = SimpleAction::new("apt_confirm_start", None);
|
||||
|
||||
apt_confirm_start_signal_action.connect_activate(clone!(
|
||||
#[weak]
|
||||
window,
|
||||
#[strong]
|
||||
retry_signal_action,
|
||||
#[strong]
|
||||
apt_confirm_dialog,
|
||||
move |_, _| {
|
||||
let retry_signal_action0 = retry_signal_action.clone();
|
||||
apt_confirm_dialog
|
||||
.clone()
|
||||
.choose(None::<&gio::Cancellable>, move |choice| {
|
||||
if choice == "apt_confirm_dialog_confirm" {
|
||||
apt_full_upgrade_from_socket(window, &retry_signal_action0);
|
||||
}
|
||||
});
|
||||
}
|
||||
));
|
||||
|
||||
let to_be_removed_packages_borrow = to_be_removed_packages_vec.borrow();
|
||||
if to_be_removed_packages_borrow.is_empty() {
|
||||
apt_confirm_start_signal_action.activate(None);
|
||||
} else {
|
||||
let apt_remove_confirm_text_buffer = TextBuffer::builder()
|
||||
.text(
|
||||
to_be_removed_packages_borrow
|
||||
.iter()
|
||||
.map(|x| x.to_string() + "\n")
|
||||
.collect::<String>()
|
||||
+ "\n",
|
||||
)
|
||||
.build();
|
||||
|
||||
let apt_remove_confirm_text_view = TextView::builder()
|
||||
.buffer(&apt_remove_confirm_text_buffer)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.editable(false)
|
||||
.build();
|
||||
|
||||
let apt_remove_confirm_text_viewport = gtk::ScrolledWindow::builder()
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.has_frame(true)
|
||||
.hscrollbar_policy(PolicyType::Never)
|
||||
.child(&apt_remove_confirm_text_view)
|
||||
.build();
|
||||
apt_remove_confirm_text_viewport.add_css_class("round-all-scroll");
|
||||
|
||||
let apt_remove_confirm_dialog = adw::MessageDialog::builder()
|
||||
.transient_for(&window)
|
||||
.heading(t!("apt_remove_confirm_dialog_heading"))
|
||||
.body(t!("apt_remove_confirm_dialog_body"))
|
||||
.extra_child(&apt_remove_confirm_text_viewport)
|
||||
.build();
|
||||
|
||||
apt_remove_confirm_dialog.add_response(
|
||||
"apt_remove_confirm_dialog_cancel",
|
||||
&t!("apt_remove_confirm_dialog_cancel_label").to_string(),
|
||||
);
|
||||
|
||||
apt_remove_confirm_dialog.add_response(
|
||||
"apt_remove_confirm_dialog_confirm",
|
||||
&t!("apt_remove_confirm_dialog_confirm_label").to_string(),
|
||||
);
|
||||
|
||||
apt_remove_confirm_dialog.set_response_appearance(
|
||||
"apt_remove_confirm_dialog_confirm",
|
||||
adw::ResponseAppearance::Destructive,
|
||||
);
|
||||
|
||||
apt_remove_confirm_dialog.set_default_response(Some("apt_remove_confirm_dialog_confirm"));
|
||||
apt_remove_confirm_dialog.set_close_response("apt_remove_confirm_dialog_cancel");
|
||||
|
||||
apt_remove_confirm_dialog.choose(None::<&gio::Cancellable>, move |choice| {
|
||||
if choice == "apt_remove_confirm_dialog_confirm" {
|
||||
apt_confirm_start_signal_action.activate(None);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn apt_full_upgrade_from_socket(
|
||||
window: adw::ApplicationWindow,
|
||||
retry_signal_action: &SimpleAction,
|
||||
) {
|
||||
let (upgrade_percent_sender, upgrade_percent_receiver) = async_channel::unbounded::<String>();
|
||||
let upgrade_percent_sender = upgrade_percent_sender.clone();
|
||||
let (upgrade_status_sender, upgrade_status_receiver) = async_channel::unbounded::<String>();
|
||||
let upgrade_status_sender = upgrade_status_sender.clone();
|
||||
let upgrade_status_sender_clone0 = upgrade_status_sender.clone();
|
||||
|
||||
let log_file_path = format!(
|
||||
"/tmp/pika-apt-upgrade_{}.log",
|
||||
chrono::offset::Local::now().format("%Y-%m-%d_%H:%M")
|
||||
);
|
||||
let log_file_path_clone0 = log_file_path.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
Runtime::new().unwrap().block_on(start_socket_server_no_log(
|
||||
upgrade_percent_sender,
|
||||
"/tmp/pika_apt_upgrade_percent.sock",
|
||||
));
|
||||
});
|
||||
|
||||
thread::spawn(move || {
|
||||
Runtime::new().unwrap().block_on(start_socket_server(
|
||||
upgrade_status_sender,
|
||||
"/tmp/pika_apt_upgrade_status.sock",
|
||||
&log_file_path,
|
||||
));
|
||||
});
|
||||
|
||||
thread::spawn(move || {
|
||||
let apt_upgrade_command = Command::new("pkexec")
|
||||
.args([
|
||||
"/home/ward/RustroverProjects/pkg-pikman-update-manager/target/debug/apt_full_upgrade",
|
||||
])
|
||||
.status()
|
||||
.unwrap();
|
||||
match apt_upgrade_command.code().unwrap() {
|
||||
0 => upgrade_status_sender_clone0
|
||||
.send_blocking("FN_OVERRIDE_SUCCESSFUL".to_owned())
|
||||
.unwrap(),
|
||||
53 => {}
|
||||
_ => {
|
||||
upgrade_status_sender_clone0
|
||||
.send_blocking(t!("upgrade_status_error_perms").to_string())
|
||||
.unwrap();
|
||||
upgrade_status_sender_clone0
|
||||
.send_blocking("FN_OVERRIDE_FAILED".to_owned())
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let apt_upgrade_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build();
|
||||
|
||||
let apt_upgrade_dialog_progress_bar =
|
||||
ProgressBar::builder().show_text(true).hexpand(true).build();
|
||||
|
||||
let apt_upgrade_dialog_spinner = Spinner::builder()
|
||||
.hexpand(true)
|
||||
.valign(Align::Start)
|
||||
.halign(Align::Center)
|
||||
.spinning(true)
|
||||
.height_request(128)
|
||||
.width_request(128)
|
||||
.build();
|
||||
|
||||
apt_upgrade_dialog_child_box.append(&apt_upgrade_dialog_spinner);
|
||||
apt_upgrade_dialog_child_box.append(&apt_upgrade_dialog_progress_bar);
|
||||
|
||||
let apt_upgrade_dialog = adw::MessageDialog::builder()
|
||||
.transient_for(&window)
|
||||
.extra_child(&apt_upgrade_dialog_child_box)
|
||||
.heading(t!("apt_upgrade_dialog_heading"))
|
||||
.width_request(500)
|
||||
.build();
|
||||
|
||||
apt_upgrade_dialog.add_response(
|
||||
"apt_upgrade_dialog_ok",
|
||||
&t!("apt_upgrade_dialog_ok_label").to_string(),
|
||||
);
|
||||
|
||||
let apt_upgrade_dialog_child_box_done =
|
||||
Box::builder().orientation(Orientation::Vertical).build();
|
||||
|
||||
let apt_upgrade_log_image = Image::builder()
|
||||
.pixel_size(128)
|
||||
.halign(Align::Center)
|
||||
.build();
|
||||
|
||||
let apt_upgrade_log_button = Button::builder()
|
||||
.label(t!("apt_upgrade_dialog_open_log_file_label"))
|
||||
.halign(Align::Center)
|
||||
.margin_start(15)
|
||||
.margin_end(15)
|
||||
.margin_top(15)
|
||||
.margin_bottom(15)
|
||||
.build();
|
||||
|
||||
apt_upgrade_dialog_child_box_done.append(&apt_upgrade_log_image);
|
||||
apt_upgrade_dialog_child_box_done.append(&apt_upgrade_log_button);
|
||||
|
||||
apt_upgrade_dialog.set_response_enabled("apt_upgrade_dialog_ok", false);
|
||||
apt_upgrade_dialog.set_close_response("apt_upgrade_dialog_ok");
|
||||
|
||||
let upgrade_percent_server_context = MainContext::default();
|
||||
// The main loop executes the asynchronous block
|
||||
upgrade_percent_server_context.spawn_local(clone!(
|
||||
#[weak]
|
||||
apt_upgrade_dialog_progress_bar,
|
||||
async move {
|
||||
while let Ok(state) = upgrade_percent_receiver.recv().await {
|
||||
match state.as_ref() {
|
||||
"FN_OVERRIDE_SUCCESSFUL" => {}
|
||||
_ => match state.parse::<f64>() {
|
||||
Ok(p) => apt_upgrade_dialog_progress_bar.set_fraction(p / 100.0),
|
||||
Err(_) => {}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
let upgrade_status_server_context = MainContext::default();
|
||||
// The main loop executes the asynchronous block
|
||||
upgrade_status_server_context.spawn_local(clone!(
|
||||
#[weak]
|
||||
apt_upgrade_dialog,
|
||||
#[weak]
|
||||
apt_upgrade_dialog_child_box,
|
||||
#[strong]
|
||||
apt_upgrade_dialog_child_box_done,
|
||||
#[strong]
|
||||
apt_upgrade_log_image,
|
||||
async move {
|
||||
while let Ok(state) = upgrade_status_receiver.recv().await {
|
||||
match state.as_ref() {
|
||||
"FN_OVERRIDE_SUCCESSFUL" => {
|
||||
apt_upgrade_dialog_child_box.set_visible(false);
|
||||
apt_upgrade_log_image.set_icon_name(Some("face-cool-symbolic"));
|
||||
apt_upgrade_dialog
|
||||
.set_extra_child(Some(&apt_upgrade_dialog_child_box_done));
|
||||
apt_upgrade_dialog.set_title(Some(
|
||||
&t!("apt_upgrade_dialog_status_successful").to_string(),
|
||||
));
|
||||
apt_upgrade_dialog.set_response_enabled("apt_upgrade_dialog_ok", true);
|
||||
}
|
||||
"FN_OVERRIDE_FAILED" => {
|
||||
apt_upgrade_dialog_child_box.set_visible(false);
|
||||
apt_upgrade_log_image.set_icon_name(Some("dialog-error-symbolic"));
|
||||
apt_upgrade_dialog
|
||||
.set_extra_child(Some(&apt_upgrade_dialog_child_box_done));
|
||||
apt_upgrade_dialog
|
||||
.set_title(Some(&t!("apt_upgrade_dialog_status_failed").to_string()));
|
||||
apt_upgrade_dialog.set_response_enabled("apt_upgrade_dialog_ok", true);
|
||||
apt_upgrade_dialog
|
||||
.set_response_enabled("apt_upgrade_dialog_open_log_file", true);
|
||||
}
|
||||
_ => apt_upgrade_dialog.set_body(&state),
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
let retry_signal_action0 = retry_signal_action.clone();
|
||||
|
||||
apt_upgrade_log_button.connect_clicked(move |_| {
|
||||
let _ = Command::new("xdg-open")
|
||||
.arg(log_file_path_clone0.to_owned())
|
||||
.spawn();
|
||||
});
|
||||
|
||||
apt_upgrade_dialog.choose(None::<&gio::Cancellable>, move |choice| {
|
||||
match choice.as_str() {
|
||||
"apt_upgrade_dialog_ok" => {
|
||||
retry_signal_action0.activate(None);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn create_color_badge(
|
||||
label0_text: &str,
|
||||
label1_text: &str,
|
||||
css_style: &str,
|
||||
group_size: &SizeGroup,
|
||||
group_size0: &SizeGroup,
|
||||
group_size1: &SizeGroup,
|
||||
) -> ListBox {
|
||||
let badge_box = Box::builder().build();
|
||||
|
||||
let label0 = Label::builder()
|
||||
.label(label0_text)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(1)
|
||||
.margin_top(1)
|
||||
.valign(Align::Center)
|
||||
.halign(Align::Center)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
group_size0.add_widget(&label0);
|
||||
|
||||
let label_separator = Separator::builder().build();
|
||||
|
||||
let label1 = Label::builder()
|
||||
.label(label1_text)
|
||||
.margin_start(3)
|
||||
.margin_end(0)
|
||||
.margin_bottom(1)
|
||||
.margin_top(1)
|
||||
.valign(Align::Center)
|
||||
.halign(Align::Center)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
group_size1.add_widget(&label1);
|
||||
|
||||
label1.add_css_class(css_style);
|
||||
|
||||
badge_box.append(&label0);
|
||||
badge_box.append(&label_separator);
|
||||
badge_box.append(&label1);
|
||||
|
||||
let boxedlist = ListBox::builder()
|
||||
.selection_mode(SelectionMode::None)
|
||||
.halign(Align::Center)
|
||||
.margin_start(10)
|
||||
.margin_end(10)
|
||||
.margin_bottom(10)
|
||||
.margin_top(10)
|
||||
.build();
|
||||
|
||||
boxedlist.add_css_class("boxed-list");
|
||||
boxedlist.append(&badge_box);
|
||||
group_size.add_widget(&boxedlist);
|
||||
boxedlist
|
||||
}
|
@ -1,295 +0,0 @@
|
||||
use crate::apt_update_page;
|
||||
use crate::config::{APP_GITHUB, APP_ICON, APP_ID, VERSION};
|
||||
use crate::flatpak_update_page;
|
||||
use adw::prelude::*;
|
||||
use adw::*;
|
||||
use gtk::glib::{clone, MainContext};
|
||||
use gtk::License;
|
||||
use std::cell::RefCell;
|
||||
use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
use std::thread;
|
||||
|
||||
pub fn build_ui(app: &Application) {
|
||||
// setup glib
|
||||
glib::set_prgname(Some(t!("application_name").to_string()));
|
||||
glib::set_application_name(&t!("application_name").to_string());
|
||||
let glib_settings = gio::Settings::new(APP_ID);
|
||||
|
||||
let internet_connected = Rc::new(RefCell::new(false));
|
||||
let (internet_loop_sender, internet_loop_receiver) = async_channel::unbounded();
|
||||
let internet_loop_sender = internet_loop_sender.clone();
|
||||
|
||||
thread::spawn(move || loop {
|
||||
match Command::new("ping").arg("google.com").arg("-c 1").output() {
|
||||
Ok(t) if t.status.success() => internet_loop_sender
|
||||
.send_blocking(true)
|
||||
.expect("The channel needs to be open"),
|
||||
_ => internet_loop_sender
|
||||
.send_blocking(false)
|
||||
.expect("The channel needs to be open"),
|
||||
};
|
||||
thread::sleep(std::time::Duration::from_secs(5));
|
||||
});
|
||||
|
||||
let window_banner = Banner::builder().revealed(false).build();
|
||||
|
||||
let internet_connected_status = internet_connected.clone();
|
||||
|
||||
let internet_loop_context = MainContext::default();
|
||||
// The main loop executes the asynchronous block
|
||||
internet_loop_context.spawn_local(clone!(
|
||||
#[weak]
|
||||
window_banner,
|
||||
async move {
|
||||
while let Ok(state) = internet_loop_receiver.recv().await {
|
||||
let banner_text = t!("banner_text_no_internet").to_string();
|
||||
if state == true {
|
||||
*internet_connected_status.borrow_mut() = true;
|
||||
if window_banner.title() == banner_text {
|
||||
window_banner.set_revealed(false)
|
||||
}
|
||||
} else {
|
||||
*internet_connected_status.borrow_mut() = false;
|
||||
window_banner.set_title(&banner_text);
|
||||
window_banner.set_revealed(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
let window_headerbar = HeaderBar::builder()
|
||||
.title_widget(&WindowTitle::builder().title(t!("application_name")).build())
|
||||
.show_title(false)
|
||||
.build();
|
||||
|
||||
let window_breakpoint = adw::Breakpoint::new(BreakpointCondition::new_length(
|
||||
BreakpointConditionLengthType::MaxWidth,
|
||||
800.0,
|
||||
LengthUnit::Px,
|
||||
));
|
||||
|
||||
let window_adw_stack = gtk::Stack::builder()
|
||||
.hhomogeneous(true)
|
||||
.vhomogeneous(true)
|
||||
.transition_type(gtk::StackTransitionType::SlideUpDown)
|
||||
.build();
|
||||
|
||||
let window_toolbar = ToolbarView::builder()
|
||||
.content(&window_adw_stack)
|
||||
.top_bar_style(ToolbarStyle::Flat)
|
||||
.bottom_bar_style(ToolbarStyle::Flat)
|
||||
.build();
|
||||
|
||||
let window_adw_view_switcher_sidebar = gtk::StackSidebar::builder()
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.stack(&window_adw_stack)
|
||||
.build();
|
||||
|
||||
let window_adw_view_switcher_sidebar_box = gtk::Box::new(gtk::Orientation::Vertical, 0);
|
||||
window_adw_view_switcher_sidebar_box.append(&WindowTitle::builder().title(t!("application_name")).margin_top(20).margin_bottom(20).margin_start(5).margin_end(5).build());
|
||||
window_adw_view_switcher_sidebar_box.append(&window_adw_view_switcher_sidebar);
|
||||
|
||||
let window_adw_view_sidebar_navigation_page = adw::NavigationPage::new(&window_adw_view_switcher_sidebar_box, "sidebar_view");
|
||||
|
||||
let sidebar_toggle_button = gtk::ToggleButton::builder()
|
||||
.icon_name("view-right-pane-symbolic")
|
||||
.visible(false)
|
||||
.build();
|
||||
|
||||
let window_content_page_split_view = adw::OverlaySplitView::builder()
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.content(&window_toolbar)
|
||||
.sidebar(&window_adw_view_sidebar_navigation_page)
|
||||
.max_sidebar_width(300.0)
|
||||
.min_sidebar_width(300.0)
|
||||
.enable_hide_gesture(true)
|
||||
.enable_show_gesture(true)
|
||||
.build();
|
||||
|
||||
let _sidebar_toggle_button_binding = window_content_page_split_view
|
||||
.bind_property("show_sidebar", &sidebar_toggle_button, "active")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.build();
|
||||
|
||||
window_breakpoint.add_setter(
|
||||
&window_content_page_split_view,
|
||||
"collapsed",
|
||||
Some(&true.to_value()),
|
||||
);
|
||||
window_breakpoint.add_setter(
|
||||
&sidebar_toggle_button,
|
||||
"visible",
|
||||
Some(&true.to_value()),
|
||||
);
|
||||
window_breakpoint.add_setter(
|
||||
&window_headerbar,
|
||||
"show_title",
|
||||
Some(&true.to_value()),
|
||||
);
|
||||
|
||||
window_headerbar.pack_end(&sidebar_toggle_button);
|
||||
|
||||
window_toolbar.add_top_bar(&window_headerbar);
|
||||
window_toolbar.add_top_bar(&window_banner);
|
||||
|
||||
// create the main Application window
|
||||
let window = ApplicationWindow::builder()
|
||||
// The text on the titlebar
|
||||
.title(t!("application_name"))
|
||||
// link it to the application "app"
|
||||
.application(app)
|
||||
// Add the box called "window_box" to it
|
||||
// Application icon
|
||||
.icon_name(APP_ICON)
|
||||
// Minimum Size/Default
|
||||
.default_width(glib_settings.int("window-width"))
|
||||
.default_height(glib_settings.int("window-height"))
|
||||
//
|
||||
.width_request(700)
|
||||
.height_request(500)
|
||||
.content(&window_content_page_split_view)
|
||||
// Startup
|
||||
.startup_id(APP_ID)
|
||||
// build the window
|
||||
.build();
|
||||
|
||||
window.add_breakpoint(window_breakpoint);
|
||||
|
||||
if glib_settings.boolean("is-maximized") == true {
|
||||
window.maximize()
|
||||
}
|
||||
|
||||
window.connect_close_request(move |window| {
|
||||
if let Some(application) = window.application() {
|
||||
let size = window.default_size();
|
||||
let _ = glib_settings.set_int("window-width", size.0);
|
||||
let _ = glib_settings.set_int("window-height", size.1);
|
||||
let _ = glib_settings.set_boolean("is-maximized", window.is_maximized());
|
||||
application.remove_window(window);
|
||||
}
|
||||
glib::Propagation::Proceed
|
||||
});
|
||||
|
||||
let credits_button = gtk::Button::builder()
|
||||
.icon_name("dialog-information-symbolic")
|
||||
.build();
|
||||
|
||||
let refresh_button = gtk::Button::builder()
|
||||
.icon_name("view-refresh-symbolic")
|
||||
.tooltip_text(t!("refresh_button_tooltip_text"))
|
||||
.build();
|
||||
|
||||
let credits_window = AboutWindow::builder()
|
||||
.application_icon(APP_ICON)
|
||||
.application_name(t!("application_name"))
|
||||
.transient_for(&window)
|
||||
.version(VERSION)
|
||||
.hide_on_close(true)
|
||||
.developer_name(t!("developer_name"))
|
||||
.license_type(License::Mpl20)
|
||||
.issue_url(APP_GITHUB.to_owned() + "/issues")
|
||||
.build();
|
||||
|
||||
window_headerbar.pack_end(&refresh_button);
|
||||
window_headerbar.pack_end(&credits_button);
|
||||
credits_button.connect_clicked(move |_| credits_window.present());
|
||||
|
||||
// show the window
|
||||
|
||||
window.present();
|
||||
|
||||
// Flatpak Update Page
|
||||
|
||||
let flatpak_retry_signal_action = gio::SimpleAction::new("retry", None);
|
||||
|
||||
let flatpak_update_view_stack_bin = Bin::builder()
|
||||
.build();
|
||||
|
||||
flatpak_retry_signal_action.connect_activate(clone!(
|
||||
#[weak]
|
||||
window,
|
||||
#[strong]
|
||||
flatpak_retry_signal_action,
|
||||
#[strong]
|
||||
flatpak_update_view_stack_bin,
|
||||
move |_, _| {
|
||||
flatpak_update_view_stack_bin.set_child(Some(&flatpak_update_page::flatpak_update_page(
|
||||
window,
|
||||
&flatpak_retry_signal_action,
|
||||
)));
|
||||
}
|
||||
));
|
||||
|
||||
// Apt Update Page
|
||||
let apt_retry_signal_action = gio::SimpleAction::new("retry", None);
|
||||
|
||||
let flatpak_ran_once = Rc::new(RefCell::new(false));
|
||||
|
||||
let apt_update_view_stack_bin = Bin::builder().build();
|
||||
|
||||
apt_retry_signal_action.connect_activate(clone!(
|
||||
#[weak]
|
||||
window,
|
||||
#[strong]
|
||||
apt_retry_signal_action,
|
||||
#[strong]
|
||||
flatpak_retry_signal_action,
|
||||
#[strong]
|
||||
apt_update_view_stack_bin,
|
||||
#[weak]
|
||||
flatpak_ran_once,
|
||||
move |_, _| {
|
||||
apt_update_view_stack_bin.set_child(Some(&apt_update_page::apt_update_page(
|
||||
window,
|
||||
&apt_retry_signal_action,
|
||||
&flatpak_retry_signal_action,
|
||||
flatpak_ran_once,
|
||||
)));
|
||||
}
|
||||
));
|
||||
|
||||
apt_update_view_stack_bin.set_child(Some(&apt_update_page::apt_update_page(
|
||||
window.clone(),
|
||||
&apt_retry_signal_action,
|
||||
&flatpak_retry_signal_action,
|
||||
flatpak_ran_once,
|
||||
)));
|
||||
|
||||
// Add to stack switcher
|
||||
|
||||
window_adw_stack.add_titled(
|
||||
&apt_update_view_stack_bin,
|
||||
Some("apt_update_page"),
|
||||
&t!("apt_update_page_title"),
|
||||
);
|
||||
|
||||
window_adw_stack.add_titled(
|
||||
&flatpak_update_view_stack_bin,
|
||||
Some("flatpak_update_page"),
|
||||
&t!("flatpak_update_page_title"),
|
||||
);
|
||||
|
||||
// Refresh button
|
||||
|
||||
refresh_button.connect_clicked(clone!(
|
||||
#[weak]
|
||||
apt_retry_signal_action,
|
||||
#[weak]
|
||||
flatpak_retry_signal_action,
|
||||
#[weak]
|
||||
window_adw_stack,
|
||||
move |_| {
|
||||
match window_adw_stack.visible_child_name().unwrap().as_str() {
|
||||
"apt_update_page" => apt_retry_signal_action.activate(None),
|
||||
"flatpak_update_page" => flatpak_retry_signal_action.activate(None),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
@ -1,4 +0,0 @@
|
||||
pub const APP_ID: &str = "com.github.pikaos-linux.pikmanupdatemanager";
|
||||
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
|
||||
pub const APP_ICON: &str = "com.github.pikaos-linux.pikmanupdatemanager";
|
||||
pub const APP_GITHUB: &str = "https://github.com/PikaOS-Linux/pkg-pikman-update-manager";
|
@ -1,486 +0,0 @@
|
||||
use std::{cell::RefCell, sync::OnceLock};
|
||||
|
||||
use adw::*;
|
||||
use adw::{prelude::*, subclass::prelude::*};
|
||||
use glib::{clone, subclass::Signal, Properties};
|
||||
use gtk::*;
|
||||
use pretty_bytes::converter::convert;
|
||||
use std::env;
|
||||
|
||||
// ANCHOR: custom_button
|
||||
// Object holding the state
|
||||
#[derive(Properties, Default)]
|
||||
#[properties(wrapper_type = super::FlatpakRefRow)]
|
||||
pub struct FlatpakRefRow {
|
||||
#[property(get, set)]
|
||||
flatref_name: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
flatref_arch: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
flatref_ref_name: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
flatref_summary: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
flatref_remote_name: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
flatref_installed_size_installed: RefCell<u64>,
|
||||
#[property(get, set)]
|
||||
flatref_installed_size_remote: RefCell<u64>,
|
||||
#[property(get, set)]
|
||||
flatref_download_size: RefCell<u64>,
|
||||
#[property(get, set)]
|
||||
flatref_ref_format: RefCell<String>,
|
||||
#[property(get, set)]
|
||||
flatref_is_system: RefCell<bool>,
|
||||
#[property(get, set)]
|
||||
flatref_marked: RefCell<bool>,
|
||||
}
|
||||
// ANCHOR_END: custom_button
|
||||
|
||||
// The central trait for subclassing a GObject
|
||||
#[glib::object_subclass]
|
||||
impl ObjectSubclass for FlatpakRefRow {
|
||||
const NAME: &'static str = "FlatpakRefRow";
|
||||
type Type = super::FlatpakRefRow;
|
||||
type ParentType = ExpanderRow;
|
||||
}
|
||||
|
||||
// ANCHOR: object_impl
|
||||
// Trait shared by all GObjects
|
||||
#[glib::derived_properties]
|
||||
impl ObjectImpl for FlatpakRefRow {
|
||||
fn signals() -> &'static [Signal] {
|
||||
static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
|
||||
SIGNALS.get_or_init(|| {
|
||||
vec![
|
||||
Signal::builder("checkbutton-toggled").build(),
|
||||
Signal::builder("checkbutton-untoggled").build(),
|
||||
]
|
||||
})
|
||||
}
|
||||
fn constructed(&self) {
|
||||
let current_locale = match env::var_os("LANG") {
|
||||
Some(v) => v
|
||||
.into_string()
|
||||
.unwrap()
|
||||
.chars()
|
||||
.take_while(|&ch| ch != '.')
|
||||
.collect::<String>(),
|
||||
None => panic!("$LANG is not set"),
|
||||
};
|
||||
rust_i18n::set_locale(¤t_locale);
|
||||
|
||||
self.parent_constructed();
|
||||
|
||||
// Bind label to number
|
||||
// `SYNC_CREATE` ensures that the label will be immediately set
|
||||
let obj = self.obj();
|
||||
|
||||
let prefix_box = Box::new(Orientation::Vertical, 0);
|
||||
|
||||
let expandable_box = Box::new(Orientation::Vertical, 0);
|
||||
|
||||
obj.connect_flatref_name_notify(clone!(
|
||||
#[weak]
|
||||
prefix_box,
|
||||
#[weak]
|
||||
expandable_box,
|
||||
#[strong]
|
||||
obj,
|
||||
move |_| {
|
||||
remove_all_children_from_box(&prefix_box);
|
||||
remove_all_children_from_box(&expandable_box);
|
||||
//
|
||||
let flatref_name = obj.flatref_name();
|
||||
let flatref_arch = obj.flatref_arch();
|
||||
let flatref_ref_name = obj.flatref_ref_name();
|
||||
let flatref_summary = obj.flatref_summary();
|
||||
let flatref_remote_name = obj.flatref_remote_name();
|
||||
let flatref_installed_size_installed = obj.flatref_installed_size_installed();
|
||||
let flatref_installed_size_remote = obj.flatref_installed_size_remote();
|
||||
let flatref_download_size = obj.flatref_download_size();
|
||||
let flatref_ref_format = obj.flatref_download_size();
|
||||
let flatref_is_system = obj.flatref_is_system();
|
||||
let flatref_marked = obj.flatref_marked();
|
||||
//
|
||||
create_prefix_content(
|
||||
&prefix_box,
|
||||
&flatref_name,
|
||||
&flatref_arch,
|
||||
flatref_is_system,
|
||||
&flatref_remote_name,
|
||||
);
|
||||
//
|
||||
create_expandable_content(
|
||||
&obj,
|
||||
&expandable_box,
|
||||
flatref_ref_name,
|
||||
flatref_summary,
|
||||
flatref_download_size,
|
||||
flatref_installed_size_remote,
|
||||
);
|
||||
}
|
||||
));
|
||||
|
||||
obj.add_prefix(&prefix_box);
|
||||
obj.add_row(&expandable_box);
|
||||
|
||||
let suffix_toggle = CheckButton::builder()
|
||||
.tooltip_text(t!("mark_for_update"))
|
||||
.halign(Align::Center)
|
||||
.valign(Align::Center)
|
||||
.hexpand(false)
|
||||
.vexpand(false)
|
||||
.build();
|
||||
|
||||
suffix_toggle.connect_toggled(clone!(
|
||||
#[weak]
|
||||
obj,
|
||||
#[weak]
|
||||
suffix_toggle,
|
||||
move |_| {
|
||||
if suffix_toggle.is_active() {
|
||||
obj.emit_by_name::<()>("checkbutton-toggled", &[]);
|
||||
} else {
|
||||
obj.emit_by_name::<()>("checkbutton-untoggled", &[]);
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
obj.add_suffix(&suffix_toggle);
|
||||
|
||||
let obj = self.obj();
|
||||
obj.bind_property("flatref_marked", &suffix_toggle, "active")
|
||||
.sync_create()
|
||||
.bidirectional()
|
||||
.build();
|
||||
|
||||
// turn on by default
|
||||
obj.set_property("flatref_marked", true)
|
||||
}
|
||||
}
|
||||
// Trait shared by all widgets
|
||||
impl WidgetImpl for FlatpakRefRow {}
|
||||
|
||||
// Trait shared by all buttons
|
||||
// Trait shared by all buttons
|
||||
|
||||
impl ListBoxRowImpl for FlatpakRefRow {}
|
||||
impl PreferencesRowImpl for FlatpakRefRow {}
|
||||
impl ExpanderRowImpl for FlatpakRefRow {}
|
||||
|
||||
fn create_remote_badge(remote_name: &str) -> ListBox {
|
||||
let remote_label = Label::builder()
|
||||
.halign(Align::Start)
|
||||
.hexpand(false)
|
||||
.label(format!("{}: {}", t!("remote_label_label"), remote_name))
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(5)
|
||||
.margin_top(5)
|
||||
.build();
|
||||
|
||||
let boxedlist = ListBox::builder()
|
||||
.selection_mode(SelectionMode::None)
|
||||
.halign(Align::Start)
|
||||
.valign(Align::End)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(10)
|
||||
.build();
|
||||
|
||||
boxedlist.add_css_class("boxed-list");
|
||||
boxedlist.append(&remote_label);
|
||||
boxedlist
|
||||
}
|
||||
fn create_arch_badge(arch: &str) -> ListBox {
|
||||
let arch_label = Label::builder()
|
||||
.halign(Align::Start)
|
||||
.hexpand(false)
|
||||
.label(format!("{}: {}", t!("arch_label_label"), arch))
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(5)
|
||||
.margin_top(5)
|
||||
.build();
|
||||
|
||||
let boxedlist = ListBox::builder()
|
||||
.selection_mode(SelectionMode::None)
|
||||
.halign(Align::Start)
|
||||
.valign(Align::End)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(10)
|
||||
.build();
|
||||
|
||||
boxedlist.add_css_class("boxed-list");
|
||||
boxedlist.append(&arch_label);
|
||||
boxedlist
|
||||
}
|
||||
|
||||
fn create_system_badge(is_system: bool) -> ListBox {
|
||||
let system_label = Label::builder()
|
||||
.halign(Align::Start)
|
||||
.hexpand(false)
|
||||
.label(match is_system {
|
||||
true => "System",
|
||||
false => "User",
|
||||
})
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(5)
|
||||
.margin_top(5)
|
||||
.build();
|
||||
|
||||
let boxedlist = ListBox::builder()
|
||||
.selection_mode(SelectionMode::None)
|
||||
.halign(Align::Start)
|
||||
.valign(Align::End)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(10)
|
||||
.build();
|
||||
|
||||
boxedlist.add_css_class("boxed-list");
|
||||
boxedlist.append(&system_label);
|
||||
boxedlist
|
||||
}
|
||||
|
||||
fn remove_all_children_from_box(parent: >k::Box) {
|
||||
while let Some(child) = parent.last_child() {
|
||||
parent.remove(&child);
|
||||
}
|
||||
}
|
||||
|
||||
fn create_prefix_content(
|
||||
prefix_box: >k::Box,
|
||||
flatref_name: &str,
|
||||
flatref_arch: &str,
|
||||
flatref_is_system: bool,
|
||||
flatref_remote_name: &str,
|
||||
) {
|
||||
let package_label = Label::builder()
|
||||
.halign(Align::Start)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(5)
|
||||
.margin_top(5)
|
||||
.label(flatref_name)
|
||||
.build();
|
||||
package_label.add_css_class("size-20-bold-text");
|
||||
let prefix_badge_box = Box::new(Orientation::Horizontal, 0);
|
||||
prefix_badge_box.append(&create_remote_badge(flatref_remote_name));
|
||||
prefix_badge_box.append(&create_arch_badge(flatref_arch));
|
||||
prefix_badge_box.append(&create_system_badge(flatref_is_system));
|
||||
prefix_box.append(&package_label);
|
||||
prefix_box.append(&prefix_badge_box);
|
||||
}
|
||||
|
||||
fn create_expandable_content(
|
||||
flatpak_package_row: &impl IsA<ExpanderRow>,
|
||||
expandable_box: >k::Box,
|
||||
flatref_ref_name: String,
|
||||
flatref_summary: String,
|
||||
flatref_download_size: u64,
|
||||
flatref_installed_size_remote: u64,
|
||||
) {
|
||||
let expandable_page_selection_box = Box::builder()
|
||||
.orientation(Orientation::Horizontal)
|
||||
.hexpand(false)
|
||||
.vexpand(false)
|
||||
.halign(Align::Start)
|
||||
.valign(Align::Start)
|
||||
.margin_bottom(10)
|
||||
.margin_top(10)
|
||||
.margin_start(10)
|
||||
.margin_end(10)
|
||||
.build();
|
||||
expandable_page_selection_box.add_css_class("linked");
|
||||
//
|
||||
let summary_page_button = ToggleButton::builder()
|
||||
.label(t!("summary_button_label"))
|
||||
.active(true)
|
||||
.build();
|
||||
let extra_info_page_button = ToggleButton::builder()
|
||||
.label(t!("extra_info_page_button_label"))
|
||||
.group(&summary_page_button)
|
||||
.build();
|
||||
expandable_page_selection_box.append(&summary_page_button);
|
||||
expandable_page_selection_box.append(&extra_info_page_button);
|
||||
//
|
||||
expandable_box.append(&expandable_page_selection_box);
|
||||
//
|
||||
let expandable_bin = Bin::builder().hexpand(true).vexpand(true).build();
|
||||
//
|
||||
summary_page_button.connect_clicked(clone!(
|
||||
#[strong]
|
||||
expandable_bin,
|
||||
#[strong]
|
||||
summary_page_button,
|
||||
move |_| {
|
||||
if summary_page_button.is_active() {
|
||||
expandable_bin.set_child(Some(&summary_stack_page(&flatref_summary)));
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
extra_info_page_button.connect_clicked(clone!(
|
||||
#[strong]
|
||||
expandable_bin,
|
||||
#[strong]
|
||||
extra_info_page_button,
|
||||
move |_| {
|
||||
if extra_info_page_button.is_active() {
|
||||
expandable_bin.set_child(Some(&extra_info_stack_page(
|
||||
&flatref_ref_name,
|
||||
flatref_download_size,
|
||||
flatref_installed_size_remote,
|
||||
)));
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
flatpak_package_row.connect_expanded_notify(clone!(
|
||||
#[strong]
|
||||
expandable_bin,
|
||||
#[strong]
|
||||
expandable_box,
|
||||
#[strong]
|
||||
flatpak_package_row,
|
||||
#[strong]
|
||||
summary_page_button,
|
||||
move |_| {
|
||||
if flatpak_package_row.property("expanded") {
|
||||
summary_page_button.set_active(true);
|
||||
summary_page_button.emit_by_name::<()>("clicked", &[]);
|
||||
expandable_box.append(&expandable_bin)
|
||||
} else {
|
||||
expandable_box.remove(&expandable_bin)
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
|
||||
fn summary_stack_page(flatref_summary: &str) -> gtk::Box {
|
||||
let summary_content_box = Box::builder()
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.orientation(Orientation::Vertical)
|
||||
.build();
|
||||
let summary_text_buffer = TextBuffer::builder()
|
||||
.text(flatref_summary.to_owned() + "\n")
|
||||
.build();
|
||||
let summary_text_view = TextView::builder()
|
||||
.buffer(&summary_text_buffer)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.margin_top(0)
|
||||
.margin_bottom(10)
|
||||
.margin_start(15)
|
||||
.margin_end(15)
|
||||
.editable(false)
|
||||
.build();
|
||||
summary_content_box.append(&summary_text_view);
|
||||
summary_content_box
|
||||
}
|
||||
|
||||
fn extra_info_stack_page(
|
||||
flatref_ref_name: &str,
|
||||
flatref_download_size: u64,
|
||||
flatref_installed_size_remote: u64,
|
||||
) -> gtk::Box {
|
||||
let extra_info_badges_content_box = Box::builder()
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.orientation(Orientation::Vertical)
|
||||
.build();
|
||||
let extra_info_badges_size_group = SizeGroup::new(SizeGroupMode::Both);
|
||||
let extra_info_badges_size_group0 = SizeGroup::new(SizeGroupMode::Both);
|
||||
let extra_info_badges_size_group1 = SizeGroup::new(SizeGroupMode::Both);
|
||||
let package_size = flatref_download_size as f64;
|
||||
let package_installed_size = flatref_installed_size_remote as f64;
|
||||
extra_info_badges_content_box.append(&create_color_badge(
|
||||
&t!("flatpak_extra_info_ref_name").to_string(),
|
||||
flatref_ref_name,
|
||||
"background-accent-bg",
|
||||
&extra_info_badges_size_group,
|
||||
&extra_info_badges_size_group0,
|
||||
&extra_info_badges_size_group1,
|
||||
));
|
||||
extra_info_badges_content_box.append(&create_color_badge(
|
||||
&t!("flatpak_extra_info_download_size").to_string(),
|
||||
&convert(package_size),
|
||||
"background-accent-bg",
|
||||
&extra_info_badges_size_group,
|
||||
&extra_info_badges_size_group0,
|
||||
&extra_info_badges_size_group1,
|
||||
));
|
||||
extra_info_badges_content_box.append(&create_color_badge(
|
||||
&t!("flatpak_extra_info_installed_size").to_string(),
|
||||
&convert(package_installed_size),
|
||||
"background-accent-bg",
|
||||
&extra_info_badges_size_group,
|
||||
&extra_info_badges_size_group0,
|
||||
&extra_info_badges_size_group1,
|
||||
));
|
||||
extra_info_badges_content_box
|
||||
}
|
||||
fn create_color_badge(
|
||||
label0_text: &str,
|
||||
label1_text: &str,
|
||||
css_style: &str,
|
||||
group_size: &SizeGroup,
|
||||
group_size0: &SizeGroup,
|
||||
group_size1: &SizeGroup,
|
||||
) -> ListBox {
|
||||
let badge_box = Box::builder().build();
|
||||
|
||||
let label0 = Label::builder()
|
||||
.label(label0_text)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(1)
|
||||
.margin_top(1)
|
||||
.valign(Align::Center)
|
||||
.halign(Align::Center)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
group_size0.add_widget(&label0);
|
||||
|
||||
let label_separator = Separator::builder().build();
|
||||
|
||||
let label1 = Label::builder()
|
||||
.label(label1_text)
|
||||
.margin_start(3)
|
||||
.margin_end(0)
|
||||
.margin_bottom(1)
|
||||
.margin_top(1)
|
||||
.valign(Align::Center)
|
||||
.halign(Align::Center)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
group_size1.add_widget(&label1);
|
||||
|
||||
label1.add_css_class(css_style);
|
||||
|
||||
badge_box.append(&label0);
|
||||
badge_box.append(&label_separator);
|
||||
badge_box.append(&label1);
|
||||
|
||||
let boxedlist = ListBox::builder()
|
||||
.selection_mode(SelectionMode::None)
|
||||
.halign(Align::Start)
|
||||
.valign(Align::Start)
|
||||
.margin_start(10)
|
||||
.margin_end(10)
|
||||
.margin_bottom(10)
|
||||
.margin_top(10)
|
||||
.build();
|
||||
|
||||
boxedlist.add_css_class("boxed-list");
|
||||
boxedlist.append(&badge_box);
|
||||
group_size.add_widget(&boxedlist);
|
||||
boxedlist
|
||||
}
|
@ -1,54 +0,0 @@
|
||||
mod imp;
|
||||
|
||||
use crate::flatpak_update_page::FlatpakRefStruct;
|
||||
use glib::Object;
|
||||
use gtk::glib;
|
||||
|
||||
glib::wrapper! {
|
||||
pub struct FlatpakRefRow(ObjectSubclass<imp::FlatpakRefRow>)
|
||||
@extends adw::ExpanderRow, gtk::Widget, gtk::ListBoxRow, adw::PreferencesRow,
|
||||
@implements gtk::Accessible, gtk::Actionable, gtk::Buildable, gtk::ConstraintTarget;
|
||||
}
|
||||
|
||||
impl FlatpakRefRow {
|
||||
pub fn new(flatref: &FlatpakRefStruct) -> Self {
|
||||
let flatref = flatref.clone();
|
||||
Object::builder()
|
||||
.property("flatref-name", flatref.name)
|
||||
.property("flatref-arch", flatref.arch)
|
||||
.property("flatref-ref-name", flatref.ref_name)
|
||||
.property("flatref-summary", flatref.summary)
|
||||
.property("flatref-remote-name", flatref.remote_name)
|
||||
.property(
|
||||
"flatref-installed-size-installed",
|
||||
flatref.installed_size_installed,
|
||||
)
|
||||
.property(
|
||||
"flatref-installed-size-remote",
|
||||
flatref.installed_size_remote,
|
||||
)
|
||||
.property("flatref-download-size", flatref.download_size)
|
||||
.property("flatref-ref-format", flatref.ref_format)
|
||||
.property("flatref-is-system", flatref.is_system)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
// ANCHOR_END: mod
|
||||
|
||||
impl Default for FlatpakRefRow {
|
||||
fn default() -> Self {
|
||||
Self::new(&FlatpakRefStruct {
|
||||
ref_name: "??".to_owned(),
|
||||
name: "??".to_owned(),
|
||||
arch: "??".to_owned(),
|
||||
summary: "??".to_owned(),
|
||||
remote_name: "??".to_owned(),
|
||||
installed_size_installed: 0,
|
||||
installed_size_remote: 0,
|
||||
download_size: 0,
|
||||
ref_format: "??".to_owned(),
|
||||
is_system: false,
|
||||
is_last: false,
|
||||
})
|
||||
}
|
||||
}
|
@ -1,784 +0,0 @@
|
||||
mod process;
|
||||
|
||||
use crate::apt_package_row::AptPackageRow;
|
||||
use crate::flatpak_ref_row::FlatpakRefRow;
|
||||
use adw::gio::SimpleAction;
|
||||
use adw::prelude::*;
|
||||
use gtk::glib::*;
|
||||
use gtk::*;
|
||||
use libflatpak::prelude::*;
|
||||
use libflatpak::InstalledRef;
|
||||
use std::cell::RefCell;
|
||||
use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
use std::thread;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct FlatpakRefStruct {
|
||||
pub ref_name: String,
|
||||
pub name: String,
|
||||
pub arch: String,
|
||||
pub summary: String,
|
||||
pub remote_name: String,
|
||||
pub installed_size_installed: u64,
|
||||
pub installed_size_remote: u64,
|
||||
pub download_size: u64,
|
||||
pub ref_format: String,
|
||||
pub is_system: bool,
|
||||
pub is_last: bool,
|
||||
}
|
||||
pub fn flatpak_update_page(
|
||||
window: adw::ApplicationWindow,
|
||||
retry_signal_action: &SimpleAction,
|
||||
) -> gtk::Box {
|
||||
let (appstream_sync_percent_sender, appstream_sync_percent_receiver) =
|
||||
async_channel::unbounded::<u32>();
|
||||
let appstream_sync_percent_sender = appstream_sync_percent_sender.clone();
|
||||
let (appstream_sync_status_sender, appstream_sync_status_receiver) =
|
||||
async_channel::unbounded::<String>();
|
||||
let appstream_sync_status_sender = appstream_sync_status_sender.clone();
|
||||
let appstream_sync_status_sender_clone0 = appstream_sync_status_sender.clone();
|
||||
|
||||
let system_refs_for_upgrade_vec: Rc<RefCell<Vec<FlatpakRefRow>>> =
|
||||
Rc::new(RefCell::new(Vec::new()));
|
||||
|
||||
let user_refs_for_upgrade_vec: Rc<RefCell<Vec<FlatpakRefRow>>> =
|
||||
Rc::new(RefCell::new(Vec::new()));
|
||||
|
||||
let system_refs_for_upgrade_vec_all: Rc<RefCell<Vec<FlatpakRefRow>>> =
|
||||
Rc::new(RefCell::new(Vec::new()));
|
||||
|
||||
let user_refs_for_upgrade_vec_all: Rc<RefCell<Vec<FlatpakRefRow>>> =
|
||||
Rc::new(RefCell::new(Vec::new()));
|
||||
|
||||
let cancellable_no = libflatpak::gio::Cancellable::NONE;
|
||||
|
||||
thread::spawn(move || {
|
||||
let cancellable_no = libflatpak::gio::Cancellable::NONE;
|
||||
let flatpak_system_installation =
|
||||
libflatpak::Installation::new_user(cancellable_no).unwrap();
|
||||
if let Ok(remotes) =
|
||||
libflatpak::Installation::list_remotes(&flatpak_system_installation, cancellable_no)
|
||||
{
|
||||
for remote in remotes {
|
||||
if remote.is_disabled() {
|
||||
continue;
|
||||
};
|
||||
let mut remote_clousre = |status: &str, progress: u32, _: bool| {
|
||||
appstream_sync_percent_sender
|
||||
.send_blocking(progress)
|
||||
.expect("appstream_sync_percent_receiver closed");
|
||||
appstream_sync_status_sender
|
||||
.send_blocking(format!(
|
||||
"{} - {}: {}",
|
||||
t!("flatpak_type_system"),
|
||||
remote.name().unwrap_or("Unknown remote".into()),
|
||||
status
|
||||
))
|
||||
.expect("appstream_sync_status_receiver closed");
|
||||
};
|
||||
match libflatpak::Installation::update_appstream_full_sync(
|
||||
&flatpak_system_installation,
|
||||
&remote.name().unwrap(),
|
||||
None,
|
||||
Some(&mut remote_clousre),
|
||||
cancellable_no,
|
||||
) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
appstream_sync_status_sender
|
||||
.send_blocking(e.to_string())
|
||||
.expect("appstream_sync_status_receiver closed");
|
||||
appstream_sync_status_sender
|
||||
.send_blocking("FN_OVERRIDE_FAILED".to_owned())
|
||||
.expect("appstream_sync_status_receiver closed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let flatpak_user_installation = libflatpak::Installation::new_user(cancellable_no).unwrap();
|
||||
if let Ok(remotes) =
|
||||
libflatpak::Installation::list_remotes(&flatpak_user_installation, cancellable_no)
|
||||
{
|
||||
for remote in remotes {
|
||||
if remote.is_disabled() {
|
||||
continue;
|
||||
};
|
||||
let mut remote_clousre = |status: &str, progress: u32, _: bool| {
|
||||
appstream_sync_percent_sender
|
||||
.send_blocking(progress)
|
||||
.expect("appstream_sync_percent_receiver closed");
|
||||
appstream_sync_status_sender
|
||||
.send_blocking(format!(
|
||||
"{} - {}: {}",
|
||||
t!("flatpak_type_user"),
|
||||
remote.name().unwrap_or("Unknown remote".into()),
|
||||
status
|
||||
))
|
||||
.expect("appstream_sync_status_receiver closed");
|
||||
};
|
||||
match libflatpak::Installation::update_appstream_full_sync(
|
||||
&flatpak_user_installation,
|
||||
&remote.name().unwrap(),
|
||||
None,
|
||||
Some(&mut remote_clousre),
|
||||
cancellable_no,
|
||||
) {
|
||||
Ok(_) => {
|
||||
appstream_sync_status_sender
|
||||
.send_blocking("FN_OVERRIDE_SUCCESSFUL".to_owned())
|
||||
.expect("appstream_sync_status_receiver closed");
|
||||
}
|
||||
Err(e) => {
|
||||
appstream_sync_status_sender
|
||||
.send_blocking(e.to_string())
|
||||
.expect("appstream_sync_status_receiver closed");
|
||||
appstream_sync_status_sender
|
||||
.send_blocking("FN_OVERRIDE_FAILED".to_owned())
|
||||
.expect("appstream_sync_status_receiver closed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let main_box = Box::builder()
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.orientation(Orientation::Vertical)
|
||||
.build();
|
||||
|
||||
let searchbar = SearchEntry::builder()
|
||||
.search_delay(500)
|
||||
.margin_top(15)
|
||||
.margin_bottom(15)
|
||||
.margin_end(30)
|
||||
.margin_start(30)
|
||||
.build();
|
||||
searchbar.add_css_class("rounded-all-25");
|
||||
|
||||
let packages_boxedlist = ListBox::builder()
|
||||
.selection_mode(SelectionMode::None)
|
||||
.sensitive(false)
|
||||
.build();
|
||||
packages_boxedlist.add_css_class("boxed-list");
|
||||
packages_boxedlist.add_css_class("round-all-scroll");
|
||||
|
||||
let packages_viewport = ScrolledWindow::builder()
|
||||
.vexpand(true)
|
||||
.hexpand(true)
|
||||
.has_frame(true)
|
||||
.margin_bottom(15)
|
||||
.margin_top(15)
|
||||
.margin_end(15)
|
||||
.margin_start(15)
|
||||
.height_request(390)
|
||||
.child(&packages_boxedlist)
|
||||
.build();
|
||||
packages_viewport.add_css_class("round-all-scroll");
|
||||
|
||||
let packages_no_viewport_page = adw::StatusPage::builder()
|
||||
.icon_name("emblem-default-symbolic")
|
||||
.title(t!("flatpak_packages_no_viewport_page_title"))
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
|
||||
let viewport_bin = adw::Bin::builder()
|
||||
.child(&packages_no_viewport_page)
|
||||
.build();
|
||||
|
||||
let flatpak_update_dialog_child_box = Box::builder().orientation(Orientation::Vertical).build();
|
||||
|
||||
let flatpak_update_dialog_progress_bar =
|
||||
ProgressBar::builder().show_text(true).hexpand(true).build();
|
||||
|
||||
let flatpak_update_dialog_spinner = Spinner::builder()
|
||||
.hexpand(true)
|
||||
.valign(Align::Start)
|
||||
.halign(Align::Center)
|
||||
.spinning(true)
|
||||
.height_request(128)
|
||||
.width_request(128)
|
||||
.build();
|
||||
|
||||
flatpak_update_dialog_child_box.append(&flatpak_update_dialog_spinner);
|
||||
flatpak_update_dialog_child_box.append(&flatpak_update_dialog_progress_bar);
|
||||
|
||||
let flatpak_update_dialog = adw::MessageDialog::builder()
|
||||
.transient_for(&window)
|
||||
.extra_child(&flatpak_update_dialog_child_box)
|
||||
.heading(t!("flatpak_update_dialog_heading"))
|
||||
.width_request(500)
|
||||
.build();
|
||||
|
||||
flatpak_update_dialog.add_response(
|
||||
"flatpak_update_dialog_retry",
|
||||
&t!("flatpak_update_dialog_retry_label").to_string(),
|
||||
);
|
||||
|
||||
flatpak_update_dialog.set_response_appearance(
|
||||
"flatpak_update_dialog_retry",
|
||||
adw::ResponseAppearance::Suggested,
|
||||
);
|
||||
|
||||
flatpak_update_dialog.set_response_enabled("flatpak_update_dialog_retry", false);
|
||||
|
||||
let retry_signal_action0 = retry_signal_action.clone();
|
||||
|
||||
flatpak_update_dialog
|
||||
.clone()
|
||||
.choose(None::<&gio::Cancellable>, move |choice| {
|
||||
if choice == "flatpak_update_dialog_retry" {
|
||||
retry_signal_action0.activate(None);
|
||||
}
|
||||
});
|
||||
|
||||
let bottom_bar = Box::builder().valign(Align::End).build();
|
||||
|
||||
let select_button = Button::builder()
|
||||
.halign(Align::End)
|
||||
.valign(Align::Center)
|
||||
.hexpand(true)
|
||||
.margin_start(10)
|
||||
.margin_end(10)
|
||||
.margin_bottom(15)
|
||||
.label(t!("select_button_deselect_all"))
|
||||
.build();
|
||||
|
||||
select_button.connect_clicked(clone!(
|
||||
#[weak]
|
||||
select_button,
|
||||
#[weak]
|
||||
packages_boxedlist,
|
||||
move |_| {
|
||||
let select_button_label = select_button.label().unwrap();
|
||||
let value_to_mark = if select_button_label == t!("select_button_select_all").to_string()
|
||||
{
|
||||
true
|
||||
} else if select_button_label == t!("select_button_deselect_all").to_string() {
|
||||
false
|
||||
} else {
|
||||
panic!("Unexpected label on selection button")
|
||||
};
|
||||
set_all_flatpak_row_marks_to(&packages_boxedlist, value_to_mark)
|
||||
}
|
||||
));
|
||||
|
||||
let update_button = Button::builder()
|
||||
.halign(Align::End)
|
||||
.valign(Align::Center)
|
||||
.hexpand(false)
|
||||
.margin_start(10)
|
||||
.margin_end(30)
|
||||
.margin_bottom(15)
|
||||
.label(t!("update_button_label"))
|
||||
.build();
|
||||
update_button.add_css_class("destructive-action");
|
||||
|
||||
let system_refs_for_upgrade_vec_all_clone0 = &system_refs_for_upgrade_vec_all.clone();
|
||||
let user_refs_for_upgrade_vec_all_clone0 = user_refs_for_upgrade_vec_all.clone();
|
||||
|
||||
let system_refs_for_upgrade_vec_clone0 = system_refs_for_upgrade_vec.clone();
|
||||
let user_refs_for_upgrade_vec_clone0 = user_refs_for_upgrade_vec.clone();
|
||||
|
||||
update_button.connect_clicked(clone!(
|
||||
#[weak]
|
||||
window,
|
||||
#[weak]
|
||||
retry_signal_action,
|
||||
#[strong]
|
||||
system_refs_for_upgrade_vec_all_clone0,
|
||||
#[strong]
|
||||
user_refs_for_upgrade_vec_all_clone0,
|
||||
move |_| {
|
||||
process::flatpak_process_update(
|
||||
Some(&system_refs_for_upgrade_vec_clone0.borrow()),
|
||||
Some(&user_refs_for_upgrade_vec_clone0.borrow()),
|
||||
&system_refs_for_upgrade_vec_all_clone0.borrow(),
|
||||
&user_refs_for_upgrade_vec_all_clone0.borrow(),
|
||||
window,
|
||||
&retry_signal_action,
|
||||
)
|
||||
}
|
||||
));
|
||||
|
||||
bottom_bar.append(&select_button);
|
||||
bottom_bar.append(&update_button);
|
||||
|
||||
let appstream_sync_percent_server_context = MainContext::default();
|
||||
// The main loop executes the asynchronous block
|
||||
appstream_sync_percent_server_context.spawn_local(clone!(
|
||||
#[weak]
|
||||
flatpak_update_dialog_progress_bar,
|
||||
async move {
|
||||
while let Ok(state) = appstream_sync_percent_receiver.recv().await {
|
||||
flatpak_update_dialog_progress_bar.set_fraction(state as f64 / 100.0)
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
let appstream_sync_status_server_context = MainContext::default();
|
||||
// The main loop executes the asynchronous block
|
||||
appstream_sync_status_server_context.spawn_local(clone!(
|
||||
#[weak]
|
||||
flatpak_update_dialog,
|
||||
#[weak]
|
||||
flatpak_update_dialog_child_box,
|
||||
#[strong]
|
||||
packages_boxedlist,
|
||||
#[strong]
|
||||
system_refs_for_upgrade_vec_all,
|
||||
#[strong]
|
||||
user_refs_for_upgrade_vec_all,
|
||||
#[strong]
|
||||
system_refs_for_upgrade_vec,
|
||||
#[strong]
|
||||
user_refs_for_upgrade_vec,
|
||||
#[strong]
|
||||
viewport_bin,
|
||||
#[strong]
|
||||
packages_viewport,
|
||||
async move {
|
||||
while let Ok(state) = appstream_sync_status_receiver.recv().await {
|
||||
match state.as_ref() {
|
||||
"FN_OVERRIDE_SUCCESSFUL" => {
|
||||
let flatpak_system_installation =
|
||||
libflatpak::Installation::new_system(cancellable_no).unwrap();
|
||||
let flatpak_system_updates = flatpak_system_installation
|
||||
.list_installed_refs_for_update(cancellable_no)
|
||||
.unwrap();
|
||||
let flatpak_system_transaction = libflatpak::Transaction::for_installation(
|
||||
&flatpak_system_installation,
|
||||
cancellable_no,
|
||||
)
|
||||
.unwrap();
|
||||
//
|
||||
let flatpak_user_installation =
|
||||
libflatpak::Installation::new_user(cancellable_no).unwrap();
|
||||
let flatpak_user_updates = flatpak_user_installation
|
||||
.list_installed_refs_for_update(cancellable_no)
|
||||
.unwrap();
|
||||
let flatpak_user_transaction = libflatpak::Transaction::for_installation(
|
||||
&flatpak_user_installation,
|
||||
cancellable_no,
|
||||
)
|
||||
.unwrap();
|
||||
//
|
||||
let mut system_last_triggered = false;
|
||||
let mut user_last_triggered = false;
|
||||
//
|
||||
if !flatpak_system_updates.is_empty() || !flatpak_user_updates.is_empty() {
|
||||
viewport_bin.set_child(Some(&packages_viewport));
|
||||
//
|
||||
let mut flatpak_system_updates_iter =
|
||||
&mut flatpak_system_updates.iter().peekable();
|
||||
//
|
||||
while let Some(flatpak_ref) = flatpak_system_updates_iter.next() {
|
||||
let mut remote_flatpak_ref: Option<libflatpak::RemoteRef> = None;
|
||||
while let Ok(remotes) = libflatpak::Installation::list_remotes(
|
||||
&flatpak_system_installation,
|
||||
cancellable_no,
|
||||
) {
|
||||
for remote in remotes {
|
||||
if remote.is_disabled() {
|
||||
continue;
|
||||
};
|
||||
match libflatpak::Installation::fetch_remote_ref_sync(
|
||||
&flatpak_system_installation,
|
||||
&match remote.name() {
|
||||
Some(t) => t,
|
||||
None => continue,
|
||||
},
|
||||
flatpak_ref.kind(),
|
||||
&match flatpak_ref.name() {
|
||||
Some(t) => t,
|
||||
None => continue,
|
||||
},
|
||||
flatpak_ref.arch().as_deref(),
|
||||
flatpak_ref.branch().as_deref(),
|
||||
cancellable_no,
|
||||
) {
|
||||
Ok(t) => {
|
||||
remote_flatpak_ref = Some(t);
|
||||
break;
|
||||
}
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
if remote_flatpak_ref.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let flatref_struct = FlatpakRefStruct {
|
||||
ref_name: flatpak_ref
|
||||
.name()
|
||||
.unwrap_or("Unknown".into())
|
||||
.to_string(),
|
||||
name: flatpak_ref
|
||||
.appdata_name()
|
||||
.unwrap_or(flatpak_ref.name().unwrap_or("Unknown".into()))
|
||||
.to_string(),
|
||||
arch: flatpak_ref
|
||||
.arch()
|
||||
.unwrap_or("Unknown Arch".into())
|
||||
.to_string(),
|
||||
summary: flatpak_ref
|
||||
.appdata_summary()
|
||||
.unwrap_or("No Summary".into())
|
||||
.to_string(),
|
||||
remote_name: match remote_flatpak_ref {
|
||||
Some(ref t) => {
|
||||
t.remote_name().unwrap_or("Unknown".into()).to_string()
|
||||
}
|
||||
None => "Unknown".into(),
|
||||
},
|
||||
installed_size_installed: flatpak_ref.installed_size(),
|
||||
installed_size_remote: match remote_flatpak_ref {
|
||||
Some(ref t) => t.installed_size(),
|
||||
None => 0,
|
||||
},
|
||||
download_size: match remote_flatpak_ref {
|
||||
Some(t) => t.download_size(),
|
||||
None => 0,
|
||||
},
|
||||
ref_format: flatpak_ref.format_ref().unwrap().into(),
|
||||
is_system: true,
|
||||
is_last: flatpak_system_updates_iter.peek().is_none(),
|
||||
};
|
||||
|
||||
let flatpak_row = FlatpakRefRow::new(&flatref_struct);
|
||||
|
||||
system_refs_for_upgrade_vec
|
||||
.borrow_mut()
|
||||
.push(flatpak_row.clone());
|
||||
|
||||
system_refs_for_upgrade_vec_all
|
||||
.borrow_mut()
|
||||
.push(flatpak_row.clone());
|
||||
|
||||
flatpak_row.connect_closure(
|
||||
"checkbutton-toggled",
|
||||
false,
|
||||
closure_local!(
|
||||
#[strong]
|
||||
select_button,
|
||||
#[strong]
|
||||
update_button,
|
||||
#[strong]
|
||||
packages_boxedlist,
|
||||
#[strong]
|
||||
system_refs_for_upgrade_vec,
|
||||
move |flatpak_row: FlatpakRefRow| {
|
||||
if is_widget_select_all_ready(&packages_boxedlist) {
|
||||
select_button.set_label(
|
||||
&t!("select_button_select_all").to_string(),
|
||||
);
|
||||
} else {
|
||||
select_button.set_label(
|
||||
&t!("select_button_deselect_all").to_string(),
|
||||
);
|
||||
}
|
||||
update_button.set_sensitive(!is_all_children_unmarked(
|
||||
&packages_boxedlist,
|
||||
));
|
||||
system_refs_for_upgrade_vec
|
||||
.borrow_mut()
|
||||
.push(flatpak_row);
|
||||
}
|
||||
),
|
||||
);
|
||||
flatpak_row.connect_closure(
|
||||
"checkbutton-untoggled",
|
||||
false,
|
||||
closure_local!(
|
||||
#[strong]
|
||||
select_button,
|
||||
#[strong]
|
||||
update_button,
|
||||
#[strong]
|
||||
packages_boxedlist,
|
||||
#[strong]
|
||||
system_refs_for_upgrade_vec,
|
||||
move |flatpak_row: FlatpakRefRow| {
|
||||
select_button.set_label(
|
||||
&t!("select_button_select_all").to_string(),
|
||||
);
|
||||
update_button.set_sensitive(!is_all_children_unmarked(
|
||||
&packages_boxedlist,
|
||||
));
|
||||
system_refs_for_upgrade_vec.borrow_mut().retain(|x| {
|
||||
x.flatref_ref_format()
|
||||
!= flatpak_row.flatref_ref_format()
|
||||
});
|
||||
}
|
||||
),
|
||||
);
|
||||
|
||||
packages_boxedlist.append(&flatpak_row);
|
||||
if flatpak_system_updates.is_empty()
|
||||
|| flatref_struct.is_system && flatref_struct.is_last
|
||||
{
|
||||
system_last_triggered = true
|
||||
}
|
||||
}
|
||||
//
|
||||
let mut flatpak_user_updates_iter =
|
||||
&mut flatpak_user_updates.iter().peekable();
|
||||
//
|
||||
while let Some(flatpak_ref) = flatpak_user_updates_iter.next() {
|
||||
let mut remote_flatpak_ref: Option<libflatpak::RemoteRef> = None;
|
||||
while let Ok(remotes) = libflatpak::Installation::list_remotes(
|
||||
&flatpak_user_installation,
|
||||
cancellable_no,
|
||||
) {
|
||||
for remote in remotes {
|
||||
if remote.is_disabled() {
|
||||
continue;
|
||||
};
|
||||
match libflatpak::Installation::fetch_remote_ref_sync(
|
||||
&flatpak_user_installation,
|
||||
&match remote.name() {
|
||||
Some(t) => t,
|
||||
None => continue,
|
||||
},
|
||||
flatpak_ref.kind(),
|
||||
&match flatpak_ref.name() {
|
||||
Some(t) => t,
|
||||
None => continue,
|
||||
},
|
||||
flatpak_ref.arch().as_deref(),
|
||||
flatpak_ref.branch().as_deref(),
|
||||
cancellable_no,
|
||||
) {
|
||||
Ok(t) => {
|
||||
remote_flatpak_ref = Some(t);
|
||||
break;
|
||||
}
|
||||
Err(_) => continue,
|
||||
}
|
||||
}
|
||||
if remote_flatpak_ref.is_some() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
let flatref_struct = FlatpakRefStruct {
|
||||
ref_name: flatpak_ref
|
||||
.name()
|
||||
.unwrap_or("Unknown".into())
|
||||
.to_string(),
|
||||
name: flatpak_ref
|
||||
.appdata_name()
|
||||
.unwrap_or(flatpak_ref.name().unwrap_or("Unknown".into()))
|
||||
.to_string(),
|
||||
arch: flatpak_ref
|
||||
.arch()
|
||||
.unwrap_or("Unknown Arch".into())
|
||||
.to_string(),
|
||||
summary: flatpak_ref
|
||||
.appdata_summary()
|
||||
.unwrap_or("No Summary".into())
|
||||
.to_string(),
|
||||
remote_name: match remote_flatpak_ref {
|
||||
Some(ref t) => {
|
||||
t.remote_name().unwrap_or("Unknown".into()).to_string()
|
||||
}
|
||||
None => "Unknown".into(),
|
||||
},
|
||||
installed_size_installed: flatpak_ref.installed_size(),
|
||||
installed_size_remote: match remote_flatpak_ref {
|
||||
Some(ref t) => t.installed_size(),
|
||||
None => 0,
|
||||
},
|
||||
download_size: match remote_flatpak_ref {
|
||||
Some(t) => t.download_size(),
|
||||
None => 0,
|
||||
},
|
||||
ref_format: flatpak_ref.format_ref().unwrap().into(),
|
||||
is_system: false,
|
||||
is_last: flatpak_user_updates_iter.peek().is_none(),
|
||||
};
|
||||
|
||||
let flatpak_row = FlatpakRefRow::new(&flatref_struct);
|
||||
|
||||
user_refs_for_upgrade_vec
|
||||
.borrow_mut()
|
||||
.push(flatpak_row.clone());
|
||||
|
||||
user_refs_for_upgrade_vec_all
|
||||
.borrow_mut()
|
||||
.push(flatpak_row.clone());
|
||||
|
||||
flatpak_row.connect_closure(
|
||||
"checkbutton-toggled",
|
||||
false,
|
||||
closure_local!(
|
||||
#[strong]
|
||||
select_button,
|
||||
#[strong]
|
||||
update_button,
|
||||
#[strong]
|
||||
packages_boxedlist,
|
||||
#[strong]
|
||||
user_refs_for_upgrade_vec,
|
||||
move |flatpak_row: FlatpakRefRow| {
|
||||
if is_widget_select_all_ready(&packages_boxedlist) {
|
||||
select_button.set_label(
|
||||
&t!("select_button_select_all").to_string(),
|
||||
);
|
||||
} else {
|
||||
select_button.set_label(
|
||||
&t!("select_button_deselect_all").to_string(),
|
||||
);
|
||||
}
|
||||
update_button.set_sensitive(!is_all_children_unmarked(
|
||||
&packages_boxedlist,
|
||||
));
|
||||
user_refs_for_upgrade_vec
|
||||
.borrow_mut()
|
||||
.push(flatpak_row);
|
||||
}
|
||||
),
|
||||
);
|
||||
flatpak_row.connect_closure(
|
||||
"checkbutton-untoggled",
|
||||
false,
|
||||
closure_local!(
|
||||
#[strong]
|
||||
select_button,
|
||||
#[strong]
|
||||
update_button,
|
||||
#[strong]
|
||||
packages_boxedlist,
|
||||
#[strong]
|
||||
user_refs_for_upgrade_vec,
|
||||
move |flatpak_row: FlatpakRefRow| {
|
||||
select_button.set_label(
|
||||
&t!("select_button_select_all").to_string(),
|
||||
);
|
||||
update_button.set_sensitive(!is_all_children_unmarked(
|
||||
&packages_boxedlist,
|
||||
));
|
||||
user_refs_for_upgrade_vec.borrow_mut().retain(|x| {
|
||||
x.flatref_ref_format()
|
||||
!= flatpak_row.flatref_ref_format()
|
||||
});
|
||||
}
|
||||
),
|
||||
);
|
||||
packages_boxedlist.append(&flatpak_row);
|
||||
if flatpak_user_updates.is_empty()
|
||||
|| !flatref_struct.is_system && flatref_struct.is_last
|
||||
{
|
||||
user_last_triggered = true
|
||||
}
|
||||
}
|
||||
if user_last_triggered && system_last_triggered {
|
||||
packages_boxedlist.set_sensitive(true);
|
||||
}
|
||||
}
|
||||
flatpak_update_dialog.close();
|
||||
}
|
||||
"FN_OVERRIDE_FAILED" => {
|
||||
flatpak_update_dialog_child_box.set_visible(false);
|
||||
flatpak_update_dialog.set_extra_child(Some(
|
||||
&Image::builder()
|
||||
.pixel_size(128)
|
||||
.icon_name("dialog-error-symbolic")
|
||||
.halign(Align::Center)
|
||||
.build(),
|
||||
));
|
||||
flatpak_update_dialog.set_title(Some(
|
||||
&t!("flatpak_update_dialog_status_failed").to_string(),
|
||||
));
|
||||
flatpak_update_dialog
|
||||
.set_response_enabled("flatpak_update_dialog_retry", true);
|
||||
}
|
||||
_ => flatpak_update_dialog.set_body(&state),
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
searchbar.connect_search_changed(clone!(
|
||||
#[weak]
|
||||
searchbar,
|
||||
#[weak]
|
||||
packages_boxedlist,
|
||||
move |_| {
|
||||
let mut counter = packages_boxedlist.first_child();
|
||||
while let Some(row) = counter {
|
||||
if row.widget_name() == "FlatpakRefRow" {
|
||||
if !searchbar.text().is_empty() {
|
||||
if row
|
||||
.property::<String>("flatref-name")
|
||||
.to_lowercase()
|
||||
.contains(&searchbar.text().to_string().to_lowercase())
|
||||
|| row
|
||||
.property::<String>("flatref-ref-name")
|
||||
.to_lowercase()
|
||||
.contains(&searchbar.text().to_string().to_lowercase())
|
||||
{
|
||||
row.set_property("visible", true);
|
||||
searchbar.grab_focus();
|
||||
} else {
|
||||
row.set_property("visible", false);
|
||||
}
|
||||
} else {
|
||||
row.set_property("visible", true);
|
||||
}
|
||||
}
|
||||
counter = row.next_sibling();
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
main_box.append(&searchbar);
|
||||
main_box.append(&viewport_bin);
|
||||
main_box.append(&bottom_bar);
|
||||
|
||||
flatpak_update_dialog.present();
|
||||
main_box
|
||||
}
|
||||
|
||||
fn is_widget_select_all_ready(parent_listbox: &impl adw::prelude::IsA<ListBox>) -> bool {
|
||||
let mut is_ready = false;
|
||||
let mut child_counter = parent_listbox.borrow().first_child();
|
||||
while let Some(child) = child_counter {
|
||||
let next_child = child.next_sibling();
|
||||
let downcast = child.downcast::<FlatpakRefRow>().unwrap();
|
||||
if !downcast.flatref_marked() {
|
||||
is_ready = true;
|
||||
break;
|
||||
}
|
||||
child_counter = next_child
|
||||
}
|
||||
is_ready
|
||||
}
|
||||
|
||||
fn is_all_children_unmarked(parent_listbox: &impl adw::prelude::IsA<ListBox>) -> bool {
|
||||
let mut is_all_unmarked = true;
|
||||
let mut child_counter = parent_listbox.borrow().first_child();
|
||||
while let Some(child) = child_counter {
|
||||
let next_child = child.next_sibling();
|
||||
let downcast = child.downcast::<FlatpakRefRow>().unwrap();
|
||||
if downcast.flatref_marked() {
|
||||
is_all_unmarked = false;
|
||||
break;
|
||||
}
|
||||
child_counter = next_child
|
||||
}
|
||||
is_all_unmarked
|
||||
}
|
||||
|
||||
fn set_all_flatpak_row_marks_to(parent_listbox: &impl adw::prelude::IsA<ListBox>, value: bool) {
|
||||
let mut child_counter = parent_listbox.borrow().first_child();
|
||||
while let Some(child) = child_counter {
|
||||
let next_child = child.next_sibling();
|
||||
let downcast = child.downcast::<FlatpakRefRow>().unwrap();
|
||||
downcast.set_flatref_marked(value);
|
||||
child_counter = next_child
|
||||
}
|
||||
}
|
@ -1,541 +0,0 @@
|
||||
use crate::flatpak_ref_row::FlatpakRefRow;
|
||||
use adw::gio::SimpleAction;
|
||||
use adw::prelude::*;
|
||||
use gtk::glib::*;
|
||||
use gtk::*;
|
||||
use libflatpak::prelude::*;
|
||||
use libflatpak::Transaction;
|
||||
use pretty_bytes::converter::convert;
|
||||
use serde::Serialize;
|
||||
use serde_json::Value;
|
||||
use std::cell::RefCell;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use std::process::Command;
|
||||
use std::rc::Rc;
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{fs, thread};
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
struct FlatpakChangesInfo {
|
||||
system_flatref_count: u64,
|
||||
user_flatref_count: u64,
|
||||
total_download_size: u64,
|
||||
total_installed_size: i64,
|
||||
}
|
||||
#[derive(Serialize)]
|
||||
struct Exclusions {
|
||||
exclusions: Vec<Value>,
|
||||
}
|
||||
|
||||
impl FlatpakChangesInfo {
|
||||
fn add_system(&mut self) {
|
||||
self.system_flatref_count += 1;
|
||||
}
|
||||
fn add_user(&mut self) {
|
||||
self.user_flatref_count += 1;
|
||||
}
|
||||
|
||||
fn increase_total_download_size_by(&mut self, value: u64) {
|
||||
self.total_download_size += value;
|
||||
}
|
||||
|
||||
fn increase_total_installed_size_by(&mut self, value: u64) {
|
||||
self.total_installed_size += value as i64;
|
||||
}
|
||||
|
||||
fn decrease_total_installed_size_by(&mut self, value: u64) {
|
||||
self.total_installed_size -= value as i64;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn flatpak_process_update(
|
||||
system_refs_for_upgrade_vec_opt: Option<&Vec<FlatpakRefRow>>,
|
||||
user_refs_for_upgrade_vec_opt: Option<&Vec<FlatpakRefRow>>,
|
||||
system_refs_for_upgrade_vec_all: &Vec<FlatpakRefRow>,
|
||||
user_refs_for_upgrade_vec_all: &Vec<FlatpakRefRow>,
|
||||
window: adw::ApplicationWindow,
|
||||
retry_signal_action: &SimpleAction,
|
||||
) {
|
||||
let cancellable = libflatpak::gio::Cancellable::NONE;
|
||||
// Emulate Flatpak Full Upgrade to get transaction info
|
||||
let mut flatpak_changes_struct = FlatpakChangesInfo {
|
||||
system_flatref_count: 0,
|
||||
user_flatref_count: 0,
|
||||
total_download_size: 0,
|
||||
total_installed_size: 0,
|
||||
};
|
||||
|
||||
let mut system_refs_for_upgrade_vec = Vec::new();
|
||||
|
||||
match system_refs_for_upgrade_vec_opt {
|
||||
Some(t) => {
|
||||
for flatpak_row in t {
|
||||
flatpak_changes_struct.add_system();
|
||||
//
|
||||
let installed_size_installed = flatpak_row.flatref_installed_size_installed();
|
||||
let installed_size_remote = flatpak_row.flatref_installed_size_installed();
|
||||
let installed_download_size = flatpak_row.flatref_download_size();
|
||||
let ref_format = flatpak_row.flatref_ref_format();
|
||||
//
|
||||
flatpak_changes_struct.decrease_total_installed_size_by(installed_size_installed);
|
||||
flatpak_changes_struct.increase_total_installed_size_by(installed_size_remote);
|
||||
//
|
||||
flatpak_changes_struct.increase_total_download_size_by(installed_download_size);
|
||||
//
|
||||
system_refs_for_upgrade_vec.push(ref_format);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
for flatpak_row in system_refs_for_upgrade_vec_all {
|
||||
flatpak_changes_struct.add_system();
|
||||
//
|
||||
let installed_size_installed = flatpak_row.flatref_installed_size_installed();
|
||||
let installed_size_remote = flatpak_row.flatref_installed_size_installed();
|
||||
let installed_download_size = flatpak_row.flatref_download_size();
|
||||
let ref_format = flatpak_row.flatref_ref_format();
|
||||
//
|
||||
flatpak_changes_struct.decrease_total_installed_size_by(installed_size_installed);
|
||||
flatpak_changes_struct.increase_total_installed_size_by(installed_size_remote);
|
||||
//
|
||||
flatpak_changes_struct.increase_total_download_size_by(installed_download_size);
|
||||
//
|
||||
system_refs_for_upgrade_vec.push(ref_format);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let mut user_refs_for_upgrade_vec = Vec::new();
|
||||
|
||||
match user_refs_for_upgrade_vec_opt {
|
||||
Some(t) => {
|
||||
for flatpak_row in t {
|
||||
flatpak_changes_struct.add_user();
|
||||
//
|
||||
let installed_size_installed = flatpak_row.flatref_installed_size_installed();
|
||||
let installed_size_remote = flatpak_row.flatref_installed_size_installed();
|
||||
let installed_download_size = flatpak_row.flatref_download_size();
|
||||
let ref_format = flatpak_row.flatref_ref_format();
|
||||
//
|
||||
flatpak_changes_struct.decrease_total_installed_size_by(installed_size_installed);
|
||||
flatpak_changes_struct.increase_total_installed_size_by(installed_size_remote);
|
||||
//
|
||||
flatpak_changes_struct.increase_total_download_size_by(installed_download_size);
|
||||
//
|
||||
user_refs_for_upgrade_vec.push(ref_format);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
for flatpak_row in user_refs_for_upgrade_vec_all {
|
||||
flatpak_changes_struct.add_user();
|
||||
//
|
||||
let installed_size_installed = flatpak_row.flatref_installed_size_installed();
|
||||
let installed_size_remote = flatpak_row.flatref_installed_size_installed();
|
||||
let installed_download_size = flatpak_row.flatref_download_size();
|
||||
let ref_format = flatpak_row.flatref_ref_format();
|
||||
//
|
||||
flatpak_changes_struct.decrease_total_installed_size_by(installed_size_installed);
|
||||
flatpak_changes_struct.increase_total_installed_size_by(installed_size_remote);
|
||||
//
|
||||
flatpak_changes_struct.increase_total_download_size_by(installed_download_size);
|
||||
//
|
||||
user_refs_for_upgrade_vec.push(ref_format);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let flatpak_confirm_dialog_child_box =
|
||||
Box::builder().orientation(Orientation::Vertical).build();
|
||||
|
||||
let flatpak_update_dialog_badges_size_group = SizeGroup::new(SizeGroupMode::Both);
|
||||
let flatpak_update_dialog_badges_size_group0 = SizeGroup::new(SizeGroupMode::Both);
|
||||
let flatpak_update_dialog_badges_size_group1 = SizeGroup::new(SizeGroupMode::Both);
|
||||
|
||||
flatpak_confirm_dialog_child_box.append(&create_color_badge(
|
||||
&t!("system_flatref_count_badge_label"),
|
||||
&flatpak_changes_struct.system_flatref_count.to_string(),
|
||||
"background-accent-bg",
|
||||
&flatpak_update_dialog_badges_size_group,
|
||||
&flatpak_update_dialog_badges_size_group0,
|
||||
&flatpak_update_dialog_badges_size_group1,
|
||||
));
|
||||
|
||||
flatpak_confirm_dialog_child_box.append(&create_color_badge(
|
||||
&t!("user_flatref_count_badge_label"),
|
||||
&flatpak_changes_struct.user_flatref_count.to_string(),
|
||||
"background-accent-bg",
|
||||
&flatpak_update_dialog_badges_size_group,
|
||||
&flatpak_update_dialog_badges_size_group0,
|
||||
&flatpak_update_dialog_badges_size_group1,
|
||||
));
|
||||
|
||||
flatpak_confirm_dialog_child_box.append(&create_color_badge(
|
||||
&t!("total_download_size_badge_label"),
|
||||
&convert(flatpak_changes_struct.total_download_size as f64),
|
||||
"background-accent-bg",
|
||||
&flatpak_update_dialog_badges_size_group,
|
||||
&flatpak_update_dialog_badges_size_group0,
|
||||
&flatpak_update_dialog_badges_size_group1,
|
||||
));
|
||||
|
||||
flatpak_confirm_dialog_child_box.append(&create_color_badge(
|
||||
&t!("total_installed_size_badge_label"),
|
||||
&convert(flatpak_changes_struct.total_installed_size as f64),
|
||||
"background-accent-bg",
|
||||
&flatpak_update_dialog_badges_size_group,
|
||||
&flatpak_update_dialog_badges_size_group0,
|
||||
&flatpak_update_dialog_badges_size_group1,
|
||||
));
|
||||
|
||||
let flatpak_confirm_dialog = adw::MessageDialog::builder()
|
||||
.transient_for(&window)
|
||||
.heading(t!("flatpak_confirm_dialog_heading"))
|
||||
.body(t!("flatpak_confirm_dialog_body"))
|
||||
.extra_child(&flatpak_confirm_dialog_child_box)
|
||||
.build();
|
||||
|
||||
flatpak_confirm_dialog.add_response(
|
||||
"flatpak_confirm_dialog_cancel",
|
||||
&t!("flatpak_confirm_dialog_cancel_label").to_string(),
|
||||
);
|
||||
|
||||
flatpak_confirm_dialog.add_response(
|
||||
"flatpak_confirm_dialog_confirm",
|
||||
&t!("flatpak_confirm_dialog_confirm_label").to_string(),
|
||||
);
|
||||
|
||||
flatpak_confirm_dialog.set_response_appearance(
|
||||
"flatpak_confirm_dialog_confirm",
|
||||
adw::ResponseAppearance::Destructive,
|
||||
);
|
||||
|
||||
flatpak_confirm_dialog.set_default_response(Some("flatpak_confirm_dialog_confirm"));
|
||||
flatpak_confirm_dialog.set_close_response("flatpak_confirm_dialog_cancel");
|
||||
|
||||
let retry_signal_action0 = retry_signal_action.clone();
|
||||
flatpak_confirm_dialog
|
||||
.clone()
|
||||
.choose(None::<&gio::Cancellable>, move |choice| {
|
||||
if choice == "flatpak_confirm_dialog_confirm" {
|
||||
flatpak_run_transactions(
|
||||
system_refs_for_upgrade_vec,
|
||||
user_refs_for_upgrade_vec,
|
||||
window,
|
||||
&retry_signal_action0,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn flatpak_run_transactions(
|
||||
system_refs_for_upgrade_vec: Vec<String>,
|
||||
user_refs_for_upgrade_vec: Vec<String>,
|
||||
window: adw::ApplicationWindow,
|
||||
retry_signal_action: &SimpleAction,
|
||||
) {
|
||||
let (transaction_percent_sender, transaction_percent_receiver) =
|
||||
async_channel::unbounded::<u32>();
|
||||
let transaction_percent_sender = transaction_percent_sender.clone();
|
||||
let (transaction_status_sender, transaction_status_receiver) =
|
||||
async_channel::unbounded::<String>();
|
||||
let transaction_status_sender = transaction_status_sender.clone();
|
||||
|
||||
thread::spawn(move || {
|
||||
let cancellable_no = libflatpak::gio::Cancellable::NONE;
|
||||
|
||||
let transaction_status_sender0 = transaction_status_sender.clone();
|
||||
let transaction_percent_sender0 = transaction_percent_sender.clone();
|
||||
|
||||
let transaction_run_closure =
|
||||
move |transaction: &libflatpak::Transaction,
|
||||
transaction_operation: &libflatpak::TransactionOperation,
|
||||
transaction_progress: &libflatpak::TransactionProgress| {
|
||||
let transaction_status_sender = transaction_status_sender0.clone();
|
||||
let transaction_percent_sender = transaction_percent_sender0.clone();
|
||||
transaction_progress.connect_changed(clone!(@strong transaction_progress, @strong transaction_operation => move |_| {
|
||||
let status_message = format!("{}: {}\n{}: {}\n{}: {}/{}\n{}: {}", t!("flatpak_ref"), transaction_operation.get_ref().unwrap_or(libflatpak::glib::GString::from_string_unchecked("Unknown".to_owned())), t!("flatpak_status") ,transaction_progress.status().unwrap_or(libflatpak::glib::GString::from_string_unchecked("Unknown".to_owned())), t!("flatpak_transaction_bytes_transferred"), convert(transaction_progress.bytes_transferred() as f64), convert(transaction_operation.download_size() as f64), t!("flatpak_transaction_installed_size"), convert(transaction_operation.installed_size() as f64));
|
||||
transaction_status_sender.send_blocking(status_message).expect("transaction_status_receiver closed!");
|
||||
transaction_percent_sender.send_blocking(transaction_progress.progress().try_into().unwrap_or(0)).expect("transaction_percent_receiver closed!");
|
||||
}));
|
||||
};
|
||||
|
||||
//
|
||||
|
||||
let flatpak_system_installation =
|
||||
libflatpak::Installation::new_system(cancellable_no).unwrap();
|
||||
let flatpak_system_transaction =
|
||||
libflatpak::Transaction::for_installation(&flatpak_system_installation, cancellable_no)
|
||||
.unwrap();
|
||||
|
||||
for ref_format in system_refs_for_upgrade_vec {
|
||||
flatpak_system_transaction
|
||||
.add_update(&ref_format, &[], None)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
flatpak_system_transaction.connect_new_operation(transaction_run_closure.clone());
|
||||
|
||||
match flatpak_system_transaction.run(cancellable_no) {
|
||||
Ok(_) => {}
|
||||
Err(e) => {
|
||||
transaction_status_sender
|
||||
.send_blocking(e.to_string())
|
||||
.expect("transaction_sync_status_receiver closed");
|
||||
transaction_status_sender
|
||||
.send_blocking("FN_OVERRIDE_FAILED".to_owned())
|
||||
.expect("transaction_sync_status_receiver closed");
|
||||
panic!("{}", e);
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
let flatpak_user_installation = libflatpak::Installation::new_user(cancellable_no).unwrap();
|
||||
let flatpak_user_transaction =
|
||||
libflatpak::Transaction::for_installation(&flatpak_user_installation, cancellable_no)
|
||||
.unwrap();
|
||||
|
||||
flatpak_user_transaction.connect_new_operation(transaction_run_closure);
|
||||
|
||||
for ref_format in user_refs_for_upgrade_vec {
|
||||
flatpak_user_transaction
|
||||
.add_update(&ref_format, &[], None)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
match flatpak_user_transaction.run(cancellable_no) {
|
||||
Ok(_) => {
|
||||
transaction_status_sender
|
||||
.send_blocking("FN_OVERRIDE_SUCCESSFUL".to_owned())
|
||||
.expect("transaction_sync_status_receiver closed");
|
||||
}
|
||||
Err(e) => {
|
||||
transaction_status_sender
|
||||
.send_blocking(e.to_string())
|
||||
.expect("transaction_sync_status_receiver closed");
|
||||
transaction_status_sender
|
||||
.send_blocking("FN_OVERRIDE_FAILED".to_owned())
|
||||
.expect("transaction_sync_status_receiver closed");
|
||||
panic!("{}", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
let log_file_path = format!(
|
||||
"/tmp/pika-flatpak-transaction_{}.log",
|
||||
chrono::offset::Local::now().format("%Y-%m-%d_%H:%M")
|
||||
);
|
||||
|
||||
let log_file_path_clone0 = log_file_path.clone();
|
||||
|
||||
if !Path::new(&log_file_path).exists() {
|
||||
match fs::File::create(&log_file_path) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
eprintln!("Warning: {} file couldn't be created", log_file_path);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
let flatpak_transaction_dialog_child_box =
|
||||
Box::builder().orientation(Orientation::Vertical).build();
|
||||
|
||||
let flatpak_transaction_dialog_progress_bar =
|
||||
ProgressBar::builder().show_text(true).hexpand(true).build();
|
||||
|
||||
let flatpak_transaction_dialog_spinner = Spinner::builder()
|
||||
.hexpand(true)
|
||||
.valign(Align::Start)
|
||||
.halign(Align::Center)
|
||||
.spinning(true)
|
||||
.height_request(128)
|
||||
.width_request(128)
|
||||
.build();
|
||||
|
||||
flatpak_transaction_dialog_child_box.append(&flatpak_transaction_dialog_spinner);
|
||||
flatpak_transaction_dialog_child_box.append(&flatpak_transaction_dialog_progress_bar);
|
||||
|
||||
let flatpak_transaction_dialog = adw::MessageDialog::builder()
|
||||
.transient_for(&window)
|
||||
.extra_child(&flatpak_transaction_dialog_child_box)
|
||||
.heading(t!("flatpak_transaction_dialog_heading"))
|
||||
.width_request(500)
|
||||
.build();
|
||||
|
||||
flatpak_transaction_dialog.add_response(
|
||||
"flatpak_transaction_dialog_ok",
|
||||
&t!("flatpak_transaction_dialog_ok_label").to_string(),
|
||||
);
|
||||
|
||||
let flatpak_transaction_dialog_child_box_done =
|
||||
Box::builder().orientation(Orientation::Vertical).build();
|
||||
|
||||
let flatpak_transaction_log_image = Image::builder()
|
||||
.pixel_size(128)
|
||||
.halign(Align::Center)
|
||||
.build();
|
||||
|
||||
let flatpak_transaction_log_button = Button::builder()
|
||||
.label(t!("flatpak_transaction_dialog_open_log_file_label"))
|
||||
.halign(Align::Center)
|
||||
.margin_start(15)
|
||||
.margin_end(15)
|
||||
.margin_top(15)
|
||||
.margin_bottom(15)
|
||||
.build();
|
||||
|
||||
flatpak_transaction_dialog_child_box_done.append(&flatpak_transaction_log_image);
|
||||
flatpak_transaction_dialog_child_box_done.append(&flatpak_transaction_log_button);
|
||||
|
||||
flatpak_transaction_dialog.set_response_enabled("flatpak_transaction_dialog_ok", false);
|
||||
flatpak_transaction_dialog.set_close_response("flatpak_transaction_dialog_ok");
|
||||
|
||||
let transaction_percent_server_context = MainContext::default();
|
||||
// The main loop executes the asynchronous block
|
||||
transaction_percent_server_context.spawn_local(clone!(
|
||||
#[weak]
|
||||
flatpak_transaction_dialog_progress_bar,
|
||||
async move {
|
||||
while let Ok(state) = transaction_percent_receiver.recv().await {
|
||||
flatpak_transaction_dialog_progress_bar.set_fraction((state as f32 / 100.0).into());
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
let transaction_status_server_context = MainContext::default();
|
||||
// The main loop executes the asynchronous block
|
||||
transaction_status_server_context.spawn_local(clone!(
|
||||
#[weak]
|
||||
flatpak_transaction_dialog,
|
||||
#[weak]
|
||||
flatpak_transaction_dialog_child_box,
|
||||
#[strong]
|
||||
flatpak_transaction_dialog_child_box_done,
|
||||
#[strong]
|
||||
flatpak_transaction_log_image,
|
||||
async move {
|
||||
while let Ok(state) = transaction_status_receiver.recv().await {
|
||||
match state.as_ref() {
|
||||
"FN_OVERRIDE_SUCCESSFUL" => {
|
||||
flatpak_transaction_dialog_child_box.set_visible(false);
|
||||
flatpak_transaction_log_image.set_icon_name(Some("face-cool-symbolic"));
|
||||
flatpak_transaction_dialog
|
||||
.set_extra_child(Some(&flatpak_transaction_dialog_child_box_done));
|
||||
flatpak_transaction_dialog.set_title(Some(
|
||||
&t!("flatpak_transaction_dialog_status_successful").to_string(),
|
||||
));
|
||||
flatpak_transaction_dialog
|
||||
.set_response_enabled("flatpak_transaction_dialog_ok", true);
|
||||
}
|
||||
"FN_OVERRIDE_FAILED" => {
|
||||
flatpak_transaction_dialog_child_box.set_visible(false);
|
||||
flatpak_transaction_log_image.set_icon_name(Some("dialog-error-symbolic"));
|
||||
flatpak_transaction_dialog
|
||||
.set_extra_child(Some(&flatpak_transaction_dialog_child_box_done));
|
||||
flatpak_transaction_dialog.set_title(Some(
|
||||
&t!("flatpak_transaction_dialog_status_failed").to_string(),
|
||||
));
|
||||
flatpak_transaction_dialog
|
||||
.set_response_enabled("flatpak_transaction_dialog_ok", true);
|
||||
flatpak_transaction_dialog
|
||||
.set_response_enabled("flatpak_transaction_dialog_open_log_file", true);
|
||||
}
|
||||
_ => {
|
||||
flatpak_transaction_dialog.set_body(&state);
|
||||
let mut log_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.append(true)
|
||||
.open(&log_file_path)
|
||||
.unwrap();
|
||||
|
||||
if let Err(e) = writeln!(
|
||||
log_file,
|
||||
"[{}] {}",
|
||||
chrono::offset::Local::now().format("%Y/%m/%d_%H:%M"),
|
||||
state
|
||||
) {
|
||||
eprintln!("Couldn't write to file: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
));
|
||||
|
||||
let retry_signal_action0 = retry_signal_action.clone();
|
||||
|
||||
flatpak_transaction_log_button.connect_clicked(move |_| {
|
||||
let _ = Command::new("xdg-open")
|
||||
.arg(log_file_path_clone0.to_owned())
|
||||
.spawn();
|
||||
});
|
||||
|
||||
flatpak_transaction_dialog.choose(None::<&gio::Cancellable>, move |choice| {
|
||||
match choice.as_str() {
|
||||
"flatpak_transaction_dialog_ok" => {
|
||||
retry_signal_action0.activate(None);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn create_color_badge(
|
||||
label0_text: &str,
|
||||
label1_text: &str,
|
||||
css_style: &str,
|
||||
group_size: &SizeGroup,
|
||||
group_size0: &SizeGroup,
|
||||
group_size1: &SizeGroup,
|
||||
) -> ListBox {
|
||||
let badge_box = Box::builder().build();
|
||||
|
||||
let label0 = Label::builder()
|
||||
.label(label0_text)
|
||||
.margin_start(5)
|
||||
.margin_end(5)
|
||||
.margin_bottom(1)
|
||||
.margin_top(1)
|
||||
.valign(Align::Center)
|
||||
.halign(Align::Center)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
group_size0.add_widget(&label0);
|
||||
|
||||
let label_separator = Separator::builder().build();
|
||||
|
||||
let label1 = Label::builder()
|
||||
.label(label1_text)
|
||||
.margin_start(3)
|
||||
.margin_end(0)
|
||||
.margin_bottom(1)
|
||||
.margin_top(1)
|
||||
.valign(Align::Center)
|
||||
.halign(Align::Center)
|
||||
.hexpand(true)
|
||||
.vexpand(true)
|
||||
.build();
|
||||
group_size1.add_widget(&label1);
|
||||
|
||||
label1.add_css_class(css_style);
|
||||
|
||||
badge_box.append(&label0);
|
||||
badge_box.append(&label_separator);
|
||||
badge_box.append(&label1);
|
||||
|
||||
let boxedlist = ListBox::builder()
|
||||
.selection_mode(SelectionMode::None)
|
||||
.halign(Align::Center)
|
||||
.margin_start(10)
|
||||
.margin_end(10)
|
||||
.margin_bottom(10)
|
||||
.margin_top(10)
|
||||
.build();
|
||||
|
||||
boxedlist.add_css_class("boxed-list");
|
||||
boxedlist.append(&badge_box);
|
||||
group_size.add_widget(&boxedlist);
|
||||
boxedlist
|
||||
}
|
@ -1,56 +0,0 @@
|
||||
mod apt_package_row;
|
||||
mod apt_update_page;
|
||||
mod build_ui;
|
||||
mod config;
|
||||
mod flatpak_ref_row;
|
||||
mod flatpak_update_page;
|
||||
|
||||
use crate::config::APP_ID;
|
||||
use adw::prelude::*;
|
||||
use adw::*;
|
||||
use build_ui::build_ui;
|
||||
use gdk::Display;
|
||||
use gtk::*;
|
||||
use std::boxed::Box;
|
||||
use std::env;
|
||||
|
||||
// Init translations for current crate.
|
||||
#[macro_use]
|
||||
extern crate rust_i18n;
|
||||
i18n!("locales", fallback = "en_US");
|
||||
|
||||
/// main function
|
||||
fn main() {
|
||||
let current_locale = match env::var_os("LANG") {
|
||||
Some(v) => v
|
||||
.into_string()
|
||||
.unwrap()
|
||||
.chars()
|
||||
.take_while(|&ch| ch != '.')
|
||||
.collect::<String>(),
|
||||
None => panic!("$LANG is not set"),
|
||||
};
|
||||
rust_i18n::set_locale(¤t_locale);
|
||||
let application = adw::Application::new(Some(APP_ID), Default::default());
|
||||
application.connect_startup(|app| {
|
||||
// The CSS "magic" happens here.
|
||||
let provider = CssProvider::new();
|
||||
provider.load_from_string(include_str!("style.css"));
|
||||
// We give the CssProvided to the default screen so the CSS rules we added
|
||||
// can be applied to our window.
|
||||
gtk::style_context_add_provider_for_display(
|
||||
&Display::default().expect("Could not connect to a display."),
|
||||
&provider,
|
||||
STYLE_PROVIDER_PRIORITY_APPLICATION,
|
||||
);
|
||||
app.connect_activate(build_ui);
|
||||
});
|
||||
|
||||
//if get_current_username().unwrap() == "pikaos" {
|
||||
// application.run();
|
||||
//} else {
|
||||
// println!("Error: This program can only be run via pikaos user");
|
||||
// std::process::exit(1)
|
||||
//}
|
||||
application.run();
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
.symbolic-accent-bg {
|
||||
color: @accent_bg_color;
|
||||
}
|
||||
|
||||
.size-20-font {
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.rounded-all-25 {
|
||||
border-radius: 25px;
|
||||
}
|
||||
|
||||
.round-all-scroll {
|
||||
border-top-right-radius: 15px;
|
||||
border-top-left-radius: 15px;
|
||||
border-bottom-right-radius: 15px;
|
||||
border-bottom-left-radius: 15px;
|
||||
padding-top: 3px;
|
||||
padding-right: 3px;
|
||||
padding-left: 3px;
|
||||
padding-bottom: 3px;
|
||||
}
|
||||
|
||||
.background-accent-bg {
|
||||
background: @accent_bg_color;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.background-green-bg {
|
||||
background: green;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.background-red-bg {
|
||||
background: #ff2a03;
|
||||
border-radius: 10px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.round-border-only-top {
|
||||
border-top-right-radius: 15px;
|
||||
border-top-left-radius: 15px;
|
||||
border-bottom-right-radius: 0px;
|
||||
border-bottom-left-radius: 0px;
|
||||
}
|
||||
|
||||
.round-border-only-bottom {
|
||||
border-top-right-radius: 0px;
|
||||
border-top-left-radius: 0px;
|
||||
border-bottom-right-radius: 15px;
|
||||
border-bottom-left-radius: 15px;
|
||||
}
|
||||
|
||||
.destructive-color-text {
|
||||
color: @destructive_bg_color;
|
||||
}
|
||||
|
||||
.success-color-text {
|
||||
color: @success_bg_color;
|
||||
}
|
||||
|
||||
.size-20-bold-text {
|
||||
font-weight: 800;
|
||||
font-size: 20px;
|
||||
}
|
@ -1,83 +0,0 @@
|
||||
use crate::pika_unixsocket_tools::*;
|
||||
use rust_apt::progress::DynInstallProgress;
|
||||
use std::process::exit;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::UnixStream;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
pub struct AptInstallProgressSocket<'a> {
|
||||
percent_socket_path: &'a str,
|
||||
status_socket_path: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> AptInstallProgressSocket<'a> {
|
||||
/// Returns a new default progress instance.
|
||||
pub fn new(percent_socket_path: &'a str, status_socket_path: &'a str) -> Self {
|
||||
let progress = Self {
|
||||
percent_socket_path: percent_socket_path,
|
||||
status_socket_path: status_socket_path,
|
||||
};
|
||||
progress
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DynInstallProgress for AptInstallProgressSocket<'a> {
|
||||
fn status_changed(
|
||||
&mut self,
|
||||
_pkgname: String,
|
||||
steps_done: u64,
|
||||
total_steps: u64,
|
||||
action: String,
|
||||
) {
|
||||
let progress_percent: f32 = (steps_done as f32 * 100.0) / total_steps as f32;
|
||||
Runtime::new().unwrap().block_on(send_progress_percent(
|
||||
progress_percent,
|
||||
self.percent_socket_path,
|
||||
));
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_progress_status(&action, self.status_socket_path));
|
||||
}
|
||||
|
||||
fn error(&mut self, pkgname: String, _steps_done: u64, _total_steps: u64, error: String) {
|
||||
let message = format!("dpkg failure on {}: {}", pkgname, error);
|
||||
eprintln!("{}", &message);
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_progress_status(&message, self.status_socket_path));
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_failed_to_socket(self.percent_socket_path));
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_failed_to_socket(self.status_socket_path));
|
||||
exit(53)
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_progress_percent(progress_f32: f32, socket_path: &str) {
|
||||
// Connect to the Unix socket
|
||||
let mut stream = UnixStream::connect(socket_path)
|
||||
.await
|
||||
.expect("Could not connect to server");
|
||||
|
||||
let message = progress_f32.to_string();
|
||||
// Send the message to the server
|
||||
stream
|
||||
.write_all(message.as_bytes())
|
||||
.await
|
||||
.expect("Failed to write to stream");
|
||||
}
|
||||
|
||||
async fn send_progress_status(message: &str, socket_path: &str) {
|
||||
// Connect to the Unix socket
|
||||
let mut stream = UnixStream::connect(socket_path)
|
||||
.await
|
||||
.expect("Could not connect to server");
|
||||
|
||||
// Send the message to the server
|
||||
stream
|
||||
.write_all(message.as_bytes())
|
||||
.await
|
||||
.expect("Failed to write to stream");
|
||||
}
|
@ -1,152 +0,0 @@
|
||||
use crate::pika_unixsocket_tools::*;
|
||||
use rust_apt::progress::DynAcquireProgress;
|
||||
use rust_apt::raw::{AcqTextStatus, ItemDesc, PkgAcquire};
|
||||
use std::process::exit;
|
||||
use tokio::io::AsyncWriteExt;
|
||||
use tokio::net::UnixStream;
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
pub struct AptUpdateProgressSocket<'a> {
|
||||
pulse_interval: usize,
|
||||
percent_socket_path: &'a str,
|
||||
status_socket_path: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> AptUpdateProgressSocket<'a> {
|
||||
/// Returns a new default progress instance.
|
||||
pub fn new(percent_socket_path: &'a str, status_socket_path: &'a str) -> Self {
|
||||
let progress = Self {
|
||||
pulse_interval: 0,
|
||||
percent_socket_path: percent_socket_path,
|
||||
status_socket_path: status_socket_path,
|
||||
};
|
||||
progress
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DynAcquireProgress for AptUpdateProgressSocket<'a> {
|
||||
/// Used to send the pulse interval to the apt progress class.
|
||||
///
|
||||
/// Pulse Interval is in microseconds.
|
||||
///
|
||||
/// Example: 1 second = 1000000 microseconds.
|
||||
///
|
||||
/// Apt default is 500000 microseconds or 0.5 seconds.
|
||||
///
|
||||
/// The higher the number, the less frequent pulse updates will be.
|
||||
///
|
||||
/// Pulse Interval set to 0 assumes the apt defaults.
|
||||
fn pulse_interval(&self) -> usize {
|
||||
self.pulse_interval
|
||||
}
|
||||
|
||||
/// Called when an item is confirmed to be up-to-date.
|
||||
///
|
||||
/// Prints out the short description and the expected size.
|
||||
fn hit(&mut self, item: &ItemDesc) {
|
||||
let message = format!("Up-to-date: {} {}", item.description(), item.short_desc());
|
||||
println!("{}", message);
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_progress_status(&message, self.status_socket_path));
|
||||
}
|
||||
|
||||
/// Called when an Item has started to download
|
||||
///
|
||||
/// Prints out the short description and the expected size.
|
||||
fn fetch(&mut self, item: &ItemDesc) {
|
||||
let message = format!("Fetching: {} {}", item.description(), item.short_desc());
|
||||
println!("{}", message);
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_progress_status(&message, self.status_socket_path));
|
||||
}
|
||||
|
||||
/// Called when an item is successfully and completely fetched.
|
||||
///
|
||||
/// We don't print anything here to remain consistent with apt.
|
||||
fn done(&mut self, item: &ItemDesc) {
|
||||
let message = format!("Downloading: {} {}", item.description(), item.short_desc());
|
||||
println!("{}", message);
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_progress_status(&message, self.status_socket_path));
|
||||
}
|
||||
|
||||
/// Called when progress has started.
|
||||
///
|
||||
/// Start does not pass information into the method.
|
||||
///
|
||||
/// We do not print anything here to remain consistent with apt.
|
||||
fn start(&mut self) {}
|
||||
|
||||
/// Called when progress has finished.
|
||||
///
|
||||
/// Stop does not pass information into the method.
|
||||
///
|
||||
/// prints out the bytes downloaded and the overall average line speed.
|
||||
fn stop(&mut self, _status: &AcqTextStatus) {}
|
||||
|
||||
/// Called when an Item fails to download.
|
||||
///
|
||||
/// Print out the ErrorText for the Item.
|
||||
fn fail(&mut self, item: &ItemDesc) {
|
||||
let message = format!(
|
||||
"Download Failed: {} {}",
|
||||
item.description(),
|
||||
item.short_desc()
|
||||
);
|
||||
eprintln!("{}", &message);
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_progress_status(&message, self.status_socket_path));
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_failed_to_socket(self.percent_socket_path));
|
||||
Runtime::new()
|
||||
.unwrap()
|
||||
.block_on(send_failed_to_socket(self.status_socket_path));
|
||||
exit(53)
|
||||
}
|
||||
|
||||
/// Called periodically to provide the overall progress information
|
||||
///
|
||||
/// Draws the current progress.
|
||||
/// Each line has an overall percent meter and a per active item status
|
||||
/// meter along with an overall bandwidth and ETA indicator.
|
||||
fn pulse(&mut self, status: &AcqTextStatus, _owner: &PkgAcquire) {
|
||||
let progress_percent: f32 =
|
||||
(status.current_bytes() as f32 * 100.0) / status.total_bytes() as f32;
|
||||
Runtime::new().unwrap().block_on(send_progress_percent(
|
||||
progress_percent,
|
||||
self.percent_socket_path,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
async fn send_progress_percent(progress_f32: f32, socket_path: &str) {
|
||||
// Connect to the Unix socket
|
||||
let mut stream = UnixStream::connect(socket_path)
|
||||
.await
|
||||
.expect("Could not connect to server");
|
||||
|
||||
let message = progress_f32.to_string();
|
||||
// Send the message to the server
|
||||
stream
|
||||
.write_all(message.as_bytes())
|
||||
.await
|
||||
.expect("Failed to write to stream");
|
||||
}
|
||||
|
||||
async fn send_progress_status(message: &str, socket_path: &str) {
|
||||
// Connect to the Unix socket
|
||||
let mut stream = UnixStream::connect(socket_path)
|
||||
.await
|
||||
.expect("Could not connect to server");
|
||||
|
||||
// Send the message to the server
|
||||
stream
|
||||
.write_all(message.as_bytes())
|
||||
.await
|
||||
.expect("Failed to write to stream");
|
||||
}
|
@ -1,3 +0,0 @@
|
||||
pub mod apt_install_progress_socket;
|
||||
pub mod apt_update_progress_socket;
|
||||
pub mod pika_unixsocket_tools;
|
@ -1,177 +0,0 @@
|
||||
use chrono;
|
||||
use std::fs;
|
||||
use std::fs::OpenOptions;
|
||||
use std::io::Write;
|
||||
use std::path::Path;
|
||||
use tokio::io::{AsyncReadExt, AsyncWriteExt};
|
||||
use tokio::net::{UnixListener, UnixStream};
|
||||
use tokio::task;
|
||||
|
||||
pub async fn send_successful_to_socket(socket_path: &str) {
|
||||
// Connect to the Unix socket
|
||||
let mut stream = UnixStream::connect(socket_path)
|
||||
.await
|
||||
.expect("Could not connect to server");
|
||||
|
||||
let message = "FN_OVERRIDE_SUCCESSFUL";
|
||||
|
||||
// Send the message to the server
|
||||
stream
|
||||
.write_all(message.as_bytes())
|
||||
.await
|
||||
.expect("Failed to write to stream");
|
||||
}
|
||||
|
||||
pub async fn send_failed_to_socket(socket_path: &str) {
|
||||
// Connect to the Unix socket
|
||||
let mut stream = UnixStream::connect(socket_path)
|
||||
.await
|
||||
.expect("Could not connect to server");
|
||||
|
||||
let message = "FN_OVERRIDE_FAILED";
|
||||
|
||||
// Send the message to the server
|
||||
stream
|
||||
.write_all(message.as_bytes())
|
||||
.await
|
||||
.expect("Failed to write to stream");
|
||||
}
|
||||
|
||||
// Function to handle a single client connection
|
||||
pub async fn handle_client(
|
||||
mut stream: UnixStream,
|
||||
buffer_sender: async_channel::Sender<String>,
|
||||
log_file_path: String,
|
||||
) {
|
||||
// Buffer to store incoming data
|
||||
let mut buffer = [0; 1024];
|
||||
|
||||
if !Path::new(&log_file_path).exists() {
|
||||
match fs::File::create(&log_file_path) {
|
||||
Ok(_) => {}
|
||||
Err(_) => {
|
||||
eprintln!("Warning: {} file couldn't be created", log_file_path);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Read data from the stream
|
||||
match stream.read(&mut buffer).await {
|
||||
Ok(size) => {
|
||||
let message = String::from_utf8_lossy(&buffer[..size]).to_string();
|
||||
// Send to async buffer sender
|
||||
buffer_sender
|
||||
.send_blocking(message.clone())
|
||||
.expect("Buffer channel closed");
|
||||
// Write to log file
|
||||
let mut log_file = OpenOptions::new()
|
||||
.write(true)
|
||||
.append(true)
|
||||
.open(&log_file_path)
|
||||
.unwrap();
|
||||
|
||||
if let Err(e) = writeln!(
|
||||
log_file,
|
||||
"[{}] {}",
|
||||
chrono::offset::Local::now().format("%Y/%m/%d_%H:%M"),
|
||||
message
|
||||
) {
|
||||
eprintln!("Couldn't write to file: {}", e);
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
// Print error message if reading fails
|
||||
eprintln!("Failed to read from stream: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_socket_server(
|
||||
buffer_sender: async_channel::Sender<String>,
|
||||
socket_path: &str,
|
||||
log_file_path: &str,
|
||||
) {
|
||||
// Remove the socket file if it already exists
|
||||
if Path::new(socket_path).exists() {
|
||||
fs::remove_file(socket_path).expect("Could not remove existing socket file");
|
||||
}
|
||||
|
||||
// Bind the Unix listener to the socket path
|
||||
let listener = UnixListener::bind(socket_path).expect("Could not bind");
|
||||
|
||||
println!("Server listening on {}", socket_path);
|
||||
|
||||
// Loop to accept incoming connections
|
||||
loop {
|
||||
// Accept an incoming connection
|
||||
match listener.accept().await {
|
||||
Ok((stream, _)) => {
|
||||
// Handle the connection in a separate task
|
||||
task::spawn(handle_client(
|
||||
stream,
|
||||
buffer_sender.clone(),
|
||||
log_file_path.to_owned(),
|
||||
));
|
||||
}
|
||||
Err(e) => {
|
||||
// Print error message if a connection fails
|
||||
eprintln!("Connection failed: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn handle_client_no_log(
|
||||
mut stream: UnixStream,
|
||||
buffer_sender: async_channel::Sender<String>,
|
||||
) {
|
||||
// Buffer to store incoming data
|
||||
let mut buffer = [0; 1024];
|
||||
|
||||
// Read data from the stream
|
||||
match stream.read(&mut buffer).await {
|
||||
Ok(size) => {
|
||||
let message = String::from_utf8_lossy(&buffer[..size]).to_string();
|
||||
// Write to log file
|
||||
|
||||
// Send to async buffer sender
|
||||
buffer_sender
|
||||
.send_blocking(message)
|
||||
.expect("Buffer channel closed")
|
||||
}
|
||||
Err(e) => {
|
||||
// Print error message if reading fails
|
||||
eprintln!("Failed to read from stream: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn start_socket_server_no_log(
|
||||
buffer_sender: async_channel::Sender<String>,
|
||||
socket_path: &str,
|
||||
) {
|
||||
// Remove the socket file if it already exists
|
||||
if Path::new(socket_path).exists() {
|
||||
fs::remove_file(socket_path).expect("Could not remove existing socket file");
|
||||
}
|
||||
|
||||
// Bind the Unix listener to the socket path
|
||||
let listener = UnixListener::bind(socket_path).expect("Could not bind");
|
||||
|
||||
println!("Server listening on {}", socket_path);
|
||||
|
||||
// Loop to accept incoming connections
|
||||
loop {
|
||||
// Accept an incoming connection
|
||||
match listener.accept().await {
|
||||
Ok((stream, _)) => {
|
||||
// Handle the connection in a separate task
|
||||
task::spawn(handle_client_no_log(stream, buffer_sender.clone()));
|
||||
}
|
||||
Err(e) => {
|
||||
// Print error message if a connection fails
|
||||
eprintln!("Connection failed: {}", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user