1 /*
2 * draws the week view. PostScript printing is also here because it uses
3 * the same high-level routines, only the low-level output is diverted.
4 *
5 * locate_in_week_calendar() find out where we are in the calendar
6 * draw_week_day(day, month, year) The day day/month/year has changed,
7 * redraw the week view if it contains
8 * that day.
9 * draw_week_calendar() If there is a week menu, resize, and
10 * redraw
11 * print_week_calendar(fp,l) print PostScript week to fp
12 */
13
14 #include <stdio.h>
15 #include <time.h>
16 #include <Xm/Xm.h>
17 #include "cal.h"
18
19 #define DBLTIME 1000 /* doubleclick time [ms] */
20 #define SLOPE 1/4 /* slope of arrow head / barheight */
21 #define MINLEN 8 /* no bar is shorter than MINLEN */
22 #define TOD(t) ((t)%86400)
23 #define BOUND(t,a,b) ((t)<(a) ? (a) : (t)>(b) ? (b) : (t));
24 #define XPOS(t) (((t) - c->week_minhour*3600) * c->week_hourwidth/3600)
25
26 static void gen_week (void);
27 static void draw_week_day_background (int);
28 static void draw_week_day_foreground (int);
29 static void draw_bar (struct weeknode *, int, int, int);
30 static int drawn_lines;
31
32 extern Display *display; /* everybody uses the same server */
33 extern XFontStruct *font[NFONTS]; /* fonts: FONT_* */
34 extern struct config config; /* global configuration data */
35 extern struct mainmenu mainmenu; /* all important main window widgets */
36 extern struct user *user; /* user list (from file_r.c) */
37 extern time_t curr_week; /* week being displayed, time in sec */
38 extern struct week week; /* info on week view */
39 extern struct plist *mainlist; /* list of all schedule entries */
40 extern struct holiday holiday[366]; /* info for each day, separate for */
41 extern struct holiday sm_holiday[366];/* full-line texts under, and small */
42
43
44 /*
45 * for a given position on the canvas, identify the entry there and return
46 * a pointer to it and which area it was hit in (one of the M_ constants).
47 * Locate an event if epp!=0, and return exactly where the event was hit.
48 * If none was found, return the time if possible for creating new events.
49 * If epp is 0, we are dragging an event right now; just return a new time.
50 *
51 * ret *epp *time *trig xp..ysp event
52 *
53 * M_ILLEGAL 0 0 - - no recognizable position
54 * M_OUTSIDE 0 time - - no event but in a valid day
55 * M_INSIDE entry time trigg - in event center, edit it
56 * M_LEFT entry time trigg pos/sz near left edge, move start date
57 * M_RIGHT entry time trigg pos/sz near right edge, move length
58 */
59
locate_in_week_calendar(struct entry ** epp,BOOL * warn,time_t * time,time_t * trigger,int * xp,int * yp,int * xsp,int * ysp,int xc,int yc)60 MOUSE locate_in_week_calendar(
61 struct entry **epp, /* returned entry or 0 */
62 BOOL *warn, /* is *epp an n-days-ahead w?*/
63 time_t *time, /* if *epp=0, clicked where */
64 time_t *trigger, /* if *epp=0, entry trigger */
65 int *xp, /* if M_INSIDE, rubber box */
66 int *yp,
67 int *xsp, /* position and size */
68 int *ysp,
69 int xc, /* pixel pos clicked */
70 int yc)
71 {
72 struct config *c = &config;
73 struct weeknode *vert, *horz; /* yov node scan pointers */
74 struct entry *ep; /* entry to test */
75 int d; /* day strip counter */
76 int yd; /* y start coord of day box */
77 int x, y; /* upper left of day bk box */
78 int xend; /* right margin of chart */
79 int ys; /* height of bar line w/ gaps*/
80 time_t bt; /* start time of bar */
81 int b, e; /* X start end end pos of bar*/
82
83 x = c->week_margin + c->week_daywidth + c->week_gap;
84 yd = 2*c->week_margin + c->week_title + c->week_hour + c->week_gap + 2;
85 ys = c->week_barheight + 2*c->week_bargap;
86 xend = x + c->week_hourwidth * (c->week_maxhour - c->week_minhour);
87 if (xc > xend || yc < c->week_margin + c->week_title)
88 return(M_ILLEGAL);
89 if (warn)
90 *warn = FALSE;
91 /* calc time in calendar */
92 *time = 0;
93 for (y=yd, d=0; d < c->week_ndays; d++) {
94 if (yc < y)
95 break;
96 y += week.nlines[d] * ys + c->week_gap;
97 if (yc < y) {
98 *time = curr_week + d * 86400 + c->week_minhour * 3600+
99 (xc - x) * 3600 / c->week_hourwidth;
100 break;
101 }
102 }
103 /* find entry under mouse */
104 if (epp)
105 for (d=0; d < c->week_ndays; d++) {
106 for (y=yd, vert=week.tree[d]; vert; vert=vert->down, y+=ys) {
107 if (yc < y || yc >= y+ys)
108 continue;
109 if (xc < x)
110 return(M_OUTSIDE);
111 for (horz=vert; horz; horz=horz->next) {
112 ep = *epp = horz->entry;
113 bt = TOD(ep->time);
114 e = x + XPOS(TOD(bt));
115 if (config.weekwarn)
116 bt -= ep->early_warn > ep->late_warn ?
117 ep->early_warn : ep->late_warn;
118 if (bt < c->week_minhour*3600)
119 bt = c->week_minhour*3600;
120 b = x + XPOS(TOD(bt));
121 if (!ep->notime)
122 e += ep->length * c->week_hourwidth/3600;
123 if (e < b + MINLEN)
124 e = b + MINLEN;
125 if (trigger)
126 *trigger = horz->trigger;
127 if (warn)
128 *warn = horz->days_warn;
129 if (xp) {
130 *xp = b + 1;
131 *yp = y + c->week_gap/2 + 1;
132 *xsp = e - b - 1;
133 *ysp = c->week_barheight & ~1;
134 }
135 if (xc < b-1)
136 continue;
137 if (xc < b + (e-b)/3)
138 return(M_LEFT);
139 if (xc < b + (e-b)*2/3)
140 return(M_INSIDE);
141 if (xc < e + 1)
142 return(M_RIGHT);
143 if (!horz->textinside && xc < e + horz->textlen)
144 return(M_INSIDE);
145 }
146 }
147 yd += week.nlines[d] * ys + c->week_gap;
148 }
149 return(*time ? M_OUTSIDE : M_ILLEGAL);
150 }
151
152
153 /*
154 * If the specified date is in the current week, redraw everything (something
155 * changed on the specified day).
156 */
157
draw_week_day(int day,int month,int year)158 void draw_week_day(
159 int day,
160 int month,
161 int year)
162 {
163 struct tm tm;
164 time_t date;
165
166 tm.tm_year = year;
167 tm.tm_mon = month;
168 tm.tm_mday = day;
169 tm.tm_hour = 0;
170 tm.tm_min = 0;
171 tm.tm_sec = 0;
172 date = tm_to_time(&tm);
173 if (date >= curr_week && date < curr_week*config.week_ndays*86400) {
174 build_week(FALSE, FALSE);
175 if (week.canvas)
176 create_view('w');
177 }
178 }
179
180
181 /*---------------------------------- drawing front-ends ---------------------*/
182 /*
183 * draw one week into the current window, in the specified region (all
184 * if null).
185 */
186
draw_week_calendar(Region region)187 void draw_week_calendar(
188 Region region)
189 {
190 if (!g_init_window(&week, region))
191 return;
192 g_setcolor(COL_WBACK);
193 g_fillrect(0, 0, week.canvas_xs, week.canvas_ys);
194 gen_week();
195 g_exit_window();
196 }
197
198
199 /*
200 * print PostScript week calendar to file fp.
201 */
202
print_week_calendar(FILE * fp,BOOL landscape)203 void print_week_calendar(
204 FILE *fp,
205 BOOL landscape)
206 {
207 time_t time = get_time();
208 struct tm *tm = time_to_tm(time);
209 time -= (tm->tm_wday + 6 + config.sunday_first)%7 * 86400;
210 if (!curr_week)
211 curr_week = time;
212 build_week(config.pr_omit_priv, config.pr_omit_appts);
213 g_init_ps(&week, fp, landscape);
214 gen_week();
215 g_exit_ps();
216 }
217
218
219 /*---------------------------------- high-level drawing ---------------------*/
220 /*
221 * draw the week, using low-level routines
222 */
223
gen_week(void)224 static void gen_week(void)
225 {
226 struct config *c = &config;
227 char buf[40], *p;
228 int i, l, x, y;
229
230 drawn_lines = 0;
231 for (i=0; i < c->week_ndays; i++) /* day background */
232 draw_week_day_background(i);
233 /* thick lines */
234 g_setcolor(COL_WGRID);
235 g_fillrect(c->week_margin,
236 2*c->week_margin + c->week_title + c->week_hour + 1,
237 week.canvas_xs - 2*c->week_margin +1,
238 2);
239 g_fillrect(c->week_margin,
240 week.canvas_ys - c->week_margin +2,
241 week.canvas_xs - 2*c->week_margin +1,
242 2);
243 /* thin lines */
244 x = c->week_margin + c->week_daywidth + c->week_gap;
245 y = 2*c->week_margin + c->week_title + c->week_hour + 3;
246 for (i=0; i <= c->week_maxhour-c->week_minhour; i++)
247 g_fillrect(x + i * c->week_hourwidth, y, 1,
248 week.canvas_ys - c->week_margin - y + 3);
249
250 for (i=0; i < c->week_ndays; i++) /* day foreground */
251 draw_week_day_foreground(i);
252 /* hour labels */
253 g_setcolor(COL_WDAY);
254 g_setfont(FONT_WHOUR);
255 for (i=0; i <= c->week_maxhour-c->week_minhour; i++) {
256 p = mktimestring((i + c->week_minhour) * 3600, FALSE);
257 l = strlen_in_pixels(p, FONT_WHOUR);
258 g_drawtext(x + i * c->week_hourwidth - l/2,
259 2*c->week_margin + c->week_title + c->week_hour -2,
260 p, strlen(p));
261 }
262 strcpy(buf, mkdatestring(curr_week)); /* centered title */
263 strcat(buf, " - ");
264 strcat(buf, mkdatestring(curr_week + (c->week_ndays-1)*86400));
265 g_setcolor(COL_WTITLE);
266 g_setfont(FONT_WTITLE);
267 g_drawtext((week.canvas_xs - strlen_in_pixels(buf,FONT_WTITLE))/2,
268 c->week_margin + c->week_title * 3/4,
269 buf, strlen(buf));
270 g_setcolor(COL_STD);
271 }
272
273
274 /*
275 * draw the background of one day of the week at its position. This includes
276 * the day number and the box, but not the bars themselves. The bars are
277 * printed after the thin hour lines are drawn by the caller, by calling the
278 * following routine.
279 */
280 #ifdef JAPAN
281 void g_draw_strpack();
282 #endif
283
draw_week_day_background(int wday)284 static void draw_week_day_background(
285 int wday) /* day of the week, < NDAYS */
286 {
287 struct config *c = &config;
288 time_t today; /* today's date */
289 struct holiday *hp, *shp; /* to check for holidays */
290 char *errmsg; /* holiday parser error */
291 char buf[20]; /* holiday text buffer */
292 struct tm *tm; /* today's date as m/d/y */
293 char *p; /* temp for day name */
294 int x, y; /* upper left of day bk box */
295 int ys; /* height of bar line w/ gaps*/
296 int yt; /* text y position */
297 int yts; /* vert space for text */
298 int i;
299 #ifdef JAPAN
300 int plen; /* pixel-length of partialstr */
301 strpack partialstr[MAXPARTIALSTRING];
302 unsigned char strpool[MAXPARTIALCHAR];
303 #endif
304
305 if (week.nlines[wday] == 0)
306 return;
307 x = c->week_margin + c->week_daywidth + c->week_gap;
308 y = 2*c->week_margin + c->week_title + c->week_hour + c->week_gap + 2;
309 ys = c->week_barheight + 2*c->week_bargap;
310 for (i=0; i < wday; i++)
311 y += week.nlines[i] * ys + c->week_gap;
312
313 /* clear weekday name*/
314 yts = week.nlines[wday] * ys + c->week_gap -1;
315 today = get_time() - (curr_week + wday*86400);
316 g_setcolor(today < 0 || today >= 86400 ? COL_WBACK : COL_CALTODAY);
317 g_fillrect(c->week_margin, y, c->week_daywidth, yts - c->week_gap);
318
319 /* weekday name */
320 yt = y + font[FONT_WDAY]->max_bounds.ascent;
321 i = (wday + 7 - c->sunday_first) % 7;
322 p = mkdatestring(curr_week + wday * 86400);
323 truncate_string(p, c->week_daywidth-2, FONT_WDAY);
324 g_setcolor(COL_WDAY);
325 g_setfont(FONT_WDAY);
326 g_drawtext(c->week_margin, yt, p, strlen(p));
327 yt += font[FONT_WDAY]->max_bounds.ascent +
328 font[FONT_WDAY]->max_bounds.descent;
329 /* holidays */
330 tm = time_to_tm(curr_week + wday*86400);
331 if ((errmsg = parse_holidays(tm->tm_year, FALSE)))
332 create_error_popup(mainmenu.cal, 0, errmsg);
333 shp = &sm_holiday[tm->tm_yday];
334 hp = &holiday[tm->tm_yday];
335 for (i=0; i < 2; i++, shp=hp)
336 if (shp->string && yt < y + yts) {
337 g_setcolor(shp->stringcolor ? shp->stringcolor :
338 shp->daycolor ? shp->daycolor
339 : COL_WDAY);
340 #ifdef JAPAN
341 partialstr->strptr = strpool;
342 if ((plen = mixedstrlen_in_pixels(shp->string,
343 partialstr, FONT_WNOTE, FONT_JNOTE))
344 > c->week_daywidth-2)
345 plen = truncate_strpack(partialstr,
346 c->week_daywidth-2,
347 FONT_WNOTE);
348
349 g_draw_strpack(c->week_margin +
350 c->week_daywidth-2 - plen, yt, partialstr);
351 yt += font[FONT_WDAY]->max_bounds.ascent +
352 font[FONT_WDAY]->max_bounds.descent >
353 font[FONT_JNOTE]->max_bounds.ascent +
354 font[FONT_JNOTE]->max_bounds.descent ?
355 font[FONT_WDAY]->max_bounds.ascent +
356 font[FONT_WDAY]->max_bounds.descent :
357 font[FONT_JNOTE]->max_bounds.ascent +
358 font[FONT_JNOTE]->max_bounds.descent;
359 #else
360 strncpy(buf, shp->string, sizeof(buf)-1);
361 truncate_string(buf, c->week_daywidth-2, FONT_WDAY);
362 g_drawtext(c->week_margin + c->week_daywidth-2 -
363 strlen_in_pixels(buf, FONT_WDAY),
364 yt, buf, strlen(buf));
365 yt += font[FONT_WDAY]->max_bounds.ascent +
366 font[FONT_WDAY]->max_bounds.descent;
367 #endif
368 }
369 /* appointment box */
370 g_setcolor(COL_WBOXBACK);
371 g_fillrect(x, y, week.canvas_xs - c->week_margin - x,
372 week.nlines[wday] * ys);
373 /* repeated times */
374 if (wday && c->week_gap > font[FONT_WHOUR]->max_bounds.ascent-2
375 && drawn_lines > 10 && config.moretimecols) {
376 drawn_lines -= 10;
377 g_setcolor(COL_WDAY);
378 g_setfont(FONT_WHOUR);
379 for (i=0; i < c->week_maxhour-c->week_minhour; i++) {
380 char buf[10];
381 sprintf(buf, "%d", i + c->week_minhour);
382 g_drawtext(x + i * c->week_hourwidth + 2, y - 1,
383 buf, strlen(buf));
384 }
385 }
386 drawn_lines += week.nlines[wday];
387 }
388
389
390 /*
391 * draw the foreground of one day of the week at its position. This includes
392 * all the bars. The background and the thin vertical lines have already been
393 * drawn by the caller.
394 */
395
draw_week_day_foreground(int wday)396 static void draw_week_day_foreground(
397 int wday) /* day of the week, < NDAYS */
398 {
399 struct config *c = &config;
400 struct weeknode *vert, *horz; /* week node scan pointers */
401 int x, y; /* upper left of day bk box */
402 int ys; /* height of bar line w/ gaps*/
403 int i, u;
404
405 x = c->week_margin + c->week_daywidth + c->week_gap;
406 y = 2*c->week_margin + c->week_title + c->week_hour + c->week_gap + 2;
407 ys = c->week_barheight + 2*c->week_bargap;
408 for (i=0; i < wday; i++)
409 y += week.nlines[i] * ys + c->week_gap;
410
411 for (vert=week.tree[wday]; vert; vert=vert->down, y+=ys)
412 for (horz=vert; horz; horz=horz->next) {
413 u = name_to_user(horz->entry->user);
414 draw_bar(horz, u > 0 ? user[u].color : 0,
415 x, y + c->week_bargap);
416 }
417 }
418
419
420 /*
421 * draw one entry bar.
422 */
423
draw_bar(struct weeknode * node,int color,int x,int y)424 static void draw_bar(
425 struct weeknode *node, /* entry node to print */
426 int color, /* color, 0..7 */
427 int x, /* top left pos in canvas */
428 int y)
429 {
430 struct entry *ep = node->entry;
431 struct config *c = &config;
432 int xend; /* right margin of chart */
433 int w[2], b, e; /* early/late, begin, end */
434 int ee; /* e with min length applied */
435 int i; /* warning counter, 0..1 */
436 int slope; /* delta-x of the arrowheads */
437 XPoint point[7]; /* polygon vertex list */
438 #ifdef JAPAN
439 int plen; /* pixel-length of partialstr */
440 strpack partialstr[MAXPARTIALSTRING];
441 unsigned char strpool[MAXPARTIALCHAR];
442 #endif
443
444 slope = c->week_barheight * SLOPE;
445 xend = x + c->week_hourwidth * (c->week_maxhour - c->week_minhour);
446 i = ep->early_warn > ep->late_warn;
447 w[!i] = TOD(ep->time) - ep->early_warn;
448 w[ i] = TOD(ep->time) - ep->late_warn;
449 b = TOD(ep->time);
450 e = TOD(ep->time) + (ep->notime ? 1200 : ep->length);
451 w[0] = x + XPOS(w[0]);
452 w[1] = x + XPOS(w[1]);
453 b = x + XPOS(b);
454 e = x + XPOS(e);
455 ee = e < b+MINLEN ? b+MINLEN : e;
456 if (ep->notime && config.week_bignotime) {
457 b = 0;
458 e = ee = 9999;
459 }
460 if (config.weekwarn && !ep->notime && !node->days_warn)
461 for (i=0; i < 2; i++)
462 if (w[i] < b && w[i] <= w[1] && b+slope > 0) {
463 point[5].x =
464 point[0].x =
465 point[2].x = BOUND(w[i]+slope, x, xend);
466 point[1].x = BOUND(w[i], x, xend);
467 point[3].x =
468 point[4].x = BOUND(b+slope, point[0].x, xend);
469 point[5].y =
470 point[0].y =
471 point[4].y = y;
472 point[1].y = y + c->week_barheight/2+1;
473 point[2].y =
474 point[3].y = y + (c->week_barheight&~1)+1;
475 g_setcolor(COL_WWARN);
476 g_drawpoly(point, 5, FALSE);
477 g_setcolor(COL_WFRAME);
478 g_drawlines(point, 6);
479 }
480
481 point[6].x =
482 point[0].x =
483 point[2].x = BOUND(b+slope, x, xend);
484 point[1].x = BOUND(b, x, xend-MINLEN);
485 point[3].x = BOUND(ee-slope, point[0].x, xend);
486 point[3].x =
487 point[5].x = BOUND(point[3].x, point[1].x+MINLEN, xend);
488 point[4].x = BOUND(e, point[3].x, xend);
489 point[6].y =
490 point[0].y =
491 point[5].y = y;
492 point[1].y =
493 point[4].y = y + c->week_barheight/2+1;
494 point[2].y =
495 point[3].y = y + (c->week_barheight&~1)+1;
496 if (ep->notime && !config.week_bignotime)
497 point[3].x = point[5].x = point[0].x;
498 g_setcolor(ep->suspended || node->days_warn ?
499 COL_WWARN : COL_WUSER_0 + (color & 7));
500 g_drawpoly(point, 6, color > 7);
501 g_setcolor(COL_WFRAME);
502 g_drawlines(point, 7);
503
504 if (*node->text) {
505 char buf[100];
506 strcpy(buf, node->text);
507 #ifdef JAPAN
508 partialstr->strptr = strpool;
509 plen = mixedstrlen_in_pixels(node->text, partialstr,
510 FONT_WNOTE, FONT_JNOTE);
511 if (node->textinside) {
512 if (plen > xend - (x = point[0].x))
513 plen = truncate_strpack(partialstr, xend - x,
514 FONT_WNOTE);
515 x += (point[3].x-point[0].x - plen) / 2;
516 if (x + plen > xend-1)
517 x = xend-1 - plen;
518 } else
519 if (plen > xend - (x = point[4].x + 3))
520 (void)truncate_strpack(partialstr, xend - x,
521 FONT_WNOTE);
522 #else
523 if (node->textinside) {
524 int l;
525 x = point[0].x;
526 truncate_string(buf, xend - x, FONT_WNOTE);
527 l = strlen_in_pixels(buf, FONT_WNOTE);
528 x += (point[3].x-point[0].x - l) / 2;
529 if (x + l > xend-1)
530 x = xend-1 - l;
531 } else {
532 x = point[4].x + 3;
533 truncate_string(buf, xend - x, FONT_WNOTE);
534 }
535 #endif
536 g_setcolor(ep->acolor ? COL_HBLACK + ep->acolor-1 : COL_WNOTE);
537 g_setfont(FONT_WNOTE);
538 #ifdef JAPAN
539 g_setfont(FONT_JNOTE);
540 g_draw_strpack(x, y + c->week_barheight/2
541 + font[FONT_WDAY]->max_bounds.ascent/2,
542 partialstr);
543 #else
544 g_drawtext(x, y + c->week_barheight/2
545 + font[FONT_WDAY]->max_bounds.ascent/2,
546 buf, strlen(buf));
547 #endif
548 }
549 }
550
551 #ifdef JAPAN
truncate_strpack(strpack * partialstr,int maxpix,int ascfont)552 int truncate_strpack(
553 strpack *partialstr,
554 int maxpix, /* Maximum width in pixels. */
555 int ascfont) /* ascii font to draw */
556 {
557 int plen = 0;
558 strpack *upperpartstr;
559
560 for (upperpartstr = partialstr + MAXPARTIALSTRING;
561 partialstr < upperpartstr && partialstr->strptr != NULL;
562 partialstr++) {
563 if (plen + partialstr->pixlen > maxpix) { /* Truncating*/
564 if (partialstr->asciistr == False) { /* NON ascii */
565 partialstr->length *= (double)(maxpix - plen) /
566 (double)partialstr->pixlen;
567 if (partialstr->length &= ~1)
568 if (++partialstr < upperpartstr)
569 partialstr->strptr = NULL;
570 else
571 partialstr->strptr = NULL;
572 } else { /* ascii */
573 (void)truncate_string(partialstr->strptr,
574 maxpix-plen, ascfont);
575 if (partialstr->length =
576 strlen(partialstr->strptr))
577 if (++partialstr < upperpartstr)
578 partialstr->strptr = NULL;
579 else
580 partialstr->strptr = NULL;
581 }
582 plen = maxpix;
583 break;
584 } else
585 plen += partialstr->pixlen;
586 }
587 return plen;
588 }
589
g_draw_strpack(int x,int y,strpack * partialstr)590 void g_draw_strpack(
591 int x,
592 int y,
593 strpack *partialstr)
594 {
595 strpack *upperpartstr;
596
597 for (upperpartstr = partialstr + MAXPARTIALSTRING;
598 partialstr < upperpartstr && partialstr->strptr != NULL;
599 partialstr++) {
600 if (partialstr->asciistr == False)
601 g_drawtext16(x, y, partialstr->strptr,
602 partialstr->length/2);
603 else
604 g_drawtext(x, y, partialstr->strptr,
605 partialstr->length);
606 x += partialstr->pixlen;
607 }
608 }
609 #endif
610