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.

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 &lt; 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.


