El Wed, Mar 04, 2009 at 11:39:17PM +0100, xscript ens deleit� amb les seg�ents 
paraules:
> Ok, here's my try (see attachment).

Forget about last one.

This has improved soon/future detection (last was miserably broken).

Hope it's useful.

apa!

-- 
 "And it's much the same thing with knowledge, for whenever you learn
 something new, the whole world becomes that much richer."
 -- The Princess of Pure Reason, as told by Norton Juster in The Phantom
 Tollbooth
----------------------------------------------------------
-- @author Lluis Vilanova <[email protected]>
-- @copyright 2009 Lluis Vilanova, licensed under the GPLv2
--
-- Based on:
--   * org-awesome by Damien Leone
--   * Code by Tassilo Horn
----------------------------------------------------------

-- Usage:
--
-- Create the widget:
--   require("orgmode")
--   w_orgmode = orgmode.new(...)
--
-- Add this to your emacs' configuration file in order to regenerate the agenda
-- file whenever you write an org-mode file:
--   ;; Export the current agenda whenever we update one of its files
--   (setq th-org-agenda-file "~/.emacs.d/org-agenda.txt")
--   ;; do it whenever I change and save an org file
--   (defun th-org-mode-init ()
--     (add-hook 'after-save-hook 'th-org-update-agenda-file t t))
--   (add-hook 'org-mode-hook 'th-org-mode-init)
--   ;; export function
--   (defun th-org-update-agenda-file (&optional force)
--     (save-excursion
--       (save-window-excursion
--         (let ((file th-org-agenda-file))
--           (org-agenda-list)
--           (org-write-agenda file)))))
--   ;; do it once at startup
--   (th-org-update-agenda-file t)
--
-- Some helpful configurable values:
--   edit root directory where I store all my org-mode files
--     launch = "emacs ~/plans/"
--   for some unknown reason, emacs in batch mode _needs_ to know its config 
file
--     force = "emacs -l ~/.emacs.d/init.el -batch"


-- Global environment
local os = os
local io = io
local table = table
local string = string
local tonumber = tonumber
local widget = widget
local button = button
local awful = awful
local naughty = naughty

-- Module declaration
module("orgmode")

-- TODO: this should overload widget's attributes
-- TODO: we can only have one orgmode widget (more could allow for multiple 
different views), but this is not checked
local config = { }

local function close ()
    if config.note ~= nil then
        naughty.destroy(config.note)
        config.note = nil
    end
end

local function open ()
    close() -- TODO: does not seem necessary...
    if config.text == nil then
        return
    end
    -- TODO: there's too much vertical empty space...
    config.note = naughty.notify({
        title = config.title,
        text = config.text,
        timeout = config.timeout,
        hover_timeout = config.hover_timeout,
        width = config.width,
        position = config.position,
    })
end

local function colorize (text, color)
   return "<span color='" .. color .. "'>" .. text .. "</span>"
end

