massive update, probably broken
[dotfiles/.git] / .config / awesome / lain / widget / cal.lua
1 --[[
2
3      Licensed under GNU General Public License v2
4       * (c) 2018, Luca CPZ
5
6 --]]
7
8 local helpers  = require("lain.helpers")
9 local markup   = require("lain.util.markup")
10 local awful    = require("awful")
11 local naughty  = require("naughty")
12 local floor    = math.floor
13 local os       = os
14 local pairs    = pairs
15 local string   = string
16 local tconcat  = table.concat
17 local type     = type
18 local tonumber = tonumber
19 local tostring = tostring
20
21 -- Calendar notification
22 -- lain.widget.cal
23
24 local function factory(args)
25     args = args or {}
26     local cal = {
27         attach_to           = args.attach_to or {},
28         week_start          = args.week_start or 2,
29         three               = args.three or false,
30         followtag           = args.followtag or false,
31         week_number         = args.week_number or "none",
32         week_number_format  = args.week_number_format or args.week_number == "left" and "%3d | " or "| %-3d",
33         icons               = args.icons or helpers.icons_dir .. "cal/white/",
34         notification_preset = args.notification_preset or {
35             font = "Monospace 10", fg = "#FFFFFF", bg = "#000000"
36         }
37     }
38
39     function cal.get_week_number(m, st_day, x)
40         return string.format(cal.week_number_format, os.date("%V", m) + (x ~= 0 and floor((x + st_day) / 7) - 1 or 0))
41     end
42
43     function cal.sum_week_days(x, y)
44         return (x + y) % 7
45     end
46
47     function cal.build(month, year)
48         local current_month, current_year = tonumber(os.date("%m")), tonumber(os.date("%Y"))
49         local is_current_month = (not month or not year) or (month == current_month and year == current_year)
50         local today = is_current_month and tonumber(os.date("%d")) -- otherwise nil and not highlighted
51         local t = os.time { year = year or current_year, month = month and month+1 or current_month+1, day = 0 }
52         local d = os.date("*t", t)
53         local mth_days, st_day, this_month = d.day, (d.wday-d.day-cal.week_start+1)%7, os.date("%B %Y", t)
54         local notifytable = { [1] = string.format("%s%s\n", string.rep(" ", floor((28 - this_month:len())/2)), markup.bold(this_month)) }
55         for x = 0,6 do notifytable[#notifytable+1] = os.date("%a", os.time { year=2006, month=1, day=x+cal.week_start }):sub(1, 3) .. " " end
56         notifytable[#notifytable] = string.format("%s\n%s", notifytable[#notifytable]:sub(1, -2), string.rep(" ", st_day*4))
57         local strx
58         for x = 1,mth_days do
59             strx = x
60             if x == today then
61                 if x < 10 then x = " " .. x end
62                 strx = markup.bold(markup.color(cal.notification_preset.bg, cal.notification_preset.fg, x) .. " ")
63             end
64             strx = string.format("%s%s", string.rep(" ", 3 - tostring(x):len()), strx)
65             notifytable[#notifytable+1] = string.format("%-4s%s", strx, (x+st_day)%7==0 and x ~= mth_days and "\n" or "")
66         end
67         if string.len(cal.icons or "") > 0 and today then cal.icon = cal.icons .. today .. ".png" end
68         cal.month, cal.year = d.month, d.year
69
70         if cal.week_number ~= "none" then
71             local m = os.time { year = year or current_year, month = month and month or current_month, day = 0 }
72             local head_prepend = string.rep(" ", tostring(string.format(cal.week_number_format, 0)):len())
73
74             if cal.week_number == "left" then
75                 notifytable[1] = head_prepend .. notifytable[1] -- month-year row
76                 notifytable[2] = head_prepend .. notifytable[2] -- weekdays row
77                 notifytable[8] = notifytable[8]:gsub("\n", "\n" .. cal.get_week_number(m, st_day, 0)) -- first week of the month
78
79                 for x = 10,#notifytable do
80                     if cal.sum_week_days(st_day, x) == 2 then
81                         notifytable[x] = cal.get_week_number(m, st_day, x) .. notifytable[x]
82                     end
83                 end
84             elseif cal.week_number == "right" then
85                 notifytable[8] = notifytable[8]:gsub("\n", head_prepend .. "\n") -- weekdays row
86                 for x = 9,#notifytable do
87                     if cal.sum_week_days(st_day, x) == 1 then
88                         notifytable[x] = notifytable[x]:gsub("\n", cal.get_week_number(m, st_day, x - 7) .. "\n")
89                     end
90                 end
91                 -- last week of the month
92                 local end_days = cal.sum_week_days(st_day, mth_days)
93                 if end_days ~= 0 then end_days = 7 - end_days end
94                 notifytable[#notifytable] = notifytable[#notifytable] .. string.rep(" ", 4 * end_days) .. cal.get_week_number(m, st_day, mth_days + end_days)
95             end
96         end
97
98         return notifytable
99     end
100
101     function cal.getdate(month, year, offset)
102         if not month or not year then
103             month = tonumber(os.date("%m"))
104             year  = tonumber(os.date("%Y"))
105         end
106
107         month = month + offset
108
109         while month > 12 do
110             month = month - 12
111             year = year + 1
112         end
113
114         while month < 1 do
115             month = month + 12
116             year = year - 1
117         end
118
119         return month, year
120     end
121
122     function cal.hide()
123         if not cal.notification then return end
124         naughty.destroy(cal.notification)
125         cal.notification = nil
126     end
127
128     function cal.show(seconds, month, year, scr)
129         cal.notification_preset.text = tconcat(cal.build(month, year))
130
131         if cal.three then
132             local current_month, current_year = cal.month, cal.year
133             local prev_month, prev_year = cal.getdate(cal.month, cal.year, -1)
134             local next_month, next_year = cal.getdate(cal.month, cal.year,  1)
135             cal.notification_preset.text = string.format("%s\n\n%s\n\n%s",
136             tconcat(cal.build(prev_month, prev_year)), cal.notification_preset.text,
137             tconcat(cal.build(next_month, next_year)))
138             cal.month, cal.year = current_month, current_year
139         end
140
141         cal.hide()
142         cal.notification = naughty.notify {
143             preset  = cal.notification_preset,
144             screen  = cal.followtag and awful.screen.focused() or scr or 1,
145             icon    = cal.icon,
146             timeout = type(seconds) == "number" and seconds or cal.notification_preset.timeout or 5
147         }
148     end
149
150     function cal.hover_on() cal.show(0) end
151     function cal.move(offset)
152         local offset = offset or 0
153         cal.month, cal.year = cal.getdate(cal.month, cal.year, offset)
154         cal.show(0, cal.month, cal.year)
155     end
156     function cal.prev() cal.move(-1) end
157     function cal.next() cal.move( 1) end
158
159     function cal.attach(widget)
160         widget:connect_signal("mouse::enter", cal.hover_on)
161         widget:connect_signal("mouse::leave", cal.hide)
162         widget:buttons(awful.util.table.join(
163                     awful.button({}, 1, cal.prev),
164                     awful.button({}, 3, cal.next),
165                     awful.button({}, 2, cal.hover_on),
166                     awful.button({}, 5, cal.prev),
167                     awful.button({}, 4, cal.next)))
168     end
169
170     for _, widget in pairs(cal.attach_to) do cal.attach(widget) end
171
172     return cal
173 end
174
175 return factory