cal.lua 7.4 KB

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