Cyberborean Chronicles

Getting new mail onto the desktop

I had nice last weekend gathering new harvest of apples, drinking fresh apple juice and playing with SuperKaramba widgets – a good opportunity to take a sort of “recreational programming”. Perhaps all modern KDE users know those nice resource eaters eye-candies which are living right on the desktop surface and displaying the clocks, calendars, weather forecasts, system monitors and so on.

Instead of developing some Yet Another Big Animated Clock, I decided to write something practical. What I’d like to have is a widget which would ask my e-mail client (KMail) for the headers of the latest unread messages to show them on the desktop.

skkmail.jpg

Two buttons in the top bar are for checking new mail from a server and for opening new message window. Clicking on the top bar somewhere else will open main KMail window.

SuperKaramba comes with a Python API, so everyone who is familiar with Pyton basics can write her own widget (aka “theme”) as a Python script. In my case, I wrote two scripts – one is for widget output and another as a simple KMail folder API wrapped around DCOP calls.

Patching KMail

There was a major problem – KMail DCOP interface had no a call to get unread message headers; only getting a number of them is possible. So I have had to dig into the KMail sourcecode (my current version is 1.9.4) for to add new function to FolderIFace interface (folderIface.cpp).

kmail.diff:

diff kmail/folderIface.cpp kmail_patched/folderIface.cpp
147a148,162
> QStringList
> FolderIface::unreadMessageHeaders()
> {
>      QStringList msgs;
>      mFolder->open();
>      for( int i = 0; i < mFolder->count(); i++) {
>          KMMsgBase *msg = mFolder->getMsgBase(i);
> 	     if (msg->isNew() || msg->isUnread()) {
> 	       msgs.append(msg->fromStrip() + "\\t" + msg->subject() + "\\t" + msg->dateStr());
>          }
>      }
>      mFolder->close();
>      return msgs;
> }
>
diff kmail/folderIface.h kmail_patched/folderIface.h
61c61,62
<
---
>     virtual QStringList unreadMessageHeaders();
>

After adding new function header to folderIface.h and rebuilding KMail, new DCOP call “kmail FolderIface unreadMessageHeaders” became available to get the list of message headers in format “From/Subject/Date” divided by tab characters (\t).

Sourcecode

skkmail.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
import karamba
import kmail
 
text = []
images = []
clickAreas = []
 
# Define widget area width here (height is adjusted automatically)
s_width = 350
 
# If an actual height is above this value, a widget area will be clipped
maxHeight = 600
 
# Margins inside a widget area
s_x = 10
s_y = 10
 
def _update(widget):
    global text, images, clickAreas, s_width, s_x, s_y
    x_gap = 10
    y_gap = 4
 
    s_height = 14
    s_col1 = (s_width-x_gap)*0.66
    s_col2 = s_width
    s_col3 = (s_width-x_gap-16)*0.33
 
    folderFont = "Franklin Gothic Medium"
    folderFontSize = 14
 
    for t in text:
        try:
          karamba.deleteText(widget, t)
        except:
          pass
    for i in images:
        try:
          karamba.deleteImage(widget, i)
        except:
          pass
    for c in clickAreas:
        try:
          karamba.removeClickArea(widget, c)
        except:
          pass
    text = []
    images = []
    clickAreas = []
    folders = kmail.getFolders()
    y = s_y + 25;
    for f in folders:
        msgs = f.unreadMessages
        if (len(msgs) > 0):
            y0 = y
            t = karamba.createText(widget, s_x+15, y, s_width, s_height,
                f.name+" ("+str(len(msgs))+")")
            karamba.changeTextFont(widget, t, folderFont)
            karamba.changeTextSize(widget, t, folderFontSize)
            text.append(t)
            images.append(karamba.createImage(widget, s_x-10, y, "icons/folder.png"))
            y = y + s_height + y_gap*2 +10
            for m in msgs:
                t = karamba.createText(widget, s_x+20, y, s_col1, s_height, m[0])
                text.append(t)
                t = karamba.createText(widget, s_x, y+s_height, s_col2, s_height, m[1])
                text.append(t)
                t = karamba.createText(widget, s_x+20+s_col1+x_gap, y, s_col3, s_height, m[2])
                karamba.setTextAlign(widget, t, "RIGHT")
                text.append(t)
                images.append(karamba.createImage(widget, s_x, y, "icons/mail.png"))
                y = y + s_height*2 + y_gap*2
            clickAreas.append(karamba.createClickArea(widget, s_x-10, y0, s_width, y-y0,
                "dcop kmail KMailIface selectFolder "+f.path+" && kmail"))
            y = y + y_gap*2
    if (y < maxHeight - 20):
        karamba.resizeWidget(widget, s_width+s_x*2, y+20)
 
#this is called when you widget is initialized
def initWidget(widget):
    bgImage = karamba.createBackgroundImage(widget, 0, 5, "icons/bg.png")
    karamba.resizeImage(widget, bgImage, s_width+s_x*2, 20)
    logo = karamba.createImage(widget, 0, 0, "icons/kmail.png")
    t = karamba.createText(widget, 35, 5, 100, 20, "new mail")
    karamba.changeTextFont(widget, t, "Franklin Gothic Medium")
    karamba.changeTextSize(widget, t, 16)
    karamba.createClickArea(widget, 0, 0, 100, 30, "kmail")
    karamba.createImage(widget, s_width+s_x*2-18, 7, "icons/mail_get.png")
    karamba.createClickArea(widget, s_width+s_x*2-18, 7, 16, 16, "dcop kmail KMailIface checkMail")
    karamba.createImage(widget, s_width+s_x*2-40, 7, "icons/mail_new.png")
    karamba.createClickArea(widget, s_width+s_x*2-40, 7, 16, 16,
        "dcop kmail KMailIface openComposer '' '' '' '' '' ''")
    _update(widget)
 
#this is called everytime your widget is updated
#the update inverval is specified in the .theme file
def widgetUpdated(widget):
    _update(widget)
 
#This gets called everytime our widget is clicked.
# Middle button click forces to check new mail
def widgetClicked(widget, x, y, button):
    if (button == 2):
        kmail.dcopCall("kmail KMailIface checkMail")

kmail.py:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#!/usr/bin/python
 
import os, subprocess, string
 
def dcopCall(call):
  cmd = "dcop "+call
  pipe = subprocess.Popen(cmd, shell=True, bufsize=1024, stdout=subprocess.PIPE).stdout
  res = pipe.read()
  pipe.close()
  return res
 
class Folder:
  path = ""
  name = ""
  unreadCount = 0
  unreadMessages = []
 
  def __init__(self, path):
    dcopCall("kmail KMailIface getFolder "+path)
    self.path = path
    self.unreadMessages = self.getUnreadMessages()
 
  def getUnreadMessages(self):
    r = dcopCall("kmail FolderIface unreadMessageHeaders")
    ss = string.split(r, "\\n")
    msg = []
    for s in ss:
        if (len(s) > 0):
            msg.append(string.split(s, "\\t"))
    unreadCount = len(msg)
    if (unreadCount > 0):
        self.name = string.strip(dcopCall("kmail FolderIface displayName"))
    msg.reverse()
    return msg
 
def getFolders():
    r = dcopCall("kmail KMailIface folderList")
    folders = []
    for f in string.split(r, "\\n"):
        folders.append(Folder(f))
    return folders

skkmail.theme:

1
2
3
# you can change initial widget position, area size and updatin interval (default = 1min)
karamba x=100 y=100 w=400 h=200 interval=60000
defaultfont font="Franklin Gothic Book" fontsize=12 color=230,230,230

All code above is GPL‘ed.

Leave a Reply