#!/usr/bin/env bash # Copyright (C) Daniel Napora 2021-22 # Dependencies: jgmenu, xdotool, xcolor, gcolor3, xdg-utils, xclip, notify-send # TODO Imagick colors - separate menu big around 700 named colors # TODO export to .gpl (GIMP,Inkscape) # TODO PL translation VERSION="0.3.1" TEMP_DIR=/tmp/colormenu ME=${0##*/} DATA_DIR="/usr/share/$ME" CNF_DIR="$HOME/.config/mbxcolors" CNF_FILE="$CNF_DIR/mbxcolors.cfg" RECENT="$CNF_DIR/recent.clr" USED="$CNF_DIR/.used" LASTPAL="$CNF_DIR/.palette" [ ! -d "$CNF_DIR" ] && mkdir -p "$CNF_DIR" if [ ! -f $CNF_FILE ]; then cat < ${CNF_FILE} # Paste mode: hex,rgb or rgba format=hex #Action when color is clicked: paste (imadietely) or copy (to clipboard) action=copy #What thing to expose on top of main menu: recently picked colors, last used colors or last used palette expose=picked #Show HTML colors? yes|no htmlcolors=yes #Show built in palettes? (slow) builtin_palettes=yes #Position on screen: topleft, top, topright, left, center, right, bottomleft, bottom, bottomright position=left EOF fi source <(grep = $CNF_FILE) if [ ! -f $RECENT ]; then cat < ${RECENT} #FFFFFF White #C0C0C0 Silver #808080 Gray #000000 Black EOF fi fgcolor() { r=$(printf "%d" 0x${1:1:2}) g=$(printf "%d" 0x${1:3:2}) b=$(printf "%d" 0x${1:5:2}) [ "$((r*299+g*587+b*114))" -gt 149000 ] && echo "#111111" || echo "#EEEEEE" } hex2rgb() { r=$(printf "%d" 0x${1:1:2}) g=$(printf "%d" 0x${1:3:2}) b=$(printf "%d" 0x${1:5:2}) echo "rgb($r, $g, $b)" } hex2rgba() { r=$(printf "%d" 0x${1:1:2}) g=$(printf "%d" 0x${1:3:2}) b=$(printf "%d" 0x${1:5:2}) echo "rgba($r, $g, $b, 1.0)" } pickcolor() { #color=$(colorpicker --short --one-shot --preview) #color=$(xcolor --preview-size "${preview-size:-255}" --scale "${scale:-8}") size=${xcolor_preview_size:-255} scale=${xcolor_scale:-8} command="xcolor --preview-size ${size} --scale ${scale}" color="$(${command})" if [ -n "${color}" ]; then echo "$color " > "$RECENT".tmp # add on top deduplicate awk '!x[$0]++' "$RECENT" >> "$RECENT".tmp awk '!x[$0]++' "$RECENT".tmp > "$RECENT" [ -n "$1" ] && file="$1" echo "$color " >> "$file" setvar expose=picked fi $ME } addcolor() { color=$(gcolor3) if [ -n "${color}" ]; then echo "$color " > "$RECENT".tmp # add on top deduplicate awk '!x[$0]++' "$RECENT" >> "$RECENT".tmp awk '!x[$0]++' "$RECENT".tmp > "$RECENT" [ -n "$1" ] && file="$1" echo "$color " >> "$file" setvar expose=picked fi $ME } getcolorcode() { if [ $action = "paste" ];then case "$format" in rgb) clr=$(hex2rgb $1);xdotool type --delay 0 "$clr";; rgba) clr=$(hex2rgba $1);xdotool type --delay 0 "$clr";; *) xdotool type --delay 0 "$1";; esac else # copy to clipboard case "$format" in rgb) clr=$(hex2rgb $1);echo $clr | tr -d "\n" | xclip -sel c;; rgba) clr=$(hex2rgba $1);echo $clr | tr -d "\n" | xclip -sel c;; *) clr="$1";echo "$1" | tr -d "\n" | xclip -sel c;; esac mkdir -p $TEMP_DIR FNAME="$TEMP_DIR/${1:1:6}.png" convert -size 100x100 xc:"$1" "$FNAME" notify-send ColorMenu "$clr" --icon="$FNAME" --expire-time=120000 fi echo "$1" > "$USED".tmp # add on top and deduplicate awk '!x[$0]++' "$USED" >> "$USED".tmp awk '!x[$0]++' "$USED".tmp > "$USED" [[ $2 ]] && echo "$2" > "$LASTPAL" } recent2palette () { DATE=$(date +"%Y%m%d-%H%M%S") mkdir -p "$CNF_DIR/palettes/my" grep ^# $RECENT |tac > "$CNF_DIR/palettes/my/My_palette_$DATE.clr" } newpalette() { DATE=$(date +"%Y%m%d-%H%M%S") mkdir -p "$CNF_DIR/palettes/my" touch "$CNF_DIR/palettes/my/My_palette_$DATE.clr" } palettes() { [ -n "$1" ] && pdir="$CNF_DIR/palettes/my/" || pdir="$DATA_DIR/palettes/" if [[ -n "$1" ]]; then out2+=("^tag(mypalettes)") out2+=("^sep(My palettes)") else out2+=("^tag(builtpalettes)") out2+=("^sep(Built-in palettes)") fi for file in "$pdir"*.clr ;do filename=${file##*/} palette=${filename%.*} pal=${palette//_/ } tagname=${palette// /_} out2+=("${pal^},^checkout($tagname)") out3+=("^tag($tagname)") out3+=("^sep(${pal^})") out3+=("EXPOSE_IN_ROOTMENU,$ME setvar expose=$filename;$ME") out3+=("^sep()") while read -r color name; do out3+=("$color ,$ME getcolorcode '$color' '$file',,,#${palette}") done < "$file" if [[ -n "$1" ]] ; then out3+=("^sep()") out3+=("Add color... from screen,$ME pickcolor '$file'") if hash gcolor3 2>/dev/null; then out3+=("^sep()") out3+=("Add color from selector...,$ME addcolor '$file'") fi out3+=("^sep()") out3+=("Edit palette file,xdg-open '$file'") out3+=("^sep(Danger zone...)") out3+=("Delete palette: ${palette^},^checkout($tagname-del)") out4+=("^tag($tagname-del)") out4+=("^sep(Are you sure?)") out4+=("Yes,rm '$file';$ME") fi done #if [[ $# = 0 ]] ; then if [[ -n "$1" ]] ; then out2+=("^sep()") out2+=("Add new palette...,$ME newpalette;$ME") out2+=("Open palette directory,xdg-open $CNF_DIR/palettes/my/") fi } if [ $htmlcolors = "yes" ]; then htmlroot="^sep() HTML colors,^checkout(html) " htmlsub="^tag(html) . ${DATA_DIR}/htmlsub.csv" htmlclrs=". ${DATA_DIR}/htmlclrs.csv" else htmlroot="" htmlsub="" htmlclrs="" fi position() { case "$position" in ipc) POSITION_MODE="ipc";; pointer) POSITION_MODE="pointer";; topleft) POSITION_MODE="fixed" MENU_VALIGN="top" MENU_HALIGN="left";; top) POSITIOM_MODE="fixed" MENU_VALIGN="top" MENU_HALIGN="center";; topright) POSITION_MODE="fixed" MENU_VALIGN="top" MENU_HALIGN="right";; left) POSITION_MODE="fixed" MENU_VALIGN="center" MENU_HALIGN="left";; right) POSITION_MODE="fixed" MENU_VALIGN="center" MENU_HALIGN="right";; bottomleft) POSITION_MODE="fixed" MENU_VALIGN="bottom" MENU_HALIGN="left";; bottom) POSITION_MODE="fixed" MENU_VALIGN="bottom" MENU_HALIGN="center";; bottomright) POSITION_MODE="fixed" MENU_VALIGN="bottom" MENU_HALIGN="right";; center|*) POSITION_MODE="center";; esac } main () { # Mabox Linux if [[ -f /usr/share/mb-jgtools/pipemenu-standalone.cfg ]];then . /usr/share/mb-jgtools/pipemenu-standalone.cfg . $HOME/.config/mabox/mabox.conf fi [ "$1" = "ipc" ] && position="ipc" MENU_PADDING_TOP=${jgtools_padding:-0} JGWIDTH="220" ITEM_HALIGN="center" jgmenu_icon_size=0 menu_margin_x=${submenu_spacing:-0} [ $(pidof picom) ] && MENU_RADIUS=$jgtools_radius #jgmenu_font="Noto Sans Medium 10" position mkconfigfile trap "rm -f ${CONFIG_FILE} ${MENU_ITEMS}" EXIT out+=("^sep(ColorMenu [$action $format])") #out+=("^sep( mbx colors [$action $format])") out+=("Pick color from screen...,$ME pickcolor") if hash gcolor3 2>/dev/null; then out+=("^sep()") out+=("Add color from selector...,$ME addcolor") fi # exposed thing case "$expose" in palette) pal=$(cat "$CNF_DIR/.palette") filename=${pal##*/} palette=${filename%.*} out+=("^sep(Last used palette ${palette^})") while read -r color name;do out+=("$color ,$ME getcolorcode '$color'") done < "${pal}" ;; used) mapfile -t < $USED if (( ${#MAPFILE[@]} > 1 )); then out+=("^sep(Recently used colors:)") while read -r color name;do out+=("$color ,$ME getcolorcode '$color'") done < <(grep ^# $USED) out+=("^sep()") out+=("Clear recently used colors,echo > $USED;$ME") fi ;; picked) mapfile -t < $RECENT if (( ${#MAPFILE[@]} > 1 )); then out+=("^sep(Recently picked colors:)") # for clear/delete out2+=("^tag(pickeddel)") out2+=("^sep(Clear All)") out2+=("Clear all,echo > $RECENT;$ME") out2+=("^sep(Just remove one color...)") while read -r color name;do out+=("$color ,$ME getcolorcode '$color'") out2+=("$color ,sed -i '/${color:1:6}/d' $RECENT;$ME") done < <(grep ^# $RECENT) out+=("^sep()") out+=("New palette from recently picked,$ME recent2palette;$ME") out+=("Clear recently picked,^checkout(pickeddel)") fi ;; wallpaper) read WALLPAPER<<< $(grep file "$HOME/.config/nitrogen/bg-saved.cfg | cut -d'=' -f2") WALLPALDIR="$HOME/.config/mbxcolors/palettes/wallp" mkdir -p "$WALLPALDIR" read WALLPATH<<< $(grep file "$HOME/.config/nitrogen/bg-saved.cfg" | cut -d'=' -f2) WALLPAPER=${WALLPATH##*/} if [ ! -f "$WALLPALDIR/$WALLPAPER.clr" ]; then convert ${WALLPATH} -resize 25% -colors 16 -unique-colors txt:- |grep -v '^#'| awk '{print $3}' > "$WALLPALDIR/$WALLPAPER.clr" fi #mapfile -t wallcolors < "$WALLPALDIR/$WALLPAPER.clr" out+=("^sep(Wallpaper colors)") while read -r color name;do out+=("$color ,$ME getcolorcode '$color'") done < "$WALLPALDIR/$WALLPAPER.clr" ;; *) if [ -f "$DATA_DIR/palettes/$expose" ];then palfile="$DATA_DIR/palettes/$expose" elif [ -f "$CNF_DIR/palettes/my/$expose" ];then palfile="$CNF_DIR/palettes/my/$expose" fi if [ $palfile ];then palette=${expose%.*} pal=${palette//_/ } out+=("^sep(Palette: ${pal^})") while read -r color name; do out+=("$color ,$ME getcolorcode '$color' '$file';$ME setvar expose=palette,,,#${palette}") done < "$palfile" fi ;; esac #if [[ "$CNF_DIR"/palettes/my/*.clr ]]; then out+=("^sep(Color palettes)") if [ "$(find "$CNF_DIR"/palettes/my/*.clr -maxdepth 1 -type f -iname \*.clr)" ]; then out+=("My palettes,^checkout(mypalettes)") palettes my fi if [[ $builtin_palettes == "yes" ]];then out+=("Built-in palettes,^checkout(builtpalettes)") palettes fi # WALLPAPER COLORS out+=("Current Wallpaper colors,^checkout(wallcolors)") read WALLPAPER<<< $(grep file "$HOME/.config/nitrogen/bg-saved.cfg | cut -d'=' -f2") WALLPALDIR="$HOME/.config/mbxcolors/palettes/wallp" mkdir -p "$WALLPALDIR" read WALLPATH<<< $(grep file "$HOME/.config/nitrogen/bg-saved.cfg" | cut -d'=' -f2) WALLPAPER=${WALLPATH##*/} if [ ! -f "$WALLPALDIR/$WALLPAPER.clr" ]; then convert ${WALLPATH} -resize 25% -colors 16 -unique-colors txt:- |grep -v '^#'| awk '{print $3}' > "$WALLPALDIR/$WALLPAPER.clr" fi #mapfile -t wallcolors < "$WALLPALDIR/$WALLPAPER.clr" out2+=("^tag(wallcolors)") out2+=("^sep(Current wallpaper colors)") out2+=("EXPOSE_IN_ROOTMENU ,$ME setvar expose=wallpaper;$ME") out2+=("^sep()") while read -r color name;do out2+=("$color ,$ME getcolorcode '$color'") done < "$WALLPALDIR/$WALLPAPER.clr" out1+=("^sep(Settings)") out1+=("Preferences,^pipe($ME preferences)") out1+=("^sep()") out1+=("QUIT,^quit()") cat << EOF > ${MENU_ITEMS} $(printf '%s\n' "${out[@]}") ${htmlroot} $(printf '%s\n' "${out1[@]}") ${htmlsub} $(printf '%s\n' "${out2[@]}") ${htmlclrs} $(printf '%s\n' "${out3[@]}") $(printf '%s\n' "${out4[@]}") EOF jgmenu --config-file=${CONFIG_FILE} --csv-file=${MENU_ITEMS} 2>/dev/null #printf '%s\n' "${out3[@]}" exit 0 } imagick() { echo "not implemented" } preferences() { out2+=("^sep(Settings)") out2+=("$(printf "%-20s %20s" "Color click action:" "[$action]"),^checkout(action)") out2+=("$(printf "%-20s %20s" "Format:" "[$format]"),^checkout(format)") out2+=("^sep()") out2+=("$(printf "%-20s %20s" "Expose on top:" "[$expose]"),^checkout(expose)") out2+=("$(printf "%-20s %20s" "HTML colors:" "[$htmlcolors]"),^checkout(showhtml)") out2+=("$(printf "%-20s %20s" "Built-in palettes:" "[$builtin_palettes]"),^checkout(builtinpalettes)") out2+=("^sep()") out2+=("$(printf "%-20s %20s" "Position:" "[$position]"),^checkout(position)") out2+=("^sep()") out2+=("Reset to defaults,rm $CNF_FILE;$ME") out2+=("^sep()") out2+=("Edit config file,xdg-open $CNF_FILE") out2+=("Open config directory,xdg-open $CNF_DIR") out2+=("^tag(format)") out2+=("^sep(Output format)") out2+=("hex #579C8E,$ME setvar format=hex;$ME") out2+=("rgb rgb(87, 156, 142),$ME setvar format=rgb;$ME") out2+=("rgba rgba(87, 156, 142, 1.0),$ME setvar format=rgba;$ME") out2+=("^tag(action)") out2+=("^sep(Color action)") out2+=("paste (paste color immediately),$ME setvar action=paste;$ME") out2+=("copy (copy color to clipboard),$ME setvar action=copy;$ME") out2+=("^tag(expose)") out2+=("^sep(Show on top of rootmenu)") out2+=("picked recently picked colors,$ME setvar expose=picked;$ME") out2+=("used recently used colors,$ME setvar expose=used;$ME") out2+=("palette recently used palette,$ME setvar expose=palette;$ME") out2+=("wallpaper colors ,$ME setvar expose=wallpaper;$ME") out2+=("^tag(showhtml)") out2+=("^sep(Show HTML colors?)") out2+=("yes,$ME setvar htmlcolors=yes;$ME") out2+=("no,$ME setvar htmlcolors=no;$ME") out2+=("^tag(builtinpalettes)") out2+=("^sep(Show Built-in palettes?)") out2+=("yes,$ME setvar builtin_palettes=yes;$ME") out2+=("no,$ME setvar builtin_palettes=no;$ME") out2+=("^tag(position)") out2+=("^sep(Position on screen)") out2+=("center,$ME setvar position=center;$ME") out2+=("topleft,$ME setvar position=topleft;$ME") out2+=("top,$ME setvar position=top;$ME") out2+=("topright,$ME setvar position=topright;$ME") out2+=("left,$ME setvar position=left;$ME") out2+=("right,$ME setvar position=right;$ME") out2+=("bottomleft,$ME setvar position=bottomleft;$ME") out2+=("bottom,$ME setvar position=bottom;$ME") out2+=("bottomright,$ME setvar position=bottomright;$ME") printf '%s\n' "${out2[@]}" } setvar() { search=$(echo $1|cut -d= -f1) FILE=${CNF_FILE} if grep -Rq $search $FILE then #found sed -i s/^"$search".*$/"$1"/ $FILE else #not found echo $1 >> $FILE fi } case "$1" in -h|--help|help) usage;; --pickcolor|pickcolor) pickcolor "$2";; --addcolor|addcolor) addcolor "$2" ;; -g|--getcolorcode|getcolorcode) getcolorcode "$2" "$3";; -p|preferences) preferences;; -s|setvar) setvar "$2";; ipc) main "$1";; recent2palette) recent2palette ;; newpalette) newpalette;; imagick) imagick;; *) main;; esac exit 0