* New version 2.20.11
[alpine.git] / alpine / titlebar.c
blob2fc35d8dce3705aceab6ae6628b2d3fbfe905c88
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
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2016 Eduardo Chappa
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
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
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"
34 * Internal prototypes
36 int digit_count(long);
37 void output_titlebar(TITLE_S *);
38 char sort_letter(SortOrder);
39 char *percentage(long, long, int);
43 * some useful macros...
45 #define MS_DEL (0x01)
46 #define MS_NEW (0x02)
47 #define MS_ANS (0x04)
48 #define MS_FWD (0x08)
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)
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" : " ")
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 enum {Normal, OnlyRead, Closed} stream_status;
88 TitleBarType style;
89 TITLE_S titlecontainer;
90 } as, titlebar_stack;
93 static int titlebar_is_dirty = 1;
96 void
97 push_titlebar_state(void)
99 titlebar_stack = as;
100 as.folder_name = NULL; /* erase knowledge of malloc'd data */
101 as.context_name = NULL;
105 void
106 pop_titlebar_state(void)
108 /* guard against case where push pushed no state */
109 if(titlebar_stack.style != TitleBarNone){
110 fs_give((void **)&(as.folder_name)); /* free malloc'd values */
111 fs_give((void **)&(as.context_name));
112 as = titlebar_stack;
118 digit_count(long int n)
120 int i;
122 return((n > 9)
123 ? (1 + (((i = digit_count(n / 10L)) == 3 || i == 7) ? i+1 : i))
124 : 1);
128 void
129 mark_titlebar_dirty(void)
131 titlebar_is_dirty = 1;
135 /*----------------------------------------------------------------------
136 Sets up style and contents of current titlebar line
138 All of the text is assumed to be UTF-8 text, which probably isn't
139 true yet.
141 Args: title -- The title that appears in the center of the line
142 This title is in UTF-8 characters.
143 display_on_screen -- flag whether to display on screen or generate
144 string
145 style -- The format/style of the titlebar line
146 msgmap -- MSGNO_S * to selected message map
147 current_pl -- The current page or line number
148 total_pl -- The total line or page count
150 Set the contents of the anchor line. It's called an anchor line
151 because it's always present and titlebars the user. This accesses a
152 number of global variables, but doesn't change any. There are several
153 different styles of the titlebar line.
155 It's OK to call this with a bogus current message - it is only used
156 to look up status of current message
158 Formats only change the right section (part3).
159 FolderName: "<folder>" xx Messages
160 MessageNumber: "<folder>" message x,xxx of x,xxx XXX
161 TextPercent: line xxx of xxx xx%
162 MsgTextPercent: "<folder>" message x,xxx of x,xxx xx% XXX
163 FileTextPercent: "<filename>" line xxx of xxx xx%
165 Several strings and column numbers are saved so later updates to the status
166 line for changes in message number or percentage can be done efficiently.
167 ----*/
169 char *
170 set_titlebar(char *title, MAILSTREAM *stream, CONTEXT_S *cntxt, char *folder,
171 MSGNO_S *msgmap, int display_on_screen, TitleBarType style,
172 long int current_pl, long int total_pl, COLOR_PAIR *color)
174 struct variable *vars = ps_global->vars;
175 MESSAGECACHE *mc = NULL;
176 PINETHRD_S *thrd = NULL;
177 TITLE_S *tc;
179 dprint((9, "set_titlebar - style: %d current message cnt:%ld",
180 style, mn_total_cur(msgmap)));
181 dprint((9, " current_pl: %ld total_pl: %ld\n",
182 current_pl, total_pl));
184 as.current_msg = (mn_get_total(msgmap) > 0L)
185 ? MAX(0, mn_get_cur(msgmap)) : 0L;
186 as.msgmap = msgmap;
187 as.style = style;
188 as.title = title;
189 as.stream = stream;
190 as.stream_status = (!as.stream || (sp_dead_stream(as.stream)))
191 ? Closed : as.stream->rdonly ? OnlyRead : Normal;
193 if(ps_global->first_open_was_attempted
194 && as.stream_status == Closed
195 && VAR_TITLECLOSED_FORE_COLOR && VAR_TITLECLOSED_BACK_COLOR){
196 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
197 strncpy(as.titlecontainer.color.fg,
198 VAR_TITLECLOSED_FORE_COLOR, MAXCOLORLEN);
199 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
200 strncpy(as.titlecontainer.color.bg,
201 VAR_TITLECLOSED_BACK_COLOR, MAXCOLORLEN);
202 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
204 else{
205 if(color){
206 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
207 if(color->fg){
208 strncpy(as.titlecontainer.color.fg, color->fg, MAXCOLORLEN);
209 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
212 if(color->bg){
213 strncpy(as.titlecontainer.color.bg, color->bg, MAXCOLORLEN);
214 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
217 else{
218 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
219 if(VAR_TITLE_FORE_COLOR){
220 strncpy(as.titlecontainer.color.fg,
221 VAR_TITLE_FORE_COLOR, MAXCOLORLEN);
222 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
225 if(VAR_TITLE_BACK_COLOR){
226 strncpy(as.titlecontainer.color.bg,
227 VAR_TITLE_BACK_COLOR, MAXCOLORLEN);
228 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
233 if(as.folder_name)
234 fs_give((void **)&as.folder_name);
236 if(folder){
237 unsigned char *fname;
238 fname = folder_name_decoded((unsigned char *) folder);
239 if(!strucmp(folder, ps_global->inbox_name) && cntxt == ps_global->context_list)
240 as.folder_name = cpystr(pretty_fn(fname ? (char *) fname : folder));
241 else
242 as.folder_name = cpystr(fname ? (char *) fname : folder);
243 if(fname) fs_give((void **)&fname);
246 if(!as.folder_name)
247 as.folder_name = cpystr("");
249 if(as.context_name)
250 fs_give((void **)&as.context_name);
253 * Handle setting up the context if appropriate.
255 if(cntxt && context_isambig(folder) && ps_global->context_list->next
256 && (strucmp(as.folder_name, ps_global->inbox_name) || cntxt != ps_global->context_list)){
258 * if there's more than one context and the current folder
259 * is in it (ambiguous name), set the context name...
261 as.context_name = cpystr(cntxt->nickname
262 ? cntxt->nickname
263 : cntxt->context);
266 if(!as.context_name)
267 as.context_name = cpystr("");
269 if(as.stream && style != FolderName
270 && style != ThrdIndex && as.current_msg > 0L) {
271 long rawno;
273 if((rawno = mn_m2raw(msgmap, as.current_msg)) > 0L
274 && rawno <= as.stream->nmsgs
275 && !((mc = mail_elt(as.stream, rawno)) && mc->valid)){
276 pine_mail_fetch_flags(as.stream, long2string(rawno), NIL);
277 mc = mail_elt(as.stream, rawno);
281 if(style == ThrdIndex || style == ThrdMsgNum || style == ThrdMsgPercent){
282 if(mn_get_total(msgmap) > 0L)
283 thrd = fetch_thread(stream, mn_m2raw(msgmap, mn_get_cur(msgmap)));
285 if(thrd && thrd->top && thrd->top != thrd->rawno)
286 thrd = fetch_thread(stream, thrd->top);
288 if(thrd)
289 as.current_thrd = thrd->thrdno;
292 switch(style) {
293 case ThrdIndex:
294 as.total_lines = msgmap->max_thrdno;
295 break;
297 case TextPercent:
298 case MsgTextPercent:
299 case FileTextPercent :
300 case ThrdMsgPercent:
301 as.total_lines = total_pl;
302 as.current_line = current_pl;
303 /* fall through to set state */
304 case ThrdMsgNum:
305 case MessageNumber:
306 as.msg_state = STATUS_BITS(mc);
308 case FolderName: /* nothing more to do for this one */
309 break;
310 default:
311 break;
314 tc = format_titlebar();
315 if(display_on_screen)
316 output_titlebar(tc);
318 return(tc->titlebar_line);
322 void
323 redraw_titlebar(void)
325 output_titlebar(format_titlebar());
329 void
330 output_titlebar(TITLE_S *tc)
332 COLOR_PAIR *lastc = NULL, *newcolor;
334 if(ps_global->ttyo
335 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
336 titlebar_is_dirty = 1;
337 return;
340 newcolor = tc ? &tc->color : NULL;
342 if(newcolor)
343 lastc = pico_set_colorp(newcolor, PSC_REV | PSC_RET);
345 if(tc && tc->titlebar_line)
346 PutLine0(0, 0, tc->titlebar_line);
348 if(lastc){
349 (void)pico_set_colorp(lastc, PSC_NONE);
350 free_color_pair(&lastc);
353 fflush(stdout);
357 void
358 titlebar_stream_closing(MAILSTREAM *stream)
360 if(stream == as.stream)
361 as.stream = NULL;
365 /* caller should free returned color pair */
366 COLOR_PAIR *
367 current_titlebar_color(void)
369 TITLE_S *tc;
370 COLOR_PAIR *col;
371 COLOR_PAIR *the_color = NULL;
373 tc = format_titlebar();
374 col = tc ? &tc->color : NULL;
376 if(col && col->fg && col->fg[0] && col->bg && col->bg[0])
377 the_color = new_color_pair(col->fg, col->bg);
379 return(the_color);
383 /*----------------------------------------------------------------------
384 Redraw or draw the top line, the title bar
386 The titlebar has Four fields:
387 1) "Version" of fixed length and is always positioned two spaces
388 in from left display edge.
389 2) "Location" which is fixed for each style of titlebar and
390 is positioned two spaces from the right display edge
391 3) "Title" which is of fixed length, and is centered if
392 there's space
393 4) "Folder" whose existence depends on style and which can
394 have it's length adjusted (within limits) so it will
395 equally share the space between 1) and 2) with the
396 "Title". The rule for existence is that in the
397 space between 1) and 2) there must be one space between
398 3) and 4) AND at least 50% of 4) must be displayed.
399 However, if the folder name can be displayed, then do
400 so, and display as much as possible of the collection name.
402 Returns - Formatted title bar
403 ----*/
404 TITLE_S *
405 format_titlebar(void)
407 char version[50], fold_tmp[6*MAXPATH+1], *titlebar_line,
408 loc1[200], loc_label[10], *thd_label, *ss_string, *q,
409 *plus, *loc2 = "", title[200];
410 int title_len = 0, ver_len, loc1_len = 0, loc2_len = 0, fold_len = 0, num_len,
411 s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0, tryloc = 1,
412 cur_mess_col_offset = -1, percent_column_offset = -1, page_column_offset = -1,
413 ss_len, thd_len, is_context, avail, extra;
415 titlebar_is_dirty = 0;
417 if(!as.title)
418 return NULL;
420 if(!as.folder_name)
421 as.folder_name = cpystr("");
423 if(!as.context_name)
424 as.context_name = cpystr("");
427 * Full blown title looks like:
429 * | LV vers VT title TF folder FL location LR |
431 #define LV 2 /* space between Left edge and Version, must be >= 2 */
432 #define VT 3 /* space between Version and Title */
433 #define TF 1 /* space between Title and Folder */
434 #define FL 2 /* space between Folder and Location */
435 #define LR 2 /* space between Location and Right edge, >= 2 */
436 /* half of n but round up */
437 #define HRU(n) (((n) <= 0) ? 0 : (((n)%2) ? ((n)+1)/2 : (n)/2))
438 /* half of n but round down */
439 #define HRD(n) (((n) <= 0) ? 0 : ((n)/2))
441 titlebar_line = as.titlecontainer.titlebar_line;
443 avail = MIN(ps_global->ttyo->screen_cols, MAX_SCREEN_COLS);
445 /* initialize */
446 as.del_column = -1;
447 as.cur_mess_col = -1;
448 as.percent_column = -1;
449 as.page_column = -1;
451 is_context = as.context_name ? strlen(as.context_name) : 0;
453 snprintf(version, sizeof(version), "ALPINE %s", ALPINE_VERSION);
454 version[sizeof(version)-1] = '\0';
455 ver_len = (int) utf8_width(version); /* fixed version field width */
457 title[0] = '\0';
458 if(as.title){
459 strncpy(title, as.title, sizeof(title));
460 title[sizeof(title)-1] = '\0';
463 /* Add Sort indicator to title */
464 if(F_ON(F_SHOW_SORT, ps_global) &&
465 !(as.style == FileTextPercent || as.style == TextPercent)){
466 SortOrder current_sort;
467 int current_rev;
468 char let;
470 current_sort = mn_get_sort(ps_global->msgmap);
471 current_rev = mn_get_revsort(ps_global->msgmap);
473 /* turn current_sort into a letter */
474 let = sort_letter(current_sort);
475 if(let == 'A' && current_rev){
476 let = 'R';
477 current_rev = 0;
480 snprintf(title+strlen(title), sizeof(title)-strlen(title),
481 " [%s%c]", current_rev ? "R" : "", let);
482 title[sizeof(title)-1] = '\0';
485 title_len = (int) utf8_width(title); /* title field width */
487 s1 = MAX(MIN(LV, avail), 0); /* left two spaces */
488 avail -= s1;
490 s6 = MAX(MIN(LR, avail), 0); /* right two spaces */
491 avail -= s6;
493 title_len = MAX(MIN(title_len, avail), 0);
494 avail -= title_len;
496 if(ver_len + VT > avail){ /* can only fit title */
497 ver_len = 0;
499 s2 = MAX(MIN(HRD(avail), avail), 0);
500 avail -= s2;
502 s3 = MAX(0, avail);
504 else{
505 s2 = MAX(MIN(VT, avail), 0);
506 avail -= s2;
508 ver_len = MAX(MIN(ver_len, avail), 0);
509 avail -= ver_len;
511 try_smaller_loc:
514 * set location field's length and value based on requested style
516 if(as.style == ThrdIndex)
517 /* TRANSLATORS: In titlebar, Thd is an abbreviation for Thread, Msg for Message.
518 They are used when there isn't enough space so need to be short.
519 The formatting isn't very flexible. These come before the number
520 of the message or thread, as in
521 Message 17
522 when reading message number 17. */
523 snprintf(loc_label, sizeof(loc_label), "%s ", (is_context || tryloc==2) ? _("Thd") : _("Thread"));
524 else
525 snprintf(loc_label, sizeof(loc_label), "%s ", (is_context || tryloc==2) ? _("Msg") : _("Message"));
527 if(tryloc == 3 && as.style != FolderName && mn_get_total(as.msgmap))
528 loc_label[0] = '\0';
530 loc_label[sizeof(loc_label)-1] = '\0';
532 if(as.style == ThrdMsgNum || as.style == ThrdMsgPercent){
533 thd_label = is_context ? _("Thd") : _("Thread");
534 thd_len = (int) utf8_width(thd_label);
537 loc1_len = (int) utf8_width(loc_label); /* initial length */
539 if(!mn_get_total(as.msgmap)){
540 loc_label[strlen(loc_label)-1] = 's';
541 snprintf(loc1, sizeof(loc1), "%s %s", _("No"), loc_label);
542 loc1[sizeof(loc1)-1]= '\0';
544 else{
545 switch(as.style){
546 case FolderName : /* "x,xxx <loc_label>s" */
547 if(*loc_label){
548 if(mn_get_total(as.msgmap) != 1)
549 loc_label[strlen(loc_label)-1] = 's';
550 else
551 loc_label[strlen(loc_label)-1] = '\0';
554 snprintf(loc1, sizeof(loc1), "%s %s", comatose(mn_get_total(as.msgmap)), loc_label);
555 loc1[sizeof(loc1)-1]= '\0';
556 break;
558 case MessageNumber : /* "<loc_label> xxx of xxx DEL" */
559 num_len = strlen(comatose(mn_get_total(as.msgmap)));
560 cur_mess_col_offset = loc1_len;
561 snprintf(loc1, sizeof(loc1), "%s%*.*s of %s", loc_label,
562 num_len, num_len,
563 comatose(as.current_msg),
564 comatose(mn_get_total(as.msgmap)));
565 loc1[sizeof(loc1)-1]= '\0';
566 loc2 = BAR_STATUS(as.msg_state);
567 loc2_len = 3;
568 break;
570 case ThrdIndex : /* "<loc_label> xxx of xxx" */
571 num_len = strlen(comatose(as.total_lines));
572 cur_mess_col_offset = loc1_len;
573 snprintf(loc1, sizeof(loc1), "%s%*.*s of %s", loc_label,
574 num_len, num_len,
575 comatose(as.current_thrd),
576 comatose(as.total_lines));
577 loc1[sizeof(loc1)-1]= '\0';
578 break;
580 case ThrdMsgNum : /* "<loc_label> xxx in Thd xxx DEL" */
581 num_len = strlen(comatose(mn_get_total(as.msgmap)));
582 cur_mess_col_offset = loc1_len;
583 snprintf(loc1, sizeof(loc1), "%s%*.*s in %s %s", loc_label,
584 num_len, num_len,
585 comatose(as.current_msg),
586 thd_label,
587 comatose(as.current_thrd));
588 loc1[sizeof(loc1)-1]= '\0';
589 loc2 = BAR_STATUS(as.msg_state);
590 loc2_len = 3;
591 break;
593 case MsgTextPercent : /* "<loc_label> xxx of xxx xx% DEL" */
594 num_len = strlen(comatose(mn_get_total(as.msgmap)));
595 cur_mess_col_offset = loc1_len;
596 percent_column_offset = 3;
597 snprintf(loc1, sizeof(loc1), "%s%*.*s of %s %s", loc_label,
598 num_len, num_len,
599 comatose(as.current_msg),
600 comatose(mn_get_total(as.msgmap)),
601 percentage(as.current_line, as.total_lines, 1));
602 loc1[sizeof(loc1)-1]= '\0';
603 loc2 = BAR_STATUS(as.msg_state);
604 loc2_len = 3;
605 break;
607 case ThrdMsgPercent : /* "<loc_label> xxx in Thd xxx xx% DEL" */
608 num_len = strlen(comatose(mn_get_total(as.msgmap)));
609 cur_mess_col_offset = loc1_len;
610 percent_column_offset = 3;
611 snprintf(loc1, sizeof(loc1), "%s%*.*s in %s %s %s", loc_label,
612 num_len, num_len,
613 comatose(as.current_msg),
614 thd_label,
615 comatose(as.current_thrd),
616 percentage(as.current_line, as.total_lines, 1));
617 loc1[sizeof(loc1)-1]= '\0';
618 loc2 = BAR_STATUS(as.msg_state);
619 loc2_len = 3;
620 break;
622 case TextPercent :
623 /* NOTE: no fold_tmp setup below for TextPercent style */
624 case FileTextPercent : /* "Line xxx of xxx xx%" */
625 num_len = strlen(comatose(as.total_lines));
626 page_column_offset = 5;
627 percent_column_offset = 3;
628 snprintf(loc1, sizeof(loc1), "Line %*.*s of %s %s",
629 num_len, num_len,
630 comatose(as.current_line),
631 comatose(as.total_lines),
632 percentage(as.current_line, as.total_lines, 1));
633 loc1[sizeof(loc1)-1]= '\0';
634 break;
635 default:
636 break;
640 loc1_len = utf8_width(loc1);
642 if(loc1_len + loc2_len + ((loc2_len > 0) ? 1 : 0) >= avail){ /* can't fit location in */
643 if(tryloc < 3){
644 tryloc++;
645 goto try_smaller_loc;
648 loc1_len = loc2_len = 0;
650 avail += s2; /* re-allocate s2 to center title */
652 s2 = MAX(MIN(HRD(avail), avail), 0);
653 avail -= s2;
655 s3 = MAX(0, avail);
657 else{
658 loc1_len = MAX(MIN(loc1_len, avail), 0);
659 avail -= loc1_len;
661 loc2_len = MAX(MIN(loc2_len, avail), 0);
662 avail -= loc2_len;
664 if(loc2_len > 0){
665 s5 = MAX(MIN(1, avail), 0);
666 avail -= s5;
668 else
669 s5 = 0;
671 s3 = MAX(MIN(TF, avail), 0);
672 avail -= s3;
674 s4 = MAX(MIN(FL, avail), 0);
675 avail -= s4;
677 if(avail){
678 /* TRANSLATORS: it might say READONLY or CLOSED in the titlebar, referring to
679 the current folder. */
680 ss_string = as.stream_status == Closed ? _("(CLOSED)") :
681 (as.stream_status == OnlyRead
682 && !IS_NEWS(as.stream))
683 ? _("(READONLY)") : "";
684 ss_len = (int) utf8_width(ss_string);
686 /* figure folder_length and what's to be displayed */
687 fold_tmp[0] = '\0';
688 if(as.style == FileTextPercent || as.style == TextPercent){
689 if(as.style == FileTextPercent){
690 extra = (int) utf8_width("File: ");
691 fold_len = (int) utf8_width(as.folder_name);
692 if(fold_len + extra <= avail){ /* all of folder fit? */
693 strncpy(fold_tmp, "File: ", sizeof(fold_tmp));
694 q = fold_tmp + strlen(fold_tmp);
695 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
697 else if(HRU(fold_len) + extra+3 <= avail){
699 * fold_tmp = ...partial_folder_name
701 strncpy(fold_tmp, "File: ...", sizeof(fold_tmp));
702 q = fold_tmp + strlen(fold_tmp);
703 (void) utf8_to_width_rhs(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(extra+3));
706 /* else leave folder/file name blank */
708 else{
709 int ct_len;
711 fold_len = (int) utf8_width(as.folder_name);
713 if(is_context
714 && as.stream_status != Closed
715 && (ct_len = (int) utf8_width(as.context_name))){
717 extra = 3; /* length from "<" ">" and SPACE */
719 if(ct_len + fold_len + ss_len + extra <= avail){
720 q = fold_tmp;
721 *q++ = '<';
722 strncpy(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp));
723 q += strlen(q);
724 *q++ = '>';
725 *q++ = ' ';
726 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
727 q += strlen(q);
728 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
730 else if(ct_len + fold_len + ss_len + extra <= avail){
731 q = fold_tmp;
732 *q++ = '<';
733 strncpy(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp));
734 q += strlen(q);
735 *q++ = '>';
736 *q++ = ' ';
737 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
738 q += strlen(q);
739 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
741 else if(HRU(ct_len) + fold_len + ss_len + extra <= avail){
742 q = fold_tmp;
743 *q++ = '<';
744 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(fold_len+ss_len+extra), 1);
745 *q++ = '>';
746 *q++ = ' ';
747 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
748 q += strlen(q);
749 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
751 else if(HRU(ct_len) + HRU(fold_len) + ss_len + extra <= avail){
752 q = fold_tmp;
753 *q++ = '<';
754 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), HRU(ct_len), 1);
755 *q++ = '>';
756 *q++ = ' ';
757 q += utf8_to_width_rhs(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(HRU(ct_len)+ss_len+extra));
758 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
760 else if(ss_len > 0 && ss_len <= avail){
761 q = fold_tmp;
762 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
763 } else if(fold_len < avail){
764 q = fold_tmp;
765 if(fold_len + 7 < avail){
766 *q++ = '<';
767 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), avail - fold_len - 3, 1);
768 *q++ = '>';
769 *q++ = ' ';
771 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
773 /* else leave it out */
775 else{
776 /* TRANSLATORS: the name of the open folder follows this in the titlebar */
777 extra = strlen(_("Folder: "));
778 if(fold_len + ss_len + extra <= avail){
779 q = fold_tmp;
780 strncpy(q, "Folder: ", sizeof(fold_tmp)-(q-fold_tmp));
781 q += strlen(q);
782 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
783 q += strlen(q);
784 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
786 else{
787 if(fold_len + ss_len <= avail){
788 q = fold_tmp;
789 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
790 q += strlen(q);
791 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
793 else if(((HRU(fold_len) + ss_len <= avail)
794 || (avail > ss_len+3 && avail > 10)) && fold_len > 5){
795 q = fold_tmp;
796 strncpy(q, "...", sizeof(fold_tmp));
797 q += strlen(q);
798 q += utf8_to_width_rhs(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(3+ss_len));
799 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
801 else if(ss_len > 0 && ss_len <= avail){
802 q = fold_tmp;
803 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
805 /* else leave it out */
810 fold_tmp[sizeof(fold_tmp)-1] = '\0';
812 /* write title, location and, optionally, the folder name */
813 fold_len = (int) utf8_width(fold_tmp);
816 fold_len = MAX(MIN(fold_len, avail), 0);
817 avail -= fold_len;
819 /* center folder in its space */
820 if(avail){
821 int inc;
823 inc = HRU(avail);
825 s3 += inc;
826 avail -= inc;
828 s4 += avail;
833 plus = "";
835 if(as.style != FileTextPercent && as.style != TextPercent){
836 NETMBX mb;
838 if(as.stream
839 && as.stream->mailbox
840 && mail_valid_net_parse(as.stream->mailbox, &mb)
841 && (mb.sslflag || mb.tlsflag))
842 plus = "+";
846 if(loc1_len > 0 && cur_mess_col_offset >= 0)
847 as.cur_mess_col = s1+ver_len+s2+title_len+s3+fold_len+s4 + cur_mess_col_offset;
849 if(loc1_len > 0 && page_column_offset >= 0)
850 as.page_column = s1+ver_len+s2+title_len+s3+fold_len+s4 + page_column_offset;
852 if(loc2_len > 0)
853 as.del_column = s1+ver_len+s2+title_len+s3+fold_len+s4+loc1_len+s5;
855 if(loc1_len > 0 && percent_column_offset > 0)
856 as.percent_column = s1+ver_len+s2+title_len+s3+fold_len+s4+loc1_len - percent_column_offset;
859 utf8_snprintf(titlebar_line, 6*MAX_SCREEN_COLS, "%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s",
860 s1, s1, "",
861 ver_len, ver_len, version,
862 s2, s2, "",
863 title_len, title_len, title,
864 s3, s3, "",
865 fold_len, fold_len, fold_tmp,
866 s4, s4, "",
867 loc1_len, loc1_len, loc1,
868 s5, s5, "",
869 loc2_len, loc2_len, loc2,
870 s6, s6, plus);
872 return(&as.titlecontainer);
876 char
877 sort_letter(SortOrder sort)
879 char let = 'A', *p;
881 if((p = sort_name(sort)) != NULL && *p){
882 while(*(p+1) && islower((unsigned char) *p))
883 p++;
885 if(*p)
886 let = *p;
889 return(let);
894 * Update the titlebar line if the message number changed
896 * Args: None, uses state setup on previous call to set_titlebar.
898 void
899 update_titlebar_message(void)
901 long curnum, maxnum, oldnum;
902 PINETHRD_S *thrd = NULL;
903 COLOR_PAIR *lastc = NULL, *titlecolor;
904 char buf[50];
905 int num_len;
906 unsigned long rawno;
908 if(ps_global->ttyo
909 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
910 titlebar_is_dirty = 1;
911 return;
914 if(as.style == ThrdIndex){
916 oldnum = as.current_thrd;
918 if(as.stream && (rawno=mn_m2raw(as.msgmap, mn_get_cur(as.msgmap))))
919 thrd = fetch_thread(as.stream, rawno);
921 if(thrd && thrd->top && (thrd=fetch_thread(as.stream, thrd->top)))
922 curnum = thrd->thrdno;
924 else if(as.cur_mess_col >= 0){
925 curnum = mn_get_cur(as.msgmap);
926 oldnum = as.current_msg;
929 if(as.cur_mess_col >= 0 && oldnum != curnum){
931 if(as.style == ThrdIndex){
932 as.current_thrd = curnum;
933 maxnum = as.msgmap->max_thrdno;
935 else{
936 as.current_msg = curnum;
937 maxnum = mn_get_total(as.msgmap);
940 titlecolor = &as.titlecontainer.color;
942 if(titlecolor)
943 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
945 num_len = strlen(comatose(maxnum));
947 snprintf(buf, sizeof(buf), "%*.*s", num_len, num_len, comatose(curnum));
949 PutLine0(0, as.cur_mess_col, buf);
951 if(lastc){
952 (void)pico_set_colorp(lastc, PSC_NONE);
953 free_color_pair(&lastc);
956 fflush(stdout);
963 * Update titlebar line's message status field ("DEL", "NEW", etc)
965 * Args: None, operates on state set during most recent set_titlebar call
968 update_titlebar_status(void)
970 unsigned long rawno;
971 MESSAGECACHE *mc;
972 COLOR_PAIR *lastc = NULL, *titlecolor;
974 if(ps_global->ttyo
975 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
976 titlebar_is_dirty = 1;
977 return(0);
980 if(!as.stream || as.current_msg <= 0L || as.del_column < 0)
981 return(1);
983 if(as.current_msg != mn_get_cur(as.msgmap))
984 update_titlebar_message();
986 mc = ((rawno = mn_m2raw(as.msgmap, as.current_msg)) > 0L
987 && rawno <= as.stream->nmsgs)
988 ? mail_elt(as.stream, rawno) : NULL;
990 if(!(mc && mc->valid))
991 return(0); /* indeterminate */
993 if(mc->deleted){ /* deleted takes precedence */
994 if(as.msg_state & MS_DEL)
995 return(1);
997 else if(mc->answered){ /* then answered */
998 if(as.msg_state & MS_ANS)
999 return(1);
1000 } /* then forwarded */
1001 else if(user_flag_is_set(as.stream, mc->msgno, FORWARDED_FLAG)){
1002 if(as.msg_state & MS_FWD)
1003 return(1);
1005 else if(!mc->seen && as.stream
1006 && (!IS_NEWS(as.stream)
1007 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global))){
1008 if(as.msg_state & MS_NEW) /* then seen */
1009 return(1);
1011 else{
1012 if(as.msg_state == 0) /* nothing to change */
1013 return(1);
1016 as.msg_state = STATUS_BITS(mc);
1018 titlecolor = &as.titlecontainer.color;
1020 if(titlecolor)
1021 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1023 PutLine0(0, as.del_column, BAR_STATUS(as.msg_state));
1025 if(lastc){
1026 (void)pico_set_colorp(lastc, PSC_NONE);
1027 free_color_pair(&lastc);
1030 fflush(stdout);
1031 return(1);
1036 * Update the percentage shown in the titlebar line
1038 * Args: new_line_number -- line number to calculate new percentage
1040 void
1041 update_titlebar_percent(long int new_line_number)
1043 COLOR_PAIR *lastc = NULL, *titlecolor;
1045 if(as.percent_column < 0 || new_line_number == as.current_line)
1046 return;
1048 if(ps_global->ttyo
1049 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
1050 titlebar_is_dirty = 1;
1051 return;
1054 as.current_line = new_line_number;
1056 titlecolor = &as.titlecontainer.color;
1058 if(titlecolor)
1059 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1061 PutLine0(0, as.percent_column,
1062 percentage(as.current_line, as.total_lines, 0));
1064 if(lastc){
1065 (void)pico_set_colorp(lastc, PSC_NONE);
1066 free_color_pair(&lastc);
1069 fflush(stdout);
1074 * Update the percentage AND line number shown in the titlebar line
1076 * Args: new_line_number -- line number to calculate new percentage
1078 void
1079 update_titlebar_lpercent(long int new_line_number)
1081 COLOR_PAIR *lastc = NULL, *titlecolor;
1082 int num_len;
1083 char buf[50];
1085 if(as.page_column < 0 || new_line_number == as.current_line)
1086 return;
1088 if(ps_global->ttyo
1089 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
1090 titlebar_is_dirty = 1;
1091 return;
1094 as.current_line = new_line_number;
1096 titlecolor = &as.titlecontainer.color;
1098 if(titlecolor)
1099 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1101 num_len = strlen(comatose(as.total_lines));
1102 snprintf(buf, sizeof(buf), "%*.*s", num_len, num_len, comatose(as.current_line));
1104 PutLine0(0, as.page_column, buf);
1106 PutLine0(0, as.percent_column,
1107 percentage(as.current_line, as.total_lines, 0));
1109 if(lastc){
1110 (void)pico_set_colorp(lastc, PSC_NONE);
1111 free_color_pair(&lastc);
1114 fflush(stdout);
1118 /*----------------------------------------------------------------------
1119 Return static buf containing portion of lines displayed
1121 Args: part -- how much so far
1122 total -- how many total
1124 ---*/
1125 char *
1126 percentage(long int part, long int total, int suppress_top)
1128 static char percent[4];
1130 if(total == 0L || (total <= ps_global->ttyo->screen_rows
1131 - HEADER_ROWS(ps_global)
1132 - FOOTER_ROWS(ps_global)))
1133 strncpy(percent, "ALL", sizeof(percent));
1134 else if(!suppress_top && part <= ps_global->ttyo->screen_rows
1135 - HEADER_ROWS(ps_global)
1136 - FOOTER_ROWS(ps_global))
1137 strncpy(percent, "TOP", sizeof(percent));
1138 else if(part >= total)
1139 strncpy(percent, "END", sizeof(percent));
1140 else
1141 snprintf(percent, sizeof(percent), "%2ld%%", (100L * part)/total);
1143 percent[sizeof(percent)-1] = '\0';
1145 return(percent);
1150 * end_titlebar - free resources associated with titlebar state struct
1152 void
1153 end_titlebar(void)
1155 if(as.folder_name)
1156 fs_give((void **)&as.folder_name);
1158 if(as.context_name)
1159 fs_give((void **)&as.context_name);
1163 /*----------------------------------------------------------------------
1164 Exported method to display status of mail check
1166 Args: putstr -- should be NO LONGER THAN 2 bytes
1168 Result: putstr displayed at upper-left-hand corner of screen
1169 ----*/
1170 void
1171 check_cue_display(char *putstr)
1173 COLOR_PAIR *lastc = NULL, *titlecolor;
1174 static char check_cue_char;
1176 if(ps_global->read_predicted &&
1177 (check_cue_char == putstr[0]
1178 || (putstr[0] == ' ' && putstr[1] == '\0')))
1179 return;
1180 else{
1181 if(putstr[0] == ' ')
1182 check_cue_char = '\0';
1183 else
1184 check_cue_char = putstr[0];
1187 titlecolor = &as.titlecontainer.color;
1189 if(titlecolor)
1190 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1192 PutLine0(0, 0, putstr); /* show delay cue */
1193 if(lastc){
1194 (void)pico_set_colorp(lastc, PSC_NONE);
1195 free_color_pair(&lastc);
1198 fflush(stdout);
1202 /*----------------------------------------------------------------------
1203 Mandatory function of ../pith/newmail.c
1205 Args: none
1207 Result: newmail cue displayed at upper-left-hand corner of screen
1208 ----*/
1209 void
1210 newmail_check_cue(int onoroff)
1212 if(F_ON(F_SHOW_DELAY_CUE, ps_global) && !ps_global->in_init_seq){
1213 check_cue_display((onoroff == TRUE) ? " *" : " ");
1214 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
1217 #ifdef _WINDOWS
1218 if(onoroff)
1219 mswin_setcursor (MSWIN_CURSOR_BUSY);
1220 else
1221 mswin_setcursor (MSWIN_CURSOR_ARROW);
1222 #endif
1226 /*----------------------------------------------------------------------
1227 Mandatory function of ../pith/newmail.c
1229 Args: none
1231 Result: checkpoint delay cue displayed at upper-left-hand corner of screen
1232 ----*/
1233 void
1234 newmail_check_point_cue(int onoroff)
1236 if(F_ON(F_SHOW_DELAY_CUE, ps_global)){
1237 check_cue_display((onoroff == TRUE) ? "**" : " ");
1238 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
1241 #ifdef _WINDOWS
1242 if(onoroff)
1243 mswin_setcursor (MSWIN_CURSOR_BUSY);
1244 else
1245 mswin_setcursor (MSWIN_CURSOR_ARROW);
1246 #endif