local function parse ()
    config.text = nil

    config.count.past   = 0
    config.count.today  = 0
    config.count.soon   = 0
    config.count.future = 0

    local fd = io.open(config.agenda, "r")
    if not fd then
        return
    end

    local text = ""
    -- %d always uses two digits
    local today = os.date("^%A *%d %B %Y"):gsub("\*0+", "*")
    local when = -1 -- <0: past  0: today  >0: future
    for l in fd:lines() do
        -- check if we're processing today
        if l:find("^%w") then
            if l:find(today) then
                when = 0
            elseif when >= 0 then
                when = when + 1
            end
        end
        -- TODO: escape '<' symbol (otherwise pango gets confused)
        if when == 0 then
            -- TODO: are counts always in days? ('In  <num> d.:')
            if l:find("^  [^:]*: *Sched. *%d+x:") or l:find("^  [^:]*: *In 
*-%d+ d\.:") then
                l = colorize(l, config.color.past)
                config.count.past = config.count.past + 1
            elseif l:find("^  [^:]*: *In *%d+ d\.:") then
                local in_days = l:match("^  [^:]*: *In *(%d+) d\.:")
                if tonumber(in_days) <= config.soon then
                    l = colorize(l, config.color.soon)
                    config.count.soon = config.count.soon + 1
                else
                    l = colorize(l, config.color.future)
                    config.count.future = config.count.future + 1
                end
            elseif l:find("^  [^:]*: *Deadline:") or l:find("^  [^:]*: 
*Scheduled:") then
                l = colorize(l, config.color.today)
                config.count.today = config.count.today + 1
            end
        elseif when > config.soon then
            if l:find("^  [^:]*: *Deadline:") or l:find("^  [^:]*: 
*Scheduled:") then
                l = colorize(l, config.color.future)
                config.count.future = config.count.future + 1
            end
        elseif when > 0 then
            if l:find("^  [^:]*: *Deadline:") or l:find("^  [^:]*: 
*Scheduled:") then
                l = colorize(l, config.color.soon)
                config.count.soon = config.count.soon + 1
            end
        end
        if config.todo and when >= 0 then
            l = l:gsub(" TODO ", colorize(" TODO ", config.color.past))
        end
        text = text .. l .. "\n"
    end

    fd:close()

    config.text = "<tt>"..text.."</tt>"
end

local function update ()
    parse()
    if config.text == nil then
        return
    end

    local format = config.format

    format = string.gsub(format, "$past"  , colorize(config.count.past  , 
config.color.past  ))
    format = string.gsub(format, "$today" , colorize(config.count.today , 
config.color.today ))
    format = string.gsub(format, "$soon"  , colorize(config.count.soon  , 
config.color.soon  ))
    format = string.gsub(format, "$future", colorize(config.count.future, 
config.color.future))

    config.widget.text = format
end

--- Create an orgmode widget (a textbox)
-- Shows the specified information when the mouse enters the widget region
--
-- Bindings:
--  * mouse 1 : launch external program
--  * mouse 2 : launch external program that forces an 'agenda' update
--  * mouse 3 : update orgmode information
--
-- @param opts Table with extra options for the widget:
--             update       : seconds between updates
--                            default: 600
--             launch       : program to run on 'mouse 1'
--                            default: none (string)
--             force        : program to run on 'mouse 2'
--                            default: none (string)
--             agenda       : file with org-mode agenda information to show
--                            default: "~/.emacs.d/org-agenda.txt"
--             title        : see naughty
--                            default: "Agenda for this week:"
--             format       : widget's text format
--                            supports:
--                              * $past
--                              * $today
--                              * $soon
--                              * $future
--                            default: "$past/$today/$soon/$future"
--             soon         : number of days in the future for "soon" (higher 
is future)
--                            default: 3
--             todo         : highlight the TODO word
--                            default: true
--             color_*      : colors for 'format' types
--             align        : widget's alignment
--                            default: "right"
--             timeout      : see naughty
--                            default: 0
--             hover_timeout: see naughty
--                            default: 0
--             width        : see naughty
--                            default: 600
--             position     : see naughty
--                            default "top_right"
-- @return The new orgmode widget
function new(opts)
    local align  = opts and opts.align or "right"
    local tupdate= opts and opts.update or 600

    config.widget = nil -- TODO: triggers previous widget's destruction?
    config.widget = widget({ type = "textbox", name = "orgmode", align = align 
})

    local b = { }
    if opts and opts.launch then
        table.insert(b, button({ }, 1, function () 
awful.util.spawn(opts.launch) end))
    end
    if opts and opts.force then
        table.insert(b, button({ }, 2, function () os.execute(opts.force) 
close() update() open() end))
    end
    table.insert(b, button({ }, 3, function() update() close() open() end))

    config.widget:buttons(b)
    config.widget.mouse_enter = open
    config.widget.mouse_leave = close

    awful.hooks.timer.register(tupdate, update)

    config.note          = nil
    config.count         = { }
    -- locations
    config.agenda        = opts and opts.agenda        or 
os.getenv("HOME").."/.emacs.d/org-agenda.txt"
    -- appearance
    config.title         = opts and opts.title         or "Agenda for this 
week:"
    config.format        = opts and opts.format        or 
"$past/$today/$soon/$future"
    config.soon          = opts and opts.soon          or 3
    config.todo          = opts and opts.todo          or true
    -- TODO: maybe we could reuse some colors from 'beautify'...
    config.color = { }
    config.color.past    = opts and opts.color_past    or "#FF0000"
    config.color.today   = opts and opts.color_today   or "#DED200"
    config.color.soon    = opts and opts.color_soon    or "#00D225"
    config.color.future  = opts and opts.color_future  or "#00921A"
    -- naughty
    config.timeout       = opts and opts.timeout       or 0
    config.hover_timeout = opts and opts.hover_timeout or 0
    config.width         = opts and opts.width         or 600
    config.position      = opts and opts.position      or "top_right"

    config.title = config.title .. "   " .. colorize("past", config.color.past)
                   .. "   " .. colorize("today", config.color.today)
                   .. "   " .. colorize("soon", config.color.soon)
                   .. "   " .. colorize("future", config.color.future)

    update()

    return config.widget
end

Reply via email to