/* -*-c-*- */
/* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "config.h"
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <pwd.h>
#include "libs/ftime.h"
#include <sys/stat.h>
#include <sys/types.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xproto.h>
#include <X11/Xatom.h>
#include <X11/Intrinsic.h>
#include "libs/fvwmlib.h"
#include "libs/FShape.h"
#include "libs/Colorset.h"
#include "libs/Module.h"
#include "libs/Rectangles.h"
#include "libs/FGettext.h"
#include "libs/ColorUtils.h"
#include "libs/Graphics.h"
#include "libs/Parse.h"
#include "libs/Strings.h"
#include "Goodies.h"
#include "FvwmTaskBar.h"
#include "Mallocs.h"
#include "minimail.xbm"
#define MAILCHECK_DEFAULT 10
extern Display *dpy;
extern Window Root, win;
extern int Fvwm_fd[2];
extern int screen;
extern ModuleArgs *module;
extern int win_width, win_height, win_y, win_border, RowHeight, Midline;
extern rectangle screen_g;
extern Pixel back, fore;
extern int colorset;
extern GC blackgc, hilite, shadow, checkered;
extern FlocaleWinString *FwinString;
GC statusgc = 0;
GC tipsgc = 0;
FlocaleFont *FStatusFont;
int stwin_width = 100, goodies_width = 0;
int anymail, unreadmail, newmail, mailcleared = 0;
int goodies_fontheight, clock_width;
char *mailpath = NULL;
char *clockfmt = NULL;
char *datefmt = NULL;
Bool do_display_clock = True;
int BellVolume = DEFAULT_BELL_VOLUME;
Pixmap mailpix = None;
Pixmap wmailpix = None;
Pixmap pmask = None;
Pixmap pclip = None;
Bool do_check_mail = True;
Bool has_mailpath = True;
int mailcheck_interval = MAILCHECK_DEFAULT;
char *TipsFore = "black",
*TipsBack = "LightYellow",
*MailCmd = "Exec xterm -e mail";
int tipscolorset = -1;
int IgnoreOldMail = False;
int ShowTips = False;
char *statusfont_string = NULL;
int last_date = -1;
Bool using_MailDir = False;
void cool_get_inboxstatus(void);
#define gray_width 8
#define gray_height 8
extern unsigned char gray_bits[];
/* x y w h tw th open type px py *text win */
TipStruct Tip = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, None };
static void CreateOrUpdateGoodyGC(void)
{
XGCValues gcval;
unsigned long gcmask;
Pixel pfore;
Pixel pback;
if (colorset >= 0)
{
pfore = Colorset[colorset].fg;
pback = Colorset[colorset].bg;
}
else
{
pfore = fore;
pback = back;
}
gcmask = GCForeground | GCBackground | GCGraphicsExposures;
gcval.foreground = pfore;
gcval.background = pback;
gcval.graphics_exposures = False;
if (FStatusFont->font != NULL)
{
gcval.font = FStatusFont->font->fid;
gcmask = GCFont;
}
if (statusgc)
XChangeGC(dpy, statusgc, gcmask, &gcval);
else
statusgc = fvwmlib_XCreateGC(dpy, win, gcmask, &gcval);
if (do_check_mail)
{
if (mailpix)
XFreePixmap(dpy, mailpix);
mailpix = XCreatePixmapFromBitmapData(
dpy, win, (char *)minimail_bits, minimail_width, minimail_height,
pfore, pback, Pdepth);
if (wmailpix)
XFreePixmap(dpy, wmailpix);
wmailpix = XCreatePixmapFromBitmapData(
dpy, win, (char *)minimail_bits, minimail_width, minimail_height,
PictureBlackPixel(), PictureWhitePixel(), Pdepth);
goodies_width += minimail_width + 7;
}
else if (do_display_clock)
{
goodies_width += 3;
}
else
{
goodies_width += 0;
}
}
Bool change_goody_colorset(int cset, Bool force)
{
if (cset < 0)
{
return False;
}
if (cset == tipscolorset && Tip.win != None)
{
char *s;
if (Tip.text != NULL)
{
s = safestrdup(Tip.text);
}
else
{
s = NULL;
}
PopupTipWindow(Tip.px, Tip.py, s);
if (s != NULL)
{
free(s);
}
}
if (!force && cset != colorset)
{
return False;
}
CreateOrUpdateGoodyGC();
return True;
}
static char *goodyopts[] =
{
"BellVolume",
"Mailbox",
"Mailcheck",
"ClockFormat",
"StatusFont",
"TipsFore",
"TipsBack",
"TipsColorset",
"MailCommand",
"IgnoreOldMail",
"ShowTips",
"DateFormat",
"MailDir",
NULL
};
/* Parse 'goodies' specific resources */
Bool GoodiesParseConfig(char *tline)
{
char *rest;
char *option;
int i;
option = tline + module->namelen+1;
i = GetTokenIndex(option, goodyopts, -1, &rest);
while (*rest && *rest != '\n' && isspace(*rest))
rest++;
switch(i)
{
case 0: /* BellVolume */
BellVolume = atoi(rest);
break;
case 1: /* Mailbox */
if (strcasecmp(rest, "None") == 0)
{
has_mailpath = False;
}
else
{
int len;
UpdateString(&mailpath, rest);
len = strlen(mailpath);
if (len > 0 && mailpath[len-1] == '\n')
mailpath[len-1] = 0;
has_mailpath = True;
}
do_check_mail = (has_mailpath && (mailcheck_interval > 0));
break;
case 2: /* Mailcheck */
mailcheck_interval = MAILCHECK_DEFAULT;
sscanf(rest, "%d", &mailcheck_interval);
do_check_mail = (has_mailpath && (mailcheck_interval > 0));
break;
case 3: /* ClockFormat */
UpdateString(&clockfmt, rest);
do_display_clock = True;
if (clockfmt && clockfmt[0] == '\0' && datefmt && datefmt[0] == '\0')
{
do_display_clock = False;
}
break;
case 4: /* StatusFont */
CopyStringWithQuotes(&statusfont_string, rest);
break;
case 5: /* TipsFore */
CopyString(&TipsFore, rest);
tipscolorset = -1;
break;
case 6: /* TipsBack */
CopyString(&TipsBack, rest);
tipscolorset = -1;
break;
case 7: /* TipsColorset */
tipscolorset = -1;
tipscolorset = atoi(rest);
AllocColorset(tipscolorset);
break;
case 8: /* MailCommand */
CopyString(&MailCmd, rest);
break;
case 9: /* IgnoreOldMail */
IgnoreOldMail = True;
break;
case 10: /* ShowTips */
ShowTips = True;
break;
case 11: /* DateFormat */
UpdateString(&datefmt, rest);
do_display_clock = True;
if (clockfmt && clockfmt[0] == '\0' && datefmt && datefmt[0] == '\0')
{
do_display_clock = False;
}
break;
case 12: /* MailDir */
using_MailDir = True;
break;
default:
/* unknown option */
return False;
} /* switch */
return True;
}
void LoadGoodiesFont(void)
{
if ((FStatusFont =
FlocaleLoadFont(dpy, statusfont_string, module->name)) == NULL)
{
fprintf(stderr, "%s: Couldn't load font. Exiting!\n",
module->name);
exit(1);
}
goodies_fontheight = FStatusFont->height;
}
void InitGoodies(void)
{
struct passwd *pwent;
char tmp[1024];
if (mailpath == NULL)
{
strcpy(tmp, DEFAULT_MAIL_PATH);
pwent = getpwuid(getuid());
strcat(tmp, (char *) (pwent->pw_name));
UpdateString(&mailpath, tmp);
}
CreateOrUpdateGoodyGC();
if (clockfmt)
{
if (!do_display_clock)
{
clock_width = 0;
}
else
{
struct tm *tms;
static time_t timer;
static char str[24];
time(&timer);
tms = localtime(&timer);
strftime(str, 24, clockfmt, tms);
clock_width = FlocaleTextWidth(
FStatusFont, str, strlen(str)) + 4;
}
}
else
{
clock_width = FlocaleTextWidth(FStatusFont, "XX:XX", 5) + 4;
}
goodies_width += clock_width;
stwin_width = goodies_width;
}
void Draw3dBox(Window wn, int x, int y, int w, int h, XRectangle *bounding)
{
XRectangle r;
if (bounding)
{
r.x = bounding->x;
r.y = bounding->y;
r.width = bounding->width;
r.height = bounding->height;
}
else
{
r.x = x;
r.y = y;
r.width = w;
r.height = h;
}
XClearArea(dpy, wn, r.x, r.y, r.width, r.height, False);
XDrawLine(dpy, win, shadow, x, y, x+w-2, y);
XDrawLine(dpy, win, shadow, x, y, x, y+h-2);
XDrawLine(dpy, win, hilite, x, y+h-1, x+w-1, y+h-1);
XDrawLine(dpy, win, hilite, x+w-1, y+h-1, x+w-1, y);
}
void DrawGoodies(XEvent *evp)
{
XRectangle rect;
struct tm *tms;
static char str[40];
static time_t timer;
static int last_mail_check = -1;
int x = win_width - stwin_width;
int y = 0;
int w = stwin_width;
int h = RowHeight;
Region t_region;
if (goodies_width == 0)
{
return;
}
if (evp)
{
if (!frect_get_intersection(
x, y, w, h,
evp->xexpose.x, evp->xexpose.y,
evp->xexpose.width, evp->xexpose.height,
&rect))
{
return;
}
}
else
{
rect.x = x;
rect.y = y;
rect.width = w;
rect.height = h;
}
time(&timer);
tms = localtime(&timer);
if (clockfmt)
{
strftime(str, 24, clockfmt, tms);
if (str[0] == '0') str[0]=' ';
}
else
{
strftime(str, 15, "%R", tms);
}
Draw3dBox(win, x, y, w, h, &rect);
t_region = XCreateRegion();
XUnionRectWithRegion (&rect, t_region, t_region);
FwinString->win = win;
FwinString->gc = statusgc;
FwinString->str = str;
FwinString->x = x + 4;
FwinString->y = ((RowHeight - goodies_fontheight) >> 1) +
FStatusFont->ascent;
FwinString->flags.has_clip_region = True;
FwinString->clip_region = t_region;
if (colorset >= 0)
{
FwinString->colorset = &Colorset[colorset];
FwinString->flags.has_colorset = True;
}
else
{
FwinString->flags.has_colorset = False;
}
FlocaleDrawString(dpy, FStatusFont, FwinString, 0);
FwinString->flags.has_clip_region = False;
XDestroyRegion(t_region);
if (!do_check_mail)
{
return;
}
if (timer - last_mail_check >= mailcheck_interval)
{
cool_get_inboxstatus();
last_mail_check = timer;
if (newmail)
XBell(dpy, BellVolume);
}
if (!mailcleared && (unreadmail || newmail))
{
XCopyArea(dpy, wmailpix, win, statusgc, 0, 0,
minimail_width, minimail_height,
win_width - stwin_width + clock_width + 3,
((RowHeight - minimail_height) >> 1));
}
else if (!IgnoreOldMail /*&& !mailcleared*/ && anymail)
{
XCopyArea(dpy, mailpix, win, statusgc, 0, 0,
minimail_width, minimail_height,
win_width - stwin_width + clock_width + 3,
((RowHeight - minimail_height) >> 1));
}
if (Tip.open)
{
if (Tip.type == DATE_TIP)
{
if (tms->tm_mday != last_date)
{
/* reflect date change */
CreateDateWindow(); /* This automatically
* deletes any old window */
}
}
last_date = tms->tm_mday;
RedrawTipWindow();
}
}
int MouseInClock(int x, int y)
{
int clockl = win_width - stwin_width;
int clockr = win_width - stwin_width + clock_width + (do_check_mail ? 2 : 3);
return (x>clockl && x<clockr && y>1 && y<RowHeight-2);
}
int MouseInMail(int x, int y)
{
int maill = win_width - stwin_width + clock_width + 2;
int mailr = win_width - (do_check_mail ? 0 : 3);
return (x>=maill && x<mailr && y>1 && y<RowHeight-2);
}
void CreateDateWindow(void)
{
struct tm *tms;
static time_t timer;
static char str[40];
time(&timer);
tms = localtime(&timer);
if (datefmt) {
strftime(str, 40, datefmt, tms);
} else {
strftime(str, 40, "%A, %B %d, %Y", tms);
}
last_date = tms->tm_mday;
PopupTipWindow(win_width, 0, str);
}
void CreateMailTipWindow(void)
{
const char *str;
if (!anymail)
{
str = _("No new mail");
}
else if (newmail || unreadmail)
{
str = _("You have new mail");
}
else
{
str = _("You have mail");
}
PopupTipWindow(win_width, 0, str);
}
void RedrawTipWindow(void)
{
/* FIXME: I am sure that we redraw too often */
if (Tip.text) {
FwinString->win = Tip.win;
FwinString->gc = tipsgc;
FwinString->str = Tip.text;
FwinString->x = 3;
FwinString->y = 2 + FStatusFont->ascent;
FwinString->flags.has_clip_region = False;
if (tipscolorset >= 0)
{
FwinString->colorset = &Colorset[tipscolorset];
FwinString->flags.has_colorset = True;
}
else
{
FwinString->flags.has_colorset = False;
}
if (FftSupport && FStatusFont->fftf.fftfont != NULL)
{
XClearArea(dpy, Tip.win, 0, 0, Tip.w, Tip.h, False);
}
FlocaleDrawString(dpy, FStatusFont, FwinString, 0);
XRaiseWindow(dpy, Tip.win);
}
}
void PopupTipWindow(int px, int py, const char *text)
{
int newx, newy;
Window child;
Tip.px = px;
Tip.py = py;
if (!ShowTips)
return;
if (Tip.win != None)
DestroyTipWindow();
Tip.tw = FlocaleTextWidth(FStatusFont, (char *)text, strlen(text)) + 6;
Tip.th = FStatusFont->height + 4;
XTranslateCoordinates(dpy, win, Root, px, py, &newx, &newy, &child);
Tip.x = newx;
/* if (win_y == win_border) */
if (win_y < Midline)
Tip.y = newy + RowHeight;
else
Tip.y = newy - Tip.th -2;
Tip.w = Tip.tw;
Tip.h = Tip.th;
if (Tip.x + Tip.tw + 4 > screen_g.x + screen_g.width - 5)
Tip.x = screen_g.x + screen_g.width - Tip.tw - 9;
if (Tip.x < screen_g.x + 5)
Tip.x = screen_g.x + 5;
UpdateString(&Tip.text, text);
CreateTipWindow(Tip.x, Tip.y, Tip.w, Tip.h);
if (Tip.open)
XMapRaised(dpy, Tip.win);
}
void ShowTipWindow(int open)
{
if (!ShowTips) return;
if (open)
{
if (Tip.win != None)
{
XMapRaised(dpy, Tip.win);
RedrawTipWindow();
}
}
else
{
XUnmapWindow(dpy, Tip.win);
}
Tip.open = open;
}
void CreateTipWindow(int x, int y, int w, int h)
{
unsigned long gcmask;
unsigned long winattrmask = CWBackPixel | CWBorderPixel | CWEventMask |
CWColormap |CWSaveUnder | CWOverrideRedirect;
XSetWindowAttributes winattr;
GC cgc = None;
GC gc0 = None;
GC gc1 = None;
XGCValues gcval;
Pixmap pchk = None;
Pixel tip_fore;
Pixel tip_back;
colorset_t *cset = NULL;
if (tipscolorset >= 0)
{
cset = &Colorset[tipscolorset];
tip_fore = cset->fg;
tip_back = cset->bg;
}
else
{
tip_fore = GetColor(TipsFore);
tip_back = GetColor(TipsBack);
}
winattr.background_pixel = tip_back;
winattr.border_pixel = PictureBlackPixel();
winattr.colormap = Pcmap;
winattr.override_redirect = True;
winattr.save_under = True;
winattr.event_mask = ExposureMask;
Tip.win = XCreateWindow(dpy, Root, x, y, w+4, h+4, 0, Pdepth, InputOutput,
Pvisual, winattrmask, &winattr);
gcmask = GCForeground | GCBackground | GCGraphicsExposures;
gcval.graphics_exposures = False;
gcval.foreground = tip_fore;
gcval.background = tip_back;
if (FStatusFont->font != NULL)
{
gcval.font = FStatusFont->font->fid;
gcmask |= GCFont;
}
if (tipsgc)
XChangeGC(dpy, tipsgc, gcmask, &gcval);
else
tipsgc = fvwmlib_XCreateGC(dpy, Tip.win, gcmask, &gcval);
pmask = XCreatePixmap(dpy, Tip.win, w+4, h+4, 1);
pclip = XCreatePixmap(dpy, Tip.win, w+4, h+4, 1);
gcmask = GCForeground | GCBackground | GCFillStyle | GCStipple |
GCGraphicsExposures;
gcval.foreground = 1;
gcval.background = 0;
gcval.fill_style = FillStippled;
if (pchk == None)
pchk = XCreatePixmapFromBitmapData(dpy, Tip.win, (char *)gray_bits,
gray_width, gray_height, 1, 0, 1);
gcval.stipple = pchk;
cgc = fvwmlib_XCreateGC(dpy, pmask, gcmask, &gcval);
gcmask = GCForeground | GCBackground | GCGraphicsExposures | GCFillStyle;
gcval.graphics_exposures = False;
gcval.fill_style = FillSolid;
gcval.foreground = 0;
gcval.background = 0;
gc0 = fvwmlib_XCreateGC(dpy, pmask, gcmask, &gcval);
gcval.foreground = 1;
gcval.background = 1;
gc1 = fvwmlib_XCreateGC(dpy, pmask, gcmask, &gcval);
XFillRectangle(dpy, pmask, gc0, 0, 0, w+4, h+4);
XFillRectangle(dpy, pmask, cgc, 3, 3, w+4, h+4);
XFillRectangle(dpy, pmask, gc1, 0, 0, w+1, h+1);
XFillRectangle(dpy, pclip, gc0, 0, 0, w+4, h+4);
XFillRectangle(dpy, pclip, gc1, 1, 1, w-1, h-1);
if (FShapesSupported)
{
FShapeCombineMask(dpy, Tip.win, FShapeBounding, 0, 0, pmask, FShapeSet);
FShapeCombineMask(dpy, Tip.win, FShapeClip, 0, 0, pclip, FShapeSet);
}
if (tipscolorset >= 0 && (cset->pixmap || cset->shape_mask))
{
SetWindowBackground(
dpy, Tip.win, w + 4, h + 4, cset, Pdepth, tipsgc, False);
}
XFreeGC(dpy, gc0);
XFreeGC(dpy, gc1);
XFreeGC(dpy, cgc);
XFreePixmap(dpy, pchk);
}
void DestroyTipWindow(void)
{
XFreePixmap(dpy, pmask);
XFreePixmap(dpy, pclip);
XDestroyWindow(dpy, Tip.win);
if (Tip.text)
{
free(Tip.text);
Tip.text = NULL;
}
Tip.win = None;
}
/*-----------------------------------------------------*/
/* Get file modification time */
/* (based on the code of 'coolmail' By Byron C. Darrah */
/*-----------------------------------------------------*/
void cool_get_inboxstatus_mbox(void)
{
static off_t oldsize = 0;
off_t newsize;
struct stat st;
int fd;
fd = open (mailpath, O_RDONLY, 0);
if (fd < 0)
{
anymail = 0;
newmail = 0;
unreadmail = 0;
newsize = 0;
}
else
{
fstat(fd, &st);
close(fd);
newsize = st.st_size;
if (newsize > 0)
anymail = 1;
else
anymail = 0;
if (st.st_mtime >= st.st_atime && newsize > 0)
unreadmail = 1;
else
unreadmail = 0;
if (newsize > oldsize && unreadmail) {
newmail = 1;
mailcleared = 0;
}
else
newmail = 0;
}
oldsize = newsize;
}
static int f_scandir(const char *dir, struct dirent *** namelist)
{
DIR * f_dir;
struct dirent * f_temp;
/* 10 is just an arbituary number */
int f_count = 0, namelist_size = 10;
void * r_ret;
f_dir = opendir(dir);
if (f_dir == NULL)
return -1;
namelist[0] =
(struct dirent **)safemalloc(sizeof(struct dirent *) * 10);
do {
f_temp = readdir(f_dir);
if (f_temp == NULL)
break;
else if (strcmp(f_temp->d_name, ".") != 0 &&
strcmp(f_temp->d_name, "..") != 0)
{
if (f_count > namelist_size)
{
r_ret = saferealloc(
(void *)(namelist[0]),
sizeof(struct dirent) * (f_count +10));
namelist[0] = (struct dirent **)r_ret;
namelist_size = f_count + 10;
}
namelist[0][f_count] = (struct dirent *)safemalloc(
sizeof(struct dirent));
memcpy(
namelist[0][f_count], f_temp,
sizeof(struct dirent));
f_count++;
}
} while (1);
return f_count;
}
void cool_get_inboxstatus_maildir(void)
{
static int oldsize = 0;
int newsize;
struct dirent **newlist;
struct dirent **curlist;
char * alt_mailpath;
int curent, newent;
alt_mailpath = safemalloc(strlen(mailpath) + strlen("/new") + 1);
strcpy(alt_mailpath, mailpath);
strcat(alt_mailpath, "/new");
newent = f_scandir(alt_mailpath, &newlist);
strcpy(alt_mailpath, mailpath);
strcat(alt_mailpath, "/cur");
curent = f_scandir(alt_mailpath, &curlist);
free(alt_mailpath);
newsize = curent + newent;
if (newent > 0)
{
anymail = 1;
unreadmail = 1;
}
else if (curent > 0)
{
anymail = 1;
unreadmail = 0;
}
else
{
anymail = 0;
unreadmail = 0;
}
if (newsize > oldsize && unreadmail)
{
newmail = 1;
mailcleared = 0;
}
else
{
newmail = 0;
}
oldsize = newsize;
while(curent--)
{
free(curlist[curent]);
}
free(curlist);
while(newent--)
{
free(newlist[newent]);
}
free(newlist);
}
void cool_get_inboxstatus(void)
{
if (using_MailDir)
{
cool_get_inboxstatus_maildir();
} else {
cool_get_inboxstatus_mbox();
}
}
/*---------------------------------------------------------------------------*/
void HandleMailClick(XEvent event)
{
static Time lastclick = 0;
if (event.xbutton.time - lastclick < 250)
{
SendText(Fvwm_fd, MailCmd, 0);
}
lastclick = event.xbutton.time;
mailcleared = 1;
}
syntax highlighted by Code2HTML, v. 0.9.1