mirror of
https://github.com/morgan9e/warehouse
synced 2026-04-15 00:34:42 +09:00
Convert spaces to tabs for indentation
This commit is contained in:
@@ -26,8 +26,8 @@
|
|||||||
|
|
||||||
- Permission management
|
- Permission management
|
||||||
- Full fledge package manager / app store
|
- Full fledge package manager / app store
|
||||||
- Viewing app icons and screenshots from remotes
|
- Viewing app icons and screenshots from remotes
|
||||||
- Reading and leaving app reviews
|
- Reading and leaving app reviews
|
||||||
- Auto updates
|
- Auto updates
|
||||||
- Management of any non Flatpak packages
|
- Management of any non Flatpak packages
|
||||||
- Supporting any repackages (apart from nixpkgs should that arise)
|
- Supporting any repackages (apart from nixpkgs should that arise)
|
||||||
|
|||||||
@@ -8,31 +8,31 @@
|
|||||||
<project_license>GPL-3.0-only</project_license>
|
<project_license>GPL-3.0-only</project_license>
|
||||||
<summary>Manage all things Flatpak</summary>
|
<summary>Manage all things Flatpak</summary>
|
||||||
<description>
|
<description>
|
||||||
<p>Warehouse provides a simple UI to control complex Flatpak options, all without resorting to the command line.</p>
|
<p>Warehouse provides a simple UI to control complex Flatpak options, all without resorting to the command line.</p>
|
||||||
<p>Features:</p>
|
<p>Features:</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Manage installed Flatpaks and view properties of any package</li>
|
<li>Manage installed Flatpaks and view properties of any package</li>
|
||||||
<li>Change versions of a Flatpak to rollback any unwanted updates</li>
|
<li>Change versions of a Flatpak to rollback any unwanted updates</li>
|
||||||
<li>Pin runtimes and mask Flatpaks</li>
|
<li>Pin runtimes and mask Flatpaks</li>
|
||||||
<li>Filter packages and sort data, to help find anything easily</li>
|
<li>Filter packages and sort data, to help find anything easily</li>
|
||||||
<li>See current app user data, and cleanup any unused data left behind</li>
|
<li>See current app user data, and cleanup any unused data left behind</li>
|
||||||
<li>Add popular Flatpak remotes with a few clicks or add custom remotes instead</li>
|
<li>Add popular Flatpak remotes with a few clicks or add custom remotes instead</li>
|
||||||
<li>Take snapshots of your apps' user data, saving your data</li>
|
<li>Take snapshots of your apps' user data, saving your data</li>
|
||||||
<li>Install new packages from any remote, or from your system</li>
|
<li>Install new packages from any remote, or from your system</li>
|
||||||
<li>Responsive UI to fit large and small screen sizes</li>
|
<li>Responsive UI to fit large and small screen sizes</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
<branding>
|
<branding>
|
||||||
<color type="primary" scheme_preference="light">#AECEF4</color>
|
<color type="primary" scheme_preference="light">#AECEF4</color>
|
||||||
<color type="primary" scheme_preference="dark">#072F5E</color>
|
<color type="primary" scheme_preference="dark">#072F5E</color>
|
||||||
</branding>
|
</branding>
|
||||||
<supports>
|
<supports>
|
||||||
<control>keyboard</control>
|
<control>keyboard</control>
|
||||||
<control>pointing</control>
|
<control>pointing</control>
|
||||||
<control>touch</control>
|
<control>touch</control>
|
||||||
</supports>
|
</supports>
|
||||||
<requires>
|
<requires>
|
||||||
<display_length compare="ge">330</display_length>
|
<display_length compare="ge">330</display_length>
|
||||||
</requires>
|
</requires>
|
||||||
<content_rating type="oars-1.1"/>
|
<content_rating type="oars-1.1"/>
|
||||||
<url type="homepage">https://github.com/flattool/warehouse</url>
|
<url type="homepage">https://github.com/flattool/warehouse</url>
|
||||||
@@ -41,268 +41,268 @@
|
|||||||
<url type="translate">https://weblate.fyralabs.com/projects/flattool/warehouse/</url>
|
<url type="translate">https://weblate.fyralabs.com/projects/flattool/warehouse/</url>
|
||||||
<url type="donation">https://ko-fi.com/heliguy</url>
|
<url type="donation">https://ko-fi.com/heliguy</url>
|
||||||
<screenshots>
|
<screenshots>
|
||||||
<screenshot type="default">
|
<screenshot type="default">
|
||||||
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/packages_page_wide.png</image>
|
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/packages_page_wide.png</image>
|
||||||
<caption>Manage Installed Packages in Three Pane UI</caption>
|
<caption>Manage Installed Packages in Three Pane UI</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/propteries_page_skinny.png</image>
|
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/propteries_page_skinny.png</image>
|
||||||
<caption>Properties Page in Narrow Window</caption>
|
<caption>Properties Page in Narrow Window</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/remotes_page_wide.png</image>
|
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/remotes_page_wide.png</image>
|
||||||
<caption>Manage Installed Remotes and Add New Remotes</caption>
|
<caption>Manage Installed Remotes and Add New Remotes</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/data_page_wide.png</image>
|
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/data_page_wide.png</image>
|
||||||
<caption>Manage Apps' User Data</caption>
|
<caption>Manage Apps' User Data</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/snapshots_page_wide.png</image>
|
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/snapshots_page_wide.png</image>
|
||||||
<caption>Backup Apps' User Data</caption>
|
<caption>Backup Apps' User Data</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/install_page_wide.png</image>
|
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/install_page_wide.png</image>
|
||||||
<caption>Install New Packages from Files or Remotes</caption>
|
<caption>Install New Packages from Files or Remotes</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
<screenshot>
|
<screenshot>
|
||||||
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/install_page_skinny.png</image>
|
<image>https://raw.githubusercontent.com/flattool/warehouse/main/app_page_screeshots/install_page_skinny.png</image>
|
||||||
<caption>Install Page in Narrow Window</caption>
|
<caption>Install Page in Narrow Window</caption>
|
||||||
</screenshot>
|
</screenshot>
|
||||||
</screenshots>
|
</screenshots>
|
||||||
<releases>
|
<releases>
|
||||||
<release version="2.0.0" date="2024-10-28" timestamp="1730014216">
|
<release version="2.0.0" date="2024-10-28" timestamp="1730014216">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>New Features</p>
|
<p>New Features</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>All new UI to make using Warehouse's features easier</li>
|
<li>All new UI to make using Warehouse's features easier</li>
|
||||||
<li>UI now better adapts to larger window sizes</li>
|
<li>UI now better adapts to larger window sizes</li>
|
||||||
<li>UI now better adapts to smaller window sizes</li>
|
<li>UI now better adapts to smaller window sizes</li>
|
||||||
<li>Improved UI for installing packages from remotes</li>
|
<li>Improved UI for installing packages from remotes</li>
|
||||||
<li>Snapshots can be given names</li>
|
<li>Snapshots can be given names</li>
|
||||||
<li>Packages can be reinstalled with a few clicks</li>
|
<li>Packages can be reinstalled with a few clicks</li>
|
||||||
<li>User Data can now be sorted in multiple ways</li>
|
<li>User Data can now be sorted in multiple ways</li>
|
||||||
<li>Active User Data can be browsed just as easily as leftover User Data</li>
|
<li>Active User Data can be browsed just as easily as leftover User Data</li>
|
||||||
<li>Custom installation locations are now supported</li>
|
<li>Custom installation locations are now supported</li>
|
||||||
<li>Leftover Snapshots are now shown</li>
|
<li>Leftover Snapshots are now shown</li>
|
||||||
<li>Apps can be reinstalled from leftover Snapshots</li>
|
<li>Apps can be reinstalled from leftover Snapshots</li>
|
||||||
<li>Installation location of disabled remotes is now shown</li>
|
<li>Installation location of disabled remotes is now shown</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Changes</p>
|
<p>Changes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Packages list filter options are now easier to understand, and more predictable with how they are applied</li>
|
<li>Packages list filter options are now easier to understand, and more predictable with how they are applied</li>
|
||||||
<li>Improved keyboard shortcuts for quick navigation</li>
|
<li>Improved keyboard shortcuts for quick navigation</li>
|
||||||
<li>The Downgrades (renamed to Change Version) interface now shows the currently installed version</li>
|
<li>The Downgrades (renamed to Change Version) interface now shows the currently installed version</li>
|
||||||
<li>Long running processes now have progress bars and can be canceled</li>
|
<li>Long running processes now have progress bars and can be canceled</li>
|
||||||
<li>Better status icons for End of Life, Masked, and Pinned packages</li>
|
<li>Better status icons for End of Life, Masked, and Pinned packages</li>
|
||||||
<li>Warehouse no longer disables closing its window when long running processes are happening</li>
|
<li>Warehouse no longer disables closing its window when long running processes are happening</li>
|
||||||
<li>Refreshing now shows a loading animation</li>
|
<li>Refreshing now shows a loading animation</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Bug Fixes and Performance Improvements</p>
|
<p>Bug Fixes and Performance Improvements</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Warehouse is now faster to open</li>
|
<li>Warehouse is now faster to open</li>
|
||||||
<li>Getting system information is now faster</li>
|
<li>Getting system information is now faster</li>
|
||||||
<li>Long running processes no longer freeze the app</li>
|
<li>Long running processes no longer freeze the app</li>
|
||||||
<li>Refreshing is no longer possible when long running processes are happening</li>
|
<li>Refreshing is no longer possible when long running processes are happening</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.6.3" date="2024-7-3" timestamp="1720030786">
|
<release version="1.6.3" date="2024-7-3" timestamp="1720030786">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>Bug Fixes</p>
|
<p>Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Downgrade Window no longer silently fails when downgrading a masked Flatpak, and instead, downgrades it</li>
|
<li>Downgrade Window no longer silently fails when downgrading a masked Flatpak, and instead, downgrades it</li>
|
||||||
<li>When downgrading and masking system Flatpaks, the password prompt only happens once instead of twice</li>
|
<li>When downgrading and masking system Flatpaks, the password prompt only happens once instead of twice</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.6.2" date="2024-4-16" timestamp="1713282319">
|
<release version="1.6.2" date="2024-4-16" timestamp="1713282319">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>Bug Fixes</p>
|
<p>Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Install from the Web no longer has issues on systems with only one remote</li>
|
<li>Install from the Web no longer has issues on systems with only one remote</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.6.1" date="2024-4-7" timestamp="1712531365">
|
<release version="1.6.1" date="2024-4-7" timestamp="1712531365">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>Bug Fixes</p>
|
<p>Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>App Properties no longer has issues with information containing colon characters</li>
|
<li>App Properties no longer has issues with information containing colon characters</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Previous Releases's New Features and Changes</p>
|
<p>Previous Releases's New Features and Changes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>The main list has a status page while refreshing</li>
|
<li>The main list has a status page while refreshing</li>
|
||||||
<li>Add ability to pin and unpin runtimes</li>
|
<li>Add ability to pin and unpin runtimes</li>
|
||||||
<li>App Properties shows license and proper commit information</li>
|
<li>App Properties shows license and proper commit information</li>
|
||||||
<li>App Properties better shows the app's name and description</li>
|
<li>App Properties better shows the app's name and description</li>
|
||||||
<li>Update to GNOME 46 GTK Technologies</li>
|
<li>Update to GNOME 46 GTK Technologies</li>
|
||||||
<li>Updated translations</li>
|
<li>Updated translations</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Previous Releases's Bug Fixes</p>
|
<p>Previous Releases's Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>When an attempt to run an app fails, correct runtime error information is shown</li>
|
<li>When an attempt to run an app fails, correct runtime error information is shown</li>
|
||||||
<li>Install From The Web no longer behaves incorrectly on remote installations with options</li>
|
<li>Install From The Web no longer behaves incorrectly on remote installations with options</li>
|
||||||
<li>The rare chance that Install From The Web selects a disabled remote has been fixed</li>
|
<li>The rare chance that Install From The Web selects a disabled remote has been fixed</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.6.0" date="2024-4-7" timestamp="1712463635">
|
<release version="1.6.0" date="2024-4-7" timestamp="1712463635">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>New Features and Changes</p>
|
<p>New Features and Changes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>The main list has a status page while refreshing</li>
|
<li>The main list has a status page while refreshing</li>
|
||||||
<li>Add ability to pin and unpin runtimes</li>
|
<li>Add ability to pin and unpin runtimes</li>
|
||||||
<li>App Properties shows license and proper commit information</li>
|
<li>App Properties shows license and proper commit information</li>
|
||||||
<li>App Properties better shows the app's name and description</li>
|
<li>App Properties better shows the app's name and description</li>
|
||||||
<li>Update to GNOME 46 GTK Technologies</li>
|
<li>Update to GNOME 46 GTK Technologies</li>
|
||||||
<li>Updated translations</li>
|
<li>Updated translations</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Bug Fixes</p>
|
<p>Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>When an attempt to run an app fails, correct runtime error information is shown</li>
|
<li>When an attempt to run an app fails, correct runtime error information is shown</li>
|
||||||
<li>Install From The Web no longer behaves incorrectly on remote installations with options</li>
|
<li>Install From The Web no longer behaves incorrectly on remote installations with options</li>
|
||||||
<li>The rare chance that Install From The Web selects a disabled remote has been fixed</li>
|
<li>The rare chance that Install From The Web selects a disabled remote has been fixed</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.5.1" date="2024-3-8" timestamp="1709921475">
|
<release version="1.5.1" date="2024-3-8" timestamp="1709921475">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>Bug Fixes</p>
|
<p>Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Main list is no longer scrolled to the bottom on launch</li>
|
<li>Main list is no longer scrolled to the bottom on launch</li>
|
||||||
<li>Leftover Data window no longer tries to use a different window for toast messages</li>
|
<li>Leftover Data window no longer tries to use a different window for toast messages</li>
|
||||||
<li>Fix issue causing Downgrade window to not be able to downgrade user installation Flatpaks</li>
|
<li>Fix issue causing Downgrade window to not be able to downgrade user installation Flatpaks</li>
|
||||||
<li>Fix the accidental removal of translations</li>
|
<li>Fix the accidental removal of translations</li>
|
||||||
<li>Fix issue causing Reset Filters button to be clickable upon Filter Window opening even when the filters are default</li>
|
<li>Fix issue causing Reset Filters button to be clickable upon Filter Window opening even when the filters are default</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.5.0" date="2024-3-5" timestamp="1709694300">
|
<release version="1.5.0" date="2024-3-5" timestamp="1709694300">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>New Features and Changes</p>
|
<p>New Features and Changes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Flatpaks can now be installed from the web, from any added remote</li>
|
<li>Flatpaks can now be installed from the web, from any added remote</li>
|
||||||
<li>Filters are now saved and restored between sessions</li>
|
<li>Filters are now saved and restored between sessions</li>
|
||||||
<li>Filters now apply live</li>
|
<li>Filters now apply live</li>
|
||||||
<li>Move Refresh Button into Main Menu</li>
|
<li>Move Refresh Button into Main Menu</li>
|
||||||
<li>Period, 0 to 9, and underscores are now allowed in new Custom Remote names</li>
|
<li>Period, 0 to 9, and underscores are now allowed in new Custom Remote names</li>
|
||||||
<li>Updated translations</li>
|
<li>Updated translations</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Bug Fixes</p>
|
<p>Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Hide Show Disabled Remotes button when there aren't any</li>
|
<li>Hide Show Disabled Remotes button when there aren't any</li>
|
||||||
<li>Fix Batch Snapshots accidentally triggering Select All</li>
|
<li>Fix Batch Snapshots accidentally triggering Select All</li>
|
||||||
<li>Misc UI element tooltips and alignments</li>
|
<li>Misc UI element tooltips and alignments</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.4.0" date="2023-12-17" timestamp="1702875630">
|
<release version="1.4.0" date="2023-12-17" timestamp="1702875630">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>New Features and Changes</p>
|
<p>New Features and Changes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>View disabled remotes, enable and disable remotes, and set a filter for a remote in the Manage Remotes window</li>
|
<li>View disabled remotes, enable and disable remotes, and set a filter for a remote in the Manage Remotes window</li>
|
||||||
<li>Added a new Snapshot feature. Snapshots can be created and applied at any time for quick saving app user data</li>
|
<li>Added a new Snapshot feature. Snapshots can be created and applied at any time for quick saving app user data</li>
|
||||||
<li>Added a batch action to create Snapshots</li>
|
<li>Added a batch action to create Snapshots</li>
|
||||||
<li>Revamped the Properties window, and it can also open app details in the software store</li>
|
<li>Revamped the Properties window, and it can also open app details in the software store</li>
|
||||||
<li>Merged the Popular Remotes window into the Manage Remotes window</li>
|
<li>Merged the Popular Remotes window into the Manage Remotes window</li>
|
||||||
<li>Removed labels in the main list, placing them in popup text buttons instead, to save room</li>
|
<li>Removed labels in the main list, placing them in popup text buttons instead, to save room</li>
|
||||||
<li>Added progress bars to show progress of batch actions</li>
|
<li>Added progress bars to show progress of batch actions</li>
|
||||||
<li>Empty search pages now display a message</li>
|
<li>Empty search pages now display a message</li>
|
||||||
<li>Added a troubleshooting information page</li>
|
<li>Added a troubleshooting information page</li>
|
||||||
<li>Added a donation link</li>
|
<li>Added a donation link</li>
|
||||||
<li>Added translations (NL, FR, RU, SV, UK)</li>
|
<li>Added translations (NL, FR, RU, SV, UK)</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Bug Fixes</p>
|
<p>Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Launching Warehouse no longer hangs when grabbing the list of Flatpaks</li>
|
<li>Launching Warehouse no longer hangs when grabbing the list of Flatpaks</li>
|
||||||
<li>Unexpected errors are caught more often and handled better</li>
|
<li>Unexpected errors are caught more often and handled better</li>
|
||||||
<li>Fixed a few typos</li>
|
<li>Fixed a few typos</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.3.0" date="2023-10-23" timestamp="1698112217">
|
<release version="1.3.0" date="2023-10-23" timestamp="1698112217">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>New Features and Changes</p>
|
<p>New Features and Changes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Names, IDs, Refs, and Launch Commands can now be copied from a dropdown</li>
|
<li>Names, IDs, Refs, and Launch Commands can now be copied from a dropdown</li>
|
||||||
<li>Updates can now be disabled and enabled for Flatpaks</li>
|
<li>Updates can now be disabled and enabled for Flatpaks</li>
|
||||||
<li>Warehouse can now downgrade Flatpaks</li>
|
<li>Warehouse can now downgrade Flatpaks</li>
|
||||||
<li>Apps can be now be ran from Warehouse</li>
|
<li>Apps can be now be ran from Warehouse</li>
|
||||||
<li>Control + Keypad Enter now also toggles select mode</li>
|
<li>Control + Keypad Enter now also toggles select mode</li>
|
||||||
<li>Added translations (HG, ES, TH)</li>
|
<li>Added translations (HG, ES, TH)</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Bug Fixes</p>
|
<p>Bug Fixes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>The runtime filter button in the properties window now only shows when that runtime is a dependent runtime</li>
|
<li>The runtime filter button in the properties window now only shows when that runtime is a dependent runtime</li>
|
||||||
<li>Removed the Select All keyboard shortcut as it interfered with the search bar</li>
|
<li>Removed the Select All keyboard shortcut as it interfered with the search bar</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.2.1" date="2023-10-15" timestamp="1697405182">
|
<release version="1.2.1" date="2023-10-15" timestamp="1697405182">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<ul>
|
<ul>
|
||||||
<li>The main list of Flatpaks is now sorted by name instead of ID</li>
|
<li>The main list of Flatpaks is now sorted by name instead of ID</li>
|
||||||
<li>Warehouse can now be found in your app menu by searching for "flatpak"</li>
|
<li>Warehouse can now be found in your app menu by searching for "flatpak"</li>
|
||||||
<li>Corrected typo in the previous release's change log</li>
|
<li>Corrected typo in the previous release's change log</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.2.0" date="2023-10-11" timestamp="1697076304">
|
<release version="1.2.0" date="2023-10-11" timestamp="1697076304">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>New Features and Changes</p>
|
<p>New Features and Changes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Flatpaks files can be installed with a drag and drop or from a file selection</li>
|
<li>Flatpaks files can be installed with a drag and drop or from a file selection</li>
|
||||||
<li>Flatpak remotes can be added with a drag and drop or from a file selection</li>
|
<li>Flatpak remotes can be added with a drag and drop or from a file selection</li>
|
||||||
<li>Apps can now be filtered by dependent runtimes</li>
|
<li>Apps can now be filtered by dependent runtimes</li>
|
||||||
<li>Properties of a runtime now shows a button to show only apps that rely on the runtime</li>
|
<li>Properties of a runtime now shows a button to show only apps that rely on the runtime</li>
|
||||||
<li>A loading indicator is now shown when adding a remote</li>
|
<li>A loading indicator is now shown when adding a remote</li>
|
||||||
<li>Added Webkit Testing to the list of popular remotes</li>
|
<li>Added Webkit Testing to the list of popular remotes</li>
|
||||||
<li>Added a search bar to the Leftover Data window</li>
|
<li>Added a search bar to the Leftover Data window</li>
|
||||||
<li>Added a button to open the entire user data folder the Leftover Data window</li>
|
<li>Added a button to open the entire user data folder the Leftover Data window</li>
|
||||||
<li>Added a button on each row in the Leftover Data list to open them directly</li>
|
<li>Added a button on each row in the Leftover Data list to open them directly</li>
|
||||||
<li>Changed margins of lists to improve legibility</li>
|
<li>Changed margins of lists to improve legibility</li>
|
||||||
<li>F10 now opens the main menu</li>
|
<li>F10 now opens the main menu</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Bug Fixes and Stability Improvements</p>
|
<p>Bug Fixes and Stability Improvements</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Fixed a crash that would sometimes occur when opening the Leftover Data window</li>
|
<li>Fixed a crash that would sometimes occur when opening the Leftover Data window</li>
|
||||||
<li>Popular remotes are now named correcting and have proper descriptions</li>
|
<li>Popular remotes are now named correcting and have proper descriptions</li>
|
||||||
<li>The filter button now disables when the Filter window is closed by the keyboard</li>
|
<li>The filter button now disables when the Filter window is closed by the keyboard</li>
|
||||||
<li>The default filter is no longer allowed to be set as a new filter</li>
|
<li>The default filter is no longer allowed to be set as a new filter</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.1.1" date="2023-10-6" timestamp="1696572524">
|
<release version="1.1.1" date="2023-10-6" timestamp="1696572524">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>Emergency Bug Fix</p>
|
<p>Emergency Bug Fix</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Fix error causing a crash on Linux Mint</li>
|
<li>Fix error causing a crash on Linux Mint</li>
|
||||||
<li>Correct typo in the app summary</li>
|
<li>Correct typo in the app summary</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.1.0" date="2023-10-4" timestamp="1696461734">
|
<release version="1.1.0" date="2023-10-4" timestamp="1696461734">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>New Features and Changes</p>
|
<p>New Features and Changes</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>Choose from a list of popular remotes when adding a new remote</li>
|
<li>Choose from a list of popular remotes when adding a new remote</li>
|
||||||
<li>App properties now shows the runtime that the app relies on</li>
|
<li>App properties now shows the runtime that the app relies on</li>
|
||||||
<li>Apps and runtimes that are End of Life are now noted as such</li>
|
<li>Apps and runtimes that are End of Life are now noted as such</li>
|
||||||
<li>Window size and state is remembered between sessions</li>
|
<li>Window size and state is remembered between sessions</li>
|
||||||
</ul>
|
</ul>
|
||||||
<p>Bug Fixes and Performance Improvements</p>
|
<p>Bug Fixes and Performance Improvements</p>
|
||||||
<ul>
|
<ul>
|
||||||
<li>The UI no longer freezes when uninstalling apps</li>
|
<li>The UI no longer freezes when uninstalling apps</li>
|
||||||
<li>The UI no longer freezes when getting file sizes of large files</li>
|
<li>The UI no longer freezes when getting file sizes of large files</li>
|
||||||
<li>Toggling batch mode no longer causes a freeze</li>
|
<li>Toggling batch mode no longer causes a freeze</li>
|
||||||
<li>Selecting all apps no longer causes a freeze</li>
|
<li>Selecting all apps no longer causes a freeze</li>
|
||||||
<li>Applying and removing a filter no longer causes a freeze</li>
|
<li>Applying and removing a filter no longer causes a freeze</li>
|
||||||
<li>Fixed issue where the no remotes status page would not be removed when a new remote was added</li>
|
<li>Fixed issue where the no remotes status page would not be removed when a new remote was added</li>
|
||||||
</ul>
|
</ul>
|
||||||
</description>
|
</description>
|
||||||
</release>
|
</release>
|
||||||
<release version="1.0.0" date="2023-9-25" timestamp="1695695940">
|
<release version="1.0.0" date="2023-9-25" timestamp="1695695940">
|
||||||
<description translate="no">
|
<description translate="no">
|
||||||
<p>First release of Warehouse</p>
|
<p>First release of Warehouse</p>
|
||||||
</description>
|
</description>
|
||||||
|
|||||||
@@ -1,55 +1,55 @@
|
|||||||
{
|
{
|
||||||
"id": "io.github.flattool.Warehouse",
|
"id": "io.github.flattool.Warehouse",
|
||||||
"runtime": "org.gnome.Platform",
|
"runtime": "org.gnome.Platform",
|
||||||
"runtime-version": "47",
|
"runtime-version": "47",
|
||||||
"sdk": "org.gnome.Sdk",
|
"sdk": "org.gnome.Sdk",
|
||||||
"command": "warehouse",
|
"command": "warehouse",
|
||||||
"finish-args": [
|
"finish-args": [
|
||||||
"--share=ipc",
|
"--share=ipc",
|
||||||
"--socket=fallback-x11",
|
"--socket=fallback-x11",
|
||||||
"--device=dri",
|
"--device=dri",
|
||||||
"--socket=wayland",
|
"--socket=wayland",
|
||||||
"--talk-name=org.freedesktop.Flatpak",
|
"--talk-name=org.freedesktop.Flatpak",
|
||||||
"--filesystem=/var/lib/flatpak/:ro",
|
"--filesystem=/var/lib/flatpak/:ro",
|
||||||
"--filesystem=~/.local/share/flatpak/:ro",
|
"--filesystem=~/.local/share/flatpak/:ro",
|
||||||
"--filesystem=~/.var/app/",
|
"--filesystem=~/.var/app/",
|
||||||
"--filesystem=host-etc"
|
"--filesystem=host-etc"
|
||||||
],
|
],
|
||||||
"cleanup": [
|
"cleanup": [
|
||||||
"/include",
|
"/include",
|
||||||
"/lib/pkgconfig",
|
"/lib/pkgconfig",
|
||||||
"/man",
|
"/man",
|
||||||
"/share/doc",
|
"/share/doc",
|
||||||
"/share/gtk-doc",
|
"/share/gtk-doc",
|
||||||
"/share/man",
|
"/share/man",
|
||||||
"/share/pkgconfig",
|
"/share/pkgconfig",
|
||||||
"*.la",
|
"*.la",
|
||||||
"*.a"
|
"*.a"
|
||||||
],
|
],
|
||||||
"modules": [
|
"modules": [
|
||||||
{
|
{
|
||||||
"name": "blueprint-compiler",
|
"name": "blueprint-compiler",
|
||||||
"buildsystem": "meson",
|
"buildsystem": "meson",
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "git",
|
"type": "git",
|
||||||
"url": "https://gitlab.gnome.org/jwestman/blueprint-compiler",
|
"url": "https://gitlab.gnome.org/jwestman/blueprint-compiler",
|
||||||
"tag": "v0.14.0"
|
"tag": "v0.14.0"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"cleanup": ["*"]
|
"cleanup": ["*"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "warehouse",
|
"name": "warehouse",
|
||||||
"builddir": true,
|
"builddir": true,
|
||||||
"buildsystem": "meson",
|
"buildsystem": "meson",
|
||||||
"config-opts": ["-Dprofile=development"],
|
"config-opts": ["-Dprofile=development"],
|
||||||
"sources": [
|
"sources": [
|
||||||
{
|
{
|
||||||
"type": "dir",
|
"type": "dir",
|
||||||
"path": "."
|
"path": "."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,118 +7,118 @@ import subprocess
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/change_version_page/change_version_page.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/change_version_page/change_version_page.ui")
|
||||||
class ChangeVersionPage(Adw.NavigationPage):
|
class ChangeVersionPage(Adw.NavigationPage):
|
||||||
__gtype_name__ = 'ChangeVersionPage'
|
__gtype_name__ = 'ChangeVersionPage'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
scrolled_window = gtc()
|
scrolled_window = gtc()
|
||||||
versions_clamp = gtc()
|
versions_clamp = gtc()
|
||||||
root_group_check_button = gtc()
|
root_group_check_button = gtc()
|
||||||
mask_group = gtc()
|
mask_group = gtc()
|
||||||
mask_row = gtc()
|
mask_row = gtc()
|
||||||
versions_group = gtc()
|
versions_group = gtc()
|
||||||
action_bar = gtc()
|
action_bar = gtc()
|
||||||
apply_button = gtc()
|
apply_button = gtc()
|
||||||
|
|
||||||
selected_commit = None
|
selected_commit = None
|
||||||
failure = None
|
failure = None
|
||||||
|
|
||||||
def get_commits(self, *args):
|
def get_commits(self, *args):
|
||||||
cmd = ['flatpak-spawn', '--host', 'sh', '-c']
|
cmd = ['flatpak-spawn', '--host', 'sh', '-c']
|
||||||
script = f"LC_ALL=C flatpak remote-info --log {self.package.info['origin']} {self.package.info['ref']} "
|
script = f"LC_ALL=C flatpak remote-info --log {self.package.info['origin']} {self.package.info['ref']} "
|
||||||
installation = self.package.info["installation"]
|
installation = self.package.info["installation"]
|
||||||
if installation == "user" or installation == "system":
|
if installation == "user" or installation == "system":
|
||||||
script += f"--{installation}"
|
script += f"--{installation}"
|
||||||
else:
|
else:
|
||||||
script += f"--installation={installation}"
|
script += f"--installation={installation}"
|
||||||
|
|
||||||
cmd.append(script)
|
cmd.append(script)
|
||||||
|
|
||||||
commits = []
|
commits = []
|
||||||
changes = []
|
changes = []
|
||||||
dates = []
|
dates = []
|
||||||
try:
|
try:
|
||||||
output = subprocess.run(cmd, check=True, capture_output=True, text=True).stdout
|
output = subprocess.run(cmd, check=True, capture_output=True, text=True).stdout
|
||||||
lines = output.strip().split('\n')
|
lines = output.strip().split('\n')
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line = line.strip().split(": ", 1)
|
line = line.strip().split(": ", 1)
|
||||||
if len(line) < 2:
|
if len(line) < 2:
|
||||||
continue
|
continue
|
||||||
elif line[0].startswith("Commit"):
|
elif line[0].startswith("Commit"):
|
||||||
commits.append(line[1])
|
commits.append(line[1])
|
||||||
elif line[0].startswith("Subject"):
|
elif line[0].startswith("Subject"):
|
||||||
changes.append(line[1])
|
changes.append(line[1])
|
||||||
elif line[0].startswith("Date"):
|
elif line[0].startswith("Date"):
|
||||||
dates.append(line[1])
|
dates.append(line[1])
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.failure = cpe.stderr
|
self.failure = cpe.stderr
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.failure = str(e)
|
self.failure = str(e)
|
||||||
return
|
return
|
||||||
|
|
||||||
if not (len(commits) == len(changes) == len(dates)):
|
if not (len(commits) == len(changes) == len(dates)):
|
||||||
self.failure = "Commits, Changes, and Dates are not of equivalent length"
|
self.failure = "Commits, Changes, and Dates are not of equivalent length"
|
||||||
return
|
return
|
||||||
|
|
||||||
def idle(*args):
|
def idle(*args):
|
||||||
for index, commit in enumerate(commits):
|
for index, commit in enumerate(commits):
|
||||||
row = Adw.ActionRow(title=GLib.markup_escape_text(changes[index]), subtitle=f"{GLib.markup_escape_text(commit)}\n{GLib.markup_escape_text(dates[index])}")
|
row = Adw.ActionRow(title=GLib.markup_escape_text(changes[index]), subtitle=f"{GLib.markup_escape_text(commit)}\n{GLib.markup_escape_text(dates[index])}")
|
||||||
if commit == self.package.cli_info.get("commit", None):
|
if commit == self.package.cli_info.get("commit", None):
|
||||||
row.set_sensitive(False)
|
row.set_sensitive(False)
|
||||||
row.add_prefix(Gtk.Image(icon_name="check-plain-symbolic", margin_start=5, margin_end=5))
|
row.add_prefix(Gtk.Image(icon_name="check-plain-symbolic", margin_start=5, margin_end=5))
|
||||||
row.set_tooltip_text(_("Currently Installed Version"))
|
row.set_tooltip_text(_("Currently Installed Version"))
|
||||||
else:
|
else:
|
||||||
check = Gtk.CheckButton()
|
check = Gtk.CheckButton()
|
||||||
check.connect("activate", lambda *_, comm=commit: self.set_commit(comm))
|
check.connect("activate", lambda *_, comm=commit: self.set_commit(comm))
|
||||||
check.set_group(self.root_group_check_button)
|
check.set_group(self.root_group_check_button)
|
||||||
row.set_activatable_widget(check)
|
row.set_activatable_widget(check)
|
||||||
row.add_prefix(check)
|
row.add_prefix(check)
|
||||||
|
|
||||||
self.versions_group.add(row)
|
self.versions_group.add(row)
|
||||||
|
|
||||||
GLib.idle_add(idle)
|
GLib.idle_add(idle)
|
||||||
|
|
||||||
def set_commit(self, commit):
|
def set_commit(self, commit):
|
||||||
self.selected_commit = commit
|
self.selected_commit = commit
|
||||||
|
|
||||||
def get_commits_callback(self, *args):
|
def get_commits_callback(self, *args):
|
||||||
if not self.failure is None:
|
if not self.failure is None:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not get versions"), self.failure).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not get versions"), self.failure).toast)
|
||||||
else:
|
else:
|
||||||
self.scrolled_window.set_child(self.versions_clamp)
|
self.scrolled_window.set_child(self.versions_clamp)
|
||||||
|
|
||||||
def callback(self, did_error):
|
def callback(self, did_error):
|
||||||
HostInfo.main_window.refresh_handler()
|
HostInfo.main_window.refresh_handler()
|
||||||
if not did_error:
|
if not did_error:
|
||||||
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Changed {}'s Version").format(self.package.info['name'])))
|
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Changed {}'s Version").format(self.package.info['name'])))
|
||||||
|
|
||||||
def error_callback(self, user_facing_label, error_message):
|
def error_callback(self, user_facing_label, error_message):
|
||||||
HostInfo.main_window.toast_overlay.add_toast(ErrorToast(user_facing_label, error_message).toast)
|
HostInfo.main_window.toast_overlay.add_toast(ErrorToast(user_facing_label, error_message).toast)
|
||||||
|
|
||||||
def on_apply(self, *args):
|
def on_apply(self, *args):
|
||||||
if ChangeVersionWorker.change_version(
|
if ChangeVersionWorker.change_version(
|
||||||
self.mask_row.get_active(),
|
self.mask_row.get_active(),
|
||||||
self.package, self.selected_commit,
|
self.package, self.selected_commit,
|
||||||
self.packages_page.changing_version,
|
self.packages_page.changing_version,
|
||||||
self.callback,
|
self.callback,
|
||||||
self.error_callback,
|
self.error_callback,
|
||||||
):
|
):
|
||||||
self.packages_page.set_status(self.packages_page.changing_version)
|
self.packages_page.set_status(self.packages_page.changing_version)
|
||||||
|
|
||||||
def __init__(self, packages_page, package, **kwargs):
|
def __init__(self, packages_page, package, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.packages_page = packages_page
|
self.packages_page = packages_page
|
||||||
self.package = package
|
self.package = package
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
pkg_name = package.info["name"]
|
pkg_name = package.info["name"]
|
||||||
self.set_title(_("{} Versions").format(pkg_name))
|
self.set_title(_("{} Versions").format(pkg_name))
|
||||||
self.mask_row.set_subtitle(_("Ensure that {} will never be updated to a newer version").format(pkg_name))
|
self.mask_row.set_subtitle(_("Ensure that {} will never be updated to a newer version").format(pkg_name))
|
||||||
self.scrolled_window.set_child(LoadingStatus(_("Fetching Releases"), _("This could take a while")))
|
self.scrolled_window.set_child(LoadingStatus(_("Fetching Releases"), _("This could take a while")))
|
||||||
Gio.Task.new(None, None, self.get_commits_callback).run_in_thread(self.get_commits)
|
Gio.Task.new(None, None, self.get_commits_callback).run_in_thread(self.get_commits)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.root_group_check_button.connect("toggled", lambda *_: self.action_bar.set_revealed(True))
|
self.root_group_check_button.connect("toggled", lambda *_: self.action_bar.set_revealed(True))
|
||||||
self.apply_button.connect("clicked", self.on_apply)
|
self.apply_button.connect("clicked", self.on_apply)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# SPDX-License-Identifier: GPL-3.0-only
|
# SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
DEVEL = '@DEVEL@' == 'Development'
|
DEVEL = '@DEVEL@' == 'Development'
|
||||||
PROFILE = '@DEVEL@'
|
PROFILE = '@DEVEL@'
|
||||||
APP_ID = '@APPID@'
|
APP_ID = '@APPID@'
|
||||||
VERSION = '@VERSION@'
|
VERSION = '@VERSION@'
|
||||||
|
|||||||
@@ -2,43 +2,43 @@ from gi.repository import Adw, Gtk, GLib
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/gtk/app_row.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/gtk/app_row.ui")
|
||||||
class AppRow(Adw.ActionRow):
|
class AppRow(Adw.ActionRow):
|
||||||
__gtype_name__ = 'AppRow'
|
__gtype_name__ = 'AppRow'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
image = gtc()
|
image = gtc()
|
||||||
eol_package_package_status_icon = gtc()
|
eol_package_package_status_icon = gtc()
|
||||||
eol_runtime_status_icon = gtc()
|
eol_runtime_status_icon = gtc()
|
||||||
pinned_status_icon = gtc()
|
pinned_status_icon = gtc()
|
||||||
masked_status_icon = gtc()
|
masked_status_icon = gtc()
|
||||||
check_button = gtc()
|
check_button = gtc()
|
||||||
|
|
||||||
def idle_stuff(self):
|
def idle_stuff(self):
|
||||||
if self.package.icon_path:
|
if self.package.icon_path:
|
||||||
self.image.add_css_class("icon-dropshadow")
|
self.image.add_css_class("icon-dropshadow")
|
||||||
self.image.set_from_file(self.package.icon_path)
|
self.image.set_from_file(self.package.icon_path)
|
||||||
|
|
||||||
def gesture_handler(self, *args):
|
def gesture_handler(self, *args):
|
||||||
if self.on_long_press:
|
if self.on_long_press:
|
||||||
self.on_long_press(self)
|
self.on_long_press(self)
|
||||||
|
|
||||||
def __init__(self, package, on_long_press=None, **kwargs):
|
def __init__(self, package, on_long_press=None, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.package = package
|
self.package = package
|
||||||
self.on_long_press = on_long_press
|
self.on_long_press = on_long_press
|
||||||
self.rclick_gesture = Gtk.GestureClick(button=3)
|
self.rclick_gesture = Gtk.GestureClick(button=3)
|
||||||
self.long_press_gesture = Gtk.GestureLongPress()
|
self.long_press_gesture = Gtk.GestureLongPress()
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
GLib.idle_add(lambda *_: self.set_title(package.info["name"]))
|
GLib.idle_add(lambda *_: self.set_title(package.info["name"]))
|
||||||
GLib.idle_add(lambda *_: self.set_subtitle(package.info["id"]))
|
GLib.idle_add(lambda *_: self.set_subtitle(package.info["id"]))
|
||||||
GLib.idle_add(lambda *_: self.idle_stuff())
|
GLib.idle_add(lambda *_: self.idle_stuff())
|
||||||
self.add_controller(self.rclick_gesture)
|
self.add_controller(self.rclick_gesture)
|
||||||
self.add_controller(self.long_press_gesture)
|
self.add_controller(self.long_press_gesture)
|
||||||
if package.info['id'] == "io.github.flattool.Warehouse":
|
if package.info['id'] == "io.github.flattool.Warehouse":
|
||||||
self.check_button.set_active = lambda *_: None
|
self.check_button.set_active = lambda *_: None
|
||||||
self.check_button.set_sensitive(False)
|
self.check_button.set_sensitive(False)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.rclick_gesture.connect("released", self.gesture_handler)
|
self.rclick_gesture.connect("released", self.gesture_handler)
|
||||||
self.long_press_gesture.connect("pressed", self.gesture_handler)
|
self.long_press_gesture.connect("pressed", self.gesture_handler)
|
||||||
|
|||||||
@@ -1,29 +1,29 @@
|
|||||||
from gi.repository import Adw, Gtk, Gdk, GLib
|
from gi.repository import Adw, Gtk, Gdk, GLib
|
||||||
|
|
||||||
class ErrorToast:
|
class ErrorToast:
|
||||||
main_window = None
|
main_window = None
|
||||||
def __init__(self, display_msg, error_msg):
|
def __init__(self, display_msg, error_msg):
|
||||||
|
|
||||||
def on_response(dialog, response_id):
|
def on_response(dialog, response_id):
|
||||||
if response_id == "copy":
|
if response_id == "copy":
|
||||||
self.clipboard.set(error_msg)
|
self.clipboard.set(error_msg)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.toast = Adw.Toast(title=display_msg, button_label=_("Details"))
|
self.toast = Adw.Toast(title=display_msg, button_label=_("Details"))
|
||||||
popup = Adw.AlertDialog.new(display_msg)
|
popup = Adw.AlertDialog.new(display_msg)
|
||||||
self.clipboard = Gdk.Display.get_default().get_clipboard()
|
self.clipboard = Gdk.Display.get_default().get_clipboard()
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
print(display_msg)
|
print(display_msg)
|
||||||
print(error_msg)
|
print(error_msg)
|
||||||
popup.add_response("copy", _("Copy"))
|
popup.add_response("copy", _("Copy"))
|
||||||
popup.add_response("ok", _("OK"))
|
popup.add_response("ok", _("OK"))
|
||||||
lb = Gtk.Label(selectable=True, wrap=True)#, natural_wrap_mode=Gtk.NaturalWrapMode.WORD)
|
lb = Gtk.Label(selectable=True, wrap=True)#, natural_wrap_mode=Gtk.NaturalWrapMode.WORD)
|
||||||
lb.set_markup(f"<tt>{GLib.markup_escape_text(error_msg)}</tt>")
|
lb.set_markup(f"<tt>{GLib.markup_escape_text(error_msg)}</tt>")
|
||||||
# lb.set_label(error_msg)
|
# lb.set_label(error_msg)
|
||||||
# lb.set_selectable(True)
|
# lb.set_selectable(True)
|
||||||
popup.set_extra_child(lb)
|
popup.set_extra_child(lb)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.toast.connect("button-clicked", lambda *_: popup.present(self.main_window))
|
self.toast.connect("button-clicked", lambda *_: popup.present(self.main_window))
|
||||||
popup.connect("response", on_response)
|
popup.connect("response", on_response)
|
||||||
|
|||||||
644
src/host_info.py
644
src/host_info.py
@@ -9,376 +9,376 @@ direction = Gtk.Image().get_direction()
|
|||||||
|
|
||||||
class Flatpak:
|
class Flatpak:
|
||||||
|
|
||||||
def open_app(self, callback=None):
|
def open_app(self, callback=None):
|
||||||
self.failed_app_run = None
|
self.failed_app_run = None
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
if self.is_runtime:
|
if self.is_runtime:
|
||||||
self.failed_app_run = "error: cannot open a runtime"
|
self.failed_app_run = "error: cannot open a runtime"
|
||||||
try:
|
try:
|
||||||
subprocess.run(['flatpak-spawn', '--host', 'flatpak', 'run', f"{self.info['ref']}"], capture_output=True, text=True, check=True)
|
subprocess.run(['flatpak-spawn', '--host', 'flatpak', 'run', f"{self.info['ref']}"], capture_output=True, text=True, check=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.failed_app_run = cpe
|
self.failed_app_run = cpe
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.failed_app_run = e
|
self.failed_app_run = e
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
def open_data(self):
|
def open_data(self):
|
||||||
if not os.path.exists(self.data_path):
|
if not os.path.exists(self.data_path):
|
||||||
return f"Path '{self.data_path}' does not exist"
|
return f"Path '{self.data_path}' does not exist"
|
||||||
try:
|
try:
|
||||||
Gio.AppInfo.launch_default_for_uri(f"file://{self.data_path}", None)
|
Gio.AppInfo.launch_default_for_uri(f"file://{self.data_path}", None)
|
||||||
except GLib.GError as e:
|
except GLib.GError as e:
|
||||||
return e
|
return e
|
||||||
|
|
||||||
def get_data_size(self, callback=None):
|
def get_data_size(self, callback=None):
|
||||||
size = [None]
|
size = [None]
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
sed = "sed 's/K/ KB/; s/M/ MB/; s/G/ GB/; s/T/ TB/; s/P/ PB/;'"
|
sed = "sed 's/K/ KB/; s/M/ MB/; s/G/ GB/; s/T/ TB/; s/P/ PB/;'"
|
||||||
size[0] = subprocess.run(['sh', '-c', f"du -sh {self.data_path} | {sed}"], capture_output=True, text=True).stdout.split("\t")[0]
|
size[0] = subprocess.run(['sh', '-c', f"du -sh {self.data_path} | {sed}"], capture_output=True, text=True).stdout.split("\t")[0]
|
||||||
def on_done(*arg):
|
def on_done(*arg):
|
||||||
if callback:
|
if callback:
|
||||||
callback(f"~ {size[0]}")
|
callback(f"~ {size[0]}")
|
||||||
Gio.Task.new(None, None, on_done).run_in_thread(thread)
|
Gio.Task.new(None, None, on_done).run_in_thread(thread)
|
||||||
|
|
||||||
def trash_data(self, callback=None):
|
def trash_data(self, callback=None):
|
||||||
try:
|
try:
|
||||||
subprocess.run(['gio', 'trash', self.data_path], capture_output=True, text=True, check=True)
|
subprocess.run(['gio', 'trash', self.data_path], capture_output=True, text=True, check=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
raise cpe
|
raise cpe
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
def set_mask(self, should_mask, callback=None):
|
def set_mask(self, should_mask, callback=None):
|
||||||
self.failed_mask = None
|
self.failed_mask = None
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
cmd = ['flatpak-spawn', '--host', 'flatpak', 'mask', self.info["id"]]
|
cmd = ['flatpak-spawn', '--host', 'flatpak', 'mask', self.info["id"]]
|
||||||
installation = self.info["installation"]
|
installation = self.info["installation"]
|
||||||
if installation == "user" or installation == "system":
|
if installation == "user" or installation == "system":
|
||||||
cmd.append(f"--{installation}")
|
cmd.append(f"--{installation}")
|
||||||
else:
|
else:
|
||||||
cmd.append(f"--installation={installation}")
|
cmd.append(f"--installation={installation}")
|
||||||
|
|
||||||
if not should_mask:
|
if not should_mask:
|
||||||
cmd.append("--remove")
|
cmd.append("--remove")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||||
self.is_masked = should_mask
|
self.is_masked = should_mask
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.failed_mask = cpe
|
self.failed_mask = cpe
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.failed_mask = e
|
self.failed_mask = e
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
def set_pin(self, should_pin, callback=None):
|
def set_pin(self, should_pin, callback=None):
|
||||||
self.failed_pin = None
|
self.failed_pin = None
|
||||||
if not self.is_runtime:
|
if not self.is_runtime:
|
||||||
self.failed_pin = "Cannot pin an application"
|
self.failed_pin = "Cannot pin an application"
|
||||||
|
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
cmd = ['flatpak-spawn', '--host', 'flatpak', 'pin', f"runtime/{self.info['ref']}"]
|
cmd = ['flatpak-spawn', '--host', 'flatpak', 'pin', f"runtime/{self.info['ref']}"]
|
||||||
installation = self.info["installation"]
|
installation = self.info["installation"]
|
||||||
if installation == "user" or installation == "system":
|
if installation == "user" or installation == "system":
|
||||||
cmd.append(f"--{installation}")
|
cmd.append(f"--{installation}")
|
||||||
else:
|
else:
|
||||||
cmd.append(f"--installation={installation}")
|
cmd.append(f"--installation={installation}")
|
||||||
|
|
||||||
if not should_pin:
|
if not should_pin:
|
||||||
cmd.append("--remove")
|
cmd.append("--remove")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.failed_pin = cpe
|
self.failed_pin = cpe
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.failed_mask = e
|
self.failed_mask = e
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
def uninstall(self, callee_callback=None):
|
def uninstall(self, callee_callback=None):
|
||||||
self.failed_uninstall = None
|
self.failed_uninstall = None
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
HostInfo.main_window.remove_refresh_lockout("uninstalling packages")
|
HostInfo.main_window.remove_refresh_lockout("uninstalling packages")
|
||||||
if not callee_callback is None:
|
if not callee_callback is None:
|
||||||
callee_callback()
|
callee_callback()
|
||||||
|
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
HostInfo.main_window.add_refresh_lockout("uninstalling packages")
|
HostInfo.main_window.add_refresh_lockout("uninstalling packages")
|
||||||
cmd = ['flatpak-spawn', '--host', 'flatpak', 'uninstall', '-y', self.info["ref"]]
|
cmd = ['flatpak-spawn', '--host', 'flatpak', 'uninstall', '-y', self.info["ref"]]
|
||||||
installation = self.info["installation"]
|
installation = self.info["installation"]
|
||||||
if installation == "system" or installation == "user":
|
if installation == "system" or installation == "user":
|
||||||
cmd.append(f"--{installation}")
|
cmd.append(f"--{installation}")
|
||||||
else:
|
else:
|
||||||
cmd.append(f"--installation={installation}")
|
cmd.append(f"--installation={installation}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(cmd, check=True, text=True, capture_output=True)
|
subprocess.run(cmd, check=True, text=True, capture_output=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.failed_uninstall = cpe
|
self.failed_uninstall = cpe
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.failed_uninstall = e
|
self.failed_uninstall = e
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
def get_cli_info(self):
|
def get_cli_info(self):
|
||||||
cli_info = {}
|
cli_info = {}
|
||||||
cmd = "LC_ALL=C flatpak info "
|
cmd = "LC_ALL=C flatpak info "
|
||||||
installation = self.info["installation"]
|
installation = self.info["installation"]
|
||||||
|
|
||||||
if installation == "user":
|
if installation == "user":
|
||||||
cmd += "--user "
|
cmd += "--user "
|
||||||
elif installation == "system":
|
elif installation == "system":
|
||||||
cmd += "--system "
|
cmd += "--system "
|
||||||
else:
|
else:
|
||||||
cmd += f"--installation={installation} "
|
cmd += f"--installation={installation} "
|
||||||
|
|
||||||
cmd += self.info["ref"]
|
cmd += self.info["ref"]
|
||||||
try:
|
try:
|
||||||
output = subprocess.run(
|
output = subprocess.run(
|
||||||
['flatpak-spawn', '--host', 'sh', '-c', cmd],
|
['flatpak-spawn', '--host', 'sh', '-c', cmd],
|
||||||
text=True, capture_output=True
|
text=True, capture_output=True
|
||||||
).stdout
|
).stdout
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
lines = output.strip().split("\n")
|
lines = output.strip().split("\n")
|
||||||
cli_info["description"] = ""
|
cli_info["description"] = ""
|
||||||
first = lines.pop(0)
|
first = lines.pop(0)
|
||||||
if " - " in first:
|
if " - " in first:
|
||||||
cli_info["description"] = first.split(" - ")[1]
|
cli_info["description"] = first.split(" - ")[1]
|
||||||
|
|
||||||
# Handle descriptions that contain newlines
|
# Handle descriptions that contain newlines
|
||||||
while (line := lines.pop(0)) and not ":" in line:
|
while (line := lines.pop(0)) and not ":" in line:
|
||||||
if len(line) > 0:
|
if len(line) > 0:
|
||||||
cli_info["description"] += f" {line}"
|
cli_info["description"] += f" {line}"
|
||||||
|
|
||||||
for i, word in enumerate(lines):
|
for i, word in enumerate(lines):
|
||||||
if not ":" in word:
|
if not ":" in word:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
word = word.strip().split(": ", 1)
|
word = word.strip().split(": ", 1)
|
||||||
if len(word) < 2:
|
if len(word) < 2:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
word[0] = word[0].lower()
|
word[0] = word[0].lower()
|
||||||
if "installed" in word[0]:
|
if "installed" in word[0]:
|
||||||
word[1] = word[1].replace("?", " ")
|
word[1] = word[1].replace("?", " ")
|
||||||
cli_info[word[0]] = word[1]
|
cli_info[word[0]] = word[1]
|
||||||
|
|
||||||
self.cli_info = cli_info
|
self.cli_info = cli_info
|
||||||
return cli_info
|
return cli_info
|
||||||
|
|
||||||
def __init__(self, columns):
|
def __init__(self, columns):
|
||||||
self.info = {
|
self.info = {
|
||||||
"name": columns[0],
|
"name": columns[0],
|
||||||
"id": columns[1],
|
"id": columns[1],
|
||||||
"version": columns[2],
|
"version": columns[2],
|
||||||
"branch": columns[3],
|
"branch": columns[3],
|
||||||
"arch": columns[4],
|
"arch": columns[4],
|
||||||
"origin": columns[5],
|
"origin": columns[5],
|
||||||
"installation": columns[6],
|
"installation": columns[6],
|
||||||
"ref": columns[7],
|
"ref": columns[7],
|
||||||
"installed_size": columns[8],
|
"installed_size": columns[8],
|
||||||
"options": columns[9],
|
"options": columns[9],
|
||||||
}
|
}
|
||||||
self.is_runtime = "runtime" in self.info["options"]
|
self.is_runtime = "runtime" in self.info["options"]
|
||||||
self.data_path = f"{home}/.var/app/{self.info["id"]}"
|
self.data_path = f"{home}/.var/app/{self.info["id"]}"
|
||||||
self.data_size = -1
|
self.data_size = -1
|
||||||
self.cli_info = None
|
self.cli_info = None
|
||||||
installation = self.info["installation"]
|
installation = self.info["installation"]
|
||||||
if len(i := installation.split(' ')) > 1:
|
if len(i := installation.split(' ')) > 1:
|
||||||
self.info["installation"] = i[1].replace("(", "").replace(")", "")
|
self.info["installation"] = i[1].replace("(", "").replace(")", "")
|
||||||
else:
|
else:
|
||||||
self.info["installation"] = installation
|
self.info["installation"] = installation
|
||||||
|
|
||||||
self.is_eol = "eol=" in self.info["options"]
|
self.is_eol = "eol=" in self.info["options"]
|
||||||
self.dependent_runtime = None
|
self.dependent_runtime = None
|
||||||
self.failed_app_run = None
|
self.failed_app_run = None
|
||||||
self.failed_mask = None
|
self.failed_mask = None
|
||||||
self.failed_uninstall = None
|
self.failed_uninstall = None
|
||||||
self.app_row = None
|
self.app_row = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.is_masked = self.info["id"] in HostInfo.masks[self.info["installation"]]
|
self.is_masked = self.info["id"] in HostInfo.masks[self.info["installation"]]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.is_masked = False
|
self.is_masked = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.is_pinned = f"runtime/{self.info['ref']}" in HostInfo.pins[self.info["installation"]]
|
self.is_pinned = f"runtime/{self.info['ref']}" in HostInfo.pins[self.info["installation"]]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.is_pinned = False
|
self.is_pinned = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.icon_path = (
|
self.icon_path = (
|
||||||
icon_theme.lookup_icon(
|
icon_theme.lookup_icon(
|
||||||
self.info["id"], None, 512, 1, direction, 0
|
self.info["id"], None, 512, 1, direction, 0
|
||||||
)
|
)
|
||||||
.get_file()
|
.get_file()
|
||||||
.get_path()
|
.get_path()
|
||||||
)
|
)
|
||||||
except GLib.GError as e:
|
except GLib.GError as e:
|
||||||
print(f"Minor error in looking up icon for {self.info['id']}", e)
|
print(f"Minor error in looking up icon for {self.info['id']}", e)
|
||||||
self.icon_path = None
|
self.icon_path = None
|
||||||
|
|
||||||
|
|
||||||
class Remote:
|
class Remote:
|
||||||
def __init__(self, name, title, disabled):
|
def __init__(self, name, title, disabled):
|
||||||
self.name = name
|
self.name = name
|
||||||
self.title = title
|
self.title = title
|
||||||
self.disabled = disabled
|
self.disabled = disabled
|
||||||
if title == "" or title == "-":
|
if title == "" or title == "-":
|
||||||
self.title = name
|
self.title = name
|
||||||
|
|
||||||
class HostInfo:
|
class HostInfo:
|
||||||
home = home
|
home = home
|
||||||
clipboard = Gdk.Display.get_default().get_clipboard()
|
clipboard = Gdk.Display.get_default().get_clipboard()
|
||||||
main_window = None
|
main_window = None
|
||||||
snapshots_path = f"{home}/.var/app/io.github.flattool.Warehouse/data/Snapshots/"
|
snapshots_path = f"{home}/.var/app/io.github.flattool.Warehouse/data/Snapshots/"
|
||||||
|
|
||||||
# Get all possible installation icon theme dirs
|
# Get all possible installation icon theme dirs
|
||||||
output = subprocess.run(
|
output = subprocess.run(
|
||||||
['flatpak-spawn', '--host',
|
['flatpak-spawn', '--host',
|
||||||
'flatpak', '--installations'],
|
'flatpak', '--installations'],
|
||||||
text=True,
|
text=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
).stdout
|
).stdout
|
||||||
lines = output.strip().split("\n")
|
lines = output.strip().split("\n")
|
||||||
for i in lines:
|
for i in lines:
|
||||||
icon_theme.add_search_path(f"{i}/exports/share/icons")
|
icon_theme.add_search_path(f"{i}/exports/share/icons")
|
||||||
|
|
||||||
flatpaks = []
|
flatpaks = []
|
||||||
id_to_flatpak = {}
|
id_to_flatpak = {}
|
||||||
ref_to_flatpak = {}
|
ref_to_flatpak = {}
|
||||||
remotes = {}
|
remotes = {}
|
||||||
installations = []
|
installations = []
|
||||||
masks = {}
|
masks = {}
|
||||||
pins = {}
|
pins = {}
|
||||||
dependent_runtime_refs = []
|
dependent_runtime_refs = []
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_flatpaks(this, callback=None):
|
def get_flatpaks(this, callback=None):
|
||||||
# Callback is a function to run after the host flatpaks are found
|
# Callback is a function to run after the host flatpaks are found
|
||||||
this.flatpaks.clear()
|
this.flatpaks.clear()
|
||||||
this.id_to_flatpak.clear()
|
this.id_to_flatpak.clear()
|
||||||
this.ref_to_flatpak.clear()
|
this.ref_to_flatpak.clear()
|
||||||
this.remotes.clear()
|
this.remotes.clear()
|
||||||
this.installations.clear()
|
this.installations.clear()
|
||||||
this.masks.clear()
|
this.masks.clear()
|
||||||
this.pins.clear()
|
this.pins.clear()
|
||||||
this.dependent_runtime_refs.clear()
|
this.dependent_runtime_refs.clear()
|
||||||
|
|
||||||
def thread(task, *args):
|
def thread(task, *args):
|
||||||
|
|
||||||
# Remotes
|
# Remotes
|
||||||
def remote_info(installation):
|
def remote_info(installation):
|
||||||
cmd = ['flatpak-spawn', '--host',
|
cmd = ['flatpak-spawn', '--host',
|
||||||
'flatpak', 'remotes', '--columns=name,title,options', '--show-disabled']
|
'flatpak', 'remotes', '--columns=name,title,options', '--show-disabled']
|
||||||
if installation == "user" or installation == "system":
|
if installation == "user" or installation == "system":
|
||||||
cmd.append(f"--{installation}")
|
cmd.append(f"--{installation}")
|
||||||
else:
|
else:
|
||||||
cmd.append(f"--installation={installation}")
|
cmd.append(f"--installation={installation}")
|
||||||
output = subprocess.run(
|
output = subprocess.run(
|
||||||
cmd, text=True,
|
cmd, text=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
).stdout
|
).stdout
|
||||||
lines = output.strip().split("\n")
|
lines = output.strip().split("\n")
|
||||||
remote_list = []
|
remote_list = []
|
||||||
if lines[0] != '':
|
if lines[0] != '':
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line = line.split("\t")
|
line = line.split("\t")
|
||||||
remote_list.append(Remote(name=line[0], title=line[1], disabled=(len(line) == 3) and "disabled" in line[2]))
|
remote_list.append(Remote(name=line[0], title=line[1], disabled=(len(line) == 3) and "disabled" in line[2]))
|
||||||
this.remotes[installation] = remote_list
|
this.remotes[installation] = remote_list
|
||||||
|
|
||||||
# Masks
|
# Masks
|
||||||
cmd = ['flatpak-spawn', '--host',
|
cmd = ['flatpak-spawn', '--host',
|
||||||
'flatpak', 'mask',]
|
'flatpak', 'mask',]
|
||||||
if installation == "user" or installation == "system":
|
if installation == "user" or installation == "system":
|
||||||
cmd.append(f"--{installation}")
|
cmd.append(f"--{installation}")
|
||||||
else:
|
else:
|
||||||
cmd.append(f"--installation={installation}")
|
cmd.append(f"--installation={installation}")
|
||||||
output = subprocess.run(
|
output = subprocess.run(
|
||||||
cmd, text=True,
|
cmd, text=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
).stdout
|
).stdout
|
||||||
lines = output.strip().replace(" ", "").split("\n")
|
lines = output.strip().replace(" ", "").split("\n")
|
||||||
if lines[0] != '':
|
if lines[0] != '':
|
||||||
this.masks[installation] = lines
|
this.masks[installation] = lines
|
||||||
|
|
||||||
# Pins
|
# Pins
|
||||||
cmd = ['flatpak-spawn', '--host',
|
cmd = ['flatpak-spawn', '--host',
|
||||||
'flatpak', 'pin',]
|
'flatpak', 'pin',]
|
||||||
if installation == "user" or installation == "system":
|
if installation == "user" or installation == "system":
|
||||||
cmd.append(f"--{installation}")
|
cmd.append(f"--{installation}")
|
||||||
else:
|
else:
|
||||||
cmd.append(f"--installation={installation}")
|
cmd.append(f"--installation={installation}")
|
||||||
output = subprocess.run(
|
output = subprocess.run(
|
||||||
cmd, text=True,
|
cmd, text=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
).stdout
|
).stdout
|
||||||
lines = output.strip().replace(" ", "").split("\n")
|
lines = output.strip().replace(" ", "").split("\n")
|
||||||
if lines[0] != '':
|
if lines[0] != '':
|
||||||
this.pins[installation] = lines
|
this.pins[installation] = lines
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# Installations
|
# Installations
|
||||||
# Get all config files for any extra installations
|
# Get all config files for any extra installations
|
||||||
custom_install_config_path = "/run/host/etc/flatpak/installations.d"
|
custom_install_config_path = "/run/host/etc/flatpak/installations.d"
|
||||||
if os.path.exists(custom_install_config_path):
|
if os.path.exists(custom_install_config_path):
|
||||||
for file in os.listdir(custom_install_config_path):
|
for file in os.listdir(custom_install_config_path):
|
||||||
with open(f"{custom_install_config_path}/{file}", "r") as f:
|
with open(f"{custom_install_config_path}/{file}", "r") as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
if line.startswith("[Installation"):
|
if line.startswith("[Installation"):
|
||||||
# Get specifically the installation name itself
|
# Get specifically the installation name itself
|
||||||
this.installations.append(line.replace("[Installation \"", "").replace("\"]", "").strip())
|
this.installations.append(line.replace("[Installation \"", "").replace("\"]", "").strip())
|
||||||
|
|
||||||
this.installations.append("user")
|
this.installations.append("user")
|
||||||
this.installations.append("system")
|
this.installations.append("system")
|
||||||
for i in this.installations:
|
for i in this.installations:
|
||||||
remote_info(i)
|
remote_info(i)
|
||||||
remote_info("user")
|
remote_info("user")
|
||||||
remote_info("system")
|
remote_info("system")
|
||||||
|
|
||||||
# Packages
|
# Packages
|
||||||
output = subprocess.run(
|
output = subprocess.run(
|
||||||
['flatpak-spawn', '--host', 'flatpak', 'list',
|
['flatpak-spawn', '--host', 'flatpak', 'list',
|
||||||
'--columns=name,application,version,branch,arch,origin,installation,ref,size,options'],
|
'--columns=name,application,version,branch,arch,origin,installation,ref,size,options'],
|
||||||
text=True, check=True,
|
text=True, check=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
).stdout
|
).stdout
|
||||||
lines = output.strip().split("\n")
|
lines = output.strip().split("\n")
|
||||||
for i in lines:
|
for i in lines:
|
||||||
package = Flatpak(i.split("\t"))
|
package = Flatpak(i.split("\t"))
|
||||||
this.flatpaks.append(package)
|
this.flatpaks.append(package)
|
||||||
this.id_to_flatpak[package.info["id"]] = package
|
this.id_to_flatpak[package.info["id"]] = package
|
||||||
this.ref_to_flatpak[package.info["ref"]] = package
|
this.ref_to_flatpak[package.info["ref"]] = package
|
||||||
|
|
||||||
# Dependent Runtimes
|
# Dependent Runtimes
|
||||||
output = subprocess.run(
|
output = subprocess.run(
|
||||||
['flatpak-spawn', '--host',
|
['flatpak-spawn', '--host',
|
||||||
'flatpak', 'list', '--columns=runtime,ref'],
|
'flatpak', 'list', '--columns=runtime,ref'],
|
||||||
text=True, check=True,
|
text=True, check=True,
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
).stdout
|
).stdout
|
||||||
lines = output.split("\n")
|
lines = output.split("\n")
|
||||||
for index, line in enumerate(lines):
|
for index, line in enumerate(lines):
|
||||||
split_line = line.split("\t")
|
split_line = line.split("\t")
|
||||||
if len(split_line) < 2 or split_line[0] == '':
|
if len(split_line) < 2 or split_line[0] == '':
|
||||||
continue
|
continue
|
||||||
|
|
||||||
package = this.flatpaks[index]
|
package = this.flatpaks[index]
|
||||||
if package.is_runtime:
|
if package.is_runtime:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
runtime = split_line[0]
|
runtime = split_line[0]
|
||||||
package.dependent_runtime = this.ref_to_flatpak[runtime]
|
package.dependent_runtime = this.ref_to_flatpak[runtime]
|
||||||
if not runtime in this.dependent_runtime_refs:
|
if not runtime in this.dependent_runtime_refs:
|
||||||
this.dependent_runtime_refs.append(runtime)
|
this.dependent_runtime_refs.append(runtime)
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
this.main_window.toast_overlay.add_toast(ErrorToast(_("Could not load packages"), cpe.stderr).toast)
|
this.main_window.toast_overlay.add_toast(ErrorToast(_("Could not load packages"), cpe.stderr).toast)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
this.main_window.toast_overlay.add_toast(ErrorToast(_("Could not load packages"), str(e)).toast)
|
this.main_window.toast_overlay.add_toast(ErrorToast(_("Could not load packages"), str(e)).toast)
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|||||||
@@ -2,117 +2,117 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $FiltersPage : Adw.NavigationPage {
|
template $FiltersPage : Adw.NavigationPage {
|
||||||
title: _("Filter Packages");
|
title: _("Filter Packages");
|
||||||
Adw.ToolbarView {
|
Adw.ToolbarView {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {}
|
Adw.HeaderBar {}
|
||||||
ScrolledWindow {
|
ScrolledWindow {
|
||||||
Adw.Clamp {
|
Adw.Clamp {
|
||||||
Box {
|
Box {
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
margin-top: 12;
|
margin-top: 12;
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
spacing: 24;
|
spacing: 24;
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
halign: fill;
|
halign: fill;
|
||||||
Adw.PreferencesGroup {
|
Adw.PreferencesGroup {
|
||||||
title: _("Filter by Package Type");
|
title: _("Filter by Package Type");
|
||||||
description: _("Show packages of these types");
|
description: _("Show packages of these types");
|
||||||
Adw.ActionRow application_row {
|
Adw.ActionRow application_row {
|
||||||
title: _("Applications");
|
title: _("Applications");
|
||||||
subtitle: _("Packages that can be opened");
|
subtitle: _("Packages that can be opened");
|
||||||
CheckButton app_check {}
|
CheckButton app_check {}
|
||||||
activatable-widget: app_check;
|
activatable-widget: app_check;
|
||||||
}
|
}
|
||||||
Adw.ActionRow runtime_row {
|
Adw.ActionRow runtime_row {
|
||||||
title: _("Runtimes");
|
title: _("Runtimes");
|
||||||
subtitle: _("Packages that applications depend on");
|
subtitle: _("Packages that applications depend on");
|
||||||
CheckButton runtime_check {}
|
CheckButton runtime_check {}
|
||||||
activatable-widget: runtime_check;
|
activatable-widget: runtime_check;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Adw.PreferencesGroup remotes_group {
|
Adw.PreferencesGroup remotes_group {
|
||||||
title: _("Filter by Remotes");
|
title: _("Filter by Remotes");
|
||||||
description: _("Show packages from selected remotes");
|
description: _("Show packages from selected remotes");
|
||||||
header-suffix:
|
header-suffix:
|
||||||
Switch all_remotes_switch {
|
Switch all_remotes_switch {
|
||||||
valign: center;
|
valign: center;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
Adw.ActionRow {
|
Adw.ActionRow {
|
||||||
visible: bind all_remotes_switch.active inverted;
|
visible: bind all_remotes_switch.active inverted;
|
||||||
[child]
|
[child]
|
||||||
Box {
|
Box {
|
||||||
spacing: 3;
|
spacing: 3;
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
Label {
|
Label {
|
||||||
margin-top: 7;
|
margin-top: 7;
|
||||||
label: _("Showing packages from all remotes");
|
label: _("Showing packages from all remotes");
|
||||||
wrap: true;
|
wrap: true;
|
||||||
halign: center;
|
halign: center;
|
||||||
styles ["heading"]
|
styles ["heading"]
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
label: _("Enable to show packages from selected remotes");
|
label: _("Enable to show packages from selected remotes");
|
||||||
margin-start: 16;
|
margin-start: 16;
|
||||||
margin-end: 16;
|
margin-end: 16;
|
||||||
margin-bottom: 8;
|
margin-bottom: 8;
|
||||||
justify: center;
|
justify: center;
|
||||||
halign: center;
|
halign: center;
|
||||||
wrap: true;
|
wrap: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Adw.PreferencesGroup runtimes_group {
|
Adw.PreferencesGroup runtimes_group {
|
||||||
title: _("Filter by Runtimes");
|
title: _("Filter by Runtimes");
|
||||||
description: _("Show apps using selected runtimes");
|
description: _("Show apps using selected runtimes");
|
||||||
header-suffix:
|
header-suffix:
|
||||||
Switch all_runtimes_switch {
|
Switch all_runtimes_switch {
|
||||||
valign: center;
|
valign: center;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
Adw.ActionRow {
|
Adw.ActionRow {
|
||||||
visible: bind all_runtimes_switch.active inverted;
|
visible: bind all_runtimes_switch.active inverted;
|
||||||
[child]
|
[child]
|
||||||
Box {
|
Box {
|
||||||
spacing: 3;
|
spacing: 3;
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
Label {
|
Label {
|
||||||
margin-top: 7;
|
margin-top: 7;
|
||||||
label: _("Showing apps using any runtime");
|
label: _("Showing apps using any runtime");
|
||||||
wrap: true;
|
wrap: true;
|
||||||
halign: center;
|
halign: center;
|
||||||
styles ["heading"]
|
styles ["heading"]
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
label: _("Enable to show apps using selected runtimes");
|
label: _("Enable to show apps using selected runtimes");
|
||||||
margin-start: 16;
|
margin-start: 16;
|
||||||
margin-end: 16;
|
margin-end: 16;
|
||||||
margin-bottom: 8;
|
margin-bottom: 8;
|
||||||
justify: center;
|
justify: center;
|
||||||
halign: center;
|
halign: center;
|
||||||
wrap: true;
|
wrap: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[bottom]
|
[bottom]
|
||||||
ActionBar action_bar {
|
ActionBar action_bar {
|
||||||
[center]
|
[center]
|
||||||
Button reset_button {
|
Button reset_button {
|
||||||
sensitive: bind action_bar.revealed;
|
sensitive: bind action_bar.revealed;
|
||||||
margin-top: 3;
|
margin-top: 3;
|
||||||
margin-bottom: 3;
|
margin-bottom: 3;
|
||||||
label: _("Reset Filters");
|
label: _("Reset Filters");
|
||||||
styles ["pill"]
|
styles ["pill"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,195 +2,195 @@ from gi.repository import Adw, Gtk, Gio
|
|||||||
from .host_info import HostInfo
|
from .host_info import HostInfo
|
||||||
|
|
||||||
class FilterRow(Adw.ActionRow):
|
class FilterRow(Adw.ActionRow):
|
||||||
__gtype_name__ = 'FilterRow'
|
__gtype_name__ = 'FilterRow'
|
||||||
def __init__(self, item=None, installation=None, **kwargs):
|
def __init__(self, item=None, installation=None, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.item = item
|
self.item = item
|
||||||
self.installation = installation
|
self.installation = installation
|
||||||
self.check_button = Gtk.CheckButton()
|
self.check_button = Gtk.CheckButton()
|
||||||
self.add_suffix(self.check_button)
|
self.add_suffix(self.check_button)
|
||||||
self.set_activatable_widget(self.check_button)
|
self.set_activatable_widget(self.check_button)
|
||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/packages_page/filters_page.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/packages_page/filters_page.ui")
|
||||||
class FiltersPage(Adw.NavigationPage):
|
class FiltersPage(Adw.NavigationPage):
|
||||||
__gtype_name__ = 'FiltersPage'
|
__gtype_name__ = 'FiltersPage'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
app_check = gtc()
|
app_check = gtc()
|
||||||
runtime_check = gtc()
|
runtime_check = gtc()
|
||||||
remotes_group = gtc()
|
remotes_group = gtc()
|
||||||
all_remotes_switch = gtc()
|
all_remotes_switch = gtc()
|
||||||
runtimes_group = gtc()
|
runtimes_group = gtc()
|
||||||
all_runtimes_switch = gtc()
|
all_runtimes_switch = gtc()
|
||||||
action_bar = gtc()
|
action_bar = gtc()
|
||||||
reset_button = gtc()
|
reset_button = gtc()
|
||||||
|
|
||||||
remote_rows = []
|
remote_rows = []
|
||||||
runtime_rows = []
|
runtime_rows = []
|
||||||
|
|
||||||
def reset_filters(self):
|
def reset_filters(self):
|
||||||
self.settings.reset("show-apps")
|
self.settings.reset("show-apps")
|
||||||
self.settings.reset("show-runtimes")
|
self.settings.reset("show-runtimes")
|
||||||
self.settings.reset("remotes-list")
|
self.settings.reset("remotes-list")
|
||||||
self.settings.reset("runtimes-list")
|
self.settings.reset("runtimes-list")
|
||||||
self.generate_filters()
|
self.generate_filters()
|
||||||
self.packages_page.apply_filters()
|
self.packages_page.apply_filters()
|
||||||
|
|
||||||
def is_defaulted(self):
|
def is_defaulted(self):
|
||||||
default = True
|
default = True
|
||||||
if not self.app_check.get_active():
|
if not self.app_check.get_active():
|
||||||
default = False
|
default = False
|
||||||
if self.runtime_check.get_active():
|
if self.runtime_check.get_active():
|
||||||
default = False
|
default = False
|
||||||
if self.all_remotes_switch.get_active():
|
if self.all_remotes_switch.get_active():
|
||||||
default = False
|
default = False
|
||||||
if self.all_runtimes_switch.get_active():
|
if self.all_runtimes_switch.get_active():
|
||||||
default = False
|
default = False
|
||||||
self.action_bar.set_revealed(not default)
|
self.action_bar.set_revealed(not default)
|
||||||
|
|
||||||
def update_gsettings(self):
|
def update_gsettings(self):
|
||||||
self.is_defaulted()
|
self.is_defaulted()
|
||||||
if not self.is_settings_settable:
|
if not self.is_settings_settable:
|
||||||
return
|
return
|
||||||
self.settings.set_boolean("show-apps", self.show_apps)
|
self.settings.set_boolean("show-apps", self.show_apps)
|
||||||
self.settings.set_boolean("show-runtimes", self.show_runtimes)
|
self.settings.set_boolean("show-runtimes", self.show_runtimes)
|
||||||
self.settings.set_string("remotes-list", self.remotes_string)
|
self.settings.set_string("remotes-list", self.remotes_string)
|
||||||
self.settings.set_string("runtimes-list", self.runtimes_string)
|
self.settings.set_string("runtimes-list", self.runtimes_string)
|
||||||
self.packages_page.apply_filters()
|
self.packages_page.apply_filters()
|
||||||
|
|
||||||
def app_check_handler(self, *args):
|
def app_check_handler(self, *args):
|
||||||
self.show_apps = self.app_check.get_active()
|
self.show_apps = self.app_check.get_active()
|
||||||
self.update_gsettings()
|
self.update_gsettings()
|
||||||
|
|
||||||
def runtime_check_handler(self, *args):
|
def runtime_check_handler(self, *args):
|
||||||
self.show_runtimes = self.runtime_check.get_active()
|
self.show_runtimes = self.runtime_check.get_active()
|
||||||
self.update_gsettings()
|
self.update_gsettings()
|
||||||
|
|
||||||
def all_remotes_handler(self, switch, state):
|
def all_remotes_handler(self, switch, state):
|
||||||
self.remotes_string = ""
|
self.remotes_string = ""
|
||||||
if not state:
|
if not state:
|
||||||
self.remotes_string = "all"
|
self.remotes_string = "all"
|
||||||
|
|
||||||
for row in self.remote_rows:
|
for row in self.remote_rows:
|
||||||
row.set_visible(state)
|
row.set_visible(state)
|
||||||
if state and row.check_button.get_active():
|
if state and row.check_button.get_active():
|
||||||
self.remotes_string += f"{row.item.name}<>{row.installation};"
|
self.remotes_string += f"{row.item.name}<>{row.installation};"
|
||||||
elif state:
|
elif state:
|
||||||
self.remotes_string.replace(f"{row.item.name}<>{row.installation};", "")
|
self.remotes_string.replace(f"{row.item.name}<>{row.installation};", "")
|
||||||
|
|
||||||
self.update_gsettings()
|
self.update_gsettings()
|
||||||
|
|
||||||
def all_runtimes_handler(self, switch, state):
|
def all_runtimes_handler(self, switch, state):
|
||||||
self.runtimes_string = ""
|
self.runtimes_string = ""
|
||||||
if not state:
|
if not state:
|
||||||
self.runtimes_string = "all"
|
self.runtimes_string = "all"
|
||||||
|
|
||||||
for row in self.runtime_rows:
|
for row in self.runtime_rows:
|
||||||
row.set_visible(state)
|
row.set_visible(state)
|
||||||
if state and row.check_button.get_active():
|
if state and row.check_button.get_active():
|
||||||
self.runtimes_string += f"{row.item};"
|
self.runtimes_string += f"{row.item};"
|
||||||
elif state:
|
elif state:
|
||||||
self.runtimes_string.replace(f"{row.item};", "")
|
self.runtimes_string.replace(f"{row.item};", "")
|
||||||
|
|
||||||
self.update_gsettings()
|
self.update_gsettings()
|
||||||
|
|
||||||
def remote_row_check_handler(self, row):
|
def remote_row_check_handler(self, row):
|
||||||
if row.check_button.get_active():
|
if row.check_button.get_active():
|
||||||
self.remotes_string += f"{row.item.name}<>{row.installation};"
|
self.remotes_string += f"{row.item.name}<>{row.installation};"
|
||||||
else:
|
else:
|
||||||
self.remotes_string = self.remotes_string.replace(f"{row.item.name}<>{row.installation};", "")
|
self.remotes_string = self.remotes_string.replace(f"{row.item.name}<>{row.installation};", "")
|
||||||
self.update_gsettings()
|
self.update_gsettings()
|
||||||
|
|
||||||
def runtime_row_check_handler(self, row):
|
def runtime_row_check_handler(self, row):
|
||||||
if row.check_button.get_active():
|
if row.check_button.get_active():
|
||||||
self.runtimes_string += f"{row.item};"
|
self.runtimes_string += f"{row.item};"
|
||||||
else:
|
else:
|
||||||
self.runtimes_string = self.runtimes_string.replace(f"{row.item};", "")
|
self.runtimes_string = self.runtimes_string.replace(f"{row.item};", "")
|
||||||
self.update_gsettings()
|
self.update_gsettings()
|
||||||
|
|
||||||
def generate_remote_filters(self):
|
def generate_remote_filters(self):
|
||||||
for row in self.remote_rows:
|
for row in self.remote_rows:
|
||||||
self.remotes_group.remove(row)
|
self.remotes_group.remove(row)
|
||||||
|
|
||||||
self.remote_rows.clear()
|
self.remote_rows.clear()
|
||||||
for installation, remotes in HostInfo.remotes.items():
|
for installation, remotes in HostInfo.remotes.items():
|
||||||
for remote in remotes:
|
for remote in remotes:
|
||||||
if remote.disabled:
|
if remote.disabled:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
row = FilterRow(remote, installation)
|
row = FilterRow(remote, installation)
|
||||||
row.set_title(remote.title)
|
row.set_title(remote.title)
|
||||||
row.set_subtitle(_("Installation: {}").format(installation))
|
row.set_subtitle(_("Installation: {}").format(installation))
|
||||||
row.check_button.set_active(f"{remote.name}<>{installation}" in self.remotes_string)
|
row.check_button.set_active(f"{remote.name}<>{installation}" in self.remotes_string)
|
||||||
row.check_button.connect("toggled", lambda *_, row=row: self.remote_row_check_handler(row))
|
row.check_button.connect("toggled", lambda *_, row=row: self.remote_row_check_handler(row))
|
||||||
row.set_visible(self.all_remotes_switch.get_active())
|
row.set_visible(self.all_remotes_switch.get_active())
|
||||||
self.remote_rows.append(row)
|
self.remote_rows.append(row)
|
||||||
self.remotes_group.add(row)
|
self.remotes_group.add(row)
|
||||||
|
|
||||||
self.remotes_group.set_visible(len(self.remote_rows) > 1)
|
self.remotes_group.set_visible(len(self.remote_rows) > 1)
|
||||||
self.all_remotes_switch.set_active("all" != self.remotes_string)
|
self.all_remotes_switch.set_active("all" != self.remotes_string)
|
||||||
|
|
||||||
def generate_runtime_filters(self):
|
def generate_runtime_filters(self):
|
||||||
for row in self.runtime_rows:
|
for row in self.runtime_rows:
|
||||||
self.runtimes_group.remove(row)
|
self.runtimes_group.remove(row)
|
||||||
self.runtime_rows.clear()
|
self.runtime_rows.clear()
|
||||||
if len(HostInfo.dependent_runtime_refs) < 2:
|
if len(HostInfo.dependent_runtime_refs) < 2:
|
||||||
self.runtimes_group.set_visible(False)
|
self.runtimes_group.set_visible(False)
|
||||||
if self.runtimes_string != "all":
|
if self.runtimes_string != "all":
|
||||||
self.runtimes_string = "all"
|
self.runtimes_string = "all"
|
||||||
self.settings.set_string("runtimes-list", self.runtimes_string)
|
self.settings.set_string("runtimes-list", self.runtimes_string)
|
||||||
self.packages_page.apply_filters()
|
self.packages_page.apply_filters()
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
for j, ref in enumerate(HostInfo.dependent_runtime_refs):
|
for j, ref in enumerate(HostInfo.dependent_runtime_refs):
|
||||||
row = FilterRow(ref)
|
row = FilterRow(ref)
|
||||||
row.set_title(ref)
|
row.set_title(ref)
|
||||||
row.check_button.set_active(ref in self.runtimes_string)
|
row.check_button.set_active(ref in self.runtimes_string)
|
||||||
row.check_button.connect("toggled", lambda *_, row=row: self.runtime_row_check_handler(row))
|
row.check_button.connect("toggled", lambda *_, row=row: self.runtime_row_check_handler(row))
|
||||||
row.set_visible(self.all_runtimes_switch.get_active())
|
row.set_visible(self.all_runtimes_switch.get_active())
|
||||||
self.runtime_rows.append(row)
|
self.runtime_rows.append(row)
|
||||||
self.runtimes_group.add(row)
|
self.runtimes_group.add(row)
|
||||||
|
|
||||||
self.runtimes_group.set_visible(len(self.runtime_rows) > 1)
|
self.runtimes_group.set_visible(len(self.runtime_rows) > 1)
|
||||||
self.all_runtimes_switch.set_active("all" != self.runtimes_string)
|
self.all_runtimes_switch.set_active("all" != self.runtimes_string)
|
||||||
|
|
||||||
def generate_filters(self):
|
def generate_filters(self):
|
||||||
self.is_settings_settable = False
|
self.is_settings_settable = False
|
||||||
self.show_apps = self.settings.get_boolean("show-apps")
|
self.show_apps = self.settings.get_boolean("show-apps")
|
||||||
self.show_runtimes = self.settings.get_boolean("show-runtimes")
|
self.show_runtimes = self.settings.get_boolean("show-runtimes")
|
||||||
self.remotes_string = self.settings.get_string("remotes-list")
|
self.remotes_string = self.settings.get_string("remotes-list")
|
||||||
self.runtimes_string = self.settings.get_string("runtimes-list")
|
self.runtimes_string = self.settings.get_string("runtimes-list")
|
||||||
|
|
||||||
self.app_check.set_active(self.show_apps)
|
self.app_check.set_active(self.show_apps)
|
||||||
self.runtime_check.set_active(self.show_runtimes)
|
self.runtime_check.set_active(self.show_runtimes)
|
||||||
|
|
||||||
self.generate_remote_filters()
|
self.generate_remote_filters()
|
||||||
self.generate_runtime_filters()
|
self.generate_runtime_filters()
|
||||||
|
|
||||||
self.is_settings_settable = True
|
self.is_settings_settable = True
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Objects Creation
|
# Extra Objects Creation
|
||||||
self.packages_page = None # To be set in packages page
|
self.packages_page = None # To be set in packages page
|
||||||
self.main_window = HostInfo.main_window
|
self.main_window = HostInfo.main_window
|
||||||
self.settings = Gio.Settings.new("io.github.flattool.Warehouse.filter")
|
self.settings = Gio.Settings.new("io.github.flattool.Warehouse.filter")
|
||||||
self.is_settings_settable = False
|
self.is_settings_settable = False
|
||||||
self.show_apps = self.settings.get_boolean("show-apps")
|
self.show_apps = self.settings.get_boolean("show-apps")
|
||||||
self.show_runtimes = self.settings.get_boolean("show-runtimes")
|
self.show_runtimes = self.settings.get_boolean("show-runtimes")
|
||||||
self.remotes_string = self.settings.get_string("remotes-list")
|
self.remotes_string = self.settings.get_string("remotes-list")
|
||||||
self.runtimes_string = self.settings.get_string("runtimes-list")
|
self.runtimes_string = self.settings.get_string("runtimes-list")
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
if "," in self.runtimes_string:
|
if "," in self.runtimes_string:
|
||||||
# Convert Warehouse 1.X runtimes filter string from , to ; for item seperationg
|
# Convert Warehouse 1.X runtimes filter string from , to ; for item seperationg
|
||||||
self.runtimes_string = self.runtimes_string.replace(",", ";")
|
self.runtimes_string = self.runtimes_string.replace(",", ";")
|
||||||
self.settings.set_string("runtimes-list", self.runtimes_string)
|
self.settings.set_string("runtimes-list", self.runtimes_string)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.app_check.connect("toggled", self.app_check_handler)
|
self.app_check.connect("toggled", self.app_check_handler)
|
||||||
self.runtime_check.connect("toggled", self.runtime_check_handler)
|
self.runtime_check.connect("toggled", self.runtime_check_handler)
|
||||||
self.all_remotes_switch.connect("state-set", self.all_remotes_handler)
|
self.all_remotes_switch.connect("state-set", self.all_remotes_handler)
|
||||||
self.all_runtimes_switch.connect("state-set", self.all_runtimes_handler)
|
self.all_runtimes_switch.connect("state-set", self.all_runtimes_handler)
|
||||||
self.reset_button.connect("clicked", lambda *_: self.reset_filters())
|
self.reset_button.connect("clicked", lambda *_: self.reset_filters())
|
||||||
|
|||||||
@@ -2,184 +2,184 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $PackagesPage : Adw.BreakpointBin {
|
template $PackagesPage : Adw.BreakpointBin {
|
||||||
width-request: 1;
|
width-request: 1;
|
||||||
height-request: 1;
|
height-request: 1;
|
||||||
|
|
||||||
Adw.Breakpoint packages_bpt {
|
Adw.Breakpoint packages_bpt {
|
||||||
condition ("max-width: 600")
|
condition ("max-width: 600")
|
||||||
|
|
||||||
setters {
|
setters {
|
||||||
packages_split.collapsed: true;
|
packages_split.collapsed: true;
|
||||||
packages_split.show-content: false;
|
packages_split.show-content: false;
|
||||||
content_stack.transition-duration: 9999999;
|
content_stack.transition-duration: 9999999;
|
||||||
reset_filters_button.visible: true;
|
reset_filters_button.visible: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Adw.NavigationPage {
|
Adw.NavigationPage {
|
||||||
title: _("Packages");
|
title: _("Packages");
|
||||||
Stack stack {
|
Stack stack {
|
||||||
Adw.ToolbarView loading_view {
|
Adw.ToolbarView loading_view {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ToolbarView uninstalling_view {
|
Adw.ToolbarView uninstalling_view {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ToolbarView reinstalling_view {
|
Adw.ToolbarView reinstalling_view {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ToolbarView changing_version_view {
|
Adw.ToolbarView changing_version_view {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.NavigationSplitView packages_split {
|
Adw.NavigationSplitView packages_split {
|
||||||
sidebar-width-fraction: 0.5;
|
sidebar-width-fraction: 0.5;
|
||||||
max-sidebar-width: 999999999;
|
max-sidebar-width: 999999999;
|
||||||
sidebar:
|
sidebar:
|
||||||
Adw.NavigationPage packages_navpage {
|
Adw.NavigationPage packages_navpage {
|
||||||
title: _("Packages");
|
title: _("Packages");
|
||||||
Adw.ToastOverlay packages_toast_overlay {
|
Adw.ToastOverlay packages_toast_overlay {
|
||||||
Adw.ToolbarView packages_tbv {
|
Adw.ToolbarView packages_tbv {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
[start]
|
[start]
|
||||||
ToggleButton search_button {
|
ToggleButton search_button {
|
||||||
icon-name: "loupe-large-symbolic";
|
icon-name: "loupe-large-symbolic";
|
||||||
tooltip-text: _("Search Packages");
|
tooltip-text: _("Search Packages");
|
||||||
}
|
}
|
||||||
[end]
|
[end]
|
||||||
ToggleButton filter_button {
|
ToggleButton filter_button {
|
||||||
icon-name: "funnel-symbolic";
|
icon-name: "funnel-symbolic";
|
||||||
tooltip-text: _("Filter Packages");
|
tooltip-text: _("Filter Packages");
|
||||||
}
|
}
|
||||||
[end]
|
[end]
|
||||||
ToggleButton select_button {
|
ToggleButton select_button {
|
||||||
icon-name: "selection-mode-symbolic";
|
icon-name: "selection-mode-symbolic";
|
||||||
tooltip-text: _("Select Packages");
|
tooltip-text: _("Select Packages");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[top]
|
[top]
|
||||||
SearchBar search_bar {
|
SearchBar search_bar {
|
||||||
search-mode-enabled: bind search_button.active bidirectional;
|
search-mode-enabled: bind search_button.active bidirectional;
|
||||||
SearchEntry search_entry {
|
SearchEntry search_entry {
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
placeholder-text: _("Search Packages");
|
placeholder-text: _("Search Packages");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stack status_stack {
|
Stack status_stack {
|
||||||
ScrolledWindow scrolled_window {
|
ScrolledWindow scrolled_window {
|
||||||
ListBox packages_list_box {
|
ListBox packages_list_box {
|
||||||
styles ["navigation-sidebar"]
|
styles ["navigation-sidebar"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.StatusPage no_filter_results {
|
Adw.StatusPage no_filter_results {
|
||||||
title: _("No Packages Match Filters");
|
title: _("No Packages Match Filters");
|
||||||
description: _("No installed package matches all of the currently applied filters");
|
description: _("No installed package matches all of the currently applied filters");
|
||||||
icon-name: "funnel-symbolic";
|
icon-name: "funnel-symbolic";
|
||||||
Button reset_filters_button {
|
Button reset_filters_button {
|
||||||
label: _("Reset Filters");
|
label: _("Reset Filters");
|
||||||
halign: center;
|
halign: center;
|
||||||
visible: false;
|
visible: false;
|
||||||
styles ["pill"]
|
styles ["pill"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.StatusPage no_packages {
|
Adw.StatusPage no_packages {
|
||||||
title: _("No Packages Found");
|
title: _("No Packages Found");
|
||||||
description: _("Warehouse cannot see the list of installed packages or your system has no packages installed");
|
description: _("Warehouse cannot see the list of installed packages or your system has no packages installed");
|
||||||
icon-name: "error-symbolic";
|
icon-name: "error-symbolic";
|
||||||
}
|
}
|
||||||
Adw.StatusPage no_results {
|
Adw.StatusPage no_results {
|
||||||
title: _("No Results Found");
|
title: _("No Results Found");
|
||||||
description: _("Try a different search");
|
description: _("Try a different search");
|
||||||
icon-name: "system-search-symbolic";
|
icon-name: "system-search-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[bottom]
|
[bottom]
|
||||||
Revealer {
|
Revealer {
|
||||||
reveal-child: bind select_button.active;
|
reveal-child: bind select_button.active;
|
||||||
transition-type: slide_up;
|
transition-type: slide_up;
|
||||||
[center]
|
[center]
|
||||||
Box bottom_bar {
|
Box bottom_bar {
|
||||||
styles ["toolbar"]
|
styles ["toolbar"]
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
homogeneous: true;
|
homogeneous: true;
|
||||||
Button select_all_button {
|
Button select_all_button {
|
||||||
styles ["raised"]
|
styles ["raised"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "selection-mode-symbolic";
|
icon-name: "selection-mode-symbolic";
|
||||||
label: _("Select All");
|
label: _("Select All");
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuButton copy_button {
|
MenuButton copy_button {
|
||||||
styles ["raised"]
|
styles ["raised"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "edit-copy-symbolic";
|
icon-name: "edit-copy-symbolic";
|
||||||
label: _("Copy");
|
label: _("Copy");
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
popover: copy_pop;
|
popover: copy_pop;
|
||||||
}
|
}
|
||||||
Button uninstall_button {
|
Button uninstall_button {
|
||||||
styles ["raised"]
|
styles ["raised"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "user-trash-symbolic";
|
icon-name: "user-trash-symbolic";
|
||||||
label: _("Uninstall");
|
label: _("Uninstall");
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
content:
|
content:
|
||||||
Adw.NavigationPage {
|
Adw.NavigationPage {
|
||||||
title: "Content Stack";
|
title: "Content Stack";
|
||||||
Stack content_stack {
|
Stack content_stack {
|
||||||
transition-type: slide_left_right;
|
transition-type: slide_left_right;
|
||||||
$PropertiesPage properties_page {}
|
$PropertiesPage properties_page {}
|
||||||
$FiltersPage filters_page {}
|
$FiltersPage filters_page {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Popover copy_pop {
|
Popover copy_pop {
|
||||||
styles ["menu"]
|
styles ["menu"]
|
||||||
ListBox copy_menu {
|
ListBox copy_menu {
|
||||||
Label copy_names {
|
Label copy_names {
|
||||||
label: _("Copy Names");
|
label: _("Copy Names");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
Label copy_ids {
|
Label copy_ids {
|
||||||
label: _("Copy IDs");
|
label: _("Copy IDs");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
Label copy_refs {
|
Label copy_refs {
|
||||||
label: _("Copy Refs");
|
label: _("Copy Refs");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,388 +13,388 @@ import subprocess, os
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/packages_page/packages_page.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/packages_page/packages_page.ui")
|
||||||
class PackagesPage(Adw.BreakpointBin):
|
class PackagesPage(Adw.BreakpointBin):
|
||||||
__gtype_name__ = 'PackagesPage'
|
__gtype_name__ = 'PackagesPage'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
packages_bpt = gtc()
|
packages_bpt = gtc()
|
||||||
packages_toast_overlay = gtc()
|
packages_toast_overlay = gtc()
|
||||||
stack = gtc()
|
stack = gtc()
|
||||||
status_stack = gtc()
|
status_stack = gtc()
|
||||||
scrolled_window = gtc()
|
scrolled_window = gtc()
|
||||||
loading_view = gtc()
|
loading_view = gtc()
|
||||||
uninstalling_view = gtc()
|
uninstalling_view = gtc()
|
||||||
reinstalling_view = gtc()
|
reinstalling_view = gtc()
|
||||||
changing_version_view = gtc()
|
changing_version_view = gtc()
|
||||||
no_filter_results = gtc()
|
no_filter_results = gtc()
|
||||||
reset_filters_button = gtc()
|
reset_filters_button = gtc()
|
||||||
no_packages = gtc()
|
no_packages = gtc()
|
||||||
no_results = gtc()
|
no_results = gtc()
|
||||||
filter_button = gtc()
|
filter_button = gtc()
|
||||||
search_button = gtc()
|
search_button = gtc()
|
||||||
search_bar = gtc()
|
search_bar = gtc()
|
||||||
search_entry = gtc()
|
search_entry = gtc()
|
||||||
packages_split = gtc()
|
packages_split = gtc()
|
||||||
packages_list_box = gtc()
|
packages_list_box = gtc()
|
||||||
select_button = gtc()
|
select_button = gtc()
|
||||||
packages_navpage = gtc()
|
packages_navpage = gtc()
|
||||||
select_all_button = gtc()
|
select_all_button = gtc()
|
||||||
content_stack = gtc()
|
content_stack = gtc()
|
||||||
copy_button = gtc()
|
copy_button = gtc()
|
||||||
copy_pop = gtc()
|
copy_pop = gtc()
|
||||||
copy_menu = gtc()
|
copy_menu = gtc()
|
||||||
copy_names = gtc()
|
copy_names = gtc()
|
||||||
copy_ids = gtc()
|
copy_ids = gtc()
|
||||||
copy_refs = gtc()
|
copy_refs = gtc()
|
||||||
uninstall_button = gtc()
|
uninstall_button = gtc()
|
||||||
properties_page = gtc()
|
properties_page = gtc()
|
||||||
filters_page = gtc()
|
filters_page = gtc()
|
||||||
|
|
||||||
# Referred to in the main window
|
# Referred to in the main window
|
||||||
# It is used to determine if a new page should be made or not
|
# It is used to determine if a new page should be made or not
|
||||||
# This must be set to the created object from within the class's __init__ method
|
# This must be set to the created object from within the class's __init__ method
|
||||||
instance = None
|
instance = None
|
||||||
page_name = "packages"
|
page_name = "packages"
|
||||||
last_activated_row = None
|
last_activated_row = None
|
||||||
|
|
||||||
def set_status(self, to_set):
|
def set_status(self, to_set):
|
||||||
|
|
||||||
if to_set is self.scrolled_window:
|
if to_set is self.scrolled_window:
|
||||||
self.properties_page.stack.set_visible_child(self.properties_page.nav_view)
|
self.properties_page.stack.set_visible_child(self.properties_page.nav_view)
|
||||||
self.select_button.set_sensitive(True)
|
self.select_button.set_sensitive(True)
|
||||||
self.filter_button.set_sensitive(True)
|
self.filter_button.set_sensitive(True)
|
||||||
self.filters_page.set_sensitive(True)
|
self.filters_page.set_sensitive(True)
|
||||||
|
|
||||||
self.search_button.set_sensitive(True)
|
self.search_button.set_sensitive(True)
|
||||||
self.search_entry.set_editable(True)
|
self.search_entry.set_editable(True)
|
||||||
else:
|
else:
|
||||||
self.select_button.set_sensitive(False)
|
self.select_button.set_sensitive(False)
|
||||||
|
|
||||||
if to_set is self.no_packages:
|
if to_set is self.no_packages:
|
||||||
self.properties_page.stack.set_visible_child(self.properties_page.error_tbv)
|
self.properties_page.stack.set_visible_child(self.properties_page.error_tbv)
|
||||||
self.filter_button.set_sensitive(False)
|
self.filter_button.set_sensitive(False)
|
||||||
self.filter_button.set_active(False)
|
self.filter_button.set_active(False)
|
||||||
|
|
||||||
if to_set is self.no_filter_results:
|
if to_set is self.no_filter_results:
|
||||||
self.properties_page.stack.set_visible_child(self.properties_page.error_tbv)
|
self.properties_page.stack.set_visible_child(self.properties_page.error_tbv)
|
||||||
self.filter_button.set_sensitive(True)
|
self.filter_button.set_sensitive(True)
|
||||||
self.filters_page.set_sensitive(True)
|
self.filters_page.set_sensitive(True)
|
||||||
if not self.packages_split.get_collapsed():
|
if not self.packages_split.get_collapsed():
|
||||||
self.filter_button.set_active(True)
|
self.filter_button.set_active(True)
|
||||||
|
|
||||||
if to_set is self.no_results:
|
if to_set is self.no_results:
|
||||||
self.filters_page.set_sensitive(False)
|
self.filters_page.set_sensitive(False)
|
||||||
|
|
||||||
if to_set is self.loading_packages:
|
if to_set is self.loading_packages:
|
||||||
self.stack.set_visible_child(self.loading_view)
|
self.stack.set_visible_child(self.loading_view)
|
||||||
elif to_set is self.uninstalling:
|
elif to_set is self.uninstalling:
|
||||||
self.stack.set_visible_child(self.uninstalling_view)
|
self.stack.set_visible_child(self.uninstalling_view)
|
||||||
elif to_set is self.reinstalling:
|
elif to_set is self.reinstalling:
|
||||||
self.stack.set_visible_child(self.reinstalling_view)
|
self.stack.set_visible_child(self.reinstalling_view)
|
||||||
elif to_set is self.changing_version:
|
elif to_set is self.changing_version:
|
||||||
self.stack.set_visible_child(self.changing_version_view)
|
self.stack.set_visible_child(self.changing_version_view)
|
||||||
else:
|
else:
|
||||||
self.stack.set_visible_child(self.packages_split)
|
self.stack.set_visible_child(self.packages_split)
|
||||||
self.status_stack.set_visible_child(to_set)
|
self.status_stack.set_visible_child(to_set)
|
||||||
|
|
||||||
def apply_filters(self):
|
def apply_filters(self):
|
||||||
i = 0
|
i = 0
|
||||||
show_apps = self.filter_settings.get_boolean("show-apps")
|
show_apps = self.filter_settings.get_boolean("show-apps")
|
||||||
show_runtimes = self.filter_settings.get_boolean("show-runtimes")
|
show_runtimes = self.filter_settings.get_boolean("show-runtimes")
|
||||||
remotes_list = self.filter_settings.get_string("remotes-list")
|
remotes_list = self.filter_settings.get_string("remotes-list")
|
||||||
runtimes_list = self.filter_settings.get_string("runtimes-list")
|
runtimes_list = self.filter_settings.get_string("runtimes-list")
|
||||||
total_visible = 0
|
total_visible = 0
|
||||||
while row := self.packages_list_box.get_row_at_index(i):
|
while row := self.packages_list_box.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
visible = True
|
visible = True
|
||||||
if row.package.is_runtime and not show_runtimes:
|
if row.package.is_runtime and not show_runtimes:
|
||||||
visible = False
|
visible = False
|
||||||
if (not row.package.is_runtime) and (not show_apps):
|
if (not row.package.is_runtime) and (not show_apps):
|
||||||
visible = False
|
visible = False
|
||||||
if remotes_list != "all" and not f"{row.package.info['origin']}<>{row.package.info['installation']}" in remotes_list:
|
if remotes_list != "all" and not f"{row.package.info['origin']}<>{row.package.info['installation']}" in remotes_list:
|
||||||
visible = False
|
visible = False
|
||||||
if runtimes_list != "all" and (row.package.is_runtime or row.package.dependent_runtime and not row.package.dependent_runtime.info["ref"] in runtimes_list):
|
if runtimes_list != "all" and (row.package.is_runtime or row.package.dependent_runtime and not row.package.dependent_runtime.info["ref"] in runtimes_list):
|
||||||
visible = False
|
visible = False
|
||||||
|
|
||||||
row.set_visible(visible)
|
row.set_visible(visible)
|
||||||
if visible:
|
if visible:
|
||||||
total_visible += 1
|
total_visible += 1
|
||||||
else:
|
else:
|
||||||
row.check_button.set_active(False)
|
row.check_button.set_active(False)
|
||||||
|
|
||||||
if total_visible == 0:
|
if total_visible == 0:
|
||||||
self.set_status(self.no_filter_results)
|
self.set_status(self.no_filter_results)
|
||||||
else:
|
else:
|
||||||
GLib.idle_add(lambda *_: self.set_status(self.scrolled_window))
|
GLib.idle_add(lambda *_: self.set_status(self.scrolled_window))
|
||||||
if self.current_row_for_properties and not self.current_row_for_properties.get_visible():
|
if self.current_row_for_properties and not self.current_row_for_properties.get_visible():
|
||||||
self.select_first_visible_row()
|
self.select_first_visible_row()
|
||||||
|
|
||||||
def select_first_visible_row(self):
|
def select_first_visible_row(self):
|
||||||
first_visible_row = None
|
first_visible_row = None
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.packages_list_box.get_row_at_index(i):
|
while row := self.packages_list_box.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
if row.get_visible():
|
if row.get_visible():
|
||||||
first_visible_row = row
|
first_visible_row = row
|
||||||
self.current_row_for_properties = row
|
self.current_row_for_properties = row
|
||||||
break
|
break
|
||||||
|
|
||||||
if not first_visible_row is None:
|
if not first_visible_row is None:
|
||||||
self.packages_list_box.select_row(first_visible_row)
|
self.packages_list_box.select_row(first_visible_row)
|
||||||
self.properties_page.set_properties(first_visible_row.package)
|
self.properties_page.set_properties(first_visible_row.package)
|
||||||
|
|
||||||
def row_select_handler(self, row):
|
def row_select_handler(self, row):
|
||||||
if row.check_button.get_active():
|
if row.check_button.get_active():
|
||||||
self.selected_rows.append(row)
|
self.selected_rows.append(row)
|
||||||
else:
|
else:
|
||||||
self.selected_rows.remove(row)
|
self.selected_rows.remove(row)
|
||||||
|
|
||||||
if (total := len(self.selected_rows)) > 0:
|
if (total := len(self.selected_rows)) > 0:
|
||||||
self.packages_navpage.set_title(_("{} Selected").format(total))
|
self.packages_navpage.set_title(_("{} Selected").format(total))
|
||||||
self.copy_button.set_sensitive(True)
|
self.copy_button.set_sensitive(True)
|
||||||
self.uninstall_button.set_sensitive(True)
|
self.uninstall_button.set_sensitive(True)
|
||||||
else:
|
else:
|
||||||
self.packages_navpage.set_title(_("Packages"))
|
self.packages_navpage.set_title(_("Packages"))
|
||||||
self.copy_button.set_sensitive(False)
|
self.copy_button.set_sensitive(False)
|
||||||
self.uninstall_button.set_sensitive(False)
|
self.uninstall_button.set_sensitive(False)
|
||||||
|
|
||||||
def select_all_handler(self, *args):
|
def select_all_handler(self, *args):
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.packages_list_box.get_row_at_index(i):
|
while row := self.packages_list_box.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
row.check_button.set_active(row.get_visible())
|
row.check_button.set_active(row.get_visible())
|
||||||
|
|
||||||
def row_rclick_handler(self, row):
|
def row_rclick_handler(self, row):
|
||||||
self.select_button.set_active(True)
|
self.select_button.set_active(True)
|
||||||
GLib.idle_add(lambda *_, button=row.check_button: button.set_active(not button.get_active()))
|
GLib.idle_add(lambda *_, button=row.check_button: button.set_active(not button.get_active()))
|
||||||
|
|
||||||
def generate_list(self, *args):
|
def generate_list(self, *args):
|
||||||
self.properties_page.nav_view.pop_to_page(self.properties_page.inner_nav_page)
|
self.properties_page.nav_view.pop_to_page(self.properties_page.inner_nav_page)
|
||||||
self.packages_list_box.remove_all()
|
self.packages_list_box.remove_all()
|
||||||
self.selected_rows.clear()
|
self.selected_rows.clear()
|
||||||
GLib.idle_add(lambda *_: self.filters_page.generate_filters())
|
GLib.idle_add(lambda *_: self.filters_page.generate_filters())
|
||||||
self.copy_button.set_sensitive(False)
|
self.copy_button.set_sensitive(False)
|
||||||
self.uninstall_button.set_sensitive(False)
|
self.uninstall_button.set_sensitive(False)
|
||||||
if len(HostInfo.flatpaks) == 0:
|
if len(HostInfo.flatpaks) == 0:
|
||||||
self.set_status(self.no_packages)
|
self.set_status(self.no_packages)
|
||||||
return
|
return
|
||||||
|
|
||||||
for package in HostInfo.flatpaks:
|
for package in HostInfo.flatpaks:
|
||||||
row = AppRow(package, self.row_rclick_handler)
|
row = AppRow(package, self.row_rclick_handler)
|
||||||
package.app_row = row
|
package.app_row = row
|
||||||
row.masked_status_icon.set_visible(package.is_masked)
|
row.masked_status_icon.set_visible(package.is_masked)
|
||||||
row.pinned_status_icon.set_visible(package.is_pinned)
|
row.pinned_status_icon.set_visible(package.is_pinned)
|
||||||
row.eol_package_package_status_icon.set_visible(package.is_eol)
|
row.eol_package_package_status_icon.set_visible(package.is_eol)
|
||||||
row.check_button.set_visible(self.select_button.get_active())
|
row.check_button.set_visible(self.select_button.get_active())
|
||||||
row.check_button.connect("toggled", lambda *_, row=row: self.row_select_handler(row))
|
row.check_button.connect("toggled", lambda *_, row=row: self.row_select_handler(row))
|
||||||
try:
|
try:
|
||||||
if not package.is_runtime:
|
if not package.is_runtime:
|
||||||
row.eol_runtime_status_icon.set_visible(package.dependent_runtime.is_eol)
|
row.eol_runtime_status_icon.set_visible(package.dependent_runtime.is_eol)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.packages_toast_overlay.add_toast(ErrorToast(_("Error getting Flatpak '{}'").format(package.info["name"]), str(e)).toast)
|
self.packages_toast_overlay.add_toast(ErrorToast(_("Error getting Flatpak '{}'").format(package.info["name"]), str(e)).toast)
|
||||||
|
|
||||||
self.packages_list_box.append(row)
|
self.packages_list_box.append(row)
|
||||||
|
|
||||||
self.apply_filters()
|
self.apply_filters()
|
||||||
self.select_first_visible_row()
|
self.select_first_visible_row()
|
||||||
|
|
||||||
self.scrolled_window.set_vadjustment(Gtk.Adjustment.new(0,0,0,0,0,0)) # Scroll list to top
|
self.scrolled_window.set_vadjustment(Gtk.Adjustment.new(0,0,0,0,0,0)) # Scroll list to top
|
||||||
|
|
||||||
def row_activate_handler(self, list_box, row):
|
def row_activate_handler(self, list_box, row):
|
||||||
if self.select_button.get_active():
|
if self.select_button.get_active():
|
||||||
row.check_button.set_active(not row.check_button.get_active())
|
row.check_button.set_active(not row.check_button.get_active())
|
||||||
return
|
return
|
||||||
|
|
||||||
self.last_activated_row = row
|
self.last_activated_row = row
|
||||||
self.properties_page.set_properties(row.package)
|
self.properties_page.set_properties(row.package)
|
||||||
self.properties_page.nav_view.pop()
|
self.properties_page.nav_view.pop()
|
||||||
self.packages_split.set_show_content(True)
|
self.packages_split.set_show_content(True)
|
||||||
self.filter_button.set_active(False)
|
self.filter_button.set_active(False)
|
||||||
self.current_row_for_properties = row
|
self.current_row_for_properties = row
|
||||||
|
|
||||||
def filter_func(self, row):
|
def filter_func(self, row):
|
||||||
search_text = self.search_entry.get_text().lower()
|
search_text = self.search_entry.get_text().lower()
|
||||||
title = row.get_title().lower()
|
title = row.get_title().lower()
|
||||||
subtitle = row.get_subtitle().lower()
|
subtitle = row.get_subtitle().lower()
|
||||||
if row.get_visible() and (search_text in title or search_text in subtitle):
|
if row.get_visible() and (search_text in title or search_text in subtitle):
|
||||||
self.is_result = True
|
self.is_result = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def set_selection_mode(self, is_enabled):
|
def set_selection_mode(self, is_enabled):
|
||||||
if is_enabled:
|
if is_enabled:
|
||||||
self.packages_list_box.set_selection_mode(Gtk.SelectionMode.NONE)
|
self.packages_list_box.set_selection_mode(Gtk.SelectionMode.NONE)
|
||||||
else:
|
else:
|
||||||
self.packages_list_box.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
self.packages_list_box.set_selection_mode(Gtk.SelectionMode.SINGLE)
|
||||||
self.packages_list_box.select_row(self.last_activated_row)
|
self.packages_list_box.select_row(self.last_activated_row)
|
||||||
|
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.packages_list_box.get_row_at_index(i):
|
while row := self.packages_list_box.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
GLib.idle_add(row.check_button.set_active, False)
|
GLib.idle_add(row.check_button.set_active, False)
|
||||||
GLib.idle_add(row.check_button.set_visible, is_enabled)
|
GLib.idle_add(row.check_button.set_visible, is_enabled)
|
||||||
|
|
||||||
def selection_copy(self, box, row):
|
def selection_copy(self, box, row):
|
||||||
self.copy_pop.popdown()
|
self.copy_pop.popdown()
|
||||||
info = ""
|
info = ""
|
||||||
feedback = ""
|
feedback = ""
|
||||||
match row.get_child():
|
match row.get_child():
|
||||||
case self.copy_names:
|
case self.copy_names:
|
||||||
info = "name"
|
info = "name"
|
||||||
feedback = _("Names")
|
feedback = _("Names")
|
||||||
case self.copy_ids:
|
case self.copy_ids:
|
||||||
info = "id"
|
info = "id"
|
||||||
feedback = _("IDs")
|
feedback = _("IDs")
|
||||||
case self.copy_refs:
|
case self.copy_refs:
|
||||||
info = "ref"
|
info = "ref"
|
||||||
feedback = _("Refs")
|
feedback = _("Refs")
|
||||||
|
|
||||||
to_copy = []
|
to_copy = []
|
||||||
for row in self.selected_rows:
|
for row in self.selected_rows:
|
||||||
to_copy.append(row.package.info[info])
|
to_copy.append(row.package.info[info])
|
||||||
to_copy += ['\n']
|
to_copy += ['\n']
|
||||||
try:
|
try:
|
||||||
HostInfo.clipboard.set("".join(to_copy[:-1]))
|
HostInfo.clipboard.set("".join(to_copy[:-1]))
|
||||||
self.packages_toast_overlay.add_toast(Adw.Toast(title=_("Copied {}").format(feedback)))
|
self.packages_toast_overlay.add_toast(Adw.Toast(title=_("Copied {}").format(feedback)))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.packages_toast_overlay.add_toast(ErrorToast(_("Could not copy {}").format(feedback), str(e)).toast)
|
self.packages_toast_overlay.add_toast(ErrorToast(_("Could not copy {}").format(feedback), str(e)).toast)
|
||||||
|
|
||||||
def selection_uninstall(self, *args):
|
def selection_uninstall(self, *args):
|
||||||
if len(self.selected_rows) < 1 or not self.uninstall_button.get_sensitive():
|
if len(self.selected_rows) < 1 or not self.uninstall_button.get_sensitive():
|
||||||
return
|
return
|
||||||
|
|
||||||
def on_response(should_trash):
|
def on_response(should_trash):
|
||||||
GLib.idle_add(lambda *_: self.set_status(self.uninstalling))
|
GLib.idle_add(lambda *_: self.set_status(self.uninstalling))
|
||||||
error = []
|
error = []
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
HostInfo.main_window.add_refresh_lockout("batch uninstalling packages")
|
HostInfo.main_window.add_refresh_lockout("batch uninstalling packages")
|
||||||
cmd = ['flatpak-spawn', '--host', 'flatpak', 'uninstall', '-y']
|
cmd = ['flatpak-spawn', '--host', 'flatpak', 'uninstall', '-y']
|
||||||
to_uninstall = {} # { <remote><><installation>: [<ref1>, <ref2>, <ref3>, ...], ... }
|
to_uninstall = {} # { <remote><><installation>: [<ref1>, <ref2>, <ref3>, ...], ... }
|
||||||
to_trash = []
|
to_trash = []
|
||||||
|
|
||||||
for row in self.selected_rows:
|
for row in self.selected_rows:
|
||||||
key = row.package.info['installation']
|
key = row.package.info['installation']
|
||||||
if ls := to_uninstall.get(key, False):
|
if ls := to_uninstall.get(key, False):
|
||||||
ls.append(row.package.info['ref'])
|
ls.append(row.package.info['ref'])
|
||||||
else:
|
else:
|
||||||
to_uninstall[key] = [row.package.info['ref']]
|
to_uninstall[key] = [row.package.info['ref']]
|
||||||
|
|
||||||
if should_trash and os.path.exists(row.package.data_path):
|
if should_trash and os.path.exists(row.package.data_path):
|
||||||
to_trash.append(row.package.data_path)
|
to_trash.append(row.package.data_path)
|
||||||
|
|
||||||
for installation, packages in to_uninstall.items():
|
for installation, packages in to_uninstall.items():
|
||||||
suffix = []
|
suffix = []
|
||||||
if installation == "user" or installation == "system":
|
if installation == "user" or installation == "system":
|
||||||
suffix.append(f"--{installation}")
|
suffix.append(f"--{installation}")
|
||||||
else:
|
else:
|
||||||
suffix.append(f"--installation={installation}")
|
suffix.append(f"--installation={installation}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(cmd + suffix + packages, check=True, text=True, capture_output=True)
|
subprocess.run(cmd + suffix + packages, check=True, text=True, capture_output=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
error.append(str(cpe.stderr))
|
error.append(str(cpe.stderr))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error.append(str(e))
|
error.append(str(e))
|
||||||
|
|
||||||
if should_trash and len(to_trash) > 0:
|
if should_trash and len(to_trash) > 0:
|
||||||
try:
|
try:
|
||||||
subprocess.run(['gio', 'trash'] + to_trash, check=True, text=True, capture_output=True)
|
subprocess.run(['gio', 'trash'] + to_trash, check=True, text=True, capture_output=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
error.append(cpe)
|
error.append(cpe)
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
self.main_window.refresh_handler()
|
self.main_window.refresh_handler()
|
||||||
HostInfo.main_window.remove_refresh_lockout("batch uninstalling packages")
|
HostInfo.main_window.remove_refresh_lockout("batch uninstalling packages")
|
||||||
if len(error) > 0:
|
if len(error) > 0:
|
||||||
details = "\n\n".join(error)
|
details = "\n\n".join(error)
|
||||||
GLib.idle_add(lambda *args: self.packages_toast_overlay.add_toast(ErrorToast(_("Errors occurred while uninstalling"), details).toast))
|
GLib.idle_add(lambda *args: self.packages_toast_overlay.add_toast(ErrorToast(_("Errors occurred while uninstalling"), details).toast))
|
||||||
else:
|
else:
|
||||||
GLib.idle_add(lambda *args: self.packages_toast_overlay.add_toast(Adw.Toast(title=_("Uninstalled Packages"))))
|
GLib.idle_add(lambda *args: self.packages_toast_overlay.add_toast(Adw.Toast(title=_("Uninstalled Packages"))))
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
dialog = UninstallDialog(on_response, True)
|
dialog = UninstallDialog(on_response, True)
|
||||||
dialog.present(self.main_window)
|
dialog.present(self.main_window)
|
||||||
|
|
||||||
def start_loading(self):
|
def start_loading(self):
|
||||||
self.search_button.set_active(False)
|
self.search_button.set_active(False)
|
||||||
self.last_activated_row = None
|
self.last_activated_row = None
|
||||||
self.packages_navpage.set_title(_("Packages"))
|
self.packages_navpage.set_title(_("Packages"))
|
||||||
self.select_button.set_active(False)
|
self.select_button.set_active(False)
|
||||||
self.set_status(self.loading_packages)
|
self.set_status(self.loading_packages)
|
||||||
|
|
||||||
def end_loading(self):
|
def end_loading(self):
|
||||||
GLib.idle_add(lambda *_: self.generate_list())
|
GLib.idle_add(lambda *_: self.generate_list())
|
||||||
|
|
||||||
def select_button_handler(self, button):
|
def select_button_handler(self, button):
|
||||||
self.set_selection_mode(button.get_active())
|
self.set_selection_mode(button.get_active())
|
||||||
|
|
||||||
def filter_button_handler(self, button):
|
def filter_button_handler(self, button):
|
||||||
if button.get_active():
|
if button.get_active():
|
||||||
self.content_stack.set_visible_child(self.filters_page)
|
self.content_stack.set_visible_child(self.filters_page)
|
||||||
self.packages_split.set_show_content(True)
|
self.packages_split.set_show_content(True)
|
||||||
else:
|
else:
|
||||||
self.content_stack.set_visible_child(self.properties_page)
|
self.content_stack.set_visible_child(self.properties_page)
|
||||||
self.packages_split.set_show_content(False)
|
self.packages_split.set_show_content(False)
|
||||||
|
|
||||||
def filter_page_handler(self, *args):
|
def filter_page_handler(self, *args):
|
||||||
if self.packages_split.get_collapsed() and not self.packages_split.get_show_content():
|
if self.packages_split.get_collapsed() and not self.packages_split.get_show_content():
|
||||||
self.filter_button.set_active(False)
|
self.filter_button.set_active(False)
|
||||||
|
|
||||||
def on_invalidate(self, row):
|
def on_invalidate(self, row):
|
||||||
current_status = self.status_stack.get_visible_child()
|
current_status = self.status_stack.get_visible_child()
|
||||||
if not current_status is self.no_results:
|
if not current_status is self.no_results:
|
||||||
self.prev_status = current_status
|
self.prev_status = current_status
|
||||||
|
|
||||||
self.is_result = False
|
self.is_result = False
|
||||||
self.packages_list_box.invalidate_filter()
|
self.packages_list_box.invalidate_filter()
|
||||||
if self.is_result:
|
if self.is_result:
|
||||||
self.set_status(self.prev_status)
|
self.set_status(self.prev_status)
|
||||||
else:
|
else:
|
||||||
self.set_status(self.no_results)
|
self.set_status(self.no_results)
|
||||||
|
|
||||||
def sort_func(self, row1, row2):
|
def sort_func(self, row1, row2):
|
||||||
return row1.package.info["name"].lower() > row2.package.info["name"].lower()
|
return row1.package.info["name"].lower() > row2.package.info["name"].lower()
|
||||||
|
|
||||||
def on_escape_handler(self):
|
def on_escape_handler(self):
|
||||||
if self.select_button.get_active():
|
if self.select_button.get_active():
|
||||||
self.select_button.set_active(False)
|
self.select_button.set_active(False)
|
||||||
elif self.filter_button.get_active():
|
elif self.filter_button.get_active():
|
||||||
self.filter_button.set_active(False)
|
self.filter_button.set_active(False)
|
||||||
|
|
||||||
def __init__(self, main_window, **kwargs):
|
def __init__(self, main_window, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.main_window = main_window
|
self.main_window = main_window
|
||||||
self.loading_packages = LoadingStatus(_("Loading Packages"), _("This should only take a moment"))
|
self.loading_packages = LoadingStatus(_("Loading Packages"), _("This should only take a moment"))
|
||||||
self.uninstalling = LoadingStatus(_("Uninstalling Packages"), _("This should only take a moment"))
|
self.uninstalling = LoadingStatus(_("Uninstalling Packages"), _("This should only take a moment"))
|
||||||
self.uninstalling_view.set_content(self.uninstalling)
|
self.uninstalling_view.set_content(self.uninstalling)
|
||||||
self.reinstalling = LoadingStatus(_("Reinstalling Package"), _("This could take a while"), True, PackageInstallWorker.cancel)
|
self.reinstalling = LoadingStatus(_("Reinstalling Package"), _("This could take a while"), True, PackageInstallWorker.cancel)
|
||||||
self.reinstalling_view.set_content(self.reinstalling)
|
self.reinstalling_view.set_content(self.reinstalling)
|
||||||
self.changing_version = LoadingStatus(_("Changing Version"), _("This could take a while"), True, ChangeVersionWorker.cancel)
|
self.changing_version = LoadingStatus(_("Changing Version"), _("This could take a while"), True, ChangeVersionWorker.cancel)
|
||||||
self.changing_version_view.set_content(self.changing_version)
|
self.changing_version_view.set_content(self.changing_version)
|
||||||
self.filter_settings = Gio.Settings.new("io.github.flattool.Warehouse.filter")
|
self.filter_settings = Gio.Settings.new("io.github.flattool.Warehouse.filter")
|
||||||
self.is_result = False
|
self.is_result = False
|
||||||
self.prev_status = None
|
self.prev_status = None
|
||||||
self.selected_rows = []
|
self.selected_rows = []
|
||||||
self.current_row_for_properties = None
|
self.current_row_for_properties = None
|
||||||
self.on_backspace_handler = self.selection_uninstall
|
self.on_backspace_handler = self.selection_uninstall
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.loading_view.set_content(self.loading_packages)
|
self.loading_view.set_content(self.loading_packages)
|
||||||
self.packages_list_box.set_filter_func(self.filter_func)
|
self.packages_list_box.set_filter_func(self.filter_func)
|
||||||
self.packages_list_box.set_sort_func(self.sort_func)
|
self.packages_list_box.set_sort_func(self.sort_func)
|
||||||
self.properties_page.packages_page = self
|
self.properties_page.packages_page = self
|
||||||
self.filters_page.packages_page = self
|
self.filters_page.packages_page = self
|
||||||
self.__class__.instance = self
|
self.__class__.instance = self
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.search_entry.connect("search-changed", self.on_invalidate)
|
self.search_entry.connect("search-changed", self.on_invalidate)
|
||||||
self.search_bar.set_key_capture_widget(main_window)
|
self.search_bar.set_key_capture_widget(main_window)
|
||||||
self.packages_list_box.connect("row-activated", self.row_activate_handler)
|
self.packages_list_box.connect("row-activated", self.row_activate_handler)
|
||||||
self.select_button.connect("toggled", self.select_button_handler)
|
self.select_button.connect("toggled", self.select_button_handler)
|
||||||
self.filter_button.connect("toggled", self.filter_button_handler)
|
self.filter_button.connect("toggled", self.filter_button_handler)
|
||||||
self.reset_filters_button.connect("clicked", lambda *_: self.filters_page.reset_filters())
|
self.reset_filters_button.connect("clicked", lambda *_: self.filters_page.reset_filters())
|
||||||
self.packages_split.connect("notify::show-content", self.filter_page_handler)
|
self.packages_split.connect("notify::show-content", self.filter_page_handler)
|
||||||
self.packages_bpt.connect("apply", self.filter_page_handler)
|
self.packages_bpt.connect("apply", self.filter_page_handler)
|
||||||
self.select_all_button.connect("clicked", self.select_all_handler)
|
self.select_all_button.connect("clicked", self.select_all_handler)
|
||||||
self.copy_menu.connect("row-activated", self.selection_copy)
|
self.copy_menu.connect("row-activated", self.selection_copy)
|
||||||
self.uninstall_button.connect("clicked", self.selection_uninstall)
|
self.uninstall_button.connect("clicked", self.selection_uninstall)
|
||||||
|
|||||||
@@ -2,26 +2,26 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $UninstallDialog : Adw.AlertDialog {
|
template $UninstallDialog : Adw.AlertDialog {
|
||||||
extra-child:
|
extra-child:
|
||||||
Adw.PreferencesGroup group {
|
Adw.PreferencesGroup group {
|
||||||
Adw.ActionRow {
|
Adw.ActionRow {
|
||||||
title: _("Keep");
|
title: _("Keep");
|
||||||
subtitle: _("Allows restoring app settings and content");
|
subtitle: _("Allows restoring app settings and content");
|
||||||
activatable-widget: keep;
|
activatable-widget: keep;
|
||||||
[prefix]
|
[prefix]
|
||||||
CheckButton keep {
|
CheckButton keep {
|
||||||
active: true;
|
active: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ActionRow {
|
Adw.ActionRow {
|
||||||
title: _("Trash");
|
title: _("Trash");
|
||||||
subtitle: _("Send data to the trash");
|
subtitle: _("Send data to the trash");
|
||||||
activatable-widget: trash;
|
activatable-widget: trash;
|
||||||
[prefix]
|
[prefix]
|
||||||
CheckButton trash {
|
CheckButton trash {
|
||||||
group: keep;
|
group: keep;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,41 +2,41 @@ from gi.repository import Adw, Gtk, GLib
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/packages_page/uninstall_dialog.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/packages_page/uninstall_dialog.ui")
|
||||||
class UninstallDialog(Adw.AlertDialog):
|
class UninstallDialog(Adw.AlertDialog):
|
||||||
__gtype_name__ = "UninstallDialog"
|
__gtype_name__ = "UninstallDialog"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
group = gtc()
|
group = gtc()
|
||||||
trash = gtc()
|
trash = gtc()
|
||||||
is_open = False
|
is_open = False
|
||||||
|
|
||||||
def on_response(self, dialog, response):
|
def on_response(self, dialog, response):
|
||||||
self.__class__.is_open = False
|
self.__class__.is_open = False
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
self.continue_callback(self.trash.get_active())
|
self.continue_callback(self.trash.get_active())
|
||||||
|
|
||||||
def present(self, *args, **kwargs):
|
def present(self, *args, **kwargs):
|
||||||
if self.__class__.is_open:
|
if self.__class__.is_open:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.__class__.is_open = True
|
self.__class__.is_open = True
|
||||||
super().present(*args, **kwargs)
|
super().present(*args, **kwargs)
|
||||||
|
|
||||||
def __init__(self, continue_callback, show_trash_option, package_name=None, **kwargs):
|
def __init__(self, continue_callback, show_trash_option, package_name=None, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
if package_name:
|
if package_name:
|
||||||
self.set_heading(GLib.markup_escape_text(_("Uninstall {}?").format(package_name)))
|
self.set_heading(GLib.markup_escape_text(_("Uninstall {}?").format(package_name)))
|
||||||
self.set_body(GLib.markup_escape_text(_("It will not be possible to use {} after removal").format(package_name)))
|
self.set_body(GLib.markup_escape_text(_("It will not be possible to use {} after removal").format(package_name)))
|
||||||
else:
|
else:
|
||||||
self.set_heading(GLib.markup_escape_text(_("Uninstall Packages?")))
|
self.set_heading(GLib.markup_escape_text(_("Uninstall Packages?")))
|
||||||
self.set_body(GLib.markup_escape_text(_("It will not be possible to use these packages after removal")))
|
self.set_body(GLib.markup_escape_text(_("It will not be possible to use these packages after removal")))
|
||||||
|
|
||||||
self.continue_callback = continue_callback
|
self.continue_callback = continue_callback
|
||||||
self.add_response("cancel", _("Cancel"))
|
self.add_response("cancel", _("Cancel"))
|
||||||
self.add_response("continue", _("Uninstall"))
|
self.add_response("continue", _("Uninstall"))
|
||||||
self.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
self.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
self.connect("response", self.on_response)
|
self.connect("response", self.on_response)
|
||||||
self.group.set_title(GLib.markup_escape_text(_("App Settings & Content")))
|
self.group.set_title(GLib.markup_escape_text(_("App Settings & Content")))
|
||||||
self.group.set_visible(show_trash_option)
|
self.group.set_visible(show_trash_option)
|
||||||
|
|||||||
@@ -2,245 +2,245 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $PropertiesPage : Adw.NavigationPage {
|
template $PropertiesPage : Adw.NavigationPage {
|
||||||
title: "Outer Page";
|
title: "Outer Page";
|
||||||
Stack stack {
|
Stack stack {
|
||||||
Adw.ToolbarView loading_tbv {
|
Adw.ToolbarView loading_tbv {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
show-title: false;
|
show-title: false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ToolbarView error_tbv {
|
Adw.ToolbarView error_tbv {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
show-title: false;
|
show-title: false;
|
||||||
}
|
}
|
||||||
Adw.StatusPage {
|
Adw.StatusPage {
|
||||||
title: _("Properties Page Unavailable");
|
title: _("Properties Page Unavailable");
|
||||||
description: _("Cannot show the properties page at this time");
|
description: _("Cannot show the properties page at this time");
|
||||||
icon-name: "error-symbolic";
|
icon-name: "error-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.NavigationView nav_view {
|
Adw.NavigationView nav_view {
|
||||||
Adw.NavigationPage inner_nav_page {
|
Adw.NavigationPage inner_nav_page {
|
||||||
title: "Inner Page";
|
title: "Inner Page";
|
||||||
Adw.ToastOverlay toast_overlay {
|
Adw.ToastOverlay toast_overlay {
|
||||||
Adw.ToolbarView {
|
Adw.ToolbarView {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar header_bar {
|
Adw.HeaderBar header_bar {
|
||||||
show-title: false;
|
show-title: false;
|
||||||
[end]
|
[end]
|
||||||
MenuButton more_menu_button {
|
MenuButton more_menu_button {
|
||||||
icon-name: "view-more-symbolic";
|
icon-name: "view-more-symbolic";
|
||||||
popover: more_menu;
|
popover: more_menu;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ScrolledWindow scrolled_window {
|
ScrolledWindow scrolled_window {
|
||||||
Adw.Clamp {
|
Adw.Clamp {
|
||||||
Box {
|
Box {
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
halign: fill;
|
halign: fill;
|
||||||
|
|
||||||
Image app_icon {
|
Image app_icon {
|
||||||
pixel-size: 100;
|
pixel-size: 100;
|
||||||
margin-top: 6;
|
margin-top: 6;
|
||||||
margin-bottom: 18;
|
margin-bottom: 18;
|
||||||
icon-name: "application-x-executable-symbolic";
|
icon-name: "application-x-executable-symbolic";
|
||||||
styles["icon-dropshadow"]
|
styles["icon-dropshadow"]
|
||||||
}
|
}
|
||||||
|
|
||||||
Label name {
|
Label name {
|
||||||
styles ["title-1"]
|
styles ["title-1"]
|
||||||
wrap: true;
|
wrap: true;
|
||||||
wrap-mode: word_char;
|
wrap-mode: word_char;
|
||||||
justify: center;
|
justify: center;
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
margin-start: 6;
|
margin-start: 6;
|
||||||
margin-end: 6;
|
margin-end: 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
Label description {
|
Label description {
|
||||||
styles ["title-4"]
|
styles ["title-4"]
|
||||||
wrap: true;
|
wrap: true;
|
||||||
wrap-mode: word_char;
|
wrap-mode: word_char;
|
||||||
justify: center;
|
justify: center;
|
||||||
margin-start: 6;
|
margin-start: 6;
|
||||||
margin-end: 6;
|
margin-end: 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
Box {
|
Box {
|
||||||
spacing: 6;
|
spacing: 6;
|
||||||
homogeneous: true;
|
homogeneous: true;
|
||||||
margin-top: 18;
|
margin-top: 18;
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
halign: center;
|
halign: center;
|
||||||
Button open_app_button {
|
Button open_app_button {
|
||||||
styles ["suggested-action", "pill"]
|
styles ["suggested-action", "pill"]
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
label: _("Open");
|
label: _("Open");
|
||||||
}
|
}
|
||||||
Button uninstall_button {
|
Button uninstall_button {
|
||||||
styles ["pill"]
|
styles ["pill"]
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
label: _("Uninstall");
|
label: _("Uninstall");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Box eol_box {
|
Box eol_box {
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
styles ["card"]
|
styles ["card"]
|
||||||
Label status_label {
|
Label status_label {
|
||||||
margin-top: 6;
|
margin-top: 6;
|
||||||
margin-bottom: 7;
|
margin-bottom: 7;
|
||||||
margin-start: 6;
|
margin-start: 6;
|
||||||
margin-end: 6;
|
margin-end: 6;
|
||||||
label: _("This package is End Of Life, and will not receive any security updates");
|
label: _("This package is End Of Life, and will not receive any security updates");
|
||||||
styles ["heading", "error"]
|
styles ["heading", "error"]
|
||||||
halign: center;
|
halign: center;
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
wrap: true;
|
wrap: true;
|
||||||
justify: center;
|
justify: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Box information {
|
Box information {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
Adw.PreferencesGroup actions {
|
Adw.PreferencesGroup actions {
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
Adw.ActionRow data_row {
|
Adw.ActionRow data_row {
|
||||||
title: _("User Data");
|
title: _("User Data");
|
||||||
styles["property"]
|
styles["property"]
|
||||||
|
|
||||||
[suffix]
|
[suffix]
|
||||||
Button open_data_button {
|
Button open_data_button {
|
||||||
styles["flat"]
|
styles["flat"]
|
||||||
valign: center;
|
valign: center;
|
||||||
icon-name: "folder-open-symbolic";
|
icon-name: "folder-open-symbolic";
|
||||||
tooltip-text: _("Open User Data");
|
tooltip-text: _("Open User Data");
|
||||||
}
|
}
|
||||||
|
|
||||||
[suffix]
|
[suffix]
|
||||||
Button trash_data_button {
|
Button trash_data_button {
|
||||||
styles["flat"]
|
styles["flat"]
|
||||||
valign: center;
|
valign: center;
|
||||||
icon-name: "user-trash-symbolic";
|
icon-name: "user-trash-symbolic";
|
||||||
tooltip-text: _("Trash User Data");
|
tooltip-text: _("Trash User Data");
|
||||||
}
|
}
|
||||||
|
|
||||||
[suffix]
|
[suffix]
|
||||||
Spinner data_spinner {
|
Spinner data_spinner {
|
||||||
spinning: true;
|
spinning: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ExpanderRow version_row {
|
Adw.ExpanderRow version_row {
|
||||||
title: _("Version");
|
title: _("Version");
|
||||||
styles ["property"]
|
styles ["property"]
|
||||||
[suffix]
|
[suffix]
|
||||||
Label mask_label {
|
Label mask_label {
|
||||||
label: _("Updates Disabled");
|
label: _("Updates Disabled");
|
||||||
styles["warning"]
|
styles["warning"]
|
||||||
}
|
}
|
||||||
Adw.ActionRow mask_row {
|
Adw.ActionRow mask_row {
|
||||||
title: _("Disable Updates");
|
title: _("Disable Updates");
|
||||||
subtitle: _("Mask this package so it's never updated");
|
subtitle: _("Mask this package so it's never updated");
|
||||||
activatable: true;
|
activatable: true;
|
||||||
Gtk.Switch mask_switch {
|
Gtk.Switch mask_switch {
|
||||||
valign: center;
|
valign: center;
|
||||||
can-focus: false;
|
can-focus: false;
|
||||||
can-target: false;
|
can-target: false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ActionRow change_version_row {
|
Adw.ActionRow change_version_row {
|
||||||
title: _("Change Version");
|
title: _("Change Version");
|
||||||
subtitle: _("Upgrade or downgrade this package");
|
subtitle: _("Upgrade or downgrade this package");
|
||||||
activatable: true;
|
activatable: true;
|
||||||
Image {
|
Image {
|
||||||
icon-name: "right-large-symbolic";
|
icon-name: "right-large-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ActionRow installed_size_row {
|
Adw.ActionRow installed_size_row {
|
||||||
styles ["property"]
|
styles ["property"]
|
||||||
title: _("Installed Size");
|
title: _("Installed Size");
|
||||||
activatable: true;
|
activatable: true;
|
||||||
Image {
|
Image {
|
||||||
icon-name: "copy-symbolic";
|
icon-name: "copy-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ActionRow runtime_row {
|
Adw.ActionRow runtime_row {
|
||||||
styles ["property"]
|
styles ["property"]
|
||||||
title: _("Runtime");
|
title: _("Runtime");
|
||||||
activatable: true;
|
activatable: true;
|
||||||
Image eol_package_package_status_icon {
|
Image eol_package_package_status_icon {
|
||||||
icon-name: "error-symbolic";
|
icon-name: "error-symbolic";
|
||||||
tooltip-text: _("This package is End Of Life, and will not recieve any security updates");
|
tooltip-text: _("This package is End Of Life, and will not recieve any security updates");
|
||||||
margin-end: 6;
|
margin-end: 6;
|
||||||
styles["error"]
|
styles["error"]
|
||||||
}
|
}
|
||||||
Image {
|
Image {
|
||||||
icon-name: "right-large-symbolic";
|
icon-name: "right-large-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ActionRow pin_row {
|
Adw.ActionRow pin_row {
|
||||||
title: _("Disable Automactic Removal");
|
title: _("Disable Automactic Removal");
|
||||||
subtitle: _("Pin this runtime to keep it installed");
|
subtitle: _("Pin this runtime to keep it installed");
|
||||||
activatable: true;
|
activatable: true;
|
||||||
Gtk.Switch pin_switch {
|
Gtk.Switch pin_switch {
|
||||||
valign: center;
|
valign: center;
|
||||||
can-focus: false;
|
can-focus: false;
|
||||||
can-target: false;
|
can-target: false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.PreferencesGroup package_info {
|
Adw.PreferencesGroup package_info {
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
title: _("Package Information");
|
title: _("Package Information");
|
||||||
Adw.ActionRow id_row {
|
Adw.ActionRow id_row {
|
||||||
styles ["property"]
|
styles ["property"]
|
||||||
title: _("Application ID");
|
title: _("Application ID");
|
||||||
activatable: true;
|
activatable: true;
|
||||||
Image {
|
Image {
|
||||||
icon-name: "copy-symbolic";
|
icon-name: "copy-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ActionRow ref_row {
|
Adw.ActionRow ref_row {
|
||||||
styles ["property"]
|
styles ["property"]
|
||||||
title: "Ref";
|
title: "Ref";
|
||||||
activatable: true;
|
activatable: true;
|
||||||
Image {
|
Image {
|
||||||
icon-name: "copy-symbolic";
|
icon-name: "copy-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ActionRow arch_row {
|
Adw.ActionRow arch_row {
|
||||||
styles ["property"]
|
styles ["property"]
|
||||||
title: _("Architecture");
|
title: _("Architecture");
|
||||||
activatable: true;
|
activatable: true;
|
||||||
Image {
|
Image {
|
||||||
icon-name: "copy-symbolic";
|
icon-name: "copy-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ActionRow branch_row {
|
Adw.ActionRow branch_row {
|
||||||
styles ["property"]
|
styles ["property"]
|
||||||
title: _("Branch");
|
title: _("Branch");
|
||||||
activatable: true;
|
activatable: true;
|
||||||
Image {
|
Image {
|
||||||
icon-name: "copy-symbolic";
|
icon-name: "copy-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ActionRow license_row {
|
Adw.ActionRow license_row {
|
||||||
styles ["property"]
|
styles ["property"]
|
||||||
title: _("License");
|
title: _("License");
|
||||||
activatable: true;
|
activatable: true;
|
||||||
Image {
|
Image {
|
||||||
icon-name: "copy-symbolic";
|
icon-name: "copy-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.PreferencesGroup remote_info {
|
Adw.PreferencesGroup remote_info {
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
title: _("Installation Information");
|
title: _("Installation Information");
|
||||||
Adw.ActionRow sdk_row {
|
Adw.ActionRow sdk_row {
|
||||||
styles ["property"]
|
styles ["property"]
|
||||||
|
|||||||
@@ -9,382 +9,382 @@ import subprocess, os
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/properties_page/properties_page.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/properties_page/properties_page.ui")
|
||||||
class PropertiesPage(Adw.NavigationPage):
|
class PropertiesPage(Adw.NavigationPage):
|
||||||
__gtype_name__ = 'PropertiesPage'
|
__gtype_name__ = 'PropertiesPage'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
stack = gtc()
|
stack = gtc()
|
||||||
error_tbv = gtc()
|
error_tbv = gtc()
|
||||||
loading_tbv = gtc()
|
loading_tbv = gtc()
|
||||||
|
|
||||||
more_menu = gtc()
|
more_menu = gtc()
|
||||||
more_list = gtc()
|
more_list = gtc()
|
||||||
|
|
||||||
nav_view = gtc()
|
nav_view = gtc()
|
||||||
inner_nav_page = gtc()
|
inner_nav_page = gtc()
|
||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
header_bar = gtc()
|
header_bar = gtc()
|
||||||
scrolled_window = gtc()
|
scrolled_window = gtc()
|
||||||
app_icon = gtc()
|
app_icon = gtc()
|
||||||
name = gtc()
|
name = gtc()
|
||||||
description = gtc()
|
description = gtc()
|
||||||
eol_box = gtc()
|
eol_box = gtc()
|
||||||
open_app_button = gtc()
|
open_app_button = gtc()
|
||||||
uninstall_button = gtc()
|
uninstall_button = gtc()
|
||||||
|
|
||||||
pin_row = gtc()
|
pin_row = gtc()
|
||||||
pin_switch = gtc()
|
pin_switch = gtc()
|
||||||
data_row = gtc()
|
data_row = gtc()
|
||||||
open_data_button = gtc()
|
open_data_button = gtc()
|
||||||
trash_data_button = gtc()
|
trash_data_button = gtc()
|
||||||
data_spinner = gtc()
|
data_spinner = gtc()
|
||||||
version_row = gtc()
|
version_row = gtc()
|
||||||
mask_label = gtc()
|
mask_label = gtc()
|
||||||
mask_row = gtc()
|
mask_row = gtc()
|
||||||
mask_switch = gtc()
|
mask_switch = gtc()
|
||||||
change_version_row = gtc()
|
change_version_row = gtc()
|
||||||
installed_size_row = gtc()
|
installed_size_row = gtc()
|
||||||
runtime_row = gtc()
|
runtime_row = gtc()
|
||||||
eol_package_package_status_icon = gtc()
|
eol_package_package_status_icon = gtc()
|
||||||
|
|
||||||
id_row = gtc()
|
id_row = gtc()
|
||||||
ref_row = gtc()
|
ref_row = gtc()
|
||||||
arch_row = gtc()
|
arch_row = gtc()
|
||||||
branch_row = gtc()
|
branch_row = gtc()
|
||||||
license_row = gtc()
|
license_row = gtc()
|
||||||
|
|
||||||
sdk_row = gtc()
|
sdk_row = gtc()
|
||||||
origin_row = gtc()
|
origin_row = gtc()
|
||||||
collection_row = gtc()
|
collection_row = gtc()
|
||||||
installation_row = gtc()
|
installation_row = gtc()
|
||||||
|
|
||||||
commit_row = gtc()
|
commit_row = gtc()
|
||||||
parent_row = gtc()
|
parent_row = gtc()
|
||||||
subject_row = gtc()
|
subject_row = gtc()
|
||||||
date_row = gtc()
|
date_row = gtc()
|
||||||
|
|
||||||
package = None
|
package = None
|
||||||
|
|
||||||
def set_properties(self, package, refresh=False):
|
def set_properties(self, package, refresh=False):
|
||||||
if package == self.package and not refresh:
|
if package == self.package and not refresh:
|
||||||
# Do not update the ui if the same app row is clicked
|
# Do not update the ui if the same app row is clicked
|
||||||
return
|
return
|
||||||
|
|
||||||
self.reinstall_did_error = False
|
self.reinstall_did_error = False
|
||||||
self.package = package
|
self.package = package
|
||||||
pkg_name = package.info["name"]
|
pkg_name = package.info["name"]
|
||||||
if pkg_name != "":
|
if pkg_name != "":
|
||||||
self.inner_nav_page.set_title(_("{} Properties").format(package.info["name"]))
|
self.inner_nav_page.set_title(_("{} Properties").format(package.info["name"]))
|
||||||
self.name.set_visible(True)
|
self.name.set_visible(True)
|
||||||
self.name.set_label(pkg_name)
|
self.name.set_label(pkg_name)
|
||||||
else:
|
else:
|
||||||
self.name.set_visible(False)
|
self.name.set_visible(False)
|
||||||
self.inner_nav_page.set_title(_("Properties"))
|
self.inner_nav_page.set_title(_("Properties"))
|
||||||
|
|
||||||
if package.icon_path:
|
if package.icon_path:
|
||||||
GLib.idle_add(lambda *_: self.app_icon.set_from_file(package.icon_path))
|
GLib.idle_add(lambda *_: self.app_icon.set_from_file(package.icon_path))
|
||||||
else:
|
else:
|
||||||
GLib.idle_add(lambda *_: self.app_icon.set_from_icon_name("application-x-executable-symbolic"))
|
GLib.idle_add(lambda *_: self.app_icon.set_from_icon_name("application-x-executable-symbolic"))
|
||||||
|
|
||||||
self.eol_box.set_visible(package.is_eol)
|
self.eol_box.set_visible(package.is_eol)
|
||||||
self.pin_row.set_visible(package.is_runtime)
|
self.pin_row.set_visible(package.is_runtime)
|
||||||
self.open_app_button.set_visible(package.is_runtime)
|
self.open_app_button.set_visible(package.is_runtime)
|
||||||
self.open_app_button.set_visible(not package.is_runtime)
|
self.open_app_button.set_visible(not package.is_runtime)
|
||||||
self.data_row.set_visible(not package.is_runtime)
|
self.data_row.set_visible(not package.is_runtime)
|
||||||
self.uninstall_button.set_sensitive(self.package.info['id'] != "io.github.flattool.Warehouse")
|
self.uninstall_button.set_sensitive(self.package.info['id'] != "io.github.flattool.Warehouse")
|
||||||
if package.is_runtime:
|
if package.is_runtime:
|
||||||
self.runtime_row.set_visible(False)
|
self.runtime_row.set_visible(False)
|
||||||
else:
|
else:
|
||||||
has_path = os.path.exists(package.data_path)
|
has_path = os.path.exists(package.data_path)
|
||||||
self.trash_data_button.set_sensitive(has_path and self.package.info['id'] != "io.github.flattool.Warehouse")
|
self.trash_data_button.set_sensitive(has_path and self.package.info['id'] != "io.github.flattool.Warehouse")
|
||||||
self.open_data_button.set_sensitive(has_path)
|
self.open_data_button.set_sensitive(has_path)
|
||||||
|
|
||||||
if not self.package.dependent_runtime is None:
|
if not self.package.dependent_runtime is None:
|
||||||
self.runtime_row.set_visible(True)
|
self.runtime_row.set_visible(True)
|
||||||
self.runtime_row.set_subtitle(self.package.dependent_runtime.info["name"])
|
self.runtime_row.set_subtitle(self.package.dependent_runtime.info["name"])
|
||||||
self.eol_package_package_status_icon.set_visible(self.package.dependent_runtime.is_eol)
|
self.eol_package_package_status_icon.set_visible(self.package.dependent_runtime.is_eol)
|
||||||
|
|
||||||
if has_path:
|
if has_path:
|
||||||
self.trash_data_button.set_visible(False)
|
self.trash_data_button.set_visible(False)
|
||||||
self.open_data_button.set_visible(False)
|
self.open_data_button.set_visible(False)
|
||||||
self.data_spinner.set_visible(True)
|
self.data_spinner.set_visible(True)
|
||||||
self.data_row.set_subtitle(_("Loading User Data"))
|
self.data_row.set_subtitle(_("Loading User Data"))
|
||||||
|
|
||||||
def callback(size):
|
def callback(size):
|
||||||
self.trash_data_button.set_visible(True)
|
self.trash_data_button.set_visible(True)
|
||||||
self.open_data_button.set_visible(True)
|
self.open_data_button.set_visible(True)
|
||||||
self.data_spinner.set_visible(False)
|
self.data_spinner.set_visible(False)
|
||||||
self.data_row.set_subtitle(size)
|
self.data_row.set_subtitle(size)
|
||||||
|
|
||||||
self.package.get_data_size(lambda size: callback(size))
|
self.package.get_data_size(lambda size: callback(size))
|
||||||
else:
|
else:
|
||||||
self.data_row.set_subtitle(_("No User Data"))
|
self.data_row.set_subtitle(_("No User Data"))
|
||||||
self.data_spinner.set_visible(False)
|
self.data_spinner.set_visible(False)
|
||||||
|
|
||||||
cli_info = None
|
cli_info = None
|
||||||
try:
|
try:
|
||||||
cli_info = package.get_cli_info()
|
cli_info = package.get_cli_info()
|
||||||
pkg_description = package.cli_info["description"]
|
pkg_description = package.cli_info["description"]
|
||||||
self.description.set_visible(pkg_description != "")
|
self.description.set_visible(pkg_description != "")
|
||||||
self.description.set_label(pkg_description)
|
self.description.set_label(pkg_description)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not get properties"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not get properties"), str(e)).toast)
|
||||||
return
|
return
|
||||||
|
|
||||||
for key, row in self.info_rows.items():
|
for key, row in self.info_rows.items():
|
||||||
row.set_visible(False)
|
row.set_visible(False)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subtitle = cli_info[key]
|
subtitle = cli_info[key]
|
||||||
row.set_subtitle(subtitle)
|
row.set_subtitle(subtitle)
|
||||||
row.set_visible(True)
|
row.set_visible(True)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
if key == "version":
|
if key == "version":
|
||||||
row.set_visible(True)
|
row.set_visible(True)
|
||||||
row.set_subtitle(_("No version information found"))
|
row.set_subtitle(_("No version information found"))
|
||||||
continue
|
continue
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not get properties"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not get properties"), str(e)).toast)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
self.mask_label.set_visible(package.is_masked)
|
self.mask_label.set_visible(package.is_masked)
|
||||||
self.mask_switch.set_active(package.is_masked)
|
self.mask_switch.set_active(package.is_masked)
|
||||||
self.pin_switch.set_active(package.is_pinned)
|
self.pin_switch.set_active(package.is_pinned)
|
||||||
GLib.idle_add(lambda *_: self.stack.set_visible_child(self.nav_view))
|
GLib.idle_add(lambda *_: self.stack.set_visible_child(self.nav_view))
|
||||||
self.more_list.remove_all()
|
self.more_list.remove_all()
|
||||||
if self.open_app_button.get_visible():
|
if self.open_app_button.get_visible():
|
||||||
self.more_list.append(self.view_snapshots)
|
self.more_list.append(self.view_snapshots)
|
||||||
self.more_list.append(self.copy_launch_command)
|
self.more_list.append(self.copy_launch_command)
|
||||||
|
|
||||||
self.more_list.append(self.show_details)
|
self.more_list.append(self.show_details)
|
||||||
self.more_list.append(self.reinstall)
|
self.more_list.append(self.reinstall)
|
||||||
|
|
||||||
def open_data_handler(self, *args):
|
def open_data_handler(self, *args):
|
||||||
if error := self.package.open_data():
|
if error := self.package.open_data():
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open data"), str(error)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open data"), str(error)).toast)
|
||||||
|
|
||||||
def trash_data_handler(self, *args):
|
def trash_data_handler(self, *args):
|
||||||
def on_choice(dialog, response):
|
def on_choice(dialog, response):
|
||||||
if response != 'continue':
|
if response != 'continue':
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
self.package.trash_data()
|
self.package.trash_data()
|
||||||
self.set_properties(self.package, refresh=True)
|
self.set_properties(self.package, refresh=True)
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new("Trashed User Data"))
|
self.toast_overlay.add_toast(Adw.Toast.new("Trashed User Data"))
|
||||||
user_data_page = HostInfo.main_window.pages[HostInfo.main_window.user_data_row]
|
user_data_page = HostInfo.main_window.pages[HostInfo.main_window.user_data_row]
|
||||||
user_data_page.start_loading()
|
user_data_page.start_loading()
|
||||||
user_data_page.end_loading()
|
user_data_page.end_loading()
|
||||||
snapshot_list_page = HostInfo.main_window.pages[HostInfo.main_window.snapshots_row].list_page
|
snapshot_list_page = HostInfo.main_window.pages[HostInfo.main_window.snapshots_row].list_page
|
||||||
snapshot_list_package = snapshot_list_page.package_or_folder
|
snapshot_list_package = snapshot_list_page.package_or_folder
|
||||||
if not snapshot_list_package is None:
|
if not snapshot_list_package is None:
|
||||||
snapshot_list_page.set_snapshots(snapshot_list_package, True)
|
snapshot_list_page.set_snapshots(snapshot_list_package, True)
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), cpe.stderr).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), cpe.stderr).toast)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(e)).toast)
|
||||||
|
|
||||||
dialog = Adw.AlertDialog(
|
dialog = Adw.AlertDialog(
|
||||||
heading=_("Send {}'s User Data to the Trash?").format(self.package.info["name"]),
|
heading=_("Send {}'s User Data to the Trash?").format(self.package.info["name"]),
|
||||||
body=_("Your settings and data for this app will be sent to the trash")
|
body=_("Your settings and data for this app will be sent to the trash")
|
||||||
)
|
)
|
||||||
dialog.add_response('cancel', _("Cancel"))
|
dialog.add_response('cancel', _("Cancel"))
|
||||||
dialog.add_response('continue', _("Trash Data"))
|
dialog.add_response('continue', _("Trash Data"))
|
||||||
dialog.connect("response", on_choice)
|
dialog.connect("response", on_choice)
|
||||||
dialog.set_response_appearance('continue', Adw.ResponseAppearance.DESTRUCTIVE)
|
dialog.set_response_appearance('continue', Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
dialog.present(self.main_window)
|
dialog.present(self.main_window)
|
||||||
|
|
||||||
def set_mask_handler(self, *args):
|
def set_mask_handler(self, *args):
|
||||||
state = not self.mask_switch.get_active()
|
state = not self.mask_switch.get_active()
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if fail := self.package.failed_mask:
|
if fail := self.package.failed_mask:
|
||||||
response = _("Could not Disable Updates") if state else _("Could not Enable Updates")
|
response = _("Could not Disable Updates") if state else _("Could not Enable Updates")
|
||||||
fail = fail.stderr if type(fail) == subprocess.CalledProcessError else fail
|
fail = fail.stderr if type(fail) == subprocess.CalledProcessError else fail
|
||||||
self.toast_overlay.add_toast(ErrorToast(response, str(fail)).toast)
|
self.toast_overlay.add_toast(ErrorToast(response, str(fail)).toast)
|
||||||
GLib.idle_add(lambda *_: self.mask_switch.set_active(not state))
|
GLib.idle_add(lambda *_: self.mask_switch.set_active(not state))
|
||||||
GLib.idle_add(lambda *_: self.mask_label.set_visible(not state))
|
GLib.idle_add(lambda *_: self.mask_label.set_visible(not state))
|
||||||
else:
|
else:
|
||||||
response = _("Disabled Updates") if state else _("Enabled Updates")
|
response = _("Disabled Updates") if state else _("Enabled Updates")
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=response))
|
self.toast_overlay.add_toast(Adw.Toast(title=response))
|
||||||
GLib.idle_add(lambda *_: self.mask_switch.set_active(state))
|
GLib.idle_add(lambda *_: self.mask_switch.set_active(state))
|
||||||
GLib.idle_add(lambda *_: self.mask_label.set_visible(state))
|
GLib.idle_add(lambda *_: self.mask_label.set_visible(state))
|
||||||
self.package.app_row.masked_status_icon.set_visible(state)
|
self.package.app_row.masked_status_icon.set_visible(state)
|
||||||
|
|
||||||
self.package.set_mask(state, callback)
|
self.package.set_mask(state, callback)
|
||||||
|
|
||||||
def set_pin_handler(self, *args):
|
def set_pin_handler(self, *args):
|
||||||
state = not self.pin_switch.get_active()
|
state = not self.pin_switch.get_active()
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if fail := self.package.failed_pin:
|
if fail := self.package.failed_pin:
|
||||||
response = _("Could not Disable Autoremoval") if state else _("Could not Enable Autoremoval")
|
response = _("Could not Disable Autoremoval") if state else _("Could not Enable Autoremoval")
|
||||||
fail = fail.stderr if type(fail) == subprocess.CalledProcessError else fail
|
fail = fail.stderr if type(fail) == subprocess.CalledProcessError else fail
|
||||||
self.toast_overlay.add_toast(ErrorToast(response, str(fail)).toast)
|
self.toast_overlay.add_toast(ErrorToast(response, str(fail)).toast)
|
||||||
GLib.idle_add(lambda *_: self.pin_switch.set_active(not state))
|
GLib.idle_add(lambda *_: self.pin_switch.set_active(not state))
|
||||||
else:
|
else:
|
||||||
response = _("Disabled Autoremoval") if state else _("Enabled Autoremoval")
|
response = _("Disabled Autoremoval") if state else _("Enabled Autoremoval")
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=response))
|
self.toast_overlay.add_toast(Adw.Toast(title=response))
|
||||||
GLib.idle_add(lambda *_: self.pin_switch.set_active(state))
|
GLib.idle_add(lambda *_: self.pin_switch.set_active(state))
|
||||||
self.package.app_row.pinned_status_icon.set_visible(state)
|
self.package.app_row.pinned_status_icon.set_visible(state)
|
||||||
|
|
||||||
self.package.set_pin(state, callback)
|
self.package.set_pin(state, callback)
|
||||||
|
|
||||||
def uninstall_handler(self, *args):
|
def uninstall_handler(self, *args):
|
||||||
def on_choice(should_trash):
|
def on_choice(should_trash):
|
||||||
self.packages_page.set_status(self.packages_page.uninstalling)
|
self.packages_page.set_status(self.packages_page.uninstalling)
|
||||||
self.package.uninstall(callback)
|
self.package.uninstall(callback)
|
||||||
if should_trash:
|
if should_trash:
|
||||||
try:
|
try:
|
||||||
self.package.trash_data()
|
self.package.trash_data()
|
||||||
self.set_properties(self.package, refresh=True)
|
self.set_properties(self.package, refresh=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), cpe.stderr).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), cpe.stderr).toast)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(e)).toast)
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if fail := self.package.failed_uninstall:
|
if fail := self.package.failed_uninstall:
|
||||||
fail = fail.stderr if type(fail) == subprocess.CalledProcessError else fail
|
fail = fail.stderr if type(fail) == subprocess.CalledProcessError else fail
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not uninstall"), str(fail)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not uninstall"), str(fail)).toast)
|
||||||
self.packages_page.set_status(self.packages_page.scrolled_window)
|
self.packages_page.set_status(self.packages_page.scrolled_window)
|
||||||
else:
|
else:
|
||||||
self.main_window.refresh_handler()
|
self.main_window.refresh_handler()
|
||||||
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Uninstalled {}").format(self.package.info["name"])))
|
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Uninstalled {}").format(self.package.info["name"])))
|
||||||
|
|
||||||
dialog = UninstallDialog(on_choice, os.path.exists(self.package.data_path), self.package.info["name"])
|
dialog = UninstallDialog(on_choice, os.path.exists(self.package.data_path), self.package.info["name"])
|
||||||
dialog.present(self.main_window)
|
dialog.present(self.main_window)
|
||||||
|
|
||||||
def runtime_row_handler(self, *args):
|
def runtime_row_handler(self, *args):
|
||||||
new_page = self.__class__()
|
new_page = self.__class__()
|
||||||
new_page.packages_page = self.packages_page
|
new_page.packages_page = self.packages_page
|
||||||
new_page.set_properties(self.package.dependent_runtime)
|
new_page.set_properties(self.package.dependent_runtime)
|
||||||
self.nav_view.push(new_page)
|
self.nav_view.push(new_page)
|
||||||
|
|
||||||
def open_app_handler(self, *args):
|
def open_app_handler(self, *args):
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=_("Opening {}…").format(self.package.info["name"])))
|
self.toast_overlay.add_toast(Adw.Toast(title=_("Opening {}…").format(self.package.info["name"])))
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if fail := self.package.failed_app_run:
|
if fail := self.package.failed_app_run:
|
||||||
fail = fail.stderr if type(fail) == subprocess.CalledProcessError else fail
|
fail = fail.stderr if type(fail) == subprocess.CalledProcessError else fail
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open {}").format(self.package.info["name"]), str(fail)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open {}").format(self.package.info["name"]), str(fail)).toast)
|
||||||
|
|
||||||
self.package.open_app(callback)
|
self.package.open_app(callback)
|
||||||
|
|
||||||
def copy_handler(self, row):
|
def copy_handler(self, row):
|
||||||
HostInfo.clipboard.set(row.get_subtitle())
|
HostInfo.clipboard.set(row.get_subtitle())
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=_("Copied {}").format(row.get_title())))
|
self.toast_overlay.add_toast(Adw.Toast(title=_("Copied {}").format(row.get_title())))
|
||||||
|
|
||||||
def change_version_handler(self, row):
|
def change_version_handler(self, row):
|
||||||
page = ChangeVersionPage(self.packages_page, self.package)
|
page = ChangeVersionPage(self.packages_page, self.package)
|
||||||
self.nav_view.push(page)
|
self.nav_view.push(page)
|
||||||
|
|
||||||
def reinstall_callback(self):
|
def reinstall_callback(self):
|
||||||
HostInfo.main_window.refresh_handler()
|
HostInfo.main_window.refresh_handler()
|
||||||
if not self.reinstall_did_error:
|
if not self.reinstall_did_error:
|
||||||
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Reinstalled {}").format(self.package.info['name'])))
|
HostInfo.main_window.toast_overlay.add_toast(Adw.Toast(title=_("Reinstalled {}").format(self.package.info['name'])))
|
||||||
|
|
||||||
def reinstall_error_callback(self, user_facing_label, error_message):
|
def reinstall_error_callback(self, user_facing_label, error_message):
|
||||||
self.reinstall_did_error = True
|
self.reinstall_did_error = True
|
||||||
GLib.idle_add(lambda *_: HostInfo.main_window.toast_overlay.add_toast(ErrorToast(user_facing_label, error_message).toast))
|
GLib.idle_add(lambda *_: HostInfo.main_window.toast_overlay.add_toast(ErrorToast(user_facing_label, error_message).toast))
|
||||||
|
|
||||||
def reinstall_handler(self):
|
def reinstall_handler(self):
|
||||||
def on_response(dialog, response):
|
def on_response(dialog, response):
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
self.reinstall_did_error = False
|
self.reinstall_did_error = False
|
||||||
PackageInstallWorker.install(
|
PackageInstallWorker.install(
|
||||||
[{
|
[{
|
||||||
"installation": self.package.info['installation'],
|
"installation": self.package.info['installation'],
|
||||||
"remote": self.package.info['origin'],
|
"remote": self.package.info['origin'],
|
||||||
"package_names": [self.package.info['ref']],
|
"package_names": [self.package.info['ref']],
|
||||||
"extra_flags": ["--reinstall"],
|
"extra_flags": ["--reinstall"],
|
||||||
}],
|
}],
|
||||||
self.packages_page.reinstalling,
|
self.packages_page.reinstalling,
|
||||||
self.reinstall_callback,
|
self.reinstall_callback,
|
||||||
self.reinstall_error_callback,
|
self.reinstall_error_callback,
|
||||||
)
|
)
|
||||||
self.packages_page.set_status(self.packages_page.reinstalling)
|
self.packages_page.set_status(self.packages_page.reinstalling)
|
||||||
|
|
||||||
dialog = Adw.AlertDialog(
|
dialog = Adw.AlertDialog(
|
||||||
heading=_("Reinstall {}?").format(self.package.info['name']),
|
heading=_("Reinstall {}?").format(self.package.info['name']),
|
||||||
body=_("This package will be uninstalled, and then reinstalled from the same remote and installation.")
|
body=_("This package will be uninstalled, and then reinstalled from the same remote and installation.")
|
||||||
)
|
)
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
dialog.add_response("continue", _("Reinstall"))
|
dialog.add_response("continue", _("Reinstall"))
|
||||||
dialog.set_response_appearance("continue", Adw.ResponseAppearance.SUGGESTED)
|
dialog.set_response_appearance("continue", Adw.ResponseAppearance.SUGGESTED)
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(HostInfo.main_window)
|
dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def more_menu_handler(self, listbox, row):
|
def more_menu_handler(self, listbox, row):
|
||||||
self.more_menu.popdown()
|
self.more_menu.popdown()
|
||||||
match row.get_child():
|
match row.get_child():
|
||||||
case self.view_snapshots:
|
case self.view_snapshots:
|
||||||
snapshots_row = HostInfo.main_window.snapshots_row
|
snapshots_row = HostInfo.main_window.snapshots_row
|
||||||
snapshots_page = HostInfo.main_window.pages[snapshots_row]
|
snapshots_page = HostInfo.main_window.pages[snapshots_row]
|
||||||
HostInfo.main_window.activate_row(snapshots_row)
|
HostInfo.main_window.activate_row(snapshots_row)
|
||||||
snapshots_page.show_snapshot(self.package)
|
snapshots_page.show_snapshot(self.package)
|
||||||
|
|
||||||
case self.copy_launch_command:
|
case self.copy_launch_command:
|
||||||
try:
|
try:
|
||||||
HostInfo.clipboard.set(f"flatpak run {self.package.info['ref']}")
|
HostInfo.clipboard.set(f"flatpak run {self.package.info['ref']}")
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new(_("Copied launch command")))
|
self.toast_overlay.add_toast(Adw.Toast.new(_("Copied launch command")))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not copy launch command"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not copy launch command"), str(e)).toast)
|
||||||
|
|
||||||
case self.show_details:
|
case self.show_details:
|
||||||
try:
|
try:
|
||||||
Gio.AppInfo.launch_default_for_uri(f"appstream://{self.package.info['id']}", None)
|
Gio.AppInfo.launch_default_for_uri(f"appstream://{self.package.info['id']}", None)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not show details"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not show details"), str(e)).toast)
|
||||||
|
|
||||||
case self.reinstall:
|
case self.reinstall:
|
||||||
self.reinstall_handler()
|
self.reinstall_handler()
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.main_window = HostInfo.main_window
|
self.main_window = HostInfo.main_window
|
||||||
self.info_rows = {
|
self.info_rows = {
|
||||||
"version": self.version_row,
|
"version": self.version_row,
|
||||||
"installed": self.installed_size_row,
|
"installed": self.installed_size_row,
|
||||||
|
|
||||||
"id": self.id_row,
|
"id": self.id_row,
|
||||||
"ref": self.ref_row,
|
"ref": self.ref_row,
|
||||||
"arch": self.arch_row,
|
"arch": self.arch_row,
|
||||||
"branch": self.branch_row,
|
"branch": self.branch_row,
|
||||||
"license": self.license_row,
|
"license": self.license_row,
|
||||||
|
|
||||||
"sdk": self.sdk_row,
|
"sdk": self.sdk_row,
|
||||||
"origin": self.origin_row,
|
"origin": self.origin_row,
|
||||||
"collection": self.collection_row,
|
"collection": self.collection_row,
|
||||||
"installation": self.installation_row,
|
"installation": self.installation_row,
|
||||||
|
|
||||||
"commit": self.commit_row,
|
"commit": self.commit_row,
|
||||||
"parent": self.parent_row,
|
"parent": self.parent_row,
|
||||||
"subject": self.subject_row,
|
"subject": self.subject_row,
|
||||||
"date": self.date_row,
|
"date": self.date_row,
|
||||||
}
|
}
|
||||||
self.loading_tbv.set_content(LoadingStatus(_("Loading Properties"), _("This should only take a moment")))
|
self.loading_tbv.set_content(LoadingStatus(_("Loading Properties"), _("This should only take a moment")))
|
||||||
self.packages_page = None # To be set in packages page
|
self.packages_page = None # To be set in packages page
|
||||||
self.__class__.main_window = self.main_window
|
self.__class__.main_window = self.main_window
|
||||||
self.view_snapshots = Gtk.Label(halign=Gtk.Align.START, label=_("View Snapshots"))
|
self.view_snapshots = Gtk.Label(halign=Gtk.Align.START, label=_("View Snapshots"))
|
||||||
self.copy_launch_command = Gtk.Label(halign=Gtk.Align.START, label=_("Copy Launch Command"))
|
self.copy_launch_command = Gtk.Label(halign=Gtk.Align.START, label=_("Copy Launch Command"))
|
||||||
self.show_details = Gtk.Label(halign=Gtk.Align.START, label=_("Show Details"))
|
self.show_details = Gtk.Label(halign=Gtk.Align.START, label=_("Show Details"))
|
||||||
self.reinstall = Gtk.Label(halign=Gtk.Align.START, label=_("Reinstall"))
|
self.reinstall = Gtk.Label(halign=Gtk.Align.START, label=_("Reinstall"))
|
||||||
self.reinstall_did_error = False
|
self.reinstall_did_error = False
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.more_list.connect("row-activated", self.more_menu_handler)
|
self.more_list.connect("row-activated", self.more_menu_handler)
|
||||||
self.open_data_button.connect("clicked", self.open_data_handler)
|
self.open_data_button.connect("clicked", self.open_data_handler)
|
||||||
self.scrolled_window.get_vadjustment().connect("value-changed", lambda adjustment: self.header_bar.set_show_title(not adjustment.get_value() == 0))
|
self.scrolled_window.get_vadjustment().connect("value-changed", lambda adjustment: self.header_bar.set_show_title(not adjustment.get_value() == 0))
|
||||||
self.trash_data_button.connect("clicked", self.trash_data_handler)
|
self.trash_data_button.connect("clicked", self.trash_data_handler)
|
||||||
self.runtime_row.connect("activated", self.runtime_row_handler)
|
self.runtime_row.connect("activated", self.runtime_row_handler)
|
||||||
self.open_app_button.connect("clicked", self.open_app_handler)
|
self.open_app_button.connect("clicked", self.open_app_handler)
|
||||||
self.uninstall_button.connect("clicked", self.uninstall_handler)
|
self.uninstall_button.connect("clicked", self.uninstall_handler)
|
||||||
self.mask_row.connect("activated", self.set_mask_handler)
|
self.mask_row.connect("activated", self.set_mask_handler)
|
||||||
self.pin_row.connect("activated", self.set_pin_handler)
|
self.pin_row.connect("activated", self.set_pin_handler)
|
||||||
self.change_version_row.connect("activated", self.change_version_handler)
|
self.change_version_row.connect("activated", self.change_version_handler)
|
||||||
for key, row in self.info_rows.items():
|
for key, row in self.info_rows.items():
|
||||||
if type(row) is Adw.ActionRow:
|
if type(row) is Adw.ActionRow:
|
||||||
row.connect("activated", self.copy_handler)
|
row.connect("activated", self.copy_handler)
|
||||||
|
|||||||
@@ -2,55 +2,55 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $AddRemoteDialog : Adw.Dialog {
|
template $AddRemoteDialog : Adw.Dialog {
|
||||||
title: _("Add a Remote");
|
title: _("Add a Remote");
|
||||||
// content-width: 500;
|
// content-width: 500;
|
||||||
// content-height: 375;
|
// content-height: 375;
|
||||||
// width-request: 400;
|
// width-request: 400;
|
||||||
follows-content-size: true;
|
follows-content-size: true;
|
||||||
Adw.ToolbarView {
|
Adw.ToolbarView {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
show-start-title-buttons: false;
|
show-start-title-buttons: false;
|
||||||
show-end-title-buttons: false;
|
show-end-title-buttons: false;
|
||||||
[start]
|
[start]
|
||||||
Button cancel_button {
|
Button cancel_button {
|
||||||
label: _("Cancel");
|
label: _("Cancel");
|
||||||
}
|
}
|
||||||
[end]
|
[end]
|
||||||
Button apply_button {
|
Button apply_button {
|
||||||
styles ["suggested-action"]
|
styles ["suggested-action"]
|
||||||
label: _("Add");
|
label: _("Add");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ToastOverlay toast_overlay {
|
Adw.ToastOverlay toast_overlay {
|
||||||
ScrolledWindow content_page {
|
ScrolledWindow content_page {
|
||||||
propagate-natural-height: true;
|
propagate-natural-height: true;
|
||||||
propagate-natural-width: true;
|
propagate-natural-width: true;
|
||||||
Adw.Clamp {
|
Adw.Clamp {
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
margin-top: 12;
|
margin-top: 12;
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
Box {
|
Box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
spacing: 12;
|
spacing: 12;
|
||||||
Adw.PreferencesGroup {
|
Adw.PreferencesGroup {
|
||||||
styles ["boxed-list"]
|
styles ["boxed-list"]
|
||||||
Adw.EntryRow title_row {
|
Adw.EntryRow title_row {
|
||||||
title: _("Title");
|
title: _("Title");
|
||||||
}
|
}
|
||||||
Adw.EntryRow name_row {
|
Adw.EntryRow name_row {
|
||||||
title: _("Name");
|
title: _("Name");
|
||||||
}
|
}
|
||||||
Adw.EntryRow url_row {
|
Adw.EntryRow url_row {
|
||||||
title: _("Repo URL");
|
title: _("Repo URL");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$InstallationChooser installation_chooser {
|
$InstallationChooser installation_chooser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,125 +7,125 @@ import subprocess, re
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/remotes_page/add_remote_dialog.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/remotes_page/add_remote_dialog.ui")
|
||||||
class AddRemoteDialog(Adw.Dialog):
|
class AddRemoteDialog(Adw.Dialog):
|
||||||
__gtype_name__ = "AddRemoteDialog"
|
__gtype_name__ = "AddRemoteDialog"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
cancel_button = gtc()
|
cancel_button = gtc()
|
||||||
apply_button = gtc()
|
apply_button = gtc()
|
||||||
content_page = gtc()
|
content_page = gtc()
|
||||||
title_row = gtc()
|
title_row = gtc()
|
||||||
name_row = gtc()
|
name_row = gtc()
|
||||||
url_row = gtc()
|
url_row = gtc()
|
||||||
installation_chooser = gtc()
|
installation_chooser = gtc()
|
||||||
is_open = False
|
is_open = False
|
||||||
|
|
||||||
def on_apply(self, *args):
|
def on_apply(self, *args):
|
||||||
self.parent_page.status_stack.set_visible_child(self.parent_page.adding_view)
|
self.parent_page.status_stack.set_visible_child(self.parent_page.adding_view)
|
||||||
self.apply_button.set_sensitive(False)
|
self.apply_button.set_sensitive(False)
|
||||||
error = [None]
|
error = [None]
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
HostInfo.main_window.add_refresh_lockout("adding remote")
|
HostInfo.main_window.add_refresh_lockout("adding remote")
|
||||||
cmd = [
|
cmd = [
|
||||||
'flatpak-spawn', '--host',
|
'flatpak-spawn', '--host',
|
||||||
'flatpak', 'remote-add',
|
'flatpak', 'remote-add',
|
||||||
f'--title={self.title_row.get_text()}',
|
f'--title={self.title_row.get_text()}',
|
||||||
self.name_row.get_text(),
|
self.name_row.get_text(),
|
||||||
self.url_row.get_text(),
|
self.url_row.get_text(),
|
||||||
]
|
]
|
||||||
installation = self.installation_chooser.get_installation()
|
installation = self.installation_chooser.get_installation()
|
||||||
if installation == "user" or installation == "system":
|
if installation == "user" or installation == "system":
|
||||||
cmd.append(f"--{installation}")
|
cmd.append(f"--{installation}")
|
||||||
else:
|
else:
|
||||||
cmd.append(f"--installation={installation}")
|
cmd.append(f"--installation={installation}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
error[0] = cpe.stderr
|
error[0] = cpe.stderr
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error[0] = e
|
error[0] = e
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
HostInfo.main_window.remove_refresh_lockout("adding remote")
|
HostInfo.main_window.remove_refresh_lockout("adding remote")
|
||||||
if error[0]:
|
if error[0]:
|
||||||
self.parent_page.status_stack.set_visible_child(self.parent_page.main_view)
|
self.parent_page.status_stack.set_visible_child(self.parent_page.main_view)
|
||||||
self.apply_button.set_sensitive(True)
|
self.apply_button.set_sensitive(True)
|
||||||
self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not add remote"), str(error[0])).toast)
|
self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not add remote"), str(error[0])).toast)
|
||||||
else:
|
else:
|
||||||
self.main_window.refresh_handler()
|
self.main_window.refresh_handler()
|
||||||
self.parent_page.toast_overlay.add_toast(Adw.Toast(title=_("Added {}").format(self.title_row.get_text())))
|
self.parent_page.toast_overlay.add_toast(Adw.Toast(title=_("Added {}").format(self.title_row.get_text())))
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def check_entries(self, row):
|
def check_entries(self, row):
|
||||||
is_passing = re.match(self.rexes[row], row.get_text())
|
is_passing = re.match(self.rexes[row], row.get_text())
|
||||||
if is_passing:
|
if is_passing:
|
||||||
row.remove_css_class("error")
|
row.remove_css_class("error")
|
||||||
else:
|
else:
|
||||||
row.add_css_class("error")
|
row.add_css_class("error")
|
||||||
|
|
||||||
match row:
|
match row:
|
||||||
case self.title_row:
|
case self.title_row:
|
||||||
self.title_passes = bool(is_passing)
|
self.title_passes = bool(is_passing)
|
||||||
case self.name_row:
|
case self.name_row:
|
||||||
self.name_passes = bool(is_passing)
|
self.name_passes = bool(is_passing)
|
||||||
case self.url_row:
|
case self.url_row:
|
||||||
self.url_passes = bool(is_passing)
|
self.url_passes = bool(is_passing)
|
||||||
|
|
||||||
self.apply_button.set_sensitive(self.title_passes and self.name_passes and self.url_passes)
|
self.apply_button.set_sensitive(self.title_passes and self.name_passes and self.url_passes)
|
||||||
|
|
||||||
def present(self, *args, **kwargs):
|
def present(self, *args, **kwargs):
|
||||||
if self.__class__.is_open:
|
if self.__class__.is_open:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.__class__.is_open = True
|
self.__class__.is_open = True
|
||||||
super().present(*args, **kwargs)
|
super().present(*args, **kwargs)
|
||||||
|
|
||||||
def on_close(self, *args):
|
def on_close(self, *args):
|
||||||
self.__class__.is_open = False
|
self.__class__.is_open = False
|
||||||
|
|
||||||
def __init__(self, main_window, parent_page, remote_info=None, **kwargs):
|
def __init__(self, main_window, parent_page, remote_info=None, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.string_list = Gtk.StringList(strings=HostInfo.installations)
|
self.string_list = Gtk.StringList(strings=HostInfo.installations)
|
||||||
self.main_window = main_window
|
self.main_window = main_window
|
||||||
self.parent_page = parent_page
|
self.parent_page = parent_page
|
||||||
|
|
||||||
self.rexes = {
|
self.rexes = {
|
||||||
self.title_row: r"^(?=.*[A-Za-z0-9])[A-Za-z0-9._-]+( +[A-Za-z0-9._-]+)*$",
|
self.title_row: r"^(?=.*[A-Za-z0-9])[A-Za-z0-9._-]+( +[A-Za-z0-9._-]+)*$",
|
||||||
self.name_row: r"^[a-zA-Z0-9\-._]+$",
|
self.name_row: r"^[a-zA-Z0-9\-._]+$",
|
||||||
self.url_row: r"^[a-zA-Z0-9\-._~:/?#[\]@!$&\'()*+,;= ]+$",
|
self.url_row: r"^[a-zA-Z0-9\-._~:/?#[\]@!$&\'()*+,;= ]+$",
|
||||||
}
|
}
|
||||||
self.title_passes = False
|
self.title_passes = False
|
||||||
self.name_passes = False
|
self.name_passes = False
|
||||||
self.url_passes = False
|
self.url_passes = False
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.installation_chooser.set_content_strings(_("remote"), False)
|
self.installation_chooser.set_content_strings(_("remote"), False)
|
||||||
if remote_info:
|
if remote_info:
|
||||||
self.title_row.set_text(remote_info["title"])
|
self.title_row.set_text(remote_info["title"])
|
||||||
self.name_row.set_text(remote_info["name"])
|
self.name_row.set_text(remote_info["name"])
|
||||||
self.url_row.set_text(remote_info["link"])
|
self.url_row.set_text(remote_info["link"])
|
||||||
if remote_info["description"] == "local file":
|
if remote_info["description"] == "local file":
|
||||||
self.check_entries(self.title_row)
|
self.check_entries(self.title_row)
|
||||||
self.check_entries(self.name_row)
|
self.check_entries(self.name_row)
|
||||||
self.check_entries(self.url_row)
|
self.check_entries(self.url_row)
|
||||||
self.url_row.set_editable(False)
|
self.url_row.set_editable(False)
|
||||||
else:
|
else:
|
||||||
self.title_row.set_editable(False)
|
self.title_row.set_editable(False)
|
||||||
self.name_row.set_editable(False)
|
self.name_row.set_editable(False)
|
||||||
self.url_row.set_editable(False)
|
self.url_row.set_editable(False)
|
||||||
self.apply_button.set_sensitive(True)
|
self.apply_button.set_sensitive(True)
|
||||||
else:
|
else:
|
||||||
self.apply_button.set_sensitive(False)
|
self.apply_button.set_sensitive(False)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.connect("closed", self.on_close)
|
self.connect("closed", self.on_close)
|
||||||
self.cancel_button.connect("clicked", lambda *_: self.close())
|
self.cancel_button.connect("clicked", lambda *_: self.close())
|
||||||
self.apply_button.connect("clicked", self.on_apply)
|
self.apply_button.connect("clicked", self.on_apply)
|
||||||
self.title_row.connect("changed", self.check_entries)
|
self.title_row.connect("changed", self.check_entries)
|
||||||
self.name_row.connect("changed", self.check_entries)
|
self.name_row.connect("changed", self.check_entries)
|
||||||
self.url_row.connect("changed", self.check_entries)
|
self.url_row.connect("changed", self.check_entries)
|
||||||
|
|||||||
@@ -2,56 +2,56 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $RemoteRow : Adw.ActionRow {
|
template $RemoteRow : Adw.ActionRow {
|
||||||
[suffix]
|
[suffix]
|
||||||
Label suffix_label {
|
Label suffix_label {
|
||||||
styles ["subtitle"]
|
styles ["subtitle"]
|
||||||
margin-end: 6;
|
margin-end: 6;
|
||||||
wrap: true;
|
wrap: true;
|
||||||
wrap-mode: word_char;
|
wrap-mode: word_char;
|
||||||
natural-wrap-mode: none;
|
natural-wrap-mode: none;
|
||||||
halign: end;
|
halign: end;
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
justify: right;
|
justify: right;
|
||||||
}
|
}
|
||||||
[suffix]
|
[suffix]
|
||||||
Button filter_button {
|
Button filter_button {
|
||||||
styles ["flat"]
|
styles ["flat"]
|
||||||
valign: center;
|
valign: center;
|
||||||
icon-name: "funnel-symbolic";
|
icon-name: "funnel-symbolic";
|
||||||
tooltip-text: _("Set a Filter for this Remote");
|
tooltip-text: _("Set a Filter for this Remote");
|
||||||
}
|
}
|
||||||
[suffix]
|
[suffix]
|
||||||
MenuButton menu_button {
|
MenuButton menu_button {
|
||||||
styles ["flat"]
|
styles ["flat"]
|
||||||
valign: center;
|
valign: center;
|
||||||
popover: menu_pop;
|
popover: menu_pop;
|
||||||
icon-name: "view-more-symbolic";
|
icon-name: "view-more-symbolic";
|
||||||
tooltip-text: _("More Actions");
|
tooltip-text: _("More Actions");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Popover menu_pop {
|
Popover menu_pop {
|
||||||
styles ["menu"]
|
styles ["menu"]
|
||||||
ListBox menu_listbox {
|
ListBox menu_listbox {
|
||||||
Label copy_title {
|
Label copy_title {
|
||||||
label: _("Copy Title");
|
label: _("Copy Title");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
Label copy_name {
|
Label copy_name {
|
||||||
label: _("Copy Name");
|
label: _("Copy Name");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
Label enable_remote {
|
Label enable_remote {
|
||||||
label: _("Enable");
|
label: _("Enable");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
Label disable_remote {
|
Label disable_remote {
|
||||||
label: _("Disable");
|
label: _("Disable");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
Label remove {
|
Label remove {
|
||||||
label: _("Remove");
|
label: _("Remove");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,168 +5,168 @@ import subprocess
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/remotes_page/remote_row.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/remotes_page/remote_row.ui")
|
||||||
class RemoteRow(Adw.ActionRow):
|
class RemoteRow(Adw.ActionRow):
|
||||||
__gtype_name__ = 'RemoteRow'
|
__gtype_name__ = 'RemoteRow'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
suffix_label = gtc()
|
suffix_label = gtc()
|
||||||
filter_button = gtc()
|
filter_button = gtc()
|
||||||
menu_pop = gtc()
|
menu_pop = gtc()
|
||||||
menu_listbox = gtc()
|
menu_listbox = gtc()
|
||||||
|
|
||||||
copy_title = gtc()
|
copy_title = gtc()
|
||||||
copy_name = gtc()
|
copy_name = gtc()
|
||||||
enable_remote = gtc()
|
enable_remote = gtc()
|
||||||
disable_remote = gtc()
|
disable_remote = gtc()
|
||||||
remove = gtc()
|
remove = gtc()
|
||||||
|
|
||||||
def enable_remote_handler(self, *args):
|
def enable_remote_handler(self, *args):
|
||||||
if not self.remote.disabled:
|
if not self.remote.disabled:
|
||||||
self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not enable remote"), _("Remote is already enabled")).toast)
|
self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not enable remote"), _("Remote is already enabled")).toast)
|
||||||
return
|
return
|
||||||
|
|
||||||
has_error = []
|
has_error = []
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
cmd = ['flatpak-spawn', '--host', 'flatpak', 'remote-modify', '--enable', self.remote.name]
|
cmd = ['flatpak-spawn', '--host', 'flatpak', 'remote-modify', '--enable', self.remote.name]
|
||||||
if self.installation == "user" or self.installation == "system":
|
if self.installation == "user" or self.installation == "system":
|
||||||
cmd.append(f"--{self.installation}")
|
cmd.append(f"--{self.installation}")
|
||||||
else:
|
else:
|
||||||
cmd.append(f"--installation={self.installation}")
|
cmd.append(f"--installation={self.installation}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
has_error.append(str(cpe.stderr))
|
has_error.append(str(cpe.stderr))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
has_error.append(str(e))
|
has_error.append(str(e))
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if len(has_error) > 0:
|
if len(has_error) > 0:
|
||||||
GLib.idle_add(lambda *args, cpe=cpe: self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not enable remote"), has_error[0]).toast))
|
GLib.idle_add(lambda *args, cpe=cpe: self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not enable remote"), has_error[0]).toast))
|
||||||
return
|
return
|
||||||
|
|
||||||
self.remove_css_class("warning")
|
self.remove_css_class("warning")
|
||||||
self.set_icon_name("")
|
self.set_icon_name("")
|
||||||
self.set_tooltip_text("")
|
self.set_tooltip_text("")
|
||||||
self.remote.disabled = False
|
self.remote.disabled = False
|
||||||
self.parent_page.toast_overlay.add_toast(Adw.Toast(title=_("Enabled remote")))
|
self.parent_page.toast_overlay.add_toast(Adw.Toast(title=_("Enabled remote")))
|
||||||
self.menu_listbox.get_row_at_index(2).set_visible(False)
|
self.menu_listbox.get_row_at_index(2).set_visible(False)
|
||||||
self.menu_listbox.get_row_at_index(3).set_visible(True)
|
self.menu_listbox.get_row_at_index(3).set_visible(True)
|
||||||
self.parent_page.total_disabled -= 1
|
self.parent_page.total_disabled -= 1
|
||||||
install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row]
|
install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row]
|
||||||
install_page.start_loading()
|
install_page.start_loading()
|
||||||
install_page.end_loading()
|
install_page.end_loading()
|
||||||
filters_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].filters_page
|
filters_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].filters_page
|
||||||
filters_page.generate_filters()
|
filters_page.generate_filters()
|
||||||
if self.parent_page.total_disabled == 0:
|
if self.parent_page.total_disabled == 0:
|
||||||
self.parent_page.show_disabled_button.set_active(False)
|
self.parent_page.show_disabled_button.set_active(False)
|
||||||
self.parent_page.show_disabled_button.set_visible(False)
|
self.parent_page.show_disabled_button.set_visible(False)
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
def disable_remote_handler(self, *args):
|
def disable_remote_handler(self, *args):
|
||||||
error = [None]
|
error = [None]
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if error[0]:
|
if error[0]:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.add_css_class("warning")
|
self.add_css_class("warning")
|
||||||
self.set_icon_name("error-symbolic")
|
self.set_icon_name("error-symbolic")
|
||||||
self.set_tooltip_text(_("Remote is Disabled"))
|
self.set_tooltip_text(_("Remote is Disabled"))
|
||||||
self.remote.disabled = True
|
self.remote.disabled = True
|
||||||
self.parent_page.toast_overlay.add_toast(Adw.Toast(title=_("Disabled remote")))
|
self.parent_page.toast_overlay.add_toast(Adw.Toast(title=_("Disabled remote")))
|
||||||
self.menu_listbox.get_row_at_index(2).set_visible(True)
|
self.menu_listbox.get_row_at_index(2).set_visible(True)
|
||||||
self.menu_listbox.get_row_at_index(3).set_visible(False)
|
self.menu_listbox.get_row_at_index(3).set_visible(False)
|
||||||
self.set_visible(self.parent_page.show_disabled_button.get_active())
|
self.set_visible(self.parent_page.show_disabled_button.get_active())
|
||||||
self.parent_page.show_disabled_button.set_visible(True)
|
self.parent_page.show_disabled_button.set_visible(True)
|
||||||
self.parent_page.total_disabled += 1
|
self.parent_page.total_disabled += 1
|
||||||
self.parent_page.none_visible_handler()
|
self.parent_page.none_visible_handler()
|
||||||
install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row]
|
install_page = HostInfo.main_window.pages[HostInfo.main_window.install_row]
|
||||||
install_page.start_loading()
|
install_page.start_loading()
|
||||||
install_page.end_loading()
|
install_page.end_loading()
|
||||||
filters_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].filters_page
|
filters_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].filters_page
|
||||||
filters_page.settings.reset("remotes-list")
|
filters_page.settings.reset("remotes-list")
|
||||||
filters_page.all_remotes_switch.set_active(False)
|
filters_page.all_remotes_switch.set_active(False)
|
||||||
filters_page.generate_filters()
|
filters_page.generate_filters()
|
||||||
filters_page.packages_page.apply_filters()
|
filters_page.packages_page.apply_filters()
|
||||||
|
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
if self.remote.disabled:
|
if self.remote.disabled:
|
||||||
self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not disable remote"), _("Remote is already disabled")).toast)
|
self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not disable remote"), _("Remote is already disabled")).toast)
|
||||||
return
|
return
|
||||||
|
|
||||||
cmd = ['flatpak-spawn', '--host', 'flatpak', 'remote-modify', '--disable', self.remote.name]
|
cmd = ['flatpak-spawn', '--host', 'flatpak', 'remote-modify', '--disable', self.remote.name]
|
||||||
if self.installation == "user" or self.installation == "system":
|
if self.installation == "user" or self.installation == "system":
|
||||||
cmd.append(f"--{self.installation}")
|
cmd.append(f"--{self.installation}")
|
||||||
else:
|
else:
|
||||||
cmd.append(f"--installation={self.installation}")
|
cmd.append(f"--installation={self.installation}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
GLib.idle_add(lambda *args, cpe=cpe: self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not disable remote"), str(cpe.stderr)).toast))
|
GLib.idle_add(lambda *args, cpe=cpe: self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not disable remote"), str(cpe.stderr)).toast))
|
||||||
error[0] = cpe
|
error[0] = cpe
|
||||||
return
|
return
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
GLib.idle_add(lambda *args, e=e: self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not disable remote"), str(e)).toast))
|
GLib.idle_add(lambda *args, e=e: self.parent_page.toast_overlay.add_toast(ErrorToast(_("Could not disable remote"), str(e)).toast))
|
||||||
error[0] = e
|
error[0] = e
|
||||||
return
|
return
|
||||||
|
|
||||||
def on_response(_, response):
|
def on_response(_, response):
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
dialog = Adw.AlertDialog(heading=_("Disable {}?").format(self.remote.title), body=_("Any installed apps from {} will stop receiving updates").format(self.remote.name))
|
dialog = Adw.AlertDialog(heading=_("Disable {}?").format(self.remote.title), body=_("Any installed apps from {} will stop receiving updates").format(self.remote.name))
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
dialog.add_response("continue", _("Disable"))
|
dialog.add_response("continue", _("Disable"))
|
||||||
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(self.parent_page.main_window)
|
dialog.present(self.parent_page.main_window)
|
||||||
|
|
||||||
def on_menu_action(self, listbox, row):
|
def on_menu_action(self, listbox, row):
|
||||||
row = row.get_child()
|
row = row.get_child()
|
||||||
match row:
|
match row:
|
||||||
case self.copy_title:
|
case self.copy_title:
|
||||||
HostInfo.clipboard.set(self.remote.title)
|
HostInfo.clipboard.set(self.remote.title)
|
||||||
self.parent_page.toast_overlay.add_toast(Adw.Toast(title=_("Copied title")))
|
self.parent_page.toast_overlay.add_toast(Adw.Toast(title=_("Copied title")))
|
||||||
case self.copy_name:
|
case self.copy_name:
|
||||||
HostInfo.clipboard.set(self.remote.name)
|
HostInfo.clipboard.set(self.remote.name)
|
||||||
self.parent_page.toast_overlay.add_toast(Adw.Toast(title=_("Copied name")))
|
self.parent_page.toast_overlay.add_toast(Adw.Toast(title=_("Copied name")))
|
||||||
case self.enable_remote:
|
case self.enable_remote:
|
||||||
self.enable_remote_handler()
|
self.enable_remote_handler()
|
||||||
case self.disable_remote:
|
case self.disable_remote:
|
||||||
self.disable_remote_handler()
|
self.disable_remote_handler()
|
||||||
case self.remove:
|
case self.remove:
|
||||||
self.parent_page.remove_remote(self)
|
self.parent_page.remove_remote(self)
|
||||||
|
|
||||||
self.menu_pop.popdown()
|
self.menu_pop.popdown()
|
||||||
|
|
||||||
def idle_stuff(self):
|
def idle_stuff(self):
|
||||||
self.set_title(self.remote.title)
|
self.set_title(self.remote.title)
|
||||||
self.set_subtitle(_("Installation: {}").format(self.installation))
|
self.set_subtitle(_("Installation: {}").format(self.installation))
|
||||||
self.suffix_label.set_label(self.remote.name)
|
self.suffix_label.set_label(self.remote.name)
|
||||||
if self.remote.disabled:
|
if self.remote.disabled:
|
||||||
self.set_icon_name("error-symbolic")
|
self.set_icon_name("error-symbolic")
|
||||||
self.add_css_class("warning")
|
self.add_css_class("warning")
|
||||||
self.set_tooltip_text(_("Remote is Disabled"))
|
self.set_tooltip_text(_("Remote is Disabled"))
|
||||||
|
|
||||||
def __init__(self, parent_page, installation, remote, **kwargs):
|
def __init__(self, parent_page, installation, remote, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.parent_page = parent_page
|
self.parent_page = parent_page
|
||||||
self.remote = remote
|
self.remote = remote
|
||||||
self.installation = installation
|
self.installation = installation
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
GLib.idle_add(lambda *_: self.idle_stuff())
|
GLib.idle_add(lambda *_: self.idle_stuff())
|
||||||
|
|
||||||
## Show / Hide the Enable / Disable actions depending on remote status
|
## Show / Hide the Enable / Disable actions depending on remote status
|
||||||
self.menu_listbox.get_row_at_index(2).set_visible(remote.disabled)
|
self.menu_listbox.get_row_at_index(2).set_visible(remote.disabled)
|
||||||
self.menu_listbox.get_row_at_index(3).set_visible(not remote.disabled)
|
self.menu_listbox.get_row_at_index(3).set_visible(not remote.disabled)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.menu_listbox.connect("row-activated", self.on_menu_action)
|
self.menu_listbox.connect("row-activated", self.on_menu_action)
|
||||||
self.filter_button.connect("clicked", lambda *_: parent_page.filter_remote(self))
|
self.filter_button.connect("clicked", lambda *_: parent_page.filter_remote(self))
|
||||||
|
|||||||
@@ -2,160 +2,160 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $RemotesPage : Adw.NavigationPage {
|
template $RemotesPage : Adw.NavigationPage {
|
||||||
title: _("Manage Remotes");
|
title: _("Manage Remotes");
|
||||||
Adw.ToastOverlay toast_overlay {
|
Adw.ToastOverlay toast_overlay {
|
||||||
Stack status_stack {
|
Stack status_stack {
|
||||||
Adw.ToolbarView loading_view {
|
Adw.ToolbarView loading_view {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ToolbarView adding_view {
|
Adw.ToolbarView adding_view {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ToolbarView main_view {
|
Adw.ToolbarView main_view {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar header_bar {
|
Adw.HeaderBar header_bar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
[start]
|
[start]
|
||||||
ToggleButton search_button {
|
ToggleButton search_button {
|
||||||
icon-name: "loupe-large-symbolic";
|
icon-name: "loupe-large-symbolic";
|
||||||
tooltip-text: _("Search Packages");
|
tooltip-text: _("Search Packages");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[top]
|
[top]
|
||||||
Adw.Clamp {
|
Adw.Clamp {
|
||||||
SearchBar search_bar {
|
SearchBar search_bar {
|
||||||
search-mode-enabled: bind search_button.active bidirectional;
|
search-mode-enabled: bind search_button.active bidirectional;
|
||||||
key-capture-widget: template;
|
key-capture-widget: template;
|
||||||
SearchEntry search_entry {
|
SearchEntry search_entry {
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
placeholder-text: _("Search Remotes");
|
placeholder-text: _("Search Remotes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stack stack {
|
Stack stack {
|
||||||
Adw.PreferencesPage content_page {
|
Adw.PreferencesPage content_page {
|
||||||
Adw.PreferencesGroup current_remotes_group {
|
Adw.PreferencesGroup current_remotes_group {
|
||||||
title: _("Current Remotes");
|
title: _("Current Remotes");
|
||||||
description: _("Remotes available on your system");
|
description: _("Remotes available on your system");
|
||||||
header-suffix:
|
header-suffix:
|
||||||
ToggleButton show_disabled_button {
|
ToggleButton show_disabled_button {
|
||||||
valign: center;
|
valign: center;
|
||||||
styles ["flat"]
|
styles ["flat"]
|
||||||
Adw.ButtonContent show_disabled_button_content {
|
Adw.ButtonContent show_disabled_button_content {
|
||||||
icon-name: "eye-not-looking-symbolic";
|
icon-name: "eye-not-looking-symbolic";
|
||||||
label: _("Show Disabled");
|
label: _("Show Disabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
Adw.ActionRow none_visible {
|
Adw.ActionRow none_visible {
|
||||||
styles ["warning"]
|
styles ["warning"]
|
||||||
[child]
|
[child]
|
||||||
Box {
|
Box {
|
||||||
spacing: 3;
|
spacing: 3;
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
Box {
|
Box {
|
||||||
halign: center;
|
halign: center;
|
||||||
Image {
|
Image {
|
||||||
valign: center;
|
valign: center;
|
||||||
margin-top: 7;
|
margin-top: 7;
|
||||||
margin-end: 6;
|
margin-end: 6;
|
||||||
icon-name: "eye-not-looking-symbolic";
|
icon-name: "eye-not-looking-symbolic";
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
margin-top: 7;
|
margin-top: 7;
|
||||||
label: _("No Enabled Remotes");
|
label: _("No Enabled Remotes");
|
||||||
wrap: true;
|
wrap: true;
|
||||||
styles ["heading"]
|
styles ["heading"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
label: _("You only have disabled remotes on this system");
|
label: _("You only have disabled remotes on this system");
|
||||||
margin-start: 16;
|
margin-start: 16;
|
||||||
margin-end: 16;
|
margin-end: 16;
|
||||||
margin-bottom: 8;
|
margin-bottom: 8;
|
||||||
justify: center;
|
justify: center;
|
||||||
halign: center;
|
halign: center;
|
||||||
wrap: true;
|
wrap: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ActionRow no_remotes {
|
Adw.ActionRow no_remotes {
|
||||||
styles ["error"]
|
styles ["error"]
|
||||||
[child]
|
[child]
|
||||||
Box {
|
Box {
|
||||||
spacing: 3;
|
spacing: 3;
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
Box {
|
Box {
|
||||||
halign: center;
|
halign: center;
|
||||||
Image {
|
Image {
|
||||||
valign: center;
|
valign: center;
|
||||||
margin-top: 7;
|
margin-top: 7;
|
||||||
margin-end: 6;
|
margin-end: 6;
|
||||||
icon-name: "error-symbolic";
|
icon-name: "error-symbolic";
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
margin-top: 7;
|
margin-top: 7;
|
||||||
label: _("No Remotes Found");
|
label: _("No Remotes Found");
|
||||||
wrap: true;
|
wrap: true;
|
||||||
styles ["heading"]
|
styles ["heading"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
label: _("Warehouse cannot see the current remotes or your system has no remotes added");
|
label: _("Warehouse cannot see the current remotes or your system has no remotes added");
|
||||||
margin-start: 16;
|
margin-start: 16;
|
||||||
margin-end: 16;
|
margin-end: 16;
|
||||||
margin-bottom: 8;
|
margin-bottom: 8;
|
||||||
justify: center;
|
justify: center;
|
||||||
halign: center;
|
halign: center;
|
||||||
wrap: true;
|
wrap: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.PreferencesGroup new_remotes_group {
|
Adw.PreferencesGroup new_remotes_group {
|
||||||
visible: bind search_button.active inverted;
|
visible: bind search_button.active inverted;
|
||||||
title: _("Add Popular Remotes");
|
title: _("Add Popular Remotes");
|
||||||
description: _("Add new remotes to get more software");
|
description: _("Add new remotes to get more software");
|
||||||
}
|
}
|
||||||
Adw.PreferencesGroup other_remotes {
|
Adw.PreferencesGroup other_remotes {
|
||||||
visible: bind search_button.active inverted;
|
visible: bind search_button.active inverted;
|
||||||
title: _("Add Other Remotes");
|
title: _("Add Other Remotes");
|
||||||
Adw.ActionRow file_remote_row {
|
Adw.ActionRow file_remote_row {
|
||||||
activatable: true;
|
activatable: true;
|
||||||
title: _("Add a Repo File");
|
title: _("Add a Repo File");
|
||||||
subtitle: _("Open a downloaded repo file to add");
|
subtitle: _("Open a downloaded repo file to add");
|
||||||
[suffix]
|
[suffix]
|
||||||
Image {
|
Image {
|
||||||
icon-name: "plus-large-symbolic";
|
icon-name: "plus-large-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ActionRow custom_remote_row {
|
Adw.ActionRow custom_remote_row {
|
||||||
activatable: true;
|
activatable: true;
|
||||||
title: _("Add a Custom Remote");
|
title: _("Add a Custom Remote");
|
||||||
subtitle: _("Manually enter new remote details");
|
subtitle: _("Manually enter new remote details");
|
||||||
[suffix]
|
[suffix]
|
||||||
Image {
|
Image {
|
||||||
icon-name: "plus-large-symbolic";
|
icon-name: "plus-large-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.StatusPage no_results {
|
Adw.StatusPage no_results {
|
||||||
title: _("No Results Found");
|
title: _("No Results Found");
|
||||||
description: _("Try a different search");
|
description: _("Try a different search");
|
||||||
icon-name: "system-search-symbolic";
|
icon-name: "system-search-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,284 +7,284 @@ from .loading_status import LoadingStatus
|
|||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
class NewRemoteRow(Adw.ActionRow):
|
class NewRemoteRow(Adw.ActionRow):
|
||||||
__gtype_name__ = "NewRemoteRow"
|
__gtype_name__ = "NewRemoteRow"
|
||||||
|
|
||||||
def idle_stuff(self, *args):
|
def idle_stuff(self, *args):
|
||||||
self.set_title(self.info["title"])
|
self.set_title(self.info["title"])
|
||||||
self.set_subtitle(self.info["description"])
|
self.set_subtitle(self.info["description"])
|
||||||
self.add_suffix(Gtk.Image.new_from_icon_name("plus-large-symbolic"))
|
self.add_suffix(Gtk.Image.new_from_icon_name("plus-large-symbolic"))
|
||||||
|
|
||||||
def __init__(self, info, **kwargs):
|
def __init__(self, info, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
self.info = info
|
self.info = info
|
||||||
GLib.idle_add(self.idle_stuff)
|
GLib.idle_add(self.idle_stuff)
|
||||||
self.set_activatable(True)
|
self.set_activatable(True)
|
||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/remotes_page/remotes_page.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/remotes_page/remotes_page.ui")
|
||||||
class RemotesPage(Adw.NavigationPage):
|
class RemotesPage(Adw.NavigationPage):
|
||||||
|
|
||||||
# Preselected Remotes
|
# Preselected Remotes
|
||||||
new_remotes = [
|
new_remotes = [
|
||||||
{
|
{
|
||||||
"title": "AppCenter",
|
"title": "AppCenter",
|
||||||
"name": "appcenter",
|
"name": "appcenter",
|
||||||
"link": "https://flatpak.elementary.io/repo.flatpakrepo",
|
"link": "https://flatpak.elementary.io/repo.flatpakrepo",
|
||||||
"description": _("The open source, pay-what-you-want app store from elementary")
|
"description": _("The open source, pay-what-you-want app store from elementary")
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Flathub",
|
"title": "Flathub",
|
||||||
"name": "flathub",
|
"name": "flathub",
|
||||||
"link": "https://dl.flathub.org/repo/flathub.flatpakrepo",
|
"link": "https://dl.flathub.org/repo/flathub.flatpakrepo",
|
||||||
"description": _("Central repository of Flatpak applications"),
|
"description": _("Central repository of Flatpak applications"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Flathub beta",
|
"title": "Flathub beta",
|
||||||
"name": "flathub-beta",
|
"name": "flathub-beta",
|
||||||
"link": "https://flathub.org/beta-repo/flathub-beta.flatpakrepo",
|
"link": "https://flathub.org/beta-repo/flathub-beta.flatpakrepo",
|
||||||
"description": _("Beta builds of Flatpak applications"),
|
"description": _("Beta builds of Flatpak applications"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "Fedora",
|
"title": "Fedora",
|
||||||
"name": "fedora",
|
"name": "fedora",
|
||||||
"link": "oci+https://registry.fedoraproject.org",
|
"link": "oci+https://registry.fedoraproject.org",
|
||||||
"description": _("Flatpaks packaged by Fedora Linux"),
|
"description": _("Flatpaks packaged by Fedora Linux"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "GNOME Nightly",
|
"title": "GNOME Nightly",
|
||||||
"name": "gnome-nightly",
|
"name": "gnome-nightly",
|
||||||
"link": "https://nightly.gnome.org/gnome-nightly.flatpakrepo",
|
"link": "https://nightly.gnome.org/gnome-nightly.flatpakrepo",
|
||||||
"description": _("The latest beta GNOME Apps and Runtimes"),
|
"description": _("The latest beta GNOME Apps and Runtimes"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"title": "WebKit Developer SDK",
|
"title": "WebKit Developer SDK",
|
||||||
"name": "webkit-sdk",
|
"name": "webkit-sdk",
|
||||||
"link": "https://software.igalia.com/flatpak-refs/webkit-sdk.flatpakrepo",
|
"link": "https://software.igalia.com/flatpak-refs/webkit-sdk.flatpakrepo",
|
||||||
"description": _("Central repository of the WebKit Developer and Runtime SDK"),
|
"description": _("Central repository of the WebKit Developer and Runtime SDK"),
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
__gtype_name__ = 'RemotesPage'
|
__gtype_name__ = 'RemotesPage'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
search_button = gtc()
|
search_button = gtc()
|
||||||
search_bar = gtc()
|
search_bar = gtc()
|
||||||
search_entry = gtc()
|
search_entry = gtc()
|
||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
stack = gtc()
|
stack = gtc()
|
||||||
current_remotes_group = gtc()
|
current_remotes_group = gtc()
|
||||||
show_disabled_button = gtc()
|
show_disabled_button = gtc()
|
||||||
show_disabled_button_content = gtc()
|
show_disabled_button_content = gtc()
|
||||||
new_remotes_group = gtc()
|
new_remotes_group = gtc()
|
||||||
file_remote_row = gtc()
|
file_remote_row = gtc()
|
||||||
custom_remote_row = gtc()
|
custom_remote_row = gtc()
|
||||||
none_visible = gtc()
|
none_visible = gtc()
|
||||||
status_stack = gtc()
|
status_stack = gtc()
|
||||||
loading_view = gtc()
|
loading_view = gtc()
|
||||||
adding_view = gtc()
|
adding_view = gtc()
|
||||||
main_view = gtc()
|
main_view = gtc()
|
||||||
|
|
||||||
no_results = gtc()
|
no_results = gtc()
|
||||||
no_remotes = gtc()
|
no_remotes = gtc()
|
||||||
content_page = gtc()
|
content_page = gtc()
|
||||||
|
|
||||||
# Referred to in the main window
|
# Referred to in the main window
|
||||||
# It is used to determine if a new page should be made or not
|
# It is used to determine if a new page should be made or not
|
||||||
# This must be set to the created object from within the class's __init__ method
|
# This must be set to the created object from within the class's __init__ method
|
||||||
instance = None
|
instance = None
|
||||||
page_name = "remotes"
|
page_name = "remotes"
|
||||||
|
|
||||||
def start_loading(self):
|
def start_loading(self):
|
||||||
self.search_button.set_active(False)
|
self.search_button.set_active(False)
|
||||||
self.status_stack.set_visible_child(self.loading_view)
|
self.status_stack.set_visible_child(self.loading_view)
|
||||||
self.total_disabled = 0
|
self.total_disabled = 0
|
||||||
for row in self.current_remote_rows:
|
for row in self.current_remote_rows:
|
||||||
self.current_remotes_group.remove(row)
|
self.current_remotes_group.remove(row)
|
||||||
|
|
||||||
self.current_remote_rows.clear()
|
self.current_remote_rows.clear()
|
||||||
|
|
||||||
def end_loading(self):
|
def end_loading(self):
|
||||||
show_disabled = self.show_disabled_button.get_active()
|
show_disabled = self.show_disabled_button.get_active()
|
||||||
self.show_disabled_button.set_visible(False)
|
self.show_disabled_button.set_visible(False)
|
||||||
total_visible = 0
|
total_visible = 0
|
||||||
for installation, remotes in HostInfo.remotes.items():
|
for installation, remotes in HostInfo.remotes.items():
|
||||||
for remote in remotes:
|
for remote in remotes:
|
||||||
row = RemoteRow(self, installation, remote)
|
row = RemoteRow(self, installation, remote)
|
||||||
self.current_remote_rows.append(row)
|
self.current_remote_rows.append(row)
|
||||||
self.current_remotes_group.add(row)
|
self.current_remotes_group.add(row)
|
||||||
if row.remote.disabled:
|
if row.remote.disabled:
|
||||||
self.total_disabled += 1
|
self.total_disabled += 1
|
||||||
self.show_disabled_button.set_visible(True)
|
self.show_disabled_button.set_visible(True)
|
||||||
if show_disabled:
|
if show_disabled:
|
||||||
total_visible += 1
|
total_visible += 1
|
||||||
else:
|
else:
|
||||||
row.set_visible(False)
|
row.set_visible(False)
|
||||||
else:
|
else:
|
||||||
total_visible += 1
|
total_visible += 1
|
||||||
|
|
||||||
self.none_visible.set_visible(total_visible == 0)
|
self.none_visible.set_visible(total_visible == 0)
|
||||||
|
|
||||||
if len(self.current_remote_rows) == 0:
|
if len(self.current_remote_rows) == 0:
|
||||||
self.no_remotes.set_visible(True)
|
self.no_remotes.set_visible(True)
|
||||||
self.none_visible.set_visible(False)
|
self.none_visible.set_visible(False)
|
||||||
else:
|
else:
|
||||||
self.no_remotes.set_visible(False)
|
self.no_remotes.set_visible(False)
|
||||||
|
|
||||||
GLib.idle_add(lambda *_: self.status_stack.set_visible_child(self.main_view))
|
GLib.idle_add(lambda *_: self.status_stack.set_visible_child(self.main_view))
|
||||||
|
|
||||||
def none_visible_handler(self):
|
def none_visible_handler(self):
|
||||||
any_visible = False
|
any_visible = False
|
||||||
for row in self.current_remote_rows:
|
for row in self.current_remote_rows:
|
||||||
if row.get_visible():
|
if row.get_visible():
|
||||||
any_visible = True
|
any_visible = True
|
||||||
break
|
break
|
||||||
|
|
||||||
self.none_visible.set_visible(not any_visible)
|
self.none_visible.set_visible(not any_visible)
|
||||||
|
|
||||||
def filter_remote(self, row):
|
def filter_remote(self, row):
|
||||||
self.filter_setting.set_boolean("show-apps", True)
|
self.filter_setting.set_boolean("show-apps", True)
|
||||||
self.filter_setting.set_boolean("show-runtimes", True)
|
self.filter_setting.set_boolean("show-runtimes", True)
|
||||||
self.filter_setting.set_string("remotes-list", f"{row.remote.name}<>{row.installation};")
|
self.filter_setting.set_string("remotes-list", f"{row.remote.name}<>{row.installation};")
|
||||||
self.filter_setting.reset("runtimes-list")
|
self.filter_setting.reset("runtimes-list")
|
||||||
packages_page = self.main_window.pages[self.main_window.packages_row]
|
packages_page = self.main_window.pages[self.main_window.packages_row]
|
||||||
packages_page.filters_page.generate_filters()
|
packages_page.filters_page.generate_filters()
|
||||||
packages_page.apply_filters()
|
packages_page.apply_filters()
|
||||||
GLib.idle_add(lambda *_: self.main_window.activate_row(self.main_window.packages_row))
|
GLib.idle_add(lambda *_: self.main_window.activate_row(self.main_window.packages_row))
|
||||||
GLib.idle_add(lambda *args: packages_page.packages_toast_overlay.add_toast(Adw.Toast(title=_("Showing all packages from {}").format(row.remote.title))))
|
GLib.idle_add(lambda *args: packages_page.packages_toast_overlay.add_toast(Adw.Toast(title=_("Showing all packages from {}").format(row.remote.title))))
|
||||||
|
|
||||||
def remove_remote(self, row):
|
def remove_remote(self, row):
|
||||||
error = [None]
|
error = [None]
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
install = row.installation
|
install = row.installation
|
||||||
cmd = ['flatpak-spawn', '--host', 'flatpak', 'remote-delete', row.remote.name, '--force']
|
cmd = ['flatpak-spawn', '--host', 'flatpak', 'remote-delete', row.remote.name, '--force']
|
||||||
if install == "user" or install == "system":
|
if install == "user" or install == "system":
|
||||||
cmd.append(f"--{install}")
|
cmd.append(f"--{install}")
|
||||||
else:
|
else:
|
||||||
cmd.append(f"--installation={install}")
|
cmd.append(f"--installation={install}")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
error[0] = cpe.stderr
|
error[0] = cpe.stderr
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error[0] = e
|
error[0] = e
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if error[0]:
|
if error[0]:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not remove remote"), str(error[0])).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not remove remote"), str(error[0])).toast)
|
||||||
else:
|
else:
|
||||||
filters_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].filters_page
|
filters_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].filters_page
|
||||||
filters_page.settings.reset("remotes-list")
|
filters_page.settings.reset("remotes-list")
|
||||||
filters_page.all_remotes_switch.set_active(False)
|
filters_page.all_remotes_switch.set_active(False)
|
||||||
# filters_page.packages_page.apply_filters()
|
# filters_page.packages_page.apply_filters()
|
||||||
self.main_window.refresh_handler()
|
self.main_window.refresh_handler()
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=_("Removed {}").format(row.remote.title)))
|
self.toast_overlay.add_toast(Adw.Toast(title=_("Removed {}").format(row.remote.title)))
|
||||||
|
|
||||||
def on_response(_, response):
|
def on_response(_, response):
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
dialog = Adw.AlertDialog(heading=_("Remove {}?").format(row.remote.title), body=_("Any installed apps from {} will stop receiving updates").format(row.remote.name))
|
dialog = Adw.AlertDialog(heading=_("Remove {}?").format(row.remote.title), body=_("Any installed apps from {} will stop receiving updates").format(row.remote.name))
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
dialog.add_response("continue", _("Remove"))
|
dialog.add_response("continue", _("Remove"))
|
||||||
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(self.main_window)
|
dialog.present(self.main_window)
|
||||||
|
|
||||||
def on_search(self, entry):
|
def on_search(self, entry):
|
||||||
text = entry.get_text().lower()
|
text = entry.get_text().lower()
|
||||||
total = 0
|
total = 0
|
||||||
show_disabled = self.show_disabled_button.get_active()
|
show_disabled = self.show_disabled_button.get_active()
|
||||||
|
|
||||||
for row in self.current_remote_rows:
|
for row in self.current_remote_rows:
|
||||||
title_match = text in row.get_title().lower()
|
title_match = text in row.get_title().lower()
|
||||||
subtitle_match = text in row.get_subtitle().lower()
|
subtitle_match = text in row.get_subtitle().lower()
|
||||||
visible = (title_match or subtitle_match) and (show_disabled or not row.remote.disabled)
|
visible = (title_match or subtitle_match) and (show_disabled or not row.remote.disabled)
|
||||||
total += visible
|
total += visible
|
||||||
row.set_visible(visible)
|
row.set_visible(visible)
|
||||||
|
|
||||||
if text == "":
|
if text == "":
|
||||||
self.stack.set_visible_child(self.content_page)
|
self.stack.set_visible_child(self.content_page)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.stack.set_visible_child(self.content_page if total > 0 else self.no_results)
|
self.stack.set_visible_child(self.content_page if total > 0 else self.no_results)
|
||||||
|
|
||||||
def local_file_handler(self, path):
|
def local_file_handler(self, path):
|
||||||
try:
|
try:
|
||||||
name = path.split("/")[-1].split(".")[0]
|
name = path.split("/")[-1].split(".")[0]
|
||||||
info = {
|
info = {
|
||||||
"title": name.title(),
|
"title": name.title(),
|
||||||
"name": name,
|
"name": name,
|
||||||
"description": "local file",
|
"description": "local file",
|
||||||
"link": path,
|
"link": path,
|
||||||
}
|
}
|
||||||
AddRemoteDialog(self.main_window, self, info).present(self.main_window)
|
AddRemoteDialog(self.main_window, self, info).present(self.main_window)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open file"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open file"), str(e)).toast)
|
||||||
|
|
||||||
def file_callback(self, chooser, result):
|
def file_callback(self, chooser, result):
|
||||||
try:
|
try:
|
||||||
file = chooser.open_finish(result)
|
file = chooser.open_finish(result)
|
||||||
path = file.get_path()
|
path = file.get_path()
|
||||||
self.local_file_handler(path)
|
self.local_file_handler(path)
|
||||||
except GLib.GError as ge:
|
except GLib.GError as ge:
|
||||||
if "Dismissed by user" in str(ge):
|
if "Dismissed by user" in str(ge):
|
||||||
return
|
return
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open file"), str(ge)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open file"), str(ge)).toast)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open file"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open file"), str(e)).toast)
|
||||||
|
|
||||||
def add_file_handler(self):
|
def add_file_handler(self):
|
||||||
file_filter = Gtk.FileFilter(name=_("Flatpak Repos"))
|
file_filter = Gtk.FileFilter(name=_("Flatpak Repos"))
|
||||||
file_filter.add_suffix("flatpakrepo")
|
file_filter.add_suffix("flatpakrepo")
|
||||||
filters = Gio.ListStore.new(Gtk.FileFilter)
|
filters = Gio.ListStore.new(Gtk.FileFilter)
|
||||||
filters.append(file_filter)
|
filters.append(file_filter)
|
||||||
file_chooser = Gtk.FileDialog()
|
file_chooser = Gtk.FileDialog()
|
||||||
file_chooser.set_filters(filters)
|
file_chooser.set_filters(filters)
|
||||||
file_chooser.set_default_filter(file_filter)
|
file_chooser.set_default_filter(file_filter)
|
||||||
file_chooser.open(self.main_window, None, self.file_callback)
|
file_chooser.open(self.main_window, None, self.file_callback)
|
||||||
|
|
||||||
def show_disabled_handler(self, button):
|
def show_disabled_handler(self, button):
|
||||||
show_disabled = button.get_active()
|
show_disabled = button.get_active()
|
||||||
self.show_disabled_button_content.set_icon_name("eye-open-negative-filled-symbolic" if show_disabled else "eye-not-looking-symbolic")
|
self.show_disabled_button_content.set_icon_name("eye-open-negative-filled-symbolic" if show_disabled else "eye-not-looking-symbolic")
|
||||||
total_visible = 0
|
total_visible = 0
|
||||||
for row in self.current_remote_rows:
|
for row in self.current_remote_rows:
|
||||||
if row.remote.disabled:
|
if row.remote.disabled:
|
||||||
if show_disabled: # show disabled
|
if show_disabled: # show disabled
|
||||||
row.set_visible(True)
|
row.set_visible(True)
|
||||||
total_visible += 1
|
total_visible += 1
|
||||||
else:
|
else:
|
||||||
row.set_visible(False)
|
row.set_visible(False)
|
||||||
else:
|
else:
|
||||||
total_visible += 1
|
total_visible += 1
|
||||||
|
|
||||||
self.none_visible.set_visible(total_visible == 0)
|
self.none_visible.set_visible(total_visible == 0)
|
||||||
|
|
||||||
def new_custom_handler(self, *args):
|
def new_custom_handler(self, *args):
|
||||||
AddRemoteDialog(self.main_window, self).present(self.main_window)
|
AddRemoteDialog(self.main_window, self).present(self.main_window)
|
||||||
|
|
||||||
def __init__(self, main_window, **kwargs):
|
def __init__(self, main_window, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.__class__.instance = self
|
self.__class__.instance = self
|
||||||
self.main_window = main_window
|
self.main_window = main_window
|
||||||
self.search_bar.set_key_capture_widget(main_window)
|
self.search_bar.set_key_capture_widget(main_window)
|
||||||
self.current_remote_rows = []
|
self.current_remote_rows = []
|
||||||
self.filter_setting = Gio.Settings.new("io.github.flattool.Warehouse.filter")
|
self.filter_setting = Gio.Settings.new("io.github.flattool.Warehouse.filter")
|
||||||
self.total_disabled = 0
|
self.total_disabled = 0
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.file_remote_row.connect("activated", lambda *_: self.add_file_handler())
|
self.file_remote_row.connect("activated", lambda *_: self.add_file_handler())
|
||||||
self.custom_remote_row.connect("activated", self.new_custom_handler)
|
self.custom_remote_row.connect("activated", self.new_custom_handler)
|
||||||
self.search_entry.connect("search-changed", self.on_search)
|
self.search_entry.connect("search-changed", self.on_search)
|
||||||
self.show_disabled_button.connect("toggled", self.show_disabled_handler)
|
self.show_disabled_button.connect("toggled", self.show_disabled_handler)
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.adding_view.set_content(LoadingStatus(_("Adding Remote"), _("This should only take a moment")))
|
self.adding_view.set_content(LoadingStatus(_("Adding Remote"), _("This should only take a moment")))
|
||||||
self.loading_view.set_content(LoadingStatus(_("Loading Remotes"), _("This should only take a moment")))
|
self.loading_view.set_content(LoadingStatus(_("Loading Remotes"), _("This should only take a moment")))
|
||||||
for item in self.new_remotes:
|
for item in self.new_remotes:
|
||||||
row = NewRemoteRow(item)
|
row = NewRemoteRow(item)
|
||||||
row.connect("activated", lambda *_, remote_info=item: AddRemoteDialog(main_window, self, remote_info).present(main_window))
|
row.connect("activated", lambda *_, remote_info=item: AddRemoteDialog(main_window, self, remote_info).present(main_window))
|
||||||
self.new_remotes_group.add(row)
|
self.new_remotes_group.add(row)
|
||||||
|
|||||||
@@ -2,93 +2,93 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $NewSnapshotDialog : Adw.Dialog {
|
template $NewSnapshotDialog : Adw.Dialog {
|
||||||
follows-content-size: true;
|
follows-content-size: true;
|
||||||
Adw.ToastOverlay toast_overlay {
|
Adw.ToastOverlay toast_overlay {
|
||||||
Adw.NavigationPage nav_page {
|
Adw.NavigationPage nav_page {
|
||||||
title: "No Title Set";
|
title: "No Title Set";
|
||||||
Adw.ToolbarView {
|
Adw.ToolbarView {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
show-start-title-buttons: false;
|
show-start-title-buttons: false;
|
||||||
show-end-title-buttons: false;
|
show-end-title-buttons: false;
|
||||||
[start]
|
[start]
|
||||||
Button list_cancel_button {
|
Button list_cancel_button {
|
||||||
label: _("Cancel");
|
label: _("Cancel");
|
||||||
}
|
}
|
||||||
[start]
|
[start]
|
||||||
ToggleButton search_button {
|
ToggleButton search_button {
|
||||||
icon-name: "loupe-large-symbolic";
|
icon-name: "loupe-large-symbolic";
|
||||||
tooltip-text: _("Search Apps");
|
tooltip-text: _("Search Apps");
|
||||||
}
|
}
|
||||||
[end]
|
[end]
|
||||||
Button create_button {
|
Button create_button {
|
||||||
sensitive: false;
|
sensitive: false;
|
||||||
label: _("Create");
|
label: _("Create");
|
||||||
styles ["suggested-action"]
|
styles ["suggested-action"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[top]
|
[top]
|
||||||
Adw.Clamp {
|
Adw.Clamp {
|
||||||
SearchBar search_bar {
|
SearchBar search_bar {
|
||||||
search-mode-enabled: bind search_button.active bidirectional;
|
search-mode-enabled: bind search_button.active bidirectional;
|
||||||
key-capture-widget: template;
|
key-capture-widget: template;
|
||||||
SearchEntry search_entry {
|
SearchEntry search_entry {
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
placeholder-text: _("Search Apps");
|
placeholder-text: _("Search Apps");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stack stack {
|
Stack stack {
|
||||||
ScrolledWindow scrolled_window {
|
ScrolledWindow scrolled_window {
|
||||||
propagate-natural-height: true;
|
propagate-natural-height: true;
|
||||||
propagate-natural-width: true;
|
propagate-natural-width: true;
|
||||||
Box {
|
Box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
Adw.EntryRow name_entry {
|
Adw.EntryRow name_entry {
|
||||||
title: "No Title Set";
|
title: "No Title Set";
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
margin-top: 12;
|
margin-top: 12;
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
styles ["card"]
|
styles ["card"]
|
||||||
}
|
}
|
||||||
ListBox listbox {
|
ListBox listbox {
|
||||||
valign: start;
|
valign: start;
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
// margin-top: 12;
|
// margin-top: 12;
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
selection-mode: none;
|
selection-mode: none;
|
||||||
styles ["boxed-list"]
|
styles ["boxed-list"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.StatusPage no_results {
|
Adw.StatusPage no_results {
|
||||||
title: _("No Results Found");
|
title: _("No Results Found");
|
||||||
description: _("Try a different search");
|
description: _("Try a different search");
|
||||||
icon-name: "system-search-symbolic";
|
icon-name: "system-search-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[bottom]
|
[bottom]
|
||||||
ActionBar {
|
ActionBar {
|
||||||
revealed: bind search_button.visible;
|
revealed: bind search_button.visible;
|
||||||
[start]
|
[start]
|
||||||
Button select_all_button {
|
Button select_all_button {
|
||||||
styles ["raised"]
|
styles ["raised"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
label: _("Select All");
|
label: _("Select All");
|
||||||
icon-name: "selection-mode-symbolic";
|
icon-name: "selection-mode-symbolic";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[end]
|
[end]
|
||||||
Label total_selected_label {
|
Label total_selected_label {
|
||||||
label: "";
|
label: "";
|
||||||
ellipsize: middle;
|
ellipsize: middle;
|
||||||
margin-end: 6;
|
margin-end: 6;
|
||||||
visible: false;
|
visible: false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,197 +7,197 @@ import os, time
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/snapshot_page/new_snapshot_dialog.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/snapshot_page/new_snapshot_dialog.ui")
|
||||||
class NewSnapshotDialog(Adw.Dialog):
|
class NewSnapshotDialog(Adw.Dialog):
|
||||||
__gtype_name__ = "NewSnapshotDialog"
|
__gtype_name__ = "NewSnapshotDialog"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
nav_page = gtc()
|
nav_page = gtc()
|
||||||
list_cancel_button = gtc()
|
list_cancel_button = gtc()
|
||||||
search_button = gtc()
|
search_button = gtc()
|
||||||
create_button = gtc()
|
create_button = gtc()
|
||||||
search_entry = gtc()
|
search_entry = gtc()
|
||||||
name_entry = gtc()
|
name_entry = gtc()
|
||||||
listbox = gtc()
|
listbox = gtc()
|
||||||
select_all_button = gtc()
|
select_all_button = gtc()
|
||||||
total_selected_label = gtc()
|
total_selected_label = gtc()
|
||||||
scrolled_window = gtc()
|
scrolled_window = gtc()
|
||||||
no_results = gtc()
|
no_results = gtc()
|
||||||
stack = gtc()
|
stack = gtc()
|
||||||
is_open = False
|
is_open = False
|
||||||
|
|
||||||
def row_gesture_handler(self, row):
|
def row_gesture_handler(self, row):
|
||||||
row.check_button.set_active(not row.check_button.get_active())
|
row.check_button.set_active(not row.check_button.get_active())
|
||||||
|
|
||||||
def row_select_handler(self, row):
|
def row_select_handler(self, row):
|
||||||
if row.check_button.get_active():
|
if row.check_button.get_active():
|
||||||
self.selected_rows.append(row)
|
self.selected_rows.append(row)
|
||||||
else:
|
else:
|
||||||
self.selected_rows.remove(row)
|
self.selected_rows.remove(row)
|
||||||
|
|
||||||
total = len(self.selected_rows)
|
total = len(self.selected_rows)
|
||||||
self.total_selected_label.set_label(_("{} Selected").format(total))
|
self.total_selected_label.set_label(_("{} Selected").format(total))
|
||||||
self.total_selected_label.set_visible(total > 0)
|
self.total_selected_label.set_visible(total > 0)
|
||||||
self.valid_checker()
|
self.valid_checker()
|
||||||
|
|
||||||
def generate_list(self, *args):
|
def generate_list(self, *args):
|
||||||
for package in HostInfo.flatpaks:
|
for package in HostInfo.flatpaks:
|
||||||
if "io.github.flattool.Warehouse" in package.info["id"]:
|
if "io.github.flattool.Warehouse" in package.info["id"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if package.is_runtime or not os.path.exists(package.data_path):
|
if package.is_runtime or not os.path.exists(package.data_path):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
row = AppRow(package, self.row_gesture_handler)
|
row = AppRow(package, self.row_gesture_handler)
|
||||||
row.check_button.set_visible(True)
|
row.check_button.set_visible(True)
|
||||||
row.check_button.connect("toggled", lambda *_, row=row: self.row_select_handler(row))
|
row.check_button.connect("toggled", lambda *_, row=row: self.row_select_handler(row))
|
||||||
row.set_activatable(True)
|
row.set_activatable(True)
|
||||||
row.set_activatable_widget(row.check_button)
|
row.set_activatable_widget(row.check_button)
|
||||||
self.listbox.append(row)
|
self.listbox.append(row)
|
||||||
|
|
||||||
def sort_func(self, row1, row2):
|
def sort_func(self, row1, row2):
|
||||||
return row1.package.info["name"].lower() > row2.package.info["name"].lower()
|
return row1.package.info["name"].lower() > row2.package.info["name"].lower()
|
||||||
|
|
||||||
def filter_func(self, row):
|
def filter_func(self, row):
|
||||||
title = row.get_title().lower()
|
title = row.get_title().lower()
|
||||||
subtitle = row.get_subtitle().lower()
|
subtitle = row.get_subtitle().lower()
|
||||||
search = self.search_entry.get_text().lower()
|
search = self.search_entry.get_text().lower()
|
||||||
if search in title or search in subtitle:
|
if search in title or search in subtitle:
|
||||||
self.is_result = True
|
self.is_result = True
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def on_close(self, *args):
|
def on_close(self, *args):
|
||||||
self.__class__.is_open = False
|
self.__class__.is_open = False
|
||||||
self.search_button.set_active(False)
|
self.search_button.set_active(False)
|
||||||
for row in self.selected_rows.copy():
|
for row in self.selected_rows.copy():
|
||||||
GLib.idle_add(lambda *_, row=row: row.check_button.set_active(False))
|
GLib.idle_add(lambda *_, row=row: row.check_button.set_active(False))
|
||||||
|
|
||||||
def valid_checker(self):
|
def valid_checker(self):
|
||||||
text = self.name_entry.get_text().strip()
|
text = self.name_entry.get_text().strip()
|
||||||
something_selected = len(self.selected_rows) > 0
|
something_selected = len(self.selected_rows) > 0
|
||||||
text_good = len(text) > 0 and not("/" in text or "\0" in text)
|
text_good = len(text) > 0 and not("/" in text or "\0" in text)
|
||||||
self.create_button.set_sensitive(something_selected and text_good)
|
self.create_button.set_sensitive(something_selected and text_good)
|
||||||
if text_good:
|
if text_good:
|
||||||
self.name_entry.remove_css_class("error")
|
self.name_entry.remove_css_class("error")
|
||||||
else:
|
else:
|
||||||
self.name_entry.add_css_class("error")
|
self.name_entry.add_css_class("error")
|
||||||
|
|
||||||
return something_selected and text_good
|
return something_selected and text_good
|
||||||
|
|
||||||
def get_total_fraction(self):
|
def get_total_fraction(self):
|
||||||
total = 0
|
total = 0
|
||||||
stopped_workers_amount = 0
|
stopped_workers_amount = 0
|
||||||
for worker in self.workers:
|
for worker in self.workers:
|
||||||
total += worker.fraction
|
total += worker.fraction
|
||||||
if worker.stop:
|
if worker.stop:
|
||||||
stopped_workers_amount += 1
|
stopped_workers_amount += 1
|
||||||
|
|
||||||
if stopped_workers_amount == len(self.workers):
|
if stopped_workers_amount == len(self.workers):
|
||||||
self.loading_status.progress_bar.set_fraction(1)
|
self.loading_status.progress_bar.set_fraction(1)
|
||||||
self.loading_status.progress_label.set_label(f"{len(self.workers)} / {len(self.workers)}")
|
self.loading_status.progress_label.set_label(f"{len(self.workers)} / {len(self.workers)}")
|
||||||
self.workers.clear()
|
self.workers.clear()
|
||||||
if self.on_done:
|
if self.on_done:
|
||||||
self.on_done()
|
self.on_done()
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
self.loading_status.progress_label.set_label(f"{stopped_workers_amount + 1} / {len(self.workers)}")
|
self.loading_status.progress_label.set_label(f"{stopped_workers_amount + 1} / {len(self.workers)}")
|
||||||
self.loading_status.progress_bar.set_fraction(total / len(self.workers))
|
self.loading_status.progress_bar.set_fraction(total / len(self.workers))
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def on_create(self, button):
|
def on_create(self, button):
|
||||||
self.loading_status.title_label.set_label(_("Creating Snapshot"))
|
self.loading_status.title_label.set_label(_("Creating Snapshot"))
|
||||||
self.loading_status.progress_bar.set_fraction(0.0)
|
self.loading_status.progress_bar.set_fraction(0.0)
|
||||||
self.snapshot_page.status_stack.set_visible_child(self.snapshot_page.snapshotting_view)
|
self.snapshot_page.status_stack.set_visible_child(self.snapshot_page.snapshotting_view)
|
||||||
self.workers.clear()
|
self.workers.clear()
|
||||||
for row in self.selected_rows:
|
for row in self.selected_rows:
|
||||||
if "io.github.flattool.Warehouse" in row.package.info["id"]:
|
if "io.github.flattool.Warehouse" in row.package.info["id"]:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
package = row.package
|
package = row.package
|
||||||
worker = TarWorker(
|
worker = TarWorker(
|
||||||
existing_path=package.data_path,
|
existing_path=package.data_path,
|
||||||
new_path=f"{HostInfo.snapshots_path}{package.info['id']}",
|
new_path=f"{HostInfo.snapshots_path}{package.info['id']}",
|
||||||
file_name=f"{int(time.time())}_{package.info["version"]}",
|
file_name=f"{int(time.time())}_{package.info["version"]}",
|
||||||
name=self.name_entry.get_text(),
|
name=self.name_entry.get_text(),
|
||||||
toast_overlay=self.snapshot_page.toast_overlay,
|
toast_overlay=self.snapshot_page.toast_overlay,
|
||||||
)
|
)
|
||||||
self.workers.append(worker)
|
self.workers.append(worker)
|
||||||
worker.compress()
|
worker.compress()
|
||||||
|
|
||||||
self.loading_status.progress_label.set_visible(len(self.workers) > 1)
|
self.loading_status.progress_label.set_visible(len(self.workers) > 1)
|
||||||
GLib.timeout_add(200, self.get_total_fraction)
|
GLib.timeout_add(200, self.get_total_fraction)
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def on_invalidate(self, search_entry):
|
def on_invalidate(self, search_entry):
|
||||||
self.is_result = False
|
self.is_result = False
|
||||||
self.listbox.invalidate_filter()
|
self.listbox.invalidate_filter()
|
||||||
if self.is_result:
|
if self.is_result:
|
||||||
self.stack.set_visible_child(self.scrolled_window)
|
self.stack.set_visible_child(self.scrolled_window)
|
||||||
else:
|
else:
|
||||||
self.stack.set_visible_child(self.no_results)
|
self.stack.set_visible_child(self.no_results)
|
||||||
|
|
||||||
def on_select_all(self, button):
|
def on_select_all(self, button):
|
||||||
i = 0
|
i = 0
|
||||||
while row := self.listbox.get_row_at_index(i):
|
while row := self.listbox.get_row_at_index(i):
|
||||||
i += 1
|
i += 1
|
||||||
row.check_button.set_active(True)
|
row.check_button.set_active(True)
|
||||||
|
|
||||||
def set_packages(self):
|
def set_packages(self):
|
||||||
for package in self.packages:
|
for package in self.packages:
|
||||||
row = AppRow(package)
|
row = AppRow(package)
|
||||||
row.set_activatable(False)
|
row.set_activatable(False)
|
||||||
self.selected_rows.append(row)
|
self.selected_rows.append(row)
|
||||||
self.listbox.append(row)
|
self.listbox.append(row)
|
||||||
|
|
||||||
def enter_handler(self, *args):
|
def enter_handler(self, *args):
|
||||||
if self.create_button.get_sensitive():
|
if self.create_button.get_sensitive():
|
||||||
self.create_button.activate()
|
self.create_button.activate()
|
||||||
|
|
||||||
def present(self, *args, **kwargs):
|
def present(self, *args, **kwargs):
|
||||||
if self.__class__.is_open:
|
if self.__class__.is_open:
|
||||||
return
|
return
|
||||||
|
|
||||||
super().present(*args, **kwargs)
|
super().present(*args, **kwargs)
|
||||||
self.__class__.is_open = True
|
self.__class__.is_open = True
|
||||||
if not self.search_button.get_visible():
|
if not self.search_button.get_visible():
|
||||||
self.name_entry.grab_focus()
|
self.name_entry.grab_focus()
|
||||||
|
|
||||||
def __init__(self, snapshot_page, loading_status, on_done=None, packages=None, **kwargs):
|
def __init__(self, snapshot_page, loading_status, on_done=None, packages=None, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creations
|
# Extra Object Creations
|
||||||
self.snapshot_page = snapshot_page
|
self.snapshot_page = snapshot_page
|
||||||
self.loading_status = loading_status
|
self.loading_status = loading_status
|
||||||
self.on_done = on_done
|
self.on_done = on_done
|
||||||
self.is_result = False
|
self.is_result = False
|
||||||
self.rows = []
|
self.rows = []
|
||||||
self.selected_rows = []
|
self.selected_rows = []
|
||||||
self.workers = []
|
self.workers = []
|
||||||
self.packages = packages
|
self.packages = packages
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.connect("closed", self.on_close)
|
self.connect("closed", self.on_close)
|
||||||
self.create_button.connect("clicked", self.on_create)
|
self.create_button.connect("clicked", self.on_create)
|
||||||
self.search_entry.connect("search-changed", self.on_invalidate)
|
self.search_entry.connect("search-changed", self.on_invalidate)
|
||||||
self.list_cancel_button.connect("clicked", lambda *_: self.close())
|
self.list_cancel_button.connect("clicked", lambda *_: self.close())
|
||||||
self.name_entry.connect("changed", lambda *_: self.valid_checker())
|
self.name_entry.connect("changed", lambda *_: self.valid_checker())
|
||||||
self.name_entry.connect("entry-activated", self.enter_handler)
|
self.name_entry.connect("entry-activated", self.enter_handler)
|
||||||
self.select_all_button.connect("clicked", self.on_select_all)
|
self.select_all_button.connect("clicked", self.on_select_all)
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.listbox.set_sort_func(self.sort_func)
|
self.listbox.set_sort_func(self.sort_func)
|
||||||
self.listbox.set_filter_func(self.filter_func)
|
self.listbox.set_filter_func(self.filter_func)
|
||||||
self.name_entry.set_title(_("Name these Snapshots"))
|
self.name_entry.set_title(_("Name these Snapshots"))
|
||||||
if not packages is None:
|
if not packages is None:
|
||||||
self.search_entry.set_editable(False)
|
self.search_entry.set_editable(False)
|
||||||
self.search_button.set_visible(False)
|
self.search_button.set_visible(False)
|
||||||
self.nav_page.set_title(_("New Snapshot"))
|
self.nav_page.set_title(_("New Snapshot"))
|
||||||
self.set_packages()
|
self.set_packages()
|
||||||
self.no_results.set_visible(False)
|
self.no_results.set_visible(False)
|
||||||
if len(packages) == 1:
|
if len(packages) == 1:
|
||||||
self.name_entry.set_title(_("Name this Snapshot"))
|
self.name_entry.set_title(_("Name this Snapshot"))
|
||||||
else:
|
else:
|
||||||
self.nav_page.set_title(_("New Snapshots"))
|
self.nav_page.set_title(_("New Snapshots"))
|
||||||
self.generate_list()
|
self.generate_list()
|
||||||
|
|||||||
@@ -2,98 +2,98 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $SnapshotBox : Gtk.Box {
|
template $SnapshotBox : Gtk.Box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
spacing: 6;
|
spacing: 6;
|
||||||
Box {
|
Box {
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
margin-top: 6;
|
margin-top: 6;
|
||||||
spacing: 12;
|
spacing: 12;
|
||||||
Box {
|
Box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
Label title {
|
Label title {
|
||||||
label: _("No Name Set");
|
label: _("No Name Set");
|
||||||
wrap: true;
|
wrap: true;
|
||||||
wrap-mode: word_char;
|
wrap-mode: word_char;
|
||||||
justify: left;
|
justify: left;
|
||||||
halign: start;
|
halign: start;
|
||||||
styles ["title-4"]
|
styles ["title-4"]
|
||||||
}
|
}
|
||||||
Label date {
|
Label date {
|
||||||
label: _("No date found");
|
label: _("No date found");
|
||||||
wrap: true;
|
wrap: true;
|
||||||
justify: left;
|
justify: left;
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Label version {
|
Label version {
|
||||||
label: _("No version found");
|
label: _("No version found");
|
||||||
wrap: true;
|
wrap: true;
|
||||||
justify: right;
|
justify: right;
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
halign: end;
|
halign: end;
|
||||||
natural-wrap-mode: none;
|
natural-wrap-mode: none;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Box {
|
Box {
|
||||||
margin-start: 6;
|
margin-start: 6;
|
||||||
margin-end: 6;
|
margin-end: 6;
|
||||||
margin-bottom: 6;
|
margin-bottom: 6;
|
||||||
spacing: 3;
|
spacing: 3;
|
||||||
homogeneous: true;
|
homogeneous: true;
|
||||||
Button apply_button {
|
Button apply_button {
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
label: _("Apply");
|
label: _("Apply");
|
||||||
icon-name: "check-plain-symbolic";
|
icon-name: "check-plain-symbolic";
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
styles ["flat"]
|
styles ["flat"]
|
||||||
}
|
}
|
||||||
MenuButton rename_button {
|
MenuButton rename_button {
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
label: _("Rename");
|
label: _("Rename");
|
||||||
icon-name: "edit-symbolic";
|
icon-name: "edit-symbolic";
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
styles ["flat"]
|
styles ["flat"]
|
||||||
popover: rename_menu;
|
popover: rename_menu;
|
||||||
}
|
}
|
||||||
Button trash_button {
|
Button trash_button {
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
label: _("Trash");
|
label: _("Trash");
|
||||||
icon-name: "user-trash-symbolic";
|
icon-name: "user-trash-symbolic";
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
styles ["flat"]
|
styles ["flat"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Popover rename_menu {
|
Popover rename_menu {
|
||||||
Box {
|
Box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
spacing: 11;
|
spacing: 11;
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
margin-top: 5;
|
margin-top: 5;
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
Label {
|
Label {
|
||||||
label: _("Rename Snapshot?");
|
label: _("Rename Snapshot?");
|
||||||
styles ["title-2"]
|
styles ["title-2"]
|
||||||
}
|
}
|
||||||
Box {
|
Box {
|
||||||
spacing: 6;
|
spacing: 6;
|
||||||
Entry rename_entry {
|
Entry rename_entry {
|
||||||
text: bind title.label;
|
text: bind title.label;
|
||||||
}
|
}
|
||||||
Button apply_rename {
|
Button apply_rename {
|
||||||
icon-name: "check-plain-symbolic";
|
icon-name: "check-plain-symbolic";
|
||||||
tooltip-text: _("Confirm Rename");
|
tooltip-text: _("Confirm Rename");
|
||||||
styles ["circular", "suggested-action"]
|
styles ["circular", "suggested-action"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,184 +6,184 @@ import os, subprocess, json
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/snapshot_page/snapshot_box.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/snapshot_page/snapshot_box.ui")
|
||||||
class SnapshotBox(Gtk.Box):
|
class SnapshotBox(Gtk.Box):
|
||||||
__gtype_name__ = "SnapshotBox"
|
__gtype_name__ = "SnapshotBox"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
title = gtc()
|
title = gtc()
|
||||||
date = gtc()
|
date = gtc()
|
||||||
version = gtc()
|
version = gtc()
|
||||||
apply_button = gtc()
|
apply_button = gtc()
|
||||||
rename_button = gtc()
|
rename_button = gtc()
|
||||||
rename_menu = gtc()
|
rename_menu = gtc()
|
||||||
rename_entry = gtc()
|
rename_entry = gtc()
|
||||||
apply_rename = gtc()
|
apply_rename = gtc()
|
||||||
trash_button = gtc()
|
trash_button = gtc()
|
||||||
|
|
||||||
def create_json(self):
|
def create_json(self):
|
||||||
try:
|
try:
|
||||||
data = {
|
data = {
|
||||||
'snapshot_version': 1,
|
'snapshot_version': 1,
|
||||||
'name': '',
|
'name': '',
|
||||||
}
|
}
|
||||||
with open(self.json_path, 'w') as file:
|
with open(self.json_path, 'w') as file:
|
||||||
json.dump(data, file, indent=4)
|
json.dump(data, file, indent=4)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)
|
||||||
|
|
||||||
def update_json(self, key, value):
|
def update_json(self, key, value):
|
||||||
try:
|
try:
|
||||||
with open(self.json_path, 'r+') as file:
|
with open(self.json_path, 'r+') as file:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
data[key] = value
|
data[key] = value
|
||||||
file.seek(0)
|
file.seek(0)
|
||||||
json.dump(data, file, indent=4)
|
json.dump(data, file, indent=4)
|
||||||
file.truncate()
|
file.truncate()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)
|
||||||
|
|
||||||
def load_from_json(self):
|
def load_from_json(self):
|
||||||
if not os.path.exists(self.json_path):
|
if not os.path.exists(self.json_path):
|
||||||
self.create_json()
|
self.create_json()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
with open(self.json_path, 'r') as file:
|
with open(self.json_path, 'r') as file:
|
||||||
data = json.load(file)
|
data = json.load(file)
|
||||||
name = data['name']
|
name = data['name']
|
||||||
if name != "":
|
if name != "":
|
||||||
self.title.set_label(GLib.markup_escape_text(name))
|
self.title.set_label(GLib.markup_escape_text(name))
|
||||||
else:
|
else:
|
||||||
self.title.set_label(_("No Name Set"))
|
self.title.set_label(_("No Name Set"))
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not write data"), str(e)).toast)
|
||||||
|
|
||||||
def on_rename(self, widget):
|
def on_rename(self, widget):
|
||||||
if not self.valid_checker():
|
if not self.valid_checker():
|
||||||
return
|
return
|
||||||
|
|
||||||
self.update_json('name', self.rename_entry.get_text().strip())
|
self.update_json('name', self.rename_entry.get_text().strip())
|
||||||
self.load_from_json()
|
self.load_from_json()
|
||||||
self.rename_menu.popdown()
|
self.rename_menu.popdown()
|
||||||
|
|
||||||
def valid_checker(self, *args):
|
def valid_checker(self, *args):
|
||||||
text = self.rename_entry.get_text().strip()
|
text = self.rename_entry.get_text().strip()
|
||||||
valid = not ("/" in text or "\0" in text) and len(text) > 0
|
valid = not ("/" in text or "\0" in text) and len(text) > 0
|
||||||
self.apply_rename.set_sensitive(valid)
|
self.apply_rename.set_sensitive(valid)
|
||||||
if valid:
|
if valid:
|
||||||
self.rename_entry.remove_css_class("error")
|
self.rename_entry.remove_css_class("error")
|
||||||
else:
|
else:
|
||||||
self.rename_entry.add_css_class("error")
|
self.rename_entry.add_css_class("error")
|
||||||
|
|
||||||
return valid
|
return valid
|
||||||
|
|
||||||
def on_trash(self, button):
|
def on_trash(self, button):
|
||||||
error = [None]
|
error = [None]
|
||||||
path = f"{self.snapshots_path}{self.folder}"
|
path = f"{self.snapshots_path}{self.folder}"
|
||||||
if self.snapshot_page.is_trash_dialog_open:
|
if self.snapshot_page.is_trash_dialog_open:
|
||||||
return
|
return
|
||||||
|
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
try:
|
try:
|
||||||
subprocess.run(['gio', 'trash', path], capture_output=True, text=True, check=True)
|
subprocess.run(['gio', 'trash', path], capture_output=True, text=True, check=True)
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
error[0] = cpe.stderr
|
error[0] = cpe.stderr
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error[0] = str(e)
|
error[0] = str(e)
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if not error[0] is None:
|
if not error[0] is None:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash snapshot"), error[0]).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash snapshot"), error[0]).toast)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.parent_page.on_trash()
|
self.parent_page.on_trash()
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new(_("Trashed snapshot")))
|
self.toast_overlay.add_toast(Adw.Toast.new(_("Trashed snapshot")))
|
||||||
|
|
||||||
def on_response(_, response):
|
def on_response(_, response):
|
||||||
self.snapshot_page.is_trash_dialog_open = False
|
self.snapshot_page.is_trash_dialog_open = False
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
self.snapshot_page.is_trash_dialog_open = True
|
self.snapshot_page.is_trash_dialog_open = True
|
||||||
dialog = Adw.AlertDialog(heading=_("Trash Snapshot?"), body=_("This snapshot will be sent to the trash"))
|
dialog = Adw.AlertDialog(heading=_("Trash Snapshot?"), body=_("This snapshot will be sent to the trash"))
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
dialog.add_response("continue", _("Trash"))
|
dialog.add_response("continue", _("Trash"))
|
||||||
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(HostInfo.main_window)
|
dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def get_fraction(self):
|
def get_fraction(self):
|
||||||
loading_status = self.snapshot_page.snapshotting_status
|
loading_status = self.snapshot_page.snapshotting_status
|
||||||
loading_status.progress_bar.set_fraction(self.worker.fraction)
|
loading_status.progress_bar.set_fraction(self.worker.fraction)
|
||||||
if self.worker.stop:
|
if self.worker.stop:
|
||||||
self.snapshot_page.status_stack.set_visible_child(self.snapshot_page.split_view)
|
self.snapshot_page.status_stack.set_visible_child(self.snapshot_page.split_view)
|
||||||
self.parent_page.set_snapshots(self.parent_page.package_or_folder, True)
|
self.parent_page.set_snapshots(self.parent_page.package_or_folder, True)
|
||||||
properties_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].properties_page
|
properties_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].properties_page
|
||||||
properties_page.set_properties(properties_page.package, True)
|
properties_page.set_properties(properties_page.package, True)
|
||||||
data_page = HostInfo.main_window.pages[HostInfo.main_window.user_data_row]
|
data_page = HostInfo.main_window.pages[HostInfo.main_window.user_data_row]
|
||||||
data_page.start_loading()
|
data_page.start_loading()
|
||||||
data_page.end_loading()
|
data_page.end_loading()
|
||||||
if self.worker in self.snapshot_page.workers:
|
if self.worker in self.snapshot_page.workers:
|
||||||
self.snapshot_page.workers.remove(self.worker)
|
self.snapshot_page.workers.remove(self.worker)
|
||||||
|
|
||||||
return False # Stop the timeout
|
return False # Stop the timeout
|
||||||
else:
|
else:
|
||||||
return True # Continue the timeout
|
return True # Continue the timeout
|
||||||
|
|
||||||
def on_apply(self, button):
|
def on_apply(self, button):
|
||||||
def on_response(dialog, response):
|
def on_response(dialog, response):
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
self.snapshot_page.snapshotting_status.title_label.set_label(_("Applying Snapshot"))
|
self.snapshot_page.snapshotting_status.title_label.set_label(_("Applying Snapshot"))
|
||||||
self.snapshot_page.snapshotting_status.progress_label.set_visible(False)
|
self.snapshot_page.snapshotting_status.progress_label.set_visible(False)
|
||||||
self.snapshot_page.snapshotting_status.progress_bar.set_fraction(0.0)
|
self.snapshot_page.snapshotting_status.progress_bar.set_fraction(0.0)
|
||||||
self.snapshot_page.status_stack.set_visible_child(self.snapshot_page.snapshotting_view)
|
self.snapshot_page.status_stack.set_visible_child(self.snapshot_page.snapshotting_view)
|
||||||
self.snapshot_page.workers.append(self.worker)
|
self.snapshot_page.workers.append(self.worker)
|
||||||
self.worker.extract()
|
self.worker.extract()
|
||||||
GLib.timeout_add(200, self.get_fraction)
|
GLib.timeout_add(200, self.get_fraction)
|
||||||
|
|
||||||
has_data = os.path.exists(self.worker.new_path)
|
has_data = os.path.exists(self.worker.new_path)
|
||||||
dialog = Adw.AlertDialog(
|
dialog = Adw.AlertDialog(
|
||||||
heading=_("Apply Snapshot?"),
|
heading=_("Apply Snapshot?"),
|
||||||
body=_("Any current user data for this app will be trashed") if has_data else "",
|
body=_("Any current user data for this app will be trashed") if has_data else "",
|
||||||
)
|
)
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
dialog.add_response("continue", _("Apply"))
|
dialog.add_response("continue", _("Apply"))
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(HostInfo.main_window)
|
dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def __init__(self, parent_page, folder, snapshots_path, toast_overlay, **kwargs):
|
def __init__(self, parent_page, folder, snapshots_path, toast_overlay, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
self.snapshot_page = parent_page.parent_page
|
self.snapshot_page = parent_page.parent_page
|
||||||
self.toast_overlay = toast_overlay
|
self.toast_overlay = toast_overlay
|
||||||
self.app_id = snapshots_path.split('/')[-2].strip()
|
self.app_id = snapshots_path.split('/')[-2].strip()
|
||||||
self.worker = TarWorker(
|
self.worker = TarWorker(
|
||||||
existing_path=f"{snapshots_path}{folder}",
|
existing_path=f"{snapshots_path}{folder}",
|
||||||
new_path=f"{HostInfo.home}/.var/app/{self.app_id}/",
|
new_path=f"{HostInfo.home}/.var/app/{self.app_id}/",
|
||||||
toast_overlay=self.toast_overlay,
|
toast_overlay=self.toast_overlay,
|
||||||
)
|
)
|
||||||
|
|
||||||
split_folder = folder.split('_')
|
split_folder = folder.split('_')
|
||||||
if len(split_folder) < 2:
|
if len(split_folder) < 2:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.parent_page = parent_page
|
self.parent_page = parent_page
|
||||||
self.folder = folder
|
self.folder = folder
|
||||||
self.snapshots_path = snapshots_path
|
self.snapshots_path = snapshots_path
|
||||||
self.epoch = int(split_folder[0])
|
self.epoch = int(split_folder[0])
|
||||||
date_data = GLib.DateTime.new_from_unix_local(self.epoch).format("%x %X")
|
date_data = GLib.DateTime.new_from_unix_local(self.epoch).format("%x %X")
|
||||||
self.date.set_label(date_data)
|
self.date.set_label(date_data)
|
||||||
self.version.set_label(_("Version: {}").format(split_folder[1].replace(".tar.zst", "")))
|
self.version.set_label(_("Version: {}").format(split_folder[1].replace(".tar.zst", "")))
|
||||||
self.json_path = f"{snapshots_path}{folder.replace('tar.zst', 'json')}"
|
self.json_path = f"{snapshots_path}{folder.replace('tar.zst', 'json')}"
|
||||||
self.load_from_json()
|
self.load_from_json()
|
||||||
self.apply_button.connect("clicked", self.on_apply)
|
self.apply_button.connect("clicked", self.on_apply)
|
||||||
self.apply_rename.connect("clicked", self.on_rename)
|
self.apply_rename.connect("clicked", self.on_rename)
|
||||||
self.rename_entry.connect("activate", self.on_rename)
|
self.rename_entry.connect("activate", self.on_rename)
|
||||||
self.rename_entry.connect("changed", self.valid_checker)
|
self.rename_entry.connect("changed", self.valid_checker)
|
||||||
self.trash_button.connect("clicked", self.on_trash)
|
self.trash_button.connect("clicked", self.on_trash)
|
||||||
|
|||||||
@@ -2,234 +2,234 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $SnapshotPage : Adw.BreakpointBin {
|
template $SnapshotPage : Adw.BreakpointBin {
|
||||||
width-request: 1;
|
width-request: 1;
|
||||||
height-request: 1;
|
height-request: 1;
|
||||||
|
|
||||||
Adw.Breakpoint bp1 {
|
Adw.Breakpoint bp1 {
|
||||||
condition ("max-width: 600")
|
condition ("max-width: 600")
|
||||||
|
|
||||||
setters {
|
setters {
|
||||||
split_view.collapsed: true;
|
split_view.collapsed: true;
|
||||||
split_view.show-content: false;
|
split_view.show-content: false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Adw.NavigationPage {
|
Adw.NavigationPage {
|
||||||
title: _("Snapshots");
|
title: _("Snapshots");
|
||||||
Adw.ToastOverlay toast_overlay {
|
Adw.ToastOverlay toast_overlay {
|
||||||
Stack status_stack {
|
Stack status_stack {
|
||||||
Adw.NavigationSplitView split_view {
|
Adw.NavigationSplitView split_view {
|
||||||
sidebar-width-fraction: 0.5;
|
sidebar-width-fraction: 0.5;
|
||||||
max-sidebar-width: 999999999;
|
max-sidebar-width: 999999999;
|
||||||
sidebar:
|
sidebar:
|
||||||
Adw.NavigationPage sidebar_navpage {
|
Adw.NavigationPage sidebar_navpage {
|
||||||
title: _("Snapshots");
|
title: _("Snapshots");
|
||||||
Adw.ToolbarView sidebar_tbv {
|
Adw.ToolbarView sidebar_tbv {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar header_bar {
|
Adw.HeaderBar header_bar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
[start]
|
[start]
|
||||||
ToggleButton search_button {
|
ToggleButton search_button {
|
||||||
icon-name: "loupe-large-symbolic";
|
icon-name: "loupe-large-symbolic";
|
||||||
tooltip-text: _("Search Packages");
|
tooltip-text: _("Search Packages");
|
||||||
}
|
}
|
||||||
[end]
|
[end]
|
||||||
Button new_button {
|
Button new_button {
|
||||||
icon-name: "plus-large-symbolic";
|
icon-name: "plus-large-symbolic";
|
||||||
tooltip-text: _("New Snapshot");
|
tooltip-text: _("New Snapshot");
|
||||||
}
|
}
|
||||||
[end]
|
[end]
|
||||||
ToggleButton select_button {
|
ToggleButton select_button {
|
||||||
icon-name: "selection-mode-symbolic";
|
icon-name: "selection-mode-symbolic";
|
||||||
tooltip-text: _("Select Packages");
|
tooltip-text: _("Select Packages");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[top]
|
[top]
|
||||||
SearchBar search_bar {
|
SearchBar search_bar {
|
||||||
search-mode-enabled: bind search_button.active bidirectional;
|
search-mode-enabled: bind search_button.active bidirectional;
|
||||||
SearchEntry search_entry {
|
SearchEntry search_entry {
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
placeholder-text: _("Search Snapshots");
|
placeholder-text: _("Search Snapshots");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stack stack {
|
Stack stack {
|
||||||
Adw.StatusPage no_results {
|
Adw.StatusPage no_results {
|
||||||
title: _("No Results Found");
|
title: _("No Results Found");
|
||||||
description: _("Try a different search");
|
description: _("Try a different search");
|
||||||
icon-name: "system-search-symbolic";
|
icon-name: "system-search-symbolic";
|
||||||
}
|
}
|
||||||
ScrolledWindow scrolled_window {
|
ScrolledWindow scrolled_window {
|
||||||
Box {
|
Box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
|
|
||||||
Box active_box {
|
Box active_box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
label: _("Active Snapshots");
|
label: _("Active Snapshots");
|
||||||
halign: start;
|
halign: start;
|
||||||
styles ["heading"]
|
styles ["heading"]
|
||||||
margin-top: 3;
|
margin-top: 3;
|
||||||
margin-bottom: 6;
|
margin-bottom: 6;
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
wrap: true;
|
wrap: true;
|
||||||
wrap-mode: word_char;
|
wrap-mode: word_char;
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
label: _("Snapshots of installed apps");
|
label: _("Snapshots of installed apps");
|
||||||
halign: start;
|
halign: start;
|
||||||
styles ["dim-label"]
|
styles ["dim-label"]
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
margin-bottom: 3;
|
margin-bottom: 3;
|
||||||
wrap: true;
|
wrap: true;
|
||||||
wrap-mode: word_char;
|
wrap-mode: word_char;
|
||||||
}
|
}
|
||||||
ListBox active_listbox {
|
ListBox active_listbox {
|
||||||
styles ["navigation-sidebar"]
|
styles ["navigation-sidebar"]
|
||||||
valign: start;
|
valign: start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Box leftover_box {
|
Box leftover_box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
|
|
||||||
Label {
|
Label {
|
||||||
label: _("Leftover Snapshots");
|
label: _("Leftover Snapshots");
|
||||||
halign: start;
|
halign: start;
|
||||||
styles ["heading"]
|
styles ["heading"]
|
||||||
margin-top: 3;
|
margin-top: 3;
|
||||||
margin-bottom: 6;
|
margin-bottom: 6;
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
wrap: true;
|
wrap: true;
|
||||||
wrap-mode: word_char;
|
wrap-mode: word_char;
|
||||||
}
|
}
|
||||||
Label {
|
Label {
|
||||||
label: _("Snapshots of apps that are no longer installed");
|
label: _("Snapshots of apps that are no longer installed");
|
||||||
halign: start;
|
halign: start;
|
||||||
styles ["dim-label"]
|
styles ["dim-label"]
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
margin-bottom: 3;
|
margin-bottom: 3;
|
||||||
wrap: true;
|
wrap: true;
|
||||||
wrap-mode: word_char;
|
wrap-mode: word_char;
|
||||||
}
|
}
|
||||||
ListBox leftover_listbox {
|
ListBox leftover_listbox {
|
||||||
styles ["navigation-sidebar"]
|
styles ["navigation-sidebar"]
|
||||||
valign: start;
|
valign: start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[bottom]
|
[bottom]
|
||||||
Revealer {
|
Revealer {
|
||||||
reveal-child: bind select_button.active;
|
reveal-child: bind select_button.active;
|
||||||
transition-type: slide_up;
|
transition-type: slide_up;
|
||||||
[center]
|
[center]
|
||||||
Box bottom_bar {
|
Box bottom_bar {
|
||||||
styles ["toolbar"]
|
styles ["toolbar"]
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
homogeneous: true;
|
homogeneous: true;
|
||||||
Button select_all_button {
|
Button select_all_button {
|
||||||
styles ["raised"]
|
styles ["raised"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "selection-mode-symbolic";
|
icon-name: "selection-mode-symbolic";
|
||||||
label: _("Select All");
|
label: _("Select All");
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button copy_button {
|
Button copy_button {
|
||||||
sensitive: false;
|
sensitive: false;
|
||||||
styles ["raised"]
|
styles ["raised"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "edit-copy-symbolic";
|
icon-name: "edit-copy-symbolic";
|
||||||
label: _("Copy");
|
label: _("Copy");
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuButton more_button {
|
MenuButton more_button {
|
||||||
sensitive: false;
|
sensitive: false;
|
||||||
popover: more_popover;
|
popover: more_popover;
|
||||||
styles ["raised"]
|
styles ["raised"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "view-more-symbolic";
|
icon-name: "view-more-symbolic";
|
||||||
label: _("More");
|
label: _("More");
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
Adw.ToolbarView no_snapshots {
|
Adw.ToolbarView no_snapshots {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
[start]
|
[start]
|
||||||
Button status_open_button {
|
Button status_open_button {
|
||||||
icon-name: "folder-open-symbolic";
|
icon-name: "folder-open-symbolic";
|
||||||
tooltip-text: _("Open Snapshots Folder");
|
tooltip-text: _("Open Snapshots Folder");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ToastOverlay no_snapshots_toast {
|
Adw.ToastOverlay no_snapshots_toast {
|
||||||
Adw.StatusPage {
|
Adw.StatusPage {
|
||||||
title: _("No Snapshots");
|
title: _("No Snapshots");
|
||||||
description: _("Create a Snapshot to save the state of any Flatpak application");
|
description: _("Create a Snapshot to save the state of any Flatpak application");
|
||||||
icon-name: "snapshots-alt-symbolic";
|
icon-name: "snapshots-alt-symbolic";
|
||||||
Button status_new_button {
|
Button status_new_button {
|
||||||
styles ["suggested-action", "pill"]
|
styles ["suggested-action", "pill"]
|
||||||
halign: center;
|
halign: center;
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "plus-large-symbolic";
|
icon-name: "plus-large-symbolic";
|
||||||
label: _("New Snapshot");
|
label: _("New Snapshot");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ToolbarView loading_view {
|
Adw.ToolbarView loading_view {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ToolbarView snapshotting_view {
|
Adw.ToolbarView snapshotting_view {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Popover more_popover {
|
Popover more_popover {
|
||||||
styles ["menu"]
|
styles ["menu"]
|
||||||
ListBox more_menu {
|
ListBox more_menu {
|
||||||
Label new_snapshots {
|
Label new_snapshots {
|
||||||
label: _("Snapshot Apps");
|
label: _("Snapshot Apps");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
Label install_from_snapshots {
|
Label install_from_snapshots {
|
||||||
label: _("Install Apps");
|
label: _("Install Apps");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
Label apply_snapshots {
|
Label apply_snapshots {
|
||||||
label: _("Apply Snapshots");
|
label: _("Apply Snapshots");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
Label trash_snapshots {
|
Label trash_snapshots {
|
||||||
label: _("Trash Snapshots");
|
label: _("Trash Snapshots");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -2,46 +2,46 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $SnapshotsListPage : Adw.NavigationPage {
|
template $SnapshotsListPage : Adw.NavigationPage {
|
||||||
title: _("Snapshots List");
|
title: _("Snapshots List");
|
||||||
Adw.ToastOverlay toast_overlay {
|
Adw.ToastOverlay toast_overlay {
|
||||||
Adw.ToolbarView toolbar_view {
|
Adw.ToolbarView toolbar_view {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[start]
|
[start]
|
||||||
Button open_button {
|
Button open_button {
|
||||||
icon-name: "folder-open-symbolic";
|
icon-name: "folder-open-symbolic";
|
||||||
tooltip-text: _("Open Snapshots Folder for this App");
|
tooltip-text: _("Open Snapshots Folder for this App");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ScrolledWindow {
|
ScrolledWindow {
|
||||||
Adw.Clamp {
|
Adw.Clamp {
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
margin-top: 12;
|
margin-top: 12;
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
ListBox listbox {
|
ListBox listbox {
|
||||||
valign: start;
|
valign: start;
|
||||||
selection-mode: none;
|
selection-mode: none;
|
||||||
styles ["boxed-list"]
|
styles ["boxed-list"]
|
||||||
Adw.PreferencesGroup {
|
Adw.PreferencesGroup {
|
||||||
Adw.ActionRow {title: "test";}
|
Adw.ActionRow {title: "test";}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[bottom]
|
[bottom]
|
||||||
ActionBar {
|
ActionBar {
|
||||||
[center]
|
[center]
|
||||||
Button new_button {
|
Button new_button {
|
||||||
margin-top: 3;
|
margin-top: 3;
|
||||||
margin-bottom: 3;
|
margin-bottom: 3;
|
||||||
styles ["pill", "suggested-action"]
|
styles ["pill", "suggested-action"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "plus-large-symbolic";
|
icon-name: "plus-large-symbolic";
|
||||||
label: _("New Snapshot");
|
label: _("New Snapshot");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,103 +8,103 @@ import os
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/snapshot_page/snapshots_list_page.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/snapshot_page/snapshots_list_page.ui")
|
||||||
class SnapshotsListPage(Adw.NavigationPage):
|
class SnapshotsListPage(Adw.NavigationPage):
|
||||||
__gtype_name__ = "SnapshotsListPage"
|
__gtype_name__ = "SnapshotsListPage"
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
toolbar_view = gtc()
|
toolbar_view = gtc()
|
||||||
listbox = gtc()
|
listbox = gtc()
|
||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
open_button = gtc()
|
open_button = gtc()
|
||||||
new_button = gtc()
|
new_button = gtc()
|
||||||
|
|
||||||
def thread(self, *args):
|
def thread(self, *args):
|
||||||
is_leftover = type(self.package_or_folder) is str
|
is_leftover = type(self.package_or_folder) is str
|
||||||
for snapshot in os.listdir(folder := f"{self.snapshots_path}{self.current_folder}/"):
|
for snapshot in os.listdir(folder := f"{self.snapshots_path}{self.current_folder}/"):
|
||||||
if snapshot.endswith(".json"):
|
if snapshot.endswith(".json"):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
row = SnapshotBox(self, snapshot, folder, self.toast_overlay)
|
row = SnapshotBox(self, snapshot, folder, self.toast_overlay)
|
||||||
row.apply_button.set_sensitive(not is_leftover)
|
row.apply_button.set_sensitive(not is_leftover)
|
||||||
self.snapshots_rows.append(row)
|
self.snapshots_rows.append(row)
|
||||||
if is_leftover:
|
if is_leftover:
|
||||||
row.apply_button.set_tooltip_text(_("App not Installed"))
|
row.apply_button.set_tooltip_text(_("App not Installed"))
|
||||||
|
|
||||||
def callback(self, *args):
|
def callback(self, *args):
|
||||||
if len(self.snapshots_rows) == 0:
|
if len(self.snapshots_rows) == 0:
|
||||||
self.parent_page.refresh()
|
self.parent_page.refresh()
|
||||||
return
|
return
|
||||||
|
|
||||||
for i, row in enumerate(self.snapshots_rows):
|
for i, row in enumerate(self.snapshots_rows):
|
||||||
self.listbox.append(row)
|
self.listbox.append(row)
|
||||||
self.listbox.get_row_at_index(i).set_activatable(False)
|
self.listbox.get_row_at_index(i).set_activatable(False)
|
||||||
|
|
||||||
def set_snapshots(self, package_or_folder, refresh=False):
|
def set_snapshots(self, package_or_folder, refresh=False):
|
||||||
if package_or_folder == self.package_or_folder and not refresh:
|
if package_or_folder == self.package_or_folder and not refresh:
|
||||||
return
|
return
|
||||||
|
|
||||||
folder = None
|
folder = None
|
||||||
self.package_or_folder = package_or_folder
|
self.package_or_folder = package_or_folder
|
||||||
if type(package_or_folder) is str:
|
if type(package_or_folder) is str:
|
||||||
self.set_title(package_or_folder)
|
self.set_title(package_or_folder)
|
||||||
folder = package_or_folder
|
folder = package_or_folder
|
||||||
self.new_button.set_sensitive(False)
|
self.new_button.set_sensitive(False)
|
||||||
self.new_button.set_tooltip_text(_("App not Installed"))
|
self.new_button.set_tooltip_text(_("App not Installed"))
|
||||||
else:
|
else:
|
||||||
folder = package_or_folder.info["id"]
|
folder = package_or_folder.info["id"]
|
||||||
self.set_title(_("{} Snapshots").format(package_or_folder.info["name"]))
|
self.set_title(_("{} Snapshots").format(package_or_folder.info["name"]))
|
||||||
if os.path.exists(package_or_folder.data_path):
|
if os.path.exists(package_or_folder.data_path):
|
||||||
self.new_button.set_sensitive(True)
|
self.new_button.set_sensitive(True)
|
||||||
self.new_button.set_tooltip_text(None)
|
self.new_button.set_tooltip_text(None)
|
||||||
else:
|
else:
|
||||||
self.new_button.set_sensitive(False)
|
self.new_button.set_sensitive(False)
|
||||||
self.new_button.set_tooltip_text(_("No Data Found to Snapshot"))
|
self.new_button.set_tooltip_text(_("No Data Found to Snapshot"))
|
||||||
|
|
||||||
self.current_folder = folder
|
self.current_folder = folder
|
||||||
self.snapshots_rows.clear()
|
self.snapshots_rows.clear()
|
||||||
self.listbox.remove_all()
|
self.listbox.remove_all()
|
||||||
|
|
||||||
Gio.Task.new(None, None, self.callback).run_in_thread(self.thread)
|
Gio.Task.new(None, None, self.callback).run_in_thread(self.thread)
|
||||||
|
|
||||||
def open_snapshots_folder(self, button):
|
def open_snapshots_folder(self, button):
|
||||||
path = f"{self.snapshots_path}{self.current_folder}/"
|
path = f"{self.snapshots_path}{self.current_folder}/"
|
||||||
try:
|
try:
|
||||||
if not os.path.exists(path):
|
if not os.path.exists(path):
|
||||||
raise Exception(f"error: File '{path}' does not exist")
|
raise Exception(f"error: File '{path}' does not exist")
|
||||||
|
|
||||||
Gio.AppInfo.launch_default_for_uri(f"file://{path}", None)
|
Gio.AppInfo.launch_default_for_uri(f"file://{path}", None)
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new(_("Opened snapshots folder")))
|
self.toast_overlay.add_toast(Adw.Toast.new(_("Opened snapshots folder")))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open folder"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open folder"), str(e)).toast)
|
||||||
|
|
||||||
def on_done(self):
|
def on_done(self):
|
||||||
self.parent_page.status_stack.set_visible_child(self.parent_page.split_view)
|
self.parent_page.status_stack.set_visible_child(self.parent_page.split_view)
|
||||||
self.set_snapshots(self.package_or_folder, refresh=True)
|
self.set_snapshots(self.package_or_folder, refresh=True)
|
||||||
|
|
||||||
def on_new(self, button):
|
def on_new(self, button):
|
||||||
self.parent_page.new_snapshot_dialog = NewSnapshotDialog(self.parent_page, self.parent_page.snapshotting_status, self.on_done, [self.package_or_folder])
|
self.parent_page.new_snapshot_dialog = NewSnapshotDialog(self.parent_page, self.parent_page.snapshotting_status, self.on_done, [self.package_or_folder])
|
||||||
self.parent_page.new_snapshot_dialog.present(HostInfo.main_window)
|
self.parent_page.new_snapshot_dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def sort_func(self, row1, row2):
|
def sort_func(self, row1, row2):
|
||||||
row1 = row1.get_child()
|
row1 = row1.get_child()
|
||||||
row2 = row2.get_child()
|
row2 = row2.get_child()
|
||||||
return row1.epoch > row2.epoch
|
return row1.epoch > row2.epoch
|
||||||
|
|
||||||
def on_trash(self):
|
def on_trash(self):
|
||||||
self.set_snapshots(self.package_or_folder, refresh=True)
|
self.set_snapshots(self.package_or_folder, refresh=True)
|
||||||
|
|
||||||
def __init__(self, parent_page, **kwargs):
|
def __init__(self, parent_page, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.parent_page = parent_page
|
self.parent_page = parent_page
|
||||||
self.snapshots_path = HostInfo.snapshots_path
|
self.snapshots_path = HostInfo.snapshots_path
|
||||||
self.current_folder = None
|
self.current_folder = None
|
||||||
self.package_or_folder = None
|
self.package_or_folder = None
|
||||||
self.snapshots_rows = []
|
self.snapshots_rows = []
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.open_button.connect("clicked", self.open_snapshots_folder)
|
self.open_button.connect("clicked", self.open_snapshots_folder)
|
||||||
self.new_button.connect("clicked", self.on_new)
|
self.new_button.connect("clicked", self.on_new)
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.listbox.set_sort_func(self.sort_func)
|
self.listbox.set_sort_func(self.sort_func)
|
||||||
|
|||||||
@@ -4,116 +4,116 @@ from .error_toast import ErrorToast
|
|||||||
import os, subprocess, json
|
import os, subprocess, json
|
||||||
|
|
||||||
class TarWorker:
|
class TarWorker:
|
||||||
def compress_thread(self, *args):
|
def compress_thread(self, *args):
|
||||||
try:
|
try:
|
||||||
if not os.path.exists(self.new_path):
|
if not os.path.exists(self.new_path):
|
||||||
os.makedirs(self.new_path)
|
os.makedirs(self.new_path)
|
||||||
|
|
||||||
self.total = int(subprocess.run(['du', '-s', self.existing_path], check=True, text=True, capture_output=True).stdout.split('\t')[0])
|
self.total = int(subprocess.run(['du', '-s', self.existing_path], check=True, text=True, capture_output=True).stdout.split('\t')[0])
|
||||||
self.total /= 2.2 # estimate for space savings
|
self.total /= 2.2 # estimate for space savings
|
||||||
self.process = subprocess.Popen(['tar', 'cafv', f'{self.new_path}/{self.file_name}.tar.zst', '-C', self.existing_path, '.'],
|
self.process = subprocess.Popen(['tar', 'cafv', f'{self.new_path}/{self.file_name}.tar.zst', '-C', self.existing_path, '.'],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE
|
stderr=subprocess.PIPE
|
||||||
)
|
)
|
||||||
stdout, stderr = self.process.communicate()
|
stdout, stderr = self.process.communicate()
|
||||||
if self.process.returncode != 0:
|
if self.process.returncode != 0:
|
||||||
raise subprocess.CalledProcessError(self.process.returncode, self.process.args, output=stdout, stderr=stderr)
|
raise subprocess.CalledProcessError(self.process.returncode, self.process.args, output=stdout, stderr=stderr)
|
||||||
|
|
||||||
with open(f"{self.new_path}/{self.file_name}.json", 'w') as file:
|
with open(f"{self.new_path}/{self.file_name}.json", 'w') as file:
|
||||||
data = {
|
data = {
|
||||||
'snapshot_version': 1,
|
'snapshot_version': 1,
|
||||||
'name': self.name,
|
'name': self.name,
|
||||||
}
|
}
|
||||||
json.dump(data, file, indent=4)
|
json.dump(data, file, indent=4)
|
||||||
|
|
||||||
self.stop = True # tell the check timeout to stop, because we know the file is done being made
|
self.stop = True # tell the check timeout to stop, because we know the file is done being made
|
||||||
HostInfo.main_window.remove_refresh_lockout("managing snapshot")
|
HostInfo.main_window.remove_refresh_lockout("managing snapshot")
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.do_cancel(cpe.stderr.decode()) # stderr is in bytes, so decode it
|
self.do_cancel(cpe.stderr.decode()) # stderr is in bytes, so decode it
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.do_cancel(str(e))
|
self.do_cancel(str(e))
|
||||||
|
|
||||||
def extract_thread(self, *args):
|
def extract_thread(self, *args):
|
||||||
try:
|
try:
|
||||||
if os.path.exists(self.new_path):
|
if os.path.exists(self.new_path):
|
||||||
subprocess.run(['gio', 'trash', self.new_path], capture_output=True, check=True) # trash the current user data, because new data will go in its place
|
subprocess.run(['gio', 'trash', self.new_path], capture_output=True, check=True) # trash the current user data, because new data will go in its place
|
||||||
|
|
||||||
os.makedirs(self.new_path) # create the new user data path
|
os.makedirs(self.new_path) # create the new user data path
|
||||||
|
|
||||||
self.total = int(subprocess.run(['du', '-s', self.existing_path], check=True, text=True, capture_output=True).stdout.split('\t')[0])
|
self.total = int(subprocess.run(['du', '-s', self.existing_path], check=True, text=True, capture_output=True).stdout.split('\t')[0])
|
||||||
self.total *= 2.2 # estimate from space savings
|
self.total *= 2.2 # estimate from space savings
|
||||||
self.process = subprocess.Popen(['tar', '--zstd', '-xvf', self.existing_path, '-C', self.new_path],
|
self.process = subprocess.Popen(['tar', '--zstd', '-xvf', self.existing_path, '-C', self.new_path],
|
||||||
stdout=subprocess.PIPE,
|
stdout=subprocess.PIPE,
|
||||||
stderr=subprocess.PIPE
|
stderr=subprocess.PIPE
|
||||||
)
|
)
|
||||||
stdout, stderr = self.process.communicate()
|
stdout, stderr = self.process.communicate()
|
||||||
if self.process.returncode != 0:
|
if self.process.returncode != 0:
|
||||||
raise subprocess.CalledProcessError(self.process.returncode, self.process.args, output=stdout, stderr=stderr)
|
raise subprocess.CalledProcessError(self.process.returncode, self.process.args, output=stdout, stderr=stderr)
|
||||||
|
|
||||||
self.stop = True # tell the check timeout to stop, because we know the file is done being made
|
self.stop = True # tell the check timeout to stop, because we know the file is done being made
|
||||||
HostInfo.main_window.remove_refresh_lockout("managing snapshot")
|
HostInfo.main_window.remove_refresh_lockout("managing snapshot")
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.do_cancel(cpe.stderr.decode())
|
self.do_cancel(cpe.stderr.decode())
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.do_cancel(str(e))
|
self.do_cancel(str(e))
|
||||||
|
|
||||||
def do_cancel(self, error_str):
|
def do_cancel(self, error_str):
|
||||||
if self.has_cancelled or self.stop:
|
if self.has_cancelled or self.stop:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.has_cancelled = True
|
self.has_cancelled = True
|
||||||
self.process.terminate()
|
self.process.terminate()
|
||||||
self.process.wait()
|
self.process.wait()
|
||||||
if len(self.files_to_trash_on_cancel) > 0:
|
if len(self.files_to_trash_on_cancel) > 0:
|
||||||
try:
|
try:
|
||||||
subprocess.run(['gio', 'trash'] + self.files_to_trash_on_cancel, capture_output=True, check=True)
|
subprocess.run(['gio', 'trash'] + self.files_to_trash_on_cancel, capture_output=True, check=True)
|
||||||
|
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.stop = True
|
self.stop = True
|
||||||
HostInfo.main_window.remove_refresh_lockout("managing snapshot")
|
HostInfo.main_window.remove_refresh_lockout("managing snapshot")
|
||||||
if self.toast_overlay and error_str != "manual_cancel":
|
if self.toast_overlay and error_str != "manual_cancel":
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Error in snapshot handling"), error_str).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Error in snapshot handling"), error_str).toast)
|
||||||
|
|
||||||
def check_size(self, check_path):
|
def check_size(self, check_path):
|
||||||
try:
|
try:
|
||||||
output = subprocess.run(['du', '-s', check_path], check=True, text=True, capture_output=True).stdout.split('\t')[0]
|
output = subprocess.run(['du', '-s', check_path], check=True, text=True, capture_output=True).stdout.split('\t')[0]
|
||||||
working_total = float(output)
|
working_total = float(output)
|
||||||
self.fraction = working_total / self.total
|
self.fraction = working_total / self.total
|
||||||
return not self.stop
|
return not self.stop
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
return not self.stop # continue the timeout or stop the timeout
|
return not self.stop # continue the timeout or stop the timeout
|
||||||
|
|
||||||
def compress(self):
|
def compress(self):
|
||||||
self.stop = False
|
self.stop = False
|
||||||
self.files_to_trash_on_cancel = [f'{self.new_path}/{self.file_name}.tar.zst', f'{self.new_path}/{self.file_name}.json']
|
self.files_to_trash_on_cancel = [f'{self.new_path}/{self.file_name}.tar.zst', f'{self.new_path}/{self.file_name}.json']
|
||||||
HostInfo.main_window.add_refresh_lockout("managing snapshot")
|
HostInfo.main_window.add_refresh_lockout("managing snapshot")
|
||||||
Gio.Task.new(None, None, None).run_in_thread(self.compress_thread)
|
Gio.Task.new(None, None, None).run_in_thread(self.compress_thread)
|
||||||
GLib.timeout_add(200, self.check_size, f"{self.new_path}/{self.file_name}.tar.zst")
|
GLib.timeout_add(200, self.check_size, f"{self.new_path}/{self.file_name}.tar.zst")
|
||||||
|
|
||||||
def extract(self):
|
def extract(self):
|
||||||
self.stop = False
|
self.stop = False
|
||||||
self.files_to_trash_on_cancel = [self.new_path]
|
self.files_to_trash_on_cancel = [self.new_path]
|
||||||
HostInfo.main_window.add_refresh_lockout("managing snapshot")
|
HostInfo.main_window.add_refresh_lockout("managing snapshot")
|
||||||
Gio.Task.new(None, None, None).run_in_thread(self.extract_thread)
|
Gio.Task.new(None, None, None).run_in_thread(self.extract_thread)
|
||||||
GLib.timeout_add(200, self.check_size, self.new_path)
|
GLib.timeout_add(200, self.check_size, self.new_path)
|
||||||
|
|
||||||
def __init__(self, existing_path, new_path, file_name="", name="", toast_overlay=None):
|
def __init__(self, existing_path, new_path, file_name="", name="", toast_overlay=None):
|
||||||
self.existing_path = existing_path
|
self.existing_path = existing_path
|
||||||
self.new_path = new_path
|
self.new_path = new_path
|
||||||
self.file_name = file_name
|
self.file_name = file_name
|
||||||
self.name = name
|
self.name = name
|
||||||
self.should_check = False
|
self.should_check = False
|
||||||
self.stop = False
|
self.stop = False
|
||||||
self.fraction = 0.0
|
self.fraction = 0.0
|
||||||
self.total = 0
|
self.total = 0
|
||||||
self.process = None
|
self.process = None
|
||||||
self.toast_overlay = toast_overlay
|
self.toast_overlay = toast_overlay
|
||||||
self.has_cancelled = False
|
self.has_cancelled = False
|
||||||
self.files_to_trash_on_cancel = []
|
self.files_to_trash_on_cancel = []
|
||||||
|
|||||||
@@ -2,83 +2,83 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $DataBox : ListBox {
|
template $DataBox : ListBox {
|
||||||
selection-mode: none;
|
selection-mode: none;
|
||||||
styles ["boxed-list"]
|
styles ["boxed-list"]
|
||||||
Adw.ActionRow row {
|
Adw.ActionRow row {
|
||||||
activatable: bind check_button.visible;
|
activatable: bind check_button.visible;
|
||||||
width-request: 275;
|
width-request: 275;
|
||||||
[child]
|
[child]
|
||||||
Box root_box {
|
Box root_box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
Box title_box {
|
Box title_box {
|
||||||
margin-top: 12;
|
margin-top: 12;
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
Image image {
|
Image image {
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
icon-name: "flatpak-symbolic";
|
icon-name: "flatpak-symbolic";
|
||||||
icon-size: large;
|
icon-size: large;
|
||||||
}
|
}
|
||||||
Box label_box {
|
Box label_box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
Label title_label {
|
Label title_label {
|
||||||
label: "No Title Set";
|
label: "No Title Set";
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
halign: start;
|
halign: start;
|
||||||
ellipsize: middle;
|
ellipsize: middle;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
styles ["title-4"]
|
styles ["title-4"]
|
||||||
}
|
}
|
||||||
Label subtitle_label {
|
Label subtitle_label {
|
||||||
label: "No subtitle set";
|
label: "No subtitle set";
|
||||||
// hexpand: true;
|
// hexpand: true;
|
||||||
halign: start;
|
halign: start;
|
||||||
ellipsize: middle;
|
ellipsize: middle;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Box content_box {
|
Box content_box {
|
||||||
spacing: 6;
|
spacing: 6;
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 6;
|
margin-end: 6;
|
||||||
margin-bottom: 6;
|
margin-bottom: 6;
|
||||||
Spinner spinner {
|
Spinner spinner {
|
||||||
spinning: true;
|
spinning: true;
|
||||||
}
|
}
|
||||||
Label size_label {
|
Label size_label {
|
||||||
label: "No size set";
|
label: "No size set";
|
||||||
halign: start;
|
halign: start;
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
}
|
}
|
||||||
Button copy_button {
|
Button copy_button {
|
||||||
icon-name: "copy-symbolic";
|
icon-name: "copy-symbolic";
|
||||||
tooltip-text: _("Copy Path");
|
tooltip-text: _("Copy Path");
|
||||||
visible: bind check_button.visible inverted;
|
visible: bind check_button.visible inverted;
|
||||||
styles ["flat", "circular"]
|
styles ["flat", "circular"]
|
||||||
}
|
}
|
||||||
Button open_button {
|
Button open_button {
|
||||||
icon-name: "folder-open-symbolic";
|
icon-name: "folder-open-symbolic";
|
||||||
tooltip-text: _("Open User Data");
|
tooltip-text: _("Open User Data");
|
||||||
visible: bind check_button.visible inverted;
|
visible: bind check_button.visible inverted;
|
||||||
styles ["flat", "circular"]
|
styles ["flat", "circular"]
|
||||||
}
|
}
|
||||||
Button install_button {
|
Button install_button {
|
||||||
icon-name: "arrow-pointing-at-line-down-symbolic";
|
icon-name: "arrow-pointing-at-line-down-symbolic";
|
||||||
tooltip-text: _("Attempt to Install");
|
tooltip-text: _("Attempt to Install");
|
||||||
styles ["flat", "circular"]
|
styles ["flat", "circular"]
|
||||||
}
|
}
|
||||||
Button trash_button {
|
Button trash_button {
|
||||||
icon-name: "user-trash-symbolic";
|
icon-name: "user-trash-symbolic";
|
||||||
tooltip-text: _("Trash User Data");
|
tooltip-text: _("Trash User Data");
|
||||||
visible: bind check_button.visible inverted;
|
visible: bind check_button.visible inverted;
|
||||||
styles ["flat", "circular"]
|
styles ["flat", "circular"]
|
||||||
}
|
}
|
||||||
CheckButton check_button {
|
CheckButton check_button {
|
||||||
visible: false;
|
visible: false;
|
||||||
styles ["selection-mode"]
|
styles ["selection-mode"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,143 +6,143 @@ import subprocess
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/user_data_page/data_box.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/user_data_page/data_box.ui")
|
||||||
class DataBox(Gtk.ListBox):
|
class DataBox(Gtk.ListBox):
|
||||||
__gtype_name__ = 'DataBox'
|
__gtype_name__ = 'DataBox'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
row = gtc()
|
row = gtc()
|
||||||
image = gtc()
|
image = gtc()
|
||||||
title_label = gtc()
|
title_label = gtc()
|
||||||
subtitle_label = gtc()
|
subtitle_label = gtc()
|
||||||
spinner = gtc()
|
spinner = gtc()
|
||||||
size_label = gtc()
|
size_label = gtc()
|
||||||
|
|
||||||
copy_button = gtc()
|
copy_button = gtc()
|
||||||
open_button = gtc()
|
open_button = gtc()
|
||||||
install_button = gtc()
|
install_button = gtc()
|
||||||
trash_button = gtc()
|
trash_button = gtc()
|
||||||
check_button = gtc()
|
check_button = gtc()
|
||||||
|
|
||||||
def human_readable_size(self):
|
def human_readable_size(self):
|
||||||
working_size = self.size
|
working_size = self.size
|
||||||
units = ['KB', 'MB', 'GB', 'TB']
|
units = ['KB', 'MB', 'GB', 'TB']
|
||||||
# size *= 1024
|
# size *= 1024
|
||||||
for unit in units:
|
for unit in units:
|
||||||
if working_size < 1024:
|
if working_size < 1024:
|
||||||
return f"~ {round(working_size)} {unit}"
|
return f"~ {round(working_size)} {unit}"
|
||||||
working_size /= 1024
|
working_size /= 1024
|
||||||
return f"~ {round(working_size)} PB"
|
return f"~ {round(working_size)} PB"
|
||||||
|
|
||||||
def get_size(self, *args):
|
def get_size(self, *args):
|
||||||
self.size = int(subprocess.run(['du', '-s', self.data_path], capture_output=True, text=True).stdout.split("\t")[0])
|
self.size = int(subprocess.run(['du', '-s', self.data_path], capture_output=True, text=True).stdout.split("\t")[0])
|
||||||
|
|
||||||
def show_size(self):
|
def show_size(self):
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
self.size_label.set_label(self.human_readable_size())
|
self.size_label.set_label(self.human_readable_size())
|
||||||
self.spinner.set_visible(False)
|
self.spinner.set_visible(False)
|
||||||
if self.callback:
|
if self.callback:
|
||||||
self.callback(self.size)
|
self.callback(self.size)
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(self.get_size)
|
Gio.Task.new(None, None, callback).run_in_thread(self.get_size)
|
||||||
|
|
||||||
def idle_stuff(self):
|
def idle_stuff(self):
|
||||||
self.title_label.set_label(self.title)
|
self.title_label.set_label(self.title)
|
||||||
self.subtitle_label.set_label(self.subtitle)
|
self.subtitle_label.set_label(self.subtitle)
|
||||||
self.install_button.set_visible(self.is_leftover)
|
self.install_button.set_visible(self.is_leftover)
|
||||||
if self.icon_path:
|
if self.icon_path:
|
||||||
self.image.add_css_class("icon-dropshadow")
|
self.image.add_css_class("icon-dropshadow")
|
||||||
self.image.set_from_file(self.icon_path)
|
self.image.set_from_file(self.icon_path)
|
||||||
|
|
||||||
def copy_handler(self, *args):
|
def copy_handler(self, *args):
|
||||||
try:
|
try:
|
||||||
HostInfo.clipboard.set(self.data_path)
|
HostInfo.clipboard.set(self.data_path)
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new(_("Copied data path")))
|
self.toast_overlay.add_toast(Adw.Toast.new(_("Copied data path")))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not copy data path"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not copy data path"), str(e)).toast)
|
||||||
|
|
||||||
def open_handler(self, *args):
|
def open_handler(self, *args):
|
||||||
try:
|
try:
|
||||||
Gio.AppInfo.launch_default_for_uri(f"file://{self.data_path}", None)
|
Gio.AppInfo.launch_default_for_uri(f"file://{self.data_path}", None)
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new(_("Opened data folder")))
|
self.toast_overlay.add_toast(Adw.Toast.new(_("Opened data folder")))
|
||||||
except GLib.GError as e:
|
except GLib.GError as e:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not open folder"), str(e)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not open folder"), str(e)).toast)
|
||||||
|
|
||||||
def install_handler(self, *args):
|
def install_handler(self, *args):
|
||||||
self.parent_page.should_rclick = False
|
self.parent_page.should_rclick = False
|
||||||
def why_cant_this_just_be_a_lambda(*args):
|
def why_cant_this_just_be_a_lambda(*args):
|
||||||
self.parent_page.should_rclick = True
|
self.parent_page.should_rclick = True
|
||||||
|
|
||||||
AttemptInstallDialog([self.subtitle], why_cant_this_just_be_a_lambda)
|
AttemptInstallDialog([self.subtitle], why_cant_this_just_be_a_lambda)
|
||||||
|
|
||||||
def trash_handler(self, *args):
|
def trash_handler(self, *args):
|
||||||
self.failed_trash = False
|
self.failed_trash = False
|
||||||
|
|
||||||
def thread(*args):
|
def thread(*args):
|
||||||
try:
|
try:
|
||||||
subprocess.run(['gio', 'trash', self.data_path], check=True, text=True, capture_output=True)
|
subprocess.run(['gio', 'trash', self.data_path], check=True, text=True, capture_output=True)
|
||||||
properties_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].properties_page
|
properties_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].properties_page
|
||||||
properties_package = properties_page.package
|
properties_package = properties_page.package
|
||||||
if not properties_package is None:
|
if not properties_package is None:
|
||||||
properties_page.set_properties(properties_package, True)
|
properties_page.set_properties(properties_package, True)
|
||||||
|
|
||||||
snapshot_list_page = HostInfo.main_window.pages[HostInfo.main_window.snapshots_row].list_page
|
snapshot_list_page = HostInfo.main_window.pages[HostInfo.main_window.snapshots_row].list_page
|
||||||
snapshot_list_package = snapshot_list_page.package_or_folder
|
snapshot_list_package = snapshot_list_page.package_or_folder
|
||||||
if not snapshot_list_package is None:
|
if not snapshot_list_package is None:
|
||||||
snapshot_list_page.set_snapshots(snapshot_list_package, True)
|
snapshot_list_page.set_snapshots(snapshot_list_package, True)
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
self.failed_trash = cpe.stderr
|
self.failed_trash = cpe.stderr
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.failed_trash = e
|
self.failed_trash = e
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
if self.failed_trash:
|
if self.failed_trash:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(self.failed_trash)).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(self.failed_trash)).toast)
|
||||||
else:
|
else:
|
||||||
self.toast_overlay.add_toast(Adw.Toast.new("Trashed data"))
|
self.toast_overlay.add_toast(Adw.Toast.new("Trashed data"))
|
||||||
if self.trash_callback:
|
if self.trash_callback:
|
||||||
self.trash_callback(self)
|
self.trash_callback(self)
|
||||||
|
|
||||||
def on_response(_, response):
|
def on_response(_, response):
|
||||||
self.parent_page.should_rclick = True
|
self.parent_page.should_rclick = True
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
Gio.Task.new(None, None, callback).run_in_thread(thread)
|
||||||
|
|
||||||
self.parent_page.should_rclick = False
|
self.parent_page.should_rclick = False
|
||||||
dialog = Adw.AlertDialog(heading=_("Trash {}'s Data?").format(self.title), body=_("{}'s data will be sent to the trash").format(self.title))
|
dialog = Adw.AlertDialog(heading=_("Trash {}'s Data?").format(self.title), body=_("{}'s data will be sent to the trash").format(self.title))
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
dialog.add_response("continue", _("Continue"))
|
dialog.add_response("continue", _("Continue"))
|
||||||
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(HostInfo.main_window)
|
dialog.present(HostInfo.main_window)
|
||||||
|
|
||||||
def __init__(self, parent_page, toast_overlay, is_leftover, title, subtitle, data_path, icon_path=None, callback=None, trash_callback=None, **kwargs):
|
def __init__(self, parent_page, toast_overlay, is_leftover, title, subtitle, data_path, icon_path=None, callback=None, trash_callback=None, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.parent_page = parent_page
|
self.parent_page = parent_page
|
||||||
self.toast_overlay = toast_overlay
|
self.toast_overlay = toast_overlay
|
||||||
self.is_leftover = is_leftover
|
self.is_leftover = is_leftover
|
||||||
self.title = title
|
self.title = title
|
||||||
self.subtitle = subtitle
|
self.subtitle = subtitle
|
||||||
self.icon_path = icon_path
|
self.icon_path = icon_path
|
||||||
self.data_path = data_path
|
self.data_path = data_path
|
||||||
self.callback = callback
|
self.callback = callback
|
||||||
self.trash_callback = trash_callback
|
self.trash_callback = trash_callback
|
||||||
self.size = None
|
self.size = None
|
||||||
self.failed_trash = None
|
self.failed_trash = None
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.idle_stuff()
|
self.idle_stuff()
|
||||||
self.show_size()
|
self.show_size()
|
||||||
if subtitle == "io.github.flattool.Warehouse":
|
if subtitle == "io.github.flattool.Warehouse":
|
||||||
self.check_button.set_active = lambda *_: None
|
self.check_button.set_active = lambda *_: None
|
||||||
self.check_button.set_sensitive(False)
|
self.check_button.set_sensitive(False)
|
||||||
self.trash_button.set_sensitive(False)
|
self.trash_button.set_sensitive(False)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.copy_button.connect("clicked", self.copy_handler)
|
self.copy_button.connect("clicked", self.copy_handler)
|
||||||
self.open_button.connect("clicked", self.open_handler)
|
self.open_button.connect("clicked", self.open_handler)
|
||||||
self.install_button.connect("clicked", self.install_handler)
|
self.install_button.connect("clicked", self.install_handler)
|
||||||
self.trash_button.connect("clicked", self.trash_handler)
|
self.trash_button.connect("clicked", self.trash_handler)
|
||||||
|
|||||||
@@ -2,72 +2,72 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $DataSubpage : Stack {
|
template $DataSubpage : Stack {
|
||||||
Box content_box {
|
Box content_box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
Box label_box {
|
Box label_box {
|
||||||
margin-start: 24;
|
margin-start: 24;
|
||||||
margin-end: 24;
|
margin-end: 24;
|
||||||
halign: fill;
|
halign: fill;
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
Label title {
|
Label title {
|
||||||
label: _("No Title Set");
|
label: _("No Title Set");
|
||||||
styles ["title-1"]
|
styles ["title-1"]
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
justify: fill;
|
justify: fill;
|
||||||
halign: start;
|
halign: start;
|
||||||
wrap: true;
|
wrap: true;
|
||||||
}
|
}
|
||||||
Box subtitle_size_box {
|
Box subtitle_size_box {
|
||||||
Spinner spinner {
|
Spinner spinner {
|
||||||
spinning: true;
|
spinning: true;
|
||||||
valign: center;
|
valign: center;
|
||||||
margin-top: 3;
|
margin-top: 3;
|
||||||
margin-end: 6;
|
margin-end: 6;
|
||||||
}
|
}
|
||||||
Label size_label {
|
Label size_label {
|
||||||
label: _("Loading Size…");
|
label: _("Loading Size…");
|
||||||
styles ["title-3"]
|
styles ["title-3"]
|
||||||
halign: start;
|
halign: start;
|
||||||
wrap: true;
|
wrap: true;
|
||||||
}
|
}
|
||||||
Label subtitle {
|
Label subtitle {
|
||||||
visible: false;
|
visible: false;
|
||||||
label: "No Subtutle Set";
|
label: "No Subtutle Set";
|
||||||
styles ["title-3"]
|
styles ["title-3"]
|
||||||
wrap: true;
|
wrap: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
margin-bottom: 9;
|
margin-bottom: 9;
|
||||||
}
|
}
|
||||||
ScrolledWindow scrolled_window {
|
ScrolledWindow scrolled_window {
|
||||||
vexpand: true;
|
vexpand: true;
|
||||||
Box {
|
Box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
Separator {
|
Separator {
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
margin-bottom: 9;
|
margin-bottom: 9;
|
||||||
}
|
}
|
||||||
FlowBox flow_box {
|
FlowBox flow_box {
|
||||||
styles ["boxed-list"]
|
styles ["boxed-list"]
|
||||||
homogeneous: true;
|
homogeneous: true;
|
||||||
valign: start;
|
valign: start;
|
||||||
selection-mode: none;
|
selection-mode: none;
|
||||||
max-children-per-line: 6;
|
max-children-per-line: 6;
|
||||||
margin-start: 12;
|
margin-start: 12;
|
||||||
margin-end: 12;
|
margin-end: 12;
|
||||||
margin-bottom: 12;
|
margin-bottom: 12;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.StatusPage no_data {
|
Adw.StatusPage no_data {
|
||||||
// Contents will be set from the subpage object
|
// Contents will be set from the subpage object
|
||||||
}
|
}
|
||||||
Adw.StatusPage no_results {
|
Adw.StatusPage no_results {
|
||||||
title: _("No Results Found");
|
title: _("No Results Found");
|
||||||
description: _("Try a different search");
|
description: _("Try a different search");
|
||||||
icon-name: "system-search-symbolic";
|
icon-name: "system-search-symbolic";
|
||||||
valign: center;
|
valign: center;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,237 +5,237 @@ from .loading_status import LoadingStatus
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/user_data_page/data_subpage.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/user_data_page/data_subpage.ui")
|
||||||
class DataSubpage(Gtk.Stack):
|
class DataSubpage(Gtk.Stack):
|
||||||
__gtype_name__ = 'DataSubpage'
|
__gtype_name__ = 'DataSubpage'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
scrolled_window = gtc()
|
scrolled_window = gtc()
|
||||||
|
|
||||||
label_box = gtc()
|
label_box = gtc()
|
||||||
subtitle_size_box = gtc()
|
subtitle_size_box = gtc()
|
||||||
title = gtc()
|
title = gtc()
|
||||||
subtitle = gtc()
|
subtitle = gtc()
|
||||||
spinner = gtc()
|
spinner = gtc()
|
||||||
size_label = gtc()
|
size_label = gtc()
|
||||||
flow_box = gtc()
|
flow_box = gtc()
|
||||||
|
|
||||||
# Statuses
|
# Statuses
|
||||||
content_box = gtc()
|
content_box = gtc()
|
||||||
no_data = gtc()
|
no_data = gtc()
|
||||||
no_results = gtc()
|
no_results = gtc()
|
||||||
|
|
||||||
def human_readable_size(self):
|
def human_readable_size(self):
|
||||||
working_size = self.total_size
|
working_size = self.total_size
|
||||||
units = ['KB', 'MB', 'GB', 'TB']
|
units = ['KB', 'MB', 'GB', 'TB']
|
||||||
# size *= 1024
|
# size *= 1024
|
||||||
for unit in units:
|
for unit in units:
|
||||||
if working_size < 1024:
|
if working_size < 1024:
|
||||||
return f"~ {round(working_size)} {unit}"
|
return f"~ {round(working_size)} {unit}"
|
||||||
working_size /= 1024
|
working_size /= 1024
|
||||||
return f"~ {round(working_size)} PB"
|
return f"~ {round(working_size)} PB"
|
||||||
|
|
||||||
def sort_func(self, box1, box2):
|
def sort_func(self, box1, box2):
|
||||||
import random
|
import random
|
||||||
# print(random.randint(1, 100), self.sort_mode, self.sort_ascend)
|
# print(random.randint(1, 100), self.sort_mode, self.sort_ascend)
|
||||||
i1 = None
|
i1 = None
|
||||||
i2 = None
|
i2 = None
|
||||||
if self.sort_mode == "name":
|
if self.sort_mode == "name":
|
||||||
i1 = box1.get_child().title.lower()
|
i1 = box1.get_child().title.lower()
|
||||||
i2 = box2.get_child().title.lower()
|
i2 = box2.get_child().title.lower()
|
||||||
|
|
||||||
if self.sort_mode == "id":
|
if self.sort_mode == "id":
|
||||||
i1 = box1.get_child().subtitle.lower()
|
i1 = box1.get_child().subtitle.lower()
|
||||||
i2 = box2.get_child().subtitle.lower()
|
i2 = box2.get_child().subtitle.lower()
|
||||||
|
|
||||||
if self.sort_mode == "size" and self.ready_to_sort_size:
|
if self.sort_mode == "size" and self.ready_to_sort_size:
|
||||||
i1 = box1.get_child().size
|
i1 = box1.get_child().size
|
||||||
i2 = box2.get_child().size
|
i2 = box2.get_child().size
|
||||||
|
|
||||||
if i1 is None or i2 is None:
|
if i1 is None or i2 is None:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
return i1 > i2 if self.sort_ascend else i1 < i2
|
return i1 > i2 if self.sort_ascend else i1 < i2
|
||||||
|
|
||||||
def box_size_callback(self, size):
|
def box_size_callback(self, size):
|
||||||
self.finished_boxes += 1
|
self.finished_boxes += 1
|
||||||
self.total_size += size
|
self.total_size += size
|
||||||
if self.finished_boxes == self.total_items:
|
if self.finished_boxes == self.total_items:
|
||||||
self.size_label.set_label(self.human_readable_size())
|
self.size_label.set_label(self.human_readable_size())
|
||||||
self.spinner.set_visible(False)
|
self.spinner.set_visible(False)
|
||||||
self.ready_to_sort_size = True
|
self.ready_to_sort_size = True
|
||||||
if self.sort_mode == "size":
|
if self.sort_mode == "size":
|
||||||
self.flow_box.invalidate_sort()
|
self.flow_box.invalidate_sort()
|
||||||
self.set_visible_child(self.content_box)
|
self.set_visible_child(self.content_box)
|
||||||
GLib.idle_add(lambda *_: self.parent_page.status_stack.set_visible_child(self.parent_page.main_view))
|
GLib.idle_add(lambda *_: self.parent_page.status_stack.set_visible_child(self.parent_page.main_view))
|
||||||
|
|
||||||
def trash_handler(self, trashed_box):
|
def trash_handler(self, trashed_box):
|
||||||
self.flow_box.remove(trashed_box)
|
self.flow_box.remove(trashed_box)
|
||||||
if not self.flow_box.get_child_at_index(0):
|
if not self.flow_box.get_child_at_index(0):
|
||||||
self.set_visible_child(self.no_data)
|
self.set_visible_child(self.no_data)
|
||||||
self.parent_page.start_loading()
|
self.parent_page.start_loading()
|
||||||
self.parent_page.end_loading()
|
self.parent_page.end_loading()
|
||||||
|
|
||||||
def set_selection_mode(self, is_enabled):
|
def set_selection_mode(self, is_enabled):
|
||||||
if not is_enabled:
|
if not is_enabled:
|
||||||
self.size_label.set_visible(True)
|
self.size_label.set_visible(True)
|
||||||
self.subtitle.set_visible(False)
|
self.subtitle.set_visible(False)
|
||||||
|
|
||||||
idx = 0
|
idx = 0
|
||||||
while box := self.flow_box.get_child_at_index(idx):
|
while box := self.flow_box.get_child_at_index(idx):
|
||||||
idx += 1
|
idx += 1
|
||||||
box = box.get_child()
|
box = box.get_child()
|
||||||
if not is_enabled:
|
if not is_enabled:
|
||||||
GLib.idle_add(lambda *_, box=box: box.check_button.set_active(False))
|
GLib.idle_add(lambda *_, box=box: box.check_button.set_active(False))
|
||||||
|
|
||||||
GLib.idle_add(lambda *_, box=box: box.check_button.set_visible(is_enabled))
|
GLib.idle_add(lambda *_, box=box: box.check_button.set_visible(is_enabled))
|
||||||
GLib.idle_add(lambda *_, box=box: box.install_button.set_visible(box.is_leftover and not is_enabled))
|
GLib.idle_add(lambda *_, box=box: box.install_button.set_visible(box.is_leftover and not is_enabled))
|
||||||
|
|
||||||
self.selected_boxes.clear()
|
self.selected_boxes.clear()
|
||||||
|
|
||||||
def box_select_handler(self, box):
|
def box_select_handler(self, box):
|
||||||
cb = box.check_button
|
cb = box.check_button
|
||||||
if cb.get_active():
|
if cb.get_active():
|
||||||
self.selected_boxes.append(box)
|
self.selected_boxes.append(box)
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
self.selected_boxes.remove(box)
|
self.selected_boxes.remove(box)
|
||||||
except ValueError:
|
except ValueError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
total = len(self.selected_boxes)
|
total = len(self.selected_boxes)
|
||||||
self.subtitle.set_visible(not total == 0)
|
self.subtitle.set_visible(not total == 0)
|
||||||
self.size_label.set_visible(total == 0)
|
self.size_label.set_visible(total == 0)
|
||||||
self.subtitle.set_label(_("{} Selected").format(total))
|
self.subtitle.set_label(_("{} Selected").format(total))
|
||||||
self.parent_page.copy_button.set_sensitive(total)
|
self.parent_page.copy_button.set_sensitive(total)
|
||||||
self.parent_page.trash_button.set_sensitive(total)
|
self.parent_page.trash_button.set_sensitive(total)
|
||||||
self.parent_page.install_button.set_sensitive(total)
|
self.parent_page.install_button.set_sensitive(total)
|
||||||
self.parent_page.more_button.set_sensitive(total)
|
self.parent_page.more_button.set_sensitive(total)
|
||||||
|
|
||||||
def box_interact_handler(self, flow_box, box):
|
def box_interact_handler(self, flow_box, box):
|
||||||
box = box.get_child()
|
box = box.get_child()
|
||||||
cb = box.check_button
|
cb = box.check_button
|
||||||
if cb.get_visible():
|
if cb.get_visible():
|
||||||
cb.set_active(not cb.get_active())
|
cb.set_active(not cb.get_active())
|
||||||
|
|
||||||
def select_all_handler(self, *args):
|
def select_all_handler(self, *args):
|
||||||
idx = 0
|
idx = 0
|
||||||
while box := self.flow_box.get_child_at_index(idx):
|
while box := self.flow_box.get_child_at_index(idx):
|
||||||
idx += 1
|
idx += 1
|
||||||
box.get_child().check_button.set_active(True)
|
box.get_child().check_button.set_active(True)
|
||||||
|
|
||||||
def box_rclick_handler(self, box):
|
def box_rclick_handler(self, box):
|
||||||
if self.should_rclick:
|
if self.should_rclick:
|
||||||
self.parent_page.select_button.set_active(True)
|
self.parent_page.select_button.set_active(True)
|
||||||
box.check_button.set_active(not box.check_button.get_active())
|
box.check_button.set_active(not box.check_button.get_active())
|
||||||
|
|
||||||
def generate_list(self, flatpaks, data):
|
def generate_list(self, flatpaks, data):
|
||||||
self.flow_box.remove_all()
|
self.flow_box.remove_all()
|
||||||
self.boxes.clear()
|
self.boxes.clear()
|
||||||
self.ready_to_sort_size = False
|
self.ready_to_sort_size = False
|
||||||
self.finished_boxes = 0
|
self.finished_boxes = 0
|
||||||
self.total_size = 0
|
self.total_size = 0
|
||||||
self.total_items = len(data)
|
self.total_items = len(data)
|
||||||
self.parent_page.search_entry.set_editable(True)
|
self.parent_page.search_entry.set_editable(True)
|
||||||
self.should_rclick = True
|
self.should_rclick = True
|
||||||
if flatpaks:
|
if flatpaks:
|
||||||
for i, pak in enumerate(flatpaks):
|
for i, pak in enumerate(flatpaks):
|
||||||
box = DataBox(self, self.parent_page.toast_overlay, False, pak.info["name"], pak.info["id"], pak.data_path, pak.icon_path, self.box_size_callback, self.trash_handler)
|
box = DataBox(self, self.parent_page.toast_overlay, False, pak.info["name"], pak.info["id"], pak.data_path, pak.icon_path, self.box_size_callback, self.trash_handler)
|
||||||
box.check_button.connect("toggled", lambda *_, box=box: self.box_select_handler(box))
|
box.check_button.connect("toggled", lambda *_, box=box: self.box_select_handler(box))
|
||||||
self.boxes.append(box)
|
self.boxes.append(box)
|
||||||
self.flow_box.append(box)
|
self.flow_box.append(box)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
for i, folder in enumerate(data):
|
for i, folder in enumerate(data):
|
||||||
box = DataBox(self, self.parent_page.toast_overlay, True, folder.split('.')[-1], folder, f"{HostInfo.home}/.var/app/{folder}", None, self.box_size_callback, self.trash_handler)
|
box = DataBox(self, self.parent_page.toast_overlay, True, folder.split('.')[-1], folder, f"{HostInfo.home}/.var/app/{folder}", None, self.box_size_callback, self.trash_handler)
|
||||||
box.check_button.connect("toggled", lambda *_, box=box: self.box_select_handler(box))
|
box.check_button.connect("toggled", lambda *_, box=box: self.box_select_handler(box))
|
||||||
self.flow_box.append(box)
|
self.flow_box.append(box)
|
||||||
|
|
||||||
idx = 0
|
idx = 0
|
||||||
while box := self.flow_box.get_child_at_index(idx):
|
while box := self.flow_box.get_child_at_index(idx):
|
||||||
idx += 1
|
idx += 1
|
||||||
box.set_focusable(False)
|
box.set_focusable(False)
|
||||||
child = box.get_child()
|
child = box.get_child()
|
||||||
child.set_focusable(False)
|
child.set_focusable(False)
|
||||||
child.row.set_focusable(child.check_button.get_visible())
|
child.row.set_focusable(child.check_button.get_visible())
|
||||||
rclick = Gtk.GestureClick(button=3)
|
rclick = Gtk.GestureClick(button=3)
|
||||||
rclick.connect("released", lambda *_, child=child: self.box_rclick_handler(child))
|
rclick.connect("released", lambda *_, child=child: self.box_rclick_handler(child))
|
||||||
box.add_controller(rclick)
|
box.add_controller(rclick)
|
||||||
long_press = Gtk.GestureLongPress()
|
long_press = Gtk.GestureLongPress()
|
||||||
long_press.connect("pressed", lambda *_, child=child: self.box_rclick_handler(child))
|
long_press.connect("pressed", lambda *_, child=child: self.box_rclick_handler(child))
|
||||||
box.add_controller(long_press)
|
box.add_controller(long_press)
|
||||||
|
|
||||||
if idx == 0:
|
if idx == 0:
|
||||||
self.set_visible_child(self.no_data)
|
self.set_visible_child(self.no_data)
|
||||||
elif self.sort_mode != "size":
|
elif self.sort_mode != "size":
|
||||||
self.set_visible_child(self.content_box)
|
self.set_visible_child(self.content_box)
|
||||||
self.parent_page.status_stack.set_visible_child(self.parent_page.main_view)
|
self.parent_page.status_stack.set_visible_child(self.parent_page.main_view)
|
||||||
|
|
||||||
def filter_func(self, box):
|
def filter_func(self, box):
|
||||||
search_text = self.parent_page.search_entry.get_text().lower()
|
search_text = self.parent_page.search_entry.get_text().lower()
|
||||||
box = box.get_child()
|
box = box.get_child()
|
||||||
if search_text in box.title.lower() or search_text in box.subtitle.lower():
|
if search_text in box.title.lower() or search_text in box.subtitle.lower():
|
||||||
self.is_result = True
|
self.is_result = True
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def on_invalidate(self, box):
|
def on_invalidate(self, box):
|
||||||
current_status = self.get_visible_child()
|
current_status = self.get_visible_child()
|
||||||
if not current_status is self.no_results:
|
if not current_status is self.no_results:
|
||||||
self.prev_status = self.get_visible_child()
|
self.prev_status = self.get_visible_child()
|
||||||
|
|
||||||
self.is_result = False
|
self.is_result = False
|
||||||
self.flow_box.invalidate_filter()
|
self.flow_box.invalidate_filter()
|
||||||
if self.is_result:
|
if self.is_result:
|
||||||
self.set_visible_child(self.prev_status)
|
self.set_visible_child(self.prev_status)
|
||||||
else:
|
else:
|
||||||
self.set_visible_child(self.no_results)
|
self.set_visible_child(self.no_results)
|
||||||
|
|
||||||
if self.parent_page.search_entry.get_text().lower() != "" and self.total_items == 0:
|
if self.parent_page.search_entry.get_text().lower() != "" and self.total_items == 0:
|
||||||
self.set_visible_child(self.no_results)
|
self.set_visible_child(self.no_results)
|
||||||
elif self.total_items == 0:
|
elif self.total_items == 0:
|
||||||
self.set_visible_child(self.no_data)
|
self.set_visible_child(self.no_data)
|
||||||
|
|
||||||
def update_sort_mode(self):
|
def update_sort_mode(self):
|
||||||
self.sort_ascend = self.settings.get_boolean("sort-ascend")
|
self.sort_ascend = self.settings.get_boolean("sort-ascend")
|
||||||
self.sort_mode = self.settings.get_string("sort-mode")
|
self.sort_mode = self.settings.get_string("sort-mode")
|
||||||
self.flow_box.invalidate_sort()
|
self.flow_box.invalidate_sort()
|
||||||
|
|
||||||
def __init__(self, title, parent_page, is_active, main_window, **kwargs):
|
def __init__(self, title, parent_page, is_active, main_window, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
GLib.idle_add(lambda *_: self.title.set_label(title))
|
GLib.idle_add(lambda *_: self.title.set_label(title))
|
||||||
|
|
||||||
# self.select_button.connect("toggled", lambda *_: self.set_selection_mode(self.select_button.get_active()))
|
# self.select_button.connect("toggled", lambda *_: self.set_selection_mode(self.select_button.get_active()))
|
||||||
# self.flow_box.connect("child-activated", lambda _, item: (cb := (row := item.get_child()).check_button).set_active((not cb.get_active()) if row.get_activatable() else False))
|
# self.flow_box.connect("child-activated", lambda _, item: (cb := (row := item.get_child()).check_button).set_active((not cb.get_active()) if row.get_activatable() else False))
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.main_window = main_window
|
self.main_window = main_window
|
||||||
self.parent_page = parent_page
|
self.parent_page = parent_page
|
||||||
# self.is_active = is_active
|
# self.is_active = is_active
|
||||||
self.sort_mode = ""
|
self.sort_mode = ""
|
||||||
self.sort_ascend = False
|
self.sort_ascend = False
|
||||||
self.total_size = 0
|
self.total_size = 0
|
||||||
self.total_items = 0
|
self.total_items = 0
|
||||||
self.boxes = []
|
self.boxes = []
|
||||||
self.selected_boxes = []
|
self.selected_boxes = []
|
||||||
self.ready_to_sort_size = False
|
self.ready_to_sort_size = False
|
||||||
self.should_rclick = True
|
self.should_rclick = True
|
||||||
self.finished_boxes = 0
|
self.finished_boxes = 0
|
||||||
self.is_result = False
|
self.is_result = False
|
||||||
self.prev_status = None
|
self.prev_status = None
|
||||||
self.settings = Gio.Settings.new("io.github.flattool.Warehouse.data_page")
|
self.settings = Gio.Settings.new("io.github.flattool.Warehouse.data_page")
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
self.flow_box.set_sort_func(self.sort_func)
|
self.flow_box.set_sort_func(self.sort_func)
|
||||||
self.flow_box.set_filter_func(self.filter_func)
|
self.flow_box.set_filter_func(self.filter_func)
|
||||||
|
|
||||||
if is_active:
|
if is_active:
|
||||||
self.no_data.set_icon_name("error-symbolic")
|
self.no_data.set_icon_name("error-symbolic")
|
||||||
self.no_data.set_title(_("No Active Data"))
|
self.no_data.set_title(_("No Active Data"))
|
||||||
self.no_data.set_description(_("Warehouse cannot see any active user data or your system has no active user data present"))
|
self.no_data.set_description(_("Warehouse cannot see any active user data or your system has no active user data present"))
|
||||||
else:
|
else:
|
||||||
self.no_data.set_icon_name("check-plain-symbolic")
|
self.no_data.set_icon_name("check-plain-symbolic")
|
||||||
self.no_data.set_title(_("No Leftover Data"))
|
self.no_data.set_title(_("No Leftover Data"))
|
||||||
self.no_data.set_description(_("There is no leftover user data"))
|
self.no_data.set_description(_("There is no leftover user data"))
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
parent_page.search_entry.connect("search-changed", self.on_invalidate)
|
parent_page.search_entry.connect("search-changed", self.on_invalidate)
|
||||||
self.flow_box.connect("child-activated", self.box_interact_handler)
|
self.flow_box.connect("child-activated", self.box_interact_handler)
|
||||||
|
|||||||
@@ -2,210 +2,210 @@ using Gtk 4.0;
|
|||||||
using Adw 1;
|
using Adw 1;
|
||||||
|
|
||||||
template $UserDataPage : Adw.BreakpointBin {
|
template $UserDataPage : Adw.BreakpointBin {
|
||||||
width-request: 1;
|
width-request: 1;
|
||||||
height-request: 1;
|
height-request: 1;
|
||||||
|
|
||||||
Adw.Breakpoint bpt {
|
Adw.Breakpoint bpt {
|
||||||
condition ("max-width: 585")
|
condition ("max-width: 585")
|
||||||
|
|
||||||
setters {
|
setters {
|
||||||
header_bar.title-widget: null;
|
header_bar.title-widget: null;
|
||||||
// header_bar.show-title: false;
|
// header_bar.show-title: false;
|
||||||
switcher_bar.reveal: true;
|
switcher_bar.reveal: true;
|
||||||
switcher_bar.visible: true;
|
switcher_bar.visible: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Adw.NavigationPage {
|
Adw.NavigationPage {
|
||||||
title: _("User Data");
|
title: _("User Data");
|
||||||
Stack status_stack {
|
Stack status_stack {
|
||||||
Adw.ToolbarView loading_view {
|
Adw.ToolbarView loading_view {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar {
|
Adw.HeaderBar {
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Adw.ToolbarView main_view {
|
Adw.ToolbarView main_view {
|
||||||
[top]
|
[top]
|
||||||
Adw.HeaderBar header_bar {
|
Adw.HeaderBar header_bar {
|
||||||
title-widget:
|
title-widget:
|
||||||
Adw.ViewSwitcher {
|
Adw.ViewSwitcher {
|
||||||
stack: stack;
|
stack: stack;
|
||||||
policy: wide;
|
policy: wide;
|
||||||
}
|
}
|
||||||
;
|
;
|
||||||
[start]
|
[start]
|
||||||
$SidebarButton {}
|
$SidebarButton {}
|
||||||
[start]
|
[start]
|
||||||
ToggleButton search_button {
|
ToggleButton search_button {
|
||||||
icon-name: "system-search-symbolic";
|
icon-name: "system-search-symbolic";
|
||||||
tooltip-text: _("Search User Data");
|
tooltip-text: _("Search User Data");
|
||||||
}
|
}
|
||||||
[end]
|
[end]
|
||||||
MenuButton sort_button {
|
MenuButton sort_button {
|
||||||
popover: sort_pop;
|
popover: sort_pop;
|
||||||
icon-name: "vertical-arrows-long-symbolic";
|
icon-name: "vertical-arrows-long-symbolic";
|
||||||
tooltip-text: _("Sort User Data");
|
tooltip-text: _("Sort User Data");
|
||||||
}
|
}
|
||||||
[end]
|
[end]
|
||||||
ToggleButton select_button {
|
ToggleButton select_button {
|
||||||
icon-name: "selection-mode-symbolic";
|
icon-name: "selection-mode-symbolic";
|
||||||
tooltip-text: _("Select User Data");
|
tooltip-text: _("Select User Data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[top]
|
[top]
|
||||||
Adw.Clamp {
|
Adw.Clamp {
|
||||||
SearchBar search_bar {
|
SearchBar search_bar {
|
||||||
search-mode-enabled: bind search_button.active bidirectional;
|
search-mode-enabled: bind search_button.active bidirectional;
|
||||||
SearchEntry search_entry {
|
SearchEntry search_entry {
|
||||||
editable: false;
|
editable: false;
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
placeholder-text: _("Search User Data");
|
placeholder-text: _("Search User Data");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[bottom]
|
[bottom]
|
||||||
Revealer revealer {
|
Revealer revealer {
|
||||||
reveal-child: bind select_button.active;
|
reveal-child: bind select_button.active;
|
||||||
transition-type: slide_up;
|
transition-type: slide_up;
|
||||||
[center]
|
[center]
|
||||||
Box bottom_bar {
|
Box bottom_bar {
|
||||||
styles ["toolbar"]
|
styles ["toolbar"]
|
||||||
hexpand: true;
|
hexpand: true;
|
||||||
homogeneous: true;
|
homogeneous: true;
|
||||||
Button select_all_button {
|
Button select_all_button {
|
||||||
styles ["raised"]
|
styles ["raised"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "selection-mode-symbolic";
|
icon-name: "selection-mode-symbolic";
|
||||||
label: _("Select All");
|
label: _("Select All");
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button copy_button {
|
Button copy_button {
|
||||||
sensitive: false;
|
sensitive: false;
|
||||||
styles ["raised"]
|
styles ["raised"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "edit-copy-symbolic";
|
icon-name: "edit-copy-symbolic";
|
||||||
label: _("Copy");
|
label: _("Copy");
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button install_button {
|
Button install_button {
|
||||||
visible: false;
|
visible: false;
|
||||||
sensitive: false;
|
sensitive: false;
|
||||||
styles ["raised"]
|
styles ["raised"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "arrow-pointing-at-line-down-symbolic";
|
icon-name: "arrow-pointing-at-line-down-symbolic";
|
||||||
label: _("Install");
|
label: _("Install");
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button trash_button {
|
Button trash_button {
|
||||||
sensitive: false;
|
sensitive: false;
|
||||||
styles ["raised"]
|
styles ["raised"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "user-trash-symbolic";
|
icon-name: "user-trash-symbolic";
|
||||||
label: _("Trash");
|
label: _("Trash");
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MenuButton more_button {
|
MenuButton more_button {
|
||||||
visible: false;
|
visible: false;
|
||||||
sensitive: false;
|
sensitive: false;
|
||||||
popover: more_popover;
|
popover: more_popover;
|
||||||
styles ["raised"]
|
styles ["raised"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "view-more-symbolic";
|
icon-name: "view-more-symbolic";
|
||||||
label: _("More");
|
label: _("More");
|
||||||
can-shrink: true;
|
can-shrink: true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
[bottom]
|
[bottom]
|
||||||
Adw.ViewSwitcherBar switcher_bar {
|
Adw.ViewSwitcherBar switcher_bar {
|
||||||
stack: stack;
|
stack: stack;
|
||||||
visible: false;
|
visible: false;
|
||||||
}
|
}
|
||||||
Adw.ToastOverlay toast_overlay {
|
Adw.ToastOverlay toast_overlay {
|
||||||
Adw.ViewStack stack {
|
Adw.ViewStack stack {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Popover more_popover {
|
Popover more_popover {
|
||||||
styles ["menu"]
|
styles ["menu"]
|
||||||
ListBox more_menu {
|
ListBox more_menu {
|
||||||
Label more_install {
|
Label more_install {
|
||||||
label: _("Install");
|
label: _("Install");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
Label more_trash {
|
Label more_trash {
|
||||||
label: _("Trash");
|
label: _("Trash");
|
||||||
halign: start;
|
halign: start;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Popover sort_pop {
|
Popover sort_pop {
|
||||||
styles ["menu"]
|
styles ["menu"]
|
||||||
Box {
|
Box {
|
||||||
orientation: vertical;
|
orientation: vertical;
|
||||||
margin-start: 6;
|
margin-start: 6;
|
||||||
margin-end: 6;
|
margin-end: 6;
|
||||||
margin-top: 6;
|
margin-top: 6;
|
||||||
margin-bottom: 6;
|
margin-bottom: 6;
|
||||||
Box {
|
Box {
|
||||||
homogeneous: true;
|
homogeneous: true;
|
||||||
spacing: 3;
|
spacing: 3;
|
||||||
ToggleButton sort_ascend {
|
ToggleButton sort_ascend {
|
||||||
styles ["flat"]
|
styles ["flat"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "view-sort-ascending-symbolic";
|
icon-name: "view-sort-ascending-symbolic";
|
||||||
label: _("Ascending");
|
label: _("Ascending");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToggleButton sort_descend {
|
ToggleButton sort_descend {
|
||||||
group: sort_ascend;
|
group: sort_ascend;
|
||||||
styles ["flat"]
|
styles ["flat"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "view-sort-descending-symbolic";
|
icon-name: "view-sort-descending-symbolic";
|
||||||
label: _("Descending");
|
label: _("Descending");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Separator {
|
Separator {
|
||||||
}
|
}
|
||||||
Box {
|
Box {
|
||||||
homogeneous: true;
|
homogeneous: true;
|
||||||
spacing: 3;
|
spacing: 3;
|
||||||
ToggleButton sort_name {
|
ToggleButton sort_name {
|
||||||
styles ["flat"]
|
styles ["flat"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "font-x-generic-symbolic";
|
icon-name: "font-x-generic-symbolic";
|
||||||
label: _("Name");
|
label: _("Name");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToggleButton sort_id {
|
ToggleButton sort_id {
|
||||||
group: sort_name;
|
group: sort_name;
|
||||||
styles ["flat"]
|
styles ["flat"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "tag-outline-symbolic";
|
icon-name: "tag-outline-symbolic";
|
||||||
label: _("ID");
|
label: _("ID");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ToggleButton sort_size {
|
ToggleButton sort_size {
|
||||||
group: sort_name;
|
group: sort_name;
|
||||||
styles ["flat"]
|
styles ["flat"]
|
||||||
Adw.ButtonContent {
|
Adw.ButtonContent {
|
||||||
icon-name: "harddisk-symbolic";
|
icon-name: "harddisk-symbolic";
|
||||||
label: _("Size");
|
label: _("Size");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,290 +9,290 @@ import os, subprocess
|
|||||||
|
|
||||||
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/user_data_page/user_data_page.ui")
|
@Gtk.Template(resource_path="/io/github/flattool/Warehouse/user_data_page/user_data_page.ui")
|
||||||
class UserDataPage(Adw.BreakpointBin):
|
class UserDataPage(Adw.BreakpointBin):
|
||||||
__gtype_name__ = 'UserDataPage'
|
__gtype_name__ = 'UserDataPage'
|
||||||
gtc = Gtk.Template.Child
|
gtc = Gtk.Template.Child
|
||||||
|
|
||||||
bpt = gtc()
|
bpt = gtc()
|
||||||
status_stack = gtc()
|
status_stack = gtc()
|
||||||
loading_view = gtc()
|
loading_view = gtc()
|
||||||
main_view = gtc()
|
main_view = gtc()
|
||||||
header_bar = gtc()
|
header_bar = gtc()
|
||||||
switcher_bar = gtc()
|
switcher_bar = gtc()
|
||||||
search_button = gtc()
|
search_button = gtc()
|
||||||
select_button = gtc()
|
select_button = gtc()
|
||||||
sort_button = gtc()
|
sort_button = gtc()
|
||||||
search_bar = gtc()
|
search_bar = gtc()
|
||||||
search_entry = gtc()
|
search_entry = gtc()
|
||||||
toast_overlay = gtc()
|
toast_overlay = gtc()
|
||||||
stack = gtc()
|
stack = gtc()
|
||||||
revealer = gtc()
|
revealer = gtc()
|
||||||
|
|
||||||
sort_ascend = gtc()
|
sort_ascend = gtc()
|
||||||
sort_descend = gtc()
|
sort_descend = gtc()
|
||||||
sort_name = gtc()
|
sort_name = gtc()
|
||||||
sort_id = gtc()
|
sort_id = gtc()
|
||||||
sort_size = gtc()
|
sort_size = gtc()
|
||||||
|
|
||||||
select_all_button = gtc()
|
select_all_button = gtc()
|
||||||
copy_button = gtc()
|
copy_button = gtc()
|
||||||
trash_button = gtc()
|
trash_button = gtc()
|
||||||
install_button = gtc()
|
install_button = gtc()
|
||||||
more_button = gtc()
|
more_button = gtc()
|
||||||
more_popover = gtc()
|
more_popover = gtc()
|
||||||
more_menu = gtc()
|
more_menu = gtc()
|
||||||
more_trash = gtc()
|
more_trash = gtc()
|
||||||
more_install = gtc()
|
more_install = gtc()
|
||||||
|
|
||||||
# Referred to in the main window
|
# Referred to in the main window
|
||||||
# It is used to determine if a new page should be made or not
|
# It is used to determine if a new page should be made or not
|
||||||
# This must be set to the created object from within the class's __init__ method
|
# This must be set to the created object from within the class's __init__ method
|
||||||
instance = None
|
instance = None
|
||||||
page_name = "user-data"
|
page_name = "user-data"
|
||||||
data_path = f"{HostInfo.home}/.var/app"
|
data_path = f"{HostInfo.home}/.var/app"
|
||||||
bpt_is_applied = False
|
bpt_is_applied = False
|
||||||
is_trash_dialog_open = False
|
is_trash_dialog_open = False
|
||||||
|
|
||||||
def sort_data(self, *args):
|
def sort_data(self, *args):
|
||||||
self.data_flatpaks.clear()
|
self.data_flatpaks.clear()
|
||||||
self.active_data.clear()
|
self.active_data.clear()
|
||||||
self.leftover_data.clear()
|
self.leftover_data.clear()
|
||||||
# paks = dict(HostInfo.id_to_flatpak)
|
# paks = dict(HostInfo.id_to_flatpak)
|
||||||
|
|
||||||
if not os.path.exists(self.data_path):
|
if not os.path.exists(self.data_path):
|
||||||
return
|
return
|
||||||
|
|
||||||
for folder in os.listdir(self.data_path):
|
for folder in os.listdir(self.data_path):
|
||||||
try:
|
try:
|
||||||
self.data_flatpaks.append(HostInfo.id_to_flatpak[folder])
|
self.data_flatpaks.append(HostInfo.id_to_flatpak[folder])
|
||||||
self.active_data.append(folder)
|
self.active_data.append(folder)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
self.leftover_data.append(folder)
|
self.leftover_data.append(folder)
|
||||||
|
|
||||||
def start_loading(self, *args):
|
def start_loading(self, *args):
|
||||||
self.status_stack.set_visible_child(self.loading_view)
|
self.status_stack.set_visible_child(self.loading_view)
|
||||||
self.search_button.set_active(False)
|
self.search_button.set_active(False)
|
||||||
self.select_button.set_active(False)
|
self.select_button.set_active(False)
|
||||||
self.adp.size_label.set_label(_("Loading Size"))
|
self.adp.size_label.set_label(_("Loading Size"))
|
||||||
self.adp.spinner.set_visible(True)
|
self.adp.spinner.set_visible(True)
|
||||||
self.ldp.size_label.set_label(_("Loading Size"))
|
self.ldp.size_label.set_label(_("Loading Size"))
|
||||||
self.ldp.spinner.set_visible(True)
|
self.ldp.spinner.set_visible(True)
|
||||||
|
|
||||||
def end_loading(self, *args):
|
def end_loading(self, *args):
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
self.adp.generate_list(self.data_flatpaks, self.active_data)
|
self.adp.generate_list(self.data_flatpaks, self.active_data)
|
||||||
self.ldp.generate_list([], self.leftover_data)
|
self.ldp.generate_list([], self.leftover_data)
|
||||||
|
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(self.sort_data)
|
Gio.Task.new(None, None, callback).run_in_thread(self.sort_data)
|
||||||
|
|
||||||
def sort_button_handler(self, button):
|
def sort_button_handler(self, button):
|
||||||
if button in {self.sort_ascend, self.sort_descend}:
|
if button in {self.sort_ascend, self.sort_descend}:
|
||||||
self.settings.set_boolean("sort-ascend", self.sort_ascend.get_active())
|
self.settings.set_boolean("sort-ascend", self.sort_ascend.get_active())
|
||||||
else:
|
else:
|
||||||
self.settings.set_string("sort-mode", self.buttons_to_sort_modes[button])
|
self.settings.set_string("sort-mode", self.buttons_to_sort_modes[button])
|
||||||
|
|
||||||
self.adp.update_sort_mode()
|
self.adp.update_sort_mode()
|
||||||
self.ldp.update_sort_mode()
|
self.ldp.update_sort_mode()
|
||||||
|
|
||||||
def load_sort_settings(self):
|
def load_sort_settings(self):
|
||||||
mode = self.settings.get_string("sort-mode")
|
mode = self.settings.get_string("sort-mode")
|
||||||
ascend = self.settings.get_boolean("sort-ascend")
|
ascend = self.settings.get_boolean("sort-ascend")
|
||||||
self.sort_modes_to_buttons[mode].set_active(True)
|
self.sort_modes_to_buttons[mode].set_active(True)
|
||||||
(self.sort_ascend if ascend else self.sort_descend).set_active(True)
|
(self.sort_ascend if ascend else self.sort_descend).set_active(True)
|
||||||
self.adp.update_sort_mode()
|
self.adp.update_sort_mode()
|
||||||
self.ldp.update_sort_mode()
|
self.ldp.update_sort_mode()
|
||||||
|
|
||||||
def view_change_handler(self, *args):
|
def view_change_handler(self, *args):
|
||||||
child = self.stack.get_visible_child()
|
child = self.stack.get_visible_child()
|
||||||
if child.total_size == 0:
|
if child.total_size == 0:
|
||||||
self.search_button.set_active(False)
|
self.search_button.set_active(False)
|
||||||
self.search_button.set_sensitive(False)
|
self.search_button.set_sensitive(False)
|
||||||
self.select_button.set_active(False)
|
self.select_button.set_active(False)
|
||||||
self.select_button.set_sensitive(False)
|
self.select_button.set_sensitive(False)
|
||||||
self.sort_button.set_active(False)
|
self.sort_button.set_active(False)
|
||||||
self.sort_button.set_sensitive(False)
|
self.sort_button.set_sensitive(False)
|
||||||
self.search_entry.set_editable(False)
|
self.search_entry.set_editable(False)
|
||||||
else:
|
else:
|
||||||
self.search_button.set_sensitive(True)
|
self.search_button.set_sensitive(True)
|
||||||
self.select_button.set_sensitive(True)
|
self.select_button.set_sensitive(True)
|
||||||
self.sort_button.set_sensitive(True)
|
self.sort_button.set_sensitive(True)
|
||||||
self.search_entry.set_editable(True)
|
self.search_entry.set_editable(True)
|
||||||
|
|
||||||
self.more_button.set_visible(child is self.ldp and self.bpt_is_applied)
|
self.more_button.set_visible(child is self.ldp and self.bpt_is_applied)
|
||||||
self.install_button.set_visible(child is self.ldp and not self.bpt_is_applied)
|
self.install_button.set_visible(child is self.ldp and not self.bpt_is_applied)
|
||||||
self.trash_button.set_visible(child is self.adp or not self.bpt_is_applied)
|
self.trash_button.set_visible(child is self.adp or not self.bpt_is_applied)
|
||||||
|
|
||||||
has_selected = len(child.selected_boxes) > 0
|
has_selected = len(child.selected_boxes) > 0
|
||||||
self.copy_button.set_sensitive(has_selected)
|
self.copy_button.set_sensitive(has_selected)
|
||||||
self.trash_button.set_sensitive(has_selected)
|
self.trash_button.set_sensitive(has_selected)
|
||||||
self.install_button.set_sensitive(has_selected)
|
self.install_button.set_sensitive(has_selected)
|
||||||
self.more_button.set_sensitive(has_selected)
|
self.more_button.set_sensitive(has_selected)
|
||||||
|
|
||||||
def select_toggle_handler(self, *args):
|
def select_toggle_handler(self, *args):
|
||||||
active = self.select_button.get_active()
|
active = self.select_button.get_active()
|
||||||
self.adp.set_selection_mode(active)
|
self.adp.set_selection_mode(active)
|
||||||
self.ldp.set_selection_mode(active)
|
self.ldp.set_selection_mode(active)
|
||||||
if not active:
|
if not active:
|
||||||
self.copy_button.set_sensitive(False)
|
self.copy_button.set_sensitive(False)
|
||||||
self.trash_button.set_sensitive(False)
|
self.trash_button.set_sensitive(False)
|
||||||
self.install_button.set_sensitive(False)
|
self.install_button.set_sensitive(False)
|
||||||
self.more_button.set_sensitive(False)
|
self.more_button.set_sensitive(False)
|
||||||
|
|
||||||
def select_all_handler(self, *args):
|
def select_all_handler(self, *args):
|
||||||
child = self.stack.get_visible_child()
|
child = self.stack.get_visible_child()
|
||||||
child.select_all_handler()
|
child.select_all_handler()
|
||||||
|
|
||||||
def copy_handler(self, *args):
|
def copy_handler(self, *args):
|
||||||
child = self.stack.get_visible_child()
|
child = self.stack.get_visible_child()
|
||||||
to_copy = ""
|
to_copy = ""
|
||||||
for box in child.selected_boxes:
|
for box in child.selected_boxes:
|
||||||
to_copy += "\n" + box.data_path
|
to_copy += "\n" + box.data_path
|
||||||
|
|
||||||
if len(to_copy) == 0:
|
if len(to_copy) == 0:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not copy paths"), _("No boxes were selected")).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not copy paths"), _("No boxes were selected")).toast)
|
||||||
else:
|
else:
|
||||||
HostInfo.clipboard.set(to_copy.replace("\n", "", 1))
|
HostInfo.clipboard.set(to_copy.replace("\n", "", 1))
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=_("Copied paths")))
|
self.toast_overlay.add_toast(Adw.Toast(title=_("Copied paths")))
|
||||||
|
|
||||||
def selection_trash_handler(self, *args):
|
def selection_trash_handler(self, *args):
|
||||||
error = [None]
|
error = [None]
|
||||||
child = self.stack.get_visible_child()
|
child = self.stack.get_visible_child()
|
||||||
|
|
||||||
def thread(path):
|
def thread(path):
|
||||||
cmd = ['gio', 'trash'] + path
|
cmd = ['gio', 'trash'] + path
|
||||||
try:
|
try:
|
||||||
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
subprocess.run(cmd, check=True, capture_output=True, text=True)
|
||||||
properties_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].properties_page
|
properties_page = HostInfo.main_window.pages[HostInfo.main_window.packages_row].properties_page
|
||||||
properties_package = properties_page.package
|
properties_package = properties_page.package
|
||||||
if not properties_package is None:
|
if not properties_package is None:
|
||||||
properties_page.set_properties(properties_package, True)
|
properties_page.set_properties(properties_package, True)
|
||||||
|
|
||||||
snapshot_list_page = HostInfo.main_window.pages[HostInfo.main_window.snapshots_row].list_page
|
snapshot_list_page = HostInfo.main_window.pages[HostInfo.main_window.snapshots_row].list_page
|
||||||
snapshot_list_package = snapshot_list_page.package_or_folder
|
snapshot_list_package = snapshot_list_page.package_or_folder
|
||||||
if not snapshot_list_package is None:
|
if not snapshot_list_package is None:
|
||||||
snapshot_list_page.set_snapshots(snapshot_list_package, True)
|
snapshot_list_page.set_snapshots(snapshot_list_package, True)
|
||||||
|
|
||||||
except subprocess.CalledProcessError as cpe:
|
except subprocess.CalledProcessError as cpe:
|
||||||
error[0] = cpe.stderr
|
error[0] = cpe.stderr
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error[0] = e
|
error[0] = e
|
||||||
|
|
||||||
def callback(*args):
|
def callback(*args):
|
||||||
self.start_loading()
|
self.start_loading()
|
||||||
self.end_loading()
|
self.end_loading()
|
||||||
if error[0]:
|
if error[0]:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(error[0])).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), str(error[0])).toast)
|
||||||
else:
|
else:
|
||||||
self.toast_overlay.add_toast(Adw.Toast(title=_("Trashed data")))
|
self.toast_overlay.add_toast(Adw.Toast(title=_("Trashed data")))
|
||||||
|
|
||||||
def on_response(dialog, response):
|
def on_response(dialog, response):
|
||||||
self.is_trash_dialog_open = False
|
self.is_trash_dialog_open = False
|
||||||
if response != "continue":
|
if response != "continue":
|
||||||
return
|
return
|
||||||
|
|
||||||
to_trash = []
|
to_trash = []
|
||||||
for box in child.selected_boxes:
|
for box in child.selected_boxes:
|
||||||
to_trash.append(box.data_path)
|
to_trash.append(box.data_path)
|
||||||
|
|
||||||
if len(to_trash) == 0:
|
if len(to_trash) == 0:
|
||||||
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), _("No boxes were selected")).toast)
|
self.toast_overlay.add_toast(ErrorToast(_("Could not trash data"), _("No boxes were selected")).toast)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.select_button.set_active(False)
|
self.select_button.set_active(False)
|
||||||
self.status_stack.set_visible_child(self.loading_view)
|
self.status_stack.set_visible_child(self.loading_view)
|
||||||
Gio.Task.new(None, None, callback).run_in_thread(lambda *_: thread(to_trash))
|
Gio.Task.new(None, None, callback).run_in_thread(lambda *_: thread(to_trash))
|
||||||
|
|
||||||
if len(child.selected_boxes) < 1 or self.is_trash_dialog_open:
|
if len(child.selected_boxes) < 1 or self.is_trash_dialog_open:
|
||||||
return
|
return
|
||||||
|
|
||||||
self.is_trash_dialog_open = True
|
self.is_trash_dialog_open = True
|
||||||
dialog = Adw.AlertDialog(heading=_("Trash Data?"), body=_("Data will be sent to the trash"))
|
dialog = Adw.AlertDialog(heading=_("Trash Data?"), body=_("Data will be sent to the trash"))
|
||||||
dialog.add_response("cancel", _("Cancel"))
|
dialog.add_response("cancel", _("Cancel"))
|
||||||
dialog.add_response("continue", _("Continue"))
|
dialog.add_response("continue", _("Continue"))
|
||||||
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
dialog.set_response_appearance("continue", Adw.ResponseAppearance.DESTRUCTIVE)
|
||||||
dialog.connect("response", on_response)
|
dialog.connect("response", on_response)
|
||||||
dialog.present(ErrorToast.main_window)
|
dialog.present(ErrorToast.main_window)
|
||||||
|
|
||||||
def breakpoint_handler(self, bpt, is_applied):
|
def breakpoint_handler(self, bpt, is_applied):
|
||||||
self.bpt_is_applied = is_applied
|
self.bpt_is_applied = is_applied
|
||||||
self.adp.label_box.set_orientation(Gtk.Orientation.VERTICAL if is_applied else Gtk.Orientation.HORIZONTAL)
|
self.adp.label_box.set_orientation(Gtk.Orientation.VERTICAL if is_applied else Gtk.Orientation.HORIZONTAL)
|
||||||
self.ldp.label_box.set_orientation(Gtk.Orientation.VERTICAL if is_applied else Gtk.Orientation.HORIZONTAL)
|
self.ldp.label_box.set_orientation(Gtk.Orientation.VERTICAL if is_applied else Gtk.Orientation.HORIZONTAL)
|
||||||
child = self.stack.get_visible_child()
|
child = self.stack.get_visible_child()
|
||||||
self.install_button.set_visible(child is self.ldp and not is_applied)
|
self.install_button.set_visible(child is self.ldp and not is_applied)
|
||||||
self.more_button.set_visible(child is self.ldp and is_applied)
|
self.more_button.set_visible(child is self.ldp and is_applied)
|
||||||
self.trash_button.set_visible(child is self.adp or not is_applied)
|
self.trash_button.set_visible(child is self.adp or not is_applied)
|
||||||
|
|
||||||
def install_handler(self, *args):
|
def install_handler(self, *args):
|
||||||
child = self.stack.get_visible_child()
|
child = self.stack.get_visible_child()
|
||||||
package_names = []
|
package_names = []
|
||||||
for box in child.selected_boxes:
|
for box in child.selected_boxes:
|
||||||
package_names.append(box.subtitle)
|
package_names.append(box.subtitle)
|
||||||
|
|
||||||
AttemptInstallDialog(package_names, lambda is_valid: self.select_button.set_active(not is_valid))
|
AttemptInstallDialog(package_names, lambda is_valid: self.select_button.set_active(not is_valid))
|
||||||
|
|
||||||
def more_menu_handler(self, listbox, row):
|
def more_menu_handler(self, listbox, row):
|
||||||
self.more_popover.popdown()
|
self.more_popover.popdown()
|
||||||
row = row.get_child()
|
row = row.get_child()
|
||||||
match row:
|
match row:
|
||||||
case self.more_install:
|
case self.more_install:
|
||||||
self.install_handler()
|
self.install_handler()
|
||||||
case self.more_trash:
|
case self.more_trash:
|
||||||
self.selection_trash_handler()
|
self.selection_trash_handler()
|
||||||
|
|
||||||
def __init__(self, main_window, **kwargs):
|
def __init__(self, main_window, **kwargs):
|
||||||
super().__init__(**kwargs)
|
super().__init__(**kwargs)
|
||||||
|
|
||||||
# Extra Object Creation
|
# Extra Object Creation
|
||||||
self.__class__.instance = self
|
self.__class__.instance = self
|
||||||
self.adp = DataSubpage(_("Active Data"), self, True, main_window)
|
self.adp = DataSubpage(_("Active Data"), self, True, main_window)
|
||||||
self.ldp = DataSubpage(_("Leftover Data"), self, False, main_window)
|
self.ldp = DataSubpage(_("Leftover Data"), self, False, main_window)
|
||||||
self.data_flatpaks = []
|
self.data_flatpaks = []
|
||||||
self.active_data = []
|
self.active_data = []
|
||||||
self.leftover_data = []
|
self.leftover_data = []
|
||||||
self.total_items = 0
|
self.total_items = 0
|
||||||
self.settings = Gio.Settings.new("io.github.flattool.Warehouse.data_page")
|
self.settings = Gio.Settings.new("io.github.flattool.Warehouse.data_page")
|
||||||
self.sort_modes_to_buttons = {
|
self.sort_modes_to_buttons = {
|
||||||
"name": self.sort_name,
|
"name": self.sort_name,
|
||||||
"id": self.sort_id,
|
"id": self.sort_id,
|
||||||
"size": self.sort_size,
|
"size": self.sort_size,
|
||||||
}
|
}
|
||||||
self.buttons_to_sort_modes = {}
|
self.buttons_to_sort_modes = {}
|
||||||
self.on_backspace_handler = self.selection_trash_handler
|
self.on_backspace_handler = self.selection_trash_handler
|
||||||
self.on_escape_handler = lambda *_: self.select_button.set_active(False)
|
self.on_escape_handler = lambda *_: self.select_button.set_active(False)
|
||||||
|
|
||||||
# Apply
|
# Apply
|
||||||
for key, button in self.sort_modes_to_buttons.items():
|
for key, button in self.sort_modes_to_buttons.items():
|
||||||
self.buttons_to_sort_modes[button] = key
|
self.buttons_to_sort_modes[button] = key
|
||||||
|
|
||||||
self.stack.add_titled_with_icon(
|
self.stack.add_titled_with_icon(
|
||||||
child=self.adp,
|
child=self.adp,
|
||||||
name="active",
|
name="active",
|
||||||
title=_("Active Data"),
|
title=_("Active Data"),
|
||||||
icon_name="file-manager-symbolic",
|
icon_name="file-manager-symbolic",
|
||||||
)
|
)
|
||||||
self.stack.add_titled_with_icon(
|
self.stack.add_titled_with_icon(
|
||||||
child=self.ldp,
|
child=self.ldp,
|
||||||
name="leftover",
|
name="leftover",
|
||||||
title=_("Leftover Data"),
|
title=_("Leftover Data"),
|
||||||
icon_name="folder-templates-symbolic",
|
icon_name="folder-templates-symbolic",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Connections
|
# Connections
|
||||||
self.stack.connect("notify::visible-child", self.view_change_handler)
|
self.stack.connect("notify::visible-child", self.view_change_handler)
|
||||||
self.select_button.connect("toggled", self.select_toggle_handler)
|
self.select_button.connect("toggled", self.select_toggle_handler)
|
||||||
self.select_all_button.connect("clicked", self.select_all_handler)
|
self.select_all_button.connect("clicked", self.select_all_handler)
|
||||||
self.copy_button.connect("clicked", self.copy_handler)
|
self.copy_button.connect("clicked", self.copy_handler)
|
||||||
self.trash_button.connect("clicked", self.selection_trash_handler)
|
self.trash_button.connect("clicked", self.selection_trash_handler)
|
||||||
self.install_button.connect("clicked", self.install_handler)
|
self.install_button.connect("clicked", self.install_handler)
|
||||||
self.more_menu.connect("row-activated", self.more_menu_handler)
|
self.more_menu.connect("row-activated", self.more_menu_handler)
|
||||||
self.sort_ascend.connect("clicked", self.sort_button_handler)
|
self.sort_ascend.connect("clicked", self.sort_button_handler)
|
||||||
self.sort_descend.connect("clicked", self.sort_button_handler)
|
self.sort_descend.connect("clicked", self.sort_button_handler)
|
||||||
self.sort_name.connect("clicked", self.sort_button_handler)
|
self.sort_name.connect("clicked", self.sort_button_handler)
|
||||||
self.sort_id.connect("clicked", self.sort_button_handler)
|
self.sort_id.connect("clicked", self.sort_button_handler)
|
||||||
self.sort_size.connect("clicked", self.sort_button_handler)
|
self.sort_size.connect("clicked", self.sort_button_handler)
|
||||||
self.bpt.connect("apply", self.breakpoint_handler, True)
|
self.bpt.connect("apply", self.breakpoint_handler, True)
|
||||||
self.bpt.connect("unapply", self.breakpoint_handler, False)
|
self.bpt.connect("unapply", self.breakpoint_handler, False)
|
||||||
|
|
||||||
# Apply again
|
# Apply again
|
||||||
self.loading_view.set_content(LoadingStatus(_("Loading User Data"), _("This should only take a moment")))
|
self.loading_view.set_content(LoadingStatus(_("Loading User Data"), _("This should only take a moment")))
|
||||||
self.search_bar.set_key_capture_widget(main_window)
|
self.search_bar.set_key_capture_widget(main_window)
|
||||||
self.load_sort_settings()
|
self.load_sort_settings()
|
||||||
|
|||||||
@@ -1,85 +1,85 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<gresources>
|
<gresources>
|
||||||
<gresource prefix="/io/github/flattool/Warehouse">
|
<gresource prefix="/io/github/flattool/Warehouse">
|
||||||
<file alias="style.css">../data/style.css</file>
|
<file alias="style.css">../data/style.css</file>
|
||||||
<file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
|
<file preprocess="xml-stripblanks">gtk/help-overlay.ui</file>
|
||||||
<file preprocess="xml-stripblanks">gtk/loading_status.ui</file>
|
<file preprocess="xml-stripblanks">gtk/loading_status.ui</file>
|
||||||
<file preprocess="xml-stripblanks">gtk/app_row.ui</file>
|
<file preprocess="xml-stripblanks">gtk/app_row.ui</file>
|
||||||
<file preprocess="xml-stripblanks">gtk/installation_chooser.ui</file>
|
<file preprocess="xml-stripblanks">gtk/installation_chooser.ui</file>
|
||||||
<file preprocess="xml-stripblanks">gtk/attempt_install_dialog.ui</file>
|
<file preprocess="xml-stripblanks">gtk/attempt_install_dialog.ui</file>
|
||||||
<file preprocess="xml-stripblanks">main_window/window.ui</file>
|
<file preprocess="xml-stripblanks">main_window/window.ui</file>
|
||||||
<file preprocess="xml-stripblanks">packages_page/packages_page.ui</file>
|
<file preprocess="xml-stripblanks">packages_page/packages_page.ui</file>
|
||||||
<file preprocess="xml-stripblanks">packages_page/filters_page.ui</file>
|
<file preprocess="xml-stripblanks">packages_page/filters_page.ui</file>
|
||||||
<file preprocess="xml-stripblanks">packages_page/uninstall_dialog.ui</file>
|
<file preprocess="xml-stripblanks">packages_page/uninstall_dialog.ui</file>
|
||||||
<file preprocess="xml-stripblanks">properties_page/properties_page.ui</file>
|
<file preprocess="xml-stripblanks">properties_page/properties_page.ui</file>
|
||||||
<file preprocess="xml-stripblanks">change_version_page/change_version_page.ui</file>
|
<file preprocess="xml-stripblanks">change_version_page/change_version_page.ui</file>
|
||||||
<file preprocess="xml-stripblanks">user_data_page/data_box.ui</file>
|
<file preprocess="xml-stripblanks">user_data_page/data_box.ui</file>
|
||||||
<file preprocess="xml-stripblanks">user_data_page/user_data_page.ui</file>
|
<file preprocess="xml-stripblanks">user_data_page/user_data_page.ui</file>
|
||||||
<file preprocess="xml-stripblanks">user_data_page/data_subpage.ui</file>
|
<file preprocess="xml-stripblanks">user_data_page/data_subpage.ui</file>
|
||||||
<file preprocess="xml-stripblanks">remotes_page/remotes_page.ui</file>
|
<file preprocess="xml-stripblanks">remotes_page/remotes_page.ui</file>
|
||||||
<file preprocess="xml-stripblanks">remotes_page/remote_row.ui</file>
|
<file preprocess="xml-stripblanks">remotes_page/remote_row.ui</file>
|
||||||
<file preprocess="xml-stripblanks">remotes_page/add_remote_dialog.ui</file>
|
<file preprocess="xml-stripblanks">remotes_page/add_remote_dialog.ui</file>
|
||||||
<file preprocess="xml-stripblanks">snapshot_page/snapshot_page.ui</file>
|
<file preprocess="xml-stripblanks">snapshot_page/snapshot_page.ui</file>
|
||||||
<file preprocess="xml-stripblanks">snapshot_page/snapshots_list_page.ui</file>
|
<file preprocess="xml-stripblanks">snapshot_page/snapshots_list_page.ui</file>
|
||||||
<file preprocess="xml-stripblanks">snapshot_page/snapshot_box.ui</file>
|
<file preprocess="xml-stripblanks">snapshot_page/snapshot_box.ui</file>
|
||||||
<file preprocess="xml-stripblanks">snapshot_page/new_snapshot_dialog.ui</file>
|
<file preprocess="xml-stripblanks">snapshot_page/new_snapshot_dialog.ui</file>
|
||||||
<file preprocess="xml-stripblanks">install_page/file_install_dialog.ui</file>
|
<file preprocess="xml-stripblanks">install_page/file_install_dialog.ui</file>
|
||||||
<file preprocess="xml-stripblanks">install_page/install_page.ui</file>
|
<file preprocess="xml-stripblanks">install_page/install_page.ui</file>
|
||||||
<file preprocess="xml-stripblanks">install_page/result_row.ui</file>
|
<file preprocess="xml-stripblanks">install_page/result_row.ui</file>
|
||||||
<file preprocess="xml-stripblanks">install_page/select_page.ui</file>
|
<file preprocess="xml-stripblanks">install_page/select_page.ui</file>
|
||||||
<file preprocess="xml-stripblanks">install_page/results_page.ui</file>
|
<file preprocess="xml-stripblanks">install_page/results_page.ui</file>
|
||||||
<file preprocess="xml-stripblanks">install_page/pending_page.ui</file>
|
<file preprocess="xml-stripblanks">install_page/pending_page.ui</file>
|
||||||
<!-- <file preprocess="xml-stripblanks">../data/io.github.flattool.Warehouse.metainfo.xml.in</file> -->
|
<!-- <file preprocess="xml-stripblanks">../data/io.github.flattool.Warehouse.metainfo.xml.in</file> -->
|
||||||
</gresource>
|
</gresource>
|
||||||
<gresource prefix="/io/github/flattool/Warehouse/icons/scalable/actions/">
|
<gresource prefix="/io/github/flattool/Warehouse/icons/scalable/actions/">
|
||||||
<file preprocess="xml-stripblanks" alias="selection-mode-symbolic.svg">../data/icons/selection-mode-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="selection-mode-symbolic.svg">../data/icons/selection-mode-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="error-symbolic.svg">../data/icons/error-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="error-symbolic.svg">../data/icons/error-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="user-trash-symbolic.svg">../data/icons/user-trash-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="user-trash-symbolic.svg">../data/icons/user-trash-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="folder-visiting-symbolic.svg">../data/icons/folder-visiting-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="folder-visiting-symbolic.svg">../data/icons/folder-visiting-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="info-symbolic.svg">../data/icons/info-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="info-symbolic.svg">../data/icons/info-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="check-plain-symbolic.svg">../data/icons/check-plain-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="check-plain-symbolic.svg">../data/icons/check-plain-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="paper-filled-symbolic.svg">../data/icons/paper-filled-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="paper-filled-symbolic.svg">../data/icons/paper-filled-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="plus-large-symbolic.svg">../data/icons/plus-large-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="plus-large-symbolic.svg">../data/icons/plus-large-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="funnel-symbolic.svg">../data/icons/funnel-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="funnel-symbolic.svg">../data/icons/funnel-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="flatpak-symbolic.svg">../data/icons/flatpak-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="flatpak-symbolic.svg">../data/icons/flatpak-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="right-large-symbolic.svg">../data/icons/right-large-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="right-large-symbolic.svg">../data/icons/right-large-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="view-more-symbolic.svg">../data/icons/view-more-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="view-more-symbolic.svg">../data/icons/view-more-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="clock-alt-symbolic.svg">../data/icons/clock-alt-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="clock-alt-symbolic.svg">../data/icons/clock-alt-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="arrow2-top-right-symbolic.svg">../data/icons/arrow2-top-right-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="arrow2-top-right-symbolic.svg">../data/icons/arrow2-top-right-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="software-update-available-symbolic.svg">../data/icons/software-update-available-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="software-update-available-symbolic.svg">../data/icons/software-update-available-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="software-update-urgent-symbolic.svg">../data/icons/software-update-urgent-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="software-update-urgent-symbolic.svg">../data/icons/software-update-urgent-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="cross-filled-symbolic.svg">../data/icons/cross-filled-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="cross-filled-symbolic.svg">../data/icons/cross-filled-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="important-small-symbolic.svg">../data/icons/important-small-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="important-small-symbolic.svg">../data/icons/important-small-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="eye-not-looking-symbolic.svg">../data/icons/eye-not-looking-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="eye-not-looking-symbolic.svg">../data/icons/eye-not-looking-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="eye-open-negative-filled-symbolic.svg">../data/icons/eye-open-negative-filled-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="eye-open-negative-filled-symbolic.svg">../data/icons/eye-open-negative-filled-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="left-large-symbolic.svg">../data/icons/left-large-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="left-large-symbolic.svg">../data/icons/left-large-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="arrow-turn-left-down-symbolic.svg">../data/icons/arrow-turn-left-down-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="arrow-turn-left-down-symbolic.svg">../data/icons/arrow-turn-left-down-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="arrow-circular-top-right-symbolic.svg">../data/icons/arrow-circular-top-right-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="arrow-circular-top-right-symbolic.svg">../data/icons/arrow-circular-top-right-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="dock-left-symbolic.svg">../data/icons/dock-left-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="dock-left-symbolic.svg">../data/icons/dock-left-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="server-pick-symbolic.svg">../data/icons/server-pick-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="server-pick-symbolic.svg">../data/icons/server-pick-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="file-manager-symbolic.svg">../data/icons/file-manager-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="file-manager-symbolic.svg">../data/icons/file-manager-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="snapshots-alt-symbolic.svg">../data/icons/snapshots-alt-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="snapshots-alt-symbolic.svg">../data/icons/snapshots-alt-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="arrow-pointing-at-line-down-symbolic.svg">../data/icons/arrow-pointing-at-line-down-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="arrow-pointing-at-line-down-symbolic.svg">../data/icons/arrow-pointing-at-line-down-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="loupe-large-symbolic.svg">../data/icons/loupe-large-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="loupe-large-symbolic.svg">../data/icons/loupe-large-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="folder-open-symbolic.svg">../data/icons/folder-open-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="folder-open-symbolic.svg">../data/icons/folder-open-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="padlock2-symbolic.svg">../data/icons/padlock2-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="padlock2-symbolic.svg">../data/icons/padlock2-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="pin-symbolic.svg">../data/icons/pin-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="pin-symbolic.svg">../data/icons/pin-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="pin-small-symbolic.svg">../data/icons/pin-small-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="pin-small-symbolic.svg">../data/icons/pin-small-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="error-small-symbolic.svg">../data/icons/error-small-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="error-small-symbolic.svg">../data/icons/error-small-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="copy-symbolic.svg">../data/icons/copy-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="copy-symbolic.svg">../data/icons/copy-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="double-ended-arrows-vertical-symbolic.svg">../data/icons/double-ended-arrows-vertical-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="double-ended-arrows-vertical-symbolic.svg">../data/icons/double-ended-arrows-vertical-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="vertical-arrows-long-symbolic.svg">../data/icons/vertical-arrows-long-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="vertical-arrows-long-symbolic.svg">../data/icons/vertical-arrows-long-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="dot-symbolic.svg">../data/icons/dot-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="dot-symbolic.svg">../data/icons/dot-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="folder-templates-symbolic.svg">../data/icons/folder-templates-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="folder-templates-symbolic.svg">../data/icons/folder-templates-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="view-sort-ascending-symbolic.svg">../data/icons/view-sort-ascending-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="view-sort-ascending-symbolic.svg">../data/icons/view-sort-ascending-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="view-sort-descending-symbolic.svg">../data/icons/view-sort-descending-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="view-sort-descending-symbolic.svg">../data/icons/view-sort-descending-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="font-x-generic-symbolic.svg">../data/icons/font-x-generic-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="font-x-generic-symbolic.svg">../data/icons/font-x-generic-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="tag-outline-symbolic.svg">../data/icons/tag-outline-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="tag-outline-symbolic.svg">../data/icons/tag-outline-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="harddisk-symbolic.svg">../data/icons/harddisk-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="harddisk-symbolic.svg">../data/icons/harddisk-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="arrow-turn-down-right-symbolic.svg">../data/icons/arrow-turn-down-right-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="arrow-turn-down-right-symbolic.svg">../data/icons/arrow-turn-down-right-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="minus-large-symbolic.svg">../data/icons/minus-large-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="minus-large-symbolic.svg">../data/icons/minus-large-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="view-list-bullet-symbolic.svg">../data/icons/view-list-bullet-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="view-list-bullet-symbolic.svg">../data/icons/view-list-bullet-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="list-remove-all-symbolic.svg">../data/icons/list-remove-all-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="list-remove-all-symbolic.svg">../data/icons/list-remove-all-symbolic.svg</file>
|
||||||
<file preprocess="xml-stripblanks" alias="edit-symbolic.svg">../data/icons/edit-symbolic.svg</file>
|
<file preprocess="xml-stripblanks" alias="edit-symbolic.svg">../data/icons/edit-symbolic.svg</file>
|
||||||
</gresource>
|
</gresource>
|
||||||
</gresources>
|
</gresources>
|
||||||
|
|||||||
@@ -36,11 +36,11 @@ locale.textdomain('warehouse')
|
|||||||
gettext.install('warehouse', localedir)
|
gettext.install('warehouse', localedir)
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
import gi
|
import gi
|
||||||
|
|
||||||
from gi.repository import Gio
|
from gi.repository import Gio
|
||||||
resource = Gio.Resource.load(os.path.join(pkgdatadir, 'warehouse.gresource'))
|
resource = Gio.Resource.load(os.path.join(pkgdatadir, 'warehouse.gresource'))
|
||||||
resource._register()
|
resource._register()
|
||||||
|
|
||||||
from Warehouse import main
|
from Warehouse import main
|
||||||
sys.exit(main.main(VERSION))
|
sys.exit(main.main(VERSION))
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
<Project xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||||
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
xmlns:foaf="http://xmlns.com/foaf/0.1/"
|
||||||
xmlns:gnome="http://api.gnome.org/doap-extensions#"
|
xmlns:gnome="http://api.gnome.org/doap-extensions#"
|
||||||
xmlns="http://usefulinc.com/ns/doap#">
|
xmlns="http://usefulinc.com/ns/doap#">
|
||||||
|
|
||||||
<name xml:lang="en">Warehouse</name>
|
<name xml:lang="en">Warehouse</name>
|
||||||
<shortdesc xml:lang="en">Manage all things Flatpak</shortdesc>
|
<shortdesc xml:lang="en">Manage all things Flatpak</shortdesc>
|
||||||
<homepage rdf:resource="https://github.com/flattool/warehouse"/>
|
<homepage rdf:resource="https://github.com/flattool/warehouse"/>
|
||||||
<bug-database rdf:resource="https://github.com/flattool/warehouse/issues"/>
|
<bug-database rdf:resource="https://github.com/flattool/warehouse/issues"/>
|
||||||
<programming-language>Python</programming-language>
|
<programming-language>Python</programming-language>
|
||||||
<platform>GTK 4</platform>
|
<platform>GTK 4</platform>
|
||||||
<platform>Libadwaita</platform>
|
<platform>Libadwaita</platform>
|
||||||
|
|
||||||
<maintainer>
|
<maintainer>
|
||||||
<foaf:Person>
|
<foaf:Person>
|
||||||
<foaf:name>Heliguy</foaf:name>
|
<foaf:name>Heliguy</foaf:name>
|
||||||
<foaf:mbox rdf:resource="mailto:heliguy4599@hotmail.com"/>
|
<foaf:mbox rdf:resource="mailto:heliguy4599@hotmail.com"/>
|
||||||
</foaf:Person>
|
</foaf:Person>
|
||||||
</maintainer>
|
</maintainer>
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
Reference in New Issue
Block a user