feat: bury offline mode, ship antergos-layan-theme
remember when i thought an offline installer was a good idea? a pre-built squashfs that still needed internet to install DEs? "offline" in name only. it was a lie. a beautiful, naive lie. so i killed it. ripped out the mode picker, the connectivity check, the whole AskMode() circus. calamares-next now just shows a notice saying "yeah that wasnt actually offline" and launches directly into online mode. no more pretending. also fixed the QML nav button because having a greyed out "Next" on the last page is terrible UX. now it says "Done" and quits. youre welcome. and since we nuked the offline mode, might as well make the online mode look fucking spectacular. enter antergos-layan-theme: - kvantum theme because your apps deserve better than qt dreck - sddm theme (AntergosDark-Layan) for a sexy login screen - every plasma plasmoid from the rice (panel colorizer, netspeed, shutdown switch, applets, separators, music toolbar, the works) - yakuake arc-dark skin - antergos logos in /usr/share/logos and /usr/share/pixmaps - konsole profile, desktop appletsrc, all that jazz - kde_settings.conf now points to AntergosDark-Layan sddm theme also replaced the old antergos-grub-theme with the Layan grub theme because the old one was a git-sourced abomination. 75 distro icons, terminal box, selection widgets, antergos wallpaper. ships to /usr/share/grub/themes/antergos/ so calamares finds it. oh and a dozen audit fixes: - kscreenlockerrc wallpaper path fixed - kdialogrc no longer says some other distro - 7 stale knsregistry files deleted (dev machine was leaking) - systemd-inhibit guarded with command -v (dinit doesnt have it) - plasma restart uses killall not systemctl --user - "XeroLinux Toolkit" -> "Antergos Toolkit" in appletsrc - KDE_SESSION_VERSION=5 -> =6 (its 2026) - mirrorlist script says Artix not Arch - SDDM Messages.sh removed from build artifacts
@@ -0,0 +1,33 @@
|
||||
import org.kde.plasma.configuration
|
||||
|
||||
ConfigModel {
|
||||
ConfigCategory {
|
||||
name: i18n("General")
|
||||
icon: "preferences-desktop"
|
||||
source: "configuration/General.qml"
|
||||
}
|
||||
|
||||
ConfigCategory {
|
||||
name: i18n("Upgrade & Management")
|
||||
icon: "preferences-system-startup"
|
||||
source: "configuration/Upgrade.qml"
|
||||
}
|
||||
|
||||
ConfigCategory {
|
||||
name: i18n("Appearance")
|
||||
icon: "preferences-desktop-display-color"
|
||||
source: "configuration/Appearance.qml"
|
||||
}
|
||||
|
||||
ConfigCategory {
|
||||
name: i18n("Rules")
|
||||
icon: "preferences-system-windows-behavior"
|
||||
source: "configuration/Rules.qml"
|
||||
}
|
||||
|
||||
ConfigCategory {
|
||||
name: i18n("Support me")
|
||||
icon: "donate"
|
||||
source: "configuration/Support.qml"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
|
||||
http://www.kde.org/standards/kcfg/1.0/kcfg.xsd">
|
||||
|
||||
<kcfgfile name="apdatifier"/>
|
||||
<group name="General">
|
||||
<entry name="checkMode" type="string"><default>interval</default></entry>
|
||||
<entry name="intervalMinutes" type="int"><default>120</default></entry>
|
||||
<entry name="dailyHour" type="int"><default>20</default></entry>
|
||||
<entry name="dailyMinute" type="int"><default>30</default></entry>
|
||||
<entry name="weeklyDay" type="int"><default>0</default></entry>
|
||||
<entry name="weeklyHour" type="int"><default>20</default></entry>
|
||||
<entry name="weeklyMinute" type="int"><default>30</default></entry>
|
||||
<entry name="arch" type="bool"><default>true</default></entry>
|
||||
<entry name="aur" type="bool"><default>false</default></entry>
|
||||
<entry name="flatpak" type="bool"><default>false</default></entry>
|
||||
<entry name="widgets" type="bool"><default>false</default></entry>
|
||||
<entry name="fwupd" type="bool"><default>false</default></entry>
|
||||
<entry name="feedsEnabled" type="bool"><default>false</default></entry>
|
||||
<entry name="feedsFetchCount" type="int"><default>3</default></entry>
|
||||
<entry name="feeds" type="string"><default>https://archlinux.org/feeds/news/</default></entry>
|
||||
<entry name="middleAction" type="string"><default>upgradeSystem</default></entry>
|
||||
<entry name="rightAction" type="string"><default></default></entry>
|
||||
<entry name="scrollUpAction" type="string"><default>switchScheduler</default></entry>
|
||||
<entry name="scrollDownAction" type="string"><default>management</default></entry>
|
||||
<entry name="notifyUpdates" type="bool"><default>true</default></entry>
|
||||
<entry name="notifyUpdatesAction" type="bool"><default>false</default></entry>
|
||||
<entry name="notifyEveryBump" type="bool"><default>true</default></entry>
|
||||
<entry name="notifyNews" type="bool"><default>true</default></entry>
|
||||
<entry name="notifyNewsAction" type="bool"><default>false</default></entry>
|
||||
<entry name="notifyErrors" type="bool"><default>true</default></entry>
|
||||
<entry name="notifySound" type="bool"><default>true</default></entry>
|
||||
<entry name="notifyPersistent" type="bool"><default>false</default></entry>
|
||||
<entry name="checkConn" type="bool"><default>false</default></entry>
|
||||
<entry name="respectMeteredConn" type="bool"><default>false</default></entry>
|
||||
</group>
|
||||
<group name="Upgrade">
|
||||
<entry name="wrapper" type="string"><default></default></entry>
|
||||
<entry name="archFlags" type="string"><default></default></entry>
|
||||
<entry name="sudoBin" type="string"><default>sudo</default></entry>
|
||||
<entry name="rebootSystem" type="bool"><default>false</default></entry>
|
||||
<entry name="mirrors" type="string"><default>false</default></entry>
|
||||
<entry name="mirrorsAge" type="int"><default>7</default></entry>
|
||||
<entry name="mirrorCount" type="int"><default>5</default></entry>
|
||||
<entry name="mirrorProgress" type="bool"><default>false</default></entry>
|
||||
<entry name="dynamicUrl" type="string"><default>null</default></entry>
|
||||
<entry name="flatpakRemoveUnused" type="bool"><default>false</default></entry>
|
||||
<entry name="flatpakFlags" type="string"><default></default></entry>
|
||||
<entry name="widgetConfirmation" type="bool"><default>true</default></entry>
|
||||
<entry name="restartShell" type="bool"><default>true</default></entry>
|
||||
<entry name="restartCommand" type="string"><default>killall plasmashell</default></entry>
|
||||
<entry name="trayEnabledByDefault" type="bool"><default>false</default></entry>
|
||||
<entry name="preExec" type="string"><default></default></entry>
|
||||
<entry name="postExec" type="string"><default></default></entry>
|
||||
<entry name="terminal" type="string"><default></default></entry>
|
||||
<entry name="idleInhibit" type="bool"><default>false</default></entry>
|
||||
<entry name="tmuxSession" type="bool"><default>false</default></entry>
|
||||
<entry name="termFont" type="bool"><default>false</default></entry>
|
||||
<entry name="mngHideFlatpak" type="bool"><default>false</default></entry>
|
||||
<entry name="mngHideHeaders" type="bool"><default>false</default></entry>
|
||||
<entry name="mngHideHelp" type="bool"><default>false</default></entry>
|
||||
<entry name="mngHideNums" type="bool"><default>false</default></entry>
|
||||
<entry name="fzfOptions" type="string"><default></default></entry>
|
||||
</group>
|
||||
<group name="Appearance">
|
||||
<entry name="hideIconPolicy" type="int"><default>0</default></entry>
|
||||
<entry name="selectedIcon" type="string"><default>apdatifier-plasmoid</default></entry>
|
||||
<entry name="busyIndicator" type="string"><default>spinner</default></entry>
|
||||
<entry name="counterMode" type="string"><default>side</default></entry>
|
||||
<entry name="counterBadgePosition" type="string"><default>topRight</default></entry>
|
||||
<entry name="counterSidePosition" type="string"><default>left</default></entry>
|
||||
<entry name="counterGapsInner" type="int"><default>0</default></entry>
|
||||
<entry name="counterGapsOuter" type="int"><default>0</default></entry>
|
||||
<entry name="counterOffsetX" type="int"><default>0</default></entry>
|
||||
<entry name="counterOffsetY" type="int"><default>0</default></entry>
|
||||
<entry name="counterColor" type="string"><default></default></entry>
|
||||
<entry name="counterRadius" type="int"><default>3</default></entry>
|
||||
<entry name="counterOpacity" type="int"><default>8</default></entry>
|
||||
<entry name="counterFontFamily" type="string"><default></default></entry>
|
||||
<entry name="counterFontSize" type="int"><default>4</default></entry>
|
||||
<entry name="counterFontBold" type="bool"><default>true</default></entry>
|
||||
<entry name="pauseBadgePosition" type="string"><default>topLeft</default></entry>
|
||||
<entry name="updatedBadgePosition" type="string"><default>topRight</default></entry>
|
||||
<entry name="badgeOffsetX" type="int"><default>0</default></entry>
|
||||
<entry name="badgeOffsetY" type="int"><default>0</default></entry>
|
||||
<entry name="ownIconsUI" type="bool"><default>true</default></entry>
|
||||
<entry name="autoHideScrollBar" type="bool"><default>true</default></entry>
|
||||
<entry name="defaultTab" type="int"><default>0</default></entry>
|
||||
<entry name="switchDefaultTab" type="bool"><default>false</default></entry>
|
||||
<entry name="sorting" type="bool"><default>true</default></entry>
|
||||
<entry name="showStatusText" type="bool"><default>true</default></entry>
|
||||
<entry name="showToolBar" type="bool"><default>true</default></entry>
|
||||
<entry name="searchButton" type="bool"><default>true</default></entry>
|
||||
<entry name="intervalButton" type="bool"><default>true</default></entry>
|
||||
<entry name="sortButton" type="bool"><default>true</default></entry>
|
||||
<entry name="viewButton" type="bool"><default>true</default></entry>
|
||||
<entry name="managementButton" type="bool"><default>true</default></entry>
|
||||
<entry name="upgradeButton" type="bool"><default>true</default></entry>
|
||||
<entry name="checkButton" type="bool"><default>true</default></entry>
|
||||
<entry name="pinButton" type="bool"><default>true</default></entry>
|
||||
<entry name="settingsButton" type="bool"><default>true</default></entry>
|
||||
<entry name="compactInfo" type="string"><default>repository</default></entry>
|
||||
<entry name="extendedShowRepository" type="bool"><default>true</default></entry>
|
||||
<entry name="extendedShowInstallReason" type="bool"><default>true</default></entry>
|
||||
<entry name="highlightVersionDiff" type="bool"><default>false</default></entry>
|
||||
<entry name="colorizeInstallReason" type="bool"><default>false</default></entry>
|
||||
<entry name="tabBarVisible" type="bool"><default>true</default></entry>
|
||||
<entry name="tabBarTexts" type="bool"><default>true</default></entry>
|
||||
</group>
|
||||
<group name="Miscellaneous">
|
||||
<entry name="version" type="string"><default>v0</default></entry>
|
||||
<entry name="configMsg" type="bool"><default>true</default></entry>
|
||||
<entry name="dbPath" type="string"><default>${TMPDIR:-/tmp}/checkup-db-$(id -u)</default></entry>
|
||||
</group>
|
||||
</kcfg>
|
||||
@@ -0,0 +1,70 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
scriptDir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")"
|
||||
appletDir="${scriptDir%/contents/*}"
|
||||
configDir="${APDATIFIER_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/apdatifier}"
|
||||
cacheDir="${APDATIFIER_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/apdatifier}"
|
||||
localDir="${XDG_LOCAL_HOME:-$HOME/.local/share}"
|
||||
iconsDir="$localDir/icons/breeze/status/22"
|
||||
notifDir="$localDir/knotifications6"
|
||||
config="$configDir/config.conf"
|
||||
notif="apdatifier.notifyrc"
|
||||
icons=(apdatifier-plasmoid.svg
|
||||
apdatifier-packages.svg
|
||||
apdatifier-package.svg
|
||||
apdatifier-flatpak.svg)
|
||||
|
||||
test -d "$configDir" || mkdir -p "$configDir"
|
||||
test -f "$config" || touch "$config"
|
||||
|
||||
test -d "$cacheDir" || mkdir -p "$cacheDir"
|
||||
# Migrate cache files from old location
|
||||
[[ -f "$configDir/updates.json" && ! -f "$cacheDir/updates.json" ]] && mv "$configDir/updates.json" "$cacheDir/updates.json"
|
||||
[[ -f "$configDir/news.json" && ! -f "$cacheDir/news.json" ]] && mv "$configDir/news.json" "$cacheDir/news.json"
|
||||
|
||||
for dir in $iconsDir $notifDir; do
|
||||
test -d "$dir" || mkdir -p "$dir"
|
||||
done
|
||||
|
||||
for icon in "${icons[@]}"; do
|
||||
test -f "$iconsDir/$icon" || cp "$appletDir/contents/ui/assets/icons/$icon" "$iconsDir"
|
||||
done
|
||||
|
||||
test -d "$notifDir" && cat > "$notifDir/$notif" << EOF
|
||||
[Global]
|
||||
IconName=apdatifier-plasmoid
|
||||
Comment=Apdatifier
|
||||
|
||||
[Event/updates]
|
||||
Name=New updates
|
||||
Comment=Event when updates notification enabled without sound
|
||||
Action=Popup
|
||||
|
||||
[Event/updatesSound]
|
||||
Name=New updates (with sound)
|
||||
Comment=Event when updates notification enabled with sound
|
||||
Action=Popup|Sound
|
||||
Sound=service-login
|
||||
|
||||
[Event/error]
|
||||
Name=Error
|
||||
Comment=Event when error notification enabled without sound
|
||||
Action=Popup
|
||||
|
||||
[Event/errorSound]
|
||||
Name=Error (with sound)
|
||||
Comment=Event when errors notification enabled with sound
|
||||
Action=Popup|Sound
|
||||
Sound=dialog-error-serious
|
||||
|
||||
[Event/news]
|
||||
Name=News
|
||||
Comment=Event when news notification without sound
|
||||
Action=Popup
|
||||
|
||||
[Event/newsSound]
|
||||
Name=News (with sound)
|
||||
Comment=Event when news notification with sound
|
||||
Action=Popup|Sound
|
||||
Sound=dialog-information
|
||||
EOF
|
||||
@@ -0,0 +1,606 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
source "$(dirname "$0")/utils"
|
||||
|
||||
[ "$1" ] && selected="$1" || selected=0
|
||||
|
||||
if [[ $aur != true || -z $wrapper ]]; then
|
||||
wrapper="pacman"
|
||||
wrapper_sudo="$sudoBin $wrapper"
|
||||
else
|
||||
wrapper="${wrapper##*/}"
|
||||
wrapper_sudo=$wrapper
|
||||
fi
|
||||
|
||||
if [[ $termFont = "true" ]]; then
|
||||
ICO_MNG_01=""
|
||||
ICO_MNG_02=""
|
||||
ICO_MNG_03=""
|
||||
ICO_MNG_04=""
|
||||
ICO_MNG_05=""
|
||||
ICO_MNG_06=""
|
||||
ICO_MNG_07=""
|
||||
ICO_MNG_08=""
|
||||
ICO_MNG_09=""
|
||||
ICO_MNG_10=""
|
||||
ICO_MNG_11=""
|
||||
ICO_MNG_12=""
|
||||
ICO_MNG_13=""
|
||||
fi
|
||||
|
||||
fzf_settings="--preview-window=right:70%,border-none \
|
||||
--height=100% \
|
||||
--margin=0 \
|
||||
--padding=0 \
|
||||
--layout=reverse \
|
||||
--style minimal \
|
||||
--scrollbar \
|
||||
--color dark \
|
||||
--multi \
|
||||
--track \
|
||||
--exact \
|
||||
--cycle \
|
||||
--no-separator \
|
||||
--info=right \
|
||||
--marker=•"
|
||||
|
||||
[[ -n "$fzfOptions" ]] && fzf_settings+=" $fzfOptions"
|
||||
|
||||
show_help=false
|
||||
|
||||
|
||||
showMenu() {
|
||||
menu_type=$1
|
||||
opts=()
|
||||
|
||||
case $menu_type in
|
||||
main)
|
||||
opts+=("$ICO_MNG_01 $MNG_BROWSE")
|
||||
opts+=("$ICO_MNG_02 $MNG_INST")
|
||||
opts+=("$ICO_MNG_03 $MNG_EXPL")
|
||||
opts+=("$ICO_MNG_04 $MNG_ORPH")
|
||||
opts+=("$ICO_MNG_05 $MNG_DOWNGR")
|
||||
opts+=("$ICO_MNG_06 $MNG_CACHE")
|
||||
opts+=("$ICO_MNG_05 $MNG_REBUILD")
|
||||
opts+=("$ICO_MNG_08 $MNG_MIRROR")
|
||||
opts+=("$ICO_MNG_10 $MNG_RECENT")
|
||||
redraw
|
||||
case $selected in
|
||||
0) [[ $mngHideFlatpak = true ]] && fzfExec Slq || showMenu browse;;
|
||||
1) [[ $mngHideFlatpak = true ]] && fzfExec Qq || showMenu installed;;
|
||||
2) showMenu explicit;;
|
||||
3) showMenu orphans;;
|
||||
4) downgrade;;
|
||||
5) showMenu cache;;
|
||||
6) showMenu rebuild;;
|
||||
7) tui_exit; exec "$scriptDir/mirrorlist" menu "$selected";;
|
||||
8) recentlyInstalled;;
|
||||
9) showMenu main;;
|
||||
esac
|
||||
;;
|
||||
browse)
|
||||
selected=0
|
||||
opts+=("$ICO_MNG_11 pacman")
|
||||
opts+=("$ICO_MNG_11 flathub")
|
||||
redraw
|
||||
case $selected in
|
||||
0) fzfExec Slq;;
|
||||
1) flatpakFlathub;;
|
||||
2) selected=0; showMenu main;;
|
||||
esac
|
||||
;;
|
||||
installed)
|
||||
selected=0
|
||||
opts+=("$ICO_MNG_11 pacman")
|
||||
opts+=("$ICO_MNG_11 flatpak")
|
||||
redraw
|
||||
case $selected in
|
||||
0) fzfExec Qq;;
|
||||
1) flatpakInstalled;;
|
||||
2) selected=1; showMenu main;;
|
||||
esac
|
||||
;;
|
||||
explicit)
|
||||
selected=0
|
||||
opts+=("$ICO_MNG_03 $MNG_EXPL_L")
|
||||
opts+=("$ICO_MNG_04 $MNG_EXPL_U")
|
||||
redraw
|
||||
case $selected in
|
||||
0) fzfExec Qqe;;
|
||||
1) fzfExec Qqet;;
|
||||
2) selected=2; showMenu main;;
|
||||
esac
|
||||
;;
|
||||
orphans)
|
||||
selected=0
|
||||
opts+=("$ICO_MNG_06 $MNG_ORPH_L")
|
||||
opts+=("$ICO_MNG_07 $MNG_ORPH_R")
|
||||
redraw
|
||||
case $selected in
|
||||
0) fzfExec Qqtd;;
|
||||
1) uninstallOrphans;;
|
||||
2) selected=3; showMenu main;;
|
||||
esac
|
||||
;;
|
||||
cache)
|
||||
selected=0
|
||||
opts+=("$ICO_MNG_07 $MNG_CACHE_A")
|
||||
opts+=("$ICO_MNG_06 $MNG_CACHE_U")
|
||||
redraw
|
||||
case $selected in
|
||||
0) cleanCache Scc;;
|
||||
1) cleanCache Sc;;
|
||||
2) selected=5; showMenu main;;
|
||||
esac
|
||||
;;
|
||||
rebuild)
|
||||
selected=0
|
||||
opts+=("$ICO_MNG_05 $MNG_REBUILD_A")
|
||||
opts+=("$ICO_MNG_09 $MNG_REBUILD_P")
|
||||
redraw
|
||||
case $selected in
|
||||
0) fzfExec Qqm;;
|
||||
1) rebuildPython;;
|
||||
2) selected=6; showMenu main;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
draw_menu() {
|
||||
printf '\e[H'
|
||||
|
||||
local min_height=$((${#opts[@]} + 4))
|
||||
if ((LINES < min_height)); then
|
||||
printf '\e[2J'
|
||||
echo "Terminal too small (need ${min_height} lines)"
|
||||
return
|
||||
fi
|
||||
|
||||
|
||||
if [[ $mngHideHeaders == false ]]; then
|
||||
local header
|
||||
case $menu_type in
|
||||
main) header="$ICO_MNG_13 $MNG_HEADER";;
|
||||
browse) header="$ICO_MNG_12 $MNG_BROWSE_H";;
|
||||
installed) header="$ICO_MNG_11 $MNG_INST_H";;
|
||||
explicit) header="$ICO_MNG_03 $MNG_EXPL_H";;
|
||||
orphans) header="$ICO_MNG_04 $MNG_ORPH_H";;
|
||||
cache) header="$ICO_MNG_06 $MNG_CACHE_H";;
|
||||
rebuild) header="$ICO_MNG_05 $MNG_REBUILD_H";;
|
||||
esac
|
||||
|
||||
echo -e "$(colorize gray bold $header)\n"
|
||||
fi
|
||||
|
||||
|
||||
for i in "${!opts[@]}"; do
|
||||
|
||||
local number=$((i + 1))
|
||||
if [[ $mngHideNums == false ]]; then
|
||||
# if [[ $termFont = "true" ]]; then
|
||||
# case $number in
|
||||
# 1) number=;;
|
||||
# 2) number=;;
|
||||
# 3) number=;;
|
||||
# 4) number=;;
|
||||
# 5) number=;;
|
||||
# 6) number=;;
|
||||
# 7) number=;;
|
||||
# 8) number=;;
|
||||
# 9) number=;;
|
||||
# esac
|
||||
# number="$(colorize gray $number) "
|
||||
# else
|
||||
number="$(colorize gray $number.) "
|
||||
# fi
|
||||
else
|
||||
number=""
|
||||
fi
|
||||
|
||||
if [[ $i -eq $selected ]]; then
|
||||
echo "$number$(colorize green bold ${opts[$i]})"
|
||||
else
|
||||
echo "$number${opts[$i]# }"
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ $mngHideHelp == false ]]; then
|
||||
tput cup $((LINES - 2)) 0
|
||||
|
||||
local footer
|
||||
if [[ $show_help == true ]]; then
|
||||
footer='→ l Enter 1-9 · select ↑ k · up ↓ j · down ← h · back q · quit'
|
||||
else
|
||||
footer='? · help'
|
||||
fi
|
||||
printf "$(colorize gray '%s')%*s\n" "$footer" "$((COLUMNS - ${#footer}))" ""
|
||||
fi
|
||||
}
|
||||
|
||||
redraw() {
|
||||
clear_screen
|
||||
draw_menu
|
||||
handleInput
|
||||
}
|
||||
|
||||
handleInput() {
|
||||
while true; do
|
||||
read -rsN1 "${read_flags[@]}" input
|
||||
[[ -z $input ]] && { draw_menu; continue; }
|
||||
|
||||
if [[ $input == $'\e' ]]; then
|
||||
read -rsN2 -t 0.02 rest || rest=""
|
||||
input+="$rest"
|
||||
fi
|
||||
|
||||
case "$input" in
|
||||
k|$'\e[A') ((selected--)) ;;
|
||||
j|$'\e[B') ((selected++)) ;;
|
||||
l|$'\e[C'|$'\n'|$'\r') break ;;
|
||||
h|$'\e[D')
|
||||
if [[ $menu_type != main ]]; then
|
||||
selected=${#opts[@]}
|
||||
break
|
||||
fi
|
||||
;;
|
||||
$'\e')
|
||||
if [[ $menu_type != main ]]; then
|
||||
selected=${#opts[@]}
|
||||
break
|
||||
else
|
||||
exit
|
||||
fi
|
||||
;;
|
||||
[1-9]) (( input>0 && input<=${#opts[@]} )) && selected=$((input-1)) && break ;;
|
||||
'?') [[ $show_help == true ]] && show_help=false || show_help=true ;;
|
||||
q|Q) exit ;;
|
||||
esac
|
||||
|
||||
(( selected < 0 )) && selected=$((${#opts[@]} - 1))
|
||||
(( selected >= ${#opts[@]} )) && selected=0
|
||||
|
||||
draw_menu
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
fzfExec() {
|
||||
tui_exit
|
||||
dependency pacman fzf || return
|
||||
|
||||
argList="-$1"
|
||||
case $1 in
|
||||
Slq) argPreview="-Si"; argAction="-Syu";;
|
||||
Qqm) argPreview="-Qi"; argAction="-S --rebuild";;
|
||||
*) argPreview="-Qil"; argAction="-Rsn";;
|
||||
esac
|
||||
|
||||
if [[ $aur == true && $1 == "Slq" && $wrapper == "paru" ]]; then
|
||||
packages=$( ( pacman $argList; curl -fsSL https://aur.archlinux.org/packages.gz | gzip -dc ) | LC_ALL=C sort -u | fzf $fzf_settings --preview "$wrapper $argPreview {}" )
|
||||
else
|
||||
packages=$($wrapper $argList | LC_ALL=C sort -u | fzf $fzf_settings --preview "$wrapper $argPreview {}")
|
||||
fi
|
||||
|
||||
if [[ -n "$packages" ]]; then
|
||||
packages=$(echo "$packages" | oneLine)
|
||||
printExec "$wrapper_sudo $argAction $packages"
|
||||
$wrapper_sudo $argAction $packages
|
||||
pressReturn
|
||||
fi
|
||||
|
||||
tui_restore
|
||||
}
|
||||
|
||||
uninstallOrphans() {
|
||||
tui_exit
|
||||
dependency pacman || return
|
||||
|
||||
orphans=$($wrapper -Qqtd | oneLine)
|
||||
if [[ -n "$orphans" ]]; then
|
||||
printExec "$wrapper_sudo -Rsn $orphans"
|
||||
printImportant "$MNG_WARN"; echo
|
||||
$wrapper_sudo -Rsn $orphans
|
||||
else
|
||||
printDone "$MNG_DONE"
|
||||
fi
|
||||
|
||||
pressReturn
|
||||
tui_restore
|
||||
}
|
||||
|
||||
downgrade() {
|
||||
tui_exit
|
||||
dependency pacman fzf || return
|
||||
|
||||
pacman_cache_dir="$(pacman-conf CacheDir)"
|
||||
wrapper_cache_dir="$HOME/.cache/$wrapper"
|
||||
|
||||
files=$(find "$pacman_cache_dir" "$wrapper_cache_dir" -type f -name "*.pkg.tar.zst" -printf "%f\n" 2>/dev/null | fzf $fzf_settings)
|
||||
|
||||
if [[ -n "$files" ]]; then
|
||||
file_paths=()
|
||||
|
||||
for file in $files; do
|
||||
if [ -f "${pacman_cache_dir%/}/${file}" ]; then
|
||||
file_paths+=("${pacman_cache_dir%/}/${file}")
|
||||
else
|
||||
path=$(find "$wrapper_cache_dir" -type f -name "$file" 2>/dev/null)
|
||||
[[ -n "$path" ]] && file_paths+=("$path")
|
||||
fi
|
||||
done
|
||||
|
||||
if [ ${#file_paths[@]} -gt 0 ]; then
|
||||
cache="${file_paths[*]}"
|
||||
printExec "$wrapper_sudo -U $cache"
|
||||
$wrapper_sudo -U $cache
|
||||
fi
|
||||
|
||||
pressReturn
|
||||
fi
|
||||
|
||||
tui_restore
|
||||
}
|
||||
|
||||
cleanCache() {
|
||||
tui_exit
|
||||
dependency pacman || return
|
||||
|
||||
pacman_cache_dir="$(pacman-conf CacheDir | head -n1)"
|
||||
mapfile -t dirs < <(find "$pacman_cache_dir" -mindepth 1 -maxdepth 1 -type d)
|
||||
if ((${#dirs[@]})); then
|
||||
printImportant "$MNG_CACHE_DIR"
|
||||
printf ' %q\n' "${dirs[@]}"
|
||||
echo
|
||||
|
||||
while true; do
|
||||
printQuestion "$MNG_CACHE_DIR_RM"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*)
|
||||
[ -w "$pacman_cache_dir" ] && prefix="" || prefix="$sudoBin "
|
||||
cmd="${prefix}rm -rf"
|
||||
for dir in "${dirs[@]}"; do
|
||||
printf -v escaped '%q' "$dir"
|
||||
cmd+=" $escaped"
|
||||
done
|
||||
echo
|
||||
printExec "$cmd"
|
||||
if [ -n "$prefix" ]; then
|
||||
"$sudoBin" rm -rf "${dirs[@]}"
|
||||
else
|
||||
rm -rf "${dirs[@]}"
|
||||
fi
|
||||
break
|
||||
;;
|
||||
[Nn]*|"") break ;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo
|
||||
fi
|
||||
|
||||
printExec "$wrapper_sudo -$1"
|
||||
$wrapper_sudo -$1
|
||||
pressReturn
|
||||
tui_restore
|
||||
}
|
||||
|
||||
rebuildPython() {
|
||||
tui_exit
|
||||
|
||||
if [[ $wrapper = "pacman" ]]; then
|
||||
found=0
|
||||
for h in yay paru pikaur; do
|
||||
if command -v "$h" >/dev/null 2>&1; then
|
||||
wrapper_sudo="$h"
|
||||
found=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if [ "$found" -eq 0 ]; then
|
||||
printError "$CMD_ERR: yay/paru/pikaur"
|
||||
pressReturn
|
||||
tui_restore
|
||||
return
|
||||
fi
|
||||
fi
|
||||
|
||||
rebuild_dir=$(find /usr/lib -maxdepth 1 -type d -name "python*.*" | sort -V | tail -1)
|
||||
|
||||
rebuild_packages=$( { pacman -Qqo "$rebuild_dir" | pacman -Qqm - | oneLine; } 2>/dev/null)
|
||||
if [[ -z "$rebuild_packages" ]]; then
|
||||
printDone "$MNG_DONE"
|
||||
else
|
||||
printExec "$wrapper_sudo -S --rebuild" "$rebuild_packages"
|
||||
while true; do
|
||||
printQuestion "$MNG_RESUME"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) echo; break;;
|
||||
[Nn]*|"") echo; pressReturn;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
pacman -Qqo "$rebuild_dir" | pacman -Qqm - | $wrapper -S --rebuild -
|
||||
fi
|
||||
|
||||
pressReturn
|
||||
tui_restore
|
||||
}
|
||||
|
||||
recentlyInstalled() {
|
||||
tui_exit
|
||||
|
||||
echo "$MNG_RECENT_SCAN"
|
||||
|
||||
local log="/var/log/pacman.log"
|
||||
if [[ ! -f "$log" ]]; then
|
||||
printError "Pacman log file not found."
|
||||
pressReturn
|
||||
tui_restore
|
||||
return
|
||||
fi
|
||||
|
||||
local explicit_pkgs orphan_pkgs installed_pkgs
|
||||
installed_pkgs=$(pacman -Qq)
|
||||
explicit_pkgs=$(pacman -Qqe)
|
||||
orphan_pkgs=$(pacman -Qqtd 2>/dev/null)
|
||||
|
||||
declare -A pkg_date
|
||||
while IFS= read -r line; do
|
||||
[[ $line =~ ^\[([0-9-]+)T([0-9:]+)[^\]]*\]\ \[ALPM\]\ installed\ ([^\ ]+) ]] || continue
|
||||
pkg_date["${BASH_REMATCH[3]}"]="${BASH_REMATCH[1]} ${BASH_REMATCH[2]}"
|
||||
done < "$log"
|
||||
|
||||
local output=""
|
||||
for pkg in "${!pkg_date[@]}"; do
|
||||
echo "$installed_pkgs" | grep -qx "$pkg" || continue
|
||||
|
||||
local status status_color
|
||||
if echo "$orphan_pkgs" | grep -qx "$pkg"; then
|
||||
status="$MNG_RECENT_ORPHAN"; status_color="31"
|
||||
elif echo "$explicit_pkgs" | grep -qx "$pkg"; then
|
||||
status="$MNG_RECENT_EXPLICIT"; status_color="32"
|
||||
else
|
||||
status="$MNG_RECENT_DEPEND"; status_color="33"
|
||||
fi
|
||||
|
||||
output+="${pkg_date[$pkg]}|${pkg}|${status}|${status_color}\n"
|
||||
done
|
||||
|
||||
printf '\e[H\e[2J\e[3J'
|
||||
{
|
||||
printf "%s\t%s\t%s\n" "$(colorize gray bold DATE)" "$(colorize gray bold PACKAGE)" "$(colorize gray bold TYPE)"
|
||||
echo -e "$output" | sort -t'|' -k1 | tail -100 | while IFS='|' read -r date pkg status color; do
|
||||
[[ -z "$pkg" ]] && continue
|
||||
printf "\e[90m%s\e[0m\t%s\t\e[%sm%s\e[0m\n" "$date" "$pkg" "$color" "$status"
|
||||
done
|
||||
} | column -t -s$'\t'
|
||||
|
||||
pressReturn
|
||||
tui_restore
|
||||
}
|
||||
|
||||
flatpakInstalled() {
|
||||
tui_exit
|
||||
dependency flatpak fzf || return
|
||||
|
||||
local apps=$(
|
||||
flatpak list --app --columns=application,name \
|
||||
| sort -t$'\t' -k2,2f -k1,1 \
|
||||
| fzf $fzf_settings -d$'\t' --with-nth=2 --preview 'flatpak info {1}'
|
||||
)
|
||||
|
||||
if [[ -n "$apps" ]]; then
|
||||
local ids=$(echo "$apps" | cut -f1 | oneLine)
|
||||
printExec "flatpak uninstall $ids"
|
||||
flatpak uninstall $ids
|
||||
|
||||
if [ "$flatpakRemoveUnused" = true ]; then
|
||||
echo
|
||||
printExec "flatpak uninstall --unused $flatpakFlags"
|
||||
flatpak uninstall --unused $flatpakFlags
|
||||
fi
|
||||
|
||||
pressReturn
|
||||
fi
|
||||
|
||||
tui_restore
|
||||
}
|
||||
|
||||
flatpakFlathub() {
|
||||
tui_exit
|
||||
dependency flatpak fzf curl jq || return
|
||||
|
||||
local apps=$(curl -s https://flathub.org/api/v2/appstream | jq -r '.[]' | fzf $fzf_settings --preview "flatpak remote-info flathub {}")
|
||||
|
||||
if [[ -n "$apps" ]]; then
|
||||
apps=$(echo "$apps" | oneLine)
|
||||
printExec "flatpak install $apps"
|
||||
flatpak install $apps
|
||||
pressReturn
|
||||
fi
|
||||
|
||||
tui_restore
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
pressReturn() {
|
||||
printf '\e[?25l'
|
||||
echo -e "\n$(colorize blue bold $ICO_RETURN $MNG_RETURN)"
|
||||
IFS= read -rs
|
||||
}
|
||||
|
||||
tui_exit() {
|
||||
reset_terminal
|
||||
clear_screen
|
||||
}
|
||||
|
||||
tui_restore() {
|
||||
clear_screen
|
||||
setup_terminal
|
||||
showMenu $menu_type
|
||||
}
|
||||
|
||||
dependency() {
|
||||
for cmd in "$@"; do
|
||||
if ! command -v "$cmd" >/dev/null; then
|
||||
printError "${CMD_ERR} ${cmd}"
|
||||
pressReturn
|
||||
tui_restore
|
||||
return 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
get_term_size() {
|
||||
read -r LINES COLUMNS < <(stty size)
|
||||
}
|
||||
|
||||
setup_terminal() {
|
||||
printf '\e[?1049h\e[?7l\e[?25l\e[2J\e[H'
|
||||
stty -echo
|
||||
}
|
||||
|
||||
reset_terminal() {
|
||||
printf '\e[?7h\e[?25h\e[2J\e[;r\e[?1049l'
|
||||
stty echo
|
||||
}
|
||||
|
||||
clear_screen() {
|
||||
printf '\e[H\e[2J'
|
||||
}
|
||||
|
||||
main() {
|
||||
((BASH_VERSINFO[0] > 3)) && read_flags=(-t 0.05)
|
||||
|
||||
trap '' SIGINT
|
||||
trap 'reset_terminal' EXIT
|
||||
trap 'get_term_size; clear_screen; draw_menu' WINCH
|
||||
|
||||
get_term_size
|
||||
setup_terminal
|
||||
showMenu main
|
||||
}
|
||||
|
||||
main
|
||||
|
||||
|
||||
|
||||
# \e[?1049h Alternative screen buffer
|
||||
# \e[?1049l Restore main screen buffer
|
||||
# \e[?7l Disable line wrapping
|
||||
# \e[?7h Enable line wrapping
|
||||
# \e[?25l Hide cursor
|
||||
# \e[?25h Show cursor
|
||||
# \e[2J Clear terminal
|
||||
# \e[H Move cursor to top-left
|
||||
# \e[;r Set the scroll region to its default value
|
||||
# stty -echo Hide user input
|
||||
# stty echo Show user input
|
||||
@@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
UPGRADE_PROGRESS = i18n("Upgrade in progress")
|
||||
UPGRADE_FULL_ARCH = i18n("Full system upgrade")
|
||||
UPGRADE_FULL_FLATPAK = i18n("Flatpak Upgrade")
|
||||
UPGRADE_FULL_WIDGETS = i18n("Plasma Widgets Upgrade")
|
||||
UPGRADE_PACKAGE = i18n("Upgrade")
|
||||
UPGRADE_EXECTIME = i18n("Total execution time:")
|
||||
UPGRADE_CRITICAL = i18n("Critical package(s) updated, reboot may be required:")
|
||||
UPGRADE_REBOOT = i18n("Do you want to reboot now?")
|
||||
UPGRADE_REBOOTING = i18n("The system is going to reboot now...")
|
||||
UPGRADE_ENTER = i18n("Press Enter to close")
|
||||
UPGRADE_SKIPPED = i18n("Skipped...")
|
||||
UPGRADE_COMPLETE = i18n("Upgrade complete, close the terminal")
|
||||
|
||||
MIRRORS_FETCH = i18n("Fetching the latest filtered mirror list")
|
||||
MIRRORS_RANK = i18n("Ranking mirrors by their connection and opening speed")
|
||||
MIRRORS_ERR = i18n("Check your mirrorlist generator settings...")
|
||||
MIRRORS_UPD = i18n("was updated with the following servers:")
|
||||
MIRRORS_SUDO = i18n("To write to the mirrorlist file, sudo privileges are required")
|
||||
MIRROR_TIME = i18n("Your current mirrorlist:")
|
||||
MIRROR_REFRESH = i18n("Refresh mirror list?")
|
||||
|
||||
WIDGETS_WARN = i18n("For some widgets you may need to Log Out or restart plasmashell after upgrade")
|
||||
WIDGETS_CHECK = i18n("Checking widgets for updates")
|
||||
WIDGETS_FETCH = i18n("Fetching data from the API")
|
||||
WIDGETS_LINK = i18n("Getting the download link")
|
||||
WIDGETS_DOWNLOADING = i18n("Downloading package")
|
||||
WIDGETS_PROCEED = i18n("Proceed with upgrade?")
|
||||
WIDGETS_RESTART = i18n("Restart plasmashell now?")
|
||||
WIDGETS_NODESC = i18n("No description")
|
||||
WIDGETS_ERR = i18n("Unable check widgets: ")
|
||||
WIDGETS_ERR_API = i18n("Too many API requests in the last 15 minutes from your IP address, please try again later")
|
||||
WIDGETS_ERR_API_FAIL = i18n("Failed to retrieve data from the API")
|
||||
WIDGETS_ERR_UNKNOWN = i18n("Unkwnown error")
|
||||
WIDGETS_ERR_NO_JSON = i18n("File metadata.json not found")
|
||||
WIDGETS_ERR_JSON = i18n("Errors in metadata.json file")
|
||||
WIDGETS_ERR_EXT = i18n("Unsupported file format")
|
||||
WIDGETS_ERR_NOFILES = i18n("No files for download")
|
||||
WIDGETS_ERR_NOTAGGED = i18n("No file tagged with version")
|
||||
WIDGETS_ERR_TAGGED = i18n("Multiple files are tagged with version")
|
||||
WIDGETS_ERR_PACKAGE_FAIL = i18n("Failed to download the package")
|
||||
|
||||
MNG_HEADER = i18n("Management")
|
||||
MNG_BROWSE = i18n("Browse available")
|
||||
MNG_BROWSE_H = i18n("Search all available packages and select what to install");
|
||||
MNG_INST = i18n("Manage installed")
|
||||
MNG_INST_H = i18n("View installed packages and select what to remove")
|
||||
MNG_EXPL = i18n("Explicit installs")
|
||||
MNG_EXPL_H = i18n("Show packages you installed directly");
|
||||
MNG_EXPL_L = i18n("All explicit installs")
|
||||
MNG_EXPL_U = i18n("Explicit installs not required by anything")
|
||||
MNG_ORPH = i18n("Orphaned dependencies")
|
||||
MNG_ORPH_H = i18n("Dependencies that are no longer needed by anything installed");
|
||||
MNG_ORPH_L = i18n("List orphans")
|
||||
MNG_ORPH_R = i18n("Remove orphans")
|
||||
MNG_DOWNGR = i18n("Downgrade from cache")
|
||||
MNG_CACHE = i18n("Clean cache")
|
||||
MNG_CACHE_H = i18n("Remove cached files to free disk space");
|
||||
MNG_CACHE_A = i18n("Remove all cache")
|
||||
MNG_CACHE_U = i18n("Remove unused cache")
|
||||
MNG_CACHE_DIR = i18n("Found directories in the pacman cache:")
|
||||
MNG_CACHE_DIR_RM = i18n("Remove these directories?")
|
||||
MNG_REBUILD = i18n("Rebuild packages")
|
||||
MNG_REBUILD_H = i18n("Select packages that need reinstall with rebuild")
|
||||
MNG_REBUILD_A = i18n("Rebuild AUR packages")
|
||||
MNG_REBUILD_P = i18n("Detect Python packages to rebuild")
|
||||
MNG_MIRROR = i18n("Refresh mirrors")
|
||||
MNG_WARN = i18n("Review dependency warnings before removing")
|
||||
MNG_RESUME = i18n("Resume?")
|
||||
MNG_RETURN = i18n("Press Enter to return menu")
|
||||
MNG_RECENT = i18n("Recent installs")
|
||||
MNG_RECENT_SCAN = i18n("Scanning pacman log...")
|
||||
MNG_RECENT_EXPLICIT = i18n("Explicitly installed")
|
||||
MNG_RECENT_DEPEND = i18n("Installed as a dependency")
|
||||
MNG_RECENT_ORPHAN = i18n("Orphaned dependency")
|
||||
|
||||
MNG_EXEC = i18n("Executed:")
|
||||
MNG_DONE = i18n("Nothing to do")
|
||||
CMD_ERR = i18n("Required installed")
|
||||
NEWS_ERR = i18n("Cannot fetch news ")
|
||||
@@ -0,0 +1,134 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
source "$(dirname "$0")/utils"
|
||||
|
||||
[[ "$1" == "menu" ]] && MENU=1 || MENU=0
|
||||
selected=$2
|
||||
|
||||
returnMenu() {
|
||||
printf '\e[?25l'
|
||||
echo -e "\n$(colorize blue bold $ICO_RETURN $MNG_RETURN)"
|
||||
IFS= read -rs
|
||||
exec "$scriptDir/management" "$selected"
|
||||
}
|
||||
|
||||
rank_mirrors() {
|
||||
while IFS= read -r line <&3; do
|
||||
[[ "$line" =~ ^[[:space:]]*#?[[:space:]]*Server[[:space:]]*=[[:space:]]*(.+) ]] || continue
|
||||
|
||||
local server="${BASH_REMATCH[1]}"
|
||||
server="${server%%#*}"
|
||||
server="${server%"${server##*[![:space:]]}"}"
|
||||
|
||||
local domain="$server"
|
||||
if [[ "$server" =~ ^([^/]+//[^/]+) ]]; then
|
||||
domain="${BASH_REMATCH[1]}"
|
||||
fi
|
||||
|
||||
local url="${server%/}"
|
||||
url="${url//'$arch'/$(uname -m)}"
|
||||
url="${url//'$repo'/core}"
|
||||
url="${url}/core.db"
|
||||
|
||||
local response=$(curl -s -m 9 -w "%{time_total} %{http_code}" "$url" -o /dev/null 2>/dev/null)
|
||||
local exit_code=$?
|
||||
local time status
|
||||
|
||||
if [[ $exit_code -ne 0 ]]; then
|
||||
status="Unreachable"
|
||||
else
|
||||
time="${response%% *}"
|
||||
local code="${response##* }"
|
||||
[[ -z "$code" || $code -lt 200 || $code -gt 299 ]] && status="Unreachable"
|
||||
fi
|
||||
|
||||
if [[ -n "$status" ]]; then
|
||||
if [ "$mirrorProgress" = true ]; then
|
||||
echo "$(colorize red bold $status) $domain" >&2
|
||||
fi
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ "$mirrorProgress" = true ]; then
|
||||
echo "$(colorize gray bold "$time" sec) $domain" >&2
|
||||
fi
|
||||
echo "$time $server" >> "$rankmirrors_time"
|
||||
done 3< "$rankmirrors_mirrorlist"
|
||||
|
||||
echo "## Artix Linux repository mirrorlist" > "$rankmirrors_mirrorlist"
|
||||
echo "## Generated by Antergos Apdatifier on $(date '+%Y-%m-%d %H:%M:%S')" >> "$rankmirrors_mirrorlist"
|
||||
|
||||
sort -n -o "$rankmirrors_time" "$rankmirrors_time"
|
||||
local i=0
|
||||
while IFS=' ' read -r time server; do
|
||||
[[ -n "$mirrorCount" && "$mirrorCount" -gt 0 && $i -ge "$mirrorCount" ]] && break
|
||||
echo "Server = $server" >> "$rankmirrors_mirrorlist"
|
||||
((i++))
|
||||
done < "$rankmirrors_time"
|
||||
}
|
||||
|
||||
validate() {
|
||||
! grep -qE '^[[:space:]]*#?[[:space:]]*Server[[:space:]]*=[[:space:]]*' "$1" && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
mirrorfile="/etc/pacman.d/mirrorlist"
|
||||
OUTDATED=false
|
||||
[[ ! -f "$mirrorfile" ]] && {
|
||||
NORM_TIME="(no file)"
|
||||
OUTDATED=true
|
||||
} || {
|
||||
FILE_TIME=$(date -r "$mirrorfile" +%s)
|
||||
NORM_TIME=$(date -d @"$FILE_TIME" +"%d %b %H:%M:%S")
|
||||
CURR_TIME=$(date +%s)
|
||||
[[ "$mirrors" == "age" && $MENU -eq 0 ]] && {
|
||||
(( CURR_TIME - FILE_TIME >= mirrorsAge * 86400 )) && OUTDATED=true || exit
|
||||
}
|
||||
}
|
||||
|
||||
[[ "$mirrors" != "force" || $MENU -eq 1 ]] && {
|
||||
printImportant "$MIRROR_TIME" "$NORM_TIME"
|
||||
while true; do
|
||||
printQuestion "$MIRROR_REFRESH"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) echo; break;;
|
||||
[Nn]*|"") (( MENU )) && { returnMenu; } || { echo; exit; };;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
}
|
||||
|
||||
rankmirrors_mirrorlist="$(mktemp)"
|
||||
rankmirrors_time="$(mktemp)"
|
||||
|
||||
trap 'rm -f "$rankmirrors_mirrorlist" "$rankmirrors_time"' INT TERM EXIT
|
||||
|
||||
curl -s -m 10 -o "$rankmirrors_mirrorlist" "$dynamicUrl" 2>/dev/null & printWhile $! "$MIRRORS_FETCH"
|
||||
if ! validate "$rankmirrors_mirrorlist"; then
|
||||
printError "$MIRRORS_ERR"
|
||||
printError "No server entries found."
|
||||
(( MENU )) && returnMenu || exit
|
||||
fi
|
||||
|
||||
if [ "$mirrorProgress" = true ]; then
|
||||
echo -e "$(colorize blue bold "$ICO_MIRRORS $MIRRORS_RANK")"
|
||||
rank_mirrors
|
||||
else
|
||||
rank_mirrors & printWhile $! "$MIRRORS_RANK"
|
||||
fi
|
||||
|
||||
if ! validate "$rankmirrors_mirrorlist"; then
|
||||
(( MENU )) && returnMenu || exit
|
||||
fi
|
||||
|
||||
${sudoBin} -n true 2>/dev/null || { echo; printImportant "$MIRRORS_SUDO"; }
|
||||
cat "$rankmirrors_mirrorlist" | ${sudoBin} tee "$mirrorfile" > /dev/null
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
printDone "$mirrorfile $MIRRORS_UPD"
|
||||
echo -e "$y$(tail -n +3 "$mirrorfile" | sed 's/Server = //g')$c\n"
|
||||
(( MENU )) && returnMenu
|
||||
else
|
||||
printError "$MIRRORS_SUDO"
|
||||
(( MENU )) && returnMenu || exit
|
||||
fi
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
source "$(dirname "$0")/utils"
|
||||
|
||||
declare -A termArg=( ["gnome-terminal"]="--" ["ptyxis"]="--" ["terminator"]="-x" )
|
||||
name="${1^}"
|
||||
sess="Apdatifier"
|
||||
term="$(basename "$terminal")"
|
||||
termArg="${termArg[$term]:-"-e"}"
|
||||
scr="$(dirname "$0")/$1"
|
||||
arg="${@:2}"
|
||||
|
||||
_attach="tmux attach-session -t $sess \; select-window -t $name"
|
||||
_script="$scr $arg"
|
||||
|
||||
[[ $tmuxSession = "true" && -x $(command -v tmux) ]] && _TMUX=1
|
||||
|
||||
run_terminal() {
|
||||
"$term" "$termArg" bash -c "$1"
|
||||
}
|
||||
run_yakuake() {
|
||||
qdbusCMD sessions runCommandInTerminal "$session" "bash -c '$1'"
|
||||
}
|
||||
create_tmux_session() {
|
||||
! tmux has-session -t "$sess" 2>/dev/null && {
|
||||
tmux new-session -d -s "$sess" -n "$name" "bash -c '$scr $arg'"
|
||||
} || {
|
||||
! tmux list-windows -t "$sess" | grep -wq "$name" && {
|
||||
tmux new-window -t "$sess:" -n "$name" "bash -c '$scr $arg'"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if [[ $term = "yakuake" ]]; then
|
||||
session=$(qdbusCMD sessions addSession)
|
||||
visible=$(qdbusCMD MainWindow_1 org.qtproject.Qt.QWidget.visible)
|
||||
qdbusCMD tabs org.kde.yakuake.setTabTitle "$session" "$name"
|
||||
if (( _TMUX )); then
|
||||
create_tmux_session
|
||||
run_yakuake "$_attach"
|
||||
else
|
||||
run_yakuake "tput sc; clear; $_script"
|
||||
fi
|
||||
[[ $visible = "false" ]] && qdbusCMD window org.kde.yakuake.toggleWindowState
|
||||
elif (( _TMUX )); then
|
||||
create_tmux_session
|
||||
run_terminal "$_attach"
|
||||
else
|
||||
run_terminal "$_script"
|
||||
fi
|
||||
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
source "$(dirname "$0")/utils"
|
||||
|
||||
trap 'echo -e "\n\n$(colorize red bold $ICO_WARN "CTRL+C. $UPGRADE_SKIPPED")"' SIGINT
|
||||
|
||||
run() {
|
||||
[[ -z $2 ]] && printExec "$1"
|
||||
|
||||
if [ "$idleInhibit" = true ] && command -v systemd-inhibit >/dev/null 2>&1; then
|
||||
read -ra cmd <<< "$1"
|
||||
systemd-inhibit --what=idle:sleep:shutdown --who="Apdatifier" --why="$UPGRADE_PROGRESS" "${cmd[@]}"
|
||||
else
|
||||
eval ${1}
|
||||
fi
|
||||
}
|
||||
|
||||
printClose() {
|
||||
tput civis
|
||||
printMsg "$UPGRADE_ENTER"
|
||||
|
||||
while ! read -t 30 -r; do
|
||||
if command -v gdbus >/dev/null; then
|
||||
gdbus call --session \
|
||||
--dest org.kde.plasmashell \
|
||||
--object-path /org/kde/osdService \
|
||||
--method org.kde.osdService.showText \
|
||||
apdatifier-plasmoid "$UPGRADE_COMPLETE" >/dev/null 2>&1
|
||||
fi
|
||||
done
|
||||
|
||||
tput cnorm
|
||||
[[ $(basename "$terminal") = "yakuake" ]] && qdbusCMD sessions removeSession "$(qdbusCMD sessions activeSessionId)"
|
||||
}
|
||||
|
||||
bin="$sudoBin pacman"; [ "$aur" = true ] && bin=$wrapper
|
||||
|
||||
fullSystemUpgrade() {
|
||||
startTime=$(date +%s)
|
||||
critical_updated=()
|
||||
critical_installed=()
|
||||
critical_packages=(
|
||||
"amd-ucode"
|
||||
"intel-ucode"
|
||||
"cryptsetup"
|
||||
"linux"
|
||||
"linux-hardened"
|
||||
"linux-lts"
|
||||
"linux-zen"
|
||||
"linux-rt"
|
||||
"linux-rt-lts"
|
||||
"linux-firmware*"
|
||||
"linux-cachyos*"
|
||||
"linux-cacule*"
|
||||
"nvidia"
|
||||
"nvidia-dkms"
|
||||
"nvidia-*xx-dkms"
|
||||
"nvidia-*xx"
|
||||
"nvidia-*lts-dkms"
|
||||
"nvidia*-lts"
|
||||
"mkinitcpio*"
|
||||
"booster*"
|
||||
"mesa"
|
||||
"systemd*"
|
||||
"wayland"
|
||||
"virtualbox-guest-utils"
|
||||
"virtualbox-host-dkms"
|
||||
"virtualbox-host-modules-arch"
|
||||
"egl-wayland"
|
||||
"xf86-video-*"
|
||||
"xorg-server*"
|
||||
"xorg-fonts*"
|
||||
"winesync-dkms"
|
||||
)
|
||||
|
||||
if [ "$arch" = true ]; then
|
||||
printMsg "$UPGRADE_FULL_ARCH"
|
||||
echo
|
||||
|
||||
[ "$mirrors" != "false" ] && "$scriptDir"/mirrorlist
|
||||
|
||||
if [ -n "$preExec" ]; then
|
||||
run "$preExec"
|
||||
printf '\n\033[32m'; printf '%.0s:' {1..48}; printf '\033[0m\n\n'
|
||||
fi
|
||||
|
||||
if [ "$rebootSystem" = true ]; then
|
||||
for package in "${critical_packages[@]}"; do
|
||||
matches=$(pacman -Qq | grep -E "^${package//\*/.*}$")
|
||||
[ -n "$matches" ] && critical_installed+=($matches)
|
||||
done
|
||||
|
||||
declare -A beforeVersions
|
||||
for pkg in "${critical_installed[@]}"; do
|
||||
beforeVersions[$pkg]=$(pacman -Q $pkg | awk '{print $2}')
|
||||
done
|
||||
fi
|
||||
|
||||
test -f "$configDir/env.sh" && source "$configDir/env.sh"
|
||||
|
||||
ignorePkgs="$@"
|
||||
run "$bin -Syu $archFlags $ignorePkgs"
|
||||
|
||||
for pkg in "${critical_installed[@]}"; do
|
||||
afterVersion=$(pacman -Q $pkg | awk '{print $2}')
|
||||
if [ "${beforeVersions[$pkg]}" != "$afterVersion" ]; then
|
||||
critical_updated+=("$pkg")
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
if [ "$flatpak" = true ]; then
|
||||
printMsg "$UPGRADE_FULL_FLATPAK"
|
||||
echo
|
||||
|
||||
run "flatpak update $flatpakFlags"
|
||||
|
||||
if [ "$flatpakRemoveUnused" = true ]; then
|
||||
echo
|
||||
run "flatpak uninstall --unused $flatpakFlags"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$widgets" = true ]; then
|
||||
printMsg "$UPGRADE_FULL_WIDGETS"
|
||||
echo
|
||||
run "$scriptDir/widgets upgradeAll" noPrint
|
||||
fi
|
||||
|
||||
if [ "$fwupd" = true ]; then
|
||||
printMsg "fwupdmgr"
|
||||
echo
|
||||
run "$sudoBin fwupdmgr update"
|
||||
fi
|
||||
|
||||
if [ -n "$postExec" ]; then
|
||||
printf '\n\033[32m'; printf '%.0s:' {1..48}; printf '\033[0m\n\n'
|
||||
run "$postExec"
|
||||
fi
|
||||
|
||||
trap ' ' SIGINT
|
||||
|
||||
endTime=$(date +%s)
|
||||
runTime=$((endTime-startTime))
|
||||
echo; echo
|
||||
printImportant "$UPGRADE_EXECTIME" "$(printf "%02dh:%02dm:%02ds" $((runTime / 3600)) $(( (runTime % 3600) / 60 )) $((runTime % 60)))"
|
||||
|
||||
if [ ${#critical_updated[@]} -gt 0 ]; then
|
||||
echo
|
||||
printImportant "$UPGRADE_CRITICAL" "${critical_updated[*]}"
|
||||
while true; do
|
||||
printQuestion "$UPGRADE_REBOOT"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) sys_reboot; exit;;
|
||||
[Nn]*|"") break;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
printClose
|
||||
}
|
||||
|
||||
sys_reboot() {
|
||||
tput civis
|
||||
printMsg "$UPGRADE_REBOOTING"
|
||||
|
||||
read -s -t 2
|
||||
|
||||
qdbus6 org.kde.Shutdown /Shutdown logoutAndReboot
|
||||
}
|
||||
|
||||
flatpak_package() {
|
||||
printMsg "$UPGRADE_PACKAGE: $2"
|
||||
echo
|
||||
|
||||
printExec "flatpak update" "$1 $flatpakFlags"
|
||||
|
||||
flatpak update $1 $flatpakFlags
|
||||
|
||||
printClose
|
||||
}
|
||||
|
||||
widget_package() {
|
||||
printMsg "$UPGRADE_PACKAGE: $2"
|
||||
echo
|
||||
|
||||
"$scriptDir"/widgets upgrade "$1" "$2"
|
||||
|
||||
printClose
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
"full") shift; fullSystemUpgrade $@ ;;
|
||||
"flatpak") shift; flatpak_package $1 $2;;
|
||||
"widget") shift; widget_package $1 $2;;
|
||||
*) exit;;
|
||||
esac
|
||||
@@ -0,0 +1,524 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
source "$(dirname "$0")/vars"
|
||||
|
||||
cleanup() {
|
||||
[ -d "$tempDir" ] && rm -rf "$tempDir"
|
||||
}
|
||||
|
||||
makeTempDir() {
|
||||
cleanup
|
||||
mkdir "$tempDir"
|
||||
}
|
||||
|
||||
colorize() {
|
||||
local red="\033[31m"
|
||||
local green="\033[32m"
|
||||
local blue="\033[34m"
|
||||
local yellow="\033[33m"
|
||||
local white="\033[37m"
|
||||
local gray="\e[90m"
|
||||
local clear="\033[0m"
|
||||
local bold="\033[1m"
|
||||
|
||||
local color="$1"; shift
|
||||
local style=""
|
||||
|
||||
if [[ "$1" == "bold" ]]; then
|
||||
style="${!1}"
|
||||
shift
|
||||
fi
|
||||
|
||||
echo -ne "${!color}${style}$*${clear}"
|
||||
}
|
||||
|
||||
printDone() {
|
||||
echo -e "$(colorize green bold "$ICO_DONE $1")"
|
||||
}
|
||||
|
||||
printError() {
|
||||
echo -e "$(colorize red bold "$ICO_ERR $1")"
|
||||
}
|
||||
|
||||
printImportant() {
|
||||
echo -e "$(colorize yellow bold "$ICO_WARN $1") $2"
|
||||
}
|
||||
|
||||
printQuestion() {
|
||||
echo -en "$(colorize yellow bold "$ICO_QUESTION $1") [y/N]: "
|
||||
}
|
||||
|
||||
printExec() {
|
||||
echo -e "$(colorize blue bold "$ICO_EXEC $MNG_EXEC") $(colorize white $1) \n"
|
||||
}
|
||||
|
||||
printMsg() {
|
||||
local text="$1"
|
||||
local padding=$(( (48 - ${#text} - 2) / 2 ))
|
||||
local l=$(printf ":%.0s" {1..48})
|
||||
local s=$(printf ":%.0s" $(seq 1 $padding))
|
||||
local p=${s}$( (( ${#text} % 2 )) && echo ":" )
|
||||
|
||||
echo
|
||||
echo -e "$(colorize green bold $l)"
|
||||
echo -e "$(colorize green bold $s) $(colorize white bold $text) $(colorize green bold $p)"
|
||||
echo -e "$(colorize green bold $l)"
|
||||
}
|
||||
|
||||
printClose() {
|
||||
tput civis
|
||||
printMsg "$UPGRADE_ENTER"
|
||||
read -r
|
||||
tput cnorm
|
||||
[[ $(basename "$terminal") = "yakuake" ]] && qdbusCMD sessions removeSession "$(qdbusCMD sessions activeSessionId)"
|
||||
}
|
||||
|
||||
printWhile() {
|
||||
tput sc
|
||||
pid=$1
|
||||
spinner $pid "$2"
|
||||
wait $pid
|
||||
exitCode=$?
|
||||
tput rc
|
||||
tput ed
|
||||
|
||||
[[ $exitCode -eq 0 ]] && printDone "$2" || printError "$2"
|
||||
|
||||
if [[ "$2" = "$WIDGETS_FETCH" ]]; then
|
||||
case $exitCode in
|
||||
1) printError "$WIDGETS_ERR_API_FAIL" ;;
|
||||
2) printError "$WIDGETS_ERR_API" ;;
|
||||
3) printError "$WIDGETS_ERR_UNKNOWN" ;;
|
||||
esac
|
||||
elif [[ "$2" = "$WIDGETS_LINK" ]]; then
|
||||
case $exitCode in
|
||||
1) printError "$WIDGETS_ERR_NOFILES. $UPGRADE_SKIPPED" ;;
|
||||
2) printError "$WIDGETS_ERR_NOTAGGED $latestVer. $UPGRADE_SKIPPED" ;;
|
||||
3) printError "$WIDGETS_ERR_TAGGED $latestVer. $UPGRADE_SKIPPED" ;;
|
||||
esac
|
||||
elif [[ "$2" = "$WIDGETS_DOWNLOADING" ]]; then
|
||||
case $exitCode in
|
||||
1) printError "$WIDGETS_ERR_PACKAGE_FAIL" ;;
|
||||
2) printError "$WIDGETS_ERR_EXT" ;;
|
||||
3) printError "$WIDGETS_ERR_NO_JSON" ;;
|
||||
4) printError "$WIDGETS_ERR_JSON" ;;
|
||||
esac
|
||||
fi
|
||||
}
|
||||
|
||||
qdbusCMD() {
|
||||
qdbus6 org.kde.yakuake /yakuake/$1 "${@:2}"
|
||||
}
|
||||
|
||||
dependencies() {
|
||||
for cmd in ${1}; do
|
||||
if ! command -v "$cmd" >/dev/null; then
|
||||
printError "${CMD_ERR} ${cmd}"
|
||||
[ $2 ] && returnMenu || exit
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
oneLine() {
|
||||
tr '\n' ' ' | sed 's/ $//'
|
||||
}
|
||||
|
||||
spinner() {
|
||||
local spin="⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏"
|
||||
while kill -0 $1 2>/dev/null; do
|
||||
i=$(( (i+1) %10 ))
|
||||
printf "\r$(colorize red ${spin:$i:1}) $(colorize blue bold "$2...")"
|
||||
sleep .2
|
||||
done
|
||||
}
|
||||
|
||||
clearVer() {
|
||||
local ver="${1}"
|
||||
ver="${ver//[^0-9.]/.}"
|
||||
while [[ "$ver" == *".."* ]]; do
|
||||
ver="${ver//../.}"
|
||||
done
|
||||
ver="${ver#.}"
|
||||
ver="${ver%.}"
|
||||
echo "${ver}"
|
||||
}
|
||||
|
||||
compareVer() {
|
||||
[[ $1 == $2 ]] && return 0
|
||||
local IFS=.; local i ver1=($1) ver2=($2)
|
||||
for ((i=${#ver1[@]}; i<${#ver2[@]}; i++)); do ver1[i]=0; done
|
||||
for ((i=0; i<${#ver1[@]}; i++)); do
|
||||
[[ -z ${ver2[i]} ]] && ver2[i]=0
|
||||
((10#${ver1[i]} > 10#${ver2[i]})) && return 1
|
||||
((10#${ver1[i]} < 10#${ver2[i]})) && return 2
|
||||
done
|
||||
return 0
|
||||
}
|
||||
|
||||
getId() {
|
||||
[[ $1 =~ ^[0-9]+$ ]] && f=2 || f=1
|
||||
grep "$1" "$(dirname "$0")/widgets-id" | cut -d' ' -f$f
|
||||
}
|
||||
|
||||
restartPlasmashell() {
|
||||
if [[ "$restartShell" = true ]]; then
|
||||
sleep 1
|
||||
while true; do
|
||||
printQuestion "$WIDGETS_RESTART"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) break;;
|
||||
[Nn]*|"") exit;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
eval ${restartCommand}
|
||||
else
|
||||
printImportant "$WIDGETS_WARN"
|
||||
fi
|
||||
}
|
||||
|
||||
combineFiles() {
|
||||
sleep 1
|
||||
[ -f "$1" ] && rm "$1"
|
||||
for cache in $(find "$cacheDir" -name "updates.json_*" 2>/dev/null | sort); do
|
||||
cat "$cache" >> "$1"; rm "$cache"
|
||||
done
|
||||
}
|
||||
|
||||
getIcons() {
|
||||
while read -r pkg file; do
|
||||
[[ "$processed" == *"$pkg"* ]] && continue
|
||||
icon=$(awk -F= '/^Icon=/ {print $2; exit}' "$file" 2>/dev/null || true) && [ -n "$icon" ] || continue
|
||||
processed="$processed $pkg"
|
||||
echo "$pkg $icon"
|
||||
done < <(pacman -Ql "$@" | grep '/usr/share/applications/.*\.desktop$')
|
||||
}
|
||||
|
||||
updateJson() {
|
||||
cat <<< "$2" > "$1.tmp" && mv "$1.tmp" "$1"
|
||||
}
|
||||
|
||||
rss() {
|
||||
command -v jq >/dev/null || { echo "${CMD_ERR} jq" >&2; return 127; }
|
||||
|
||||
# Wait for DNS to be ready (up to 30s) — avoids failures at boot/resume
|
||||
local host=$(echo "$1" | sed -e 's|^[^/]*//||' -e 's|/.*$||')
|
||||
for i in $(seq 1 30); do
|
||||
getent hosts "$host" >/dev/null 2>&1 && break
|
||||
sleep 1
|
||||
done
|
||||
|
||||
makeTempDir
|
||||
trap cleanup EXIT
|
||||
|
||||
local newsFile="$cacheDir/news.json"
|
||||
local errors=""
|
||||
|
||||
if [[ ! -s "$newsFile" ]] \
|
||||
|| ! jq -e '.' "$newsFile" >/dev/null 2>&1 \
|
||||
|| ! jq -e 'all(.[]; .timestamp | numbers)' "$newsFile" >/dev/null 2>&1; then
|
||||
echo '[]' > "$newsFile"
|
||||
fi
|
||||
|
||||
# Build per-URL output file list for parallel download
|
||||
local -a urls=("$@")
|
||||
local -a rssFiles=()
|
||||
local -a curlArgs=()
|
||||
for url in "${urls[@]}"; do
|
||||
local rssFile=$(mktemp "$tempDir/XXXXXX.xml")
|
||||
rssFiles+=("$rssFile")
|
||||
curlArgs+=(-o "$rssFile" --url "$url")
|
||||
done
|
||||
|
||||
# Fetch all feeds simultaneously
|
||||
curl -s --parallel --parallel-immediate \
|
||||
--connect-timeout 5 --retry 3 --retry-delay 1 --retry-all-errors --max-time 15 \
|
||||
"${curlArgs[@]}" 2>/dev/null
|
||||
|
||||
# Process each downloaded file
|
||||
for i in "${!urls[@]}"; do
|
||||
local url="${urls[$i]}"
|
||||
local rssFile="${rssFiles[$i]}"
|
||||
|
||||
# Strip default XML namespace to avoid XPath 1.0 namespace issues
|
||||
local cleanFile=$(mktemp "$tempDir/XXXXXX.xml")
|
||||
sed 's/ xmlns="[^"]*"//g' "$rssFile" > "$cleanFile"
|
||||
|
||||
xmllint --noout "$cleanFile" 2>/dev/null || {
|
||||
errors+="$url "
|
||||
continue
|
||||
}
|
||||
|
||||
# Detect RSS 2.0 vs Atom 1.0 by root element name
|
||||
local root=$(xmllint --xpath "name(/*)" "$cleanFile" 2>/dev/null)
|
||||
|
||||
# Count available items in feed (capped at feedsFetchCount)
|
||||
local count title
|
||||
if [[ "$root" == "feed" ]]; then
|
||||
count=$(xmllint --xpath "count(/feed/entry)" "$cleanFile" 2>/dev/null || echo 0)
|
||||
title=$(xmllint --xpath "string(/feed/title)" "$cleanFile")
|
||||
else
|
||||
count=$(xmllint --xpath "count(/rss/channel/item)" "$cleanFile" 2>/dev/null || echo 0)
|
||||
title=$(xmllint --xpath "string(/rss/channel/title)" "$cleanFile")
|
||||
fi
|
||||
|
||||
[[ -z "$title" ]] && { errors+="$url "; continue; }
|
||||
|
||||
local max=${feedsFetchCount:-3}
|
||||
(( count > max )) && count=$max
|
||||
|
||||
local n
|
||||
for (( n=1; n<=count; n++ )); do
|
||||
local article timestamp link
|
||||
if [[ "$root" == "feed" ]]; then
|
||||
article=$(xmllint --xpath "string(/feed/entry[$n]/title)" "$cleanFile")
|
||||
timestamp=$(date -d "$(xmllint --xpath "string(/feed/entry[$n]/updated)" "$cleanFile")" +%s 2>/dev/null)
|
||||
link=$(xmllint --xpath "string(/feed/entry[$n]/link/@href)" "$cleanFile")
|
||||
else
|
||||
article=$(xmllint --xpath "string(/rss/channel/item[$n]/title)" "$cleanFile")
|
||||
timestamp=$(date -d "$(xmllint --xpath "string(/rss/channel/item[$n]/pubDate)" "$cleanFile")" +%s 2>/dev/null)
|
||||
link=$(xmllint --xpath "string(/rss/channel/item[$n]/link)" "$cleanFile")
|
||||
fi
|
||||
|
||||
[[ -z "$article" || -z "$timestamp" || -z "$link" ]] && continue
|
||||
|
||||
if ! jq -e --arg l "$link" '.[] | select(.link == $l)' "$newsFile" > /dev/null; then
|
||||
local item=$(jq -n --arg t "$title" --arg a "$article" --arg l "$link" --argjson ts "$timestamp" \
|
||||
'{title: $t, article: $a, timestamp: $ts, link: $l, removed: false}')
|
||||
updateJson "$newsFile" "$(jq --argjson item "$item" '[$item] + .' "$newsFile")"
|
||||
fi
|
||||
done
|
||||
|
||||
# Prune oldest entries for this feed down to feedsFetchCount
|
||||
while (( $(jq --arg t "$title" '[.[] | select(.title == $t)] | length' "$newsFile") > max )); do
|
||||
index=$(jq --arg t "$title" 'to_entries | reverse | map(select(.value.title == $t)) | .[0].key' "$newsFile")
|
||||
updateJson "$newsFile" "$(jq --argjson index "$index" 'del(.[$index])' "$newsFile")"
|
||||
done
|
||||
done
|
||||
|
||||
updateJson "$newsFile" "$(jq 'sort_by(.timestamp) | reverse' "$newsFile")"
|
||||
|
||||
jq . $newsFile
|
||||
|
||||
if [[ -n "$errors" ]]; then
|
||||
errors="$NEWS_ERR: ${errors// /, }"
|
||||
errors="${errors%, }"
|
||||
echo "$errors" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
sync_dbs() {
|
||||
! type -p fakeroot >/dev/null && {
|
||||
echo "fakeroot not found" >&2
|
||||
exit 127
|
||||
}
|
||||
|
||||
! mkdir -p "$dbPath" && {
|
||||
echo "Failed to create directory $dbPath" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
db_conf="$(pacman-conf DBPath)"
|
||||
db_conf="${db_conf%/}"
|
||||
! ln -sf "${db_conf}/local" "$dbPath" &> /dev/null && {
|
||||
echo "Failed to create symlink to $db_conf/local" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
test -f "$dbPath/db.lck" && rm -f "$dbPath/db.lck"
|
||||
|
||||
! fakeroot -- pacman -Sy --disable-sandbox-filesystem --dbpath "$dbPath" --logfile /dev/null 2>/dev/null && {
|
||||
echo "Failed to synchronize pacman databases" >&2
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
|
||||
checkCurrentVersions() {
|
||||
local list
|
||||
|
||||
if [ "$arch" = true ]; then
|
||||
list+=$(pacman -Q | awk '{print "{\"NM\": \"" $1 "\", \"VO\": \"" $2 "\"},"}')
|
||||
fi
|
||||
|
||||
if [ "$flatpak" = true ]; then
|
||||
list+=$(flatpak list --app --columns=name,version,active | awk -F'\t' '{print "{\"NM\": \"" $1 "\", \"VO\": \"" $2 $3 "\"},"}')
|
||||
fi
|
||||
|
||||
if [ "$widgets" = true ]; then
|
||||
list+=$("$scriptDir"/widgets list)
|
||||
fi
|
||||
|
||||
if [ "$fwupd" = true ]; then
|
||||
if command -v jq >/dev/null 2>&1; then
|
||||
list+=$(fwupdmgr get-devices --json | jq -cr '.Devices | map({NM: ((.Vendor + " " + .Name)), VO: (.Version // "")}) | .[] | @json + ","')
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "[${list%,}]"
|
||||
}
|
||||
|
||||
checkSetup() {
|
||||
if (( systemWide )); then
|
||||
printError "Operation not permitted - the widget is installed system-wide"
|
||||
printClose
|
||||
exit
|
||||
fi
|
||||
}
|
||||
|
||||
install_devel() {
|
||||
checkSetup
|
||||
|
||||
printMsg "Downloading devel"
|
||||
echo
|
||||
|
||||
required="git jq"
|
||||
for cmd in ${required}; do command -v "$cmd" >/dev/null || { printError "Required installed ${cmd}"; printClose; exit; }; done;
|
||||
|
||||
commit=$(curl -s https://api.github.com/repos/exequtic/apdatifier/commits/main | jq -r '.sha' | cut -c1-7)
|
||||
|
||||
if [[ "github commit: $commit" = "$(jq -r '.KPlugin.Description' "$appletDir/metadata.json")" ]]; then
|
||||
printDone "Up to date"
|
||||
printClose
|
||||
exit
|
||||
fi
|
||||
|
||||
savedir=$(pwd)
|
||||
clonedir=$(mktemp -d)
|
||||
cd "$clonedir"
|
||||
|
||||
{ git clone -n --depth=10 --filter=tree:0 -b main https://github.com/exequtic/apdatifier 2>/dev/null
|
||||
} & printWhile $! "Clone main branch"
|
||||
[[ $exitCode -ne 0 ]] && { printClose; exit; }
|
||||
|
||||
cd apdatifier
|
||||
|
||||
{ git sparse-checkout set --no-cone package 2>/dev/null && git checkout 2>/dev/null
|
||||
} & printWhile $! "Checkout package directory"
|
||||
[[ $exitCode -ne 0 ]] && { printClose; exit; }
|
||||
|
||||
if command -v less &>/dev/null; then
|
||||
echo
|
||||
printImportant "Last 10 commits:"
|
||||
git log --oneline
|
||||
sleep 2
|
||||
fi
|
||||
|
||||
echo
|
||||
printMsg "Upgrading Apdatifier"
|
||||
echo
|
||||
cd package || exit 1
|
||||
updateJson metadata.json "$(jq --arg new_value "github commit: $commit" '.KPlugin.Description = $new_value' metadata.json)"
|
||||
[[ $trayEnabledByDefault == "true" ]] && updateJson $metadata "$(jq '.KPlugin.EnabledByDefault = true' $metadata)"
|
||||
while true; do
|
||||
printQuestion "Do you want build translations?"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) cd translate; bash build; cd ..; break;;
|
||||
[Nn]*|"") break;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
echo
|
||||
tar --exclude=./apdatifier.tar -cf apdatifier.tar .
|
||||
kpackagetool6 -t Plasma/Applet -u apdatifier.tar 2>/dev/null
|
||||
echo
|
||||
|
||||
cd "$savedir"
|
||||
[ ! -d "$clonedir" ] || rm -rf "$clonedir"
|
||||
|
||||
restartShell=true
|
||||
restartPlasmashell
|
||||
|
||||
printClose
|
||||
exit
|
||||
}
|
||||
|
||||
install_stable() {
|
||||
checkSetup
|
||||
|
||||
if [[ "$(jq -r '.KPlugin.Description' "$appletDir/metadata.json")" = "Arch Update Notifier" ]]; then
|
||||
while true; do
|
||||
printImportant "It looks like you already have the stable version."
|
||||
printQuestion "Do you still want to proceed?"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) break;;
|
||||
[Nn]*|"") printClose; exit;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
$scriptDir/upgrade widget "2135796" "apdatifier"
|
||||
}
|
||||
|
||||
uninstall() {
|
||||
checkSetup
|
||||
|
||||
iconsDir="$localDir/icons/breeze/status/22"
|
||||
notifDir="$localDir/knotifications6"
|
||||
notif="apdatifier.notifyrc"
|
||||
icons=(apdatifier-plasmoid.svg
|
||||
apdatifier-packages.svg
|
||||
apdatifier-package.svg
|
||||
apdatifier-flatpak.svg)
|
||||
|
||||
printMsg "Uninstall Apdatifier"
|
||||
echo
|
||||
|
||||
while true; do
|
||||
printQuestion "Continue?"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) break;;
|
||||
[Nn]*|"") printClose; exit;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo
|
||||
|
||||
showCommand() {
|
||||
echo -e "$(colorize yellow bold "$ICO_EXEC Command:") $1"
|
||||
}
|
||||
|
||||
printImportant "Remove icons"
|
||||
for icon in "${icons[@]}"; do
|
||||
showCommand "rm $iconsDir/$icon"
|
||||
test -f "$iconsDir/$icon" && rm "$iconsDir/$icon"
|
||||
done
|
||||
showCommand "rmdir -p --ignore-fail-on-non-empty $iconsDir"
|
||||
test -d "$iconsDir" && rmdir -p --ignore-fail-on-non-empty "$iconsDir"
|
||||
sleep 1; echo
|
||||
|
||||
printImportant "Remove notification"
|
||||
showCommand "rm $notifDir/$notif"
|
||||
test -f "$notifDir/$notif" && rm "$notifDir/$notif"
|
||||
showCommand "rmdir -p --ignore-fail-on-non-empty $notifDir"
|
||||
test -d "$notifDir" && rmdir -p --ignore-fail-on-non-empty "$notifDir"
|
||||
sleep 1; echo
|
||||
|
||||
printImportant "Remove configuration"
|
||||
showCommand "rm -rf $configDir"
|
||||
test -d "$configDir" && rm -rf "$configDir"
|
||||
sleep 1; echo
|
||||
|
||||
printImportant "Uninstall plasmoid"
|
||||
showCommand "kpackagetool6 -t Plasma/Applet -r com.github.exequtic.apdatifier"
|
||||
kpackagetool6 -t Plasma/Applet -r com.github.exequtic.apdatifier 2>/dev/null
|
||||
sleep 1; echo
|
||||
|
||||
echo "Bye :("; sleep 1
|
||||
|
||||
printClose
|
||||
exit
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
"syncdb") sync_dbs ;;
|
||||
"rss") shift; rss "$@" ;;
|
||||
"combineFiles") shift; combineFiles $1 ;;
|
||||
"currentVersions") checkCurrentVersions ;;
|
||||
"getIcons") shift; getIcons "$@" ;;
|
||||
"installDev") install_devel ;;
|
||||
"installStable") install_stable ;;
|
||||
"uninstall") uninstall ;;
|
||||
esac
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
scriptDir="$(dirname -- "$(readlink -f -- "${BASH_SOURCE[0]}")")"
|
||||
appletDir="${scriptDir%/contents/*}"
|
||||
[[ "$appletDir" == /home/*/.local* ]]; systemWide=$?
|
||||
|
||||
configDir="${APDATIFIER_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/apdatifier}"
|
||||
cacheDir="${APDATIFIER_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/apdatifier}"
|
||||
localDir="${XDG_LOCAL_HOME:-$HOME/.local/share}"
|
||||
tempDir="${TMPDIR:-/tmp}/apdatifier"
|
||||
config="$configDir/config.conf"
|
||||
|
||||
export TEXTDOMAINDIR="$scriptDir/../../locale"
|
||||
export TEXTDOMAIN="plasma_applet_com.github.exequtic.apdatifier"
|
||||
|
||||
source "$config"
|
||||
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" || "$line" =~ ^[[:space:]]*# ]] && continue
|
||||
key="${line%%=*}"
|
||||
key="${key%% *}"
|
||||
value="${line#*=}"
|
||||
text=$(echo "$value" | grep -oP '(?<=\().*(?=\))' | sed 's/"//g')
|
||||
[[ -n "$key" && -n "$text" ]] && eval "$key=\"\$(gettext \"$text\")\""
|
||||
done < "$scriptDir/messages"
|
||||
|
||||
if [[ $termFont = "true" ]]; then
|
||||
ICO_ERR=""
|
||||
ICO_DONE=""
|
||||
ICO_WARN=""
|
||||
ICO_QUESTION=""
|
||||
ICO_EXEC=""
|
||||
ICO_MIRRORS=""
|
||||
ICO_RETURN=""
|
||||
ICO_SELECT=""
|
||||
else
|
||||
ICO_ERR="✘"
|
||||
ICO_DONE="✔"
|
||||
ICO_WARN="::"
|
||||
ICO_QUESTION="::"
|
||||
ICO_EXEC="::"
|
||||
ICO_MIRRORS="::"
|
||||
ICO_RETURN="<<"
|
||||
ICO_SELECT=">"
|
||||
fi
|
||||
@@ -0,0 +1,365 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
source "$(dirname "$0")/utils"
|
||||
|
||||
fetch_api_data() {
|
||||
echo '<?xml version="1.0" encoding="UTF-8"?><data>' > "$XML"
|
||||
|
||||
local ps=100 url="https://api.kde-look.org/ocs/v1/content/data"
|
||||
local q="categories=705x715x719x720&sort=new&pagesize=$ps"
|
||||
|
||||
local p0=$(mktemp "$tempDir/XXXXXX.xml")
|
||||
curl -s -o "$p0" --connect-timeout 5 --retry 2 --max-time 60 --request GET --url "$url?$q&page=0"
|
||||
! xmllint --noout "$p0" 2>/dev/null && { echo '</data>' >> "$XML"; return 1; }
|
||||
case $(xmllint --xpath "string(//ocs/meta/statuscode)" "$p0") in
|
||||
""|200) echo '</data>' >> "$XML"; return 2;;
|
||||
[!100]*) echo '</data>' >> "$XML"; return 3;;
|
||||
esac
|
||||
|
||||
local total=$(xmllint --xpath "string(//ocs/meta/totalitems)" "$p0")
|
||||
xmllint --xpath "//content[@details='summary']" "$p0" 2>/dev/null >> "$XML"
|
||||
[[ $total =~ ^[0-9]+$ ]] || total=0
|
||||
(( total <= ps )) && { echo '</data>' >> "$XML"; return 0; }
|
||||
|
||||
local pages=$(( (total - 1) / ps ))
|
||||
local -a files=() args=()
|
||||
for ((p=1; p<=pages; p++)); do
|
||||
f=$(mktemp "$tempDir/XXXXXX.xml"); files+=("$f")
|
||||
args+=(-o "$f" --url "$url?$q&page=$p")
|
||||
done
|
||||
curl -s --parallel --parallel-immediate --connect-timeout 5 --retry 2 --max-time 60 "${args[@]}"
|
||||
|
||||
for f in "${files[@]}"; do
|
||||
xmllint --noout "$f" 2>/dev/null || { echo '</data>' >> "$XML"; return 2; }
|
||||
case $(xmllint --xpath "string(//ocs/meta/statuscode)" "$f") in
|
||||
100) xmllint --xpath "//content[@details='summary']" "$f" 2>/dev/null >> "$XML";;
|
||||
*) echo '</data>' >> "$XML"; return 2;;
|
||||
esac
|
||||
done
|
||||
|
||||
echo '</data>' >> "$XML"
|
||||
}
|
||||
|
||||
get_packages() {
|
||||
packages=($(find \
|
||||
"$localDir/kwin/effects" \
|
||||
"$localDir/kwin/scripts" \
|
||||
"$localDir/plasma/plasmoids" \
|
||||
"$localDir/plasma/wallpapers" \
|
||||
-mindepth 1 -maxdepth 1 -type d 2>/dev/null))
|
||||
[ ${#packages[@]} -eq 0 ] && return 1
|
||||
return 0
|
||||
}
|
||||
|
||||
get_package_type() {
|
||||
case $(xmllint --xpath "string(//id[text()='$contentId']/../typeid)" "$XML") in
|
||||
"715") echo "Plasma/Wallpaper";;
|
||||
"719") echo "KWin/Effect";;
|
||||
"720") echo "KWin/Script";;
|
||||
*) echo "Plasma/Applet";;
|
||||
esac
|
||||
}
|
||||
|
||||
get_package_info() {
|
||||
local id=$(basename "$package")
|
||||
|
||||
local metadata="$package/metadata.json"
|
||||
[[ -s "$metadata" ]] || return 1
|
||||
jq . "$metadata" >/dev/null 2>&1 || return 2
|
||||
|
||||
local originName=$(jq -r '.KPlugin.Name' "$metadata")
|
||||
name=$(echo "$originName" | sed 's/ /-/g; s/.*/\L&/')
|
||||
|
||||
local quotedOriginName='"'$(echo "$originName" | sed 's/"/\\"/g')'"'
|
||||
contentId=$(xmllint --xpath "string(//name[text()=$quotedOriginName]/../id)" "$XML")
|
||||
if [[ -z "$contentId" ]]; then
|
||||
knsregistry=("plasmoids" "kwinscripts" "kwineffect" "wallpaperplugin")
|
||||
for kns in "${knsregistry[@]}"; do
|
||||
kns="$HOME/.local/share/knewstuff3/$kns.knsregistry"
|
||||
[[ -s "$kns" ]] && contentId=$(xmllint --xpath "string(//installedfile[contains(text(), '/$id')]/../id)" "$kns")
|
||||
[[ -n "$contentId" ]] && break
|
||||
done
|
||||
fi
|
||||
[[ -z "$contentId" ]] && contentId="$(getId "$id" | head -n 1)"
|
||||
[[ -z "$contentId" ]] && return 3
|
||||
|
||||
currentVer=$(clearVer "$(jq -r '.KPlugin.Version' "$metadata")")
|
||||
latestVer=$(clearVer "$(xmllint --xpath "string(//id[text()='$contentId']/../version)" $XML)")
|
||||
[ -z "$currentVer" ] || [ -z "$latestVer" ] && return 4
|
||||
compareVer "$currentVer" "$latestVer"
|
||||
[[ $? != 2 ]] && return 5
|
||||
|
||||
description=$(jq -r '.KPlugin.Description' "$metadata" | sed 's/"/\\"/g' | tr -d '\n')
|
||||
[ -z "$description" ] || [ "$description" = "null" ] && description="$WIDGETS_NODESC"
|
||||
|
||||
author=$(jq -r '.KPlugin.Authors[].Name' "$metadata" | paste -sd "," - | sed 's/,/, /g')
|
||||
[ -z "$author" ] || [ "$author" = "null" ] && author="?"
|
||||
|
||||
icon=$(jq -r '.KPlugin.Icon' "$metadata")
|
||||
local fallbackIcon="start-here-kde-plasma-symbolic"
|
||||
if [ -z "$icon" ]; then
|
||||
icon=$fallbackIcon
|
||||
else
|
||||
local iconTheme=$(kreadconfig6 --file kdeglobals --group Icons --key Theme)
|
||||
local themeDir1="/usr/share/icons/$iconTheme/"
|
||||
local themeDir2="$HOME/.local/share/icons/$iconTheme/"
|
||||
local themeDir3="$HOME/.icons/$iconTheme/"
|
||||
! find -L "$themeDir1" "$themeDir2" "$themeDir3" \
|
||||
-type f -name "$icon.svg" -print -quit 2>/dev/null | grep -q . \
|
||||
&& icon=$fallbackIcon
|
||||
fi
|
||||
|
||||
url="https://store.kde.org/p/$contentId"
|
||||
repo="kde-store"
|
||||
type=$(get_package_type)
|
||||
|
||||
if ! jq -e ".KPackageStructure == \"$type\"" "$metadata" >/dev/null 2>&1; then
|
||||
updateJson "$metadata" "$(jq ". + { \"KPackageStructure\": \"$type\" }" "$metadata")"
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
get_download_link() {
|
||||
local signed=()
|
||||
local files=0
|
||||
|
||||
while read -r download_version; do
|
||||
((files++))
|
||||
if [[ "$latestVer" == "$(clearVer "$(xmllint --xpath "string(//id[text()='$contentId']/../$download_version)" "$XML")")" ]]; then
|
||||
signed+=("${download_version#download_version}")
|
||||
fi
|
||||
done < <(xmllint --xpath "//content[id='$contentId']/*" "$XML" | grep -o 'download_version[0-9]\+' | uniq)
|
||||
|
||||
if [[ $files -eq 1 || ${#signed[@]} -eq 1 ]]; then
|
||||
echo $(xmllint --xpath "string(//id[text()='$contentId']/../downloadlink${signed[0]:-1})" "$XML") > "$tempDir/link"
|
||||
return 0
|
||||
else
|
||||
[[ $files -eq 0 ]] && return 1
|
||||
[[ ${#signed[@]} -eq 0 ]] && return 2
|
||||
(( ${#signed[@]} > 1 )) && return 3
|
||||
fi
|
||||
}
|
||||
|
||||
make_updates_list() {
|
||||
local tmp="$tempDir/update.list.lines.json"
|
||||
: > "$tmp"
|
||||
|
||||
for package in "${packages[@]}"; do
|
||||
get_package_info
|
||||
[[ $? -ne 0 ]] && continue
|
||||
|
||||
jq -cn \
|
||||
--arg NM "$name" \
|
||||
--arg RE "$repo" \
|
||||
--arg TP "$type" \
|
||||
--arg CN "$contentId" \
|
||||
--arg IN "$icon" \
|
||||
--arg DE "$description" \
|
||||
--arg AU "$author" \
|
||||
--arg VO "$currentVer" \
|
||||
--arg VN "$latestVer" \
|
||||
--arg LN "$url" \
|
||||
'{NM:$NM,RE:$RE,TP:$TP,CN:$CN,IN:$IN,DE:$DE,AU:$AU,VO:$VO,VN:$VN,LN:$LN}' >> "$tmp"
|
||||
done
|
||||
|
||||
jq -s '.' "$tmp" > "$updatesList"
|
||||
}
|
||||
|
||||
check_packages_updates() {
|
||||
command -v jq >/dev/null || {
|
||||
echo "$WIDGETS_ERR $CMD_ERR jq" >&2
|
||||
exit 127
|
||||
}
|
||||
|
||||
declare -a packages
|
||||
! get_packages && {
|
||||
echo "[]"
|
||||
exit
|
||||
}
|
||||
|
||||
makeTempDir
|
||||
XML="$tempDir/api.kde-look.xml"
|
||||
updatesList="$tempDir/update.list.json"
|
||||
trap cleanup EXIT
|
||||
|
||||
fetch_api_data
|
||||
case $? in
|
||||
1) echo "$WIDGETS_ERR $WIDGETS_ERR_API_FAIL" >&2; exit 1 ;;
|
||||
2) echo "$WIDGETS_ERR $WIDGETS_ERR_API" >&2; exit 1 ;;
|
||||
3) echo "$WIDGETS_ERR $WIDGETS_ERR_UNKNOWN" >&2; exit 1 ;;
|
||||
esac
|
||||
|
||||
make_updates_list
|
||||
jq . $updatesList
|
||||
}
|
||||
|
||||
download_package() {
|
||||
mkdir -p "$tempDir/$name/unpacked"
|
||||
link=$(cat "$tempDir/link")
|
||||
package="$tempDir/$name/$(basename "${link}")"
|
||||
|
||||
curl -s -o "$package" --connect-timeout 5 --retry 2 --max-time 120 --request GET --location --url "$link" 2>/dev/null
|
||||
|
||||
[ ! -s "$package" ] && return 1
|
||||
|
||||
bsdtar -xf "$package" -C "$tempDir/$name/unpacked" || return 2
|
||||
|
||||
metadata=$(find "$tempDir/$name/unpacked" -name metadata.json)
|
||||
[ -z "metadata" ] && return 3
|
||||
|
||||
jq . "$metadata" >/dev/null 2>&1 || return 4
|
||||
|
||||
if ! jq -e ".KPackageStructure == \"$type\"" "$metadata" >/dev/null 2>&1; then
|
||||
updateJson "$metadata" "$(jq ". + { \"KPackageStructure\": \"$type\" }" "$metadata")"
|
||||
fi
|
||||
|
||||
updateJson "$metadata" "$(jq --arg new_value "$latestVer" '.KPlugin.Version = $new_value' "$metadata")"
|
||||
|
||||
if [[ $name == "apdatifier" && $trayEnabledByDefault == "true" ]]; then
|
||||
if jq -e '.KPlugin.EnabledByDefault == false' "$metadata" > /dev/null; then
|
||||
updateJson "$metadata" "$(jq '.KPlugin.EnabledByDefault = true' "$metadata")"
|
||||
fi
|
||||
fi
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
upgrade_all_packages() {
|
||||
dependencies "jq"
|
||||
|
||||
declare -a packages
|
||||
! get_packages && {
|
||||
printDone "$MNG_DONE"
|
||||
exit
|
||||
}
|
||||
|
||||
makeTempDir
|
||||
XML="$tempDir/api.kde-look.xml"
|
||||
updatesList="$tempDir/update.list.json"
|
||||
trap cleanup EXIT
|
||||
trap 'kill "$bg_pid" 2>/dev/null; cleanup' INT TERM
|
||||
|
||||
fetch_api_data & bg_pid=$!
|
||||
printWhile "$bg_pid" "$WIDGETS_FETCH"
|
||||
[[ $exitCode -ne 0 ]] && exit
|
||||
|
||||
make_updates_list & bg_pid=$!
|
||||
printWhile "$bg_pid" "$WIDGETS_CHECK"
|
||||
|
||||
if [[ -s "$updatesList" ]] && jq -e '(. | length) > 0' "$updatesList" > /dev/null 2>&1; then
|
||||
echo
|
||||
jq -c 'sort_by(.NM)[]' "$updatesList" | while read -r info; do
|
||||
echo -e "$(colorize white bold $(echo "$info" | jq -r '.NM')) \t \
|
||||
$(colorize yellow bold $(echo "$info" | jq -r '.TP')) \t \
|
||||
$(colorize red bold $(echo "$info" | jq -r '.VO')) \t \
|
||||
$(colorize white "->") \t \
|
||||
$(colorize green bold $(echo "$info" | jq -r '.VN'))"
|
||||
done | column -t
|
||||
echo
|
||||
|
||||
if [[ $widgetConfirmation = true ]]; then
|
||||
while true; do
|
||||
printQuestion "$WIDGETS_PROCEED"; read -r answer
|
||||
case "$answer" in
|
||||
[Yy]*) echo; break;;
|
||||
[Nn]*|"") return 0;;
|
||||
*) ;;
|
||||
esac
|
||||
done
|
||||
fi
|
||||
|
||||
updated=false
|
||||
while read -r info; do
|
||||
name=$(echo "$info" | jq -r '.NM')
|
||||
contentId=$(echo "$info" | jq -r '.CN')
|
||||
latestVer=$(echo "$info" | jq -r '.VN')
|
||||
type=$(echo "$info" | jq -r '.TP')
|
||||
|
||||
echo "$(colorize blue bold "$ICO_EXEC $name ($latestVer)")"
|
||||
|
||||
get_download_link & printWhile $! "$WIDGETS_LINK"
|
||||
[[ $exitCode -ne 0 ]] && { echo; continue; }
|
||||
|
||||
download_package & printWhile $! "$WIDGETS_DOWNLOADING"
|
||||
[[ $exitCode -ne 0 ]] && { echo; continue; }
|
||||
|
||||
kpackagetool6 -t "$type" -u "$(dirname "$(find "$tempDir/$name" -name "metadata.json")")" 2>/dev/null
|
||||
updated=true
|
||||
sleep 1
|
||||
echo
|
||||
done < <(jq -c 'sort_by(.NM)[]' "$updatesList")
|
||||
|
||||
[[ $updated = true ]] && restartPlasmashell
|
||||
else
|
||||
printDone "$MNG_DONE"
|
||||
fi
|
||||
}
|
||||
|
||||
upgrade_package() {
|
||||
[ -n "$1" ] && contentId="$1" || exit
|
||||
[ -n "$2" ] && name="$2" || exit
|
||||
|
||||
dependencies "jq"
|
||||
|
||||
mkdir -p "$tempDir/$name/unpacked"
|
||||
XML="$tempDir/$name/api.kde-look.xml"
|
||||
|
||||
trap 'kill "$bg_pid" 2>/dev/null; cleanup' INT TERM EXIT
|
||||
|
||||
{
|
||||
curl -s -o "$XML" --connect-timeout 5 --retry 2 --max-time 60 --request GET --url "https://api.kde-look.org/ocs/v1/content/data/$contentId" 2>/dev/null
|
||||
if xmllint --noout "$XML" 2>/dev/null; then
|
||||
statuscode=$(xmllint --xpath "string(//ocs/meta/statuscode)" "$XML")
|
||||
case $statuscode in
|
||||
100) return 0 ;;
|
||||
200) return 2 ;;
|
||||
*) return 3 ;;
|
||||
esac
|
||||
else
|
||||
return 1
|
||||
fi
|
||||
} & bg_pid=$!
|
||||
printWhile "$bg_pid" "$WIDGETS_FETCH"
|
||||
[[ $exitCode -ne 0 ]] && exit
|
||||
|
||||
latestVer=$(clearVer "$(xmllint --xpath "string(//id[text()='$contentId']/../version)" "$XML")")
|
||||
type=$(get_package_type)
|
||||
|
||||
get_download_link & bg_pid=$!
|
||||
printWhile "$bg_pid" "$WIDGETS_LINK"
|
||||
[[ $exitCode -ne 0 ]] && exit
|
||||
|
||||
download_package & bg_pid=$!
|
||||
printWhile "$bg_pid" "$WIDGETS_DOWNLOADING"
|
||||
[[ $exitCode -ne 0 ]] && exit
|
||||
|
||||
kpackagetool6 -t "$type" -u "$(dirname "$(find "$tempDir/$name" -name "metadata.json")")" 2>/dev/null
|
||||
sleep 1
|
||||
echo
|
||||
|
||||
restartPlasmashell
|
||||
}
|
||||
|
||||
list_current_packages() {
|
||||
declare -a packages
|
||||
! get_packages && return
|
||||
|
||||
local out=""
|
||||
for package in "${packages[@]}"; do
|
||||
local json="$package/metadata.json"; [ -s "$json" ] || continue
|
||||
local name="$(jq -r '.KPlugin.Name' "$json")"; [ -z "$name" ] && continue
|
||||
local currentVer=$(clearVer "$(jq -r '.KPlugin.Version' "$json")"); [ -z "$currentVer" ] && continue
|
||||
out+="{\"NM\": \"${name}\","
|
||||
out+="\"VO\": \"${currentVer}\"},\n"
|
||||
done
|
||||
echo -e $out
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
"list") list_current_packages;;
|
||||
"check") check_packages_updates;;
|
||||
"upgrade") shift; upgrade_package $1 $2;;
|
||||
"upgradeAll") upgrade_all_packages;;
|
||||
*) exit;;
|
||||
esac
|
||||
@@ -0,0 +1,189 @@
|
||||
998890 com.bxabi.bumblebee-indicator
|
||||
998913 org.kde.plasma.awesomewidget
|
||||
1155946 com.dschopf.plasma.qalculate
|
||||
1288430 org.kde.plasma.shutdownorswitch
|
||||
1377704 org.kde.mediabar
|
||||
1384066 org.kde.workraveApplet
|
||||
1396415 com.github.solant.plasmataker
|
||||
1627256 com.github.xyz32.timekeeper
|
||||
1804745 com.github.heqro.day-night-switcher
|
||||
1898708 org.nielsvm.plasma.menupager
|
||||
2079446 dev.vili.sahkoporssi
|
||||
2100417 org.kde.mcwsremote
|
||||
2100418 org.kde.olib.thermalmonitor
|
||||
2107649 org.kde.panel.transparency.toggle
|
||||
2112443 com.github.tilorenz.compact_pager
|
||||
2113872 com.github.korapp.cloudflare-warp
|
||||
2114471 de.davidhi.ddcci-brightness
|
||||
2115883 org.kde.plasma.simplekickoff
|
||||
2117117 com.dv.fokus
|
||||
2118132 com.github.stepan-zubkov.days-to-new-year
|
||||
2118492 com.github.korapp.nordvpn
|
||||
2126775 com.github.tilorenz.timeprogressbar
|
||||
2128047 luisbocanegra.panelspacer.extended
|
||||
2128143 plasmusic-toolbar
|
||||
2128477 luisbocanegra.intel.gpu.monitor
|
||||
2129423 org.kde.windowtitle
|
||||
2130222 luisbocanegra.panel.modes.switcher
|
||||
2130541 org.kde.archupdatechecker
|
||||
2130967 luisbocanegra.panel.colorizer
|
||||
2131364 com.github.korapp.homeassistant
|
||||
2131462 org.kde.plasma.plasm6desktopindicator
|
||||
2132405 com.github.dhruv8sh.year-progress-mod
|
||||
2132554 com.himdek.kde.plasma.overview
|
||||
2132555 com.himdek.kde.plasma.runcommand
|
||||
2134470 a2n.archupdate.plasmoid
|
||||
2135509 com.github.antroids.application-title-bar
|
||||
2135511 org.kde.placesWidget
|
||||
2135552 org.kde.plasma.yesplaymusic-lyrics
|
||||
2135642 com.github.prayag2.minimalistclock
|
||||
2135653 com.github.prayag2.modernclock
|
||||
2135796 com.github.exequtic.apdatifier
|
||||
2135799 com.github.k-donn.plasmoid-wunderground
|
||||
2135898 com.dv.uswitcher
|
||||
2136288 org.kde.Big.Clock
|
||||
2136291 zayron.chaac.weather
|
||||
2136295 Clock.Asitoki.Color
|
||||
2136299 CircleClock
|
||||
2136302 zayron.almanac
|
||||
2136307 Minimal.chaac.weather
|
||||
2136321 com.Petik.clock
|
||||
2136329 weather.bicolor.widget
|
||||
2136505 org.kde.netspeedWidget
|
||||
2136546 com.nemmayan.clock
|
||||
2136631 com.github.scriptinator
|
||||
2136636 com.github.zren.commandoutput
|
||||
2136852 org.kde.latte.separator
|
||||
2136860 com.github.zren.alphablackcontrol
|
||||
2136933 org.kde.plasma.advancedradio
|
||||
2136963 luisbocanegra.kdematerialyou.colors
|
||||
2137016 org.kde.plasma.Beclock
|
||||
2137185 com.github.zren.dailyforecast
|
||||
2137197 com.github.zren.condensedweather
|
||||
2137217 org.kde.plasma.scpmk
|
||||
2137231 com.github.eatsu.spaceraspager
|
||||
2137418 zayron.simple.separator
|
||||
2137431 com.github.zren.simpleweather
|
||||
2137675 com.gitlab.scias.advancedreboot
|
||||
2137726 org.zayronxio.vector.clock
|
||||
2137844 org.kde.plasma.catwalk
|
||||
2138251 org.kpple.kppleMenu
|
||||
2138283 ink.chyk.minimumMediaController
|
||||
2138365 optimus-gpu-switcher
|
||||
2138473 org.kde.plasma.videocard
|
||||
2138476 lenovo-conservation-mode-switcher
|
||||
2138485 com.github.boraerciyas.controlcentre
|
||||
2138746 org.kde.plasma.pminhibition
|
||||
2138853 org.kde.Date.Bubble.P6
|
||||
2138907 org.kde.latte.spacer
|
||||
2139337 split-clock
|
||||
2139541 com.github.configurable_button
|
||||
2139890 Plasma.Control.Hub
|
||||
2140275 com.github.davide-sd.ip_address
|
||||
2140856 d4rkwzd.colorpicker-tray
|
||||
2141133 org.kde.MinimalMusic.P6
|
||||
2142681 Audio.Wave.Widget
|
||||
2142716 com.github.zren.tiledmenu
|
||||
2143899 org.kde.plasma.resources-monitor
|
||||
2144212 AndromedaLauncher
|
||||
2144426 org.previewqt.previewqt.plasmoidpreviewqt
|
||||
2144969 SoloDay.P6
|
||||
2145065 com.github.liujed.rssfeeds
|
||||
2145280 com.github.DenysMb.Kicker-AppsOnly
|
||||
2145723 luisbocanegra.desktop.wallpaper.effects
|
||||
2146553 org.kde.plasma.ginti
|
||||
2147850 zayron.almanac.V2
|
||||
2147871 org.kde.plasma.clearclock
|
||||
2147882 org.kde.windowtitle.Fork
|
||||
2148373 lyrics-on-panel-plasma6
|
||||
2148469 thot.observer.ram
|
||||
2148472 thot.observer.cpu
|
||||
2150544 org.kde.plasma.composeEmail
|
||||
2150610 com.gitlab.scias.plasmavantage
|
||||
2150916 org.kde.paneltransparencybutton
|
||||
2151247 org.kde.plasma.win7showdesktop
|
||||
2151576 ppk.plasma6.hydrobot
|
||||
2151585 com.samirgaire10.Brave-plasma6
|
||||
2151600 com.samirgaire10.chatgpt-plasma6
|
||||
2151622 com.samirgaire10.Deepl-plasma6
|
||||
2151683 com.samirgaire10.google_gemini-plasma6
|
||||
2151687 com.samirgaire10.Google-plasma6
|
||||
2151694 com.samirgaire10.GoogleTranslater-plasma6
|
||||
2151931 org.kde.windowbuttons
|
||||
2151962 com.samirgaire10.perplexityAi-plasma6
|
||||
2152642 com.samirgaire10.blackbox.AI_plasma6
|
||||
2153869 org.kde.plasma.private.BusyTasksPlasmoid
|
||||
2154331 com.softtechok.systemmonitorplasmoid
|
||||
2154894 org.51n7.kMenu
|
||||
2157390 org.latgardi.darwinmenu
|
||||
2158349 Almanac.Asimetric
|
||||
2158358 org.kde.losungen6
|
||||
2159592 com.samirgaire10.Ollama-plasma6
|
||||
2160638 com.softtechok.processmonitor
|
||||
2161461 Weather.IntiSol.kde
|
||||
2162586 org.kde.dwardor.fanspeedmonitor
|
||||
2163165 OnlyText.Date.Kde
|
||||
2163199 com.samirgaire10.DuckDuckGo-plasma6
|
||||
2163340 ChatAI-Plasmoid
|
||||
2163368 Circle.Music.Widget
|
||||
2164459 org.kde.redshiftControl6
|
||||
2164590 org.kde.plasma.mediacontroller.panel
|
||||
2164679 com.github.chrtall.kppleMenu
|
||||
2166656 Aligned.Music.Widget
|
||||
2166934 adhe.runcommand
|
||||
2167594 adhe.dittomenu
|
||||
2167836 com.github.jonmagon.plasma-screendimmer
|
||||
2168131 Date.Urban.kde
|
||||
2168466 Rates.Mercurius.Widget
|
||||
2169597 com.github.danielwjchen.folding-at-home-plasma-widget
|
||||
2170656 com.samirgaire10.Github-plasma6
|
||||
2171750 adhe.menu.11
|
||||
2172470 org.magpie.sonomatic.separator
|
||||
2173278 org.kde.plasma.powerusage
|
||||
2174238 adhe.launchpadPlasma
|
||||
2174265 Chaac.Complete.Weather
|
||||
2175475 Redmi.Clock
|
||||
2179264 org.magpie.nightglow.separator
|
||||
2179317 Start.Next.Menu
|
||||
2179829 com.kevinbburns.grammarly
|
||||
2179833 Small.Weather.Plasma6
|
||||
2180314 Soniq.Widget
|
||||
2180887 DeepinMenu.Classic
|
||||
2182964 adhe.menu.11 #Ignored, not a unique ID
|
||||
2183258 Compact.Menu
|
||||
2183752 luisbocanegra.cursor.eyes
|
||||
2184730 OnlyText.Date.Kde.v2
|
||||
2184747 ChatQT-Plasmoid
|
||||
2185174 Seeua.Weather
|
||||
2185549 org.kde.plasma.pipewiresettings
|
||||
2185626 org.kde.plasma.dockio
|
||||
2185931 asus-battery-health-switcher
|
||||
2185962 Start.11.Simple
|
||||
2186442 Music.Waves
|
||||
2187344 MusicMini
|
||||
2187962 MechanicalClock
|
||||
2188275 com.junongx.kde.currentlyplaying
|
||||
2188581 Lyndo.Kde
|
||||
2189544 Plain.Circle.Weather
|
||||
2189637 Solid.Plain.Music
|
||||
2193670 qinvvv.qtodo.widget
|
||||
2194992 org.kde.osymUpdates
|
||||
2195583 org.ruiny.NowPlaying
|
||||
2196105 KdeControlStation
|
||||
2196149 ff.qtodo.widget
|
||||
2196368 com.github.imoize.ollamacontrol
|
||||
2199942 Bix.Plasma.Player
|
||||
2200890 org.dhruv8sh.kara
|
||||
2201084 Emulator.Spectrum.Audio
|
||||
2202249 com.espaker.simpleradioplayer-plasma6
|
||||
2204865 org.dr42.analogclock
|
||||
2206628 runcommand.fork
|
||||
2211264 com.ekaaty.vinyl-launcher
|
||||
2216429 org.kde.plasma.ipaddress
|
||||
2216432 org.kde.plasma.shamsi-calendar
|
||||
2217171 Concepto.menu.12
|
||||
2217301 hermes.observer.ram.P6
|
||||
2217313 hermes.observer.cpu.P6
|
||||
2224768 org.kde.plasma.dayprogress
|
||||
2225123 org.kde.olib.pinpanel
|
||||
2225378 material.clock
|
||||
@@ -0,0 +1,983 @@
|
||||
const scriptDir = Qt.resolvedUrl("sh/").toString().replace("file://", "");
|
||||
const configDir = "${APDATIFIER_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/apdatifier}/"
|
||||
const cacheDir = "${APDATIFIER_CACHE_DIR:-${XDG_CACHE_HOME:-$HOME/.cache}/apdatifier}/"
|
||||
const configFile = configDir + "config.conf"
|
||||
const cacheFile = cacheDir + "updates.json"
|
||||
const rulesFile = configDir + "rules.json"
|
||||
const newsFile = cacheDir + "news.json"
|
||||
const timestampFile = cacheDir + "lastCheck"
|
||||
|
||||
const procOpt = { stoppable: true }
|
||||
|
||||
function execute(command, callback, opt) {
|
||||
const component = Qt.createComponent("../ui/components/Shell.qml")
|
||||
if (component.status === Component.Ready) {
|
||||
const componentObject = component.createObject(root)
|
||||
if (componentObject) {
|
||||
if (opt && opt.stoppable) sts.proc = componentObject
|
||||
componentObject.exec(command, callback)
|
||||
} else {
|
||||
Error(1, "Failed to create executable DataSource object")
|
||||
}
|
||||
} else {
|
||||
Error(1, "Executable DataSource component not ready")
|
||||
}
|
||||
}
|
||||
|
||||
const readFile = (file) => `[ -f "${file}" ] && cat "${file}"`
|
||||
const writeFile = (data, redir, file) => `echo '${data}' ${redir} "${file}"`
|
||||
const saveTimestamp = () => execute(writeFile(Math.round(sts.lastCheck).toString(), '>', timestampFile))
|
||||
|
||||
const bash = (script, ...args) => scriptDir + script + ' ' + args.join(' ')
|
||||
const runInTerminal = (script, ...args) => {
|
||||
const cmd = bash('terminal', script, ...args)
|
||||
execute(`kstart -- bash -c '${cmd}'`)
|
||||
if (script === "upgrade") {
|
||||
runLater(5000, () => upgradeTimer.start())
|
||||
scheduler.stop()
|
||||
sts.busy = sts.upgrading = true
|
||||
sts.statusMsg = i18n("Upgrade in progress") + "..."
|
||||
sts.statusIco = cfg.ownIconsUI ? "toolbar_upgrade" : "akonadiconsole"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function runLater(ms, fn) {
|
||||
const timer = Qt.createQmlObject('import QtQuick 2.0; Timer { repeat: false }', parent)
|
||||
timer.interval = ms
|
||||
timer.triggered.connect(() => {
|
||||
fn()
|
||||
timer.destroy()
|
||||
})
|
||||
timer.start()
|
||||
}
|
||||
|
||||
const debug = true
|
||||
function log(message) {
|
||||
if (debug) console.log("[" + new Date().getTime().toString() + "] "+ "APDATIFIER: " + message)
|
||||
}
|
||||
|
||||
function Error(code, err) {
|
||||
if (err) {
|
||||
sts.errors = sts.errors.concat([{code: code, message: err.trim(), type: ""}])
|
||||
cfg.notifyErrors && notify.send("error", i18n("Exit code: ") + code, err.trim())
|
||||
setStatusBar()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function handleError(code, err, type, onError) {
|
||||
if (code && err) {
|
||||
sts.errors = sts.errors.concat([{code: code, message: err.trim(), type: type}])
|
||||
onError()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function init() {
|
||||
execute(bash('init'), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
loadConfig()
|
||||
})
|
||||
|
||||
function loadConfig() {
|
||||
execute(readFile(configFile), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
if (out) {
|
||||
const config = out.trim().split("\n")
|
||||
const convert = value => {
|
||||
if (!isNaN(parseFloat(value))) return parseFloat(value)
|
||||
if (value === "true" || value === "false") return value === 'true'
|
||||
return value
|
||||
}
|
||||
config.forEach(line => {
|
||||
const match = line.match(/(\w+)="([^"]*)"/)
|
||||
if (match) plasmoid.configuration[match[1]] = convert(match[2])
|
||||
})
|
||||
}
|
||||
|
||||
execute(readFile(timestampFile), (cmd, out, err, code) => {
|
||||
if (out) sts.lastCheck = parseInt(out.trim()) || 0
|
||||
loadCache()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function loadCache() {
|
||||
execute(readFile(cacheFile), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
if (out && validJSON(out, cacheFile)) cache = keys(JSON.parse(out.trim()))
|
||||
loadRules()
|
||||
})
|
||||
}
|
||||
|
||||
function loadRules() {
|
||||
execute(readFile(rulesFile), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
if (out && validJSON(out, rulesFile)) plasmoid.configuration.rules = out
|
||||
loadNews()
|
||||
})
|
||||
}
|
||||
|
||||
function loadNews() {
|
||||
execute(readFile(newsFile), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
if (out && validJSON(out, newsFile)) {
|
||||
const news = JSON.parse(out.trim())
|
||||
let migrate = false //
|
||||
for (const article of news) {
|
||||
// todo: remove later
|
||||
if (article.timestamp === undefined && article.date) {
|
||||
migrate = true
|
||||
const [d, t] = article.date.split(" | ")
|
||||
const [day, month, year] = d.split(".").map(Number)
|
||||
const [hour, minute] = t.split(":").map(Number)
|
||||
article.timestamp = Math.floor(new Date(year, month - 1, day, hour, minute).getTime() / 1000)
|
||||
delete article.date
|
||||
}//
|
||||
|
||||
addNewsItem(article)
|
||||
}
|
||||
|
||||
if (migrate) saveNews() //
|
||||
}
|
||||
|
||||
onStartup()
|
||||
})
|
||||
}
|
||||
|
||||
function onStartup() {
|
||||
sts.init = true
|
||||
checkDependencies()
|
||||
refreshListModel()
|
||||
upgradingState()
|
||||
}
|
||||
}
|
||||
|
||||
function saveConfig() {
|
||||
if (saveTimer.running) return
|
||||
let config = ""
|
||||
Object.keys(cfg).forEach(key => {
|
||||
if (key.endsWith("Default")) {
|
||||
let name = key.slice(0, -7)
|
||||
config += `${name}="${cfg[name]}"\n`
|
||||
}
|
||||
})
|
||||
execute(writeFile(config, ">", configFile))
|
||||
}
|
||||
|
||||
function checkDependencies() {
|
||||
const pkgs = "pacman flatpak fwupdmgr paru pikaur yay jq tmux alacritty foot ghostty gnome-terminal kitty konsole lxterminal ptyxis terminator tilix wezterm xterm yakuake"
|
||||
const checkPkg = (pkgs) => `for pkg in ${pkgs}; do command -v $pkg || echo; done`
|
||||
const populate = (data) => data.map(item => ({ "name": item.split("/").pop(), "value": item }))
|
||||
|
||||
execute(checkPkg(pkgs), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
|
||||
const output = out.split("\n")
|
||||
|
||||
const [pacman, flatpak, fwupdmgr, paru, pikaur, yay, jq, tmux ] = output.map(Boolean)
|
||||
cfg.packages = { pacman, flatpak, fwupdmgr, paru, pikaur, yay, jq, tmux }
|
||||
if (!cfg.wrapper) cfg.wrapper = paru ? "paru" : yay ? "yay" : pikaur ? "pikaur" : ""
|
||||
|
||||
const terminals = populate(output.slice(8).filter(Boolean))
|
||||
cfg.terminals = terminals.length > 0 ? terminals : null
|
||||
if (!cfg.terminal) cfg.terminal = cfg.terminals.length > 0 ? cfg.terminals[0].value : ""
|
||||
|
||||
if (!pacman) plasmoid.configuration.arch = false
|
||||
if (!pacman || (!yay && !paru && !pikaur)) plasmoid.configuration.aur = false
|
||||
if (!flatpak) plasmoid.configuration.flatpak = false
|
||||
if (!fwupdmgr) plasmoid.configuration.fwupd = false
|
||||
if (!tmux) plasmoid.configuration.tmuxSession = false
|
||||
if (!jq) {
|
||||
plasmoid.configuration.widgets = false
|
||||
plasmoid.configuration.feedsEnabled = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function upgradePackage(name, appID, contentID) {
|
||||
if (sts.upgrading) return
|
||||
|
||||
if (appID) {
|
||||
runInTerminal("upgrade", "flatpak", appID, name)
|
||||
} else if (contentID) {
|
||||
runInTerminal("upgrade", "widget", contentID, name)
|
||||
}
|
||||
}
|
||||
|
||||
function management() {
|
||||
runInTerminal("management")
|
||||
}
|
||||
|
||||
|
||||
function upgradingState() {
|
||||
const checkProc = `pgrep -f "apdatifier.*upgrade*"`
|
||||
execute(checkProc, (cmd, out, err, code) => {
|
||||
if (!out) {
|
||||
sts.busy = sts.upgrading = false
|
||||
upgradeTimer.stop()
|
||||
execute(bash('utils', "currentVersions"), (cmd, out, err, code) => {
|
||||
if (Error(code, err)) return
|
||||
if (!out || !validJSON(out)) return
|
||||
const currentVersions = JSON.parse(out)
|
||||
const newList = cache.filter(cached => {
|
||||
const current = currentVersions.find(pkg => pkg.NM.replace(/ /g, "-").toLowerCase() === cached.NM)
|
||||
return current && current.VO === cached.VO + cached.AC
|
||||
})
|
||||
if (JSON.stringify(cache) !== JSON.stringify(newList)) {
|
||||
cache = newList
|
||||
refreshListModel()
|
||||
saveCache(cache)
|
||||
}
|
||||
setStatusBar()
|
||||
resumeScheduler()
|
||||
})
|
||||
} else {
|
||||
scheduler.stop()
|
||||
upgradeTimer.start()
|
||||
sts.busy = sts.upgrading = true
|
||||
sts.statusMsg = i18n("Upgrade in progress") + "..."
|
||||
sts.statusIco = cfg.ownIconsUI ? "toolbar_upgrade" : "akonadiconsole"
|
||||
}
|
||||
updatePlasmoidStatus()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function upgradeSystem() {
|
||||
if (sts.upgrading && !cfg.tmuxSession) return
|
||||
const ignorePkgs = buildIgnoreString()
|
||||
runInTerminal("upgrade", "full", `${ignorePkgs}`)
|
||||
}
|
||||
|
||||
|
||||
function stopCheck() {
|
||||
sts.errors = []
|
||||
sts.busy = false
|
||||
sts.proc?.cleanup()
|
||||
setStatusBar()
|
||||
resumeScheduler()
|
||||
|
||||
if (isOnline) {
|
||||
sts.lastCheck = Date.now()
|
||||
saveTimestamp()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function loadIgnorePkgs() {
|
||||
return new Promise(resolve => {
|
||||
if (!cfg.packages || !cfg.packages.pacman) {
|
||||
resolve([])
|
||||
return
|
||||
}
|
||||
|
||||
execute("pacman-conf IgnorePkg", (cmd, out, err, code) => {
|
||||
if (code || !out) {
|
||||
resolve([])
|
||||
return
|
||||
}
|
||||
|
||||
const tokens = out.trim().split(/\s+/).filter(Boolean)
|
||||
const escapeRe = s => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
|
||||
|
||||
const patterns = tokens.map(token => {
|
||||
let pat = token.split(/[<>=]/)[0].trim()
|
||||
if (!pat) return null
|
||||
let re = escapeRe(pat)
|
||||
re = re.replace(/\\\*/g, ".*").replace(/\\\?/g, ".")
|
||||
return new RegExp("^" + re + "$")
|
||||
}).filter(Boolean)
|
||||
|
||||
resolve(patterns)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function buildIgnoreString() {
|
||||
const rules = !cfg.rules ? [] : JSON.parse(cfg.rules)
|
||||
if (!Array.isArray(rules)) return ''
|
||||
|
||||
const ignoreItems = rules.filter(rule =>
|
||||
rule && rule.ignore === true
|
||||
)
|
||||
|
||||
if (ignoreItems.length === 0) return ''
|
||||
|
||||
const values = ignoreItems.map(rule => rule.value)
|
||||
return `--ignore ${values.join(',')}`
|
||||
}
|
||||
|
||||
|
||||
function checkUpdates() {
|
||||
if (sts.upgrading) return
|
||||
if (!isOnline) return
|
||||
|
||||
sts.errors = []
|
||||
scheduler.stop()
|
||||
sts.busy = true
|
||||
|
||||
let archRepos = [], archAur = [], flatpak = [], widgets = [], firmwares = []
|
||||
|
||||
const feeds = cfg.feedsEnabled ? parseCustomFeeds(cfg.feeds).map(u => `'${u}'`).join(' ') : ""
|
||||
const news = feeds && cfg.packages?.jq
|
||||
|
||||
news ? checkNews() :
|
||||
cfg.arch ? checkRepos() :
|
||||
cfg.aur ? checkAur() :
|
||||
cfg.flatpak ? checkFlatpak() :
|
||||
cfg.widgets ? checkWidgets() :
|
||||
cfg.fwupd ? checkFirmwares() :
|
||||
merge()
|
||||
|
||||
function checkNews() {
|
||||
const next = () => cfg.arch ? checkRepos() : cfg.aur ? checkAur() : cfg.flatpak ? checkFlatpak() :
|
||||
cfg.widgets ? checkWidgets() : cfg.fwupd ? checkFirmwares() : merge()
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_news" : "news-subscribe"
|
||||
sts.statusMsg = i18n("Checking latest news...")
|
||||
execute(bash('utils', 'rss', feeds), (cmd, out, err, code) => {
|
||||
if (out) {
|
||||
const news = JSON.parse(out.trim())
|
||||
if (cfg.notifyNews) {
|
||||
const currentLinks = new Set(Array.from(Array(newsModel.count), (_, i) => newsModel.get(i).link))
|
||||
const newItems = news.filter(item => !currentLinks.has(item.link))
|
||||
if (newItems.length > 0) {
|
||||
const title = i18np("%1 new article", "%1 new articles", newItems.length)
|
||||
const body = newItems.map(item => `<b>${item.title}</b>: ${item.article}`).join("\n")
|
||||
notify.send("news", title, body, newItems[0].link)
|
||||
}
|
||||
}
|
||||
|
||||
if (newsModel.count === 0) {
|
||||
news.forEach(item => addNewsItem(item))
|
||||
} else {
|
||||
let newLinks = news.map(item => item.link)
|
||||
let prevLinks = modelToArray(newsModel).map(item => item.link)
|
||||
|
||||
news.forEach(item => {
|
||||
if (!prevLinks.includes(item.link)) addNewsItem(item)
|
||||
})
|
||||
|
||||
for (let i = newsModel.count - 1; i >= 0; --i) {
|
||||
let modelItem = newsModel.get(i)
|
||||
if (!newLinks.includes(modelItem.link)) newsModel.remove(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (handleError(code, err, "news", next)) return
|
||||
next()
|
||||
}, procOpt)
|
||||
}
|
||||
|
||||
function checkRepos() {
|
||||
const next = () => cfg.aur ? checkAur() : cfg.flatpak ? checkFlatpak() :
|
||||
cfg.widgets ? checkWidgets() : cfg.fwupd ? checkFirmwares() : merge()
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_package" : "apdatifier-package"
|
||||
sts.statusMsg = i18n("Synchronizing pacman databases...")
|
||||
execute(bash('utils', 'syncdb'), (cmd, out, err, code) => {
|
||||
if (handleError(code, err, "repositories", next)) return
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_package" : "apdatifier-package"
|
||||
sts.statusMsg = i18n("Checking system updates...")
|
||||
execute(`pacman -Qu --dbpath "${cfg.dbPath}" 2>&1`, (cmd, out, err, code) => {
|
||||
if (handleError(code, err, "repositories", next)) return
|
||||
const updates = out ? out.trim().split("\n") : []
|
||||
makeArchList(updates, "repositories").then(result => {
|
||||
archRepos = result
|
||||
next()
|
||||
})
|
||||
}, procOpt)
|
||||
}, procOpt)
|
||||
}
|
||||
|
||||
function checkAur() {
|
||||
const next = () => cfg.flatpak ? checkFlatpak() : cfg.widgets ? checkWidgets() : cfg.fwupd ? checkFirmwares() : merge()
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_package" : "apdatifier-package"
|
||||
sts.statusMsg = i18n("Checking AUR updates...")
|
||||
const cmdArg = cfg.wrapper === "pikaur" ? " -Qua --noconfirm" : " -Qua"
|
||||
execute(cfg.wrapper + cmdArg, (cmd, out, err, code) => {
|
||||
if (handleError(code, err, "aur", next)) return
|
||||
const updates = out ? out.trim().split("\n") : []
|
||||
makeArchList(updates, "aur").then(result => {
|
||||
archAur = result
|
||||
next()
|
||||
})
|
||||
}, procOpt)
|
||||
}
|
||||
|
||||
function checkFlatpak() {
|
||||
const next = () => cfg.widgets ? checkWidgets() : cfg.fwupd ? checkFirmwares() : merge()
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_flatpak" : "apdatifier-flatpak"
|
||||
sts.statusMsg = i18n("Synchronizing flatpak appstream...")
|
||||
execute("flatpak update --appstream >/dev/null 2>&1", (cmd, out, err, code) => {
|
||||
if (handleError(code, err, "flatpak", next)) return
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_flatpak" : "apdatifier-flatpak"
|
||||
sts.statusMsg = i18n("Checking flatpak updates...")
|
||||
execute("flatpak remote-ls --app --updates --show-details", (cmd, out, err, code) => {
|
||||
if (handleError(code, err, "flatpak", next)) return
|
||||
const updates = out.trim()
|
||||
makeFlatpakList(updates, "flatpak").then(result => {
|
||||
flatpak = result
|
||||
next()
|
||||
})
|
||||
}, procOpt)
|
||||
}, procOpt)
|
||||
}
|
||||
|
||||
function checkWidgets() {
|
||||
const next = () => cfg.fwupd ? checkFirmwares() : merge()
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_widgets" : "start-here-kde-plasma-symbolic"
|
||||
sts.statusMsg = i18n("Checking widgets updates...")
|
||||
execute(bash('widgets', 'check'), (cmd, out, err, code) => {
|
||||
if (handleError(code, err, "widgets", next)) return
|
||||
widgets = JSON.parse(out.trim())
|
||||
next()
|
||||
}, procOpt)
|
||||
}
|
||||
|
||||
function checkFirmwares() {
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_package" : "apdatifier-package"
|
||||
sts.statusMsg = i18n("Checking firmware updates...")
|
||||
execute("fwupdmgr refresh --force", (cmd, out, err, code) => {
|
||||
if (handleError(code, err, "fwupdmgr", merge)) return
|
||||
execute("fwupdmgr get-updates --json", (cmd, out, err, code) => {
|
||||
if (handleError(code, err, "fwupdmgr", merge)) return
|
||||
firmwares = JSON.parse(out).Devices.map(device => ({
|
||||
NM: `${device.Vendor}-${device.Name}`.replace(/ /g, "-").toLowerCase(),
|
||||
RE: "fwupd",
|
||||
DE: device.Releases?.[0]?.Summary || "",
|
||||
LN: device.Releases?.[0]?.Homepage || "",
|
||||
IN: device.Icons?.[0] || "",
|
||||
VO: device.Version || "",
|
||||
VN: device.Releases?.[0]?.Version || ""
|
||||
}))
|
||||
|
||||
merge()
|
||||
}, procOpt)
|
||||
}, procOpt)
|
||||
}
|
||||
|
||||
function merge() {
|
||||
const list = keys(archRepos.concat(archAur, flatpak, widgets, firmwares))
|
||||
|
||||
const finish = (finalList) => {
|
||||
sts.errors.length > 0
|
||||
? runLater(3000, () => isOnline ? finalize(finalList) : stopCheck())
|
||||
: finalize(finalList)
|
||||
}
|
||||
|
||||
loadIgnorePkgs().then(ignorePatterns => {
|
||||
if (!ignorePatterns || ignorePatterns.length === 0) {
|
||||
finish(list)
|
||||
} else {
|
||||
const filtered = list.filter(pkg => !ignorePatterns.some(re => re.test(pkg.NM)))
|
||||
finish(filtered)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function makeArchList(updates, source) {
|
||||
return new Promise((resolve) => {
|
||||
if (updates.length === 0) {
|
||||
resolve([])
|
||||
} else {
|
||||
const updateRe = /^([A-Za-z0-9@._+-]+)\s+(\S+)\s+->\s+(\S+)/
|
||||
const parsed = updates.map(l => l.trim()).map(l => l.match(updateRe)).filter(Boolean)
|
||||
if (parsed.length === 0) {
|
||||
resolve([])
|
||||
return
|
||||
}
|
||||
|
||||
const pkgs = parsed.map(m => m[1]).join(' ')
|
||||
execute(`LC_ALL=C.UTF-8 pacman -Sl --dbpath "${cfg.dbPath}" | awk 'index($0,"[installed:")>0'`, (cmd, out, err, code) => {
|
||||
if (code && handleError(code, err, source, () => resolve([]))) return
|
||||
const repositories = out.trim().split('\n')
|
||||
|
||||
execute("LC_ALL=C.UTF-8 pacman -Qi " + pkgs, (cmd, out, err, code) => {
|
||||
if (code && handleError(code, err, source, () => resolve([]))) return
|
||||
const descriptions = out.trim().split('\n\n')
|
||||
|
||||
execute(bash('utils', 'getIcons', pkgs), (cmd, out, err, code) => {
|
||||
if (code && handleError(code, err, source, () => resolve([]))) return
|
||||
const icons = out.split('\n')
|
||||
|
||||
const iconsMap = new Map()
|
||||
icons.map(l => l.split(' ', 2))
|
||||
.filter(([name, icon]) => name && icon)
|
||||
.forEach(([name, icon]) => iconsMap.set(name, icon))
|
||||
|
||||
const versionsMap = new Map()
|
||||
parsed.forEach(m => versionsMap.set(m[1], { currentVer: m[2], newVer: m[3] }))
|
||||
|
||||
const repositoriesMap = new Map()
|
||||
repositories.forEach(line => {
|
||||
const [repo, name] = line.trim().split(/\s+/)
|
||||
if (!repositoriesMap.has(name)) {
|
||||
repositoriesMap.set(name, repo)
|
||||
}
|
||||
})
|
||||
|
||||
const keyMap = {
|
||||
'Name': 'NM',
|
||||
'Description': 'DE',
|
||||
'URL': 'LN',
|
||||
'Groups': 'GR',
|
||||
'Provides': 'PR',
|
||||
'Depends On': 'DP',
|
||||
'Required By': 'RQ',
|
||||
'Optional For': 'OF',
|
||||
'Conflicts With': 'CF',
|
||||
'Replaces': 'RP',
|
||||
'Installed Size': 'IS',
|
||||
'Install Date': 'DT',
|
||||
'Install Reason': 'RN'
|
||||
}
|
||||
|
||||
const packagesData = descriptions.map(description => {
|
||||
const pkg = {}
|
||||
const lines = description.split('\n')
|
||||
for (const line of lines) {
|
||||
const match = line.match(/^(\w+(?:\s+\w+)*)\s+:\s*(.*)/)
|
||||
if (match) {
|
||||
const key = match[1].trim()
|
||||
const value = match[2].trim()
|
||||
if (value === "None") continue
|
||||
const mappedKey = keyMap[key]
|
||||
if (mappedKey) pkg[mappedKey] = value
|
||||
}
|
||||
}
|
||||
|
||||
if (iconsMap.has(pkg.NM)) pkg.IN = iconsMap.get(pkg.NM)
|
||||
|
||||
const versions = versionsMap.get(pkg.NM) || {}
|
||||
pkg.VO = versions.currentVer || ""
|
||||
pkg.VN = (versions.newVer === "latest-commit") ? i18n("latest commit") : (versions.newVer || "")
|
||||
pkg.RE = repositoriesMap.get(pkg.NM) || (pkg.NM.endsWith("-git") || pkg.VN === i18n("latest commit") ? "devel" : "aur")
|
||||
|
||||
if (pkg.RN.includes("Explicitly")) {
|
||||
pkg.RN = "explicit"
|
||||
} else if (!pkg.RQ && !pkg.OF) {
|
||||
pkg.RN = "orphan"
|
||||
} else {
|
||||
pkg.RN = "dependency"
|
||||
}
|
||||
|
||||
delete pkg.OF
|
||||
|
||||
return pkg
|
||||
})
|
||||
|
||||
resolve([...new Map(packagesData.map(item => [item.NM, item])).values()])
|
||||
}, procOpt)
|
||||
}, procOpt)
|
||||
}, procOpt)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function makeFlatpakList(updates) {
|
||||
return new Promise((resolve) => {
|
||||
if (!updates) {
|
||||
resolve([])
|
||||
} else {
|
||||
execute("flatpak list --app --columns=application,version,active", (cmd, out, err, code) => {
|
||||
if (code && handleError(code, err, "flatpak", () => resolve([]))) return
|
||||
const description = out.trim().split("\n").reduce((obj, line) => {
|
||||
const [ID, VO, AC] = line.split("\t").map(entry => entry.trim())
|
||||
obj[ID] = { VO, AC }
|
||||
return obj
|
||||
}, {})
|
||||
const extendedList = updates.split("\n").map(line => {
|
||||
const [NM, DE, ID, VN, BR, , RE, , CM, RT, IS, DS] = line.split("\t").map(entry => entry.trim())
|
||||
const { VO, AC } = description[ID]
|
||||
return {
|
||||
NM: NM.replace(/ /g, "-").toLowerCase(),
|
||||
DE, LN: "https://flathub.org/apps/" + ID,
|
||||
ID, BR, RE, AC, CM, RT, IS, DS, VO,
|
||||
VN: VO === VN ? i18n("latest commit") : VN
|
||||
}
|
||||
})
|
||||
resolve(extendedList)
|
||||
}, procOpt)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function sortList(list, byName) {
|
||||
if (!list) return
|
||||
|
||||
return list.sort((a, b) => {
|
||||
const name = a.NM.localeCompare(b.NM)
|
||||
const repo = a.RE.localeCompare(b.RE)
|
||||
if (byName || !cfg.sorting) return name
|
||||
|
||||
if (a.IM !== b.IM) return a.IM ? -1 : 1
|
||||
|
||||
const develA = a.RE.includes("devel")
|
||||
const develB = b.RE.includes("devel")
|
||||
if (develA !== develB) return develA ? -1 : 1
|
||||
|
||||
const aurA = a.RE.includes("aur")
|
||||
const aurB = b.RE.includes("aur")
|
||||
if (aurA !== aurB) return aurA ? -1 : 1
|
||||
|
||||
return repo || name
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
function refreshListModel(list) {
|
||||
list = sortList(applyRules(list || cache)) || []
|
||||
sts.count = list.length || 0
|
||||
setStatusBar()
|
||||
|
||||
if (!list) return
|
||||
|
||||
listModel.clear()
|
||||
list.forEach(item => listModel.append(item))
|
||||
}
|
||||
|
||||
|
||||
function finalize(list) {
|
||||
sts.busy = false
|
||||
resumeScheduler()
|
||||
|
||||
sts.lastCheck = Date.now()
|
||||
saveTimestamp()
|
||||
|
||||
if (cfg.notifyErrors && sts.error) {
|
||||
const notifyMsg = sts.errors.map(err => `<b>${err.type}</b> => ${err.message} (Exit code ${err.code})`).join('\n\n')
|
||||
notify.send("error", i18np("%1 error occurred", "%1 errors occurred", sts.errors.length), notifyMsg)
|
||||
}
|
||||
|
||||
if (!list) {
|
||||
listModel.clear()
|
||||
execute(writeFile("[]", '>', cacheFile))
|
||||
cache = []
|
||||
sts.count = 0
|
||||
setStatusBar()
|
||||
return
|
||||
}
|
||||
|
||||
if (sts.error && cache.length > 0) {
|
||||
var errorTypes = {}
|
||||
for (var i = 0; i < sts.errors.length; i++) errorTypes[sts.errors[i].type] = true
|
||||
|
||||
var currentNames = {}
|
||||
for (var j = 0; j < list.length; j++) currentNames[list[j].NM] = true
|
||||
|
||||
var cachedPackages = []
|
||||
for (var k = 0; k < cache.length; k++) {
|
||||
var cached = cache[k]
|
||||
if (currentNames[cached.NM]) continue
|
||||
|
||||
if (errorTypes.repositories && cached.RE && cached.RE !== "aur" && cached.RE !== "devel") {
|
||||
cachedPackages.push(cached)
|
||||
} else if (errorTypes.aur && (cached.RE === "aur" || cached.RE === "devel")) {
|
||||
cachedPackages.push(cached)
|
||||
} else if (errorTypes.flatpak && cached.ID) {
|
||||
cachedPackages.push(cached)
|
||||
} else if (errorTypes.widgets && cached.CN) {
|
||||
cachedPackages.push(cached)
|
||||
}
|
||||
}
|
||||
|
||||
list = list.concat(cachedPackages)
|
||||
}
|
||||
|
||||
refreshListModel(list)
|
||||
|
||||
if (cfg.notifyUpdates) {
|
||||
const cached = new Map(cache.map(el => [el.NM, el.VN]))
|
||||
const newList = applyRules(list).filter(el => !cached.has(el.NM) || (cfg.notifyEveryBump && cached.get(el.NM) !== el.VN))
|
||||
|
||||
if (newList.length > 0) {
|
||||
const title = i18np("+%1 new update", "+%1 new updates", newList.length)
|
||||
const body = newList.map(pkg => `${pkg.NM} → ${pkg.VN}`).join("\n")
|
||||
notify.send("updates", title, body)
|
||||
}
|
||||
}
|
||||
|
||||
cache = list
|
||||
saveCache(cache)
|
||||
}
|
||||
|
||||
function saveCache(list) {
|
||||
if (JSON.stringify(list).length > 130000) {
|
||||
let start = 0
|
||||
const chunkSize = 200
|
||||
const json = JSON.stringify(keys(sortList(JSON.parse(JSON.stringify(list)), true))).replace(/},/g, "},\n").replace(/'/g, "")
|
||||
const lines = json.split("\n")
|
||||
while (start < lines.length) {
|
||||
const chunk = lines.slice(start, start + chunkSize).join("\n")
|
||||
const redir = start === 0 ? ">" : ">>"
|
||||
execute(writeFile(chunk, redir, `${cacheFile}_${Math.ceil(start / chunkSize)}`))
|
||||
start += chunkSize
|
||||
}
|
||||
execute(bash('utils', 'combineFiles', cacheFile))
|
||||
} else {
|
||||
const json = toFileFormat(keys(sortList(JSON.parse(JSON.stringify(list)), true)))
|
||||
execute(writeFile(json, '>', cacheFile))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setStatusBar() {
|
||||
if (sts.count > 0) {
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_pending" : "accept_time_event"
|
||||
sts.statusMsg = sts.count + " " + i18np("update is pending", "updates are pending", sts.count)
|
||||
} else {
|
||||
sts.statusIco = cfg.ownIconsUI ? "status_blank" : ""
|
||||
sts.statusMsg = ""
|
||||
}
|
||||
}
|
||||
|
||||
function resumeScheduler() {
|
||||
if (cfg.checkMode !== "manual") {
|
||||
scheduler.start()
|
||||
}
|
||||
}
|
||||
|
||||
function searchScheduler(options) {
|
||||
if (!isOnline) return
|
||||
if (isMetered) return
|
||||
|
||||
const mode = cfg.checkMode
|
||||
if (mode === "manual") {
|
||||
scheduler.stop()
|
||||
return
|
||||
}
|
||||
|
||||
const currTime = new Date().getTime()
|
||||
const lastCheck = sts.lastCheck || 0
|
||||
let nextCheck = null
|
||||
|
||||
if (mode === "interval") {
|
||||
const interval = parseFloat(cfg.intervalMinutes)
|
||||
const intervalMs = interval * 60 * 1000
|
||||
nextCheck = lastCheck ? (lastCheck + intervalMs) : (currTime + intervalMs)
|
||||
} else if (mode === "daily") {
|
||||
const scheduled = new Date(currTime)
|
||||
scheduled.setHours(cfg.dailyHour, cfg.dailyMinute, 0, 0)
|
||||
if (scheduled.getTime() <= currTime) scheduled.setDate(scheduled.getDate() + 1)
|
||||
nextCheck = scheduled.getTime()
|
||||
} else if (mode === "weekly") {
|
||||
const scheduled = new Date(currTime)
|
||||
const targetDay = Number(cfg.weeklyDay)
|
||||
const daysAhead = (targetDay - scheduled.getDay() + 7) % 7
|
||||
scheduled.setDate(scheduled.getDate() + daysAhead)
|
||||
scheduled.setHours(cfg.weeklyHour, cfg.weeklyMinute, 0, 0)
|
||||
if (scheduled.getTime() <= currTime) scheduled.setDate(scheduled.getDate() + 7)
|
||||
nextCheck = scheduled.getTime()
|
||||
}
|
||||
|
||||
options = options || {}
|
||||
if (options.simulate) return nextCheck
|
||||
|
||||
if (nextCheck && nextCheck <= (currTime + 10000) && nextCheck > lastCheck) {
|
||||
checkUpdates()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
function getCheckTime() {
|
||||
const currTime = new Date().getTime()
|
||||
const lastCheck = sts.lastCheck || currTime
|
||||
|
||||
const formatDelta = (ms1, ms2) => {
|
||||
const diff = Math.max(0, Math.floor((ms1 - ms2) / 1000))
|
||||
const days = Math.floor(diff / 86400)
|
||||
const hours = Math.floor((diff % 86400) / 3600)
|
||||
const minutes = Math.floor((diff % 3600) / 60)
|
||||
const seconds = diff % 60
|
||||
const parts = []
|
||||
if (days > 0) parts.push([days, i18np("%1 day", "%1 days", days)])
|
||||
if (hours > 0) parts.push([hours, i18np("%1 hour", "%1 hours", hours)])
|
||||
if (minutes > 0) parts.push([minutes, i18np("%1 minute", "%1 minutes", minutes)])
|
||||
if (parts.length === 0 && seconds > 0) return i18n("less than a minute")
|
||||
if (seconds > 0) parts.push([seconds, i18np("%1 second", "%1 seconds", seconds)])
|
||||
const take = parts.slice(0, 2).map(p => p[1])
|
||||
return take.join(' ')
|
||||
}
|
||||
|
||||
const lastCheckStr = (currTime > lastCheck) ? `${i18n("Last check:")} ${formatDelta(currTime, lastCheck)} ${i18n("ago")}` : ""
|
||||
|
||||
if (!scheduler.running) return lastCheckStr
|
||||
|
||||
const nextCheck = searchScheduler({ simulate: true })
|
||||
if (!nextCheck) return lastCheckStr
|
||||
|
||||
const nextCheckStr = (currTime < nextCheck) ? `${i18n("Next check in:")} ${formatDelta(nextCheck, currTime)}` : ""
|
||||
|
||||
if (lastCheckStr && nextCheckStr) return `${lastCheckStr}\n${nextCheckStr}`
|
||||
if (lastCheckStr && !nextCheckStr) return lastCheckStr
|
||||
if (!lastCheckStr && nextCheckStr) return nextCheckStr
|
||||
}
|
||||
|
||||
|
||||
function setIndex(value, arr) {
|
||||
let index = 0
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (arr[i]["value"] == value) {
|
||||
index = i
|
||||
break
|
||||
}
|
||||
}
|
||||
return index
|
||||
}
|
||||
|
||||
const defaultIcon = "apdatifier-plasmoid"
|
||||
function setIcon(icon) {
|
||||
return icon === "" ? defaultIcon : icon
|
||||
}
|
||||
|
||||
|
||||
function applyRules(list) {
|
||||
const rules = !cfg.rules ? [] : JSON.parse(cfg.rules)
|
||||
|
||||
list.forEach(el => {
|
||||
el.IC = el.IN ? el.IN : el.ID ? el.ID : "apdatifier-package"
|
||||
el.EX = false
|
||||
el.IM = false
|
||||
})
|
||||
|
||||
function applyRule(el, rule) {
|
||||
const types = {
|
||||
'name' : () => el.NM === rule.value,
|
||||
'regex' : () => { try { return new RegExp(rule.value).test(el.NM) } catch(e) { return false } }
|
||||
}
|
||||
|
||||
if (types[rule.type] && types[rule.type]()) {
|
||||
el.IC = rule.icon
|
||||
el.EX = rule.excluded
|
||||
el.IM = rule.important
|
||||
}
|
||||
}
|
||||
|
||||
rules.forEach(rule => list.forEach(el => applyRule(el, rule)))
|
||||
return list.filter(el => !el.EX)
|
||||
}
|
||||
|
||||
|
||||
function keys(list) {
|
||||
const keysList = ["GR", "PR", "DP", "RQ", "CF", "RP", "IS", "DT", "RN", "ID", "BR", "AC", "CM", "RT", "DS", "CN", "AU"]
|
||||
|
||||
list.forEach(el => {
|
||||
keysList.forEach(key => {
|
||||
if (!el.hasOwnProperty(key)) el[key] = ""
|
||||
else if (el[key] === "") delete el[key]
|
||||
})
|
||||
|
||||
if (el.hasOwnProperty("IC")) delete el["IC"]
|
||||
if (el.hasOwnProperty("EX")) delete el["EX"]
|
||||
if (el.hasOwnProperty("IM")) delete el["IM"]
|
||||
})
|
||||
|
||||
return list
|
||||
}
|
||||
|
||||
|
||||
function switchScheduler() {
|
||||
if (cfg.checkMode === "manual" || sts.busy) return
|
||||
scheduler.running ? scheduler.stop() : scheduler.start()
|
||||
}
|
||||
|
||||
function toFileFormat(obj) {
|
||||
const jsonStringWithSpace = JSON.stringify(obj, null, 2)
|
||||
const writebleJsonStrings = jsonStringWithSpace.replace(/'/g, "")
|
||||
return writebleJsonStrings
|
||||
}
|
||||
|
||||
function validJSON(string, file) {
|
||||
try {
|
||||
const json = JSON.parse(string)
|
||||
if (json && typeof json === "object") return json
|
||||
}
|
||||
catch (e) {
|
||||
file ? Error(1, `JSON data at ${file} is corrupted or broken and cannot be processed`)
|
||||
: Error(1, "JSON data is broken and cannot be processed")
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
function isValidFeedUrl(value) {
|
||||
return /^https?:\/\/\S+$/i.test((value || "").trim())
|
||||
}
|
||||
|
||||
function parseCustomFeeds(value) {
|
||||
return String(value || "")
|
||||
.split(/[\n|]/)
|
||||
.map(feed => feed.trim())
|
||||
.filter(isValidFeedUrl)
|
||||
}
|
||||
|
||||
function serializeCustomFeeds(value) {
|
||||
return (Array.isArray(value) ? value : parseCustomFeeds(value))
|
||||
.map(feed => feed.trim())
|
||||
.filter((feed, index, feeds) => isValidFeedUrl(feed) && feeds.indexOf(feed) === index)
|
||||
.join("|")
|
||||
}
|
||||
|
||||
|
||||
|
||||
function modelToArray(model) {
|
||||
const array = new Array(model.count)
|
||||
for (let i = 0; i < model.count; i++) {
|
||||
array[i] = model.get(i)
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
function findInsertIndex(news) {
|
||||
const n = newsModel.count
|
||||
let firstRemoved = n
|
||||
|
||||
for (let i = 0; i < n; i++) {
|
||||
if (newsModel.get(i).removed) {
|
||||
firstRemoved = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
const start = news.removed ? firstRemoved : 0
|
||||
const end = news.removed ? n : firstRemoved
|
||||
|
||||
for (let i = start; i < end; i++) {
|
||||
if (newsModel.get(i).timestamp < news.timestamp)
|
||||
return i
|
||||
}
|
||||
|
||||
return end
|
||||
}
|
||||
|
||||
function saveNews() {
|
||||
execute(writeFile(toFileFormat(modelToArray(newsModel)), '>', newsFile))
|
||||
}
|
||||
|
||||
function addNewsItem(news) {
|
||||
newsModel.insert(findInsertIndex(news), news)
|
||||
}
|
||||
|
||||
function removeNewsItem(index) {
|
||||
const item = newsModel.get(index)
|
||||
if (!item || item.removed) return
|
||||
|
||||
let to = findInsertIndex({ timestamp: item.timestamp, removed: true })
|
||||
if (to > index) to--
|
||||
|
||||
newsModel.move(index, to, 1)
|
||||
newsModel.setProperty(to, "removed", true)
|
||||
|
||||
saveNews()
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 64 KiB |
|
After Width: | Height: | Size: 57 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 23 23">
|
||||
<defs>
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text { color: #232629; }
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
class="ColorScheme-Text"
|
||||
style="fill:currentColor"
|
||||
d="m22.43,12.56s-.05-.04-.07-.07c-.04-.04-.07-.08-.12-.11,0,0,0,0,0,0,0,0,0,0,0,0l-4.97-2.6v-5.2c0-.09-.03-.17-.07-.24-.02-.03-.05-.04-.07-.07-.04-.04-.07-.08-.12-.11,0,0,0,0,0,0,0,0,0,0,0,0l-4.75-2.48c-.47-.24-1.04-.24-1.5,0l-4.75,2.48s0,0,0,0c0,0,0,0,0,0-.05.03-.08.07-.12.11-.02.02-.06.04-.07.07-.05.07-.07.15-.07.24v5.2L.76,12.38s0,0,0,0c0,0,0,0,0,0-.06.03-.09.08-.13.12-.02.02-.04.03-.06.05-.05.07-.07.15-.07.24v5.48c0,.17.1.33.26.42l5.24,2.74c.08.04.17.06.26.06s.18-.02.26-.06l4.98-2.6,4.98,2.6c.08.04.17.06.26.06s.18-.02.26-.06l5.24-2.74c.16-.09.26-.24.26-.42v-5.48c0-.09-.03-.17-.07-.24Zm-16.9,2.05l-2.7-1.41c-.34-.18-.34-.62,0-.79l2.7-1.41c.45-.24,1-.24,1.45,0l2.7,1.41c.34.18.34.62,0,.79l-2.7,1.41c-.45.24-1,.24-1.45,0Zm5.44,2.75c0,.4-.23.76-.61.96l-2.75,1.44c-.37.19-.83-.05-.83-.44v-2.86c0-.4.23-.76.61-.96l2.75-1.44c.37-.19.83.05.83.44v2.86Zm-.2-10.97l-2.7-1.41c-.34-.18-.34-.62,0-.79l2.7-1.41c.45-.24,1-.24,1.45,0l2.7,1.41c.34.18.34.62,0,.79l-2.7,1.41c-.45.24-1,.24-1.45,0Zm1.25,4.71v-2.86c0-.4.23-.76.61-.96l2.75-1.44c.37-.19.83.05.83.44v2.86c0,.4-.23.76-.61.96l-2.75,1.44c-.37.19-.83-.05-.83-.44Zm3.99,3.51l-2.7-1.41c-.34-.18-.34-.62,0-.79l2.7-1.41c.45-.24,1-.24,1.45,0l2.7,1.41c.34.18.34.62,0,.79l-2.7,1.41c-.45.24-1,.24-1.45,0Zm5.44,2.75c0,.4-.23.76-.61.96l-2.75,1.44c-.37.19-.83-.05-.83-.44v-2.86c0-.4.23-.76.61-.96l2.75-1.44c.37-.19.83.05.83.44v2.86Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 23 23">
|
||||
<defs>
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text { color: #232629; }
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
class="ColorScheme-Text"
|
||||
style="fill:currentColor"
|
||||
d="m21.5,6.21c0-.06-.04-.11-.06-.18-.02-.09-.04-.19-.09-.27-.04-.06-.11-.1-.16-.15-.06-.06-.11-.13-.19-.17,0,0-.02,0-.03,0,0,0-.01-.01-.02-.02l-7.71-3.55c-1.1-.51-2.39-.51-3.49,0l-7.71,3.55s-.01.01-.02.02c0,0-.02,0-.03,0-.08.04-.12.11-.19.17-.05.05-.12.09-.16.15-.05.08-.07.17-.09.27-.02.06-.06.11-.06.18,0,0,0,0,0,.01,0,0,0,0,0,.01v9.57c0,.32.19.62.49.78l9.01,4.79c.15.08.32.12.49.12s.34-.04.49-.12l8.5-4.51c.62-.33,1.01-.94,1.01-1.6V6.24s0,0,0-.01c0,0,0,0,0-.01Zm-16.07-.32l5.2-2.4c.55-.25,1.19-.25,1.73,0l5.2,2.4c.37.17.39.66.03.85l-5.13,2.72c-.6.32-1.33.32-1.93,0l-5.13-2.72c-.36-.19-.35-.68.03-.85Zm14.1,8.5c0,.55-.32,1.06-.84,1.33l-5.44,2.88c-.34.18-.77-.05-.77-.41v-5.77c0-.55.32-1.05.83-1.33l3.24-1.72,2.2-1.16c.34-.18.77.05.77.41v5.77Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,17 @@
|
||||
<svg
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text { color: #232629; }
|
||||
</style>
|
||||
</defs>
|
||||
<path class="ColorScheme-Text" style="fill:currentColor"
|
||||
d="m9.6094 13.32c-.68385 0-1.25.56615-1.25 1.25v.12891c0 .68385.56615 1.25 1.25 1.25h4.791c.68385 0 1.25-.56615 1.25-1.25v-.12891c0-.68385-.56615-1.25-1.25-1.25zm0 1h4.791c.14715 0 .25.10285.25.25v.12891c0 .14715-.10285.25-.25.25h-4.791c-.14715 0-.25-.10285-.25-.25v-.12891c0-.14715.10285-.25.25-.25z"/>
|
||||
<g stroke-linecap="round" stroke-linejoin="round" class="ColorScheme-Text" style="fill:currentColor">
|
||||
<path d="m4.9707 10.02a.75008.75008 0 00-.75.75v9.0996c0 1.0232.84592 1.8711 1.8691 1.8711h11.91c1.0232 0 1.8691-.84787 1.8691-1.8711v-9.0996a.75008.75008 0 00-.75-.75zm.75 1.5h12.648v8.3496c0 .21677-.15237.37109-.36914.37109h-11.91c-.21677 0-.36914-.15432-.36914-.37109z"/>
|
||||
<path d="m7.5098 7.1602a.75008.75008 0 00-.56055.25195l-2.5391 2.8594a.75008.75008 0 00.56055 1.248h14.148a.75008.75008 0 00.5625-1.248l-2.541-2.8594a.75008.75008 0 00-.56055-.25195zm.33594 1.5h8.3965l1.207 1.3594h-10.811z"/>
|
||||
<path d="m15.746 3.6992-7.4453.041016c-.57783 0-1.1067.42379-1.209 1.0117a.75008.75008 0 00-.00781.046875l-.20703 1.9082-2.3945-1.375c-.37855-.23296-.83629-.23296-1.2148 0a.75008.75008 0 00-.00781.00391l-2.7324 1.7246c-.00054767.000337.00054683.00162 0 .00195-.71708.44295-.71521 1.5381.005859 1.9707a.75008.75008 0 00.003906.00195l4.0313 2.3906a.75.75 0 001.0273-.26367.75.75 0 00-.26367-1.0273l-3.5391-2.0996 2.0898-1.3184 3.2441 1.8633a.75008.75008 0 001.1191-.57031l.30078-2.7715 6.9668-.037109.31055 2.8105a.75008.75008 0 001.127.56445l3.1641-1.8613 2.0918 1.3145-3.4746 2.0996a.75.75 0 00-.25391 1.0293.75.75 0 001.0293.25391l3.9336-2.377c.72984-.4325.72984-1.5675 0-2l-2.7031-1.6992a.75008.75008 0 00-.0059-.00391c-.35475-.21833-.84467-.24519-1.2382.0078162l-2.3047 1.3555-.21484-1.9277a.75008.75008 0 00-.0059-.046875c-.10095-.5807-.61862-1.0458-1.25-1.0195z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.0 KiB |
@@ -0,0 +1,14 @@
|
||||
<svg
|
||||
version="1.1"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 24 24">
|
||||
<defs>
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text { color: #232629; }
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
class="ColorScheme-Text"
|
||||
style="fill:currentColor"
|
||||
d="m 19.854756,20.481011 c -1.262396,-0.531251 -2.820748,-1.029352 -4.628787,-1.322602 -0.496751,-0.08075 -0.849454,-0.542301 -0.763897,-1.025102 0.05151,-0.28815 0.0777,-0.5899 0.0777,-0.905251 0,-1.996653 -1.097395,-3.618455 -2.445348,-3.618455 -1.357556,0 -2.4549513,1.621802 -2.4549513,3.618455 0,0.30005 0.025317,0.594151 0.074207,0.875501 0.082937,0.485351 -0.2697652,0.948602 -0.7691364,1.025102 -1.8831212,0.28815 -3.5008395,0.811751 -4.8121254,1.361702 -0.7988193,0.1122 -1.4439859,-0.777751 -0.9725516,-1.490052 0.1152394,-0.169151 0.231352,-0.346801 0.3369882,-0.525301 0.2793686,-0.430951 0.5587371,-0.862751 0.8276293,-1.312402 1.2327135,-1.977953 2.464554,-4.125056 3.6588544,-6.431109 0.028809,-0.0561 0.05762,-0.122401 0.086429,-0.178501 C 8.3203287,10.075296 8.560411,9.5967448 8.7917631,9.1190441 8.9070025,8.8937938 9.0231151,8.6591935 9.1287513,8.4339431 9.3120869,8.5461433 9.4945495,8.6770435 9.706695,8.8087937 10.669644,9.3901945 11.574972,9.718295 12.229743,9.8865952 11.748705,9.5304447 11.218778,9.1088441 10.679247,8.5835434 10.274162,8.1899928 9.9179674,7.7964422 9.6097889,7.4122417 10.062017,6.4372903 10.495912,5.4810389 10.880916,4.5528376 c 0.06722,-0.1496002 0.134447,-0.3000505 0.192939,-0.4403007 0.336988,-0.8151511 1.502479,-0.8151511 1.84907,-0.00935 0.163257,0.3842006 0.327385,0.7777511 0.501118,1.1721517 0.144049,0.3468005 0.308178,0.693601 0.471434,1.0497515 0.105636,0.2431004 0.221748,0.4964007 0.336989,0.7497011 0.231352,0.5066007 0.471434,1.0123514 0.721992,1.5283022 0.231352,0.4972507 0.481038,0.9936514 0.7412,1.4994026 0,0.0187 0.0096,0.02805 0.01921,0.04675 0.616356,1.228251 1.25192,2.399553 1.897086,3.534305 0.461831,0.833851 0.934139,1.640502 1.415176,2.419103 0.164128,0.290701 0.336989,0.572051 0.520325,0.853402 0.124842,0.1972 0.240954,0.39355 0.365797,0.5814 h -0.01048 c -0.336988,-0.17765 -0.702787,-0.3468 -1.097395,-0.49725 -0.683579,-0.262651 -1.319143,-0.421601 -1.868276,-0.515951 0.750802,0.365501 1.617718,0.844051 2.523047,1.481552 0.558736,0.402901 1.05898,0.796451 1.482399,1.190852 0.250558,0.674901 -0.356196,1.405902 -1.087792,1.284352 z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -0,0 +1 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23"><path d=""/></svg>
|
||||
|
After Width: | Height: | Size: 92 B |
@@ -0,0 +1,3 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m11.5,1.63C5.99,1.63,1.5,6.11,1.5,11.63s4.49,10,10,10,10-4.49,10-10S17.01,1.63,11.5,1.63Zm-7.26,10c0-4,3.26-7.26,7.26-7.26,1.56,0,3,.5,4.19,1.34L5.56,15.78c-.83-1.18-1.32-2.61-1.32-4.16Zm7.26,7.26c-1.46,0-2.83-.44-3.97-1.19l10.06-10c.74,1.13,1.17,2.48,1.17,3.93,0,4-3.26,7.26-7.26,7.26Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 384 B |
@@ -0,0 +1,3 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m22.43,12.56s-.05-.04-.07-.07c-.04-.04-.07-.08-.12-.11,0,0,0,0,0,0,0,0,0,0,0,0l-4.97-2.6v-5.2c0-.09-.03-.17-.07-.24-.02-.03-.05-.04-.07-.07-.04-.04-.07-.08-.12-.11,0,0,0,0,0,0,0,0,0,0,0,0l-4.75-2.48c-.47-.24-1.04-.24-1.5,0l-4.75,2.48s0,0,0,0c0,0,0,0,0,0-.05.03-.08.07-.12.11-.02.02-.06.04-.07.07-.05.07-.07.15-.07.24v5.2L.76,12.38s0,0,0,0c0,0,0,0,0,0-.06.03-.09.08-.13.12-.02.02-.04.03-.06.05-.05.07-.07.15-.07.24v5.48c0,.17.1.33.26.42l5.24,2.74c.08.04.17.06.26.06s.18-.02.26-.06l4.98-2.6,4.98,2.6c.08.04.17.06.26.06s.18-.02.26-.06l5.24-2.74c.16-.09.26-.24.26-.42v-5.48c0-.09-.03-.17-.07-.24Zm-16.9,2.05l-2.7-1.41c-.34-.18-.34-.62,0-.79l2.7-1.41c.45-.24,1-.24,1.45,0l2.7,1.41c.34.18.34.62,0,.79l-2.7,1.41c-.45.24-1,.24-1.45,0Zm5.44,2.75c0,.4-.23.76-.61.96l-2.75,1.44c-.37.19-.83-.05-.83-.44v-2.86c0-.4.23-.76.61-.96l2.75-1.44c.37-.19.83.05.83.44v2.86Zm-.2-10.97l-2.7-1.41c-.34-.18-.34-.62,0-.79l2.7-1.41c.45-.24,1-.24,1.45,0l2.7,1.41c.34.18.34.62,0,.79l-2.7,1.41c-.45.24-1,.24-1.45,0Zm1.25,4.71v-2.86c0-.4.23-.76.61-.96l2.75-1.44c.37-.19.83.05.83.44v2.86c0,.4-.23.76-.61.96l-2.75,1.44c-.37.19-.83-.05-.83-.44Zm3.99,3.51l-2.7-1.41c-.34-.18-.34-.62,0-.79l2.7-1.41c.45-.24,1-.24,1.45,0l2.7,1.41c.34.18.34.62,0,.79l-2.7,1.41c-.45.24-1,.24-1.45,0Zm5.44,2.75c0,.4-.23.76-.61.96l-2.75,1.44c-.37.19-.83-.05-.83-.44v-2.86c0-.4.23-.76.61-.96l2.75-1.44c.37-.19.83.05.83.44v2.86Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m18.18,3.3h-3.48c-.43-1.1-1.53-1.88-2.81-1.88s-2.37.78-2.81,1.88h-3.36c-1.53,0-2.77,1.2-2.77,2.68v12.75c0,1.48,1.24,2.68,2.77,2.68h12.46c1.53,0,2.77-1.2,2.77-2.68V5.98c0-1.48-1.24-2.68-2.77-2.68Zm-6.29-.35c.59,0,1.07.47,1.07,1.04s-.48,1.04-1.07,1.04-1.07-.47-1.07-1.04.48-1.04,1.07-1.04Zm-.27,14.89h-4.95c-.54,0-.98-.43-.98-.95s.44-.95.98-.95h4.95c.54,0,.98.43.98.95s-.44.95-.98.95Zm3.22-4.36H6.68c-.54,0-.98-.43-.98-.95s.44-.95.98-.95h8.17c.54,0,.98.43.98.95s-.44.95-.98.95Zm2.56-4.36H6.68c-.54,0-.98-.43-.98-.95s.44-.95.98-.95h10.73c.54,0,.98.43.98.95s-.44.95-.98.95Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 667 B |
@@ -0,0 +1,3 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m21.5,6.21c0-.06-.04-.11-.06-.18-.02-.09-.04-.19-.09-.27-.04-.06-.11-.1-.16-.15-.06-.06-.11-.13-.19-.17,0,0-.02,0-.03,0,0,0-.01-.01-.02-.02l-7.71-3.55c-1.1-.51-2.39-.51-3.49,0l-7.71,3.55s-.01.01-.02.02c0,0-.02,0-.03,0-.08.04-.12.11-.19.17-.05.05-.12.09-.16.15-.05.08-.07.17-.09.27-.02.06-.06.11-.06.18,0,0,0,0,0,.01,0,0,0,0,0,.01v9.57c0,.32.19.62.49.78l9.01,4.79c.15.08.32.12.49.12s.34-.04.49-.12l8.5-4.51c.62-.33,1.01-.94,1.01-1.6V6.24s0,0,0-.01c0,0,0,0,0-.01Zm-16.07-.32l5.2-2.4c.55-.25,1.19-.25,1.73,0l5.2,2.4c.37.17.39.66.03.85l-5.13,2.72c-.6.32-1.33.32-1.93,0l-5.13-2.72c-.36-.19-.35-.68.03-.85Zm14.1,8.5c0,.55-.32,1.06-.84,1.33l-5.44,2.88c-.34.18-.77-.05-.77-.41v-5.77c0-.55.32-1.05.83-1.33l3.24-1.72,2.2-1.16c.34-.18.77.05.77.41v5.77Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 839 B |
@@ -0,0 +1,4 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<polyline fill="none" stroke="#000" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.13px" points="15.35 15.15 11.68 12.23 11.68 7.15"/>
|
||||
<path d="m22.5,11.61c0-.33-.02-.65-.05-.97,0-.01,0-.02,0-.03-.03-.31-.08-.61-.14-.92,0-.03,0-.05-.01-.08-.06-.27-.13-.54-.21-.8-.02-.06-.03-.11-.05-.17-.08-.24-.17-.48-.26-.72-.03-.07-.05-.13-.08-.2-.02-.04-.03-.08-.05-.12-.1-.23-.22-.46-.34-.68,0-.02-.02-.03-.02-.05-1.06-1.97-2.72-3.5-4.78-4.4-.05-.02-.09-.04-.14-.06-.07-.03-.14-.07-.22-.1-.19-.07-.37-.13-.56-.19-.04-.01-.08-.03-.13-.04-3.14-.98-6.44-.33-8.92,1.57-.12.09-.23.19-.34.29-.11.09-.22.17-.32.27-1.69,1.53-2.84,3.65-3.17,6.08h-1.34c-.78,0-1.17.96-.62,1.51l2.85,2.85c.33.33.87.33,1.21,0l2.83-2.86c.55-.56.16-1.51-.62-1.5h-1.54c.12-.64.32-1.25.6-1.82,0-.02.02-.03.03-.04.41-.83.96-1.55,1.61-2.15.15-.14.31-.27.47-.4,0,0,0,0,0,0,1.21-.92,2.73-1.47,4.37-1.47.74,0,1.46.11,2.13.32.01,0,.03,0,.04.01.21.07.41.14.61.22.03.01.05.02.07.03,1.49.65,2.71,1.78,3.48,3.2,0,0,0,0,0,.01.11.2.2.4.29.61.02.04.03.09.05.13.07.18.14.35.2.54.01.04.02.08.03.11.06.19.11.39.15.59,0,.02,0,.04,0,.06.04.22.08.44.1.67,0,0,0,.02,0,.03.02.23.03.46.04.7,0,.03,0,.05,0,.08,0,.21-.01.42-.03.62,0,.07-.02.14-.03.21-.02.16-.04.31-.07.47-.02.11-.05.23-.08.34-.03.11-.05.22-.08.33-.04.14-.09.28-.14.41-.03.08-.06.17-.09.25-.06.15-.12.29-.19.43-.04.08-.07.16-.12.24-.06.12-.13.24-.2.35-.05.09-.11.18-.17.27-.06.1-.14.19-.2.29-.07.1-.14.19-.21.29-.07.09-.15.18-.23.26-.08.08-.15.17-.23.25-.1.11-.21.2-.32.3-.06.05-.11.11-.17.16-.16.14-.33.27-.5.39,0,0-.02.01-.03.02-1.13.78-2.46,1.23-3.84,1.29-.03,0-.05,0-.08,0-.19,0-.38,0-.58-.01-.05,0-.11,0-.16,0-.2-.02-.39-.04-.59-.07-.04,0-.07-.01-.11-.02-.21-.04-.42-.08-.63-.14-.02,0-.04-.01-.06-.02-.22-.06-.43-.13-.65-.22-.02,0-.03-.01-.05-.02-.96-.38-1.81-.94-2.52-1.68-.53-.54-1.39-.55-1.93-.02-.54.53-.55,1.4-.02,1.94.87.89,1.88,1.59,3.01,2.1v.02c.13.06.28.11.42.17.03.01.06.03.09.04.03.01.06.02.09.03.24.09.48.17.72.24.07.02.13.04.2.06.24.06.48.12.72.17.08.01.16.02.23.04.23.04.47.08.71.1.06,0,.11,0,.17,0,.25.02.5.04.75.04.16,0,.31-.02.47-.02.11,0,.21,0,.32-.02.2-.02.39-.04.58-.07.07,0,.14-.02.21-.03.93-.15,1.81-.43,2.63-.82.05-.02.1-.05.14-.07.19-.09.37-.19.55-.29.06-.03.11-.07.17-.1.25-.15.49-.31.73-.48.14-.1.29-.21.42-.32.08-.07.16-.13.24-.2.16-.14.31-.28.46-.42.06-.05.11-.11.17-.17.19-.2.38-.4.55-.61,0-.01.02-.02.03-.03.2-.24.38-.49.56-.75.01-.02.02-.04.03-.05.15-.22.29-.46.42-.69.04-.08.08-.15.12-.23.09-.18.18-.37.27-.56.04-.1.09-.2.13-.3.02-.05.04-.1.06-.15.04-.1.06-.2.1-.3.06-.17.12-.33.17-.5.05-.17.09-.34.13-.51.03-.14.07-.27.09-.41.04-.23.08-.45.1-.68.01-.09.02-.17.03-.26.03-.29.04-.59.04-.88,0-.02,0-.05,0-.07,0,0,0-.01,0-.02Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.7 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m11.5,1.5C5.98,1.5,1.5,5.98,1.5,11.5s4.48,10,10,10,10-4.48,10-10S17.02,1.5,11.5,1.5Zm6.45,6.42l-6.94,8.96c-.34.44-.86.73-1.42.77-.06,0-.11,0-.17,0-.5,0-.99-.19-1.36-.53l-2.84-2.61c-.58-.53-.62-1.43-.09-2,.53-.58,1.43-.61,2-.09l2.19,2.01,6.39-8.25c.48-.62,1.37-.73,1.99-.25.62.48.73,1.37.25,1.99Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 393 B |
@@ -0,0 +1,6 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m14.49,15.49c-.46,0-.92-.2-1.24-.6-.58-.72-.5-1.8.18-2.41l3.92-3.5-3.93-3.55c-.68-.62-.76-1.7-.17-2.41.59-.71,1.62-.8,2.3-.18l5.38,4.85c.36.33.57.8.57,1.3,0,.5-.21.97-.57,1.3l-5.38,4.8c-.31.27-.68.41-1.06.41Z"/>
|
||||
<ellipse cx="7.09" cy="4.35" rx="1.51" ry="1.58"/>
|
||||
<ellipse cx="3.68" cy="11.77" rx="2.18" ry="2.28"/>
|
||||
<ellipse cx="8.48" cy="19.22" rx="3.04" ry="3.19"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 473 B |
@@ -0,0 +1,15 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<defs>
|
||||
<style>
|
||||
.cls-1 { fill: none; stroke: #000; stroke-linecap: round; stroke-linejoin: round; stroke-width: 2.13px; }
|
||||
</style>
|
||||
</defs>
|
||||
<ellipse cx="2.48" cy="5.61" rx="1.53" ry="1.5"/>
|
||||
<line class="cls-1" x1="8" y1="5.61" x2="20.94" y2="5.61"/>
|
||||
<ellipse cx="2.48" cy="9.61" rx="1.53" ry="1.5"/>
|
||||
<line class="cls-1" x1="8" y1="9.61" x2="20.94" y2="9.61"/>
|
||||
<ellipse cx="2.48" cy="13.61" rx="1.53" ry="1.5"/>
|
||||
<line class="cls-1" x1="8" y1="13.61" x2="20.94" y2="13.61"/>
|
||||
<ellipse cx="2.48" cy="17.61" rx="1.53" ry="1.5"/>
|
||||
<line class="cls-1" x1="8" y1="17.61" x2="20.94" y2="17.61"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 733 B |
@@ -0,0 +1,5 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m1.5,2.42v3.68c0,.45.32.82.71.82h18.58c.39,0,.71-.36.71-.82v-3.68c0-.45-.32-.82-.71-.82H2.21c-.39,0-.71.36-.71.82Zm2.14,3.07c-.63,0-1.14-.64-1.14-1.42s.51-1.42,1.14-1.42,1.14.64,1.14,1.42-.51,1.42-1.14,1.42Zm16.11-.43H7.53c-.39,0-.71-.36-.71-.82v-.34c0-.45.32-.82.71-.82h12.21c.39,0,.71.36.71.82v.34c0,.45-.32.82-.71.82Z"/>
|
||||
<path d="m1.5,9.77v3.68c0,.45.32.82.71.82h18.58c.39,0,.71-.36.71-.82v-3.68c0-.45-.32-.82-.71-.82H2.21c-.39,0-.71.36-.71.82Zm2.14,3.07c-.63,0-1.14-.64-1.14-1.42s.51-1.42,1.14-1.42,1.14.64,1.14,1.42-.51,1.42-1.14,1.42Zm16.11-.43H7.53c-.39,0-.71-.36-.71-.82v-.34c0-.45.32-.82.71-.82h12.21c.39,0,.71.36.71.82v.34c0,.45-.32.82-.71.82Z"/>
|
||||
<path d="m1.5,17.11v3.68c0,.45.32.82.71.82h18.58c.39,0,.71-.36.71-.82v-3.68c0-.45-.32-.82-.71-.82H2.21c-.39,0-.71.36-.71.82Zm2.14,3.07c-.63,0-1.14-.64-1.14-1.42s.51-1.42,1.14-1.42,1.14.64,1.14,1.42-.51,1.42-1.14,1.42Zm16.11-.43H7.53c-.39,0-.71-.36-.71-.82v-.34c0-.45.32-.82.71-.82h12.21c.39,0,.71.36.71.82v.34c0,.45-.32.82-.71.82Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
@@ -0,0 +1,4 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m20.02,13.39c-.59,0-.93.29-1.07.68-.95,2.82-3.56,4.52-6.64,4.52-1.67,0-3.26-.58-4.52-1.64-.19-.16-.19-.44-.01-.61l1.5-1.46c.53-.52.16-1.42-.58-1.42l-5.62.1c-.55.01-1,.46-1,1.01v5.37c.18.8,1.1.88,1.54.45l1.39-1.36c.16-.15.4-.15.56,0,1.86,1.66,4.23,2.58,6.74,2.58,5.25,0,8.46-3.36,9.71-7.7.08-.26-.13-.53-.41-.53h-1.6Z"/>
|
||||
<path d="m4.07,9.82c.59,0,.93-.29,1.07-.68.95-2.82,3.56-4.52,6.64-4.52,1.68,0,3.26.58,4.53,1.64.19.15.19.44.01.61l-1.5,1.46c-.53.52-.16,1.42.58,1.42l5.62-.1c.55-.01.99-.46.99-1.01V3.28c-.18-.8-1.1-.88-1.54-.45l-1.39,1.36c-.16.15-.4.15-.56,0-1.86-1.67-4.23-2.58-6.75-2.58-5.25,0-8.46,3.37-9.71,7.71-.08.26.13.53.41.53h1.6Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 743 B |
@@ -0,0 +1,5 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m9.72,11.51h13.28v-3.68c0-1.31-1.04-2.37-2.33-2.37h-4.1v-2.09c0-1.09-.88-1.98-1.95-1.98h-6.72c-1.08,0-1.95.89-1.95,1.98v2.09h-3.6c-1.29,0-2.33,1.06-2.33,2.37v3.68h9.72Zm-1.66-7.97h6.37v1.91h-6.37v-1.91Z"/>
|
||||
<path d="m9.72,12.93h3.71c.16,0,.29.13.29.29v.67c0,.16-.13.29-.29.29h-3.71c-.16,0-.29-.13-.29-.29v-.67c0-.16.13-.29.29-.29Z"/>
|
||||
<path d="m15.12,12.93c.02.1.03.19.03.29v.67c0,.94-.77,1.71-1.71,1.71h-3.71c-.94,0-1.71-.77-1.71-1.71v-.67c0-.1.01-.2.03-.29H0v6.08c0,1.31,1.04,2.37,2.33,2.37h18.33c1.29,0,2.33-1.06,2.33-2.37v-6.08h-7.88Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 642 B |
@@ -0,0 +1,3 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m11.5,1.51C5.98,1.51,1.5,5.99,1.5,11.51s4.48,10,10,10,10-4.48,10-10S17.02,1.51,11.5,1.51Zm-1.48,13.45c0,.88-.62,1.59-1.4,1.59s-1.4-.71-1.4-1.59v-6.91c0-.88.62-1.59,1.4-1.59s1.4.71,1.4,1.59v6.91Zm5.75,0c0,.88-.62,1.59-1.4,1.59s-1.4-.71-1.4-1.59v-6.91c0-.88.62-1.59,1.4-1.59s1.4.71,1.4,1.59v6.91Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 392 B |
@@ -0,0 +1,12 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="-1 -1 18 18">
|
||||
<defs>
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text { color: #000000; }
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
class="ColorScheme-Text"
|
||||
style="fill:currentColor"
|
||||
d="m 4.0214839,5.02e-5 a 0.9947634,1.0001 0 1 0 0,2 h 0.978516 v 3 c 0,1.328 -1.695715,2 -2.9999998,2 v 2 H 6.9999999 V 15.00005 l 0.976562,1 1.023438,-1 V 9.0000502 H 14 v -2 c -1.304286,0 -3.055749,-0.673171 -3,-2 v -3 h 0.978516 a 0.9947634,1.0001 0 1 0 0,-2 z"
|
||||
/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 567 B |
@@ -0,0 +1,3 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m20.88,17.92l-3.29-3.28c.85-1.32,1.35-2.88,1.35-4.56,0-4.73-3.91-8.57-8.72-8.57S1.5,5.35,1.5,10.08s3.91,8.57,8.72,8.57c1.55,0,3-.4,4.26-1.1.01.01.02.03.03.04l3.31,3.3c.41.4.95.63,1.53.63.55,0,1.07-.2,1.47-.57.43-.39.67-.92.68-1.5.01-.57-.21-1.11-.62-1.52ZM3.98,10.08c0-3.36,2.8-6.09,6.24-6.09s6.24,2.73,6.24,6.09-2.8,6.09-6.24,6.09-6.24-2.73-6.24-6.09Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 450 B |
@@ -0,0 +1,4 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="2 2 18 18">
|
||||
<path d="m9.2502 3.0001c-0.51888 1.549e-4 -0.95146 0.39711-0.99609 0.91406l-0.11719 1.3398c-0.23504 0.11806-0.46067 0.24888-0.67969 0.39453l-1.2207-0.57422c-0.47103-0.22177-1.0334-0.045069-1.293 0.40625l-1.75 3.0293c-0.25943 0.44992-0.13129 1.0235 0.29492 1.3203l1.0488 0.73242c-0.010017 0.14609-0.036985 0.29094-0.037109 0.4375 1.241e-4 0.14591 0.02718 0.2901 0.037109 0.43555l-1.0449 0.7207c-0.42981 0.29586-0.55992 0.87245-0.29883 1.3242l1.75 3.0293c0.2603 0.45166 0.82388 0.62761 1.2949 0.4043l1.2109-0.57422c0.22158 0.14673 0.44964 0.27991 0.6875 0.39844l0.11719 1.3477c0.044639 0.51695 0.47722 0.91391 0.99609 0.91406h3.5c0.52095-4.1e-4 0.95433-0.40065 0.99609-0.91992l0.10938-1.3438c0.23718-0.11834 0.46457-0.25009 0.68555-0.39648l1.2227 0.57422c0.47054 0.2221 1.0328 0.0463 1.293-0.4043l1.75-3.0293c0.26026-0.45085 0.13114-1.0259-0.29688-1.3223l-1.0508-0.72852c0.01082-0.13896 0.03915-0.27653 0.04102-0.41602 3.1e-5 -0.0046 3.1e-5 -0.0091 0-0.01367 1.45e-4 -0.14944-0.02697-0.29635-0.03711-0.44531l1.0449-0.72266c0.4287-0.29572 0.5587-0.87093 0.29883-1.3223l-1.75-3.0293c-0.25955-0.45132-0.82194-0.62802-1.293-0.40625l-1.2227 0.57617c-0.22098-0.14639-0.44836-0.27814-0.68555-0.39648l-0.10938-1.3359c-0.04273-0.5185-0.47584-0.91764-0.99609-0.91797zm0.91602 2h1.6621l0.07422 0.92188c0.03216 0.3873 0.28579 0.72091 0.65039 0.85547 0.4791 0.17704 0.92563 0.43516 1.3184 0.76172 0.29825 0.24821 0.71316 0.3015 1.0645 0.13672l0.84766-0.40039 0.83008 1.4355-0.76172 0.52539c-0.32136 0.22144-0.48455 0.60959-0.41797 0.99414 0.04336 0.25146 0.06621 0.50656 0.06641 0.76172-0.0037 0.25058-0.02895 0.50156-0.07422 0.74805-0.07068 0.38631 0.09129 0.77824 0.41406 1.002l0.77344 0.53516-0.83008 1.4375-0.84766-0.40039c-0.3513-0.16478-0.7662-0.11149-1.0645 0.13672-0.39273 0.32656-0.83926 0.58468-1.3184 0.76172-0.3646 0.13456-0.61823 0.46816-0.65039 0.85547l-0.07422 0.93164h-1.6602l-0.08203-0.93555c-0.03328-0.38536-0.2858-0.71699-0.64844-0.85156-0.4791-0.17704-0.92563-0.43516-1.3184-0.76172-0.29906-0.24836-0.71496-0.30092-1.0664-0.13477l-0.83789 0.39648-0.82812-1.4336 0.76172-0.52734c0.32064-0.22112 0.48374-0.60829 0.41797-0.99219-0.043342-0.25132-0.06619-0.50473-0.066407-0.75977 2.169e-4 -0.25503 0.023066-0.50844 0.066406-0.75977 0.065792-0.38246-0.095608-0.76845-0.41406-0.99023l-0.76758-0.53516 0.83203-1.4395 0.84766 0.40039c0.35219 0.16508 0.76815 0.11099 1.0664-0.13867 0.39026-0.32576 0.83238-0.58189 1.3086-0.75977 0.36187-0.13518 0.61353-0.46668 0.64648-0.85156z"/>
|
||||
<circle cx="11" cy="11" r="2"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m9.15,17.22h-2.11V2.27c0-.42-.38-.77-.86-.77h-1.15c-.47,0-.86.34-.86.77v14.96h-2.11c-.49,0-.74.52-.4.84l3.55,3.29c.22.2.58.2.8,0l3.55-3.29c.34-.31.09-.84-.4-.84Z"/>
|
||||
<path d="m19.62,9.26l-.42-1.08h-3.54l-.42,1.1c-.16.43-.3.72-.42.87s-.3.23-.56.23c-.22,0-.42-.08-.59-.24s-.26-.34-.26-.54c0-.12.02-.24.06-.36s.1-.3.2-.52l2.23-5.58c.06-.16.14-.35.23-.58s.18-.41.28-.56.23-.27.4-.36.37-.14.61-.14.45.05.61.14.3.21.4.35.19.3.26.47.16.39.26.67l2.27,5.54c.18.42.27.73.27.92s-.08.38-.25.55-.37.25-.61.25c-.14,0-.26-.02-.36-.07s-.18-.12-.25-.2-.14-.21-.22-.39-.14-.33-.2-.46Zm-3.49-2.38h2.6l-1.31-3.54-1.29,3.54Z"/>
|
||||
<path d="m14.27,19.5l4.47-5.28h-3.84c-.27,0-.47-.06-.61-.18s-.2-.27-.2-.46.07-.36.2-.48.34-.18.61-.18h4.95c.64,0,.96.29.96.88,0,.28-.05.51-.16.67s-.32.44-.65.81l-4.18,4.91h4.61c.27,0,.48.06.61.17s.2.27.2.47-.07.37-.2.49-.34.18-.61.18h-5.65c-.38,0-.67-.08-.87-.25s-.29-.4-.29-.68c0-.1.02-.19.05-.27s.07-.16.13-.23.13-.16.22-.28.18-.21.25-.29Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
@@ -0,0 +1,3 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m11.5,1.51C5.98,1.51,1.5,5.99,1.5,11.51s4.48,10,10,10,10-4.48,10-10S17.02,1.51,11.5,1.51Zm5.17,10.81l-5.87,3.76c-1.03.66-2.37-.08-2.37-1.3v-6.54c0-1.22,1.35-1.95,2.37-1.3l5.87,3.76c.59.38.59,1.25,0,1.63Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 301 B |
@@ -0,0 +1,3 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m11.76,1.5C6.23,1.5,1.76,5.98,1.76,11.5s4.48,10,10,10,10-4.48,10-10S17.28,1.5,11.76,1.5Zm3.85,12.29c0,.86-.7,1.56-1.56,1.56h-4.58c-.86,0-1.56-.7-1.56-1.56v-4.58c0-.86.7-1.56,1.56-1.56h4.58c.86,0,1.56.7,1.56,1.56v4.58Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 315 B |
@@ -0,0 +1,15 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="-1 -1 18 18">
|
||||
<defs>
|
||||
<style type="text/css" id="current-color-scheme">
|
||||
.ColorScheme-Text { color: #000000; }
|
||||
</style>
|
||||
</defs>
|
||||
<path
|
||||
class="ColorScheme-Text"
|
||||
style="fill:currentColor"
|
||||
d="M 4.0214844,0 C 3.7305148,4.096652e-4 3.4543107,0.12887668 3.265625,0.3515625 L 11.914062,9 H 14 V 7 C 12.695714,7 10.944251,6.326829 11,5 V 2 h 0.978516 c 1.345107,0.019125 1.345107,-2.01912521 0,-2 z M 3.71875,6.6328125 C 3.1820953,6.8759067 2.5561157,7 2,7 V 9 H 6.0859375 Z M 7,9.9140625 V 15 L 7.9765625,16 9,15 v -3.085938 z"/>
|
||||
<path
|
||||
class="ColorScheme-Text"
|
||||
style="fill:currentColor"
|
||||
d="M -1e-7,9.9999999e-8 V 1.5000001 L 14.5,16 H 16 V 14.5 L 1.4999999,9.9999999e-8 Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 805 B |
@@ -0,0 +1,3 @@
|
||||
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 23 23">
|
||||
<path d="m20.33,1.42H2.89C1.63,1.42.61,2.47.61,3.77v15.31c0,1.29,1.02,2.34,2.28,2.34h17.44c1.26,0,2.28-1.05,2.28-2.34V3.77c0-1.29-1.02-2.34-2.28-2.34Zm-9.99,10.67l-3.1,3.29c-.21.22-.48.33-.76.33-.26,0-.53-.1-.73-.3-.42-.42-.43-1.1-.02-1.53l2.38-2.53-2.35-2.45c-.41-.43-.4-1.11.01-1.53.41-.42,1.08-.41,1.49.01l3.07,3.2c.4.42.4,1.09,0,1.51Zm6.5,3.62h-4.07c-.58,0-1.05-.48-1.05-1.08s.47-1.08,1.05-1.08h4.07c.58,0,1.05.48,1.05,1.08s-.47,1.08-1.05,1.08Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 537 B |
@@ -0,0 +1,52 @@
|
||||
import QtQuick
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
Rectangle {
|
||||
id: badge
|
||||
|
||||
property var iconName: ""
|
||||
property var iconColor: ""
|
||||
property var position: ""
|
||||
|
||||
width: Math.min(parent.width, parent.height) / 3
|
||||
height: width
|
||||
radius: width / 2
|
||||
color: cfg.counterColor ? cfg.counterColor : Kirigami.Theme.backgroundColor
|
||||
|
||||
anchors {
|
||||
topMargin: state.includes("top") ? cfg.badgeOffsetX : 0
|
||||
leftMargin: state.includes("Left") ? cfg.badgeOffsetY : 0
|
||||
rightMargin: state.includes("Right") ? cfg.badgeOffsetY : 0
|
||||
bottomMargin: state.includes("bottom") ? cfg.badgeOffsetX : 0
|
||||
}
|
||||
|
||||
state: position || "topRight"
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "topLeft"
|
||||
AnchorChanges { target: badge; anchors.top: parent.top; anchors.left: parent.left }
|
||||
},
|
||||
State {
|
||||
name: "topRight"
|
||||
AnchorChanges { target: badge; anchors.top: parent.top; anchors.right: parent.right }
|
||||
},
|
||||
State {
|
||||
name: "bottomLeft"
|
||||
AnchorChanges { target: badge; anchors.bottom: parent.bottom; anchors.left: parent.left }
|
||||
},
|
||||
State {
|
||||
name: "bottomRight"
|
||||
AnchorChanges { target: badge; anchors.bottom: parent.bottom; anchors.right: parent.right }
|
||||
}
|
||||
]
|
||||
|
||||
transitions: Transition { AnchorAnimation { duration: 120 } }
|
||||
|
||||
Kirigami.Icon {
|
||||
anchors.fill: parent
|
||||
source: cfg.ownIconsUI ? Qt.resolvedUrl("../assets/icons/" + iconName + ".svg") : iconName
|
||||
color: iconColor
|
||||
isMask: cfg.ownIconsUI
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import "../../tools/tools.js" as JS
|
||||
|
||||
RowLayout {
|
||||
property alias labelText: label.text
|
||||
|
||||
ComboBox {
|
||||
id: comboBox
|
||||
implicitWidth: 250
|
||||
textRole: "name"
|
||||
model: [
|
||||
{"name": i18n("None"), "value": ""},
|
||||
{"name": i18n("Check updates"), "value": "checkUpdates"},
|
||||
{"name": i18n("Upgrade system"), "value": "upgradeSystem"},
|
||||
{"name": i18n("Pause auto check"), "value": "switchScheduler"},
|
||||
{"name": i18n("Management"), "value": "management"}
|
||||
]
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
var action = model[currentIndex]["value"]
|
||||
switch (type) {
|
||||
case "middle": cfg_middleAction = action; break;
|
||||
case "right": cfg_rightAction = action; break;
|
||||
case "scrollUp": cfg_scrollUpAction = action; break;
|
||||
case "scrollDown": cfg_scrollDownAction = action; break;
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
currentIndex = JS.setIndex(plasmoid.configuration[type + "Action"], model)
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: label
|
||||
text: ""
|
||||
}
|
||||
|
||||
property string type: ""
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
import QtQuick
|
||||
import org.kde.notification
|
||||
import "../../tools/tools.js" as JS
|
||||
|
||||
Item {
|
||||
Component {
|
||||
id: notifyComponent
|
||||
Notification {
|
||||
componentName: "apdatifier"
|
||||
flags: cfg.notifyPersistent ? Notification.Persistent : Notification.CloseOnTimeout
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: upgradeAction
|
||||
NotificationAction {
|
||||
label: i18n("Upgrade system")
|
||||
onActivated: JS.upgradeSystem()
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: openArticleAction
|
||||
NotificationAction {
|
||||
property string link
|
||||
label: i18n("Read article")
|
||||
onActivated: Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
|
||||
function send(event, title, body, link) {
|
||||
const params = {
|
||||
updates: {
|
||||
icon: "apdatifier-packages",
|
||||
urgency: "DefaultUrgency"
|
||||
},
|
||||
news: {
|
||||
icon: "news-subscribe",
|
||||
urgency: "HighUrgency"
|
||||
},
|
||||
error: {
|
||||
icon: "error",
|
||||
urgency: "CriticalUrgency"
|
||||
}
|
||||
}
|
||||
|
||||
const { icon, urgency } = params[event]
|
||||
|
||||
const action = (event === "updates" && cfg.notifyUpdatesAction) ? upgradeAction.createObject(root)
|
||||
: (event === "news" && cfg.notifyNewsAction) ? openArticleAction.createObject(root, { link: link })
|
||||
: []
|
||||
|
||||
if (cfg.notifySound) event += "Sound"
|
||||
|
||||
notifyComponent.createObject(root, {
|
||||
eventId: event,
|
||||
iconName: icon,
|
||||
title: title,
|
||||
text: body,
|
||||
actions: action,
|
||||
urgency: urgency
|
||||
})?.sendEvent()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
import QtQuick
|
||||
|
||||
Item {
|
||||
id: root
|
||||
|
||||
required property Item target
|
||||
required property bool running
|
||||
|
||||
property real scaleTo: 1.20
|
||||
property real opacityTo: 0.60
|
||||
property int duration: 400
|
||||
|
||||
onRunningChanged: {
|
||||
if (!running && target) {
|
||||
target.scale = 1
|
||||
target.opacity = 1
|
||||
}
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: scaleAnimation
|
||||
running: root.running
|
||||
loops: Animation.Infinite
|
||||
|
||||
NumberAnimation {
|
||||
target: root.target
|
||||
property: "scale"
|
||||
to: root.scaleTo
|
||||
duration: root.duration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
target: root.target
|
||||
property: "scale"
|
||||
to: 1.00
|
||||
duration: root.duration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: opacityAnimation
|
||||
running: root.running
|
||||
loops: Animation.Infinite
|
||||
|
||||
NumberAnimation {
|
||||
target: root.target
|
||||
property: "opacity"
|
||||
to: root.opacityTo
|
||||
duration: root.duration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
NumberAnimation {
|
||||
target: root.target
|
||||
property: "opacity"
|
||||
to: 1.00
|
||||
duration: root.duration
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls.Basic
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
ScrollBar {
|
||||
id: root
|
||||
|
||||
property bool showByTimer: false
|
||||
|
||||
visible: root.visualSize < 1.0
|
||||
|
||||
Timer {
|
||||
id: hideTimer
|
||||
interval: 1200
|
||||
running: false
|
||||
repeat: false
|
||||
onTriggered: root.showByTimer = false
|
||||
}
|
||||
|
||||
onActiveChanged: {
|
||||
if (active) {
|
||||
showByTimer = true
|
||||
hideTimer.restart()
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onHoveredChanged: scroll.opacity = hovered ? 1 : 0.5
|
||||
}
|
||||
|
||||
opacity: (!cfg.autoHideScrollBar || root.active || root.hovered || root.showByTimer) ? 0.8 : 0
|
||||
Behavior on opacity { NumberAnimation { duration: 300 } }
|
||||
|
||||
contentItem: Rectangle {
|
||||
id: scroll
|
||||
implicitWidth: 5
|
||||
radius: width / 2
|
||||
color: Kirigami.Theme.highlightColor
|
||||
opacity: 0.5
|
||||
border.width: 1
|
||||
border.color: Qt.darker(Qt.rgba(color.r, color.g, color.b, 0.8), 2)
|
||||
Behavior on opacity { NumberAnimation { duration: 300 } }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
import QtQuick
|
||||
import org.kde.plasma.plasma5support as Plasma5Support
|
||||
|
||||
Plasma5Support.DataSource {
|
||||
id: executable
|
||||
engine: "executable"
|
||||
connectedSources: []
|
||||
onNewData: (sourceName, data) => {
|
||||
var cmd = sourceName
|
||||
var out = data["stdout"]
|
||||
var err = data["stderr"]
|
||||
var code = data["exit code"]
|
||||
|
||||
exited(cmd, out, err, code)
|
||||
|
||||
const cb = listeners[cmd]
|
||||
if (cb) cb(cmd, out, err, code)
|
||||
}
|
||||
|
||||
signal exited(string cmd, string out, string err, int code)
|
||||
|
||||
property var listeners: ({})
|
||||
|
||||
function exec(cmd, callback) {
|
||||
listeners[cmd] = execCallback.bind(executable, callback)
|
||||
connectSource(cmd)
|
||||
}
|
||||
|
||||
function execCallback(callback, cmd, out, err, code) {
|
||||
cleanup()
|
||||
if (callback) callback(cmd, out, err, code)
|
||||
}
|
||||
|
||||
function cleanup() {
|
||||
for (var cmd in listeners) {
|
||||
delete listeners[cmd]
|
||||
disconnectSource(cmd)
|
||||
}
|
||||
|
||||
if (typeof sts !== 'undefined' && sts?.proc === executable) sts.proc = null
|
||||
|
||||
executable.destroy()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.components
|
||||
|
||||
ToolButton {
|
||||
property string tooltipText: ""
|
||||
property string iconSource: ""
|
||||
property color iconColor: Kirigami.Theme.colorSet
|
||||
property alias buttonTooltip: tooltip
|
||||
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
|
||||
hoverEnabled: enabled
|
||||
highlighted: enabled
|
||||
|
||||
ToolTip {
|
||||
id: tooltip
|
||||
text: tooltipText
|
||||
}
|
||||
|
||||
Kirigami.Icon {
|
||||
height: parent.height
|
||||
width: parent.height
|
||||
anchors.centerIn: parent
|
||||
source: iconSource
|
||||
color: iconColor
|
||||
scale: cfg.ownIconsUI ? 0.7 : 0.9
|
||||
isMask: cfg.ownIconsUI
|
||||
smooth: true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,819 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Dialogs
|
||||
import QtQuick.Controls
|
||||
|
||||
import org.kde.ksvg
|
||||
import org.kde.kcmutils
|
||||
import org.kde.iconthemes
|
||||
import org.kde.kquickcontrolsaddons
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
|
||||
import "../../tools/tools.js" as JS
|
||||
|
||||
SimpleKCM {
|
||||
property alias cfg_hideIconPolicy: hideIconPolicy.value
|
||||
property string cfg_selectedIcon: plasmoid.configuration.selectedIcon
|
||||
property string cfg_busyIndicator: plasmoid.configuration.busyIndicator
|
||||
|
||||
property string cfg_pauseBadgePosition: plasmoid.configuration.pauseBadgePosition
|
||||
property string cfg_updatedBadgePosition: plasmoid.configuration.updatedBadgePosition
|
||||
property alias cfg_badgeOffsetX: badgeOffsetX.value
|
||||
property alias cfg_badgeOffsetY: badgeOffsetY.value
|
||||
|
||||
property string cfg_counterMode: plasmoid.configuration.counterMode
|
||||
property string cfg_counterSidePosition: plasmoid.configuration.counterSidePosition
|
||||
property string cfg_counterBadgePosition: plasmoid.configuration.counterBadgePosition
|
||||
property alias cfg_counterOffsetX: counterOffsetX.value
|
||||
property alias cfg_counterOffsetY: counterOffsetY.value
|
||||
property string cfg_counterColor: plasmoid.configuration.counterColor
|
||||
property alias cfg_counterRadius: counterRadius.value
|
||||
property alias cfg_counterOpacity: counterOpacity.value
|
||||
property string cfg_counterFontFamily: plasmoid.configuration.counterFontFamily
|
||||
property alias cfg_counterFontBold: counterFontBold.checked
|
||||
property alias cfg_counterFontSize: counterFontSize.value
|
||||
property alias cfg_counterGapsInner: counterGapsInner.value
|
||||
property alias cfg_counterGapsOuter: counterGapsOuter.value
|
||||
|
||||
property alias cfg_ownIconsUI: ownIconsUI.checked
|
||||
property alias cfg_autoHideScrollBar: autoHideScrollBar.checked
|
||||
property int cfg_defaultTab: plasmoid.configuration.defaultTab
|
||||
property alias cfg_switchDefaultTab: switchDefaultTab.checked
|
||||
property alias cfg_sorting: sorting.checked
|
||||
property alias cfg_showStatusText: showStatusText.checked
|
||||
property alias cfg_showToolBar: showToolBar.checked
|
||||
property alias cfg_searchButton: searchButton.checked
|
||||
property alias cfg_intervalButton: intervalButton.checked
|
||||
property alias cfg_sortButton: sortButton.checked
|
||||
property alias cfg_viewButton: viewButton.checked
|
||||
property alias cfg_managementButton: managementButton.checked
|
||||
property alias cfg_upgradeButton: upgradeButton.checked
|
||||
property alias cfg_checkButton: checkButton.checked
|
||||
property alias cfg_pinButton: pinButton.checked
|
||||
property alias cfg_settingsButton: settingsButton.checked
|
||||
property alias cfg_tabBarVisible: tabBarVisible.checked
|
||||
property alias cfg_tabBarTexts: tabBarTexts.checked
|
||||
property string cfg_compactInfo: plasmoid.configuration.compactInfo || "repository"
|
||||
property alias cfg_extendedShowRepository: extendedShowRepository.checked
|
||||
property alias cfg_extendedShowInstallReason: extendedShowInstallReason.checked
|
||||
property alias cfg_highlightVersionDiff: highlightVersionDiff.checked
|
||||
property alias cfg_colorizeInstallReason: colorizeInstallReason.checked
|
||||
|
||||
readonly property bool inTray: (plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentDrawsPlasmoidHeading)
|
||||
readonly property bool onDesktop: plasmoid.location === PlasmaCore.Types.Floating
|
||||
readonly property bool horizontal: plasmoid.location === PlasmaCore.Types.TopEdge || plasmoid.location === PlasmaCore.Types.BottomEdge
|
||||
|
||||
readonly property bool allowOnlyCounterBadge: inTray || !horizontal
|
||||
|
||||
readonly property bool counterEnabled: cfg_counterMode !== "disabled"
|
||||
readonly property bool sideMode: cfg_counterMode === "side"
|
||||
readonly property bool badgeMode: cfg_counterMode === "badge"
|
||||
|
||||
readonly property var positions: [
|
||||
{ name: i18n("Disabled"), value: "disabled" },
|
||||
{ name: i18n("Top-Left"), value: "topLeft" },
|
||||
{ name: i18n("Top-Right"), value: "topRight" },
|
||||
{ name: i18n("Bottom-Left"), value: "bottomLeft" },
|
||||
{ name: i18n("Bottom-Right"), value: "bottomRight" }
|
||||
]
|
||||
|
||||
property int currentTab
|
||||
signal tabChanged(currentTab: int)
|
||||
onCurrentTabChanged: tabChanged(currentTab)
|
||||
|
||||
header: Kirigami.NavigationTabBar {
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
icon.name: "view-list-icons"
|
||||
text: i18n("Panel Icon")
|
||||
checked: currentTab === 0
|
||||
onTriggered: currentTab = 0
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: "view-split-left-right"
|
||||
text: i18n("Full View")
|
||||
checked: currentTab === 1
|
||||
onTriggered: currentTab = 1
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: iconViewTab
|
||||
visible: currentTab === 0
|
||||
|
||||
Kirigami.Separator {
|
||||
Kirigami.FormData.label: i18n("Icon")
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
Button {
|
||||
id: iconButton
|
||||
|
||||
implicitWidth: iconFrame.width + Kirigami.Units.smallSpacing
|
||||
implicitHeight: implicitWidth
|
||||
hoverEnabled: true
|
||||
|
||||
FrameSvgItem {
|
||||
id: iconFrame
|
||||
anchors.centerIn: parent
|
||||
width: Kirigami.Units.iconSizes.large + fixedMargins.left + fixedMargins.right
|
||||
height: width
|
||||
imagePath: "widgets/background"
|
||||
|
||||
Kirigami.Icon {
|
||||
anchors.centerIn: parent
|
||||
width: Kirigami.Units.iconSizes.large
|
||||
height: width
|
||||
source: JS.setIcon(cfg_selectedIcon)
|
||||
}
|
||||
}
|
||||
|
||||
IconDialog {
|
||||
id: iconDialog
|
||||
onIconNameChanged: cfg_selectedIcon = iconName || JS.defaultIcon
|
||||
}
|
||||
|
||||
onClicked: menu.opened ? menu.close() : menu.open()
|
||||
|
||||
Menu {
|
||||
id: menu
|
||||
y: +parent.height
|
||||
|
||||
MenuItem {
|
||||
text: i18n("Default") + " 1"
|
||||
icon.name: "apdatifier-plasmoid"
|
||||
enabled: cfg_selectedIcon !== JS.defaultIcon
|
||||
onClicked: cfg_selectedIcon = JS.defaultIcon
|
||||
}
|
||||
MenuItem {
|
||||
text: i18n("Default") + " 2"
|
||||
icon.name: "apdatifier-packages"
|
||||
enabled: cfg_selectedIcon !== icon.name
|
||||
onClicked: cfg_selectedIcon = icon.name
|
||||
}
|
||||
MenuItem {
|
||||
text: i18n("Default") + " 3"
|
||||
icon.name: "apdatifier-package"
|
||||
enabled: cfg_selectedIcon !== icon.name
|
||||
onClicked: cfg_selectedIcon = icon.name
|
||||
}
|
||||
MenuItem {
|
||||
text: i18n("Default") + " 4"
|
||||
icon.name: "apdatifier-flatpak"
|
||||
enabled: cfg_selectedIcon !== icon.name
|
||||
onClicked: cfg_selectedIcon = icon.name
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: i18n("Select...")
|
||||
icon.name: "document-open-folder"
|
||||
onClicked: iconDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
text: cfg_selectedIcon === JS.defaultIcon ? i18n("Default icon") : cfg_selectedIcon
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
visible: iconButton.hovered
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Hide") + ":"
|
||||
|
||||
Label {
|
||||
text: i18n("when less than")
|
||||
}
|
||||
|
||||
SpinBox {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
|
||||
id: hideIconPolicy
|
||||
from: 0
|
||||
to: 999
|
||||
stepSize: 1
|
||||
}
|
||||
|
||||
Label {
|
||||
text: i18np("update", "updates", hideIconPolicy.value)
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("If the widget is on a panel, you can access the hidden icon in Panel Configuration mode.")
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Busy indicator") + ":"
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
ButtonGroup {
|
||||
id: busyIndicatorGroup
|
||||
onCheckedButtonChanged: {
|
||||
if (checkedButton) {
|
||||
cfg_busyIndicator = checkedButton.modeValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
text: i18n("Spinner")
|
||||
property string modeValue: "spinner"
|
||||
ButtonGroup.group: busyIndicatorGroup
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
text: i18n("Pulse")
|
||||
property string modeValue: "pulse"
|
||||
ButtonGroup.group: busyIndicatorGroup
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
const current = plasmoid.configuration.busyIndicator || "spinner"
|
||||
for (let i = 0; i < busyIndicatorGroup.buttons.length; ++i) {
|
||||
const b = busyIndicatorGroup.buttons[i]
|
||||
if (b.modeValue === current) {
|
||||
b.checked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("Spinner uses the standard Plasmoid busy indicator. Pulse animates the panel icon. On the desktop, the busy indicator is always off.")
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Kirigami.FormData.label: i18n("Counter")
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Mode") + ":"
|
||||
|
||||
RowLayout {
|
||||
id: counterMode
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
ButtonGroup {
|
||||
id: counterModeGroup
|
||||
onCheckedButtonChanged: {
|
||||
if (checkedButton) {
|
||||
cfg_counterMode = checkedButton.modeValue
|
||||
}
|
||||
}
|
||||
}
|
||||
RadioButton {
|
||||
text: i18n("Off")
|
||||
property string modeValue: "disabled"
|
||||
ButtonGroup.group: counterModeGroup
|
||||
}
|
||||
RadioButton {
|
||||
text: i18n("Side")
|
||||
property string modeValue: "side"
|
||||
ButtonGroup.group: counterModeGroup
|
||||
enabled: !allowOnlyCounterBadge
|
||||
}
|
||||
RadioButton {
|
||||
text: i18n("Badge")
|
||||
property string modeValue: "badge"
|
||||
ButtonGroup.group: counterModeGroup
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (allowOnlyCounterBadge && plasmoid.configuration.counterMode === "side") {
|
||||
plasmoid.configuration.counterMode = "badge"
|
||||
}
|
||||
|
||||
for (let i = 0; i < counterModeGroup.buttons.length; ++i) {
|
||||
const b = counterModeGroup.buttons[i]
|
||||
if (b.modeValue === plasmoid.configuration.counterMode) {
|
||||
b.checked = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Position") + ":"
|
||||
ComboBox {
|
||||
id: counterBadgePosition
|
||||
enabled: badgeMode
|
||||
textRole: "name"
|
||||
model: positions.slice(1).concat([{ name: i18n("Center"), value: "center" }])
|
||||
onCurrentIndexChanged: cfg_counterBadgePosition = model[currentIndex].value
|
||||
Component.onCompleted: currentIndex = JS.setIndex(plasmoid.configuration.counterBadgePosition, model)
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Side") + ":"
|
||||
enabled: sideMode
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
RadioButton {
|
||||
text: i18n("Left")
|
||||
checked: cfg_counterSidePosition === "left"
|
||||
onCheckedChanged: {
|
||||
if (checked) cfg_counterSidePosition = "left"
|
||||
}
|
||||
}
|
||||
RadioButton {
|
||||
text: i18n("Right")
|
||||
checked: cfg_counterSidePosition === "right"
|
||||
onCheckedChanged: {
|
||||
if (checked) cfg_counterSidePosition = "right"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Gaps") + ":"
|
||||
enabled: sideMode
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
RowLayout {
|
||||
Label { text: i18n("Inner") + ":" }
|
||||
SpinBox {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
|
||||
enabled: sideMode
|
||||
id: counterGapsInner
|
||||
from: 0
|
||||
to: 99
|
||||
stepSize: 1
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Label { text: i18n("Outer") + ":" }
|
||||
SpinBox {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
|
||||
enabled: sideMode
|
||||
id: counterGapsOuter
|
||||
from: 0
|
||||
to: 99
|
||||
stepSize: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Offset") + ":"
|
||||
enabled: badgeMode
|
||||
Label { text: "X:" }
|
||||
SpinBox {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
|
||||
id: counterOffsetX
|
||||
from: -5
|
||||
to: 5
|
||||
stepSize: 1
|
||||
}
|
||||
|
||||
Label { text: "Y:" }
|
||||
SpinBox {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
|
||||
id: counterOffsetY
|
||||
from: -5
|
||||
to: 5
|
||||
stepSize: 1
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
Kirigami.FormData.label: i18n("Font family") + ":"
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 12
|
||||
enabled: counterEnabled
|
||||
editable: true
|
||||
textRole: "name"
|
||||
model: {
|
||||
let fonts = Qt.fontFamilies()
|
||||
let arr = []
|
||||
arr.push({"name": i18n("Default system font"), "value": ""})
|
||||
for (let i = 0; i < fonts.length; i++) {
|
||||
arr.push({"name": fonts[i], "value": fonts[i]})
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
onCurrentIndexChanged: cfg_counterFontFamily = model[currentIndex]["value"]
|
||||
Component.onCompleted: currentIndex = JS.setIndex(plasmoid.configuration.counterFontFamily, model)
|
||||
}
|
||||
|
||||
Slider {
|
||||
Kirigami.FormData.label: i18n("Font size") + ":"
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 10
|
||||
enabled: counterEnabled
|
||||
id: counterFontSize
|
||||
from: 2
|
||||
to: 8
|
||||
stepSize: 1
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
Kirigami.FormData.label: i18n("Font bold") + ":"
|
||||
enabled: counterEnabled
|
||||
id: counterFontBold
|
||||
text: i18n("Enable")
|
||||
onCheckedChanged: cfg_counterFontBold = counterFontBold.checked
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
Button {
|
||||
Kirigami.FormData.label: i18n("Badge color") + ":"
|
||||
Layout.leftMargin: Kirigami.Units.gridUnit
|
||||
enabled: badgeMode
|
||||
|
||||
id: counterColor
|
||||
implicitWidth: Kirigami.Units.gridUnit * 4
|
||||
implicitHeight: Kirigami.Units.gridUnit
|
||||
|
||||
background: Item {
|
||||
Rectangle {
|
||||
id: bg
|
||||
anchors.fill: parent
|
||||
radius: counterRadius.value
|
||||
border.width: 1
|
||||
border.color: "black"
|
||||
color: cfg_counterColor ? cfg_counterColor : Kirigami.Theme.backgroundColor
|
||||
}
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: bg.color.toString().toUpperCase()
|
||||
font.bold: true
|
||||
color: Kirigami.ColorUtils.brightnessForColor(bg.color) === Kirigami.ColorUtils.Dark ? "white" : "black"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onPressed: menuColor.opened ? menuColor.close() : menuColor.open()
|
||||
|
||||
Menu {
|
||||
id: menuColor
|
||||
y: +parent.height
|
||||
|
||||
MenuItem {
|
||||
text: i18n("Default color")
|
||||
icon.name: "edit-clear"
|
||||
enabled: cfg_counterColor && cfg_counterColor !== Kirigami.Theme.backgroundColor
|
||||
onClicked: cfg_counterColor = ""
|
||||
}
|
||||
|
||||
MenuItem {
|
||||
text: i18n("Select...")
|
||||
icon.name: "document-open-folder"
|
||||
onClicked: colorDialog.open()
|
||||
}
|
||||
}
|
||||
|
||||
ColorDialog {
|
||||
id: colorDialog
|
||||
visible: false
|
||||
title: i18n("Select counter background color")
|
||||
selectedColor: cfg_counterColor
|
||||
|
||||
onAccepted: {
|
||||
cfg_counterColor = selectedColor
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
ToolTip {
|
||||
text: cfg_counterColor ? cfg_counterColor : i18n("Default background color from current theme")
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
visible: !cfg_counterColor && counterColor.hovered
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Badge radius") + ":"
|
||||
enabled: badgeMode
|
||||
|
||||
Slider {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 10
|
||||
id: counterRadius
|
||||
from: 0
|
||||
to: 100
|
||||
stepSize: 1
|
||||
}
|
||||
|
||||
Label {
|
||||
text: counterRadius.value
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Badge opacity") + ":"
|
||||
enabled: badgeMode
|
||||
|
||||
Slider {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 10
|
||||
id: counterOpacity
|
||||
from: 0
|
||||
to: 10
|
||||
stepSize: 1
|
||||
}
|
||||
|
||||
Label {
|
||||
text: counterOpacity.value / 10
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Kirigami.FormData.label: i18n("Icon Badges")
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Paused") + ":"
|
||||
ComboBox {
|
||||
id: pauseBadgePosition
|
||||
textRole: "name"
|
||||
model: positions
|
||||
onCurrentIndexChanged: cfg_pauseBadgePosition = model[currentIndex].value
|
||||
Component.onCompleted: currentIndex = JS.setIndex(plasmoid.configuration.pauseBadgePosition, model)
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Updated") + ":"
|
||||
ComboBox {
|
||||
id: updatedBadgePosition
|
||||
textRole: "name"
|
||||
model: positions
|
||||
onCurrentIndexChanged: cfg_updatedBadgePosition = model[currentIndex].value
|
||||
Component.onCompleted: currentIndex = JS.setIndex(plasmoid.configuration.updatedBadgePosition, model)
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Offset") + ":"
|
||||
Label { text: "X:" }
|
||||
SpinBox {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
|
||||
id: badgeOffsetX
|
||||
from: -5
|
||||
to: 5
|
||||
stepSize: 1
|
||||
}
|
||||
|
||||
Label { text: "Y:" }
|
||||
SpinBox {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 3
|
||||
id: badgeOffsetY
|
||||
from: -5
|
||||
to: 5
|
||||
stepSize: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: listViewTab
|
||||
visible: currentTab === 1
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: "UI:"
|
||||
CheckBox {
|
||||
id: ownIconsUI
|
||||
text: i18n("Use built-in icons")
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("Override custom icon theme and use default Apdatifier icons instead.")
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
Kirigami.FormData.label: i18n("Scrollbar") + ":"
|
||||
id: autoHideScrollBar
|
||||
text: i18n("Auto-hide scrollbar")
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: viewGroup
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
Kirigami.FormData.label: i18n("Default view") + ":"
|
||||
id: extendedView
|
||||
ButtonGroup.group: viewGroup
|
||||
text: i18n("Extended")
|
||||
Component.onCompleted: checked = !plasmoid.configuration.defaultTab
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
ButtonGroup.group: viewGroup
|
||||
text: i18n("Compact")
|
||||
onCheckedChanged: cfg_defaultTab = checked
|
||||
Component.onCompleted: checked = plasmoid.configuration.defaultTab
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
Kirigami.FormData.label: i18n("Behavior") + ":"
|
||||
id: switchDefaultTab
|
||||
text: i18n("Always switch to default view")
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: sortGroup
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
id: sorting
|
||||
Kirigami.FormData.label: i18n("Sorting") + ":"
|
||||
text: i18n("By repository")
|
||||
checked: true
|
||||
Component.onCompleted: checked = plasmoid.configuration.sorting
|
||||
ButtonGroup.group: sortGroup
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
text: i18n("By name")
|
||||
Component.onCompleted: checked = !plasmoid.configuration.sorting
|
||||
ButtonGroup.group: sortGroup
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Kirigami.FormData.label: i18n("Toolbar")
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: showStatusText
|
||||
Kirigami.FormData.label: i18n("Header") + ":"
|
||||
text: i18n("Show status")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: showToolBar
|
||||
text: i18n("Show tool bar")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
enabled: showToolBar.checked
|
||||
ToolButton {
|
||||
id: searchButton
|
||||
icon.name: "search"
|
||||
checkable: true
|
||||
}
|
||||
ToolButton {
|
||||
id: intervalButton
|
||||
icon.name: "media-playback-paused"
|
||||
checkable: true
|
||||
}
|
||||
ToolButton {
|
||||
id: sortButton
|
||||
icon.name: "sort-name"
|
||||
checkable: true
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
enabled: showToolBar.checked
|
||||
ToolButton {
|
||||
id: viewButton
|
||||
icon.name: "view-split-top-bottom"
|
||||
checkable: true
|
||||
}
|
||||
ToolButton {
|
||||
id: managementButton
|
||||
icon.name: "tools"
|
||||
checkable: true
|
||||
}
|
||||
ToolButton {
|
||||
id: upgradeButton
|
||||
icon.name: "akonadiconsole"
|
||||
checkable: true
|
||||
}
|
||||
|
||||
}
|
||||
RowLayout {
|
||||
enabled: showToolBar.checked
|
||||
ToolButton {
|
||||
id: checkButton
|
||||
icon.name: "view-refresh"
|
||||
checkable: true
|
||||
}
|
||||
ToolButton {
|
||||
id: settingsButton
|
||||
icon.name: "settings-configure"
|
||||
checkable: true
|
||||
enabled: !inTray
|
||||
}
|
||||
ToolButton {
|
||||
id: pinButton
|
||||
icon.name: "pin"
|
||||
checkable: true
|
||||
enabled: !inTray && !onDesktop
|
||||
ToolTip {
|
||||
text: !pinButton.enabled ? "Pinning button is not available in the system tray and on the desktop" : ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Kirigami.FormData.label: i18n("Additional Package Info")
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
Kirigami.FormData.label: i18n("Compact view") + ":"
|
||||
id: compactInfo
|
||||
textRole: "name"
|
||||
model: [
|
||||
{ name: i18n("Repository"), value: "repository" },
|
||||
{ name: i18n("Install reason"), value: "installReason" },
|
||||
{ name: i18n("Versions"), value: "versionDiff" }
|
||||
]
|
||||
onCurrentIndexChanged: cfg_compactInfo = model[currentIndex].value
|
||||
Component.onCompleted: currentIndex = JS.setIndex(plasmoid.configuration.compactInfo || "repository", model)
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
Kirigami.FormData.label: i18n("Extended view") + ":"
|
||||
id: extendedShowRepository
|
||||
text: i18n("Repository")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: extendedShowInstallReason
|
||||
text: i18n("Install reason")
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: highlightVersionDiff
|
||||
text: i18n("Highlight version differences")
|
||||
}
|
||||
CheckBox {
|
||||
id: colorizeInstallReason
|
||||
text: i18n("Colorize install reason")
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Kirigami.FormData.label: i18n("Tab Bar")
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Footer") + ":"
|
||||
|
||||
CheckBox {
|
||||
id: tabBarVisible
|
||||
text: i18n("Show tab bar")
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: tabBarTexts
|
||||
text: i18n("Show tab texts")
|
||||
enabled: tabBarVisible.checked
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,800 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import org.kde.kcmutils
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import "../components" as QQC
|
||||
import "../../tools/tools.js" as JS
|
||||
|
||||
SimpleKCM {
|
||||
property string cfg_checkMode: plasmoid.configuration.checkMode
|
||||
property alias cfg_intervalMinutes: intervalMinutes.value
|
||||
property alias cfg_dailyHour: dailyHour.value
|
||||
property alias cfg_dailyMinute: dailyMinute.value
|
||||
property alias cfg_weeklyDay: weeklyDay.currentIndex
|
||||
property alias cfg_weeklyHour: weeklyHour.value
|
||||
property alias cfg_weeklyMinute: weeklyMinute.value
|
||||
|
||||
property alias cfg_arch: arch.checked
|
||||
property alias cfg_aur: aur.checked
|
||||
property alias cfg_flatpak: flatpak.checked
|
||||
property alias cfg_fwupd: fwupd.checked
|
||||
property alias cfg_widgets: widgets.checked
|
||||
|
||||
property bool cfg_feedsEnabled: plasmoid.configuration.feedsEnabled
|
||||
property alias cfg_feedsFetchCount: feedsFetchCount.value
|
||||
property string cfg_feeds: plasmoid.configuration.feeds
|
||||
|
||||
property string cfg_middleAction: plasmoid.configuration.middleAction
|
||||
property string cfg_rightAction: plasmoid.configuration.rightAction
|
||||
property string cfg_scrollUpAction: plasmoid.configuration.scrollUpAction
|
||||
property string cfg_scrollDownAction: plasmoid.configuration.scrollDownAction
|
||||
|
||||
property alias cfg_notifyUpdates: notifyUpdates.checked
|
||||
property alias cfg_notifyUpdatesAction: notifyUpdatesAction.checked
|
||||
property alias cfg_notifyEveryBump: notifyEveryBump.checked
|
||||
property alias cfg_notifyNews: notifyNews.checked
|
||||
property alias cfg_notifyNewsAction: notifyNewsAction.checked
|
||||
property alias cfg_notifyErrors: notifyErrors.checked
|
||||
property alias cfg_notifySound: notifySound.checked
|
||||
property alias cfg_notifyPersistent: notifyPersistent.checked
|
||||
|
||||
property alias cfg_checkConn: checkConn.checked
|
||||
property alias cfg_respectMeteredConn: respectMeteredConn.checked
|
||||
|
||||
property var cfg: plasmoid.configuration
|
||||
property var pkg: plasmoid.configuration.packages
|
||||
property var terminals: plasmoid.configuration.terminals
|
||||
property var feeds: []
|
||||
property alias cfg_dbPath: dbPath.text
|
||||
|
||||
property bool systemWide: !/^\/home\/[^/]+\/\.local/.test(JS.scriptDir)
|
||||
|
||||
property int installButton
|
||||
property var dialogTitles: {
|
||||
"0": i18n("Install Development version"),
|
||||
"1": i18n("Install Stable version"),
|
||||
"2": i18n("Uninstall widget")
|
||||
}
|
||||
property var dialogSubtitles: {
|
||||
"0": i18n("Note: version with the latest commits may be unstable."),
|
||||
"1": i18n("Note: if you haven't installed the Devel version before, there's no need to install the Stable version."),
|
||||
"2": i18n("Removal of the widget and all related files, including the directory with its configuration.")
|
||||
}
|
||||
|
||||
property int currentTab
|
||||
signal tabChanged(currentTab: int)
|
||||
onCurrentTabChanged: tabChanged(currentTab)
|
||||
|
||||
Component.onCompleted: {
|
||||
feeds = JS.parseCustomFeeds(cfg_feeds)
|
||||
cfg_feeds = JS.serializeCustomFeeds(feeds)
|
||||
feedsUrl.syncToModel()
|
||||
JS.checkDependencies()
|
||||
}
|
||||
|
||||
header: Kirigami.NavigationTabBar {
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
icon.name: "search"
|
||||
text: i18n("Search")
|
||||
checked: currentTab === 0
|
||||
onTriggered: currentTab = 0
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: "notification-active"
|
||||
text: i18n("Notifications")
|
||||
checked: currentTab === 1
|
||||
onTriggered: currentTab = 1
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: "followmouse-symbolic"
|
||||
text: i18n("Mouse actions")
|
||||
checked: currentTab === 2
|
||||
onTriggered: currentTab = 2
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: "documentinfo"
|
||||
text: i18n("Misc")
|
||||
checked: currentTab === 3
|
||||
onTriggered: currentTab = 3
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Kirigami.InlineMessage {
|
||||
id: configMsg
|
||||
Layout.fillWidth: true
|
||||
icon.source: "document-save"
|
||||
text: "<b>" + i18n("Configuration is automatically saved in a config file and loaded at every startup, ensuring you never lose your settings. The config file is stored in ") + "~/.config/apdatifier" + "</b>"
|
||||
type: Kirigami.MessageType.Positive
|
||||
visible: plasmoid.configuration.configMsg
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
text: "OK"
|
||||
icon.name: "checkmark"
|
||||
onTriggered: plasmoid.configuration.configMsg = false
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: searchTab
|
||||
visible: currentTab === 0
|
||||
|
||||
Kirigami.Separator {
|
||||
Kirigami.FormData.label: i18n("Schedule")
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Mode") + ":"
|
||||
|
||||
ComboBox {
|
||||
id: checkMode
|
||||
textRole: "name"
|
||||
model: [
|
||||
{ name: i18n("Manual"), value: "manual" },
|
||||
{ name: i18n("Interval"), value: "interval" },
|
||||
{ name: i18n("Daily"), value: "daily" },
|
||||
{ name: i18n("Weekly"), value: "weekly" }]
|
||||
currentIndex: JS.setIndex(cfg_checkMode, model)
|
||||
onCurrentIndexChanged: cfg_checkMode = model[currentIndex].value
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: cfg_checkMode === "interval"
|
||||
Kirigami.FormData.label: i18n("Interval") + ":"
|
||||
|
||||
SpinBox {
|
||||
id: intervalMinutes
|
||||
from: 30
|
||||
to: 43200
|
||||
stepSize: 5
|
||||
value: intervalMinutes.value
|
||||
onValueChanged: cfg_intervalMinutes = value
|
||||
}
|
||||
|
||||
Label { text: i18n("minutes") }
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: cfg_checkMode === "daily"
|
||||
Kirigami.FormData.label: i18n("Daily time") + ":"
|
||||
|
||||
SpinBox {
|
||||
id: dailyHour
|
||||
from: 0
|
||||
to: 23
|
||||
stepSize: 1
|
||||
value: dailyHour.value
|
||||
onValueChanged: cfg_dailyHour = value
|
||||
Layout.preferredWidth: 50
|
||||
}
|
||||
|
||||
Label { text: ":" }
|
||||
|
||||
SpinBox {
|
||||
id: dailyMinute
|
||||
from: 0
|
||||
to: 59
|
||||
stepSize: 1
|
||||
value: dailyMinute.value
|
||||
onValueChanged: cfg_dailyMinute = value
|
||||
Layout.preferredWidth: 50
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: cfg_checkMode === "weekly"
|
||||
Kirigami.FormData.label: i18n("Schedule") + ":"
|
||||
|
||||
ComboBox {
|
||||
id: weeklyDay
|
||||
textRole: "name"
|
||||
model: [
|
||||
{ name: i18n("Sunday"), value: 0 },
|
||||
{ name: i18n("Monday"), value: 1 },
|
||||
{ name: i18n("Tuesday"), value: 2 },
|
||||
{ name: i18n("Wednesday"), value: 3 },
|
||||
{ name: i18n("Thursday"), value: 4 },
|
||||
{ name: i18n("Friday"), value: 5 },
|
||||
{ name: i18n("Saturday"), value: 6 }
|
||||
]
|
||||
currentIndex: JS.setIndex(plasmoid.configuration.weeklyDay, model)
|
||||
onCurrentIndexChanged: plasmoid.configuration.weeklyDay = model[currentIndex].value
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
SpinBox {
|
||||
id: weeklyHour
|
||||
from: 0
|
||||
to: 23
|
||||
stepSize: 1
|
||||
value: weeklyHour.value
|
||||
onValueChanged: cfg_weeklyHour = value
|
||||
Layout.preferredWidth: 50
|
||||
}
|
||||
|
||||
Label {
|
||||
text: ":"
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
|
||||
SpinBox {
|
||||
id: weeklyMinute
|
||||
from: 0
|
||||
to: 59
|
||||
stepSize: 1
|
||||
value: weeklyMinute.value
|
||||
onValueChanged: cfg_weeklyMinute = value
|
||||
Layout.preferredWidth: 50
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Kirigami.FormData.label: i18n("Updates")
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
CheckBox {
|
||||
id: arch
|
||||
text: i18n("Arch Official Repositories")
|
||||
enabled: pkg.pacman
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.gridUnit
|
||||
visible: pkg.pacman
|
||||
|
||||
CheckBox {
|
||||
id: aur
|
||||
text: i18n("Arch User Repository") + " (AUR)"
|
||||
enabled: pkg.paru || pkg.yay || pkg.pikaur
|
||||
}
|
||||
|
||||
Kirigami.UrlButton {
|
||||
url: "https://github.com/exequtic/apdatifier#supported-pacman-wrappers"
|
||||
text: instTip.text
|
||||
font.pointSize: instTip.font.pointSize
|
||||
color: instTip.color
|
||||
visible: !aur.enabled
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.gridUnit
|
||||
|
||||
CheckBox {
|
||||
id: flatpak
|
||||
text: i18n("Flatpak applications")
|
||||
enabled: pkg.flatpak
|
||||
}
|
||||
|
||||
Kirigami.UrlButton {
|
||||
id: instTip
|
||||
url: "https://flathub.org/setup"
|
||||
text: i18n("Not installed")
|
||||
font.pointSize: Kirigami.Theme.smallFont.pointSize
|
||||
color: Kirigami.Theme.neutralTextColor
|
||||
visible: !pkg.flatpak
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
CheckBox {
|
||||
id: widgets
|
||||
text: i18n("Plasma Widgets")
|
||||
enabled: pkg.jq
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.gridUnit
|
||||
|
||||
CheckBox {
|
||||
id: fwupd
|
||||
text: i18n("Firmware")
|
||||
enabled: pkg.fwupdmgr
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
visible: currentTab === 0
|
||||
Layout.fillWidth: true
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
|
||||
ColumnLayout {
|
||||
id: feedsUrl
|
||||
Layout.fillWidth: true
|
||||
enabled: pkg.jq
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
function syncFromModel() {
|
||||
var items = []
|
||||
for (var i = 0; i < feedsModel.count; i++) {
|
||||
items.push(feedsModel.get(i).url)
|
||||
}
|
||||
feeds = items
|
||||
cfg_feeds = JS.serializeCustomFeeds(feeds)
|
||||
}
|
||||
function syncToModel() {
|
||||
feedsModel.clear()
|
||||
for (var i = 0; i < feeds.length; i++) {
|
||||
feedsModel.append({url: feeds[i]})
|
||||
}
|
||||
}
|
||||
function reset() {
|
||||
feedsModel.clear()
|
||||
var defaults = JS.parseCustomFeeds(plasmoid.configuration.feedsDefault)
|
||||
for (var i = 0; i < defaults.length; i++) {
|
||||
feedsModel.append({url: defaults[i]})
|
||||
}
|
||||
syncFromModel()
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: feedsModel
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: feedsList
|
||||
Layout.fillWidth: true
|
||||
implicitHeight: contentHeight
|
||||
model: feedsModel
|
||||
headerPositioning: ListView.OverlayHeader
|
||||
header: Kirigami.InlineViewHeader {
|
||||
width: feedsList.width
|
||||
text: i18n("RSS Feeds")
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
icon.name: "rating"
|
||||
enabled: cfg_feedsEnabled && pkg.jq
|
||||
onTriggered: recommendedFeedsDialog.open()
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: cfg_feedsEnabled ? "news-subscribe" : "news-unsubscribe"
|
||||
text: cfg_feedsEnabled ? i18n("Enabled") : i18n("Disabled")
|
||||
onTriggered: cfg_feedsEnabled = !cfg_feedsEnabled
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: "edit-reset"
|
||||
text: i18n("Reset")
|
||||
enabled: cfg_feedsEnabled && pkg.jq
|
||||
onTriggered: feedsUrl.reset()
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: "list-add-symbolic"
|
||||
text: i18n("Add")
|
||||
enabled: cfg_feedsEnabled && pkg.jq
|
||||
onTriggered: {
|
||||
feedsModel.insert(0, {url: ""})
|
||||
feedsUrl.syncFromModel()
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
delegate: RowLayout {
|
||||
required property string url
|
||||
required property int index
|
||||
width: ListView.view.width
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
|
||||
TextField {
|
||||
Layout.fillWidth: true
|
||||
text: url
|
||||
placeholderText: "https://..."
|
||||
font.family: "monospace"
|
||||
font.pointSize: Kirigami.Theme.smallFont.pointSize
|
||||
selectByMouse: true
|
||||
implicitHeight: Kirigami.Units.gridUnit * 1.8
|
||||
enabled: cfg_feedsEnabled
|
||||
onTextChanged: {
|
||||
feedsModel.setProperty(index, "url", text)
|
||||
feedsUrl.syncFromModel()
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
icon.name: "delete-symbolic"
|
||||
implicitWidth: Kirigami.Units.gridUnit * 2
|
||||
implicitHeight: Kirigami.Units.gridUnit * 2
|
||||
ToolTip {
|
||||
text: i18n("Remove")
|
||||
delay: Kirigami.Units.toolTipDelay
|
||||
}
|
||||
onClicked: {
|
||||
feedsModel.remove(index)
|
||||
feedsUrl.syncFromModel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add: Transition { NumberAnimation { property: "x"; from: 80; to: 0; duration: 320; easing.type: Easing.OutCubic } }
|
||||
moveDisplaced: Transition { NumberAnimation { properties: "x,y"; duration: 300; easing.type: Easing.OutCubic } }
|
||||
move: Transition { NumberAnimation { properties: "x,y"; duration: 300; easing.type: Easing.OutCubic } }
|
||||
removeDisplaced: Transition { NumberAnimation { properties: "x,y"; duration: 280; easing.type: Easing.OutCubic } }
|
||||
remove: Transition { ParallelAnimation {
|
||||
NumberAnimation { property: "opacity"; to: 0; duration: 220; easing.type: Easing.InQuad }
|
||||
NumberAnimation { property: "x"; to: 80; duration: 300; easing.type: Easing.InCubic } } }
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
enabled: pkg.jq && cfg_feedsEnabled && cfg_feeds !== ""
|
||||
|
||||
SpinBox {
|
||||
id: feedsFetchCount
|
||||
from: 1
|
||||
to: 10
|
||||
stepSize: 1
|
||||
}
|
||||
|
||||
Label {
|
||||
text: i18np(
|
||||
"latest article from each feed",
|
||||
"latest articles from each feed",
|
||||
feedsFetchCount.value
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Kirigami.Dialog {
|
||||
id: recommendedFeedsDialog
|
||||
title: i18n("Recommended RSS Feeds")
|
||||
preferredWidth: Kirigami.Units.gridUnit * 25
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
{ name: "This week in Plasma", url: "https://blogs.kde.org/categories/this-week-in-plasma/index.xml" },
|
||||
{ name: "This week in KDE Apps", url: "https://blogs.kde.org/categories/this-week-in-kde-apps/index.xml" },
|
||||
{ name: "KDE Community", url: "https://kde.org/index.xml" },
|
||||
{ name: "CachyOS's Blog", url: "https://cachyos.org/rss.xml" },
|
||||
{ name: "Apdatifier Release Notes", url: "https://github.com/exequtic/apdatifier/releases.atom" }
|
||||
]
|
||||
|
||||
delegate: Frame {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: index > 0 ? 2 : 0
|
||||
RowLayout {
|
||||
anchors.fill: parent
|
||||
Label {
|
||||
text: modelData.name
|
||||
font.bold: true
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Button {
|
||||
icon.name: "list-add"
|
||||
text: i18n("Add")
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 6
|
||||
onClicked: {
|
||||
if (!feeds.includes(modelData.url)) {
|
||||
feedsModel.insert(0, {url: modelData.url})
|
||||
feedsUrl.syncFromModel()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: notificationsTab
|
||||
visible: currentTab === 1
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing * 2
|
||||
|
||||
CheckBox {
|
||||
Kirigami.FormData.label: i18n("Notifications") + ":"
|
||||
id: notifyUpdates
|
||||
text: i18n("For new updates")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: notifyUpdatesAction
|
||||
text: i18n("Action button")
|
||||
enabled: notifyUpdates.checked
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
CheckBox {
|
||||
id: notifyEveryBump
|
||||
text: i18n("For every version bump")
|
||||
enabled: notifyUpdates.checked
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("If the option is <b>enabled</b>, notifications will be sent when a new version of the package is bumped, even if the package is already on the list. <b>More notifications.</b> <br><br>If the option is <b>disabled</b>, notifications will only be sent for packages that are not yet on the list. <b>Less notifications.</b>")
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
spacing: Kirigami.Units.largeSpacing * 2
|
||||
|
||||
CheckBox {
|
||||
id: notifyNews
|
||||
text: i18n("For news")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: notifyNewsAction
|
||||
text: i18n("Action button")
|
||||
enabled: notifyNews.checked
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: notifyErrors
|
||||
text: i18n("For errors")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: notifySound
|
||||
text: i18n("With sound")
|
||||
enabled: notifyUpdates.checked || notifyNews.checked || notifyErrors.checked
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: notifyPersistent
|
||||
text: i18n("Persistent")
|
||||
enabled: notifyUpdates.checked || notifyNews.checked || notifyErrors.checked
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: notifyTip
|
||||
|
||||
Label {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
Layout.maximumWidth: 250
|
||||
font.pointSize: instTip.font.pointSize
|
||||
text: i18n("To further configure, click the button below -> Application Settings -> Apdatifier")
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: notifyTip.horizontalCenter
|
||||
enabled: notifyUpdates.checked || notifyNews.checked || notifyErrors.checked
|
||||
icon.name: "settings-configure"
|
||||
text: i18n("Configure...")
|
||||
onClicked: KCMLauncher.openSystemSettings("kcm_notifications")
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: mouseActionsTab
|
||||
visible: currentTab === 2
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
QQC.ComboBox {
|
||||
Kirigami.FormData.label: i18n("Middle click") + ":"
|
||||
type: "middle"
|
||||
}
|
||||
|
||||
QQC.ComboBox {
|
||||
Kirigami.FormData.label: i18n("Right click") + ":"
|
||||
type: "right"
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("Do not enable this option if the widget is not used in the system tray; otherwise, you will not be able to open the settings by right-clicking.")
|
||||
}
|
||||
}
|
||||
|
||||
QQC.ComboBox {
|
||||
Kirigami.FormData.label: i18n("Scroll up") + ":"
|
||||
type: "scrollUp"
|
||||
}
|
||||
|
||||
QQC.ComboBox {
|
||||
Kirigami.FormData.label: i18n("Scroll down") + ":"
|
||||
type: "scrollDown"
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: miscTab
|
||||
visible: currentTab === 3
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.preferredWidth: miscTab.width - Kirigami.Units.largeSpacing * 10
|
||||
Button {
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: 500
|
||||
icon.name: "backup"
|
||||
text: i18n("Restore hidden tooltips")
|
||||
onClicked: {
|
||||
plasmoid.configuration.configMsg = true
|
||||
plasmoid.configuration.newsMsg = true
|
||||
plasmoid.configuration.version = "v0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.preferredWidth: miscTab.width - Kirigami.Units.largeSpacing * 10
|
||||
Button {
|
||||
enabled: !systemWide
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: 500
|
||||
icon.name: "folder-git-symbolic"
|
||||
text: i18n("Install Development version")
|
||||
onClicked: {
|
||||
installButton = 0
|
||||
installDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.preferredWidth: miscTab.width - Kirigami.Units.largeSpacing * 10
|
||||
Button {
|
||||
enabled: !systemWide
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: 500
|
||||
icon.name: "run-build"
|
||||
text: i18n("Install Stable version")
|
||||
onClicked: {
|
||||
installButton = 1
|
||||
installDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
RowLayout {
|
||||
Layout.preferredWidth: miscTab.width - Kirigami.Units.largeSpacing * 10
|
||||
Button {
|
||||
enabled: !systemWide
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: 500
|
||||
icon.name: "delete"
|
||||
text: i18n("Uninstall widget")
|
||||
onClicked: {
|
||||
installButton = 2
|
||||
installDialog.open()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Layout.preferredWidth: miscTab.width - Kirigami.Units.largeSpacing * 10
|
||||
Label {
|
||||
text: "pacman DBPath:"
|
||||
}
|
||||
TextArea {
|
||||
id: dbPath
|
||||
Layout.fillWidth: true
|
||||
Layout.maximumWidth: 320
|
||||
readOnly: true
|
||||
Component.onCompleted: dbPath.text = plasmoid.configuration.dbPath
|
||||
}
|
||||
Button {
|
||||
icon.name: "document-open"
|
||||
onClicked: folderDialog.open()
|
||||
}
|
||||
Button {
|
||||
icon.name: "edit-reset"
|
||||
ToolTip.delay: 0
|
||||
ToolTip.visible: hovered
|
||||
ToolTip.text: i18n("Reset to default. By default, the path is the same as for checkupdates.")
|
||||
onClicked: {
|
||||
dbPath.text = plasmoid.configuration.dbPathDefault
|
||||
pathError.visible = false
|
||||
}
|
||||
}
|
||||
FolderDialog {
|
||||
id: folderDialog
|
||||
onAccepted: {
|
||||
const path = selectedFolder.toString().substring(7)
|
||||
if (path.includes(' ')) {
|
||||
pathError.visible = true
|
||||
} else {
|
||||
dbPath.text = path
|
||||
pathError.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
id: pathError
|
||||
text: i18n("Path must not contain spaces")
|
||||
color: Kirigami.Theme.negativeTextColor
|
||||
visible: false
|
||||
}
|
||||
|
||||
Kirigami.PromptDialog {
|
||||
id: installDialog
|
||||
title: dialogTitles[installButton]
|
||||
subtitle: dialogSubtitles[installButton]
|
||||
standardButtons: Kirigami.Dialog.Ok | Kirigami.Dialog.Cancel
|
||||
onAccepted: {
|
||||
if (installButton === 0) {
|
||||
JS.execute(JS.runInTerminal("utils", "installDev"))
|
||||
} else if (installButton === 1) {
|
||||
JS.execute(JS.runInTerminal("utils", "installStable"))
|
||||
} else {
|
||||
JS.execute(JS.runInTerminal("utils", "uninstall"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Kirigami.FormData.label: i18n("Network")
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: checkConn
|
||||
text: i18n("Connectivity check")
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: respectMeteredConn
|
||||
text: i18n("Respect metered connection")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
|
||||
import org.kde.iconthemes
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import "../../tools/tools.js" as JS
|
||||
|
||||
|
||||
ColumnLayout {
|
||||
ListModel {
|
||||
id: rulesModel
|
||||
Component.onCompleted: {
|
||||
JS.execute(JS.readFile(JS.rulesFile), (cmd, out, err, code) => {
|
||||
if (JS.Error(code, err)) return
|
||||
if (out && JS.validJSON(out, JS.rulesFile)) {
|
||||
JSON.parse(out).forEach(el => {
|
||||
if (el.type !== "name" && el.type !== "regex") return
|
||||
rulesModel.append({
|
||||
type: el.type,
|
||||
value: el.value,
|
||||
icon: el.icon,
|
||||
excluded: el.excluded,
|
||||
ignore: ('ignore' in el) ? el.ignore : false,
|
||||
important: ('important' in el) ? el.important : false
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: typesModel
|
||||
Component.onCompleted: {
|
||||
let types = [
|
||||
{name: i18n("Name"), type: "name", tip: i18n("Exact package name")},
|
||||
{name: i18n("Regex"), type: "regex", tip: i18n("See Help")}
|
||||
]
|
||||
|
||||
for (var i = 0; i < types.length; ++i) {
|
||||
typesModel.append({name: types[i].name, type: types[i].type, tip: types[i].tip})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: rule
|
||||
Item {
|
||||
id: ruleItem
|
||||
required property int index
|
||||
required property var model
|
||||
width: rulesList.width - Kirigami.Units.largeSpacing * 2
|
||||
height: swipeListItem.height
|
||||
|
||||
Kirigami.SwipeListItem {
|
||||
id: swipeListItem
|
||||
width: ruleItem.width
|
||||
down: false
|
||||
hoverEnabled: true
|
||||
separatorVisible: true
|
||||
|
||||
contentItem: RowLayout {
|
||||
Kirigami.ListItemDragHandle {
|
||||
listItem: swipeListItem
|
||||
listView: rulesList
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.small
|
||||
Layout.minimumWidth: Kirigami.Units.iconSizes.small
|
||||
Layout.maximumWidth: Kirigami.Units.iconSizes.small
|
||||
onMoveRequested: {
|
||||
var to = Math.max(0, Math.min(newIndex, rulesModel.count - 1))
|
||||
rulesModel.move(oldIndex, to, 1)
|
||||
}
|
||||
}
|
||||
ComboBox {
|
||||
implicitWidth: 120
|
||||
id: type
|
||||
model: typesModel
|
||||
textRole: "name"
|
||||
currentIndex: -1
|
||||
property bool ready: false
|
||||
onCurrentIndexChanged: {
|
||||
if (!ready) return
|
||||
rulesList.model.set(index, {
|
||||
"type": model.get(currentIndex).type,
|
||||
"value": "",
|
||||
"ignore": false
|
||||
})
|
||||
}
|
||||
Component.onCompleted: {
|
||||
var currentType = rulesList.model.get(index).type
|
||||
for (var i = 0; i < model.count; ++i) {
|
||||
if (model.get(i).type === currentType) {
|
||||
currentIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
ready = true
|
||||
}
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: valueField
|
||||
Layout.fillWidth: true
|
||||
text: model.value
|
||||
placeholderText: type.model.get(type.currentIndex).tip
|
||||
enabled: true
|
||||
onTextChanged: {
|
||||
if (type.model.get(type.currentIndex).type === "name") {
|
||||
var filtered = valueField.text.replace(/[^a-z0-9_\-+.]/g, "")
|
||||
if (valueField.text !== filtered) {
|
||||
valueField.text = filtered
|
||||
return
|
||||
}
|
||||
}
|
||||
if (model.value !== valueField.text) {
|
||||
model.value = valueField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
ToolTip { text: model.icon }
|
||||
icon.name: model.icon
|
||||
onClicked: iconDialog.open()
|
||||
|
||||
IconDialog {
|
||||
id: iconDialog
|
||||
onIconNameChanged: model.icon = iconName
|
||||
}
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
ToolTip { text: i18n("Mark as important") }
|
||||
icon.name: model.important ? "flag-red" : "flag"
|
||||
checkable: true
|
||||
checked: model.important
|
||||
onClicked: model.important = !model.important
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
ToolTip { text: model.excluded ? i18n("Show in the list") : i18n("Exclude from the list") }
|
||||
icon.name: model.excluded ? "hint" : "view-visible"
|
||||
checkable: true
|
||||
checked: model.excluded
|
||||
onClicked: model.excluded = !model.excluded
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
ToolTip {
|
||||
text: i18n("Ignore a package upgrade. <b>Be careful in skipping packages, since partial upgrades are unsupported.</b>")
|
||||
}
|
||||
icon.name: model.ignore ? "process-stop" : "software-updates-updates"
|
||||
enabled: type.model.get(type.currentIndex).type === "name"
|
||||
checkable: true
|
||||
checked: model.ignore
|
||||
opacity: enabled ? 1 : 0.5
|
||||
onClicked: model.ignore = !model.ignore
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
ToolTip { text: i18n("Remove") }
|
||||
icon.name: 'delete'
|
||||
onClicked: rulesList.model.remove(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.InlineViewHeader {
|
||||
Layout.fillWidth: true
|
||||
text: i18n("Rules")
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
icon.name: "help-about-symbolic"
|
||||
text: i18n("Help")
|
||||
checkable: true
|
||||
onTriggered: rulesMsg.visible = checked
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: "list-add-symbolic"
|
||||
text: i18n("Add")
|
||||
onTriggered: rulesModel.append({
|
||||
type: "name", value: "",
|
||||
icon: plasmoid.configuration.ownIconsUI ? "apdatifier-package" : "server-database",
|
||||
excluded: false, important: false, ignore: false
|
||||
})
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: "dialog-ok-apply"
|
||||
text: i18n("Apply")
|
||||
onTriggered: {
|
||||
var array = []
|
||||
for (var i = 0; i < rulesModel.count; i++) {
|
||||
var item = rulesModel.get(i)
|
||||
if (item.value.trim() !== "")
|
||||
array.push({
|
||||
type: item.type, value: item.value, icon: item.icon,
|
||||
excluded: item.excluded, important: item.important, ignore: item.ignore
|
||||
})
|
||||
}
|
||||
var rules = JS.toFileFormat(array)
|
||||
plasmoid.configuration.rules = rules
|
||||
JS.execute(JS.writeFile(rules, '>', JS.rulesFile))
|
||||
rulesModel.clear()
|
||||
for (var i = 0; i < array.length; i++)
|
||||
rulesModel.append(array[i])
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
id: rulesMsg
|
||||
Layout.fillWidth: true
|
||||
icon.source: "showinfo"
|
||||
text: i18n("Rules apply top-to-bottom: later rules override earlier ones.<br><br>Regex examples:<br><b>^nvidia</b> — names starting with nvidia<br><b>wayland$</b> — names ending with wayland<br><b>.*qt.*</b> — names containing qt<br><b>(ttf|font|otf)</b> — any of ttf, font, or otf<br><b>linux(-zen|-lts)?$</b> — linux, linux-zen or linux-lts")
|
||||
visible: false
|
||||
}
|
||||
|
||||
ListView {
|
||||
Layout.fillHeight: true
|
||||
Layout.fillWidth: true
|
||||
id: rulesList
|
||||
model: rulesModel
|
||||
delegate: rule
|
||||
clip: true
|
||||
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
reuseItems: true
|
||||
moveDisplaced: Transition {
|
||||
YAnimator { duration: Kirigami.Units.longDuration; easing.type: Easing.InOutQuad }
|
||||
}
|
||||
ScrollBar.vertical: ScrollBar { active: true }
|
||||
|
||||
Label {
|
||||
anchors.centerIn: parent
|
||||
visible: rulesModel.count === 0
|
||||
text: i18n("No rules yet")
|
||||
font.pointSize: Kirigami.Theme.defaultFont.pointSize * 1.3
|
||||
opacity: 0.6
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
Kirigami.Page {
|
||||
id: supportPage
|
||||
|
||||
leftPadding: Kirigami.Units.gridUnit
|
||||
rightPadding: Kirigami.Units.gridUnit
|
||||
|
||||
header: Item {
|
||||
height: layout.implicitHeight + (Kirigami.Units.gridUnit * 2)
|
||||
|
||||
ColumnLayout {
|
||||
id: layout
|
||||
width: parent.width - (Kirigami.Units.gridUnit * 2)
|
||||
anchors.centerIn: parent
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
text: i18n("Thanks for using my widget! If you appreciate my work, you can support me by starring the GitHub repository or buying me a coffee ;)")
|
||||
font.bold: true
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Menu {
|
||||
id: menu
|
||||
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutsideParent
|
||||
MenuItem {
|
||||
text: "buymeacoffee.com"
|
||||
icon.name: "internet-web-browser-symbolic"
|
||||
onTriggered: Qt.openUrlExternally("https://buymeacoffee.com/evgk")
|
||||
}
|
||||
MenuItem {
|
||||
text: "nowpayments.io (crypto)"
|
||||
icon.name: "internet-web-browser-symbolic"
|
||||
onTriggered: Qt.openUrlExternally("https://nowpayments.io/donation/exequtic")
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
anchors.centerIn: parent
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
|
||||
Image {
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
source: "../assets/art/apdatifier-donate.png"
|
||||
sourceSize.width: supportPage.width / 2
|
||||
sourceSize.height: supportPage.height
|
||||
|
||||
HoverHandler {
|
||||
id: handlerDonate
|
||||
cursorShape: menu.opened ? Qt.ArrowCursor : Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onTapped: (event) => menu.opened ? menu.close() : menu.popup(event.position.x, event.position.y)
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "Donate"
|
||||
font.bold: true
|
||||
visible: handlerDonate.hovered && !menu.opened
|
||||
}
|
||||
}
|
||||
|
||||
Image {
|
||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||
source: "../assets/art/apdatifier-githubstar.png"
|
||||
sourceSize.width: supportPage.width / 2
|
||||
sourceSize.height: supportPage.height
|
||||
|
||||
HoverHandler {
|
||||
id: handlerGithub
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onTapped: Qt.openUrlExternally("https://github.com/exequtic/apdatifier")
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
font.bold: true
|
||||
visible: handlerGithub.hovered
|
||||
text: "https://github.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,766 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Dialogs
|
||||
|
||||
import org.kde.kcmutils
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import "../../tools/tools.js" as JS
|
||||
|
||||
SimpleKCM {
|
||||
property string cfg_terminal: plasmoid.configuration.terminal
|
||||
property alias cfg_tmuxSession: tmuxSession.checked
|
||||
property alias cfg_idleInhibit: idleInhibit.checked
|
||||
property alias cfg_termFont: termFont.checked
|
||||
property alias cfg_mngHideFlatpak: mngHideFlatpak.checked
|
||||
property alias cfg_mngHideHeaders: mngHideHeaders.checked
|
||||
property alias cfg_mngHideHelp: mngHideHelp.checked
|
||||
property alias cfg_mngHideNums: mngHideNums.checked
|
||||
property alias cfg_fzfOptions: fzfOptions.text
|
||||
|
||||
property string cfg_wrapper: plasmoid.configuration.wrapper
|
||||
property alias cfg_archFlags: archFlags.text
|
||||
property alias cfg_sudoBin: sudoBin.text
|
||||
property alias cfg_rebootSystem: rebootSystem.checked
|
||||
property string cfg_mirrors: plasmoid.configuration.mirrors
|
||||
property bool mirrorsGeneratorEnabled: cfg_mirrors !== "false"
|
||||
property alias cfg_mirrorsAge: mirrorsAge.value
|
||||
property alias cfg_mirrorCount: mirrorCount.value
|
||||
property alias cfg_mirrorProgress: mirrorProgress.checked
|
||||
property var countryList: []
|
||||
property string cfg_dynamicUrl: plasmoid.configuration.dynamicUrl
|
||||
|
||||
property alias cfg_flatpakRemoveUnused: flatpakRemoveUnused.checked
|
||||
property string cfg_flatpakFlags: plasmoid.configuration.flatpakFlags
|
||||
|
||||
property alias cfg_widgetConfirmation: widgetConfirmation.checked
|
||||
property alias cfg_restartShell: restartShell.checked
|
||||
property alias cfg_restartCommand: restartCommand.text
|
||||
property alias cfg_trayEnabledByDefault: trayEnabledByDefault.checked
|
||||
|
||||
property var pkg: plasmoid.configuration.packages
|
||||
property var terminals: plasmoid.configuration.terminals
|
||||
property alias cfg_preExec: preExec.text
|
||||
property alias cfg_postExec: postExec.text
|
||||
|
||||
property int currentTab
|
||||
signal tabChanged(currentTab: int)
|
||||
onCurrentTabChanged: tabChanged(currentTab)
|
||||
|
||||
header: Kirigami.NavigationTabBar {
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
icon.name: "akonadiconsole"
|
||||
text: i18n("General")
|
||||
checked: currentTab === 0
|
||||
onTriggered: currentTab = 0
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: "apdatifier-package"
|
||||
text: "Arch"
|
||||
checked: currentTab === 1
|
||||
onTriggered: currentTab = 1
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: "apdatifier-flatpak"
|
||||
text: "Flatpak"
|
||||
checked: currentTab === 2
|
||||
onTriggered: currentTab = 2
|
||||
},
|
||||
Kirigami.Action {
|
||||
icon.name: "start-here-kde-plasma-symbolic"
|
||||
text: i18n("Widgets")
|
||||
checked: currentTab === 3
|
||||
onTriggered: currentTab = 3
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: terminalTab
|
||||
visible: currentTab === 0
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Terminal") + ":"
|
||||
|
||||
ComboBox {
|
||||
model: terminals
|
||||
textRole: "name"
|
||||
enabled: terminals
|
||||
|
||||
onCurrentIndexChanged: cfg_terminal = model[currentIndex]["value"]
|
||||
|
||||
Component.onCompleted: {
|
||||
if (terminals) {
|
||||
currentIndex = JS.setIndex(plasmoid.configuration.terminal, terminals)
|
||||
|
||||
if (!plasmoid.configuration.terminal) {
|
||||
plasmoid.configuration.terminal = model[0]["value"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.UrlButton {
|
||||
url: "https://github.com/exequtic/apdatifier#supported-terminals"
|
||||
text: i18n("Not installed")
|
||||
font.pointSize: Kirigami.Theme.smallFont.pointSize
|
||||
color: Kirigami.Theme.neutralTextColor
|
||||
visible: !terminals
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
CheckBox {
|
||||
id: termFont
|
||||
text: i18n("Use NerdFont icons")
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("If your terminal utilizes any <b>Nerd Font</b>, icons from that font will be used.")
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
CheckBox {
|
||||
id: tmuxSession
|
||||
text: i18n("tmux session")
|
||||
enabled: pkg.tmux
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
CheckBox {
|
||||
id: idleInhibit
|
||||
text: i18n("Idle Inhibit")
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("Disables automatic sleep and screen lock while upgrading.")
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Kirigami.FormData.label: i18n("Pre/post upgrade scripts")
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Pre-exec") + ":"
|
||||
|
||||
TextField {
|
||||
id: preExec
|
||||
placeholderText: i18n("Command or script path")
|
||||
placeholderTextColor: "grey"
|
||||
}
|
||||
|
||||
Button {
|
||||
icon.name: "document-open"
|
||||
onClicked: fileDialogPreExec.open()
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: fileDialogPreExec
|
||||
fileMode: FileDialog.OpenFile
|
||||
onAccepted: preExec.text = selectedFile.toString().substring(7)
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("Running your command or script BEFORE the upgrade.<br>For example, you can specify your command to update the mirrorlist if you have unofficial repositories.")
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Post-exec") + ":"
|
||||
|
||||
TextField {
|
||||
id: postExec
|
||||
placeholderText: i18n("Command or script path")
|
||||
placeholderTextColor: "grey"
|
||||
}
|
||||
|
||||
Button {
|
||||
icon.name: "document-open"
|
||||
onClicked: fileDialogPostExec.open()
|
||||
}
|
||||
|
||||
FileDialog {
|
||||
id: fileDialogPostExec
|
||||
fileMode: FileDialog.OpenFile
|
||||
onAccepted: postExec.text = selectedFile.toString().substring(7)
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("Running your command or script AFTER the upgrade.<br>For example, you can specify your command to upgrade something else.")
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Kirigami.FormData.label: i18n("Management")
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: mngHideFlatpak
|
||||
text: i18n("Without Flatpak options")
|
||||
}
|
||||
CheckBox {
|
||||
id: mngHideHeaders
|
||||
text: i18n("Hide headers")
|
||||
}
|
||||
CheckBox {
|
||||
id: mngHideHelp
|
||||
text: i18n("Hide help")
|
||||
}
|
||||
CheckBox {
|
||||
id: mngHideNums
|
||||
text: i18n("Hide option numbers")
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Fzf options") + ":"
|
||||
|
||||
TextField {
|
||||
id: fzfOptions
|
||||
placeholderText: "--style full --layout default"
|
||||
placeholderTextColor: "grey"
|
||||
Layout.preferredWidth: 250
|
||||
onTextChanged: {
|
||||
var sanitized = fzfOptions.text.replace(/["'\\$`\n\r]/g, "")
|
||||
if (sanitized !== fzfOptions.text)
|
||||
fzfOptions.text = sanitized
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("Override default fzf options.<br>See <b>fzf --help</b> for available options.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: archTab
|
||||
visible: currentTab === 1
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
enabled: plasmoid.configuration.aur
|
||||
Kirigami.FormData.label: i18n("Wrapper") + ":"
|
||||
spacing: Kirigami.Units.largeSpacing * 2
|
||||
|
||||
ButtonGroup {
|
||||
id: wrappersGroup
|
||||
}
|
||||
RadioButton {
|
||||
ButtonGroup.group: wrappersGroup
|
||||
text: "paru"
|
||||
enabled: pkg.paru
|
||||
checked: plasmoid.configuration.wrapper === text
|
||||
onCheckedChanged: if (checked) cfg_wrapper = text
|
||||
}
|
||||
RadioButton {
|
||||
ButtonGroup.group: wrappersGroup
|
||||
text: "yay"
|
||||
enabled: pkg.yay
|
||||
checked: plasmoid.configuration.wrapper === text
|
||||
onCheckedChanged: if (checked) cfg_wrapper = text
|
||||
}
|
||||
RadioButton {
|
||||
ButtonGroup.group: wrappersGroup
|
||||
text: "pikaur"
|
||||
enabled: pkg.pikaur
|
||||
checked: plasmoid.configuration.wrapper === text
|
||||
onCheckedChanged: if (checked) cfg_wrapper = text
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
visible: !plasmoid.configuration.aur
|
||||
Label {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pointSize: Kirigami.Theme.smallFont.pointSize
|
||||
font.bold: true
|
||||
color: Kirigami.Theme.negativeTextColor
|
||||
text: i18n("AUR disabled in search settings")
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Upgrade options") + ":"
|
||||
|
||||
Label {
|
||||
text: plasmoid.configuration.aur ? cfg_wrapper+" "+"-Syu" : cfg_sudoBin+" "+"pacman -Syu"
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: archFlags
|
||||
onTextChanged: {
|
||||
var allow = /^[a-z0-9\- ]*$/
|
||||
if (!allow.test(archFlags.text))
|
||||
archFlags.text = archFlags.text.replace(/[^a-z0-9\- ]/g, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: "sudobin:"
|
||||
spacing: 0
|
||||
enabled: pkg.pacman
|
||||
|
||||
TextField {
|
||||
id: sudoBin
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Reboot system") + ":"
|
||||
|
||||
CheckBox {
|
||||
id: rebootSystem
|
||||
text: i18n("Suggest after upgrading")
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("This option suggests restarting the system after upgrading critical packages.<br><br><b>Note that not all critical packages require a full system restart; some may only need a session restart or no action at all, such as when an updated package is not currently running (e.g., an alternative kernel) or not in use (e.g., an alternative driver).</b>")
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.Separator {
|
||||
Kirigami.FormData.label: i18n("Pacman Mirrorlist Generator")
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
height: 30
|
||||
Label {
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
font.pointSize: Kirigami.Theme.smallFont.pointSize
|
||||
font.bold: true
|
||||
color: Kirigami.Theme.negativeTextColor
|
||||
text: i18n("Only for official repositories")+"!\n"+" (core, extra, multilib, ...)"
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("Also see https://archlinux.org/mirrorlist (click button to open link)")
|
||||
onClicked: Qt.openUrlExternally("https://archlinux.org/mirrorlist")
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: Kirigami.Units.smallSpacing * 2
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: generator
|
||||
}
|
||||
|
||||
RowLayout{
|
||||
Kirigami.FormData.label: i18n("Generator") + ":"
|
||||
|
||||
RadioButton {
|
||||
ButtonGroup.group: generator
|
||||
id: mirrors
|
||||
text: i18n("Disabled")
|
||||
enabled: pkg.pacman
|
||||
checked: {
|
||||
plasmoid.configuration.mirrors === "false"
|
||||
}
|
||||
onCheckedChanged: {
|
||||
if (checked) cfg_mirrors = "false"
|
||||
}
|
||||
Component.onCompleted: {
|
||||
if (!checked && !enabled) {
|
||||
checked = true
|
||||
plasmoid.configuration.mirrors = "false"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
ButtonGroup.group: generator
|
||||
text: i18n("Always ask")
|
||||
checked: plasmoid.configuration.mirrors === "alwaysAsk"
|
||||
onCheckedChanged: {
|
||||
if (checked) cfg_mirrors = "alwaysAsk"
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout{
|
||||
RadioButton {
|
||||
ButtonGroup.group: generator
|
||||
text: i18n("Ask if older than")
|
||||
checked: plasmoid.configuration.mirrors === "age"
|
||||
onCheckedChanged: {
|
||||
if (checked) cfg_mirrors = "age"
|
||||
}
|
||||
}
|
||||
|
||||
SpinBox {
|
||||
id: mirrorsAge
|
||||
from: 1
|
||||
to: 999
|
||||
stepSize: 1
|
||||
value: mirrorsAge
|
||||
}
|
||||
|
||||
Label {
|
||||
text: i18np("day", "days", mirrorsAge.value)
|
||||
}
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
ButtonGroup.group: generator
|
||||
text: i18n("No ask, force refresh")
|
||||
checked: plasmoid.configuration.mirrors === "force"
|
||||
onCheckedChanged: {
|
||||
if (checked) cfg_mirrors = "force"
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: Kirigami.Units.smallSpacing * 2
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Protocol") + ":"
|
||||
|
||||
CheckBox {
|
||||
id: http
|
||||
text: "http"
|
||||
onClicked: updateUrl()
|
||||
enabled: mirrorsGeneratorEnabled
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: https
|
||||
text: "https"
|
||||
onClicked: updateUrl()
|
||||
enabled: mirrorsGeneratorEnabled
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("IP version") + ":"
|
||||
|
||||
CheckBox {
|
||||
id: ipv4
|
||||
text: "IPv4"
|
||||
onClicked: updateUrl()
|
||||
enabled: mirrorsGeneratorEnabled
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
id: ipv6
|
||||
text: "IPv6"
|
||||
onClicked: updateUrl()
|
||||
enabled: mirrorsGeneratorEnabled
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
Kirigami.FormData.label: i18n("Mirror status") + ":"
|
||||
id: mirrorstatus
|
||||
text: i18n("Enable")
|
||||
onClicked: updateUrl()
|
||||
enabled: mirrorsGeneratorEnabled
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Number output") + ":"
|
||||
|
||||
SpinBox {
|
||||
id: mirrorCount
|
||||
from: 0
|
||||
to: 10
|
||||
stepSize: 1
|
||||
value: mirrorCount
|
||||
enabled: mirrorsGeneratorEnabled
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("Number of servers to write to mirrorlist file. 0 for all.")
|
||||
}
|
||||
}
|
||||
|
||||
CheckBox {
|
||||
Kirigami.FormData.label: i18n("Show progress") + ":"
|
||||
id: mirrorProgress
|
||||
text: i18n("Enable")
|
||||
enabled: mirrorsGeneratorEnabled
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Country") + ":"
|
||||
enabled: mirrorsGeneratorEnabled
|
||||
|
||||
Label {
|
||||
textFormat: Text.RichText
|
||||
text: {
|
||||
var matchResult = cfg_dynamicUrl.match(/country=([A-Z]+)/g)
|
||||
if (matchResult !== null) {
|
||||
var countries = matchResult.map(str => str.split("=")[1]).join(", ")
|
||||
return countries
|
||||
} else {
|
||||
return '<a style="color: ' + Kirigami.Theme.negativeTextColor + '">' + i18n("Select at least one!") + '</a>'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("You must select at least one country, otherwise all will be chosen by default. <br><br><b>The more countries you select, the longer it will take to generate the mirrors!</b> <br><br>It is optimal to choose <b>1-2</b> countries closest to you.")
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Layout.maximumWidth: archTab.width / 2.5
|
||||
Layout.maximumHeight: 200
|
||||
enabled: mirrorsGeneratorEnabled
|
||||
|
||||
ScrollView {
|
||||
Layout.preferredWidth: archTab.width / 2.5
|
||||
Layout.preferredHeight: 200
|
||||
|
||||
GridLayout {
|
||||
columns: 1
|
||||
|
||||
Repeater {
|
||||
model: countryListModel
|
||||
delegate: CheckBox {
|
||||
text: model.text
|
||||
checked: model.checked
|
||||
onClicked: {
|
||||
model.checked = checked
|
||||
checked ? countryList.push(model.code) : countryList.splice(countryList.indexOf(model.code), 1)
|
||||
updateUrl()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: flatpakTab
|
||||
visible: currentTab === 2
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Uninstall unused") + ":"
|
||||
|
||||
CheckBox {
|
||||
id: flatpakRemoveUnused
|
||||
text: i18n("Enable")
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
ButtonGroup {
|
||||
id: flatpakFlags
|
||||
}
|
||||
|
||||
RowLayout{
|
||||
Kirigami.FormData.label: i18n("Upgrade options") + ":"
|
||||
|
||||
RadioButton {
|
||||
ButtonGroup.group: flatpakFlags
|
||||
text: i18n("Normal")
|
||||
checked: plasmoid.configuration.flatpakFlags === ""
|
||||
onCheckedChanged: {
|
||||
if (checked) cfg_flatpakFlags = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
ButtonGroup.group: flatpakFlags
|
||||
text: i18n("Normal, skip questions")
|
||||
checked: plasmoid.configuration.flatpakFlags === "--assumeyes"
|
||||
onCheckedChanged: {
|
||||
if (checked) cfg_flatpakFlags = "--assumeyes"
|
||||
}
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
ButtonGroup.group: flatpakFlags
|
||||
text: i18n("Non interactive, skip questions")
|
||||
checked: plasmoid.configuration.flatpakFlags === "--noninteractive"
|
||||
onCheckedChanged: {
|
||||
if (checked) cfg_flatpakFlags = "--noninteractive"
|
||||
}
|
||||
}
|
||||
|
||||
RadioButton {
|
||||
ButtonGroup.group: flatpakFlags
|
||||
text: i18n("Verbose")
|
||||
checked: plasmoid.configuration.flatpakFlags === "--verbose"
|
||||
onCheckedChanged: {
|
||||
if (checked) cfg_flatpakFlags = "--verbose"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Kirigami.FormLayout {
|
||||
id: widgetsTab
|
||||
visible: currentTab === 3
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Upgrade confirmation") + ":"
|
||||
|
||||
CheckBox {
|
||||
id: widgetConfirmation
|
||||
text: i18n("Enable")
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Restart plasmashell") + ":"
|
||||
|
||||
CheckBox {
|
||||
id: restartShell
|
||||
text: i18n("Suggest after upgrading")
|
||||
}
|
||||
|
||||
Kirigami.ContextualHelpButton {
|
||||
toolTipText: i18n("After upgrading widget, the old version will still remain in memory until you restart plasmashell. To avoid doing this manually, enable this option.")
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Command") + ":"
|
||||
|
||||
TextField {
|
||||
id: restartCommand
|
||||
enabled: restartShell.checked
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Kirigami.FormData.isSection: true
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
Kirigami.FormData.label: i18n("Apdatifier tray icon") + ":"
|
||||
|
||||
CheckBox {
|
||||
id: trayEnabledByDefault
|
||||
text: i18n("Enabled by default")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
if (tmuxSession.checked && !pkg.tmux) tmuxSession.checked = plasmoid.configuration.tmuxSession = false
|
||||
|
||||
if(cfg_dynamicUrl) {
|
||||
var urlParams = plasmoid.configuration.dynamicUrl.split("?")[1].split("&")
|
||||
|
||||
for (var i = 0; i < urlParams.length; i++) {
|
||||
var param = urlParams[i]
|
||||
if (param.includes("use_mirror_status=on")) mirrorstatus.checked = true
|
||||
if (/protocol=http\b/.test(param)) http.checked = true
|
||||
if (param.includes("protocol=https")) https.checked = true
|
||||
if (param.includes("ip_version=4")) ipv4.checked = true
|
||||
if (param.includes("ip_version=6")) ipv6.checked = true
|
||||
if (param.includes("country=")) {
|
||||
var country = decodeURIComponent(param.split("=")[1])
|
||||
countryList.push(country)
|
||||
for (var j = 0; j < countryListModel.count; ++j) {
|
||||
if (countryListModel.get(j).code === country) {
|
||||
countryListModel.get(j).checked = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateUrl() {
|
||||
var params = ""
|
||||
if (http.checked) params += "&protocol=http"
|
||||
if (https.checked) params += "&protocol=https"
|
||||
if (ipv4.checked) params += "&ip_version=4"
|
||||
if (ipv6.checked) params += "&ip_version=6"
|
||||
if (mirrorstatus.checked) params += "&use_mirror_status=on"
|
||||
|
||||
for (var i = 0; i < countryList.length; i++) {
|
||||
params += "&country=" + countryList[i]
|
||||
}
|
||||
|
||||
var baseUrl = "https://archlinux.org/mirrorlist/?"
|
||||
cfg_dynamicUrl = baseUrl + params.substring(1)
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: countryListModel
|
||||
|
||||
function createCountryList() {
|
||||
let countries =
|
||||
"Australia:AU, Austria:AT, Azerbaijan:AZ, Bangladesh:BD, Belarus:BY, Belgium:BE, " +
|
||||
"Brazil:BR, Bulgaria:BG, Cambodia:KH, Canada:CA, Chile:CL, China:CN, Colombia:CO, " +
|
||||
"Croatia:HR, Czech Republic:CZ, Denmark:DK, Ecuador:EC, Estonia:EE, Finland:FI, " +
|
||||
"France:FR, Georgia:GE, Germany:DE, Greece:GR, Hong Kong:HK, Hungary:HU, Iceland:IS, " +
|
||||
"India:IN, Indonesia:ID, Iran:IR, Israel:IL, Italy:IT, Japan:JP, Kazakhstan:KZ, Kenya:KE, " +
|
||||
"Latvia:LV, Lithuania:LT, Luxembourg:LU, Malaysia:MY, Mauritius:MU, Mexico:MX, Moldova:MD, " +
|
||||
"Morocco:MA, Nepal:NP, Netherlands:NL, New Caledonia:NC, New Zealand:NZ, North Macedonia:MK, " +
|
||||
"Norway:NO, Paraguay:PY, Poland:PL, Portugal:PT, Romania:RO, Russia:RU, Réunion:RE, " +
|
||||
"Saudi Arabia:SA, Serbia:RS, Singapore:SG, Slovakia:SK, Slovenia:SI, South Africa:ZA, " +
|
||||
"South Korea:KR, Spain:ES, Sweden:SE, Switzerland:CH, Taiwan:TW, Thailand:TH, Turkey:TR, " +
|
||||
"Ukraine:UA, United Arab Emirates:AE, United Kingdom:GB, United States:US, Uzbekistan:UZ, Vietnam:VN"
|
||||
|
||||
countries.split(", ").map(item => {
|
||||
let [country, code] = item.split(":")
|
||||
countryListModel.append({text: country, code: code, checked: false})
|
||||
})
|
||||
}
|
||||
|
||||
Component.onCompleted: createCountryList()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtNetwork
|
||||
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
|
||||
import "components"
|
||||
import "representation" as Rep
|
||||
import "../tools/tools.js" as JS
|
||||
|
||||
PlasmoidItem {
|
||||
id: root
|
||||
compactRepresentation: Rep.Panel {}
|
||||
fullRepresentation: Rep.Expanded {
|
||||
Layout.minimumWidth: Kirigami.Units.gridUnit * 24
|
||||
Layout.minimumHeight: Kirigami.Units.gridUnit * 16
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
}
|
||||
|
||||
switchWidth: Kirigami.Units.gridUnit * 24
|
||||
switchHeight: Kirigami.Units.gridUnit * 16
|
||||
|
||||
Plasmoid.busy: plasmoid.location === PlasmaCore.Types.Floating ? false : ((cfg.busyIndicator || "spinner") === "spinner" ? sts.busy : false)
|
||||
Plasmoid.backgroundHints: PlasmaCore.Types.DefaultBackground | PlasmaCore.Types.ConfigurableBackground
|
||||
Plasmoid.icon: plasmoid.configuration.selectedIcon
|
||||
|
||||
function updatePlasmoidStatus() {
|
||||
if (sts.count >= cfg.hideIconPolicy || sts.busy || sts.error || panelConfigurationMode) {
|
||||
Plasmoid.status = PlasmaCore.Types.ActiveStatus
|
||||
} else {
|
||||
Plasmoid.status = inTray ? PlasmaCore.Types.PassiveStatus : PlasmaCore.Types.HiddenStatus
|
||||
}
|
||||
}
|
||||
|
||||
property int hideIconPolicy: cfg.hideIconPolicy
|
||||
property int countUpdates: sts.count
|
||||
onHideIconPolicyChanged: updatePlasmoidStatus()
|
||||
onCountUpdatesChanged: updatePlasmoidStatus()
|
||||
|
||||
toolTipMainText: sts.paused ? i18n("Auto check disabled") : ""
|
||||
toolTipSubText: sts.busy ? sts.statusMsg : sts.checkTimeTooltip
|
||||
|
||||
hideOnWindowDeactivate: !pinned
|
||||
|
||||
property bool isOnline: cfg.checkConn ? (NetworkInformation.reachability === NetworkInformation.Reachability.Online) : true
|
||||
property bool isMetered: NetworkInformation.isMetered && cfg.respectMeteredConn
|
||||
property bool inTray: (plasmoid.containmentDisplayHints & PlasmaCore.Types.ContainmentDrawsPlasmoidHeading)
|
||||
property bool onDesktop: plasmoid.location === PlasmaCore.Types.Floating
|
||||
property bool horizontal: plasmoid.location === PlasmaCore.Types.TopEdge || plasmoid.location === PlasmaCore.Types.BottomEdge
|
||||
property bool panelConfigurationMode: Plasmoid.containment.corona?.editMode ?? false
|
||||
property bool pinned: false
|
||||
property bool listCompactMode: false
|
||||
property var cache: []
|
||||
property string checkMode: plasmoid.configuration.checkMode
|
||||
property bool sorting: plasmoid.configuration.sorting
|
||||
property string rules: plasmoid.configuration.rules || ""
|
||||
property var pkg: plasmoid.configuration.packages || ""
|
||||
property var cfg: plasmoid.configuration
|
||||
property var configuration: JSON.stringify(cfg)
|
||||
|
||||
QtObject {
|
||||
id: sts
|
||||
property bool init: false
|
||||
property var errors: []
|
||||
property int count: 0
|
||||
property double lastCheck: 0
|
||||
property bool busy: true
|
||||
property bool upgrading: false
|
||||
property bool error: !busy && errors.length > 0
|
||||
property bool paused: !busy && !scheduler.running && cfg.checkMode !== "manual"
|
||||
property string statusMsg: ""
|
||||
property string statusIco: ""
|
||||
property string checkTimeTooltip: ""
|
||||
property var proc: null
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: listModel
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: newsModel
|
||||
}
|
||||
|
||||
ListModel {
|
||||
id: activeNewsModel
|
||||
}
|
||||
|
||||
Notification {
|
||||
id: notify
|
||||
}
|
||||
|
||||
Plasmoid.contextualActions: [
|
||||
PlasmaCore.Action {
|
||||
text: i18n("Check updates")
|
||||
icon.name: "view-refresh"
|
||||
enabled: !sts.upgrading
|
||||
onTriggered: sts.proc ? JS.stopCheck() : JS.checkUpdates()
|
||||
},
|
||||
PlasmaCore.Action {
|
||||
text: i18n("Upgrade system")
|
||||
icon.name: "akonadiconsole"
|
||||
enabled: (cfg.terminal && cfg.tmuxSession && sts.count) || (cfg.terminal && !sts.busy && sts.count)
|
||||
onTriggered: JS.upgradeSystem()
|
||||
},
|
||||
PlasmaCore.Action {
|
||||
text: i18n("Management")
|
||||
icon.name: "tools"
|
||||
enabled: cfg.terminal && pkg.pacman
|
||||
onTriggered: JS.management()
|
||||
}
|
||||
]
|
||||
|
||||
Timer {
|
||||
id: scheduler
|
||||
interval: 10 * 1000
|
||||
repeat: true
|
||||
triggeredOnStart: true
|
||||
onTriggered: JS.searchScheduler()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: upgradeTimer
|
||||
interval: 1000
|
||||
repeat: true
|
||||
onTriggered: JS.upgradingState()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: saveTimer
|
||||
interval: 1000
|
||||
onTriggered: JS.saveConfig()
|
||||
}
|
||||
|
||||
onIsOnlineChanged: (!isOnline && sts.proc) && JS.stopCheck()
|
||||
onCheckModeChanged: sts.init && scheduler.restart()
|
||||
onSortingChanged: sts.init && JS.refreshListModel()
|
||||
onRulesChanged: sts.init && JS.refreshListModel()
|
||||
onConfigurationChanged: saveTimer.start()
|
||||
Component.onCompleted: JS.init()
|
||||
}
|
||||
@@ -0,0 +1,396 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.kitemmodels
|
||||
import org.kde.plasma.extras
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.plasma.components
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
|
||||
import "../components"
|
||||
import "../scrollview" as View
|
||||
import "../../tools/tools.js" as JS
|
||||
|
||||
Representation {
|
||||
property string currVersion: "v2.9.9"
|
||||
property bool searchFieldOpen: false
|
||||
property bool expanded: root.expanded
|
||||
onExpandedChanged: {
|
||||
if (plasmoid.configuration.switchDefaultTab && !expanded)
|
||||
listCompactMode = (plasmoid.configuration.defaultTab !== 0)
|
||||
}
|
||||
|
||||
function svg(icon) {
|
||||
return Qt.resolvedUrl("../assets/icons/" + icon + ".svg")
|
||||
}
|
||||
|
||||
property var backgroundHidden: (Plasmoid.formFactor === PlasmaCore.Types.Planar) && (Plasmoid.userBackgroundHints === PlasmaCore.Types.ShadowBackground)
|
||||
onBackgroundHiddenChanged: topHeader.background.visible = bottomHeader.background.visible = !backgroundHidden
|
||||
|
||||
|
||||
property bool activeNewsItems: false
|
||||
function checkActiveNewsItems() {
|
||||
activeNewsItems = false
|
||||
for (let i = 0; i < newsModel.count; ++i) {
|
||||
if (newsModel.get(i).removed === false) {
|
||||
activeNewsItems = true
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: newsModel
|
||||
function onDataChanged() {
|
||||
checkActiveNewsItems()
|
||||
}
|
||||
function onCountChanged() {
|
||||
checkActiveNewsItems()
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: checkActiveNewsItems()
|
||||
|
||||
header: PlasmoidHeading {
|
||||
id: topHeader
|
||||
visible: (cfg.showStatusText || cfg.showToolBar) && !sts.error
|
||||
contentItem: RowLayout {
|
||||
id: toolBar
|
||||
Layout.fillWidth: true
|
||||
Layout.minimumHeight: Kirigami.Units.iconSizes.medium
|
||||
Layout.maximumHeight: Kirigami.Units.iconSizes.medium
|
||||
|
||||
RowLayout {
|
||||
id: status
|
||||
Layout.alignment: cfg.showToolBar ? Qt.AlignLeft : Qt.AlignHCenter
|
||||
spacing: Kirigami.Units.smallSpacing / 2
|
||||
visible: cfg.showStatusText
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.smallMedium
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.smallMedium
|
||||
|
||||
Kirigami.Icon {
|
||||
height: parent.height
|
||||
width: parent.height
|
||||
anchors.centerIn: parent
|
||||
source: cfg.ownIconsUI ? svg(sts.statusIco) : sts.statusIco
|
||||
color: Kirigami.Theme.colorSet
|
||||
scale: cfg.ownIconsUI ? 0.7 : 0.9
|
||||
isMask: cfg.ownIconsUI
|
||||
smooth: true
|
||||
}
|
||||
}
|
||||
|
||||
Label {
|
||||
Layout.maximumWidth: toolBar.width - toolBarButtons.width - Kirigami.Units.iconSizes.smallMedium
|
||||
Layout.alignment: Qt.AlignLeft
|
||||
text: sts.statusMsg
|
||||
elide: Text.ElideRight
|
||||
font.bold: true
|
||||
}
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
id: toolBarButtons
|
||||
Layout.alignment: Qt.AlignRight
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
visible: cfg.showToolBar
|
||||
|
||||
ToolbarButton {
|
||||
id: searchButton
|
||||
tooltipText: i18n("Filter by package name")
|
||||
iconSource: cfg.ownIconsUI ? svg("toolbar_search") : "search"
|
||||
visible: cfg.searchButton && sts.count
|
||||
enabled: visible && swipeView.currentIndex === 0
|
||||
onClicked: {
|
||||
if (searchFieldOpen) searchField.text = ""
|
||||
searchFieldOpen = !searchField.visible
|
||||
searchField.focus = searchFieldOpen
|
||||
}
|
||||
}
|
||||
|
||||
ToolbarButton {
|
||||
tooltipText: sts.paused ? i18n("Enable auto search updates") : i18n("Disable auto search updates")
|
||||
iconSource: cfg.ownIconsUI ? (!sts.paused ? svg("toolbar_pause") : svg("toolbar_start"))
|
||||
: (!sts.paused ? "media-playback-paused" : "media-playback-playing")
|
||||
iconColor: sts.paused ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.colorSet
|
||||
enabled: !sts.busy
|
||||
visible: enabled && cfg.intervalButton && cfg.checkMode !== "manual"
|
||||
onClicked: JS.switchScheduler()
|
||||
}
|
||||
|
||||
ToolbarButton {
|
||||
tooltipText: cfg.sorting ? i18n("Sort packages by name") : i18n("Sort packages by repository")
|
||||
iconSource: cfg.ownIconsUI ? svg("toolbar_sort") : "sort-name"
|
||||
visible: cfg.sortButton && !sts.busy && sts.count
|
||||
enabled: visible && swipeView.currentIndex === 0
|
||||
onClicked: cfg.sorting = !cfg.sorting
|
||||
}
|
||||
|
||||
ToolbarButton {
|
||||
tooltipText: listCompactMode ? i18n("Extended") : i18n("Compact")
|
||||
iconSource: cfg.ownIconsUI ? (listCompactMode ? svg("tab_extended") : svg("tab_compact"))
|
||||
: (listCompactMode ? "view-split-top-bottom" : "view-split-left-right")
|
||||
|
||||
|
||||
visible: cfg.viewButton && sts.count && swipeView.currentIndex === 0
|
||||
enabled: visible
|
||||
onClicked: listCompactMode = !listCompactMode
|
||||
}
|
||||
|
||||
ToolbarButton {
|
||||
id: managementButton
|
||||
tooltipText: i18n("Management")
|
||||
iconSource: cfg.ownIconsUI ? svg("toolbar_management") : "tools"
|
||||
enabled: !sts.busy && pkg.pacman !== "" && cfg.terminal
|
||||
visible: enabled && cfg.managementButton
|
||||
onClicked: { buttonTooltip.hide(); JS.management() }
|
||||
}
|
||||
|
||||
ToolbarButton {
|
||||
id: upgradeButton
|
||||
tooltipText: i18n("Upgrade system")
|
||||
iconSource: cfg.ownIconsUI ? svg("toolbar_upgrade") : "akonadiconsole"
|
||||
enabled: !sts.busy && sts.count && cfg.terminal
|
||||
visible: enabled && cfg.upgradeButton
|
||||
onClicked: { buttonTooltip.hide(); JS.upgradeSystem() }
|
||||
}
|
||||
|
||||
ToolbarButton {
|
||||
tooltipText: sts.busy ? i18n("Stop checking") : i18n("Check updates")
|
||||
iconSource: cfg.ownIconsUI ? (sts.busy ? svg("toolbar_stop") : svg("toolbar_check"))
|
||||
: (sts.busy ? "media-playback-stopped" : "view-refresh")
|
||||
visible: cfg.checkButton && !sts.upgrading
|
||||
onClicked: sts.proc ? JS.stopCheck() : JS.checkUpdates()
|
||||
}
|
||||
|
||||
ToolbarButton {
|
||||
tooltipText: i18n("Open settings")
|
||||
iconSource: cfg.ownIconsUI ? svg("toolbar_settings") : "settings-configure"
|
||||
visible: cfg.settingsButton && !inTray && !sts.busy
|
||||
onClicked: plasmoid.internalAction("configure").trigger()
|
||||
}
|
||||
|
||||
ToolbarButton {
|
||||
tooltipText: pinned ? i18n("Unpin window") : i18n("Keep open")
|
||||
iconSource: cfg.ownIconsUI ? (pinned ? svg("toolbar_unpin") : svg("toolbar_pin"))
|
||||
: (pinned ? "window-unpin" : "window-pin")
|
||||
visible: cfg.pinButton && !inTray && !onDesktop
|
||||
onClicked: pinned = !pinned
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: PlasmoidHeading {
|
||||
id: bottomHeader
|
||||
spacing: 0
|
||||
topPadding: 0
|
||||
height: Kirigami.Units.iconSizes.medium
|
||||
visible: cfg.tabBarVisible && cfg.feedsEnabled && !sts.error
|
||||
|
||||
contentItem: TabBar {
|
||||
id: tabBar
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
position: TabBar.Footer
|
||||
currentIndex: swipeView.currentIndex
|
||||
onCurrentIndexChanged: {
|
||||
swipeView.currentIndex = currentIndex
|
||||
if (swipeView.currentIndex >= 1) {
|
||||
searchFieldOpen = false
|
||||
searchField.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
Repeater {
|
||||
model: [
|
||||
{
|
||||
id: "updates",
|
||||
icon: "status_package",
|
||||
fallback: "kpackagekit-updates",
|
||||
label: i18n("Updates"),
|
||||
},
|
||||
cfg.feedsEnabled ? {
|
||||
id: "news",
|
||||
icon: "status_news",
|
||||
fallback: "news-subscribe",
|
||||
label: i18n("News"),
|
||||
} : null
|
||||
].filter(Boolean)
|
||||
|
||||
delegate: TabButton {
|
||||
required property var modelData
|
||||
contentItem: RowLayout {
|
||||
Kirigami.Theme.inherit: true
|
||||
Item { Layout.fillWidth: true }
|
||||
Kirigami.Icon {
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.small
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.small
|
||||
source: cfg.ownIconsUI ? svg(modelData.icon) : modelData.fallback
|
||||
color: (modelData.id === "news" && activeNewsItems) ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.colorSet
|
||||
isMask: cfg.ownIconsUI
|
||||
smooth: true
|
||||
}
|
||||
Label { text: modelData.label; visible: cfg.tabBarTexts }
|
||||
Item { Layout.fillWidth: true }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Loader {
|
||||
Layout.fillWidth: true
|
||||
active: sts.busy
|
||||
sourceComponent: ProgressBar { from: 0; to: 100; indeterminate: true }
|
||||
}
|
||||
|
||||
TextField {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing * 2
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing * 2
|
||||
Layout.leftMargin: Kirigami.Units.smallSpacing * 2
|
||||
Layout.rightMargin: Kirigami.Units.smallSpacing * 2
|
||||
|
||||
id: searchField
|
||||
clearButtonShown: true
|
||||
visible: searchFieldOpen && sts.count
|
||||
placeholderText: i18n("Filter by package name")
|
||||
onTextChanged: modelList.setFilterFixedString(text)
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing * 2
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing * 2
|
||||
text: "<b>" + i18n("Check out release notes")+" "+currVersion+"</b>"
|
||||
type: Kirigami.MessageType.Positive
|
||||
visible: !searchFieldOpen && isOnline &&
|
||||
plasmoid.configuration.version.localeCompare(currVersion, undefined, { numeric: true, sensitivity: 'base' }) < 0
|
||||
|
||||
actions: [
|
||||
Kirigami.Action {
|
||||
tooltip: i18n("Select...")
|
||||
icon.name: "application-menu"
|
||||
expandible: true
|
||||
|
||||
Kirigami.Action {
|
||||
text: "GitHub"
|
||||
icon.name: "internet-web-browser-symbolic"
|
||||
onTriggered: Qt.openUrlExternally("https://github.com/exequtic/apdatifier/releases")
|
||||
}
|
||||
Kirigami.Action {
|
||||
text: i18n("Dismiss")
|
||||
icon.name: "dialog-close"
|
||||
onTriggered: plasmoid.configuration.version = currVersion
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
Kirigami.InlineMessage {
|
||||
Layout.fillWidth: true
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing * 2
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing * 2
|
||||
text: "<b>" + i18n("No internet connection") + "</b>"
|
||||
type: Kirigami.MessageType.Error
|
||||
visible: !isOnline
|
||||
}
|
||||
|
||||
SwipeView {
|
||||
id: swipeView
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
currentIndex: 0
|
||||
Repeater {
|
||||
model: [
|
||||
{ component: "updates" },
|
||||
cfg.feedsEnabled ? { component: "news" } : null
|
||||
].filter(Boolean)
|
||||
|
||||
delegate: Loader {
|
||||
required property var modelData
|
||||
sourceComponent: modelData.component === "updates" ? updatesComp : newsComp
|
||||
}
|
||||
}
|
||||
|
||||
Component { id: updatesComp; View.Updates {} }
|
||||
Component { id: newsComp; View.News { id: newsPage } }
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
z: 9998
|
||||
anchors.fill: parent
|
||||
color: Kirigami.Theme.backgroundColor
|
||||
visible: errorLoader.active
|
||||
}
|
||||
Loader {
|
||||
z: 9999
|
||||
id: errorLoader
|
||||
anchors.centerIn: parent
|
||||
active: !sts.busy && sts.error
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: Kirigami.Units.largeSpacing * 2
|
||||
Layout.fillWidth: true
|
||||
|
||||
RowLayout {
|
||||
Kirigami.Icon {
|
||||
Layout.preferredWidth: Kirigami.Units.iconSizes.small
|
||||
Layout.preferredHeight: Kirigami.Units.iconSizes.small
|
||||
source: cfg.ownIconsUI ? svg("status_error") : "error"
|
||||
isMask: cfg.ownIconsUI
|
||||
}
|
||||
Kirigami.Heading {
|
||||
text: i18np("%1 error occurred", "%1 errors occurred", sts.errors.length)
|
||||
type: Kirigami.Heading.Primary
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
}
|
||||
|
||||
ScrollView {
|
||||
Layout.preferredWidth: Kirigami.Units.gridUnit * 20
|
||||
Layout.preferredHeight: Kirigami.Units.gridUnit * 8
|
||||
|
||||
TextArea {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
readOnly: true
|
||||
wrapMode: Text.Wrap
|
||||
font.family: "Monospace"
|
||||
font.pointSize: Kirigami.Theme.smallFont.pointSize
|
||||
text: sts.errors.map(err => err?.type ? `${err.type}: ${err.message}` : `${err.message}`).join("\n\n")
|
||||
color: Kirigami.Theme.textColor
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
icon.name: "checkmark"
|
||||
text: "OK"
|
||||
onClicked: {
|
||||
sts.errors = []
|
||||
JS.setStatusBar()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
KSortFilterProxyModel {
|
||||
id: modelList
|
||||
sourceModel: listModel
|
||||
filterRoleName: "name"
|
||||
filterRowCallback: (sourceRow, sourceParent) => {
|
||||
return sourceModel.data(sourceModel.index(sourceRow, 0, sourceParent), filterRole).includes(searchField.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import org.kde.plasma.plasmoid
|
||||
import org.kde.kirigami as Kirigami
|
||||
import org.kde.plasma.core as PlasmaCore
|
||||
import Qt5Compat.GraphicalEffects
|
||||
|
||||
import "../components" as QQC
|
||||
import "../../tools/tools.js" as JS
|
||||
|
||||
MouseArea {
|
||||
id: mouseArea
|
||||
|
||||
property bool wasExpanded: false
|
||||
|
||||
readonly property bool counterSide: !inTray && horizontal && plasmoid.configuration.counterMode === "side"
|
||||
readonly property bool counterEnabled: plasmoid.configuration.counterMode !== "disabled"
|
||||
readonly property bool counterCenter: plasmoid.configuration.counterBadgePosition === "center"
|
||||
readonly property bool pauseBadgeEnabled: plasmoid.configuration.pauseBadgePosition !== "disabled"
|
||||
readonly property bool updatedBadgeEnabled: plasmoid.configuration.updatedBadgePosition !== "disabled"
|
||||
|
||||
readonly property bool onDesktop: plasmoid.location === PlasmaCore.Types.Floating
|
||||
readonly property bool pulseActive: (plasmoid.configuration.busyIndicator === "pulse") && sts.busy && !onDesktop
|
||||
|
||||
function darkColor(color) {
|
||||
return Kirigami.ColorUtils.brightnessForColor(color) === Kirigami.ColorUtils.Dark
|
||||
}
|
||||
readonly property bool isDarkText: darkColor(Kirigami.Theme.textColor)
|
||||
readonly property color lightText: isDarkText ? Kirigami.Theme.backgroundColor : Kirigami.Theme.textColor
|
||||
readonly property color darkText: isDarkText ? Kirigami.Theme.textColor : Kirigami.Theme.backgroundColor
|
||||
|
||||
readonly property string errorIcon: cfg.ownIconsUI ? "status_error" : "error"
|
||||
readonly property string updatedIcon: cfg.ownIconsUI ? "status_updated" : "checkmark"
|
||||
readonly property string pausedIcon: cfg.ownIconsUI ? "toolbar_pause" : "media-playback-paused"
|
||||
|
||||
readonly property real panelIconSize: horizontal ? parent.height : parent.width
|
||||
readonly property real iconSize: inTray ? Math.min(parent.width, parent.height) : panelIconSize
|
||||
readonly property real counterPixelSize: Math.max(iconSize / 4, iconSize * (cfg.counterFontSize / 10))
|
||||
|
||||
implicitHeight: iconSize
|
||||
implicitWidth: viewLoader.item ? viewLoader.item.implicitWidth : iconSize
|
||||
|
||||
Layout.preferredHeight: implicitHeight
|
||||
Layout.minimumHeight: implicitHeight
|
||||
Layout.preferredWidth: implicitWidth
|
||||
Layout.minimumWidth: implicitWidth
|
||||
|
||||
hoverEnabled: true
|
||||
acceptedButtons: cfg.rightAction ? Qt.AllButtons : Qt.LeftButton | Qt.MiddleButton
|
||||
|
||||
onEntered: sts.checkTimeTooltip = JS.getCheckTime()
|
||||
|
||||
onPressed: mouse => {
|
||||
wasExpanded = expanded
|
||||
if (!cfg.rightAction && mouse.button == Qt.RightButton) mouse.accepted = false
|
||||
}
|
||||
|
||||
onClicked: mouse => {
|
||||
if (mouse.button == Qt.LeftButton) expanded = !wasExpanded
|
||||
if (mouse.button == Qt.MiddleButton && cfg.middleAction) JS[cfg.middleAction]()
|
||||
if (mouse.button == Qt.RightButton && cfg.rightAction) JS[cfg.rightAction]()
|
||||
}
|
||||
|
||||
WheelHandler {
|
||||
acceptedDevices: PointerDevice.Mouse | PointerDevice.TouchPad
|
||||
onWheel: (event) => {
|
||||
if (scrollTimer.running) return
|
||||
scrollTimer.start()
|
||||
var action = event.angleDelta.y > 0 ? cfg.scrollUpAction : cfg.scrollDownAction
|
||||
if (!action) return
|
||||
JS[action]()
|
||||
}
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: scrollTimer
|
||||
interval: 500
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: viewLoader
|
||||
anchors.fill: parent
|
||||
sourceComponent: counterSide ? iconWithCounterSide : iconWithCounterBadge
|
||||
}
|
||||
|
||||
Component {
|
||||
id: iconWithCounterSide
|
||||
|
||||
RowLayout {
|
||||
implicitHeight: mouseArea.iconSize
|
||||
layoutDirection: cfg.counterSidePosition === "left" ? Qt.RightToLeft : Qt.LeftToRight
|
||||
spacing: 0
|
||||
anchors.centerIn: parent
|
||||
|
||||
Item { Layout.preferredWidth: Kirigami.Units.smallSpacing + cfg.counterGapsOuter }
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: mouseArea.iconSize
|
||||
Layout.preferredWidth: mouseArea.iconSize
|
||||
|
||||
Kirigami.Icon {
|
||||
id: panelIcon
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
source: JS.setIcon(plasmoid.icon)
|
||||
active: mouseArea.containsMouse
|
||||
|
||||
layer.enabled: sts.error
|
||||
layer.effect: sts.error ? errorShadowEffect : null
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
sourceComponent: badgesLayer
|
||||
active: sts.init
|
||||
}
|
||||
}
|
||||
|
||||
QQC.Pulse {
|
||||
target: panelIcon
|
||||
running: mouseArea.pulseActive
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredWidth: cfg.counterGapsInner
|
||||
visible: counterText.visible
|
||||
}
|
||||
|
||||
Label {
|
||||
id: counterText
|
||||
visible: mouseArea.counterEnabled && !sts.busy && sts.count
|
||||
font.family: plasmoid.configuration.counterFontFamily || Kirigami.Theme.defaultFont.family
|
||||
font.pixelSize: Math.round(mouseArea.counterPixelSize)
|
||||
font.bold: cfg.counterFontBold
|
||||
fontSizeMode: Text.FixedSize
|
||||
text: sts.count
|
||||
}
|
||||
|
||||
Item { Layout.preferredWidth: Kirigami.Units.smallSpacing + cfg.counterGapsOuter }
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: iconWithCounterBadge
|
||||
|
||||
Item {
|
||||
implicitWidth: mouseArea.iconSize
|
||||
implicitHeight: mouseArea.iconSize
|
||||
|
||||
Item {
|
||||
width: mouseArea.iconSize
|
||||
height: mouseArea.iconSize
|
||||
anchors.centerIn: parent
|
||||
|
||||
Kirigami.Icon {
|
||||
id: trayIcon
|
||||
anchors.fill: parent
|
||||
source: JS.setIcon(plasmoid.icon)
|
||||
active: mouseArea.containsMouse
|
||||
|
||||
layer.enabled: sts.error
|
||||
layer.effect: sts.error ? errorShadowEffect : null
|
||||
}
|
||||
|
||||
QQC.Pulse {
|
||||
target: trayIcon
|
||||
running: mouseArea.pulseActive
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: frame
|
||||
anchors.centerIn: parent
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
opacity: 0
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: counterFrame
|
||||
width: mouseArea.counterCenter ? frame.width : counter.implicitWidth + 2
|
||||
height: mouseArea.counterCenter ? frame.height : counter.implicitHeight
|
||||
radius: cfg.counterRadius
|
||||
opacity: cfg.counterOpacity / 10
|
||||
color: cfg.counterColor ? cfg.counterColor : Kirigami.Theme.backgroundColor
|
||||
visible: mouseArea.counterEnabled && !sts.busy && sts.count
|
||||
|
||||
layer.enabled: true
|
||||
layer.effect: DropShadow {
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 0
|
||||
radius: 2
|
||||
samples: radius * 2
|
||||
color: Qt.rgba(0, 0, 0, 0.5)
|
||||
}
|
||||
|
||||
state: cfg.counterBadgePosition
|
||||
|
||||
states: [
|
||||
State { name: "center"; AnchorChanges { target: counterFrame; anchors.horizontalCenter: frame.horizontalCenter; anchors.verticalCenter: frame.verticalCenter } },
|
||||
State { name: "topLeft"; AnchorChanges { target: counterFrame; anchors.top: frame.top; anchors.left: frame.left } },
|
||||
State { name: "topRight"; AnchorChanges { target: counterFrame; anchors.top: frame.top; anchors.right: frame.right } },
|
||||
State { name: "bottomLeft"; AnchorChanges { target: counterFrame; anchors.bottom: frame.bottom; anchors.left: frame.left } },
|
||||
State { name: "bottomRight"; AnchorChanges { target: counterFrame; anchors.bottom: frame.bottom; anchors.right: frame.right } }
|
||||
]
|
||||
|
||||
anchors {
|
||||
topMargin: state.includes("top") ? cfg.counterOffsetX : 0
|
||||
leftMargin: state.includes("Left") ? cfg.counterOffsetY : 0
|
||||
rightMargin: state.includes("Right") ? cfg.counterOffsetY : 0
|
||||
bottomMargin: state.includes("bottom") ? cfg.counterOffsetX : 0
|
||||
}
|
||||
|
||||
transitions: Transition { AnchorAnimation { duration: 120 } }
|
||||
}
|
||||
|
||||
Label {
|
||||
id: counter
|
||||
anchors.centerIn: counterFrame
|
||||
text: sts.count
|
||||
font.family: plasmoid.configuration.counterFontFamily || Kirigami.Theme.defaultFont.family
|
||||
font.pixelSize: Math.round(mouseArea.counterPixelSize)
|
||||
font.bold: cfg.counterFontBold
|
||||
color: cfg.counterColor ? (mouseArea.darkColor(counterFrame.color) ? mouseArea.lightText : mouseArea.darkText) : Kirigami.Theme.textColor
|
||||
visible: counterFrame.visible
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.fill: parent
|
||||
sourceComponent: badgesLayer
|
||||
active: sts.init
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: badgesLayer
|
||||
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
|
||||
QQC.Badge {
|
||||
iconName: updatedIcon
|
||||
position: cfg.updatedBadgePosition
|
||||
iconColor: Kirigami.Theme.positiveTextColor
|
||||
visible: !sts.busy && !sts.count && updatedBadgeEnabled
|
||||
}
|
||||
QQC.Badge {
|
||||
iconName: pausedIcon
|
||||
position: cfg.pauseBadgePosition
|
||||
iconColor: Kirigami.Theme.neutralTextColor
|
||||
visible: sts.paused && pauseBadgeEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: errorShadowEffect
|
||||
DropShadow {
|
||||
horizontalOffset: 0
|
||||
verticalOffset: 0
|
||||
radius: 6
|
||||
samples: 16
|
||||
color: "red"
|
||||
spread: 0.35
|
||||
// transparentBorder: true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls as Controls
|
||||
|
||||
import org.kde.plasma.components
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import "../../tools/tools.js" as JS
|
||||
import "../components"
|
||||
|
||||
Item {
|
||||
ListView {
|
||||
model: newsModel
|
||||
spacing: Kirigami.Units.largeSpacing * 2
|
||||
topMargin: spacing
|
||||
rightMargin: spacing
|
||||
leftMargin: spacing
|
||||
bottomMargin: spacing
|
||||
anchors.fill: parent
|
||||
|
||||
ScrollBar.vertical: Scroll {
|
||||
policy: newsModel.count > 0 ? ScrollBar.AsNeeded : ScrollBar.AlwaysOff
|
||||
}
|
||||
|
||||
delegate: Kirigami.AbstractCard {
|
||||
contentItem: Item {
|
||||
implicitWidth: delegateLayout.implicitWidth
|
||||
implicitHeight: delegateLayout.implicitHeight
|
||||
GridLayout {
|
||||
id: delegateLayout
|
||||
anchors {
|
||||
left: parent.left
|
||||
top: parent.top
|
||||
right: parent.right
|
||||
}
|
||||
rowSpacing: Kirigami.Units.largeSpacing
|
||||
columnSpacing: Kirigami.Units.largeSpacing
|
||||
columns: width > Kirigami.Units.gridUnit * 20 ? 4 : 2
|
||||
opacity: model.removed ? 0.5 : 1.0
|
||||
Kirigami.Icon {
|
||||
Layout.fillHeight: true
|
||||
Layout.maximumHeight: Kirigami.Units.iconSizes.huge
|
||||
Layout.preferredWidth: height
|
||||
source: model.title.includes("Arch") ? "apdatifier-plasmoid" :
|
||||
model.title.includes("Plasma") ? "note-symbolic" :
|
||||
model.title.includes("Apps") ? "applications-all-symbolic" :
|
||||
model.title.includes("Community") ? "start-here-kde-plasma-symbolic"
|
||||
: "news-subscribe"
|
||||
}
|
||||
|
||||
RowLayout {
|
||||
ColumnLayout {
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: model.title
|
||||
font.bold: true
|
||||
}
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: Qt.formatDateTime(new Date(model.timestamp * 1000), Qt.DefaultLocaleShortDate)
|
||||
opacity: 0.6
|
||||
}
|
||||
Kirigami.Separator {
|
||||
Layout.fillWidth: true
|
||||
}
|
||||
Controls.Label {
|
||||
Layout.fillWidth: true
|
||||
wrapMode: Text.WordWrap
|
||||
text: model.article
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Controls.Button {
|
||||
ToolTip { id: tip; text: i18n("Read article") }
|
||||
icon.name: "internet-web-browser-symbolic"
|
||||
onClicked: {
|
||||
tip.hide()
|
||||
Qt.openUrlExternally(model.link)
|
||||
JS.removeNewsItem(index)
|
||||
}
|
||||
}
|
||||
Controls.Button {
|
||||
visible: !model.removed
|
||||
ToolTip { text: i18n("Mark as read") }
|
||||
icon.name: model.removed ? "backup" : "delete"
|
||||
onClicked: JS.removeNewsItem(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
active: newsModel.count === 0
|
||||
sourceComponent: Kirigami.PlaceholderMessage {
|
||||
icon.name: "news-subscribe"
|
||||
text: i18n("No unread news")
|
||||
}
|
||||
}
|
||||
|
||||
add: Transition { animations: [ NumberAnimation { properties: "opacity,scale"; from: 0; to: 1; duration: 380; easing.type: Easing.OutBack; easing.overshoot: 1.12 } ] }
|
||||
addDisplaced: Transition { animations: [
|
||||
NumberAnimation { property: "y"; duration: 340; easing.type: Easing.OutCubic },
|
||||
NumberAnimation { properties: "opacity,scale"; to: 1; duration: 340; easing.type: Easing.OutBack; easing.overshoot: 1.08 }
|
||||
] }
|
||||
displaced: Transition { animations: [
|
||||
NumberAnimation { property: "y"; duration: 340; easing.type: Easing.OutBack; easing.overshoot: 1.05 },
|
||||
NumberAnimation { properties: "opacity,scale"; to: 1; duration: 260; easing.type: Easing.OutQuad }
|
||||
] }
|
||||
move: Transition { animations: [
|
||||
NumberAnimation { property: "y"; duration: 360; easing.type: Easing.OutBack; easing.overshoot: 1.06 },
|
||||
NumberAnimation { properties: "opacity,scale"; to: 1; duration: 260; easing.type: Easing.OutQuad }
|
||||
] }
|
||||
moveDisplaced: Transition { animations: [
|
||||
NumberAnimation { property: "y"; duration: 340; easing.type: Easing.OutBack; easing.overshoot: 1.04 },
|
||||
NumberAnimation { properties: "opacity,scale"; to: 1; duration: 260; easing.type: Easing.OutQuad }
|
||||
] }
|
||||
remove: Transition { animations: [
|
||||
NumberAnimation { property: "x"; to: width + 120; duration: 300; easing.type: Easing.InBack; easing.overshoot: 1.15 },
|
||||
NumberAnimation { property: "opacity"; to: 0; duration: 240; easing.type: Easing.InQuad },
|
||||
NumberAnimation { property: "scale"; to: 0.92; duration: 300; easing.type: Easing.InBack }
|
||||
] }
|
||||
removeDisplaced: Transition { animations: [
|
||||
NumberAnimation { property: "y"; duration: 340; easing.type: Easing.OutBack; easing.overshoot: 1.03 },
|
||||
NumberAnimation { properties: "opacity,scale"; to: 1; duration: 260; easing.type: Easing.OutQuad }
|
||||
] }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,409 @@
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import QtQuick.Controls
|
||||
|
||||
import org.kde.ksvg
|
||||
import org.kde.plasma.extras
|
||||
import org.kde.kirigami as Kirigami
|
||||
|
||||
import "../../tools/tools.js" as JS
|
||||
import "../components"
|
||||
|
||||
Item {
|
||||
id: view
|
||||
|
||||
property bool compactMode: listCompactMode
|
||||
clip: true
|
||||
|
||||
ListView {
|
||||
id: listView
|
||||
|
||||
ScrollBar.vertical: Scroll {
|
||||
policy: !sts.count ? ScrollBar.AlwaysOff : ScrollBar.AsNeeded
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
model: modelList
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
spacing: view.compactMode ? Kirigami.Units.smallSpacing / 2 : Kirigami.Units.smallSpacing
|
||||
|
||||
delegate: Item {
|
||||
width: listView.width
|
||||
height: delegateItem.height
|
||||
|
||||
Rectangle {
|
||||
id: delegateItem
|
||||
|
||||
anchors {
|
||||
left: parent.left
|
||||
right: parent.right
|
||||
leftMargin: Kirigami.Units.largeSpacing
|
||||
rightMargin: Kirigami.Units.largeSpacing
|
||||
}
|
||||
|
||||
height: mainColumn.implicitHeight
|
||||
clip: true
|
||||
color: "transparent"
|
||||
|
||||
property bool expanded: false
|
||||
property bool hovered: false
|
||||
property var pkg: []
|
||||
|
||||
Behavior on height {
|
||||
NumberAnimation {
|
||||
duration: 220
|
||||
easing.type: Easing.OutBack
|
||||
easing.overshoot: 0.7
|
||||
}
|
||||
}
|
||||
|
||||
HoverHandler {
|
||||
id: hoverHandler
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onHoveredChanged: delegateItem.hovered = hovered
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: delegateItem.expanded = !delegateItem.expanded
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
property color hColor: Kirigami.Theme.highlightColor
|
||||
property bool highlighted: (delegateItem.hovered && !listView.moving) || delegateItem.expanded
|
||||
|
||||
anchors.fill: parent
|
||||
radius: Kirigami.Units.cornerRadius
|
||||
color: highlighted ? Qt.rgba(hColor.r, hColor.g, hColor.b, 0.4) : "transparent"
|
||||
border.width: highlighted ? 1 : 0
|
||||
border.color: Qt.darker(Qt.rgba(hColor.r, hColor.g, hColor.b, 0.8), 2)
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: 100
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: mainColumn
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
|
||||
RowLayout {
|
||||
id: mainRow
|
||||
spacing: Kirigami.Units.largeSpacing
|
||||
|
||||
Kirigami.Icon {
|
||||
id: pkgIcon
|
||||
Layout.preferredWidth: view.compactMode ? Kirigami.Units.iconSizes.small : Kirigami.Units.iconSizes.medium
|
||||
Layout.preferredHeight: view.compactMode ? Kirigami.Units.iconSizes.small : Kirigami.Units.iconSizes.medium
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
source: model.IC
|
||||
isMask: false
|
||||
}
|
||||
|
||||
// Extended
|
||||
RowLayout {
|
||||
visible: !view.compactMode
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
Layout.topMargin: Kirigami.Units.smallSpacing
|
||||
Layout.bottomMargin: Kirigami.Units.smallSpacing
|
||||
|
||||
ColumnLayout {
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: model.NM
|
||||
color: model.IM ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.textColor
|
||||
elide: Text.ElideRight
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.fillWidth: true
|
||||
sourceComponent: versionDiffLabel
|
||||
onLoaded: {
|
||||
item.oldVer = model.VO
|
||||
item.newVer = model.VN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
Loader {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
sourceComponent: infoChip
|
||||
active: cfg.extendedShowRepository !== false
|
||||
visible: active
|
||||
onLoaded: item.text = model.RE
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.alignment: Qt.AlignRight | Qt.AlignVCenter
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
sourceComponent: infoChip
|
||||
active: model.RN && cfg.extendedShowInstallReason !== false
|
||||
visible: active
|
||||
onLoaded: {
|
||||
item.text = model.RN
|
||||
item.reason = model.RN || ""
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Compact
|
||||
RowLayout {
|
||||
visible: view.compactMode
|
||||
spacing: Kirigami.Units.smallSpacing
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
|
||||
Label {
|
||||
Layout.fillWidth: true
|
||||
text: model.NM
|
||||
color: model.IM ? Kirigami.Theme.negativeTextColor : Kirigami.Theme.textColor
|
||||
elide: Text.ElideRight
|
||||
font.weight: Font.DemiBold
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
sourceComponent: infoChip
|
||||
active: (cfg.compactInfo || "repository") === "repository"
|
||||
visible: active
|
||||
onLoaded: item.text = model.RE
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
sourceComponent: infoChip
|
||||
active: model.RN && (cfg.compactInfo || "repository") === "installReason"
|
||||
visible: active
|
||||
onLoaded: {
|
||||
item.text = model.RN
|
||||
item.reason = model.RN || ""
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
Layout.fillWidth: true
|
||||
sourceComponent: versionDiffLabel
|
||||
active: (cfg.compactInfo || "repository") === "versionDiff"
|
||||
visible: active
|
||||
onLoaded: {
|
||||
item.oldVer = model.VO
|
||||
item.newVer = model.VN
|
||||
item.hAlign = Text.AlignRight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
id: detailsLoader
|
||||
Layout.fillWidth: true
|
||||
active: delegateItem.expanded
|
||||
visible: active
|
||||
|
||||
sourceComponent: ColumnLayout {
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
Layout.topMargin: Kirigami.Units.largeSpacing
|
||||
}
|
||||
|
||||
GridLayout {
|
||||
id: details
|
||||
width: parent.width
|
||||
columns: 2
|
||||
rowSpacing: Kirigami.Units.smallSpacing / 4
|
||||
columnSpacing: 40
|
||||
Layout.leftMargin: Kirigami.Units.largeSpacing
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: delegateItem.pkg.length
|
||||
|
||||
delegate: Label {
|
||||
property bool header: !(index % 2)
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignTop
|
||||
horizontalAlignment: Text.AlignLeft
|
||||
font: Kirigami.Theme.smallFont
|
||||
opacity: header ? 0.6 : 1
|
||||
text: {
|
||||
if (header)
|
||||
return "<b>" + delegateItem.pkg[index] + ":</b>"
|
||||
if (delegateItem.pkg[index].indexOf("://") !== -1)
|
||||
return "<a href=\"" + delegateItem.pkg[index] + "\">" + delegateItem.pkg[index] + "</a>"
|
||||
return delegateItem.pkg[index]
|
||||
}
|
||||
textFormat: header ? Text.StyledText : delegateItem.pkg[index].indexOf("://") !== -1 ? Text.StyledText : Text.PlainText
|
||||
wrapMode: header ? Text.NoWrap : Text.WordWrap
|
||||
onLinkActivated: Qt.openUrlExternally(link)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: Kirigami.Units.smallSpacing * 2
|
||||
}
|
||||
|
||||
ToolButton {
|
||||
id: upgradeButton
|
||||
// Layout.alignment: Qt.AlignHCenter
|
||||
Layout.alignment: Qt.AlignRight
|
||||
Layout.rightMargin: Kirigami.Units.largeSpacing
|
||||
visible: (model.ID || model.CN) && cfg.terminal
|
||||
icon.name: "edit-download"
|
||||
text: i18n("Upgrade package")
|
||||
onClicked: JS.upgradePackage(model.NM, model.ID, model.CN)
|
||||
}
|
||||
|
||||
Item {
|
||||
Layout.preferredHeight: Kirigami.Units.smallSpacing * 2
|
||||
visible: upgradeButton.visible
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
const F = [
|
||||
["TP", i18n("Package type")],
|
||||
["DE", i18n("Description")],
|
||||
["AU", i18n("Author")],
|
||||
["RE", i18n("Repository")],
|
||||
["LN", "URL"],
|
||||
["ID", i18n("App ID")],
|
||||
["BR", i18n("Branch")],
|
||||
["CM", i18n("Commit")],
|
||||
["RT", i18n("Runtime")],
|
||||
["GR", i18n("Groups")],
|
||||
["PR", i18n("Provides")],
|
||||
["DP", i18n("Depends on")],
|
||||
["RQ", i18n("Required by")],
|
||||
["CF", i18n("Conflicts with")],
|
||||
["RP", i18n("Replaces")],
|
||||
["IS", i18n("Installed size")],
|
||||
["DS", i18n("Download size")],
|
||||
["DT", i18n("Install date")],
|
||||
["RN", i18n("Install reason")]
|
||||
]
|
||||
|
||||
const details = []
|
||||
for (const [k, l] of F) {
|
||||
if (!model[k]) continue
|
||||
|
||||
let value = model[k]
|
||||
|
||||
if (k === "RN") {
|
||||
value = model.RN === "explicit" ? i18n("Explicitly installed")
|
||||
: model.RN === "dependency" ? i18n("Installed as a dependency")
|
||||
: i18n("Orphaned dependency")
|
||||
}
|
||||
|
||||
details.push(l, value)
|
||||
}
|
||||
|
||||
delegateItem.pkg = details
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loader {
|
||||
anchors.centerIn: parent
|
||||
width: parent.width - (Kirigami.Units.largeSpacing * 4)
|
||||
|
||||
active: !sts.count
|
||||
sourceComponent: Kirigami.PlaceholderMessage {
|
||||
width: parent.width - (Kirigami.Units.largeSpacing * 4)
|
||||
icon.name: "checkmark"
|
||||
text: i18n("System updated")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: infoChip
|
||||
|
||||
Rectangle {
|
||||
property alias text: label.text
|
||||
property string reason: ""
|
||||
readonly property color bgColor: {
|
||||
if (reason) {
|
||||
if (!cfg.colorizeInstallReason)
|
||||
return Kirigami.Theme.highlightColor
|
||||
else if (reason === "explicit")
|
||||
return Kirigami.Theme.positiveBackgroundColor
|
||||
else if (reason === "dependency")
|
||||
return Kirigami.Theme.neutralBackgroundColor
|
||||
else
|
||||
return Kirigami.Theme.negativeBackgroundColor
|
||||
}
|
||||
|
||||
return Kirigami.Theme.highlightColor
|
||||
}
|
||||
|
||||
radius: 10
|
||||
color: Qt.rgba(bgColor.r, bgColor.g, bgColor.b, 0.3)
|
||||
implicitWidth: label.contentWidth + 16
|
||||
implicitHeight: label.contentHeight
|
||||
|
||||
border.width: 1
|
||||
border.color: Qt.lighter(Qt.rgba(bgColor.r, bgColor.g, bgColor.b, 0.5), 1.2)
|
||||
|
||||
Label {
|
||||
id: label
|
||||
anchors.centerIn: parent
|
||||
font: Kirigami.Theme.smallFont
|
||||
color: Qt.lighter(bgColor, 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Component {
|
||||
id: versionDiffLabel
|
||||
|
||||
Label {
|
||||
property string oldVer: ""
|
||||
property string newVer: ""
|
||||
property int hAlign: Text.AlignLeft
|
||||
|
||||
horizontalAlignment: hAlign
|
||||
text: cfg.highlightVersionDiff ? versionDiff(oldVer, newVer) : `${oldVer} → ${newVer}`
|
||||
font: Kirigami.Theme.smallFont
|
||||
textFormat: Text.StyledText
|
||||
elide: Text.ElideRight
|
||||
opacity: 0.7
|
||||
|
||||
function versionDiff(oldVer, newVer) {
|
||||
if (oldVer === newVer) return oldVer
|
||||
|
||||
let i = 0
|
||||
|
||||
while (i < oldVer.length &&
|
||||
i < newVer.length &&
|
||||
oldVer[i] === newVer[i]) {
|
||||
++i
|
||||
}
|
||||
|
||||
const prefix = oldVer.slice(0, i)
|
||||
const oldDiff = oldVer.slice(i)
|
||||
const newDiff = newVer.slice(i)
|
||||
|
||||
return `${prefix}<font color="${Kirigami.Theme.negativeTextColor}">${oldDiff}</font> → ${prefix}<font color="${Kirigami.Theme.positiveTextColor}">${newDiff}</font>`
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"KPlugin": {
|
||||
"Authors": [
|
||||
{
|
||||
"Name": "Evgeny Kazantsev",
|
||||
"Email": "exequtic@gmail.com"
|
||||
}
|
||||
],
|
||||
"Category": "System Information",
|
||||
"Description": "Arch Update Notifier",
|
||||
"Icon": "apdatifier-plasmoid",
|
||||
"Id": "com.github.exequtic.apdatifier",
|
||||
"Name": "Apdatifier",
|
||||
"EnabledByDefault": false,
|
||||
"Version": "2.9.9",
|
||||
"License": "MIT",
|
||||
"BugReportUrl": "https://github.com/exequtic/apdatifier/issues",
|
||||
"Website": "https://github.com/exequtic/apdatifier"
|
||||
},
|
||||
"KPackageStructure": "Plasma/Applet",
|
||||
"X-Plasma-API-Minimum-Version": "6.0",
|
||||
"X-Plasma-NotificationArea": "true",
|
||||
"X-Plasma-NotificationAreaCategory": "SystemServices"
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
> Version 7 of Zren's i18n scripts.
|
||||
|
||||
## New Translations
|
||||
|
||||
Fill out [`template.pot`](template.pot) with your translations then open a [new issue](https://github.com/exequtic/apdatifier/issues/new), name the file with the extension `.txt`, attach the txt file to the issue (drag and drop).
|
||||
|
||||
Or if you know how to make a pull request:
|
||||
|
||||
Copy the [`template.pot`](template.pot) file to [`./po`](po) directory and name it your locale's code (Eg: `en`/`de`/`fr`) with the extension `.po`. Then fill out all the `msgstr ""`.
|
||||
|
||||
## Scripts
|
||||
|
||||
* `sh ./merge` will parse the `i18n()` calls in the `*.qml` files and write it to the `template.pot` file. Then it will merge any changes into the `*.po` language files.
|
||||
* `sh ./build` will convert the `*.po` files to it's binary `*.mo` version and move it to `contents/locale/...`
|
||||
|
||||
## Links
|
||||
|
||||
* https://zren.github.io/kde/docs/widget/#translations-i18n
|
||||
* https://github.com/Zren/plasma-applet-lib/tree/master/package/translate
|
||||
|
||||
## Status
|
||||
| Locale | Lines | % Done|
|
||||
|----------|---------|-------|
|
||||
| Template | 330 | |
|
||||
| de | 261/330 | 79% |
|
||||
| es | 261/330 | 79% |
|
||||
| fr | 316/330 | 95% |
|
||||
| hu_HU | 306/330 | 92% |
|
||||
| ko | 261/330 | 79% |
|
||||
| nl | 261/330 | 79% |
|
||||
| pl | 261/330 | 79% |
|
||||
| pt_BR | 299/330 | 90% |
|
||||
| ru | 330/330 | 100% |
|
||||
| tr | 261/330 | 79% |
|
||||
| uk | 261/330 | 79% |
|
||||
| zh_CN | 294/330 | 89% |
|
||||
| zh_HK | 294/330 | 89% |
|
||||
| zh_TW | 294/330 | 89% |
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/bin/bash
|
||||
# Version: 6
|
||||
|
||||
# This script will convert the *.po files to *.mo files, rebuilding the package/contents/locale folder.
|
||||
# Feature discussion: https://phabricator.kde.org/D5209
|
||||
# Eg: contents/locale/fr_CA/LC_MESSAGES/plasma_applet_org.kde.plasma.eventcalendar.mo
|
||||
|
||||
DIR=`cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd`
|
||||
plasmoidName=com.github.exequtic.apdatifier
|
||||
widgetName="${plasmoidName##*.}" # Strip namespace
|
||||
website=https://github.com/exequtic/apdatifier
|
||||
bugAddress="$website"
|
||||
packageRoot=".." # Root of translatable sources
|
||||
projectName="plasma_applet_${plasmoidName}" # project name
|
||||
|
||||
#---
|
||||
if [ -z "$plasmoidName" ]; then
|
||||
echo "[build] Error: Couldn't read plasmoidName."
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -z "$(which msgfmt)" ]; then
|
||||
echo "[build] Error: msgfmt command not found. Need to install gettext"
|
||||
echo "[build] Running 'sudo apt install gettext'"
|
||||
sudo apt install gettext
|
||||
echo "[build] gettext installation should be finished. Going back to installing translations."
|
||||
fi
|
||||
|
||||
#---
|
||||
echo "[build] Compiling messages"
|
||||
|
||||
catalogs=`find ./po -name '*.po' | sort`
|
||||
for cat in $catalogs; do
|
||||
catLocale=`basename ${cat%.*}`
|
||||
msgfmt -o "${catLocale}.mo" "$cat"
|
||||
|
||||
installPath="$DIR/../contents/locale/${catLocale}/LC_MESSAGES/${projectName}.mo"
|
||||
|
||||
echo "[build] Install ${cat} to ${installPath}"
|
||||
mkdir -p "$(dirname "$installPath")"
|
||||
mv "${catLocale}.mo" "${installPath}"
|
||||
done
|
||||
|
||||
echo "[build] Done building messages"
|
||||
|
||||
if [ "$1" = "--restartplasma" ]; then
|
||||
echo "[build] Restarting plasmashell"
|
||||
killall plasmashell
|
||||
kstart5 plasmashell
|
||||
echo "[build] Done restarting plasmashell"
|
||||
else
|
||||
echo "[build] (re)install the plasmoid and restart plasmashell to test."
|
||||
fi
|
||||
@@ -0,0 +1,165 @@
|
||||
#!/bin/bash
|
||||
# Version: 22
|
||||
|
||||
# https://techbase.kde.org/Development/Tutorials/Localization/i18n_Build_Systems
|
||||
# https://techbase.kde.org/Development/Tutorials/Localization/i18n_Build_Systems/Outside_KDE_repositories
|
||||
# https://invent.kde.org/sysadmin/l10n-scripty/-/blob/master/extract-messages.sh
|
||||
|
||||
DIR=`cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd`
|
||||
plasmoidName=com.github.exequtic.apdatifier
|
||||
widgetName="${plasmoidName##*.}" # Strip namespace
|
||||
website=https://github.com/exequtic/apdatifier
|
||||
bugAddress="$website"
|
||||
packageRoot=".." # Root of translatable sources
|
||||
projectName="plasma_applet_${plasmoidName}" # project name
|
||||
|
||||
#---
|
||||
if [ -z "$plasmoidName" ]; then
|
||||
echo "[merge] Error: Couldn't read plasmoidName."
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ -z "$(which xgettext)" ]; then
|
||||
echo "[merge] Error: xgettext command not found. Need to install gettext"
|
||||
echo "[merge] Running 'sudo apt install gettext'"
|
||||
sudo apt install gettext
|
||||
echo "[merge] gettext installation should be finished. Going back to merging translations."
|
||||
fi
|
||||
|
||||
#---
|
||||
echo "[merge] Extracting messages"
|
||||
potArgs="--from-code=UTF-8 --width=200 --add-location=file"
|
||||
|
||||
# See Ki18n's extract-messages.sh for a full example:
|
||||
# https://invent.kde.org/sysadmin/l10n-scripty/-/blob/master/extract-messages.sh#L25
|
||||
# The -kN_ and -kaliasLocale keywords are mentioned in the Outside_KDE_repositories wiki.
|
||||
# We don't need -kN_ since we don't use intltool-extract but might as well keep it.
|
||||
# I have no idea what -kaliasLocale is used for. Googling aliasLocale found only listed kde1 code.
|
||||
# We don't need to parse -ki18nd since that'll extract messages from other domains.
|
||||
find "${packageRoot}" -name '*.cpp' -o -name '*.h' -o -name '*.c' -o -name '*.qml' -o -name '*.js' -o -name 'messages' | sort > "${DIR}/infiles.list"
|
||||
xgettext \
|
||||
${potArgs} \
|
||||
--files-from="${DIR}/infiles.list" \
|
||||
-C -kde \
|
||||
-ci18n \
|
||||
-ki18n:1 -ki18nc:1c,2 -ki18np:1,2 -ki18ncp:1c,2,3 \
|
||||
-kki18n:1 -kki18nc:1c,2 -kki18np:1,2 -kki18ncp:1c,2,3 \
|
||||
-kxi18n:1 -kxi18nc:1c,2 -kxi18np:1,2 -kxi18ncp:1c,2,3 \
|
||||
-kkxi18n:1 -kkxi18nc:1c,2 -kkxi18np:1,2 -kkxi18ncp:1c,2,3 \
|
||||
-kI18N_NOOP:1 -kI18NC_NOOP:1c,2 \
|
||||
-kI18N_NOOP2:1c,2 -kI18N_NOOP2_NOSTRIP:1c,2 \
|
||||
-ktr2i18n:1 -ktr2xi18n:1 \
|
||||
-kN_:1 \
|
||||
-kaliasLocale \
|
||||
--package-name="${widgetName}" \
|
||||
--msgid-bugs-address="${bugAddress}" \
|
||||
-D "${packageRoot}" \
|
||||
-D "${DIR}" \
|
||||
-o "template.pot.new" \
|
||||
|| \
|
||||
{ echo "[merge] error while calling xgettext. aborting."; exit 1; }
|
||||
|
||||
sed -i 's/"Content-Type: text\/plain; charset=CHARSET\\n"/"Content-Type: text\/plain; charset=UTF-8\\n"/' "template.pot.new"
|
||||
sed -i 's/# SOME DESCRIPTIVE TITLE./'"# Translation of ${widgetName} in LANGUAGE"'/' "template.pot.new"
|
||||
sed -i 's/# Copyright (C) YEAR THE PACKAGE'"'"'S COPYRIGHT HOLDER/'"# Copyright (C) $(date +%Y)"'/' "template.pot.new"
|
||||
|
||||
if [ -f "template.pot" ]; then
|
||||
newPotDate=`grep "POT-Creation-Date:" template.pot.new | sed 's/.\{3\}$//'`
|
||||
oldPotDate=`grep "POT-Creation-Date:" template.pot | sed 's/.\{3\}$//'`
|
||||
sed -i 's/'"${newPotDate}"'/'"${oldPotDate}"'/' "template.pot.new"
|
||||
changes=`diff "template.pot" "template.pot.new"`
|
||||
if [ ! -z "$changes" ]; then
|
||||
# There's been changes
|
||||
sed -i 's/'"${oldPotDate}"'/'"${newPotDate}"'/' "template.pot.new"
|
||||
mv "template.pot.new" "template.pot"
|
||||
|
||||
addedKeys=`echo "$changes" | grep "> msgid" | cut -c 9- | sort`
|
||||
removedKeys=`echo "$changes" | grep "< msgid" | cut -c 9- | sort`
|
||||
echo ""
|
||||
echo "Added Keys:"
|
||||
echo "$addedKeys"
|
||||
echo ""
|
||||
echo "Removed Keys:"
|
||||
echo "$removedKeys"
|
||||
echo ""
|
||||
|
||||
else
|
||||
# No changes
|
||||
rm "template.pot.new"
|
||||
fi
|
||||
else
|
||||
# template.pot didn't already exist
|
||||
mv "template.pot.new" "template.pot"
|
||||
fi
|
||||
|
||||
potMessageCount=`expr $(grep -Pzo 'msgstr ""\n(\n|$)' "template.pot" | grep -c 'msgstr ""')`
|
||||
echo "| Locale | Lines | % Done|" > "./Status.md"
|
||||
echo "|----------|---------|-------|" >> "./Status.md"
|
||||
entryFormat="| %-8s | %7s | %5s |"
|
||||
templateLine=`perl -e "printf(\"$entryFormat\", \"Template\", \"${potMessageCount}\", \"\")"`
|
||||
echo "$templateLine" >> "./Status.md"
|
||||
|
||||
rm "${DIR}/infiles.list"
|
||||
echo "[merge] Done extracting messages"
|
||||
|
||||
#---
|
||||
echo "[merge] Merging messages"
|
||||
catalogs=`find ./po -name '*.po' | sort`
|
||||
for cat in $catalogs; do
|
||||
echo "[merge] $cat"
|
||||
catLocale=`basename ${cat%.*}`
|
||||
|
||||
widthArg=""
|
||||
catUsesGenerator=`grep "X-Generator:" "$cat"`
|
||||
if [ -z "$catUsesGenerator" ]; then
|
||||
widthArg="--width=400"
|
||||
fi
|
||||
|
||||
compendiumArg=""
|
||||
if [ ! -z "$COMPENDIUM_DIR" ]; then
|
||||
langCode=`basename "${cat%.*}"`
|
||||
compendiumPath=`realpath "$COMPENDIUM_DIR/compendium-${langCode}.po"`
|
||||
if [ -f "$compendiumPath" ]; then
|
||||
echo "compendiumPath=$compendiumPath"
|
||||
compendiumArg="--compendium=$compendiumPath"
|
||||
fi
|
||||
fi
|
||||
|
||||
cp "$cat" "$cat.new"
|
||||
sed -i 's/"Content-Type: text\/plain; charset=CHARSET\\n"/"Content-Type: text\/plain; charset=UTF-8\\n"/' "$cat.new"
|
||||
|
||||
msgmerge \
|
||||
${widthArg} \
|
||||
--add-location=file \
|
||||
--no-fuzzy-matching \
|
||||
${compendiumArg} \
|
||||
-o "$cat.new" \
|
||||
"$cat.new" "${DIR}/template.pot"
|
||||
|
||||
sed -i 's/# SOME DESCRIPTIVE TITLE./'"# Translation of ${widgetName} in ${catLocale}"'/' "$cat.new"
|
||||
sed -i 's/# Translation of '"${widgetName}"' in LANGUAGE/'"# Translation of ${widgetName} in ${catLocale}"'/' "$cat.new"
|
||||
sed -i 's/# Copyright (C) YEAR THE PACKAGE'"'"'S COPYRIGHT HOLDER/'"# Copyright (C) $(date +%Y)"'/' "$cat.new"
|
||||
|
||||
poEmptyMessageCount=`expr $(grep -Pzo 'msgstr ""\n(\n|$)' "$cat.new" | grep -c 'msgstr ""')`
|
||||
poMessagesDoneCount=`expr $potMessageCount - $poEmptyMessageCount`
|
||||
poCompletion=`perl -e "printf(\"%d\", $poMessagesDoneCount * 100 / $potMessageCount)"`
|
||||
poLine=`perl -e "printf(\"$entryFormat\", \"$catLocale\", \"${poMessagesDoneCount}/${potMessageCount}\", \"${poCompletion}%\")"`
|
||||
echo "$poLine" >> "./Status.md"
|
||||
|
||||
# mv "$cat" "$cat.old"
|
||||
mv "$cat.new" "$cat"
|
||||
done
|
||||
echo "[merge] Done merging messages"
|
||||
|
||||
#---
|
||||
# Populate ReadMe.md
|
||||
echo "[merge] Updating translate/ReadMe.md"
|
||||
sed -i -E 's`share\/plasma\/plasmoids\/(.+)\/translate`share/plasma/plasmoids/'"${plasmoidName}"'/translate`' ./ReadMe.md
|
||||
if [[ "$website" == *"github.com"* ]]; then
|
||||
sed -i -E 's`\[new issue\]\(https:\/\/github\.com\/(.+)\/(.+)\/issues\/new\)`[new issue]('"${website}"'/issues/new)`' ./ReadMe.md
|
||||
fi
|
||||
sed -i '/^|/ d' ./ReadMe.md # Remove status table from ReadMe
|
||||
cat ./Status.md >> ./ReadMe.md
|
||||
rm ./Status.md
|
||||
|
||||
echo "[merge] Done"
|
||||