├── README.md └── orglendar.lua /README.md: -------------------------------------------------------------------------------- 1 | ## Description ## 2 | 3 | Orglendar is a calendar/organizer widget for Awesome. It parses Emacs org-mode files and show the scheduled and deadline dates on the calendar. 4 | 5 | See more information and installation guide here: http://awesome.naquadah.org/wiki/Orglendar_widget 6 | 7 | ## Version ## 8 | 9 | This version is intended for the git/master version of Awesome. If you use Awesome v.3.4.X please use this version: https://github.com/alexander-yakushev/Orglendar/tree/awesome-3.4.x . -------------------------------------------------------------------------------- /orglendar.lua: -------------------------------------------------------------------------------- 1 | -- Calendar with Emacs org-mode agenda for Awesome WM 2 | -- Inspired by and contributed from the org-awesome module, copyright of Damien Leone 3 | -- Licensed under GPLv2 4 | -- Version 1.1-awesome-git 5 | -- @author Alexander Yakushev 6 | 7 | local awful = require("awful") 8 | local util = awful.util 9 | local format = string.format 10 | local theme = require("beautiful") 11 | local naughty = require("naughty") 12 | 13 | local orglendar = { files = {}, 14 | char_width = nil, 15 | text_color = theme.fg_normal or "#FFFFFF", 16 | today_color = theme.fg_focus or "#00FF00", 17 | event_color = theme.fg_urgent or "#FF0000", 18 | font = theme.font or 'monospace 8', 19 | parse_on_show = true, 20 | limit_todo_length = nil, 21 | date_format = "%d-%m-%Y" } 22 | 23 | local freq_table = 24 | { d = { lapse = 86400, 25 | occur = 5, 26 | next = function(t, i) 27 | local date = os.date("*t", t) 28 | return os.time{ day = date.day + i, month = date.month, 29 | year = date.year } 30 | end }, 31 | w = { lapse = 604800, 32 | occur = 3, 33 | next = function(t, i) 34 | return t + 604800 * i 35 | end }, 36 | y = { lapse = 220752000, 37 | occur = 1, 38 | next = function(t, i) 39 | local date = os.date("*t", t) 40 | return os.time{ day = date.day, month = date.month, 41 | year = date.year + i } 42 | end }, 43 | m = { lapse = 2592000, 44 | occur = 1, 45 | next = function(t, i) 46 | local date = os.date("*t", t) 47 | return os.time{ day = date.day, month = date.month + i, 48 | year = date.year } 49 | end } 50 | } 51 | 52 | local calendar = nil 53 | local todo = nil 54 | local offset = 0 55 | 56 | local data = nil 57 | 58 | local function pop_spaces(s1, s2, maxsize) 59 | local sps = "" 60 | for i = 1, maxsize - string.len(s1) - string.len(s2) do 61 | sps = sps .. " " 62 | end 63 | return s1 .. sps .. s2 64 | end 65 | 66 | local function strip_time(time_obj) 67 | local tbl = os.date("*t", time_obj) 68 | return os.time{day = tbl.day, month = tbl.month, year = tbl.year} 69 | end 70 | 71 | function orglendar.parse_agenda() 72 | local today = os.time() 73 | data = { tasks = {}, dates = {}, maxlen = 20 } 74 | 75 | local task_name 76 | for _, file in pairs(orglendar.files) do 77 | local fd = io.open(file, "r") 78 | if not fd then 79 | print("W: orglendar: cannot find " .. file) 80 | else 81 | for line in fd:lines() do 82 | local scheduled = string.find(line, "SCHEDULED:") 83 | local closed = string.find(line, "CLOSED:") 84 | local deadline = string.find(line, "DEADLINE:") 85 | 86 | if (scheduled and not closed) or (deadline and not closed) then 87 | local _, _, y, m, d, h, min, recur = string.find(line, "(%d%d%d%d)%-(%d%d)%-(%d%d) %w%w%w ?(%d*)%:?(%d*)[^%+]*%+?([^>]*)>") 88 | if h ~= "" then 89 | h = tonumber(h) 90 | else 91 | h = 23 92 | end 93 | 94 | if min ~= "" then 95 | min = tonumber(min) 96 | else 97 | min = 59 98 | end 99 | 100 | local task_date = os.time{day = tonumber(d), month = tonumber(m), 101 | year = tonumber(y), hour = h, min = min} 102 | 103 | if d and task_name and (task_date >= today or recur ~= "") then 104 | local find_begin, task_start = string.find(task_name, "[A-Z]+%s+") 105 | if task_start and find_begin == 1 then 106 | task_name = string.sub(task_name, task_start + 1) 107 | end 108 | local task_end, _, task_tags = string.find(task_name,"%s+(:.+):") 109 | if task_tags then 110 | task_name = string.sub(task_name, 1, task_end - 1) 111 | else 112 | task_tags = " " 113 | end 114 | 115 | local len = string.len(task_name) + string.len(task_tags) 116 | if (len > data.maxlen) and (task_date >= today) then 117 | data.maxlen = len 118 | end 119 | 120 | if recur ~= "" then 121 | local _, _, interval, freq = string.find(recur, "(%d)(%w)") 122 | local now = os.time() 123 | local curr 124 | local event_time = task_date -- os.time({day = tonumber(d), month = tonumber(m), year = y}) 125 | if freq == "d" then 126 | curr = math.max(now, event_time) 127 | elseif freq == "w" then 128 | local count = math.floor((now - event_time) / (freq_table.w.lapse * interval)) 129 | if count < 0 then count = 0 end 130 | curr = event_time + count * (freq_table.w.lapse * interval) 131 | else 132 | curr = event_time 133 | end 134 | while curr < now do 135 | curr = freq_table[freq].next(curr, interval) 136 | end 137 | for i = 1, freq_table[freq].occur do 138 | local curr_date = os.date("*t", curr) 139 | table.insert(data.tasks, { name = task_name, 140 | tags = task_tags, 141 | date = curr, 142 | recur = recur}) 143 | data.dates[strip_time(curr)] = true 144 | curr = freq_table[freq].next(curr, interval) 145 | end 146 | else 147 | table.insert(data.tasks, { name = task_name, 148 | tags = task_tags, 149 | date = task_date, 150 | recur = recur}) 151 | data.dates[strip_time(task_date)] = true 152 | end 153 | end 154 | end 155 | _, _, task_name = string.find(line, "%*+%s+(.+)") 156 | end 157 | end 158 | end 159 | table.sort(data.tasks, function (a, b) return a.date < b.date end) 160 | end 161 | 162 | local function create_calendar() 163 | offset = offset or 0 164 | 165 | local now = os.date("*t") 166 | local cal_month = now.month + offset 167 | local cal_year = now.year 168 | if cal_month > 12 then 169 | cal_month = (cal_month % 12) 170 | cal_year = cal_year + 1 171 | elseif cal_month < 1 then 172 | cal_month = (cal_month + 12) 173 | cal_year = cal_year - 1 174 | end 175 | 176 | local last_day = tonumber(os.date("%d", os.time({ day = 1, year = cal_year, 177 | month = cal_month + 1}) - 86400)) 178 | local first_day = os.time({ day = 1, month = cal_month, year = cal_year}) 179 | local first_day_in_week = 180 | (os.date("%w", first_day) + 6) % 7 181 | local result = "Mo Tu We Th Fr Sa Su\n" 182 | for i = 1, first_day_in_week do 183 | result = result .. " " 184 | end 185 | 186 | local this_month = false 187 | for day = 1, last_day do 188 | local last_in_week = (day + first_day_in_week) % 7 == 0 189 | local day_str = pop_spaces("", day, 2) .. (last_in_week and "" or " ") 190 | if cal_month == now.month and cal_year == now.year and day == now.day then 191 | this_month = true 192 | result = result .. 193 | format('%s', 194 | orglendar.today_color, day_str) 195 | elseif data.dates[os.time{day = day, month = cal_month, year = cal_year}] then 196 | result = result .. 197 | format('%s', 198 | orglendar.event_color, day_str) 199 | else 200 | result = result .. day_str 201 | end 202 | if last_in_week and day ~= last_day then 203 | result = result .. "\n" 204 | end 205 | end 206 | 207 | local header 208 | if this_month then 209 | header = os.date("%a, %d %b %Y") 210 | else 211 | header = os.date("%B %Y", first_day) 212 | end 213 | return header, format('%s', 214 | orglendar.font, orglendar.text_color, result) 215 | end 216 | 217 | local function create_todo() 218 | local result = "" 219 | local maxlen = data.maxlen + 3 220 | if limit_todo_length and limit_todo_length < maxlen then 221 | maxlen = limit_todo_length 222 | end 223 | local prev_date, limit, tname 224 | for i, task in ipairs(data.tasks) do 225 | if strip_time(prev_date) ~= strip_time(task.date) then 226 | result = result .. 227 | format('%s\n', 228 | orglendar.event_color, 229 | pop_spaces("", os.date(orglendar.date_format, task.date), maxlen)) 230 | end 231 | tname = task.name 232 | limit = maxlen - string.len(task.tags) - 3 233 | if limit < string.len(tname) then 234 | tname = string.sub(tname, 1, limit - 3) .. "..." 235 | end 236 | result = result .. pop_spaces(tname, task.tags, maxlen) 237 | 238 | if i ~= #data.tasks then 239 | result = result .. "\n" 240 | end 241 | prev_date = task.date 242 | end 243 | if result == "" then 244 | result = " " 245 | end 246 | return format('%s', 247 | orglendar.font, orglendar.text_color, result), data.maxlen + 3 248 | end 249 | 250 | function orglendar.get_calendar_and_todo_text(_offset) 251 | if not data or parse_on_show then 252 | orglendar.parse_agenda() 253 | end 254 | 255 | offset = _offset 256 | local header, cal = create_calendar() 257 | return format('%s\n%s', 258 | orglendar.font, orglendar.text_color, header, cal), create_todo() 259 | end 260 | 261 | local function calculate_char_width() 262 | return theme.get_font_height(font) * 0.555 263 | end 264 | 265 | function orglendar.hide() 266 | if calendar ~= nil then 267 | naughty.destroy(calendar) 268 | naughty.destroy(todo) 269 | calendar = nil 270 | offset = 0 271 | end 272 | end 273 | 274 | function orglendar.show(inc_offset) 275 | inc_offset = inc_offset or 0 276 | 277 | if not data or parse_on_show then 278 | orglendar.parse_agenda() 279 | end 280 | 281 | local save_offset = offset 282 | orglendar.hide() 283 | offset = save_offset + inc_offset 284 | 285 | local char_width = char_width or calculate_char_width() 286 | local header, cal_text = create_calendar() 287 | calendar = naughty.notify({ title = header, 288 | text = cal_text, 289 | timeout = 0, hover_timeout = 0.5, 290 | screen = mouse.screen, 291 | }) 292 | todo = naughty.notify({ title = "TO-DO list", 293 | text = create_todo(), 294 | timeout = 0, hover_timeout = 0.5, 295 | screen = mouse.screen, 296 | }) 297 | end 298 | 299 | function orglendar.register(widget) 300 | widget:connect_signal("mouse::enter", function() orglendar.show(0) end) 301 | widget:connect_signal("mouse::leave", orglendar.hide) 302 | widget:buttons(util.table.join( awful.button({ }, 3, function() 303 | orglendar.parse_agenda() 304 | end), 305 | awful.button({ }, 4, function() 306 | orglendar.show(-1) 307 | end), 308 | awful.button({ }, 5, function() 309 | orglendar.show(1) 310 | end))) 311 | end 312 | 313 | return orglendar 314 | --------------------------------------------------------------------------------