]> git.madduck.net Git - etc/awesome.git/blob - widget/cal.lua

madduck's git repository

Every one of the projects in this repository is available at the canonical URL git://git.madduck.net/madduck/pub/<projectpath> — see each project's metadata for the exact URL.

All patches and comments are welcome. Please squash your changes to logical commits before using git-format-patch and git-send-email to patches@git.madduck.net. If you'd read over the Git project's submission guidelines and adhered to them, I'd be especially grateful.

SSH access, as well as push access can be individually arranged.

If you use my repositories frequently, consider adding the following snippet to ~/.gitconfig and using the third clone URL listed for each project:

[url "git://git.madduck.net/madduck/"]
  insteadOf = madduck:

Merge pull request #430 from trap000d/weather_widget
[etc/awesome.git] / 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