1 /*
2  * Main calendar window widgets.
3  *
4  *	locate_in_month_calendar()	find out where we are in the calendar
5  *					pop up a one-day list menu
6  *	draw_month_year()		Draw the month and year into the
7  *					main calendar window
8  *	draw_calendar()			Draw day grid into the main calendar
9  *					window
10  *	draw_day(day)			Draw one day box into the day grid
11  *					in the main calendar window
12  */
13 
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <assert.h>
17 #include <time.h>
18 #include <Xm/Xm.h>
19 #include "cal.h"
20 
21 static void draw_day_notes	(int);
22 static BOOL set_day_bkg_color	(int);
23 static int day_to_pos		(int *, int *, int);
24 static int pos_to_day		(int *, int, int);
25 #ifdef JAPAN
26 static int chkctype		(char, int);
27 static int mixedstrtok		(char *, char *);
28 static void mixedstr_draw	(Window, int, int,char*, int, int, int);
29 
30 extern char		*localename;	/* locale name */
31 extern unsigned short	(*kanji2jis)();	/* kanji2jis == e2j or sj2j */
32 #endif
33 extern Display		*display;	/* everybody uses the same server */
34 extern GC		jgc, gc;	/* graphic context, Japanese and std */
35 extern GC		chk_gc;		/* checkered gc for light background */
36 extern XFontStruct	*font[NFONTS];	/* fonts: FONT_* */
37 extern struct mainmenu	mainmenu;	/* all important main window widgets */
38 extern struct config	config;		/* global configuration data */
39 extern struct user	*user;		/* user list (from file_r.c) */
40 extern int		curr_month;	/* month being displayed, 0..11 */
41 extern int		curr_year;	/* year being displayed, since 1900 */
42 extern time_t		curr_week;	/* week being displayed, time in sec */
43 extern time_t		curr_day;	/* day being displayed, time in sec */
44 extern struct plist	*mainlist;	/* list of all schedule entries */
45 extern struct holiday	holiday[366];	/* info for each day, separate for */
46 extern struct holiday	sm_holiday[366];/* full-line texts under, and small */
47 extern short		monthbegin[12];	/* julian date each month begins with*/
48 extern short		monthlen[];	/* length of each month */
49 static int		lines_per_day;	/* max # of entries in a day box */
50 static int		line_height;	/* height of one entry line in pixels*/
51 static struct entry	**drawn;	/* 31*n entries drawn in month cal, */
52 					/* for locating entries for drag&drop*/
53 extern char *weekday_name[];
54 extern char *monthname[];
55 
56 
57 /*
58  * for a given position on the canvas, identify the entry there and return
59  * a pointer to it and which area it was hit in (one of the M_ constants).
60  * Locate an event if epp!=0, and return exactly where the event was hit.
61  * If none was found, return the time if possible for creating new events.
62  * If epp is 0, we are dragging an event right now; just return a new time.
63  * In month calendars, there are no multiday appointments and the trigger
64  * always agrees with the time if set.
65  *
66  *   ret	*epp	*time	*trig	xp..ysp	event
67  *
68  *   M_ILLEGAL	0	0	-	-	no recognizable position
69  *   M_OUTSIDE	0	time	-	-	no event but in a valid day
70  *   M_INSIDE	entry	time	trigg	-	in event center, edit it
71  *   M_LEFT	entry	time	trigg	pos/sz	near left edge, move start date
72  *   M_RIGHT	entry	time	trigg	pos/sz	near right edge, move end date
73  *   M_DAY_CAL	-	time	-	-	pop up a day calendar view
74  *   M_WEEK_CAL	-	time	-	-	pop up a week calendar view
75  */
76 
locate_in_month_calendar(struct entry ** epp,BOOL * warn,time_t * time,UNUSED time_t * trigger,int * xp,int * yp,int * xsp,int * ysp,int xc,int yc)77 MOUSE locate_in_month_calendar(
78 	struct entry	**epp,		/* returned entry or 0 */
79 	BOOL		*warn,		/* is *epp an n-days-ahead w?*/
80 	time_t		*time,		/* if *epp=0, clicked where */
81 	UNUSED time_t	*trigger,	/* if *epp=0, entry trigger */
82 	int		*xp,		/* if M_INSIDE, rubber box position */
83 	int		*yp,
84 	int		*xsp,		/* and size */
85 	int		*ysp,
86 	int		xc,		/* pixel pos clicked */
87 	int		yc)
88 {
89 	struct config	*c = &config;
90 	int		xd, yd;		/* top left of clicked day box */
91 	int		day, n;		/* clicked day number, note line */
92 	struct tm	tm;		/* for pos->time_t conversion */
93 	int		sm = c->smallmonth;
94 
95 	tm.tm_year = curr_year;
96 	tm.tm_mon  = curr_month;
97 	tm.tm_mday = 1;
98 	tm.tm_hour = 0;
99 	tm.tm_min  = 0;
100 	tm.tm_sec  = 0;
101 							/* call week view? */
102 	if (xc < c->calbox_marg[sm] + c->calbox_arrow[sm]) {
103 		yc -= c->calbox_marg[sm] + c->calbox_title[sm];
104 		yc /= c->calbox_ys[sm];
105 		(void)day_to_pos(&xd, &yd, 1);
106 		tm.tm_mday = 7 * yc - xd +1;
107 		if (tm.tm_mday < 1) {
108 			if (!tm.tm_mon--) {
109 				tm.tm_mon = 11;
110 				tm.tm_year--;
111 			}
112 			tm.tm_mday += monthlen[tm.tm_mon] +
113 					(tm.tm_mon==1 && !(tm.tm_year&3));
114 		}
115 		*time = tm_to_time(&tm);
116 		return(*time != (time_t)-1 ? M_WEEK_CAL : M_ILLEGAL);
117 	}
118 							/* call day view? */
119 	if (!pos_to_day(&day, xc, yc))
120 		return(M_ILLEGAL);
121 	if (day_to_pos(&xd, &yd, day)) {
122 		yd = c->calbox_marg[sm] + c->calbox_title[sm] +
123 							yd * c->calbox_ys[sm];
124 		if (yc < yd + font[FONT_DAY+sm]->max_bounds.ascent) {
125 			tm.tm_mday = day;
126 			*time = tm_to_time(&tm);
127 			return(M_DAY_CAL);
128 		}
129 	}
130 							/* hit appointment */
131 	if (warn)
132 		*warn = FALSE;
133 	if (epp)
134 		*epp = 0;
135 	if ((*time = date_to_time(day, curr_month, curr_year, 0, 0, 0))) {
136 		int day_xs = c->calbox_xs[sm];
137 		int yo = font[FONT_DAY+sm]->max_bounds.ascent;
138 		xd  = c->calbox_marg[sm] + c->calbox_arrow[sm] + xd * day_xs;
139 		yd += yo + (c->calbox_ys[sm]-yo-lines_per_day*line_height)/2+3;
140 		n   = (yc - yd) / line_height;
141 		assert(drawn && day >= 1 && day <= 31);
142 		if (!epp || n < 0 || n > lines_per_day ||
143 				!(*epp = drawn[(day-1) * lines_per_day + n]))
144 			return(M_OUTSIDE);
145 		if (xp) {
146 			*xp   = xd + 1;
147 			*yp   = yd + n * line_height;
148 			*xsp  = day_xs - 5;
149 			*ysp  = line_height - 1;
150 		}
151 		return(xc < xd + day_xs/3   ? M_LEFT  : M_INSIDE);
152 	}
153 	return(M_ILLEGAL);
154 }
155 
156 
157 /*-------------------------------------------------- drawing ----------------*/
158 /*
159  * draw the weekday names, depending on the Sunday-first flag
160  */
161 
draw_month_year(void)162 void draw_month_year(void)
163 {
164 	print_button(mainmenu.month, _(monthname[curr_month]));
165 	print_button(mainmenu.year, "%d", curr_year+1900);
166 }
167 
168 
169 /*
170  * draw all day boxes into a clip region, or all if the region is null
171  */
172 
draw_calendar(Region region)173 void draw_calendar(
174 	Region		region)
175 {
176 	struct config	*c = &config;
177 	Window		window;
178 	XRectangle	rects[7+8], *r = rects;
179 	XPoint		point[4];
180 	int		i, j, x, y;
181 	int		sm = c->smallmonth;
182 
183 	if (!mainmenu.cal) {
184 		if (region)
185 			XDestroyRegion(region);
186 		return;
187 	}
188 	if (region) {
189 		XSetRegion(display, gc,     region);
190 		XSetRegion(display, chk_gc, region);
191 	}
192 	window = XtWindow(mainmenu.cal);
193 	set_color(COL_CALBACK);				/* clear */
194 	XFillRectangle(display, window, gc, 0, 0,
195 		2*c->calbox_marg[sm] + c->calbox_xs[sm] * 7
196 				     + c->calbox_arrow[sm],
197 		2*c->calbox_marg[sm] + c->calbox_ys[sm] * 6
198 				     + c->calbox_title[sm]);
199 
200 							/* vert lines */
201 	for (i=0; i < 8; i++, r++) {
202 		r->x	  = c->calbox_marg[sm] + i * c->calbox_xs[sm]
203 					       + c->calbox_arrow[sm];
204 		r->y	  = c->calbox_marg[sm] + c->calbox_title[sm];
205 		r->width  = 3;
206 		r->height = c->calbox_ys[sm] * 6;
207 	}
208 							/* horz lines */
209 	for (i=0; i < 7; i++, r++) {
210 		r->x	  = c->calbox_marg[sm] + c->calbox_arrow[sm];
211 		r->y	  = c->calbox_marg[sm] + c->calbox_title[sm]
212 					       + c->calbox_ys[sm] * i;
213 		r->width  = c->calbox_xs[sm] * 7 + 3;
214 		r->height = 3;
215 	}
216 	set_color(COL_GRID);
217 	XFillRectangles(display, window, gc, rects, 7+8);
218 	set_color(COL_CALFRAME);
219 	XDrawRectangle(display, window, gc,
220 			c->calbox_marg[sm] + c->calbox_arrow[sm] -1,
221 			c->calbox_marg[sm] + c->calbox_title[sm] -1,
222 			c->calbox_xs[sm] * 7 + 4,
223 			c->calbox_ys[sm] * 6 + 4);
224 
225 	point[3].x =					/* week call arrows */
226 	point[0].x =
227 	point[1].x = c->calbox_marg[sm]/2 +1;
228 	point[2].x = point[0].x + c->calbox_arrow[sm] -1;
229 	point[2].y = c->calbox_marg[sm] + c->calbox_title[sm]
230 					+ c->calbox_ys[sm] / 2;
231 	point[3].y =
232 	point[0].y = point[2].y - (point[2].x - point[0].x);
233 	point[1].y = point[2].y + (point[2].x - point[0].x);
234 	if (c->calbox_arrow[sm] > 3)
235 		for (i=0; i < 6; i++) {
236 			set_color(COL_CALSHADE);
237 			XFillPolygon(display, window, gc, point, 3, Convex,
238 							CoordModeOrigin);
239 			set_color(COL_STD);
240 			XDrawLines(display, window, gc, point, 4,
241 							CoordModeOrigin);
242 			point[0].y += c->calbox_ys[sm];
243 			point[1].y += c->calbox_ys[sm];
244 			point[2].y += c->calbox_ys[sm];
245 			point[3].y += c->calbox_ys[sm];
246 		}
247 	set_color(COL_STD);
248 
249 							/* weekday names */
250 	j = c->sunday_first ? 6 : 0;
251 	XSetFont(display, gc, font[sm ? FONT_NOTE : FONT_STD]->fid);
252 	for (i=0; i < 7; i++, j++) {
253 		char *wdn = _(weekday_name[j%7]);
254 		XDrawString(display, window, gc,
255 			c->calbox_marg[sm] + c->calbox_arrow[sm]
256 					   + c->calbox_xs[sm] * i,
257 			c->calbox_marg[sm] + c->calbox_title[sm] * 3/4,
258 			wdn, strlen(wdn));
259 	}
260 	day_to_pos(&x, &y, 1);				/* day boxes */
261 	for (i=1-x; i < 43-x; i++)
262 		draw_day(i);
263 
264 	if (region) {
265 		XSetClipMask(display, gc,     None);
266 		XSetClipMask(display, chk_gc, None);
267 		XDestroyRegion(region);
268 	}
269 }
270 
271 
272 /*
273  * draw one day box.
274  */
275 
draw_day(int day)276 void draw_day(
277 	int		day)		/* 1..31 : current month */
278 					/* < 1 : previous month */
279 					/* > monthlen : next month */
280 {
281 	Window		window;
282 	char		buf[200];	/* julian, weeknum, holiday */
283 	int		x, y;		/* position in calendar */
284 	int		jul;		/* julian date of daybox */
285 	int		daynum_xs;	/* width of day# in pixels */
286 	int		tmp_curr_month;	/* tmp storage for curr_month*/
287 	int		tmp_curr_year;	/* tmp storage for curr_year */
288 	int		orig_day;	/* original value of day */
289 	int		last_day;	/* last day of current month */
290 	BOOL		leftedge;	/* in leftmost daybox? */
291 	BOOL		weekend;	/* saturday or sunday? */
292 	BOOL		today;		/* is this today's box? */
293 	BOOL		othermonth;	/* not current month ? */
294 	struct holiday	*hp, *shp;	/* to check for holidays */
295 	char		*errmsg;	/* holiday parser error */
296 	int		sm = config.smallmonth;
297 
298 	if (!mainmenu.cal)
299 		return;
300 	orig_day = day;
301 	tmp_curr_month = curr_month;
302 	tmp_curr_year  = curr_year;
303 	last_day = monthlen[curr_month] + (curr_month==1 && !(curr_year&3));
304 	if (day > last_day) {
305 		day_to_pos(&x, &y, last_day);
306 		othermonth = TRUE;
307 		day = day - last_day;
308 		curr_month = (curr_month+1) % 12;
309 		if (curr_month == 0)
310 			curr_year++;
311 		y = (y*7 + x + day) / 7;
312 		x = (x + day) % 7;
313 	} else {
314 		day_to_pos(&x, &y, day);
315 		if (day < 1) {
316 			othermonth = TRUE;
317 			curr_month = curr_month == 0 ? 11 : curr_month-1;
318 			if (curr_month == 11)
319 				curr_year--;
320 			last_day = monthlen[curr_month] +
321 					(curr_month==1 && !(curr_year&3));
322 			day = last_day + day;
323 		} else
324 			othermonth = FALSE;
325 	}
326 	if (config.nomonthshadow && othermonth) {
327 		curr_month = tmp_curr_month;
328 		curr_year  = tmp_curr_year;
329 		return;
330 	}
331 	window = XtWindow(mainmenu.cal);
332 	weekend = x == 6 || x == (config.sunday_first ? 0 : 5);
333 	leftedge = x == 0;
334 	daynum_xs = 5 + 2 * CHARWIDTH(FONT_DAY+sm, '0');
335 	x = config.calbox_marg[sm] + x * config.calbox_xs[sm] + 3 +
336 					 config.calbox_arrow[sm];
337 	y = config.calbox_marg[sm] + y * config.calbox_ys[sm] + 3 +
338 					 config.calbox_title[sm];
339 	today = set_day_bkg_color(day);
340 	XFillRectangle(display, window, gc, x, y, config.calbox_xs[sm]-3,
341 						  config.calbox_ys[sm]-3);
342 	sprintf(buf, "%d", day);
343 	XSetFont(display, gc, font[FONT_DAY+sm]->fid);
344 
345 	if ((errmsg = parse_holidays(curr_year, FALSE)))
346 		create_error_popup(mainmenu.cal, 0, errmsg);
347 	jul = monthbegin[curr_month] + day-1 +(curr_month>1 && !(curr_year&3));
348 	hp  =    &holiday[jul];
349 	shp = &sm_holiday[jul];
350 
351 							/* day number */
352 	set_color(othermonth    ? COL_NOTEOFF   :
353 		  hp->daycolor	?  hp->daycolor :
354 		  shp->daycolor	? shp->daycolor :
355 		  weekend	? COL_WEEKEND	: COL_WEEKDAY);
356 
357 	XDrawString(display, window, gc,
358 		x+2, y+font[FONT_DAY+sm]->max_bounds.ascent, buf, strlen(buf));
359 
360 							/* julian & week num */
361 	if (config.julian || config.weeknum) {
362 		int wkday, weeknum;
363 		(void)date_to_time(day, curr_month, curr_year,
364 						&wkday, &jul, &weeknum);
365 		*buf = 0;
366 		if (config.weeknum && leftedge) {
367 			if (config.gpsweek)
368 				sprintf(buf, "[%d]", ((curr_year * 365
369 						+ (curr_year+3)/4
370 						+ jul) / 7 + 945) & 1023);
371 			else
372 				sprintf(buf, "(%d)", weeknum+1);
373 		}
374 		if (config.julian)
375 			sprintf(buf+strlen(buf), "%d", jul+1);
376 		truncate_string(buf, config.calbox_xs[sm] - 3 - daynum_xs,
377 								FONT_NOTE);
378 		XSetFont(display, gc, font[FONT_NOTE]->fid);
379 		XDrawString(display, window, gc,
380 			x + daynum_xs,
381 			y + font[FONT_NOTE]->max_bounds.ascent,
382 							buf, strlen(buf));
383 
384 	}
385 							/* small holiday */
386 	if (shp->string && ((!config.julian && !config.weeknum) ||
387 				font[FONT_DAY+sm]->max_bounds.ascent >=
388 				font[FONT_NOTE]->max_bounds.ascent * 2 - 1)) {
389 		strncpy(buf, shp->string, sizeof(buf)-1);
390 		buf[sizeof(buf)-1] = 0;
391 #ifndef JAPAN
392 		truncate_string(buf, config.calbox_xs[sm] - 3 - daynum_xs,
393 								FONT_NOTE);
394 #endif
395 		if (othermonth == TRUE)
396 			set_color(COL_NOTEOFF);
397 		else if (shp->stringcolor)
398 			set_color(shp->stringcolor);
399 		else if (shp->daycolor)
400 			set_color(shp->daycolor);
401 		XSetFont(display, gc, font[FONT_NOTE]->fid);
402 #ifdef JAPAN
403 		XSetFont(display, jgc, font[FONT_JNOTE]->fid);
404 		mixedstr_draw(window, x + daynum_xs,
405 			      y + font[FONT_DAY+sm]->max_bounds.ascent, buf,
406 			      config.calbox_xs[sm] - 3 - daynum_xs,
407 			      FONT_NOTE, FONT_JNOTE);
408 #else
409 		XDrawString(display, window, gc,
410 			x + daynum_xs,
411 			y + font[FONT_DAY+sm]->max_bounds.ascent,
412 							buf, strlen(buf));
413 #endif
414 	}
415 							/* entry notes */
416 	set_color(COL_STD);
417 	curr_month = tmp_curr_month;
418 	curr_year  = tmp_curr_year;
419 	draw_day_notes(orig_day);
420 							/* opt. today frame */
421 	if (today && config.frame_today) {
422 		XDrawRectangle(display, window, gc, x+1, y+1,
423 			       config.calbox_xs[sm]-6, config.calbox_ys[sm]-6);
424 		XDrawRectangle(display, window, gc, x+2, y+2,
425 			       config.calbox_xs[sm]-8, config.calbox_ys[sm]-8);
426 	}
427 }
428 
429 
430 /*
431  * draw (or undraw) the notes in a day box. Looks up the day in the database.
432  * Remember which entry was drawn where; this simplifies drag&drop because
433  * it's easy to find the entry under the cursor.
434  */
435 
draw_day_notes(int day)436 static void draw_day_notes(
437 	int		day)		/* 1..31 */
438 {
439 	BOOL		today;		/* TRUE: it's today's daybox */
440 	struct tm	*tm;		/* for daylight-saving tzone */
441 	time_t		tod;		/* current time-of-day */
442 	time_t		daytime;	/* time of day box, in sec */
443 	BOOL		found;		/* TRUE if lookup succeeded */
444 	BOOL		othermonth;	/* not current month ? */
445 	int		tmp_curr_month;	/* tmp storage for curr_month*/
446 	int		tmp_curr_year;	/* tmp storage for curr_year */
447 	int		last_day;	/* last day of current month */
448 	struct lookup	lookup;		/* result of entry lookup */
449 	struct entry	*ep;		/* ptr to entry in mainlist */
450 	int		u;		/* if other user, user[] indx*/
451 	int		x, y;		/* position in calendar */
452 	int		xo, i;		/* for aligning ':' in time */
453 	int		ye;		/* bottom limit of day box */
454 	int		ys;		/* height of a text line */
455 	char		buf[100];	/* string buffer (time/note) */
456 	int		jul;		/* julian date of daybox */
457 	int		nlines = 0;	/* # printed, <lines_per_day */
458 	struct holiday	*hp;		/* holiday info for this day */
459 	char		*errmsg;	/* parser error */
460 	Window		window;
461 	int		sm = config.smallmonth;
462 	int		ascent, descent;
463 	struct entry	**drawn_entry = 0;
464 
465 	if (!mainmenu.cal)
466 		return;
467 							/* which day box? */
468 	tmp_curr_month = curr_month;
469 	tmp_curr_year = curr_year;
470 	last_day = monthlen[curr_month] + (curr_month==1 && !(curr_year&3));
471 	if (day > last_day) {
472 		day_to_pos(&x, &y, last_day);
473 		othermonth = TRUE;
474 		day = day - last_day;
475 		curr_month = (curr_month+1) % 12;
476 		if (curr_month == 0)
477 			curr_year++;
478 		y = (y*7 + x + day) / 7;
479 		x = (x + day) % 7;
480 	} else {
481 		day_to_pos(&x, &y, day);
482 		if (day < 1) {
483 			othermonth = TRUE;
484 			curr_month = curr_month == 0 ? 11 : curr_month-1;
485 			if (curr_month == 11)
486 				curr_year--;
487 			last_day = monthlen[curr_month] +
488 					(curr_month==1 && !(curr_year&3));
489 			day = last_day + day;
490 		} else
491 			othermonth = FALSE;
492 	}
493 	daytime = date_to_time(day, curr_month, curr_year, 0, 0, 0);
494 
495 	window = XtWindow(mainmenu.cal);
496 	tod    = get_time();
497 	tm     = time_to_tm(tod);
498 	today  = day        == tm->tm_mday &&
499 		 curr_month == tm->tm_mon  &&
500 		 curr_year  == tm->tm_year;
501 							/* day box geometry */
502 	x  = config.calbox_marg[sm] + x * config.calbox_xs[sm] + 3 +
503 						config.calbox_arrow[sm];
504 	y  = config.calbox_marg[sm] + y * config.calbox_ys[sm] + 3 +
505 						config.calbox_title[sm];
506 	ye = y + config.calbox_ys[sm] - 3;
507 
508 	ascent  = font[FONT_NOTE]->max_bounds.ascent;
509 	descent = font[FONT_NOTE]->max_bounds.descent/2;
510 	XSetFont(display, gc,  font[FONT_NOTE]->fid);
511 #ifdef JAPAN
512 	XSetFont(display, jgc, font[FONT_JNOTE]->fid);
513 	if (ascent + descent < font[FONT_JNOTE]->max_bounds.ascent +
514 			       font[FONT_JNOTE]->max_bounds.descent/2) {
515 		ascent  = font[FONT_JNOTE]->max_bounds.ascent;
516 		descent = font[FONT_JNOTE]->max_bounds.descent/2;
517 	}
518 #endif
519 	ys = ascent + descent;
520 	y += font[FONT_DAY+sm]->max_bounds.ascent + (ye - y) % ys / 2;
521 
522 	if (!othermonth) {				/* drawn entry buffer*/
523 		i = (ye - y) / ys;
524 		if (i < 0) i = 0;
525 		if (!drawn || i > lines_per_day) {
526 			if (drawn)
527 				free(drawn);
528 			drawn = (struct entry **)malloc(31 * i *
529 							sizeof(struct entry *));
530 			memset(drawn, 0, 31 * i * sizeof(struct entry *));
531 			lines_per_day = i;
532 			line_height   = ys;
533 		}
534 		drawn_entry = drawn + (day - 1) * i;
535 		memset(drawn_entry, 0, i * sizeof(struct entry *));
536 	}
537 							/* background */
538 	(void)set_day_bkg_color(day);
539 	XFillRectangle(display, window, gc, x,y, config.calbox_xs[sm]-3, ye-y);
540 
541 							/* large holiday? */
542 	if ((errmsg = parse_holidays(curr_year, FALSE)))
543 		create_error_popup(mainmenu.cal, 0, errmsg);
544 	jul = monthbegin[curr_month] +day-1 + (curr_month>1 && !(curr_year&3));
545 	hp  = &holiday[jul];
546 	if (hp->string && *hp->string && y + descent*2 <= ye) {
547 		y += ascent;
548 		set_color(othermonth ? COL_NOTEOFF :
549 			   hp->stringcolor ? hp->stringcolor : COL_NOTE);
550 #ifdef JAPAN
551 		mixedstr_draw(window, x+2, y, hp->string,
552 				config.calbox_xs[sm]-3, FONT_NOTE, FONT_JNOTE);
553 #else
554 		strncpy(buf, hp->string, sizeof(buf)-1);
555 		truncate_string(buf, config.calbox_xs[sm] - 3, FONT_NOTE);
556 		XDrawString(display, window, gc, x+2, y, buf, strlen(buf));
557 #endif
558 		y += descent/2;
559 		if (drawn_entry) {
560 			nlines++;
561 			drawn_entry++;
562 		}
563 	}
564 							/* entry loop */
565 	found = lookup_entry(&lookup, mainlist, daytime, TRUE, FALSE, 'm', -1);
566 	for (; found; found = lookup_next_entry(&lookup)) {
567 
568 		ep = &mainlist->entry[lookup.index];
569 		u = name_to_user(ep->user);
570 		if (ep->hide_in_m || user[u].suspend_m)
571 			continue;
572 		for (i=0; i < NEXC; i++)
573 			if (ep->except[i] == daytime)
574 				break;
575 		if (i < NEXC)
576 			continue;
577 		if (lookup.index >= mainlist->nentries	||
578 		    lookup.trigger <  daytime		||
579 		    lookup.trigger >= daytime + 86400)
580 			break;
581 		if (today && config.nopast &&
582 		    lookup.trigger < tod   &&
583 		    !ep->notime)
584 			continue;
585 
586 		if (y + font[FONT_NOTE]->max_bounds.descent
587 		      + font[FONT_NOTE]->max_bounds.descent > ye) {
588 			int xe = x + config.calbox_xs[sm] -3;
589 			set_color(othermonth ? COL_NOTEOFF : COL_NOTE);
590 			XFillRectangle(display, window, gc, xe-4,  ye-3, 2, 2);
591 			XFillRectangle(display, window, gc, xe-8,  ye-3, 2, 2);
592 			XFillRectangle(display, window, gc, xe-12, ye-3, 2, 2);
593 			break;
594 		}
595 		if (config.colorcode_m && u > 0 && !othermonth) {
596 			set_color(COL_WUSER_0 + (user[u].color & 7));
597 			XFillRectangle(display, window, gc,
598 					x, y+1, config.calbox_xs[sm] - 3,
599 					font[FONT_NOTE]->max_bounds.ascent+1);
600 			if (user[u].color > 7)
601 				XFillRectangle(display, window, chk_gc,
602 					x, y+1, config.calbox_xs[sm] - 3,
603 					font[FONT_NOTE]->max_bounds.ascent+1);
604 		}
605 		if (ep->notime)
606 			sprintf(buf, "%s%.90s", lookup.days_warn ? "W: " : "",
607 						mknotestring(ep));
608 		else
609 			sprintf(buf, "%s %s%.90s", mktimestring(lookup.trigger,
610 								FALSE),
611 						lookup.days_warn ? "W: " : "",
612 						mknotestring(ep));
613 #ifdef JAPAN
614 		y += (font[FONT_NOTE ]->max_bounds.ascent <
615 		      font[FONT_JNOTE]->max_bounds.ascent) ?
616 		      font[FONT_JNOTE]->max_bounds.ascent :
617 		      font[FONT_NOTE ]->max_bounds.ascent;
618 
619 		set_color(othermonth    ? COL_NOTEOFF :
620 			  ep->suspended ? COL_NOTEOFF :
621 			  ep->acolor    ? COL_HBLACK + ep->acolor-1
622 			  		: COL_NOTE);
623 		mixedstr_draw(window, x+2, y, buf, config.calbox_xs[sm] - 3,
624 			      FONT_NOTE, FONT_JNOTE);
625 #else
626 		truncate_string(buf, config.calbox_xs[sm] - 5, FONT_NOTE);
627 		y += font[FONT_NOTE]->max_bounds.ascent;
628 		set_color(othermonth    ? COL_NOTEOFF :
629 			  ep->suspended ? COL_NOTEOFF :
630 			  ep->acolor    ? COL_HBLACK + ep->acolor-1
631 			  		: COL_NOTE);
632 		xo = CHARWIDTH(FONT_NOTE, '0') - CHARWIDTH(FONT_NOTE, *buf);
633 		if (xo < 0) xo = 0;
634 		XDrawString(display, window, gc, x+2+xo, y, buf, strlen(buf));
635 #endif
636 		y += font[FONT_NOTE]->max_bounds.descent/2;
637 		if (drawn_entry && nlines++ < lines_per_day)
638 			*drawn_entry++ = ep;
639 	}
640 	set_color(COL_STD);
641 	curr_month = tmp_curr_month;
642 	curr_year  = tmp_curr_year;
643 }
644 
645 
set_day_bkg_color(int day)646 static BOOL set_day_bkg_color(
647 	int			day)		/* 1..31 */
648 {
649 	struct tm		*tm;
650 	BOOL			ret;
651 
652 	tm = time_to_tm(get_time());
653 	ret = day == tm->tm_mday && curr_month == tm->tm_mon
654 				 && curr_year  == tm->tm_year;
655 	set_color(ret ? COL_CALTODAY : COL_CALSHADE);
656 	return(ret);
657 }
658 
659 
660 /*-------------------------------------------------- low-level --------------*/
661 /*
662  * convert day numbers 1..31 to a pixel position, and vice versa.
663  */
664 
day_to_pos(int * x,int * y,int day)665 static int day_to_pos(
666 	int			*x,		/* returned box, 0..6/0..5 */
667 	int			*y,
668 	int			day)		/* 1..31 */
669 {
670 	int			wkday;
671 
672 	if (!date_to_time(day, curr_month, curr_year, &wkday, 0, 0))
673 		return(FALSE);
674 	*x = config.sunday_first ? wkday : (wkday + 6) % 7;
675 	*y = (day-1 - *x + 6) / 7;
676 	return(TRUE);
677 }
678 
679 
pos_to_day(int * day,int x,int y)680 static int pos_to_day(
681 	int			*day,		/* returned day #, 1..31 */
682 	int			x,		/* pixel pos in drawable */
683 	int			y)
684 {
685 	int			xtest, ytest;
686 	int			sm = config.smallmonth;
687 
688 	(void)day_to_pos(&xtest, &ytest, 1);
689 	x -= config.calbox_marg[sm] + config.calbox_arrow[sm];
690 	y -= config.calbox_marg[sm] + config.calbox_title[sm];
691 	if (x < 0 || y < 0)
692 		return(FALSE);
693 	x /= config.calbox_xs[sm];
694 	y /= config.calbox_ys[sm];
695 	*day = y * 7 + x - xtest + 1;
696 	if (*day < 1)
697 		return(FALSE);
698 	return(day_to_pos(&xtest, &ytest, *day));
699 }
700 
701 
702 /*
703  * the rest of this file is for Japanese version only. Written by Ogura
704  * Yoshito, like everything else enclosed in #ifdef JAPAN and #endif.
705  */
706 
707 #ifdef JAPAN
708 
709 #define CT_ASC 0
710 #define CT_KJ1 1
711 #define CT_KJ2 2
712 #define CT_UHOH 3
713 
714 /*
715  *	chkctype() and e2j() is originally coded by
716  * NISHIJIMA, Takanori (racsho@cpdc.canon.co.jp).
717  * Ogura Yoshito changed a few lines of chkctype();
718  */
719 
chkctype(char ch,int stat)720 static int chkctype(
721 	char	ch,
722 	int 	stat)
723 {
724 	switch (stat) {
725 	  case CT_ASC:
726 	  case CT_KJ2:
727 		if (ch & 0x80)
728 			return CT_KJ1;
729 		else
730 			return CT_ASC;
731 		break;
732 	  case CT_KJ1:
733 		if (ch)
734 			return CT_KJ2;
735 		else
736 			return CT_UHOH;
737 		break;
738 	  default:
739 		return CT_UHOH;
740 	}
741 }
742 
e2j(unsigned short wc)743 unsigned short e2j(
744 	unsigned short wc)
745 {
746 	return (wc & 0x7F7F);
747 }
748 
749 
750 /*
751  *  sj2j() is coded with referring to nkf. This function is called
752  * very frequently. So you can obtain better response with modification.
753  * nkf, Network Kanji Filter
754  * AUTHOR: Itaru ICHIKAWA (ichikawa@flab.fujitsu.co.jp)
755  */
756 
sj2j(unsigned short wc)757 unsigned short sj2j(
758 	unsigned short wc)
759 {
760 	return (((((wc >> 8) << 1) - (((wc >> 8) > 0x9f) << 7) - 0xe1) << 8) +
761 		((wc & 0x00ff) >= 0x9f ? (wc & 0x00ff) - 0x7e | 0x100
762 				       : (wc & 0x00ff) - 0x1f
763 						       - ((wc & 0x00ff) >> 7)));
764 }
765 
766 
mixedstrtok(char * jisstr,char * sjstr)767 static int mixedstrtok(
768 	char			*jisstr,
769 	char			*sjstr)
770 {
771 	unsigned char		*dptr = (unsigned char *)jisstr,
772 				*sptr = (unsigned char *)sjstr;
773 	unsigned short		wc;	/* works for a 2-byte character */
774 	int			prev_ct = CT_ASC, stat;
775 
776 	if (sptr == NULL) {
777 		*dptr = '\0';
778 		return (-1);	/* Illegal pointer. */
779 	}
780 	stat = chkctype(*(char *)sptr, CT_ASC);
781 	while (*sptr != '\0') {
782 		switch (prev_ct = chkctype(*sptr, prev_ct)) {
783 		  case CT_ASC:
784 			if (stat != CT_ASC)
785 				goto STATCHG;
786 			*dptr++ = *sptr++;
787 			break;
788 		  case CT_KJ1:
789 			if (stat == CT_ASC)
790 				goto STATCHG;
791 			wc = *sptr++ << 8;
792 			break;
793 		  case CT_KJ2:
794 			if (stat == CT_ASC)
795 				goto STATCHG;
796 			wc |= *sptr++;
797 			wc = (*kanji2jis)(wc);	/* kanji2jis == e2j or sj2j */
798 			*dptr++ = wc >> 8;
799 			*dptr++ = wc;
800 			break;
801 		  default:
802 			*dptr = '\0';
803 			return (-1);		/* Parse error. */
804 		}
805 	}
806 STATCHG:
807 	*dptr = '\0';
808 	if (jisstr != (char *)dptr)		/* 1: ASCII, 2: SJIS string */
809 		return(stat == CT_ASC ? 1 : 2);
810 	return(0);
811 }
812 
813 
mixedstrlen_in_pixels(char * sjstr,strpack * stp,int ascfont,int jfont)814 int mixedstrlen_in_pixels(
815 	char			*sjstr,
816 	strpack			*stp,
817 	int			ascfont,
818         int                     jfont)
819 {
820 	int			jlen, plen = 0, i = 0, j = 0, k;
821 	char			*sjptr = sjstr;
822 	unsigned char		buf[100], *cpyptr, *strpool = stp->strptr;
823 
824 	k = mixedstrtok(cpyptr = buf, sjptr);
825 	do {
826 		if (k <= 0) {
827 			if (k)
828 				fprintf(stderr,_("String conversion error.\n"));
829 			stp[i].strptr = NULL;
830 			break;
831 		}
832 		if (j+1+(jlen = stp[i].length = strlen(buf))>=MAXPARTIALCHAR) {
833 			if (jlen = stp[i].length = (MAXPARTIALCHAR-j-1 & ~1)) {
834 				for (stp[i].strptr = strpool + j;
835 						j < MAXPARTIALCHAR-1; j++)
836 					strpool[j] = *cpyptr++;
837 				strpool[j] = '\0';
838 				if (k == 1) {
839 					stp[i].asciistr = True;
840 					plen += stp[i].pixlen = XTextWidth(
841 						    font[ascfont], buf, jlen);
842 				} else {
843 					stp[i].asciistr = False;
844 					plen += stp[i].pixlen = XTextWidth16(
845 						    font[jfont],
846 						    (XChar2b *)buf, jlen/2);
847 				}
848 				if (++i < MAXPARTIALSTRING)
849 					stp[i].strptr = NULL;
850 			} else
851 				stp[i].strptr = NULL;
852 			break;
853 		}
854 		sjptr += jlen;
855 		stp[i].strptr = strpool + j;
856 		while (*cpyptr != '\0')
857 			strpool[j++] = *cpyptr++;
858 		strpool[j++] = '\0';
859 		if (k == 1) {
860 			stp[i].asciistr = True;
861 			plen += stp[i].pixlen = XTextWidth(font[ascfont],
862 							buf, jlen);
863 		} else {
864 			stp[i].asciistr = False;
865 			plen += stp[i].pixlen = XTextWidth16(font[jfont],
866 						(XChar2b *)buf, jlen/2);
867 		}
868 	} while (++i < MAXPARTIALSTRING	&&
869 				((k = mixedstrtok(cpyptr = buf, sjptr)) ||
870 						((stp[i].strptr = NULL), 0)));
871 	return plen;
872 }
873 
874 
mixedstr_draw(Window window,int x,int y,char * mxstr,int maxpix,int ascfont,int jfont)875 static void mixedstr_draw(
876 			  Window window,
877 			  int x,/* coordinate */
878 			  int y,
879 			  char *mxstr,/* string to draw */
880 			  int maxpix,        /* Max width in pixels. */
881 			  int ascfont,/* fonts of string */
882 			  int jfont )
883 {
884 	int		i, len, deltax = 0;
885 	char		buf[100];
886 
887 	i = mixedstrtok(buf, mxstr);
888 	do {
889 		if (i <= 0) {
890 			if (i)
891 				fprintf(stderr,_("String conversion error.\n"));
892 			break;
893 		}
894 		mxstr += (len = strlen(buf));
895 		if (i == 1) {
896 			i = XTextWidth(font[ascfont], buf, len);
897 			if (maxpix < i) {
898 				/*
899 				 * It makes me happy 'truncate_string'
900 				 * to return length of truncated string.
901 				XDrawString(display, window, gc,
902 					x+deltax, y, buf,
903 					truncate_string(buf, maxpix, ascfont);
904 				 */
905 				(void)truncate_string(buf, maxpix, ascfont);
906 				XDrawString(display, window, gc, x+deltax, y,
907 					    buf, strlen(buf));
908 				break;
909 			} else
910 				XDrawString(display, window, gc, x+deltax, y,
911 					    buf, len);
912 		}
913 		else {
914 			i = XTextWidth16(font[jfont], (XChar2b *)buf, len/2);
915 			if(maxpix < i)
916 				len *= (double)maxpix/(double)i;
917 			XDrawString16(display, window, jgc, x+deltax, y,
918 				      (XChar2b *)buf, len/2);
919 		}
920 		deltax += i;
921 		maxpix -= i;
922 	} while (maxpix > 0 && (i = mixedstrtok(buf, mxstr)));
923 }
924 #endif
925