2 /* This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
24 #include "libs/ftime.h"
26 #include <sys/types.h>
28 #include <X11/Xutil.h>
29 #include <X11/Xproto.h>
30 #include <X11/Xatom.h>
31 #include <X11/Intrinsic.h>
33 #include "libs/fvwmlib.h"
34 #include "libs/FShape.h"
35 #include "libs/Colorset.h"
36 #include "libs/Module.h"
37 #include "libs/Rectangles.h"
38 #include "libs/FGettext.h"
39 #include "libs/ColorUtils.h"
40 #include "libs/Graphics.h"
41 #include "libs/Parse.h"
42 #include "libs/Strings.h"
44 #include "FvwmTaskBar.h"
46 #include "minimail.xbm"
48 #define MAILCHECK_DEFAULT 10
51 extern Window Root
, win
;
52 extern int Fvwm_fd
[2];
54 extern ModuleArgs
*module
;
55 extern int win_width
, win_height
, win_y
, win_border
, RowHeight
, Midline
;
56 extern rectangle screen_g
;
57 extern Pixel back
, fore
;
59 extern GC blackgc
, hilite
, shadow
, checkered
;
60 extern FlocaleWinString
*FwinString
;
64 FlocaleFont
*FStatusFont
;
65 int stwin_width
= 100, goodies_width
= 0;
66 int anymail
, unreadmail
, newmail
, mailcleared
= 0;
67 int goodies_fontheight
, clock_width
;
68 char *mailpath
= NULL
;
69 char *clockfmt
= NULL
;
71 Bool do_display_clock
= True
;
72 int BellVolume
= DEFAULT_BELL_VOLUME
;
73 Pixmap mailpix
= None
;
74 Pixmap wmailpix
= None
;
77 Bool do_check_mail
= True
;
78 Bool has_mailpath
= True
;
79 int mailcheck_interval
= MAILCHECK_DEFAULT
;
80 char *TipsFore
= "black",
81 *TipsBack
= "LightYellow",
82 *MailCmd
= "Exec xterm -e mail";
83 int tipscolorset
= -1;
84 int IgnoreOldMail
= False
;
86 char *statusfont_string
= NULL
;
88 Bool using_MailDir
= False
;
90 void cool_get_inboxstatus(void);
94 extern unsigned char gray_bits
[];
96 /* x y w h tw th open type px py *text win */
97 TipStruct Tip
= { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL
, None
};
100 static void CreateOrUpdateGoodyGC(void)
103 unsigned long gcmask
;
109 pfore
= Colorset
[colorset
].fg
;
110 pback
= Colorset
[colorset
].bg
;
117 gcmask
= GCForeground
| GCBackground
| GCGraphicsExposures
;
118 gcval
.foreground
= pfore
;
119 gcval
.background
= pback
;
120 gcval
.graphics_exposures
= False
;
121 if (FStatusFont
->font
!= NULL
)
123 gcval
.font
= FStatusFont
->font
->fid
;
127 XChangeGC(dpy
, statusgc
, gcmask
, &gcval
);
129 statusgc
= fvwmlib_XCreateGC(dpy
, win
, gcmask
, &gcval
);
134 XFreePixmap(dpy
, mailpix
);
135 mailpix
= XCreatePixmapFromBitmapData(
136 dpy
, win
, (char *)minimail_bits
, minimail_width
, minimail_height
,
137 pfore
, pback
, Pdepth
);
139 XFreePixmap(dpy
, wmailpix
);
140 wmailpix
= XCreatePixmapFromBitmapData(
141 dpy
, win
, (char *)minimail_bits
, minimail_width
, minimail_height
,
142 PictureBlackPixel(), PictureWhitePixel(), Pdepth
);
143 goodies_width
+= minimail_width
+ 7;
145 else if (do_display_clock
)
156 Bool
change_goody_colorset(int cset
, Bool force
)
162 if (cset
== tipscolorset
&& Tip
.win
!= None
)
166 if (Tip
.text
!= NULL
)
168 s
= safestrdup(Tip
.text
);
174 PopupTipWindow(Tip
.px
, Tip
.py
, s
);
180 if (!force
&& cset
!= colorset
)
184 CreateOrUpdateGoodyGC();
189 static char *goodyopts
[] =
207 /* Parse 'goodies' specific resources */
208 Bool
GoodiesParseConfig(char *tline
)
214 option
= tline
+ module
->namelen
+1;
215 i
= GetTokenIndex(option
, goodyopts
, -1, &rest
);
216 while (*rest
&& *rest
!= '\n' && isspace(*rest
))
220 case 0: /* BellVolume */
221 BellVolume
= atoi(rest
);
223 case 1: /* Mailbox */
224 if (strcasecmp(rest
, "None") == 0)
226 has_mailpath
= False
;
232 UpdateString(&mailpath
, rest
);
233 len
= strlen(mailpath
);
234 if (len
> 0 && mailpath
[len
-1] == '\n')
238 do_check_mail
= (has_mailpath
&& (mailcheck_interval
> 0));
240 case 2: /* Mailcheck */
241 mailcheck_interval
= MAILCHECK_DEFAULT
;
242 sscanf(rest
, "%d", &mailcheck_interval
);
243 do_check_mail
= (has_mailpath
&& (mailcheck_interval
> 0));
245 case 3: /* ClockFormat */
246 UpdateString(&clockfmt
, rest
);
247 do_display_clock
= True
;
248 if (clockfmt
&& clockfmt
[0] == '\0' && datefmt
&& datefmt
[0] == '\0')
250 do_display_clock
= False
;
253 case 4: /* StatusFont */
254 CopyStringWithQuotes(&statusfont_string
, rest
);
256 case 5: /* TipsFore */
257 CopyString(&TipsFore
, rest
);
260 case 6: /* TipsBack */
261 CopyString(&TipsBack
, rest
);
264 case 7: /* TipsColorset */
266 tipscolorset
= atoi(rest
);
267 AllocColorset(tipscolorset
);
269 case 8: /* MailCommand */
270 CopyString(&MailCmd
, rest
);
272 case 9: /* IgnoreOldMail */
273 IgnoreOldMail
= True
;
275 case 10: /* ShowTips */
278 case 11: /* DateFormat */
279 UpdateString(&datefmt
, rest
);
280 do_display_clock
= True
;
281 if (clockfmt
&& clockfmt
[0] == '\0' && datefmt
&& datefmt
[0] == '\0')
283 do_display_clock
= False
;
286 case 12: /* MailDir */
287 using_MailDir
= True
;
297 void LoadGoodiesFont(void)
300 FlocaleLoadFont(dpy
, statusfont_string
, module
->name
)) == NULL
)
302 fprintf(stderr
, "%s: Couldn't load font. Exiting!\n",
306 goodies_fontheight
= FStatusFont
->height
;
309 void InitGoodies(void)
311 struct passwd
*pwent
;
314 if (mailpath
== NULL
)
316 strcpy(tmp
, DEFAULT_MAIL_PATH
);
317 pwent
= getpwuid(getuid());
318 strcat(tmp
, (char *) (pwent
->pw_name
));
319 UpdateString(&mailpath
, tmp
);
322 CreateOrUpdateGoodyGC();
325 if (!do_display_clock
)
335 tms
= localtime(&timer
);
336 strftime(str
, 24, clockfmt
, tms
);
337 clock_width
= FlocaleTextWidth(
338 FStatusFont
, str
, strlen(str
)) + 4;
343 clock_width
= FlocaleTextWidth(FStatusFont
, "XX:XX", 5) + 4;
345 goodies_width
+= clock_width
;
346 stwin_width
= goodies_width
;
349 void Draw3dBox(Window wn
, int x
, int y
, int w
, int h
, XRectangle
*bounding
)
357 r
.width
= bounding
->width
;
358 r
.height
= bounding
->height
;
367 XClearArea(dpy
, wn
, r
.x
, r
.y
, r
.width
, r
.height
, False
);
369 XDrawLine(dpy
, win
, shadow
, x
, y
, x
+w
-2, y
);
370 XDrawLine(dpy
, win
, shadow
, x
, y
, x
, y
+h
-2);
372 XDrawLine(dpy
, win
, hilite
, x
, y
+h
-1, x
+w
-1, y
+h
-1);
373 XDrawLine(dpy
, win
, hilite
, x
+w
-1, y
+h
-1, x
+w
-1, y
);
376 void DrawGoodies(XEvent
*evp
)
382 static int last_mail_check
= -1;
383 int x
= win_width
- stwin_width
;
389 if (goodies_width
== 0)
395 if (!frect_get_intersection(
397 evp
->xexpose
.x
, evp
->xexpose
.y
,
398 evp
->xexpose
.width
, evp
->xexpose
.height
,
412 tms
= localtime(&timer
);
415 strftime(str
, 24, clockfmt
, tms
);
416 if (str
[0] == '0') str
[0]=' ';
420 strftime(str
, 15, "%R", tms
);
423 Draw3dBox(win
, x
, y
, w
, h
, &rect
);
424 t_region
= XCreateRegion();
425 XUnionRectWithRegion (&rect
, t_region
, t_region
);
426 FwinString
->win
= win
;
427 FwinString
->gc
= statusgc
;
428 FwinString
->str
= str
;
429 FwinString
->x
= x
+ 4;
430 FwinString
->y
= ((RowHeight
- goodies_fontheight
) >> 1) +
432 FwinString
->flags
.has_clip_region
= True
;
433 FwinString
->clip_region
= t_region
;
436 FwinString
->colorset
= &Colorset
[colorset
];
437 FwinString
->flags
.has_colorset
= True
;
441 FwinString
->flags
.has_colorset
= False
;
443 FlocaleDrawString(dpy
, FStatusFont
, FwinString
, 0);
444 FwinString
->flags
.has_clip_region
= False
;
445 XDestroyRegion(t_region
);
451 if (timer
- last_mail_check
>= mailcheck_interval
)
453 cool_get_inboxstatus();
454 last_mail_check
= timer
;
456 XBell(dpy
, BellVolume
);
459 if (!mailcleared
&& (unreadmail
|| newmail
))
461 XCopyArea(dpy
, wmailpix
, win
, statusgc
, 0, 0,
462 minimail_width
, minimail_height
,
463 win_width
- stwin_width
+ clock_width
+ 3,
464 ((RowHeight
- minimail_height
) >> 1));
466 else if (!IgnoreOldMail
/*&& !mailcleared*/ && anymail
)
468 XCopyArea(dpy
, mailpix
, win
, statusgc
, 0, 0,
469 minimail_width
, minimail_height
,
470 win_width
- stwin_width
+ clock_width
+ 3,
471 ((RowHeight
- minimail_height
) >> 1));
476 if (Tip
.type
== DATE_TIP
)
478 if (tms
->tm_mday
!= last_date
)
480 /* reflect date change */
481 CreateDateWindow(); /* This automatically
482 * deletes any old window */
485 last_date
= tms
->tm_mday
;
490 int MouseInClock(int x
, int y
)
492 int clockl
= win_width
- stwin_width
;
493 int clockr
= win_width
- stwin_width
+ clock_width
+ (do_check_mail
? 2 : 3);
494 return (x
>clockl
&& x
<clockr
&& y
>1 && y
<RowHeight
-2);
497 int MouseInMail(int x
, int y
)
499 int maill
= win_width
- stwin_width
+ clock_width
+ 2;
500 int mailr
= win_width
- (do_check_mail
? 0 : 3);
501 return (x
>=maill
&& x
<mailr
&& y
>1 && y
<RowHeight
-2);
504 void CreateDateWindow(void)
511 tms
= localtime(&timer
);
513 strftime(str
, 40, datefmt
, tms
);
515 strftime(str
, 40, "%A, %B %d, %Y", tms
);
517 last_date
= tms
->tm_mday
;
519 PopupTipWindow(win_width
, 0, str
);
522 void CreateMailTipWindow(void)
528 str
= _("No new mail");
530 else if (newmail
|| unreadmail
)
532 str
= _("You have new mail");
536 str
= _("You have mail");
539 PopupTipWindow(win_width
, 0, str
);
542 void RedrawTipWindow(void)
544 /* FIXME: I am sure that we redraw too often */
546 FwinString
->win
= Tip
.win
;
547 FwinString
->gc
= tipsgc
;
548 FwinString
->str
= Tip
.text
;
550 FwinString
->y
= 2 + FStatusFont
->ascent
;
551 FwinString
->flags
.has_clip_region
= False
;
552 if (tipscolorset
>= 0)
554 FwinString
->colorset
= &Colorset
[tipscolorset
];
555 FwinString
->flags
.has_colorset
= True
;
559 FwinString
->flags
.has_colorset
= False
;
561 if (FftSupport
&& FStatusFont
->fftf
.fftfont
!= NULL
)
563 XClearArea(dpy
, Tip
.win
, 0, 0, Tip
.w
, Tip
.h
, False
);
565 FlocaleDrawString(dpy
, FStatusFont
, FwinString
, 0);
566 XRaiseWindow(dpy
, Tip
.win
);
570 void PopupTipWindow(int px
, int py
, const char *text
)
582 Tip
.tw
= FlocaleTextWidth(FStatusFont
, (char *)text
, strlen(text
)) + 6;
583 Tip
.th
= FStatusFont
->height
+ 4;
584 XTranslateCoordinates(dpy
, win
, Root
, px
, py
, &newx
, &newy
, &child
);
587 /* if (win_y == win_border) */
589 Tip
.y
= newy
+ RowHeight
;
591 Tip
.y
= newy
- Tip
.th
-2;
596 if (Tip
.x
+ Tip
.tw
+ 4 > screen_g
.x
+ screen_g
.width
- 5)
597 Tip
.x
= screen_g
.x
+ screen_g
.width
- Tip
.tw
- 9;
598 if (Tip
.x
< screen_g
.x
+ 5)
599 Tip
.x
= screen_g
.x
+ 5;
601 UpdateString(&Tip
.text
, text
);
602 CreateTipWindow(Tip
.x
, Tip
.y
, Tip
.w
, Tip
.h
);
605 XMapRaised(dpy
, Tip
.win
);
608 void ShowTipWindow(int open
)
610 if (!ShowTips
) return;
615 XMapRaised(dpy
, Tip
.win
);
621 XUnmapWindow(dpy
, Tip
.win
);
626 void CreateTipWindow(int x
, int y
, int w
, int h
)
628 unsigned long gcmask
;
629 unsigned long winattrmask
= CWBackPixel
| CWBorderPixel
| CWEventMask
|
630 CWColormap
|CWSaveUnder
| CWOverrideRedirect
;
631 XSetWindowAttributes winattr
;
639 colorset_t
*cset
= NULL
;
641 if (tipscolorset
>= 0)
643 cset
= &Colorset
[tipscolorset
];
649 tip_fore
= GetColor(TipsFore
);
650 tip_back
= GetColor(TipsBack
);
653 winattr
.background_pixel
= tip_back
;
654 winattr
.border_pixel
= PictureBlackPixel();
655 winattr
.colormap
= Pcmap
;
656 winattr
.override_redirect
= True
;
657 winattr
.save_under
= True
;
658 winattr
.event_mask
= ExposureMask
;
659 Tip
.win
= XCreateWindow(dpy
, Root
, x
, y
, w
+4, h
+4, 0, Pdepth
, InputOutput
,
660 Pvisual
, winattrmask
, &winattr
);
662 gcmask
= GCForeground
| GCBackground
| GCGraphicsExposures
;
663 gcval
.graphics_exposures
= False
;
664 gcval
.foreground
= tip_fore
;
665 gcval
.background
= tip_back
;
666 if (FStatusFont
->font
!= NULL
)
668 gcval
.font
= FStatusFont
->font
->fid
;
672 XChangeGC(dpy
, tipsgc
, gcmask
, &gcval
);
674 tipsgc
= fvwmlib_XCreateGC(dpy
, Tip
.win
, gcmask
, &gcval
);
676 pmask
= XCreatePixmap(dpy
, Tip
.win
, w
+4, h
+4, 1);
677 pclip
= XCreatePixmap(dpy
, Tip
.win
, w
+4, h
+4, 1);
679 gcmask
= GCForeground
| GCBackground
| GCFillStyle
| GCStipple
|
681 gcval
.foreground
= 1;
682 gcval
.background
= 0;
683 gcval
.fill_style
= FillStippled
;
685 pchk
= XCreatePixmapFromBitmapData(dpy
, Tip
.win
, (char *)gray_bits
,
686 gray_width
, gray_height
, 1, 0, 1);
687 gcval
.stipple
= pchk
;
688 cgc
= fvwmlib_XCreateGC(dpy
, pmask
, gcmask
, &gcval
);
690 gcmask
= GCForeground
| GCBackground
| GCGraphicsExposures
| GCFillStyle
;
691 gcval
.graphics_exposures
= False
;
692 gcval
.fill_style
= FillSolid
;
693 gcval
.foreground
= 0;
694 gcval
.background
= 0;
695 gc0
= fvwmlib_XCreateGC(dpy
, pmask
, gcmask
, &gcval
);
697 gcval
.foreground
= 1;
698 gcval
.background
= 1;
699 gc1
= fvwmlib_XCreateGC(dpy
, pmask
, gcmask
, &gcval
);
701 XFillRectangle(dpy
, pmask
, gc0
, 0, 0, w
+4, h
+4);
702 XFillRectangle(dpy
, pmask
, cgc
, 3, 3, w
+4, h
+4);
703 XFillRectangle(dpy
, pmask
, gc1
, 0, 0, w
+1, h
+1);
704 XFillRectangle(dpy
, pclip
, gc0
, 0, 0, w
+4, h
+4);
705 XFillRectangle(dpy
, pclip
, gc1
, 1, 1, w
-1, h
-1);
706 if (FShapesSupported
)
708 FShapeCombineMask(dpy
, Tip
.win
, FShapeBounding
, 0, 0, pmask
, FShapeSet
);
709 FShapeCombineMask(dpy
, Tip
.win
, FShapeClip
, 0, 0, pclip
, FShapeSet
);
711 if (tipscolorset
>= 0 && (cset
->pixmap
|| cset
->shape_mask
))
714 dpy
, Tip
.win
, w
+ 4, h
+ 4, cset
, Pdepth
, tipsgc
, False
);
719 XFreePixmap(dpy
, pchk
);
722 void DestroyTipWindow(void)
724 XFreePixmap(dpy
, pmask
);
725 XFreePixmap(dpy
, pclip
);
726 XDestroyWindow(dpy
, Tip
.win
);
735 /*-----------------------------------------------------*/
736 /* Get file modification time */
737 /* (based on the code of 'coolmail' By Byron C. Darrah */
738 /*-----------------------------------------------------*/
740 void cool_get_inboxstatus_mbox(void)
742 static off_t oldsize
= 0;
747 fd
= open (mailpath
, O_RDONLY
, 0);
759 newsize
= st
.st_size
;
766 if (st
.st_mtime
>= st
.st_atime
&& newsize
> 0)
771 if (newsize
> oldsize
&& unreadmail
) {
782 static int f_scandir(const char *dir
, struct dirent
*** namelist
)
785 struct dirent
* f_temp
;
786 /* 10 is just an arbituary number */
787 int f_count
= 0, namelist_size
= 10;
790 f_dir
= opendir(dir
);
794 (struct dirent
**)safemalloc(sizeof(struct dirent
*) * 10);
796 f_temp
= readdir(f_dir
);
799 else if (strcmp(f_temp
->d_name
, ".") != 0 &&
800 strcmp(f_temp
->d_name
, "..") != 0)
802 if (f_count
> namelist_size
)
805 (void *)(namelist
[0]),
806 sizeof(struct dirent
) * (f_count
+10));
807 namelist
[0] = (struct dirent
**)r_ret
;
808 namelist_size
= f_count
+ 10;
810 namelist
[0][f_count
] = (struct dirent
*)safemalloc(
811 sizeof(struct dirent
));
813 namelist
[0][f_count
], f_temp
,
814 sizeof(struct dirent
));
822 void cool_get_inboxstatus_maildir(void)
824 static int oldsize
= 0;
826 struct dirent
**newlist
;
827 struct dirent
**curlist
;
831 alt_mailpath
= safemalloc(strlen(mailpath
) + strlen("/new") + 1);
832 strcpy(alt_mailpath
, mailpath
);
833 strcat(alt_mailpath
, "/new");
834 newent
= f_scandir(alt_mailpath
, &newlist
);
835 strcpy(alt_mailpath
, mailpath
);
836 strcat(alt_mailpath
, "/cur");
837 curent
= f_scandir(alt_mailpath
, &curlist
);
840 newsize
= curent
+ newent
;
857 if (newsize
> oldsize
&& unreadmail
)
871 free(curlist
[curent
]);
876 free(newlist
[newent
]);
881 void cool_get_inboxstatus(void)
885 cool_get_inboxstatus_maildir();
887 cool_get_inboxstatus_mbox();
891 /*---------------------------------------------------------------------------*/
893 void HandleMailClick(XEvent event
)
895 static Time lastclick
= 0;
896 if (event
.xbutton
.time
- lastclick
< 250)
898 SendText(Fvwm_fd
, MailCmd
, 0);
900 lastclick
= event
.xbutton
.time
;