1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: titlebar.c 1075 2008-06-04 00:19:39Z hubert@u.washington.edu $";
3 #endif
4
5 /*
6 * ========================================================================
7 * Copyright 2013-2021 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
9 *
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
13 *
14 * http://www.apache.org/licenses/LICENSE-2.0
15 *
16 * ========================================================================
17 */
18
19 #include "headers.h"
20 #include "titlebar.h"
21 #include "folder.h"
22 #include "../pith/state.h"
23 #include "../pith/bitmap.h"
24 #include "../pith/msgno.h"
25 #include "../pith/thread.h"
26 #include "../pith/conf.h"
27 #include "../pith/init.h"
28 #include "../pith/sort.h"
29 #include "../pith/news.h"
30 #include "../pith/util.h"
31 #include "../pith/folder.h"
32
33 /*
34 * Internal prototypes
35 */
36 int digit_count(long);
37 void output_titlebar(TITLE_S *);
38 char sort_letter(SortOrder);
39 char *percentage(long, long, int);
40
41
42 /*
43 * some useful macros...
44 */
45 #define MS_DEL (0x01)
46 #define MS_NEW (0x02)
47 #define MS_ANS (0x04)
48 #define MS_FWD (0x08)
49
50 #define STATUS_BITS(X) (!(X && (X)->valid) ? 0 \
51 : (X)->deleted ? MS_DEL \
52 : (X)->answered ? MS_ANS \
53 : (as.stream && user_flag_is_set(as.stream, (X)->msgno, FORWARDED_FLAG)) ? MS_FWD \
54 : (as.stream \
55 && (ps_global->unseen_in_view \
56 || (!(X)->seen \
57 && (!IS_NEWS(as.stream) \
58 || F_ON(F_FAKE_NEW_IN_NEWS, \
59 ps_global))))) \
60 ? MS_NEW : 0)
61
62 #define BAR_STATUS(X) (((X) & MS_DEL) ? "DEL" \
63 : ((X) & MS_ANS) ? "ANS" \
64 : ((X) & MS_FWD) ? "FWD" \
65 : (as.stream \
66 && (!IS_NEWS(as.stream) \
67 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)) \
68 && ((X) & MS_NEW)) ? "NEW" : " ")
69
70
71 static struct titlebar_state {
72 MAILSTREAM *stream;
73 MSGNO_S *msgmap;
74 char *title,
75 *folder_name,
76 *context_name;
77 long current_msg,
78 current_line,
79 current_thrd,
80 total_lines;
81 int msg_state,
82 cur_mess_col,
83 del_column,
84 percent_column,
85 page_column,
86 screen_cols,
87 pushed;
88 enum {Normal, OnlyRead, Closed} stream_status;
89 TitleBarType style;
90 TITLE_S titlecontainer;
91 } as, titlebar_stack;
92
93
94 static int titlebar_is_dirty = 1;
95
96 char *as_fname; /* folder name */
97 char *as_cname; /* context name */
98
99 void
push_titlebar_state(void)100 push_titlebar_state(void)
101 {
102 as.pushed = 1;
103 titlebar_stack = as;
104 if(as_fname) fs_give((void **)&as_fname);
105 as_fname = cpystr(as.folder_name);
106 if(as_cname) fs_give((void **)&as_cname);
107 as_cname = cpystr(as.context_name);
108 if(as.folder_name) fs_give((void **)&as.folder_name);
109 if(as.context_name) fs_give((void **)&as.context_name);
110 }
111
112
113 void
pop_titlebar_state(void)114 pop_titlebar_state(void)
115 {
116 /* guard against case where push pushed no state */
117 if(titlebar_stack.style != TitleBarNone){
118 fs_give((void **)&(as.folder_name)); /* free malloc'd values */
119 fs_give((void **)&(as.context_name));
120 as = titlebar_stack;
121 if(as.pushed){
122 as.folder_name = as_fname ? cpystr(as_fname) : NULL;
123 as.context_name = as_cname ? cpystr(as_cname): NULL;
124 }
125 as.pushed = 0;
126 }
127 }
128
129
130 int
digit_count(long int n)131 digit_count(long int n)
132 {
133 int i;
134
135 return((n > 9)
136 ? (1 + (((i = digit_count(n / 10L)) == 3 || i == 7) ? i+1 : i))
137 : 1);
138 }
139
140
141 void
mark_titlebar_dirty(void)142 mark_titlebar_dirty(void)
143 {
144 titlebar_is_dirty = 1;
145 }
146
147
148 /*----------------------------------------------------------------------
149 Sets up style and contents of current titlebar line
150
151 All of the text is assumed to be UTF-8 text, which probably isn't
152 true yet.
153
154 Args: title -- The title that appears in the center of the line
155 This title is in UTF-8 characters.
156 display_on_screen -- flag whether to display on screen or generate
157 string
158 style -- The format/style of the titlebar line
159 msgmap -- MSGNO_S * to selected message map
160 current_pl -- The current page or line number
161 total_pl -- The total line or page count
162
163 Set the contents of the anchor line. It's called an anchor line
164 because it's always present and titlebars the user. This accesses a
165 number of global variables, but doesn't change any. There are several
166 different styles of the titlebar line.
167
168 It's OK to call this with a bogus current message - it is only used
169 to look up status of current message
170
171 Formats only change the right section (part3).
172 FolderName: "<folder>" xx Messages
173 MessageNumber: "<folder>" message x,xxx of x,xxx XXX
174 TextPercent: line xxx of xxx xx%
175 MsgTextPercent: "<folder>" message x,xxx of x,xxx xx% XXX
176 FileTextPercent: "<filename>" line xxx of xxx xx%
177
178 Several strings and column numbers are saved so later updates to the status
179 line for changes in message number or percentage can be done efficiently.
180 ----*/
181
182 char *
set_titlebar(char * title,MAILSTREAM * stream,CONTEXT_S * cntxt,char * folder,MSGNO_S * msgmap,int display_on_screen,TitleBarType style,long int current_pl,long int total_pl,COLOR_PAIR * color)183 set_titlebar(char *title, MAILSTREAM *stream, CONTEXT_S *cntxt, char *folder,
184 MSGNO_S *msgmap, int display_on_screen, TitleBarType style,
185 long int current_pl, long int total_pl, COLOR_PAIR *color)
186 {
187 struct variable *vars = ps_global->vars;
188 MESSAGECACHE *mc = NULL;
189 PINETHRD_S *thrd = NULL;
190 TITLE_S *tc;
191
192 dprint((9, "set_titlebar - style: %d current message cnt:%ld",
193 style, mn_total_cur(msgmap)));
194 dprint((9, " current_pl: %ld total_pl: %ld\n",
195 current_pl, total_pl));
196
197 as.current_msg = (mn_get_total(msgmap) > 0L)
198 ? MAX(0, mn_get_cur(msgmap)) : 0L;
199 as.msgmap = msgmap;
200 as.style = style;
201 as.title = title;
202 as.stream = stream;
203 as.stream_status = (!as.stream || (sp_dead_stream(as.stream)))
204 ? Closed : as.stream->rdonly ? OnlyRead : Normal;
205
206 if(ps_global->first_open_was_attempted
207 && as.stream_status == Closed
208 && VAR_TITLECLOSED_FORE_COLOR && VAR_TITLECLOSED_BACK_COLOR){
209 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
210 strncpy(as.titlecontainer.color.fg,
211 VAR_TITLECLOSED_FORE_COLOR, MAXCOLORLEN);
212 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
213 strncpy(as.titlecontainer.color.bg,
214 VAR_TITLECLOSED_BACK_COLOR, MAXCOLORLEN);
215 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
216 }
217 else{
218 if(color){
219 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
220 if(color->fg){
221 strncpy(as.titlecontainer.color.fg, color->fg, MAXCOLORLEN);
222 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
223 }
224
225 if(color->bg){
226 strncpy(as.titlecontainer.color.bg, color->bg, MAXCOLORLEN);
227 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
228 }
229 }
230 else{
231 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
232 if(VAR_TITLE_FORE_COLOR){
233 strncpy(as.titlecontainer.color.fg,
234 VAR_TITLE_FORE_COLOR, MAXCOLORLEN);
235 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
236 }
237
238 if(VAR_TITLE_BACK_COLOR){
239 strncpy(as.titlecontainer.color.bg,
240 VAR_TITLE_BACK_COLOR, MAXCOLORLEN);
241 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
242 }
243 }
244 }
245
246 if(as.folder_name)
247 fs_give((void **)&as.folder_name);
248
249 if(folder){
250 unsigned char *fname;
251 fname = folder_name_decoded((unsigned char *) folder);
252 if(!strucmp(folder, ps_global->inbox_name) && cntxt == ps_global->context_list)
253 as.folder_name = cpystr(pretty_fn(fname ? (char *) fname : folder));
254 else
255 as.folder_name = cpystr(fname ? (char *) fname : folder);
256 if(fname) fs_give((void **)&fname);
257 }
258
259 if(!as.folder_name)
260 as.folder_name = cpystr("");
261
262 if(as.context_name)
263 fs_give((void **)&as.context_name);
264
265 /*
266 * Handle setting up the context if appropriate.
267 */
268 if(cntxt && context_isambig(folder) && ps_global->context_list->next
269 && (strucmp(as.folder_name, ps_global->inbox_name) || cntxt != ps_global->context_list)){
270 /*
271 * if there's more than one context and the current folder
272 * is in it (ambiguous name), set the context name...
273 */
274 as.context_name = cpystr(cntxt->nickname
275 ? cntxt->nickname
276 : cntxt->context);
277 }
278
279 if(!as.context_name)
280 as.context_name = cpystr("");
281
282 if(as.stream && style != FolderName
283 && style != ThrdIndex && as.current_msg > 0L) {
284 long rawno;
285
286 if((rawno = mn_m2raw(msgmap, as.current_msg)) > 0L
287 && rawno <= as.stream->nmsgs
288 && !((mc = mail_elt(as.stream, rawno)) && mc->valid)){
289 pine_mail_fetch_flags(as.stream, long2string(rawno), NIL);
290 if(rawno <= as.stream->nmsgs && as.stream && rawno > 0L)
291 mc = mail_elt(as.stream, rawno);
292 else
293 mc = NULL;
294 if(mc && !mc->valid)
295 mc = NULL;
296 }
297 }
298
299 if(style == ThrdIndex || style == ThrdMsgNum || style == ThrdMsgPercent){
300 if(mn_get_total(msgmap) > 0L)
301 thrd = fetch_thread(stream, mn_m2raw(msgmap, mn_get_cur(msgmap)));
302
303 if(thrd && thrd->top && thrd->top != thrd->rawno)
304 thrd = fetch_thread(stream, thrd->top);
305
306 if(thrd)
307 as.current_thrd = thrd->thrdno;
308 }
309
310 switch(style) {
311 case ThrdIndex:
312 as.total_lines = msgmap->max_thrdno;
313 break;
314
315 case TextPercent:
316 case MsgTextPercent:
317 case FileTextPercent :
318 case ThrdMsgPercent:
319 as.total_lines = total_pl;
320 as.current_line = current_pl;
321 /* fall through to set state */
322 case ThrdMsgNum:
323 case MessageNumber:
324 as.msg_state = STATUS_BITS(mc);
325
326 case FolderName: /* nothing more to do for this one */
327 break;
328 default:
329 break;
330 }
331
332 tc = format_titlebar();
333 if(display_on_screen)
334 output_titlebar(tc);
335
336 return(tc->titlebar_line);
337 }
338
339
340 void
redraw_titlebar(void)341 redraw_titlebar(void)
342 {
343 output_titlebar(format_titlebar());
344 }
345
346
347 void
output_titlebar(TITLE_S * tc)348 output_titlebar(TITLE_S *tc)
349 {
350 COLOR_PAIR *lastc = NULL, *newcolor;
351
352 if(ps_global->ttyo
353 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
354 titlebar_is_dirty = 1;
355 return;
356 }
357
358 newcolor = tc ? &tc->color : NULL;
359
360 if(newcolor)
361 lastc = pico_set_colorp(newcolor, PSC_REV | PSC_RET);
362
363 if(tc && tc->titlebar_line)
364 PutLine0(0, 0, tc->titlebar_line);
365
366 if (ps_global->ttyo)
367 MoveCursor(0, ps_global->ttyo->screen_cols); /* move to the last column */
368
369 if(lastc){
370 (void)pico_set_colorp(lastc, PSC_NONE);
371 free_color_pair(&lastc);
372 }
373
374 fflush(stdout);
375 }
376
377
378 void
titlebar_stream_closing(MAILSTREAM * stream)379 titlebar_stream_closing(MAILSTREAM *stream)
380 {
381 if(stream == as.stream)
382 as.stream = NULL;
383 }
384
385
386 /* caller should free returned color pair */
387 COLOR_PAIR *
current_titlebar_color(void)388 current_titlebar_color(void)
389 {
390 COLOR_PAIR *col;
391 COLOR_PAIR *the_color = NULL;
392
393 col = as.title ? &as.titlecontainer.color : NULL;
394
395 if(col && col->fg && col->fg[0] && col->bg && col->bg[0])
396 the_color = new_color_pair(col->fg, col->bg);
397
398 return(the_color);
399 }
400
401
402 /*----------------------------------------------------------------------
403 Redraw or draw the top line, the title bar
404
405 The titlebar has Four fields:
406 1) "Version" of fixed length and is always positioned two spaces
407 in from left display edge.
408 2) "Location" which is fixed for each style of titlebar and
409 is positioned two spaces from the right display edge
410 3) "Title" which is of fixed length, and is centered if
411 there's space
412 4) "Folder" whose existence depends on style and which can
413 have it's length adjusted (within limits) so it will
414 equally share the space between 1) and 2) with the
415 "Title". The rule for existence is that in the
416 space between 1) and 2) there must be one space between
417 3) and 4) AND at least 50% of 4) must be displayed.
418 However, if the folder name can be displayed, then do
419 so, and display as much as possible of the collection name.
420
421 Returns - Formatted title bar
422 ----*/
423 TITLE_S *
format_titlebar(void)424 format_titlebar(void)
425 {
426 char version[50], fold_tmp[6*MAXPATH+1], *titlebar_line,
427 loc1[200], loc_label[10], *thd_label, *ss_string, *q,
428 *plus, *loc2 = "", title[200];
429 int title_len = 0, ver_len, loc1_len = 0, loc2_len = 0, fold_len = 0, num_len,
430 s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0, tryloc = 1,
431 cur_mess_col_offset = -1, percent_column_offset = -1, page_column_offset = -1,
432 ss_len, thd_len, is_context, avail, extra;
433
434 titlebar_is_dirty = 0;
435
436 if(!as.title)
437 return NULL;
438
439 if(!as.folder_name)
440 as.folder_name = cpystr("");
441
442 if(!as.context_name)
443 as.context_name = cpystr("");
444
445 /*
446 * Full blown title looks like:
447 *
448 * | LV vers VT title TF folder FL location LR |
449 */
450 #define LV 2 /* space between Left edge and Version, must be >= 2 */
451 #define VT 3 /* space between Version and Title */
452 #define TF 1 /* space between Title and Folder */
453 #define FL 2 /* space between Folder and Location */
454 #define LR 2 /* space between Location and Right edge, >= 2 */
455 /* half of n but round up */
456 #define HRU(n) (((n) <= 0) ? 0 : (((n)%2) ? ((n)+1)/2 : (n)/2))
457 /* half of n but round down */
458 #define HRD(n) (((n) <= 0) ? 0 : ((n)/2))
459
460 titlebar_line = as.titlecontainer.titlebar_line;
461
462 avail = MIN(ps_global->ttyo->screen_cols, MAX_SCREEN_COLS);
463
464 /* initialize */
465 as.del_column = -1;
466 as.cur_mess_col = -1;
467 as.percent_column = -1;
468 as.page_column = -1;
469
470 is_context = as.context_name ? strlen(as.context_name) : 0;
471
472 snprintf(version, sizeof(version), "ALPINE %s", ALPINE_VERSION);
473 version[sizeof(version)-1] = '\0';
474 ver_len = (int) utf8_width(version); /* fixed version field width */
475
476 title[0] = '\0';
477 if(as.title){
478 strncpy(title, as.title, sizeof(title));
479 title[sizeof(title)-1] = '\0';
480 }
481
482 /* Add Sort indicator to title */
483 if(F_ON(F_SHOW_SORT, ps_global) &&
484 !(as.style == FileTextPercent || as.style == TextPercent)){
485 SortOrder current_sort;
486 int current_rev;
487 char let;
488
489 current_sort = mn_get_sort(ps_global->msgmap);
490 current_rev = mn_get_revsort(ps_global->msgmap);
491
492 /* turn current_sort into a letter */
493 let = sort_letter(current_sort);
494 if(let == 'A' && current_rev){
495 let = 'R';
496 current_rev = 0;
497 }
498
499 snprintf(title+strlen(title), sizeof(title)-strlen(title),
500 " [%s%c]", current_rev ? "R" : "", let);
501 title[sizeof(title)-1] = '\0';
502 }
503
504 title_len = (int) utf8_width(title); /* title field width */
505
506 s1 = MAX(MIN(LV, avail), 0); /* left two spaces */
507 avail -= s1;
508
509 s6 = MAX(MIN(LR, avail), 0); /* right two spaces */
510 avail -= s6;
511
512 title_len = MAX(MIN(title_len, avail), 0);
513 avail -= title_len;
514
515 if(ver_len + VT > avail){ /* can only fit title */
516 ver_len = 0;
517
518 s2 = MAX(MIN(HRD(avail), avail), 0);
519 avail -= s2;
520
521 s3 = MAX(0, avail);
522 }
523 else{
524 s2 = MAX(MIN(VT, avail), 0);
525 avail -= s2;
526
527 ver_len = MAX(MIN(ver_len, avail), 0);
528 avail -= ver_len;
529
530 try_smaller_loc:
531
532 /*
533 * set location field's length and value based on requested style
534 */
535 if(as.style == ThrdIndex)
536 /* TRANSLATORS: In titlebar, Thd is an abbreviation for Thread, Msg for Message.
537 They are used when there isn't enough space so need to be short.
538 The formatting isn't very flexible. These come before the number
539 of the message or thread, as in
540 Message 17
541 when reading message number 17. */
542 snprintf(loc_label, sizeof(loc_label), "%s ", (is_context || tryloc==2) ? _("Thd") : _("Thread"));
543 else
544 snprintf(loc_label, sizeof(loc_label), "%s ", (is_context || tryloc==2) ? _("Msg") : _("Message"));
545
546 if(tryloc == 3 && as.style != FolderName && mn_get_total(as.msgmap))
547 loc_label[0] = '\0';
548
549 loc_label[sizeof(loc_label)-1] = '\0';
550
551 if(as.style == ThrdMsgNum || as.style == ThrdMsgPercent){
552 thd_label = is_context ? _("Thd") : _("Thread");
553 thd_len = (int) utf8_width(thd_label);
554 }
555
556 loc1_len = (int) utf8_width(loc_label); /* initial length */
557
558 if(!mn_get_total(as.msgmap)){
559 loc_label[strlen(loc_label)-1] = 's';
560 snprintf(loc1, sizeof(loc1), "%s %s", _("No"), loc_label);
561 loc1[sizeof(loc1)-1]= '\0';
562 }
563 else{
564 switch(as.style){
565 case FolderName : /* "x,xxx <loc_label>s" */
566 if(*loc_label){
567 if(mn_get_total(as.msgmap) != 1)
568 loc_label[strlen(loc_label)-1] = 's';
569 else
570 loc_label[strlen(loc_label)-1] = '\0';
571 }
572
573 snprintf(loc1, sizeof(loc1), "%s %s", comatose(mn_get_total(as.msgmap)), loc_label);
574 loc1[sizeof(loc1)-1]= '\0';
575 break;
576
577 case MessageNumber : /* "<loc_label> xxx of xxx DEL" */
578 num_len = strlen(comatose(mn_get_total(as.msgmap)));
579 cur_mess_col_offset = loc1_len;
580 snprintf(loc1, sizeof(loc1), "%s%*.*s of %s", loc_label,
581 num_len, num_len,
582 comatose(as.current_msg),
583 comatose(mn_get_total(as.msgmap)));
584 loc1[sizeof(loc1)-1]= '\0';
585 loc2 = BAR_STATUS(as.msg_state);
586 loc2_len = 3;
587 break;
588
589 case ThrdIndex : /* "<loc_label> xxx of xxx" */
590 num_len = strlen(comatose(as.total_lines));
591 cur_mess_col_offset = loc1_len;
592 snprintf(loc1, sizeof(loc1), "%s%*.*s of %s", loc_label,
593 num_len, num_len,
594 comatose(as.current_thrd),
595 comatose(as.total_lines));
596 loc1[sizeof(loc1)-1]= '\0';
597 break;
598
599 case ThrdMsgNum : /* "<loc_label> xxx in Thd xxx DEL" */
600 num_len = strlen(comatose(mn_get_total(as.msgmap)));
601 cur_mess_col_offset = loc1_len;
602 snprintf(loc1, sizeof(loc1), "%s%*.*s in %s %s", loc_label,
603 num_len, num_len,
604 comatose(as.current_msg),
605 thd_label,
606 comatose(as.current_thrd));
607 loc1[sizeof(loc1)-1]= '\0';
608 loc2 = BAR_STATUS(as.msg_state);
609 loc2_len = 3;
610 break;
611
612 case MsgTextPercent : /* "<loc_label> xxx of xxx xx% DEL" */
613 num_len = strlen(comatose(mn_get_total(as.msgmap)));
614 cur_mess_col_offset = loc1_len;
615 percent_column_offset = 3;
616 snprintf(loc1, sizeof(loc1), "%s%*.*s of %s %s", loc_label,
617 num_len, num_len,
618 comatose(as.current_msg),
619 comatose(mn_get_total(as.msgmap)),
620 percentage(as.current_line, as.total_lines, 1));
621 loc1[sizeof(loc1)-1]= '\0';
622 loc2 = BAR_STATUS(as.msg_state);
623 loc2_len = 3;
624 break;
625
626 case ThrdMsgPercent : /* "<loc_label> xxx in Thd xxx xx% DEL" */
627 num_len = strlen(comatose(mn_get_total(as.msgmap)));
628 cur_mess_col_offset = loc1_len;
629 percent_column_offset = 3;
630 snprintf(loc1, sizeof(loc1), "%s%*.*s in %s %s %s", loc_label,
631 num_len, num_len,
632 comatose(as.current_msg),
633 thd_label,
634 comatose(as.current_thrd),
635 percentage(as.current_line, as.total_lines, 1));
636 loc1[sizeof(loc1)-1]= '\0';
637 loc2 = BAR_STATUS(as.msg_state);
638 loc2_len = 3;
639 break;
640
641 case TextPercent :
642 /* NOTE: no fold_tmp setup below for TextPercent style */
643 case FileTextPercent : /* "Line xxx of xxx xx%" */
644 num_len = strlen(comatose(as.total_lines));
645 page_column_offset = 5;
646 percent_column_offset = 3;
647 snprintf(loc1, sizeof(loc1), "Line %*.*s of %s %s",
648 num_len, num_len,
649 comatose(as.current_line),
650 comatose(as.total_lines),
651 percentage(as.current_line, as.total_lines, 1));
652 loc1[sizeof(loc1)-1]= '\0';
653 break;
654 default:
655 break;
656 }
657 }
658
659 loc1_len = utf8_width(loc1);
660
661 if(loc1_len + loc2_len + ((loc2_len > 0) ? 1 : 0) >= avail){ /* can't fit location in */
662 if(tryloc < 3){
663 tryloc++;
664 goto try_smaller_loc;
665 }
666
667 loc1_len = loc2_len = 0;
668
669 avail += s2; /* re-allocate s2 to center title */
670
671 s2 = MAX(MIN(HRD(avail), avail), 0);
672 avail -= s2;
673
674 s3 = MAX(0, avail);
675 }
676 else{
677 loc1_len = MAX(MIN(loc1_len, avail), 0);
678 avail -= loc1_len;
679
680 loc2_len = MAX(MIN(loc2_len, avail), 0);
681 avail -= loc2_len;
682
683 if(loc2_len > 0){
684 s5 = MAX(MIN(1, avail), 0);
685 avail -= s5;
686 }
687 else
688 s5 = 0;
689
690 s3 = MAX(MIN(TF, avail), 0);
691 avail -= s3;
692
693 s4 = MAX(MIN(FL, avail), 0);
694 avail -= s4;
695
696 if(avail){
697 /* TRANSLATORS: it might say READONLY or CLOSED in the titlebar, referring to
698 the current folder. */
699 ss_string = as.stream_status == Closed ? _("(CLOSED)") :
700 (as.stream_status == OnlyRead
701 && !IS_NEWS(as.stream))
702 ? _("(READONLY)") : "";
703 ss_len = (int) utf8_width(ss_string);
704
705 /* figure folder_length and what's to be displayed */
706 fold_tmp[0] = '\0';
707 if(as.style == FileTextPercent || as.style == TextPercent){
708 if(as.style == FileTextPercent){
709 extra = (int) utf8_width("File: ");
710 fold_len = (int) utf8_width(as.folder_name);
711 if(fold_len + extra <= avail){ /* all of folder fit? */
712 strncpy(fold_tmp, "File: ", sizeof(fold_tmp));
713 q = fold_tmp + strlen(fold_tmp);
714 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
715 }
716 else if(HRU(fold_len) + extra+3 <= avail){
717 /*
718 * fold_tmp = ...partial_folder_name
719 */
720 strncpy(fold_tmp, "File: ...", sizeof(fold_tmp));
721 q = fold_tmp + strlen(fold_tmp);
722 (void) utf8_to_width_rhs(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(extra+3));
723 }
724 }
725 /* else leave folder/file name blank */
726 }
727 else{
728 int ct_len;
729
730 fold_len = (int) utf8_width(as.folder_name);
731
732 if(is_context
733 && as.stream_status != Closed
734 && (ct_len = (int) utf8_width(as.context_name))){
735
736 extra = 3; /* length from "<" ">" and SPACE */
737
738 if(ct_len + fold_len + ss_len + extra <= avail){
739 q = fold_tmp;
740 *q++ = '<';
741 strncpy(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp));
742 q += strlen(q);
743 *q++ = '>';
744 *q++ = ' ';
745 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
746 q += strlen(q);
747 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
748 }
749 else if(ct_len + fold_len + ss_len + extra <= avail){
750 q = fold_tmp;
751 *q++ = '<';
752 strncpy(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp));
753 q += strlen(q);
754 *q++ = '>';
755 *q++ = ' ';
756 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
757 q += strlen(q);
758 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
759 }
760 else if(HRU(ct_len) + fold_len + ss_len + extra <= avail){
761 q = fold_tmp;
762 *q++ = '<';
763 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(fold_len+ss_len+extra), 1);
764 *q++ = '>';
765 *q++ = ' ';
766 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
767 q += strlen(q);
768 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
769 }
770 else if(HRU(ct_len) + HRU(fold_len) + ss_len + extra <= avail){
771 q = fold_tmp;
772 *q++ = '<';
773 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), HRU(ct_len), 1);
774 *q++ = '>';
775 *q++ = ' ';
776 q += utf8_to_width_rhs(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(HRU(ct_len)+ss_len+extra));
777 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
778 }
779 else if(ss_len > 0 && ss_len <= avail){
780 q = fold_tmp;
781 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
782 } else if(fold_len < avail){
783 q = fold_tmp;
784 if(fold_len + 7 < avail){
785 *q++ = '<';
786 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), avail - fold_len - 3, 1);
787 *q++ = '>';
788 *q++ = ' ';
789 }
790 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
791 }
792 /* else leave it out */
793 }
794 else{
795 /* TRANSLATORS: the name of the open folder follows this in the titlebar */
796 extra = strlen(_("Folder: "));
797 if(fold_len + ss_len + extra <= avail){
798 q = fold_tmp;
799 strncpy(q, "Folder: ", sizeof(fold_tmp)-(q-fold_tmp));
800 q += strlen(q);
801 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
802 q += strlen(q);
803 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
804 }
805 else{
806 if(fold_len + ss_len <= avail){
807 q = fold_tmp;
808 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
809 q += strlen(q);
810 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
811 }
812 else if(((HRU(fold_len) + ss_len <= avail)
813 || (avail > ss_len+3 && avail > 10)) && fold_len > 5){
814 q = fold_tmp;
815 strncpy(q, "...", sizeof(fold_tmp));
816 q += strlen(q);
817 q += utf8_to_width_rhs(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(3+ss_len));
818 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
819 }
820 else if(ss_len > 0 && ss_len <= avail){
821 q = fold_tmp;
822 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
823 }
824 /* else leave it out */
825 }
826 }
827 }
828
829 fold_tmp[sizeof(fold_tmp)-1] = '\0';
830
831 /* write title, location and, optionally, the folder name */
832 fold_len = (int) utf8_width(fold_tmp);
833 }
834
835 fold_len = MAX(MIN(fold_len, avail), 0);
836 avail -= fold_len;
837
838 /* center folder in its space */
839 if(avail){
840 int inc;
841
842 inc = HRU(avail);
843
844 s3 += inc;
845 avail -= inc;
846
847 s4 += avail;
848 }
849 }
850 }
851
852 plus = "";
853
854 if(as.style != FileTextPercent && as.style != TextPercent){
855 NETMBX mb;
856
857 if(as.stream
858 && as.stream->mailbox
859 && mail_valid_net_parse(as.stream->mailbox, &mb)
860 && (mb.sslflag || mb.tlsflag))
861 plus = "+";
862 }
863
864
865 if(loc1_len > 0 && cur_mess_col_offset >= 0)
866 as.cur_mess_col = s1+ver_len+s2+title_len+s3+fold_len+s4 + cur_mess_col_offset;
867
868 if(loc1_len > 0 && page_column_offset >= 0)
869 as.page_column = s1+ver_len+s2+title_len+s3+fold_len+s4 + page_column_offset;
870
871 if(loc2_len > 0)
872 as.del_column = s1+ver_len+s2+title_len+s3+fold_len+s4+loc1_len+s5;
873
874 if(loc1_len > 0 && percent_column_offset > 0)
875 as.percent_column = s1+ver_len+s2+title_len+s3+fold_len+s4+loc1_len - percent_column_offset;
876
877
878 utf8_snprintf(titlebar_line, 6*MAX_SCREEN_COLS, "%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s",
879 s1, s1, "",
880 ver_len, ver_len, version,
881 s2, s2, "",
882 title_len, title_len, title,
883 s3, s3, "",
884 fold_len, fold_len, fold_tmp,
885 s4, s4, "",
886 loc1_len, loc1_len, loc1,
887 s5, s5, "",
888 loc2_len, loc2_len, loc2,
889 s6, s6, plus);
890
891 return(&as.titlecontainer);
892 }
893
894
895 char
sort_letter(SortOrder sort)896 sort_letter(SortOrder sort)
897 {
898 char let = 'A', *p;
899
900 if((p = sort_name(sort)) != NULL && *p){
901 while(*(p+1) && islower((unsigned char) *p))
902 p++;
903
904 if(*p)
905 let = *p;
906 }
907
908 return(let);
909 }
910
911
912 /*
913 * Update the titlebar line if the message number changed
914 *
915 * Args: None, uses state setup on previous call to set_titlebar.
916 */
917 void
update_titlebar_message(void)918 update_titlebar_message(void)
919 {
920 long curnum = 0, maxnum, oldnum = 0;
921 PINETHRD_S *thrd = NULL;
922 COLOR_PAIR *lastc = NULL, *titlecolor;
923 char buf[50];
924 int num_len;
925 unsigned long rawno;
926
927 if(ps_global->ttyo
928 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
929 titlebar_is_dirty = 1;
930 return;
931 }
932
933 if(as.style == ThrdIndex){
934
935 oldnum = as.current_thrd;
936
937 if(as.stream && (rawno=mn_m2raw(as.msgmap, mn_get_cur(as.msgmap))))
938 thrd = fetch_thread(as.stream, rawno);
939
940 if(thrd && thrd->top && (thrd=fetch_thread(as.stream, thrd->top)))
941 curnum = thrd->thrdno;
942 }
943 else if(as.cur_mess_col >= 0){
944 curnum = mn_get_cur(as.msgmap);
945 oldnum = as.current_msg;
946 }
947
948 if(as.cur_mess_col >= 0 && oldnum != curnum){
949
950 if(as.style == ThrdIndex){
951 as.current_thrd = curnum;
952 maxnum = as.msgmap->max_thrdno;
953 }
954 else{
955 as.current_msg = curnum;
956 maxnum = mn_get_total(as.msgmap);
957 }
958
959 titlecolor = &as.titlecontainer.color;
960
961 if(titlecolor)
962 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
963
964 num_len = strlen(comatose(maxnum));
965
966 snprintf(buf, sizeof(buf), "%*.*s", num_len, num_len, comatose(curnum));
967
968 PutLine0(0, as.cur_mess_col, buf);
969
970 if (ps_global->ttyo)
971 MoveCursor(0, ps_global->ttyo->screen_cols); /* move to the last column */
972
973 if(lastc){
974 (void)pico_set_colorp(lastc, PSC_NONE);
975 free_color_pair(&lastc);
976 }
977
978 fflush(stdout);
979 }
980 }
981
982
983
984 /*
985 * Update titlebar line's message status field ("DEL", "NEW", etc)
986 *
987 * Args: None, operates on state set during most recent set_titlebar call
988 */
989 int
update_titlebar_status(void)990 update_titlebar_status(void)
991 {
992 unsigned long rawno;
993 MESSAGECACHE *mc;
994 COLOR_PAIR *lastc = NULL, *titlecolor;
995
996 if(ps_global->ttyo
997 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
998 titlebar_is_dirty = 1;
999 return(0);
1000 }
1001
1002 if(!as.stream || as.current_msg <= 0L || as.del_column < 0)
1003 return(1);
1004
1005 if(as.current_msg != mn_get_cur(as.msgmap))
1006 update_titlebar_message();
1007
1008 mc = ((rawno = mn_m2raw(as.msgmap, as.current_msg)) > 0L
1009 && rawno <= as.stream->nmsgs)
1010 ? mail_elt(as.stream, rawno) : NULL;
1011
1012 if(!(mc && mc->valid))
1013 return(0); /* indeterminate */
1014
1015 if(mc->deleted){ /* deleted takes precedence */
1016 if(as.msg_state & MS_DEL)
1017 return(1);
1018 }
1019 else if(mc->answered){ /* then answered */
1020 if(as.msg_state & MS_ANS)
1021 return(1);
1022 } /* then forwarded */
1023 else if(user_flag_is_set(as.stream, mc->msgno, FORWARDED_FLAG)){
1024 if(as.msg_state & MS_FWD)
1025 return(1);
1026 }
1027 else if(!mc->seen && as.stream
1028 && (!IS_NEWS(as.stream)
1029 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global))){
1030 if(as.msg_state & MS_NEW) /* then seen */
1031 return(1);
1032 }
1033 else{
1034 if(as.msg_state == 0) /* nothing to change */
1035 return(1);
1036 }
1037
1038 as.msg_state = STATUS_BITS(mc);
1039
1040 titlecolor = &as.titlecontainer.color;
1041
1042 if(titlecolor)
1043 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1044
1045 PutLine0(0, as.del_column, BAR_STATUS(as.msg_state));
1046
1047 if (ps_global->ttyo)
1048 MoveCursor(0, ps_global->ttyo->screen_cols); /* move to the last column */
1049
1050 if(lastc){
1051 (void)pico_set_colorp(lastc, PSC_NONE);
1052 free_color_pair(&lastc);
1053 }
1054
1055 fflush(stdout);
1056 return(1);
1057 }
1058
1059
1060 /*
1061 * Update the percentage shown in the titlebar line
1062 *
1063 * Args: new_line_number -- line number to calculate new percentage
1064 */
1065 void
update_titlebar_percent(long int new_line_number)1066 update_titlebar_percent(long int new_line_number)
1067 {
1068 COLOR_PAIR *lastc = NULL, *titlecolor;
1069
1070 if(as.percent_column < 0 || new_line_number == as.current_line)
1071 return;
1072
1073 if(ps_global->ttyo
1074 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
1075 titlebar_is_dirty = 1;
1076 return;
1077 }
1078
1079 as.current_line = new_line_number;
1080
1081 titlecolor = &as.titlecontainer.color;
1082
1083 if(titlecolor)
1084 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1085
1086 PutLine0(0, as.percent_column,
1087 percentage(as.current_line, as.total_lines, 0));
1088
1089 if (ps_global->ttyo)
1090 MoveCursor(0, ps_global->ttyo->screen_cols); /* move to the last column */
1091
1092 if(lastc){
1093 (void)pico_set_colorp(lastc, PSC_NONE);
1094 free_color_pair(&lastc);
1095 }
1096
1097 fflush(stdout);
1098 }
1099
1100
1101 /*
1102 * Update the percentage AND line number shown in the titlebar line
1103 *
1104 * Args: new_line_number -- line number to calculate new percentage
1105 */
1106 void
update_titlebar_lpercent(long int new_line_number)1107 update_titlebar_lpercent(long int new_line_number)
1108 {
1109 COLOR_PAIR *lastc = NULL, *titlecolor;
1110 int num_len;
1111 char buf[50];
1112
1113 if(as.page_column < 0 || new_line_number == as.current_line)
1114 return;
1115
1116 if(ps_global->ttyo
1117 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
1118 titlebar_is_dirty = 1;
1119 return;
1120 }
1121
1122 as.current_line = new_line_number;
1123
1124 titlecolor = &as.titlecontainer.color;
1125
1126 if(titlecolor)
1127 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1128
1129 num_len = strlen(comatose(as.total_lines));
1130 snprintf(buf, sizeof(buf), "%*.*s", num_len, num_len, comatose(as.current_line));
1131
1132 PutLine0(0, as.page_column, buf);
1133
1134 PutLine0(0, as.percent_column,
1135 percentage(as.current_line, as.total_lines, 0));
1136
1137 if (ps_global->ttyo)
1138 MoveCursor(0, ps_global->ttyo->screen_cols); /* move to the last column */
1139
1140 if(lastc){
1141 (void)pico_set_colorp(lastc, PSC_NONE);
1142 free_color_pair(&lastc);
1143 }
1144
1145 fflush(stdout);
1146 }
1147
1148
1149 /*----------------------------------------------------------------------
1150 Return static buf containing portion of lines displayed
1151
1152 Args: part -- how much so far
1153 total -- how many total
1154
1155 ---*/
1156 char *
percentage(long int part,long int total,int suppress_top)1157 percentage(long int part, long int total, int suppress_top)
1158 {
1159 static char percent[4];
1160
1161 if(total == 0L || (total <= ps_global->ttyo->screen_rows
1162 - HEADER_ROWS(ps_global)
1163 - FOOTER_ROWS(ps_global)))
1164 strncpy(percent, "ALL", sizeof(percent));
1165 else if(!suppress_top && part <= ps_global->ttyo->screen_rows
1166 - HEADER_ROWS(ps_global)
1167 - FOOTER_ROWS(ps_global))
1168 strncpy(percent, "TOP", sizeof(percent));
1169 else if(part >= total)
1170 strncpy(percent, "END", sizeof(percent));
1171 else
1172 snprintf(percent, sizeof(percent), "%2ld%%", (100L * part)/total);
1173
1174 percent[sizeof(percent)-1] = '\0';
1175
1176 return(percent);
1177 }
1178
1179
1180 /*
1181 * end_titlebar - free resources associated with titlebar state struct
1182 */
1183 void
end_titlebar(void)1184 end_titlebar(void)
1185 {
1186 if(as.folder_name)
1187 fs_give((void **)&as.folder_name);
1188
1189 if(as.context_name)
1190 fs_give((void **)&as.context_name);
1191 }
1192
1193
1194 /*----------------------------------------------------------------------
1195 Exported method to display status of mail check
1196
1197 Args: putstr -- should be NO LONGER THAN 2 bytes
1198
1199 Result: putstr displayed at upper-left-hand corner of screen
1200 ----*/
1201 void
check_cue_display(char * putstr)1202 check_cue_display(char *putstr)
1203 {
1204 COLOR_PAIR *lastc = NULL, *titlecolor;
1205 static char check_cue_char;
1206
1207 if(ps_global->read_predicted &&
1208 (check_cue_char == putstr[0]
1209 || (putstr[0] == ' ' && putstr[1] == '\0')))
1210 return;
1211 else{
1212 if(putstr[0] == ' ')
1213 check_cue_char = '\0';
1214 else
1215 check_cue_char = putstr[0];
1216 }
1217
1218 titlecolor = &as.titlecontainer.color;
1219
1220 if(titlecolor)
1221 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1222
1223 PutLine0(0, 0, putstr); /* show delay cue */
1224
1225 if (ps_global->ttyo)
1226 MoveCursor(0, ps_global->ttyo->screen_cols); /* move to the last column */
1227
1228 if(lastc){
1229 (void)pico_set_colorp(lastc, PSC_NONE);
1230 free_color_pair(&lastc);
1231 }
1232
1233 fflush(stdout);
1234 }
1235
1236
1237 /*----------------------------------------------------------------------
1238 Mandatory function of ../pith/newmail.c
1239
1240 Args: none
1241
1242 Result: newmail cue displayed at upper-left-hand corner of screen
1243 ----*/
1244 void
newmail_check_cue(int onoroff)1245 newmail_check_cue(int onoroff)
1246 {
1247 if(F_ON(F_SHOW_DELAY_CUE, ps_global) && !ps_global->in_init_seq){
1248 check_cue_display((onoroff == TRUE) ? " *" : " ");
1249 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
1250 }
1251
1252 #ifdef _WINDOWS
1253 if(onoroff)
1254 mswin_setcursor (MSWIN_CURSOR_BUSY);
1255 else
1256 mswin_setcursor (MSWIN_CURSOR_ARROW);
1257 #endif
1258 }
1259
1260
1261 /*----------------------------------------------------------------------
1262 Mandatory function of ../pith/newmail.c
1263
1264 Args: none
1265
1266 Result: checkpoint delay cue displayed at upper-left-hand corner of screen
1267 ----*/
1268 void
newmail_check_point_cue(int onoroff)1269 newmail_check_point_cue(int onoroff)
1270 {
1271 if(F_ON(F_SHOW_DELAY_CUE, ps_global)){
1272 check_cue_display((onoroff == TRUE) ? "**" : " ");
1273 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
1274 }
1275
1276 #ifdef _WINDOWS
1277 if(onoroff)
1278 mswin_setcursor (MSWIN_CURSOR_BUSY);
1279 else
1280 mswin_setcursor (MSWIN_CURSOR_ARROW);
1281 #endif
1282 }
1283
1284 void
free_titlebar_globals(void)1285 free_titlebar_globals(void)
1286 {
1287 if(as_fname) fs_give((void **) &as_fname);
1288 if(as_cname) fs_give((void **) &as_cname);
1289 if(as.folder_name) fs_give((void **)&as.folder_name);
1290 if(as.context_name) fs_give((void **)&as.context_name);
1291 }
1292