├── LICENSE ├── README.md ├── TODO └── dzen-cal /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Aleksander 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dzen-cal 2 | Just a dzen2 based calendar written in bash 3 | 4 | ![...](https://user-images.githubusercontent.com/21217636/45256332-a12b3180-b39d-11e8-91f5-65012a30d3b5.png) 5 | 6 | The script takes **cal** Linux command output, highlight holidays in dzen2 style and send it to **dzen2**. 7 | 8 | ### Features: 9 | - month/year view 10 | - you can switch between months and years by clicking **>>** and **<<** buttons 11 | - in a month view you can open a whole year by clicking **year** area 12 | - you can specify alignment and indention from edges of the screen 13 | - by default, highlighted holidays are Saturdays and Sundays. But you can specify extra holidays (like Easter day and etc...) or extra work days (if some holidays become workdays :)) 14 | - you can also specify dates, which you want to highlight with some color, like birthdays and etc... 15 | -------------------------------------------------------------------------------- /TODO: -------------------------------------------------------------------------------- 1 | TODOs: 2 | - separate colors and dates configuration and the script 3 | - add configurable colors to custom-highlighted dates 4 | -------------------------------------------------------------------------------- /dzen-cal: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | highlighted_days=( 4 | # salary days 5 | "2018-09-14" 6 | "2018-09-28" 7 | "2018-10-12" 8 | "2018-10-29" 9 | "2018-11-14" 10 | "2018-11-29" 11 | "2018-12-14" 12 | "2018-12-28" 13 | ) 14 | 15 | holidays=( 16 | # 2018 holidays 17 | "2018-01-01" 18 | "2018-01-02" 19 | "2018-01-03" 20 | "2018-01-04" 21 | "2018-01-05" 22 | "2018-01-06" 23 | "2018-01-07" 24 | "2018-01-08" 25 | "2018-02-23" 26 | "2018-03-08" 27 | "2018-03-09" 28 | "2018-04-30" 29 | "2018-05-01" 30 | "2018-05-02" 31 | "2018-06-11" 32 | "2018-06-12" 33 | "2018-11-05" 34 | "2018-12-31" 35 | # 2019 holidays 36 | "2019-01-01" 37 | "2019-01-02" 38 | "2019-01-03" 39 | "2019-01-04" 40 | "2019-01-05" 41 | "2019-01-06" 42 | "2019-01-07" 43 | "2019-01-08" 44 | "2019-02-23" 45 | "2019-03-08" 46 | "2019-05-01" 47 | "2019-05-02" 48 | "2019-05-03" 49 | "2019-05-09" 50 | "2019-05-10" 51 | "2019-06-12" 52 | "2019-11-04" 53 | ) 54 | 55 | workdays=( 56 | # 2018 workdays 57 | "2018-04-28" 58 | "2018-06-09" 59 | "2018-12-29" 60 | ) 61 | 62 | declare -a aligns=( "tl" "tm" "tr" 63 | "ml" "mm" "mr" 64 | "bl" "bm" "br" ) 65 | 66 | declare -A aligns_map 67 | 68 | for key in "${!aligns[@]}"; do 69 | aligns_map[${aligns[$key]}]=1; 70 | done 71 | 72 | function test_args() { 73 | if [[ -z "${aligns_map[$align]}" ]]; then 74 | echo "ERROR: unknown align: $align" 75 | exit 1 76 | fi 77 | if [[ "$view_type" != "month" ]] && [[ "$view_type" != "year" ]]; then 78 | echo "ERROR: unknown view type: $view_type" 79 | exit 1 80 | fi 81 | } 82 | 83 | function read_args() { 84 | OPTS=`getopt -o a:,m:,y:,v:,s: -l x-indent:,y-indent: -- "$@"` 85 | eval set -- "$OPTS" 86 | 87 | while true; do 88 | case "$1" in 89 | -v ) view_type="$2"; shift 2 ;; 90 | -m ) month="$2"; shift 2 ;; 91 | -y ) year="$2"; shift 2 ;; 92 | -a ) align="$2"; shift 2 ;; 93 | -s ) xs="$2"; shift 2;; 94 | --x-indent) x_indent="$2"; shift 2;; 95 | --y-indent) y_indent="$2"; shift 2;; 96 | -- ) shift; break ;; 97 | * ) break ;; 98 | esac 99 | done 100 | 101 | x_indent="${x_indent:-0}" 102 | y_indent="${y_indent:-0}" 103 | day=$(date +%d) 104 | month=${month:-$current_month} 105 | year=${year:-$current_year} 106 | 107 | if (( month > 12 )); then 108 | month=1 109 | ((year++)) 110 | fi 111 | if (( month < 1 )); then 112 | month=12 113 | ((year--)) 114 | fi 115 | } 116 | 117 | defaultBg="#222222" 118 | defaultFg="#bbbbbb" 119 | curDayBg="#ffeb3b" 120 | curDayFg="#222222" 121 | holidayFg="#f44336" 122 | holidayBg="" 123 | highlightedFg="#00bcd4" 124 | highlightedBg="" 125 | titleName="dzen-calendar" 126 | font="Liberation Mono:pixelsize=16:antialias=true:autohint=true" 127 | font_height=21 128 | width_month="220" 129 | width_year="680" 130 | 131 | screenW=$(xdpyinfo | grep dimensions | egrep -o "[0-9]+x[0-9]+ pixels" | egrep -o "[0-9]+x[0-9]+" | sed "s/x.*//") 132 | screenH=$(xdpyinfo | grep dimensions | egrep -o "[0-9]+x[0-9]+ pixels" | egrep -o "[0-9]+x[0-9]+" | sed "s/.*x//") 133 | 134 | script_path="$(cd "$(dirname "$0")"; pwd -P)/$(basename "$0")" 135 | 136 | current_month=$(date +%-m) 137 | current_year=$(date +%-Y) 138 | view_type="month" 139 | align="mm" 140 | xs=1 141 | 142 | read_args "$@" 143 | test_args 144 | 145 | next_cmd="$script_path -s $xs -a $align --x-indent $x_indent --y-indent $y_indent -m $((month + 1)) -y $year" 146 | prev_cmd="$script_path -s $xs -a $align --x-indent $x_indent --y-indent $y_indent -m $((month - 1)) -y $year" 147 | next="^bg($defaultFg)^fg($defaultBg)^ca(1,$next_cmd)>>^ca()^fg()^bg()" 148 | prev="^bg($defaultFg)^fg($defaultBg)^ca(1,$prev_cmd)<<^ca()^fg()^bg()" 149 | 150 | year_cmd="$script_path -s $xs -a $align --x-indent $x_indent --y-indent $y_indent -y $year -v year" 151 | year_btn="^bg($defaultFg)^fg($defaultBg)^ca(1,$year_cmd)$year^ca()^fg()^bg()" 152 | 153 | next_year_cmd="$script_path -s $xs -a $align --x-indent $x_indent --y-indent $y_indent -y $((year + 1)) -v year" 154 | prev_year_cmd="$script_path -s $xs -a $align --x-indent $x_indent --y-indent $y_indent -y $((year - 1)) -v year" 155 | next_year="^bg($defaultFg)^fg($defaultBg)^ca(1,$next_year_cmd)>>^ca()^fg()^bg()" 156 | prev_year="^bg($defaultFg)^fg($defaultBg)^ca(1,$prev_year_cmd)<<^ca()^fg()^bg()" 157 | 158 | generate_colored_month_output() { 159 | cal --monday $month $year | awk -v hd="${highlighted_days[*]}" -v hds="${holidays[*]}" -v wds="${workdays[*]}" ' 160 | function isHighlighted(d,m,y,cur_d,cur_m,cur_y) { 161 | return int(d) == int(cur_d) && int(m) == int(cur_m) && int(y) == int(cur_y) 162 | } 163 | function isHoliday(d,m,y,weekend) { 164 | if (weekend) { 165 | for (ii in workdays) { 166 | if (int(workdays[ii][3]) == int(d) && int(workdays[ii][2]) == int(m) && int(workdays[ii][1]) == int(y)) { 167 | return 0 168 | } 169 | } 170 | return 1 171 | } 172 | else { 173 | for (ii in holidays) { 174 | if (int(holidays[ii][3]) == int(d) && int(holidays[ii][2]) == int(m) && int(holidays[ii][1]) == int(y)) { 175 | return 1 176 | } 177 | } 178 | return 0 179 | } 180 | } 181 | BEGIN { 182 | split(hd, tmp, " ") 183 | for (i in tmp) { 184 | h_days[i][0]="" 185 | split(tmp[i],h_days[i],"-") 186 | } 187 | split(hds, tmp2, " ") 188 | for (i in tmp2) { 189 | holidays[i][0]="" 190 | split(tmp2[i],holidays[i],"-") 191 | } 192 | split(wds, tmp3, " ") 193 | for (i in tmp3) { 194 | workdays[i][0]="" 195 | split(tmp3[i],workdays[i],"-") 196 | } 197 | } 198 | { 199 | # skip title line 200 | if (NR > 1) { 201 | for (i = 1; i <= length($0); i+=3) { 202 | day=substr($0,i,2) 203 | prefix = "" 204 | postfix = "" 205 | if (isHoliday(day,"'"$month"'","'"$year"'", int(i/3) >= 5)) { 206 | prefix = "^bg('"$holidayBg"')^fg('"$holidayFg"')" 207 | postfix = "^fg()^bg()" 208 | } 209 | for (j in h_days) { 210 | if (isHighlighted(h_days[j][3],h_days[j][2],h_days[j][1],day,"'"$month"'","'"$year"'")) { 211 | prefix = "^bg('"$highlightedBg"')^fg('"$highlightedFg"')" 212 | postfix = "^fg()^bg()" 213 | break; 214 | } 215 | } 216 | if (isHighlighted("'"$day"'", "'"$month"'", "'"$year"'", 217 | day,"'"$current_month"'","'"$current_year"'")) { 218 | prefix = "^bg('"$curDayBg"')^fg('"$curDayFg"')" 219 | postfix = "^fg()^bg()" 220 | } 221 | printf "%s%s%s%s",prefix,day,postfix,substr($0,i+2,1) 222 | } 223 | printf "\n" 224 | } 225 | else { 226 | print $0 227 | } 228 | }' | sed "1s|$year|$year_btn|; 1s|^ |$prev|; 1s| $|$next|" 229 | } 230 | 231 | generate_colored_year_output() { 232 | cal --monday -y $year | awk -v hd="${highlighted_days[*]}" -v hds="${holidays[*]}" -v wds="${workdays[*]}" ' 233 | function monthEquals(m, cur_line, cur_col) { 234 | cur_row = int((cur_line-3)/8) 235 | row = int((int(m)-1)/3) 236 | col = (int(m)-1) - row*3 237 | return col == cur_col && row == cur_row 238 | } 239 | function isHighlighted(d,m,y,cur_d, cur_line, cur_col, cur_y) { 240 | return int(d) == int(cur_d) && int(y) == int(cur_y) && monthEquals(m, cur_line, cur_col) 241 | } 242 | function formatMonthLine(line,start,col) { 243 | for(i = start; i < start + 21; i+=3) { 244 | day=substr(line,i,2) 245 | prefix = "" 246 | postfix = "" 247 | if (isHoliday(day,NR,col,"'"$year"'", int((i-start)/3) >= 5)) { 248 | prefix = "^bg('"$holidayBg"')^fg('"$holidayFg"')" 249 | postfix = "^fg()^bg()" 250 | } 251 | for (j in h_days) { 252 | if (isHighlighted(h_days[j][3],h_days[j][2],h_days[j][1],day,NR,col,"'"$year"'")) { 253 | prefix = "^bg('"$highlightedBg"')^fg('"$highlightedFg"')" 254 | postfix = "^fg()^bg()" 255 | break; 256 | } 257 | } 258 | if (isHighlighted("'"$day"'","'"$month"'","'"$year"'",day,NR,col,"'"$current_year"'")) { 259 | prefix = "^bg('"$curDayBg"')^fg('"$curDayFg"')" 260 | postfix = "^fg()^bg()" 261 | } 262 | printf "%s%s%s%s",prefix,day,postfix,substr(line,i+2,1) 263 | } 264 | } 265 | function isHoliday(d,cur_line,cur_col,y,weekend) { 266 | if (weekend) { 267 | for (ii in workdays) { 268 | if (int(workdays[ii][3]) == int(d) && monthEquals(int(workdays[ii][2]), cur_line, cur_col) && int(workdays[ii][1]) == int(y)) { 269 | return 0 270 | } 271 | } 272 | return 1 273 | } 274 | else { 275 | for (ii in holidays) { 276 | if (int(holidays[ii][3]) == int(d) && monthEquals(int(holidays[ii][2]), cur_line, cur_col) && int(holidays[ii][1]) == int(y)) { 277 | return 1 278 | } 279 | } 280 | return 0 281 | } 282 | } 283 | BEGIN { 284 | split(hd, tmp, " ") 285 | for (i in tmp) { 286 | h_days[i][0]="" 287 | split(tmp[i],h_days[i],"-") 288 | } 289 | split(hds, tmp2, " ") 290 | for (i in tmp2) { 291 | holidays[i][0]="" 292 | split(tmp2[i],holidays[i],"-") 293 | } 294 | split(wds, tmp3, " ") 295 | for (i in tmp3) { 296 | workdays[i][0]="" 297 | split(tmp3[i],workdays[i],"-") 298 | } 299 | } 300 | { 301 | # skip title lines 302 | if (NR == 1 || NR == 2 || NR == 3 || NR == 11 || NR == 19 || NR == 27) { 303 | print $0 304 | } 305 | else { 306 | formatMonthLine($0,1,0) 307 | printf "%s",substr($0,22,1) 308 | formatMonthLine($0,24,1) 309 | printf "%s",substr($0,45,2) 310 | formatMonthLine($0,47,2) 311 | printf "\n" 312 | } 313 | }' | sed "1s|^ |$prev_year|; 1s| $|$next_year|" 314 | } 315 | 316 | # kill running dzen-calendar instances 317 | pkill -f "dzen2 -title-name $titleName" 318 | 319 | if [[ "$view_type" == "year" ]]; then 320 | out=$(generate_colored_year_output) 321 | width=$width_year 322 | else 323 | out=$(generate_colored_month_output) 324 | width=$width_month 325 | fi 326 | 327 | lines=$(echo "$out" | wc -l) 328 | height=$(( $lines * $font_height )) 329 | 330 | x_align=${align:1:1} 331 | y_align=${align:0:1} 332 | 333 | case "$x_align" in 334 | "l" ) xpos=$x_indent ;; 335 | "m" ) xpos=$(( $screenW / 2 - $width / 2 )) ;; 336 | "r" ) xpos=$(( - $width - $x_indent )) ;; 337 | esac 338 | 339 | case "$y_align" in 340 | "t" ) ypos=$y_indent ;; 341 | "m" ) ypos=$(( $screenH / 2 - $height / 2 )) ;; 342 | "b" ) ypos=$(( - $height - $y_indent )) ;; 343 | esac 344 | 345 | echo "$out" | dzen2 -title-name "$titleName" \ 346 | -fn "$font" \ 347 | -fg "$defaultFg" \ 348 | -bg "$defaultBg" \ 349 | -p \ 350 | -xs "$xs" \ 351 | -x "$xpos" \ 352 | -y "$ypos" \ 353 | -w "$width" \ 354 | -l "$((lines-1))" \ 355 | -sa 'c' \ 356 | -ta 'c' \ 357 | -e 'onstart=uncollapse;button1=exit;button3=exit' & 358 | --------------------------------------------------------------------------------