1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: titlebar.c 1075 2008-06-04 00:19:39Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2013-2018 Eduardo Chappa
8 * Copyright 2006-2008 University of Washington
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 * ========================================================================
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"
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...
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 \
55 && (ps_global->unseen_in_view \
57 && (!IS_NEWS(as.stream) \
58 || F_ON(F_FAKE_NEW_IN_NEWS, \
62 #define BAR_STATUS(X) (((X) & MS_DEL) ? "DEL" \
63 : ((X) & MS_ANS) ? "ANS" \
64 : ((X) & MS_FWD) ? "FWD" \
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
{
88 enum {Normal
, OnlyRead
, Closed
} stream_status
;
90 TITLE_S titlecontainer
;
94 static int titlebar_is_dirty
= 1;
96 char *as_fname
; /* folder name */
97 char *as_cname
; /* context name */
100 push_titlebar_state(void)
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
);
114 pop_titlebar_state(void)
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
));
122 as
.folder_name
= as_fname
? cpystr(as_fname
) : NULL
;
123 as
.context_name
= as_cname
? cpystr(as_cname
): NULL
;
131 digit_count(long int n
)
136 ? (1 + (((i
= digit_count(n
/ 10L)) == 3 || i
== 7) ? i
+1 : i
))
142 mark_titlebar_dirty(void)
144 titlebar_is_dirty
= 1;
148 /*----------------------------------------------------------------------
149 Sets up style and contents of current titlebar line
151 All of the text is assumed to be UTF-8 text, which probably isn't
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
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
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.
168 It's OK to call this with a bogus current message - it is only used
169 to look up status of current message
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%
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.
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
)
187 struct variable
*vars
= ps_global
->vars
;
188 MESSAGECACHE
*mc
= NULL
;
189 PINETHRD_S
*thrd
= NULL
;
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
));
197 as
.current_msg
= (mn_get_total(msgmap
) > 0L)
198 ? MAX(0, mn_get_cur(msgmap
)) : 0L;
203 as
.stream_status
= (!as
.stream
|| (sp_dead_stream(as
.stream
)))
204 ? Closed
: as
.stream
->rdonly
? OnlyRead
: Normal
;
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';
219 memset(&as
.titlecontainer
.color
, 0, sizeof(as
.titlecontainer
.color
));
221 strncpy(as
.titlecontainer
.color
.fg
, color
->fg
, MAXCOLORLEN
);
222 as
.titlecontainer
.color
.fg
[MAXCOLORLEN
] = '\0';
226 strncpy(as
.titlecontainer
.color
.bg
, color
->bg
, MAXCOLORLEN
);
227 as
.titlecontainer
.color
.bg
[MAXCOLORLEN
] = '\0';
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';
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';
247 fs_give((void **)&as
.folder_name
);
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
));
255 as
.folder_name
= cpystr(fname
? (char *) fname
: folder
);
256 if(fname
) fs_give((void **)&fname
);
260 as
.folder_name
= cpystr("");
263 fs_give((void **)&as
.context_name
);
266 * Handle setting up the context if appropriate.
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
)){
271 * if there's more than one context and the current folder
272 * is in it (ambiguous name), set the context name...
274 as
.context_name
= cpystr(cntxt
->nickname
280 as
.context_name
= cpystr("");
282 if(as
.stream
&& style
!= FolderName
283 && style
!= ThrdIndex
&& as
.current_msg
> 0L) {
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
);
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
)));
303 if(thrd
&& thrd
->top
&& thrd
->top
!= thrd
->rawno
)
304 thrd
= fetch_thread(stream
, thrd
->top
);
307 as
.current_thrd
= thrd
->thrdno
;
312 as
.total_lines
= msgmap
->max_thrdno
;
317 case FileTextPercent
:
319 as
.total_lines
= total_pl
;
320 as
.current_line
= current_pl
;
321 /* fall through to set state */
324 as
.msg_state
= STATUS_BITS(mc
);
326 case FolderName
: /* nothing more to do for this one */
332 tc
= format_titlebar();
333 if(display_on_screen
)
336 return(tc
->titlebar_line
);
341 redraw_titlebar(void)
343 output_titlebar(format_titlebar());
348 output_titlebar(TITLE_S
*tc
)
350 COLOR_PAIR
*lastc
= NULL
, *newcolor
;
353 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
354 titlebar_is_dirty
= 1;
358 newcolor
= tc
? &tc
->color
: NULL
;
361 lastc
= pico_set_colorp(newcolor
, PSC_REV
| PSC_RET
);
363 if(tc
&& tc
->titlebar_line
)
364 PutLine0(0, 0, tc
->titlebar_line
);
367 (void)pico_set_colorp(lastc
, PSC_NONE
);
368 free_color_pair(&lastc
);
376 titlebar_stream_closing(MAILSTREAM
*stream
)
378 if(stream
== as
.stream
)
383 /* caller should free returned color pair */
385 current_titlebar_color(void)
388 COLOR_PAIR
*the_color
= NULL
;
390 col
= as
.title
? &as
.titlecontainer
.color
: NULL
;
392 if(col
&& col
->fg
&& col
->fg
[0] && col
->bg
&& col
->bg
[0])
393 the_color
= new_color_pair(col
->fg
, col
->bg
);
399 /*----------------------------------------------------------------------
400 Redraw or draw the top line, the title bar
402 The titlebar has Four fields:
403 1) "Version" of fixed length and is always positioned two spaces
404 in from left display edge.
405 2) "Location" which is fixed for each style of titlebar and
406 is positioned two spaces from the right display edge
407 3) "Title" which is of fixed length, and is centered if
409 4) "Folder" whose existence depends on style and which can
410 have it's length adjusted (within limits) so it will
411 equally share the space between 1) and 2) with the
412 "Title". The rule for existence is that in the
413 space between 1) and 2) there must be one space between
414 3) and 4) AND at least 50% of 4) must be displayed.
415 However, if the folder name can be displayed, then do
416 so, and display as much as possible of the collection name.
418 Returns - Formatted title bar
421 format_titlebar(void)
423 char version
[50], fold_tmp
[6*MAXPATH
+1], *titlebar_line
,
424 loc1
[200], loc_label
[10], *thd_label
, *ss_string
, *q
,
425 *plus
, *loc2
= "", title
[200];
426 int title_len
= 0, ver_len
, loc1_len
= 0, loc2_len
= 0, fold_len
= 0, num_len
,
427 s1
= 0, s2
= 0, s3
= 0, s4
= 0, s5
= 0, s6
= 0, tryloc
= 1,
428 cur_mess_col_offset
= -1, percent_column_offset
= -1, page_column_offset
= -1,
429 ss_len
, thd_len
, is_context
, avail
, extra
;
431 titlebar_is_dirty
= 0;
437 as
.folder_name
= cpystr("");
440 as
.context_name
= cpystr("");
443 * Full blown title looks like:
445 * | LV vers VT title TF folder FL location LR |
447 #define LV 2 /* space between Left edge and Version, must be >= 2 */
448 #define VT 3 /* space between Version and Title */
449 #define TF 1 /* space between Title and Folder */
450 #define FL 2 /* space between Folder and Location */
451 #define LR 2 /* space between Location and Right edge, >= 2 */
452 /* half of n but round up */
453 #define HRU(n) (((n) <= 0) ? 0 : (((n)%2) ? ((n)+1)/2 : (n)/2))
454 /* half of n but round down */
455 #define HRD(n) (((n) <= 0) ? 0 : ((n)/2))
457 titlebar_line
= as
.titlecontainer
.titlebar_line
;
459 avail
= MIN(ps_global
->ttyo
->screen_cols
, MAX_SCREEN_COLS
);
463 as
.cur_mess_col
= -1;
464 as
.percent_column
= -1;
467 is_context
= as
.context_name
? strlen(as
.context_name
) : 0;
469 snprintf(version
, sizeof(version
), "ALPINE %s", ALPINE_VERSION
);
470 version
[sizeof(version
)-1] = '\0';
471 ver_len
= (int) utf8_width(version
); /* fixed version field width */
475 strncpy(title
, as
.title
, sizeof(title
));
476 title
[sizeof(title
)-1] = '\0';
479 /* Add Sort indicator to title */
480 if(F_ON(F_SHOW_SORT
, ps_global
) &&
481 !(as
.style
== FileTextPercent
|| as
.style
== TextPercent
)){
482 SortOrder current_sort
;
486 current_sort
= mn_get_sort(ps_global
->msgmap
);
487 current_rev
= mn_get_revsort(ps_global
->msgmap
);
489 /* turn current_sort into a letter */
490 let
= sort_letter(current_sort
);
491 if(let
== 'A' && current_rev
){
496 snprintf(title
+strlen(title
), sizeof(title
)-strlen(title
),
497 " [%s%c]", current_rev
? "R" : "", let
);
498 title
[sizeof(title
)-1] = '\0';
501 title_len
= (int) utf8_width(title
); /* title field width */
503 s1
= MAX(MIN(LV
, avail
), 0); /* left two spaces */
506 s6
= MAX(MIN(LR
, avail
), 0); /* right two spaces */
509 title_len
= MAX(MIN(title_len
, avail
), 0);
512 if(ver_len
+ VT
> avail
){ /* can only fit title */
515 s2
= MAX(MIN(HRD(avail
), avail
), 0);
521 s2
= MAX(MIN(VT
, avail
), 0);
524 ver_len
= MAX(MIN(ver_len
, avail
), 0);
530 * set location field's length and value based on requested style
532 if(as
.style
== ThrdIndex
)
533 /* TRANSLATORS: In titlebar, Thd is an abbreviation for Thread, Msg for Message.
534 They are used when there isn't enough space so need to be short.
535 The formatting isn't very flexible. These come before the number
536 of the message or thread, as in
538 when reading message number 17. */
539 snprintf(loc_label
, sizeof(loc_label
), "%s ", (is_context
|| tryloc
==2) ? _("Thd") : _("Thread"));
541 snprintf(loc_label
, sizeof(loc_label
), "%s ", (is_context
|| tryloc
==2) ? _("Msg") : _("Message"));
543 if(tryloc
== 3 && as
.style
!= FolderName
&& mn_get_total(as
.msgmap
))
546 loc_label
[sizeof(loc_label
)-1] = '\0';
548 if(as
.style
== ThrdMsgNum
|| as
.style
== ThrdMsgPercent
){
549 thd_label
= is_context
? _("Thd") : _("Thread");
550 thd_len
= (int) utf8_width(thd_label
);
553 loc1_len
= (int) utf8_width(loc_label
); /* initial length */
555 if(!mn_get_total(as
.msgmap
)){
556 loc_label
[strlen(loc_label
)-1] = 's';
557 snprintf(loc1
, sizeof(loc1
), "%s %s", _("No"), loc_label
);
558 loc1
[sizeof(loc1
)-1]= '\0';
562 case FolderName
: /* "x,xxx <loc_label>s" */
564 if(mn_get_total(as
.msgmap
) != 1)
565 loc_label
[strlen(loc_label
)-1] = 's';
567 loc_label
[strlen(loc_label
)-1] = '\0';
570 snprintf(loc1
, sizeof(loc1
), "%s %s", comatose(mn_get_total(as
.msgmap
)), loc_label
);
571 loc1
[sizeof(loc1
)-1]= '\0';
574 case MessageNumber
: /* "<loc_label> xxx of xxx DEL" */
575 num_len
= strlen(comatose(mn_get_total(as
.msgmap
)));
576 cur_mess_col_offset
= loc1_len
;
577 snprintf(loc1
, sizeof(loc1
), "%s%*.*s of %s", loc_label
,
579 comatose(as
.current_msg
),
580 comatose(mn_get_total(as
.msgmap
)));
581 loc1
[sizeof(loc1
)-1]= '\0';
582 loc2
= BAR_STATUS(as
.msg_state
);
586 case ThrdIndex
: /* "<loc_label> xxx of xxx" */
587 num_len
= strlen(comatose(as
.total_lines
));
588 cur_mess_col_offset
= loc1_len
;
589 snprintf(loc1
, sizeof(loc1
), "%s%*.*s of %s", loc_label
,
591 comatose(as
.current_thrd
),
592 comatose(as
.total_lines
));
593 loc1
[sizeof(loc1
)-1]= '\0';
596 case ThrdMsgNum
: /* "<loc_label> xxx in Thd xxx DEL" */
597 num_len
= strlen(comatose(mn_get_total(as
.msgmap
)));
598 cur_mess_col_offset
= loc1_len
;
599 snprintf(loc1
, sizeof(loc1
), "%s%*.*s in %s %s", loc_label
,
601 comatose(as
.current_msg
),
603 comatose(as
.current_thrd
));
604 loc1
[sizeof(loc1
)-1]= '\0';
605 loc2
= BAR_STATUS(as
.msg_state
);
609 case MsgTextPercent
: /* "<loc_label> xxx of xxx xx% DEL" */
610 num_len
= strlen(comatose(mn_get_total(as
.msgmap
)));
611 cur_mess_col_offset
= loc1_len
;
612 percent_column_offset
= 3;
613 snprintf(loc1
, sizeof(loc1
), "%s%*.*s of %s %s", loc_label
,
615 comatose(as
.current_msg
),
616 comatose(mn_get_total(as
.msgmap
)),
617 percentage(as
.current_line
, as
.total_lines
, 1));
618 loc1
[sizeof(loc1
)-1]= '\0';
619 loc2
= BAR_STATUS(as
.msg_state
);
623 case ThrdMsgPercent
: /* "<loc_label> xxx in Thd xxx xx% DEL" */
624 num_len
= strlen(comatose(mn_get_total(as
.msgmap
)));
625 cur_mess_col_offset
= loc1_len
;
626 percent_column_offset
= 3;
627 snprintf(loc1
, sizeof(loc1
), "%s%*.*s in %s %s %s", loc_label
,
629 comatose(as
.current_msg
),
631 comatose(as
.current_thrd
),
632 percentage(as
.current_line
, as
.total_lines
, 1));
633 loc1
[sizeof(loc1
)-1]= '\0';
634 loc2
= BAR_STATUS(as
.msg_state
);
639 /* NOTE: no fold_tmp setup below for TextPercent style */
640 case FileTextPercent
: /* "Line xxx of xxx xx%" */
641 num_len
= strlen(comatose(as
.total_lines
));
642 page_column_offset
= 5;
643 percent_column_offset
= 3;
644 snprintf(loc1
, sizeof(loc1
), "Line %*.*s of %s %s",
646 comatose(as
.current_line
),
647 comatose(as
.total_lines
),
648 percentage(as
.current_line
, as
.total_lines
, 1));
649 loc1
[sizeof(loc1
)-1]= '\0';
656 loc1_len
= utf8_width(loc1
);
658 if(loc1_len
+ loc2_len
+ ((loc2_len
> 0) ? 1 : 0) >= avail
){ /* can't fit location in */
661 goto try_smaller_loc
;
664 loc1_len
= loc2_len
= 0;
666 avail
+= s2
; /* re-allocate s2 to center title */
668 s2
= MAX(MIN(HRD(avail
), avail
), 0);
674 loc1_len
= MAX(MIN(loc1_len
, avail
), 0);
677 loc2_len
= MAX(MIN(loc2_len
, avail
), 0);
681 s5
= MAX(MIN(1, avail
), 0);
687 s3
= MAX(MIN(TF
, avail
), 0);
690 s4
= MAX(MIN(FL
, avail
), 0);
694 /* TRANSLATORS: it might say READONLY or CLOSED in the titlebar, referring to
695 the current folder. */
696 ss_string
= as
.stream_status
== Closed
? _("(CLOSED)") :
697 (as
.stream_status
== OnlyRead
698 && !IS_NEWS(as
.stream
))
699 ? _("(READONLY)") : "";
700 ss_len
= (int) utf8_width(ss_string
);
702 /* figure folder_length and what's to be displayed */
704 if(as
.style
== FileTextPercent
|| as
.style
== TextPercent
){
705 if(as
.style
== FileTextPercent
){
706 extra
= (int) utf8_width("File: ");
707 fold_len
= (int) utf8_width(as
.folder_name
);
708 if(fold_len
+ extra
<= avail
){ /* all of folder fit? */
709 strncpy(fold_tmp
, "File: ", sizeof(fold_tmp
));
710 q
= fold_tmp
+ strlen(fold_tmp
);
711 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
713 else if(HRU(fold_len
) + extra
+3 <= avail
){
715 * fold_tmp = ...partial_folder_name
717 strncpy(fold_tmp
, "File: ...", sizeof(fold_tmp
));
718 q
= fold_tmp
+ strlen(fold_tmp
);
719 (void) utf8_to_width_rhs(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
-(extra
+3));
722 /* else leave folder/file name blank */
727 fold_len
= (int) utf8_width(as
.folder_name
);
730 && as
.stream_status
!= Closed
731 && (ct_len
= (int) utf8_width(as
.context_name
))){
733 extra
= 3; /* length from "<" ">" and SPACE */
735 if(ct_len
+ fold_len
+ ss_len
+ extra
<= avail
){
738 strncpy(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
742 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
744 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
746 else if(ct_len
+ fold_len
+ ss_len
+ extra
<= avail
){
749 strncpy(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
753 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
755 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
757 else if(HRU(ct_len
) + fold_len
+ ss_len
+ extra
<= avail
){
760 q
+= utf8_pad_to_width(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
-(fold_len
+ss_len
+extra
), 1);
763 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
765 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
767 else if(HRU(ct_len
) + HRU(fold_len
) + ss_len
+ extra
<= avail
){
770 q
+= utf8_pad_to_width(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), HRU(ct_len
), 1);
773 q
+= utf8_to_width_rhs(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
-(HRU(ct_len
)+ss_len
+extra
));
774 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
776 else if(ss_len
> 0 && ss_len
<= avail
){
778 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
779 } else if(fold_len
< avail
){
781 if(fold_len
+ 7 < avail
){
783 q
+= utf8_pad_to_width(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
- fold_len
- 3, 1);
787 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
789 /* else leave it out */
792 /* TRANSLATORS: the name of the open folder follows this in the titlebar */
793 extra
= strlen(_("Folder: "));
794 if(fold_len
+ ss_len
+ extra
<= avail
){
796 strncpy(q
, "Folder: ", sizeof(fold_tmp
)-(q
-fold_tmp
));
798 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
800 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
803 if(fold_len
+ ss_len
<= avail
){
805 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
807 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
809 else if(((HRU(fold_len
) + ss_len
<= avail
)
810 || (avail
> ss_len
+3 && avail
> 10)) && fold_len
> 5){
812 strncpy(q
, "...", sizeof(fold_tmp
));
814 q
+= utf8_to_width_rhs(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
-(3+ss_len
));
815 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
817 else if(ss_len
> 0 && ss_len
<= avail
){
819 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
821 /* else leave it out */
826 fold_tmp
[sizeof(fold_tmp
)-1] = '\0';
828 /* write title, location and, optionally, the folder name */
829 fold_len
= (int) utf8_width(fold_tmp
);
832 fold_len
= MAX(MIN(fold_len
, avail
), 0);
835 /* center folder in its space */
851 if(as
.style
!= FileTextPercent
&& as
.style
!= TextPercent
){
855 && as
.stream
->mailbox
856 && mail_valid_net_parse(as
.stream
->mailbox
, &mb
)
857 && (mb
.sslflag
|| mb
.tlsflag
))
862 if(loc1_len
> 0 && cur_mess_col_offset
>= 0)
863 as
.cur_mess_col
= s1
+ver_len
+s2
+title_len
+s3
+fold_len
+s4
+ cur_mess_col_offset
;
865 if(loc1_len
> 0 && page_column_offset
>= 0)
866 as
.page_column
= s1
+ver_len
+s2
+title_len
+s3
+fold_len
+s4
+ page_column_offset
;
869 as
.del_column
= s1
+ver_len
+s2
+title_len
+s3
+fold_len
+s4
+loc1_len
+s5
;
871 if(loc1_len
> 0 && percent_column_offset
> 0)
872 as
.percent_column
= s1
+ver_len
+s2
+title_len
+s3
+fold_len
+s4
+loc1_len
- percent_column_offset
;
875 utf8_snprintf(titlebar_line
, 6*MAX_SCREEN_COLS
, "%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s",
877 ver_len
, ver_len
, version
,
879 title_len
, title_len
, title
,
881 fold_len
, fold_len
, fold_tmp
,
883 loc1_len
, loc1_len
, loc1
,
885 loc2_len
, loc2_len
, loc2
,
888 return(&as
.titlecontainer
);
893 sort_letter(SortOrder sort
)
897 if((p
= sort_name(sort
)) != NULL
&& *p
){
898 while(*(p
+1) && islower((unsigned char) *p
))
910 * Update the titlebar line if the message number changed
912 * Args: None, uses state setup on previous call to set_titlebar.
915 update_titlebar_message(void)
917 long curnum
, maxnum
, oldnum
;
918 PINETHRD_S
*thrd
= NULL
;
919 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
925 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
926 titlebar_is_dirty
= 1;
930 if(as
.style
== ThrdIndex
){
932 oldnum
= as
.current_thrd
;
934 if(as
.stream
&& (rawno
=mn_m2raw(as
.msgmap
, mn_get_cur(as
.msgmap
))))
935 thrd
= fetch_thread(as
.stream
, rawno
);
937 if(thrd
&& thrd
->top
&& (thrd
=fetch_thread(as
.stream
, thrd
->top
)))
938 curnum
= thrd
->thrdno
;
940 else if(as
.cur_mess_col
>= 0){
941 curnum
= mn_get_cur(as
.msgmap
);
942 oldnum
= as
.current_msg
;
945 if(as
.cur_mess_col
>= 0 && oldnum
!= curnum
){
947 if(as
.style
== ThrdIndex
){
948 as
.current_thrd
= curnum
;
949 maxnum
= as
.msgmap
->max_thrdno
;
952 as
.current_msg
= curnum
;
953 maxnum
= mn_get_total(as
.msgmap
);
956 titlecolor
= &as
.titlecontainer
.color
;
959 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
961 num_len
= strlen(comatose(maxnum
));
963 snprintf(buf
, sizeof(buf
), "%*.*s", num_len
, num_len
, comatose(curnum
));
965 PutLine0(0, as
.cur_mess_col
, buf
);
968 (void)pico_set_colorp(lastc
, PSC_NONE
);
969 free_color_pair(&lastc
);
979 * Update titlebar line's message status field ("DEL", "NEW", etc)
981 * Args: None, operates on state set during most recent set_titlebar call
984 update_titlebar_status(void)
988 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
991 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
992 titlebar_is_dirty
= 1;
996 if(!as
.stream
|| as
.current_msg
<= 0L || as
.del_column
< 0)
999 if(as
.current_msg
!= mn_get_cur(as
.msgmap
))
1000 update_titlebar_message();
1002 mc
= ((rawno
= mn_m2raw(as
.msgmap
, as
.current_msg
)) > 0L
1003 && rawno
<= as
.stream
->nmsgs
)
1004 ? mail_elt(as
.stream
, rawno
) : NULL
;
1006 if(!(mc
&& mc
->valid
))
1007 return(0); /* indeterminate */
1009 if(mc
->deleted
){ /* deleted takes precedence */
1010 if(as
.msg_state
& MS_DEL
)
1013 else if(mc
->answered
){ /* then answered */
1014 if(as
.msg_state
& MS_ANS
)
1016 } /* then forwarded */
1017 else if(user_flag_is_set(as
.stream
, mc
->msgno
, FORWARDED_FLAG
)){
1018 if(as
.msg_state
& MS_FWD
)
1021 else if(!mc
->seen
&& as
.stream
1022 && (!IS_NEWS(as
.stream
)
1023 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
))){
1024 if(as
.msg_state
& MS_NEW
) /* then seen */
1028 if(as
.msg_state
== 0) /* nothing to change */
1032 as
.msg_state
= STATUS_BITS(mc
);
1034 titlecolor
= &as
.titlecontainer
.color
;
1037 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
1039 PutLine0(0, as
.del_column
, BAR_STATUS(as
.msg_state
));
1042 (void)pico_set_colorp(lastc
, PSC_NONE
);
1043 free_color_pair(&lastc
);
1052 * Update the percentage shown in the titlebar line
1054 * Args: new_line_number -- line number to calculate new percentage
1057 update_titlebar_percent(long int new_line_number
)
1059 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
1061 if(as
.percent_column
< 0 || new_line_number
== as
.current_line
)
1065 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
1066 titlebar_is_dirty
= 1;
1070 as
.current_line
= new_line_number
;
1072 titlecolor
= &as
.titlecontainer
.color
;
1075 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
1077 PutLine0(0, as
.percent_column
,
1078 percentage(as
.current_line
, as
.total_lines
, 0));
1081 (void)pico_set_colorp(lastc
, PSC_NONE
);
1082 free_color_pair(&lastc
);
1090 * Update the percentage AND line number shown in the titlebar line
1092 * Args: new_line_number -- line number to calculate new percentage
1095 update_titlebar_lpercent(long int new_line_number
)
1097 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
1101 if(as
.page_column
< 0 || new_line_number
== as
.current_line
)
1105 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
1106 titlebar_is_dirty
= 1;
1110 as
.current_line
= new_line_number
;
1112 titlecolor
= &as
.titlecontainer
.color
;
1115 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
1117 num_len
= strlen(comatose(as
.total_lines
));
1118 snprintf(buf
, sizeof(buf
), "%*.*s", num_len
, num_len
, comatose(as
.current_line
));
1120 PutLine0(0, as
.page_column
, buf
);
1122 PutLine0(0, as
.percent_column
,
1123 percentage(as
.current_line
, as
.total_lines
, 0));
1126 (void)pico_set_colorp(lastc
, PSC_NONE
);
1127 free_color_pair(&lastc
);
1134 /*----------------------------------------------------------------------
1135 Return static buf containing portion of lines displayed
1137 Args: part -- how much so far
1138 total -- how many total
1142 percentage(long int part
, long int total
, int suppress_top
)
1144 static char percent
[4];
1146 if(total
== 0L || (total
<= ps_global
->ttyo
->screen_rows
1147 - HEADER_ROWS(ps_global
)
1148 - FOOTER_ROWS(ps_global
)))
1149 strncpy(percent
, "ALL", sizeof(percent
));
1150 else if(!suppress_top
&& part
<= ps_global
->ttyo
->screen_rows
1151 - HEADER_ROWS(ps_global
)
1152 - FOOTER_ROWS(ps_global
))
1153 strncpy(percent
, "TOP", sizeof(percent
));
1154 else if(part
>= total
)
1155 strncpy(percent
, "END", sizeof(percent
));
1157 snprintf(percent
, sizeof(percent
), "%2ld%%", (100L * part
)/total
);
1159 percent
[sizeof(percent
)-1] = '\0';
1166 * end_titlebar - free resources associated with titlebar state struct
1172 fs_give((void **)&as
.folder_name
);
1175 fs_give((void **)&as
.context_name
);
1179 /*----------------------------------------------------------------------
1180 Exported method to display status of mail check
1182 Args: putstr -- should be NO LONGER THAN 2 bytes
1184 Result: putstr displayed at upper-left-hand corner of screen
1187 check_cue_display(char *putstr
)
1189 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
1190 static char check_cue_char
;
1192 if(ps_global
->read_predicted
&&
1193 (check_cue_char
== putstr
[0]
1194 || (putstr
[0] == ' ' && putstr
[1] == '\0')))
1197 if(putstr
[0] == ' ')
1198 check_cue_char
= '\0';
1200 check_cue_char
= putstr
[0];
1203 titlecolor
= &as
.titlecontainer
.color
;
1206 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
1208 PutLine0(0, 0, putstr
); /* show delay cue */
1210 (void)pico_set_colorp(lastc
, PSC_NONE
);
1211 free_color_pair(&lastc
);
1218 /*----------------------------------------------------------------------
1219 Mandatory function of ../pith/newmail.c
1223 Result: newmail cue displayed at upper-left-hand corner of screen
1226 newmail_check_cue(int onoroff
)
1228 if(F_ON(F_SHOW_DELAY_CUE
, ps_global
) && !ps_global
->in_init_seq
){
1229 check_cue_display((onoroff
== TRUE
) ? " *" : " ");
1230 MoveCursor(ps_global
->ttyo
->screen_rows
-FOOTER_ROWS(ps_global
), 0);
1235 mswin_setcursor (MSWIN_CURSOR_BUSY
);
1237 mswin_setcursor (MSWIN_CURSOR_ARROW
);
1242 /*----------------------------------------------------------------------
1243 Mandatory function of ../pith/newmail.c
1247 Result: checkpoint delay cue displayed at upper-left-hand corner of screen
1250 newmail_check_point_cue(int onoroff
)
1252 if(F_ON(F_SHOW_DELAY_CUE
, ps_global
)){
1253 check_cue_display((onoroff
== TRUE
) ? "**" : " ");
1254 MoveCursor(ps_global
->ttyo
->screen_rows
-FOOTER_ROWS(ps_global
), 0);
1259 mswin_setcursor (MSWIN_CURSOR_BUSY
);
1261 mswin_setcursor (MSWIN_CURSOR_ARROW
);