2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
18 #include "../pith/state.h"
19 #include "../pith/bitmap.h"
20 #include "../pith/msgno.h"
21 #include "../pith/thread.h"
22 #include "../pith/conf.h"
23 #include "../pith/init.h"
24 #include "../pith/sort.h"
25 #include "../pith/news.h"
26 #include "../pith/util.h"
27 #include "../pith/folder.h"
32 int digit_count(long);
33 void output_titlebar(TITLE_S
*);
34 char sort_letter(SortOrder
);
35 char *percentage(long, long, int);
39 * some useful macros...
46 #define STATUS_BITS(X) (!(X && (X)->valid) ? 0 \
47 : (X)->deleted ? MS_DEL \
48 : (X)->answered ? MS_ANS \
49 : (as.stream && user_flag_is_set(as.stream, (X)->msgno, FORWARDED_FLAG)) ? MS_FWD \
51 && (ps_global->unseen_in_view \
53 && (!IS_NEWS(as.stream) \
54 || F_ON(F_FAKE_NEW_IN_NEWS, \
58 #define BAR_STATUS(X) (((X) & MS_DEL) ? "DEL" \
59 : ((X) & MS_ANS) ? "ANS" \
60 : ((X) & MS_FWD) ? "FWD" \
62 && (!IS_NEWS(as.stream) \
63 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)) \
64 && ((X) & MS_NEW)) ? "NEW" : " ")
67 static struct titlebar_state
{
84 enum {Normal
, OnlyRead
, Closed
} stream_status
;
86 TITLE_S titlecontainer
;
90 static int titlebar_is_dirty
= 1;
92 char *as_fname
; /* folder name */
93 char *as_cname
; /* context name */
96 push_titlebar_state(void)
100 if(as_fname
) fs_give((void **)&as_fname
);
101 as_fname
= cpystr(as
.folder_name
);
102 if(as_cname
) fs_give((void **)&as_cname
);
103 as_cname
= cpystr(as
.context_name
);
104 if(as
.folder_name
) fs_give((void **)&as
.folder_name
);
105 if(as
.context_name
) fs_give((void **)&as
.context_name
);
110 pop_titlebar_state(void)
112 /* guard against case where push pushed no state */
113 if(titlebar_stack
.style
!= TitleBarNone
){
114 fs_give((void **)&(as
.folder_name
)); /* free malloc'd values */
115 fs_give((void **)&(as
.context_name
));
118 as
.folder_name
= as_fname
? cpystr(as_fname
) : NULL
;
119 as
.context_name
= as_cname
? cpystr(as_cname
): NULL
;
127 digit_count(long int n
)
132 ? (1 + (((i
= digit_count(n
/ 10L)) == 3 || i
== 7) ? i
+1 : i
))
138 mark_titlebar_dirty(void)
140 titlebar_is_dirty
= 1;
144 /*----------------------------------------------------------------------
145 Sets up style and contents of current titlebar line
147 All of the text is assumed to be UTF-8 text, which probably isn't
150 Args: title -- The title that appears in the center of the line
151 This title is in UTF-8 characters.
152 display_on_screen -- flag whether to display on screen or generate
154 style -- The format/style of the titlebar line
155 msgmap -- MSGNO_S * to selected message map
156 current_pl -- The current page or line number
157 total_pl -- The total line or page count
159 Set the contents of the anchor line. It's called an anchor line
160 because it's always present and titlebars the user. This accesses a
161 number of global variables, but doesn't change any. There are several
162 different styles of the titlebar line.
164 It's OK to call this with a bogus current message - it is only used
165 to look up status of current message
167 Formats only change the right section (part3).
168 FolderName: "<folder>" xx Messages
169 MessageNumber: "<folder>" message x,xxx of x,xxx XXX
170 TextPercent: line xxx of xxx xx%
171 MsgTextPercent: "<folder>" message x,xxx of x,xxx xx% XXX
172 FileTextPercent: "<filename>" line xxx of xxx xx%
174 Several strings and column numbers are saved so later updates to the status
175 line for changes in message number or percentage can be done efficiently.
179 set_titlebar(char *title
, MAILSTREAM
*stream
, CONTEXT_S
*cntxt
, char *folder
,
180 MSGNO_S
*msgmap
, int display_on_screen
, TitleBarType style
,
181 long int current_pl
, long int total_pl
, COLOR_PAIR
*color
)
183 struct variable
*vars
= ps_global
->vars
;
184 MESSAGECACHE
*mc
= NULL
;
185 PINETHRD_S
*thrd
= NULL
;
188 dprint((9, "set_titlebar - style: %d current message cnt:%ld",
189 style
, mn_total_cur(msgmap
)));
190 dprint((9, " current_pl: %ld total_pl: %ld\n",
191 current_pl
, total_pl
));
193 as
.current_msg
= (mn_get_total(msgmap
) > 0L)
194 ? MAX(0, mn_get_cur(msgmap
)) : 0L;
199 as
.stream_status
= (!as
.stream
|| (sp_dead_stream(as
.stream
)))
200 ? Closed
: as
.stream
->rdonly
? OnlyRead
: Normal
;
202 if(ps_global
->first_open_was_attempted
203 && as
.stream_status
== Closed
204 && VAR_TITLECLOSED_FORE_COLOR
&& VAR_TITLECLOSED_BACK_COLOR
){
205 memset(&as
.titlecontainer
.color
, 0, sizeof(as
.titlecontainer
.color
));
206 strncpy(as
.titlecontainer
.color
.fg
,
207 VAR_TITLECLOSED_FORE_COLOR
, MAXCOLORLEN
);
208 as
.titlecontainer
.color
.fg
[MAXCOLORLEN
] = '\0';
209 strncpy(as
.titlecontainer
.color
.bg
,
210 VAR_TITLECLOSED_BACK_COLOR
, MAXCOLORLEN
);
211 as
.titlecontainer
.color
.bg
[MAXCOLORLEN
] = '\0';
215 memset(&as
.titlecontainer
.color
, 0, sizeof(as
.titlecontainer
.color
));
217 strncpy(as
.titlecontainer
.color
.fg
, color
->fg
, MAXCOLORLEN
);
218 as
.titlecontainer
.color
.fg
[MAXCOLORLEN
] = '\0';
222 strncpy(as
.titlecontainer
.color
.bg
, color
->bg
, MAXCOLORLEN
);
223 as
.titlecontainer
.color
.bg
[MAXCOLORLEN
] = '\0';
227 memset(&as
.titlecontainer
.color
, 0, sizeof(as
.titlecontainer
.color
));
228 if(VAR_TITLE_FORE_COLOR
){
229 strncpy(as
.titlecontainer
.color
.fg
,
230 VAR_TITLE_FORE_COLOR
, MAXCOLORLEN
);
231 as
.titlecontainer
.color
.fg
[MAXCOLORLEN
] = '\0';
234 if(VAR_TITLE_BACK_COLOR
){
235 strncpy(as
.titlecontainer
.color
.bg
,
236 VAR_TITLE_BACK_COLOR
, MAXCOLORLEN
);
237 as
.titlecontainer
.color
.bg
[MAXCOLORLEN
] = '\0';
243 fs_give((void **)&as
.folder_name
);
246 unsigned char *fname
;
247 fname
= folder_name_decoded((unsigned char *) folder
);
248 if(!strucmp(folder
, ps_global
->inbox_name
) && cntxt
== ps_global
->context_list
)
249 as
.folder_name
= cpystr(pretty_fn(fname
? (char *) fname
: folder
));
251 as
.folder_name
= cpystr(fname
? (char *) fname
: folder
);
252 if(fname
) fs_give((void **)&fname
);
256 as
.folder_name
= cpystr("");
259 fs_give((void **)&as
.context_name
);
262 * Handle setting up the context if appropriate.
264 if(cntxt
&& context_isambig(folder
) && ps_global
->context_list
->next
265 && (strucmp(as
.folder_name
, ps_global
->inbox_name
) || cntxt
!= ps_global
->context_list
)){
267 * if there's more than one context and the current folder
268 * is in it (ambiguous name), set the context name...
270 as
.context_name
= cpystr(cntxt
->nickname
276 as
.context_name
= cpystr("");
278 if(as
.stream
&& style
!= FolderName
279 && style
!= ThrdIndex
&& as
.current_msg
> 0L) {
282 if((rawno
= mn_m2raw(msgmap
, as
.current_msg
)) > 0L
283 && rawno
<= as
.stream
->nmsgs
284 && !((mc
= mail_elt(as
.stream
, rawno
)) && mc
->valid
)){
285 pine_mail_fetch_flags(as
.stream
, long2string(rawno
), NIL
);
286 if(rawno
<= as
.stream
->nmsgs
&& as
.stream
&& rawno
> 0L)
287 mc
= mail_elt(as
.stream
, rawno
);
295 if(style
== ThrdIndex
|| style
== ThrdMsgNum
|| style
== ThrdMsgPercent
){
296 if(mn_get_total(msgmap
) > 0L)
297 thrd
= fetch_thread(stream
, mn_m2raw(msgmap
, mn_get_cur(msgmap
)));
299 if(thrd
&& thrd
->top
&& thrd
->top
!= thrd
->rawno
)
300 thrd
= fetch_thread(stream
, thrd
->top
);
303 as
.current_thrd
= thrd
->thrdno
;
308 as
.total_lines
= msgmap
->max_thrdno
;
313 case FileTextPercent
:
315 as
.total_lines
= total_pl
;
316 as
.current_line
= current_pl
;
317 /* fall through to set state */
320 as
.msg_state
= STATUS_BITS(mc
);
322 case FolderName
: /* nothing more to do for this one */
328 tc
= format_titlebar();
329 if(display_on_screen
)
332 return(tc
->titlebar_line
);
337 redraw_titlebar(void)
339 output_titlebar(format_titlebar());
344 output_titlebar(TITLE_S
*tc
)
346 COLOR_PAIR
*lastc
= NULL
, *newcolor
;
349 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
350 titlebar_is_dirty
= 1;
354 newcolor
= tc
? &tc
->color
: NULL
;
357 lastc
= pico_set_colorp(newcolor
, PSC_REV
| PSC_RET
);
359 if(tc
&& tc
->titlebar_line
)
360 PutLine0(0, 0, tc
->titlebar_line
);
363 MoveCursor(0, ps_global
->ttyo
->screen_cols
); /* move to the last column */
366 (void)pico_set_colorp(lastc
, PSC_NONE
);
367 free_color_pair(&lastc
);
375 titlebar_stream_closing(MAILSTREAM
*stream
)
377 if(stream
== as
.stream
)
382 /* caller should free returned color pair */
384 current_titlebar_color(void)
387 COLOR_PAIR
*the_color
= NULL
;
389 col
= as
.title
? &as
.titlecontainer
.color
: NULL
;
391 if(col
&& col
->fg
&& col
->fg
[0] && col
->bg
&& col
->bg
[0])
392 the_color
= new_color_pair(col
->fg
, col
->bg
);
398 /*----------------------------------------------------------------------
399 Redraw or draw the top line, the title bar
401 The titlebar has Four fields:
402 1) "Version" of fixed length and is always positioned two spaces
403 in from left display edge.
404 2) "Location" which is fixed for each style of titlebar and
405 is positioned two spaces from the right display edge
406 3) "Title" which is of fixed length, and is centered if
408 4) "Folder" whose existence depends on style and which can
409 have it's length adjusted (within limits) so it will
410 equally share the space between 1) and 2) with the
411 "Title". The rule for existence is that in the
412 space between 1) and 2) there must be one space between
413 3) and 4) AND at least 50% of 4) must be displayed.
414 However, if the folder name can be displayed, then do
415 so, and display as much as possible of the collection name.
417 Returns - Formatted title bar
420 format_titlebar(void)
422 char version
[50], fold_tmp
[6*MAXPATH
+1], *titlebar_line
,
423 loc1
[200], loc_label
[10], *thd_label
, *ss_string
, *q
,
424 *plus
, *loc2
= "", title
[200];
425 int title_len
= 0, ver_len
, loc1_len
= 0, loc2_len
= 0, fold_len
= 0, num_len
,
426 s1
= 0, s2
= 0, s3
= 0, s4
= 0, s5
= 0, s6
= 0, tryloc
= 1,
427 cur_mess_col_offset
= -1, percent_column_offset
= -1, page_column_offset
= -1,
428 ss_len
, thd_len
, is_context
, avail
, extra
;
430 titlebar_is_dirty
= 0;
436 as
.folder_name
= cpystr("");
439 as
.context_name
= cpystr("");
442 * Full blown title looks like:
444 * | LV vers VT title TF folder FL location LR |
446 #define LV 2 /* space between Left edge and Version, must be >= 2 */
447 #define VT 3 /* space between Version and Title */
448 #define TF 1 /* space between Title and Folder */
449 #define FL 2 /* space between Folder and Location */
450 #define LR 2 /* space between Location and Right edge, >= 2 */
451 /* half of n but round up */
452 #define HRU(n) (((n) <= 0) ? 0 : (((n)%2) ? ((n)+1)/2 : (n)/2))
453 /* half of n but round down */
454 #define HRD(n) (((n) <= 0) ? 0 : ((n)/2))
456 titlebar_line
= as
.titlecontainer
.titlebar_line
;
458 avail
= MIN(ps_global
->ttyo
->screen_cols
, MAX_SCREEN_COLS
);
462 as
.cur_mess_col
= -1;
463 as
.percent_column
= -1;
466 is_context
= as
.context_name
? strlen(as
.context_name
) : 0;
468 snprintf(version
, sizeof(version
), "ALPINE %s", ALPINE_VERSION
);
469 version
[sizeof(version
)-1] = '\0';
470 ver_len
= (int) utf8_width(version
); /* fixed version field width */
474 strncpy(title
, as
.title
, sizeof(title
));
475 title
[sizeof(title
)-1] = '\0';
478 /* Add Sort indicator to title */
479 if(F_ON(F_SHOW_SORT
, ps_global
) &&
480 !(as
.style
== FileTextPercent
|| as
.style
== TextPercent
)){
481 SortOrder current_sort
;
485 current_sort
= mn_get_sort(ps_global
->msgmap
);
486 current_rev
= mn_get_revsort(ps_global
->msgmap
);
488 /* turn current_sort into a letter */
489 let
= sort_letter(current_sort
);
490 if(let
== 'A' && current_rev
){
495 snprintf(title
+strlen(title
), sizeof(title
)-strlen(title
),
496 " [%s%c]", current_rev
? "R" : "", let
);
497 title
[sizeof(title
)-1] = '\0';
500 title_len
= (int) utf8_width(title
); /* title field width */
502 s1
= MAX(MIN(LV
, avail
), 0); /* left two spaces */
505 s6
= MAX(MIN(LR
, avail
), 0); /* right two spaces */
508 title_len
= MAX(MIN(title_len
, avail
), 0);
511 if(ver_len
+ VT
> avail
){ /* can only fit title */
514 s2
= MAX(MIN(HRD(avail
), avail
), 0);
520 s2
= MAX(MIN(VT
, avail
), 0);
523 ver_len
= MAX(MIN(ver_len
, avail
), 0);
529 * set location field's length and value based on requested style
531 if(as
.style
== ThrdIndex
)
532 /* TRANSLATORS: In titlebar, Thd is an abbreviation for Thread, Msg for Message.
533 They are used when there isn't enough space so need to be short.
534 The formatting isn't very flexible. These come before the number
535 of the message or thread, as in
537 when reading message number 17. */
538 snprintf(loc_label
, sizeof(loc_label
), "%s ", (is_context
|| tryloc
==2) ? _("Thd") : _("Thread"));
540 snprintf(loc_label
, sizeof(loc_label
), "%s ", (is_context
|| tryloc
==2) ? _("Msg") : _("Message"));
542 if(tryloc
== 3 && as
.style
!= FolderName
&& mn_get_total(as
.msgmap
))
545 loc_label
[sizeof(loc_label
)-1] = '\0';
547 if(as
.style
== ThrdMsgNum
|| as
.style
== ThrdMsgPercent
){
548 thd_label
= is_context
? _("Thd") : _("Thread");
549 thd_len
= (int) utf8_width(thd_label
);
552 loc1_len
= (int) utf8_width(loc_label
); /* initial length */
554 if(!mn_get_total(as
.msgmap
)){
555 loc_label
[strlen(loc_label
)-1] = 's';
556 snprintf(loc1
, sizeof(loc1
), "%s %s", _("No"), loc_label
);
557 loc1
[sizeof(loc1
)-1]= '\0';
561 case FolderName
: /* "x,xxx <loc_label>s" */
563 if(mn_get_total(as
.msgmap
) != 1)
564 loc_label
[strlen(loc_label
)-1] = 's';
566 loc_label
[strlen(loc_label
)-1] = '\0';
569 snprintf(loc1
, sizeof(loc1
), "%s %s", comatose(mn_get_total(as
.msgmap
)), loc_label
);
570 loc1
[sizeof(loc1
)-1]= '\0';
573 case MessageNumber
: /* "<loc_label> xxx of xxx DEL" */
574 num_len
= strlen(comatose(mn_get_total(as
.msgmap
)));
575 cur_mess_col_offset
= loc1_len
;
576 snprintf(loc1
, sizeof(loc1
), "%s%*.*s of %s", loc_label
,
578 comatose(as
.current_msg
),
579 comatose(mn_get_total(as
.msgmap
)));
580 loc1
[sizeof(loc1
)-1]= '\0';
581 loc2
= BAR_STATUS(as
.msg_state
);
585 case ThrdIndex
: /* "<loc_label> xxx of xxx" */
586 num_len
= strlen(comatose(as
.total_lines
));
587 cur_mess_col_offset
= loc1_len
;
588 snprintf(loc1
, sizeof(loc1
), "%s%*.*s of %s", loc_label
,
590 comatose(as
.current_thrd
),
591 comatose(as
.total_lines
));
592 loc1
[sizeof(loc1
)-1]= '\0';
595 case ThrdMsgNum
: /* "<loc_label> xxx in Thd xxx DEL" */
596 num_len
= strlen(comatose(mn_get_total(as
.msgmap
)));
597 cur_mess_col_offset
= loc1_len
;
598 snprintf(loc1
, sizeof(loc1
), "%s%*.*s in %s %s", loc_label
,
600 comatose(as
.current_msg
),
602 comatose(as
.current_thrd
));
603 loc1
[sizeof(loc1
)-1]= '\0';
604 loc2
= BAR_STATUS(as
.msg_state
);
608 case MsgTextPercent
: /* "<loc_label> xxx of xxx xx% DEL" */
609 num_len
= strlen(comatose(mn_get_total(as
.msgmap
)));
610 cur_mess_col_offset
= loc1_len
;
611 percent_column_offset
= 3;
612 snprintf(loc1
, sizeof(loc1
), "%s%*.*s of %s %s", loc_label
,
614 comatose(as
.current_msg
),
615 comatose(mn_get_total(as
.msgmap
)),
616 percentage(as
.current_line
, as
.total_lines
, 1));
617 loc1
[sizeof(loc1
)-1]= '\0';
618 loc2
= BAR_STATUS(as
.msg_state
);
622 case ThrdMsgPercent
: /* "<loc_label> xxx in Thd xxx xx% DEL" */
623 num_len
= strlen(comatose(mn_get_total(as
.msgmap
)));
624 cur_mess_col_offset
= loc1_len
;
625 percent_column_offset
= 3;
626 snprintf(loc1
, sizeof(loc1
), "%s%*.*s in %s %s %s", loc_label
,
628 comatose(as
.current_msg
),
630 comatose(as
.current_thrd
),
631 percentage(as
.current_line
, as
.total_lines
, 1));
632 loc1
[sizeof(loc1
)-1]= '\0';
633 loc2
= BAR_STATUS(as
.msg_state
);
638 /* NOTE: no fold_tmp setup below for TextPercent style */
639 case FileTextPercent
: /* "Line xxx of xxx xx%" */
640 num_len
= strlen(comatose(as
.total_lines
));
641 page_column_offset
= 5;
642 percent_column_offset
= 3;
643 snprintf(loc1
, sizeof(loc1
), "Line %*.*s of %s %s",
645 comatose(as
.current_line
),
646 comatose(as
.total_lines
),
647 percentage(as
.current_line
, as
.total_lines
, 1));
648 loc1
[sizeof(loc1
)-1]= '\0';
655 loc1_len
= utf8_width(loc1
);
657 if(loc1_len
+ loc2_len
+ ((loc2_len
> 0) ? 1 : 0) >= avail
){ /* can't fit location in */
660 goto try_smaller_loc
;
663 loc1_len
= loc2_len
= 0;
665 avail
+= s2
; /* re-allocate s2 to center title */
667 s2
= MAX(MIN(HRD(avail
), avail
), 0);
673 loc1_len
= MAX(MIN(loc1_len
, avail
), 0);
676 loc2_len
= MAX(MIN(loc2_len
, avail
), 0);
680 s5
= MAX(MIN(1, avail
), 0);
686 s3
= MAX(MIN(TF
, avail
), 0);
689 s4
= MAX(MIN(FL
, avail
), 0);
693 /* TRANSLATORS: it might say READONLY or CLOSED in the titlebar, referring to
694 the current folder. */
695 ss_string
= as
.stream_status
== Closed
? _("(CLOSED)") :
696 (as
.stream_status
== OnlyRead
697 && !IS_NEWS(as
.stream
))
698 ? _("(READONLY)") : "";
699 ss_len
= (int) utf8_width(ss_string
);
701 /* figure folder_length and what's to be displayed */
703 if(as
.style
== FileTextPercent
|| as
.style
== TextPercent
){
704 if(as
.style
== FileTextPercent
){
705 extra
= (int) utf8_width("File: ");
706 fold_len
= (int) utf8_width(as
.folder_name
);
707 if(fold_len
+ extra
<= avail
){ /* all of folder fit? */
708 strncpy(fold_tmp
, "File: ", sizeof(fold_tmp
));
709 q
= fold_tmp
+ strlen(fold_tmp
);
710 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
712 else if(HRU(fold_len
) + extra
+3 <= avail
){
714 * fold_tmp = ...partial_folder_name
716 strncpy(fold_tmp
, "File: ...", sizeof(fold_tmp
));
717 q
= fold_tmp
+ strlen(fold_tmp
);
718 (void) utf8_to_width_rhs(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
-(extra
+3));
721 /* else leave folder/file name blank */
726 fold_len
= (int) utf8_width(as
.folder_name
);
729 && as
.stream_status
!= Closed
730 && (ct_len
= (int) utf8_width(as
.context_name
))){
732 extra
= 3; /* length from "<" ">" and SPACE */
734 if(ct_len
+ fold_len
+ ss_len
+ extra
<= avail
){
737 strncpy(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
741 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
743 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
745 else if(ct_len
+ fold_len
+ ss_len
+ extra
<= avail
){
748 strncpy(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
752 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
754 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
756 else if(HRU(ct_len
) + fold_len
+ ss_len
+ extra
<= avail
){
759 q
+= utf8_pad_to_width(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
-(fold_len
+ss_len
+extra
), 1);
762 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
764 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
766 else if(HRU(ct_len
) + HRU(fold_len
) + ss_len
+ extra
<= avail
){
769 q
+= utf8_pad_to_width(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), HRU(ct_len
), 1);
772 q
+= utf8_to_width_rhs(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
-(HRU(ct_len
)+ss_len
+extra
));
773 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
775 else if(ss_len
> 0 && ss_len
<= avail
){
777 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
778 } else if(fold_len
< avail
){
780 if(fold_len
+ 7 < avail
){
782 q
+= utf8_pad_to_width(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
- fold_len
- 3, 1);
786 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
788 /* else leave it out */
791 /* TRANSLATORS: the name of the open folder follows this in the titlebar */
792 extra
= strlen(_("Folder: "));
793 if(fold_len
+ ss_len
+ extra
<= avail
){
795 strncpy(q
, "Folder: ", sizeof(fold_tmp
)-(q
-fold_tmp
));
797 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
799 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
802 if(fold_len
+ ss_len
<= avail
){
804 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
806 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
808 else if(((HRU(fold_len
) + ss_len
<= avail
)
809 || (avail
> ss_len
+3 && avail
> 10)) && fold_len
> 5){
811 strncpy(q
, "...", sizeof(fold_tmp
));
813 q
+= utf8_to_width_rhs(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
-(3+ss_len
));
814 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
816 else if(ss_len
> 0 && ss_len
<= avail
){
818 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
820 /* else leave it out */
825 fold_tmp
[sizeof(fold_tmp
)-1] = '\0';
827 /* write title, location and, optionally, the folder name */
828 fold_len
= (int) utf8_width(fold_tmp
);
831 fold_len
= MAX(MIN(fold_len
, avail
), 0);
834 /* center folder in its space */
850 if(as
.style
!= FileTextPercent
&& as
.style
!= TextPercent
){
854 && as
.stream
->mailbox
855 && mail_valid_net_parse(as
.stream
->mailbox
, &mb
)
856 && (mb
.sslflag
|| mb
.tlsflag
))
861 if(loc1_len
> 0 && cur_mess_col_offset
>= 0)
862 as
.cur_mess_col
= s1
+ver_len
+s2
+title_len
+s3
+fold_len
+s4
+ cur_mess_col_offset
;
864 if(loc1_len
> 0 && page_column_offset
>= 0)
865 as
.page_column
= s1
+ver_len
+s2
+title_len
+s3
+fold_len
+s4
+ page_column_offset
;
868 as
.del_column
= s1
+ver_len
+s2
+title_len
+s3
+fold_len
+s4
+loc1_len
+s5
;
870 if(loc1_len
> 0 && percent_column_offset
> 0)
871 as
.percent_column
= s1
+ver_len
+s2
+title_len
+s3
+fold_len
+s4
+loc1_len
- percent_column_offset
;
874 utf8_snprintf(titlebar_line
, 6*MAX_SCREEN_COLS
, "%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s",
876 ver_len
, ver_len
, version
,
878 title_len
, title_len
, title
,
880 fold_len
, fold_len
, fold_tmp
,
882 loc1_len
, loc1_len
, loc1
,
884 loc2_len
, loc2_len
, loc2
,
887 return(&as
.titlecontainer
);
892 sort_letter(SortOrder sort
)
896 if((p
= sort_name(sort
)) != NULL
&& *p
){
897 while(*(p
+1) && islower((unsigned char) *p
))
909 * Update the titlebar line if the message number changed
911 * Args: None, uses state setup on previous call to set_titlebar.
914 update_titlebar_message(void)
916 long curnum
= 0, maxnum
, oldnum
= 0;
917 PINETHRD_S
*thrd
= NULL
;
918 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
924 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
925 titlebar_is_dirty
= 1;
929 if(as
.style
== ThrdIndex
){
931 oldnum
= as
.current_thrd
;
933 if(as
.stream
&& (rawno
=mn_m2raw(as
.msgmap
, mn_get_cur(as
.msgmap
))))
934 thrd
= fetch_thread(as
.stream
, rawno
);
936 if(thrd
&& thrd
->top
&& (thrd
=fetch_thread(as
.stream
, thrd
->top
)))
937 curnum
= thrd
->thrdno
;
939 else if(as
.cur_mess_col
>= 0){
940 curnum
= mn_get_cur(as
.msgmap
);
941 oldnum
= as
.current_msg
;
944 if(as
.cur_mess_col
>= 0 && oldnum
!= curnum
){
946 if(as
.style
== ThrdIndex
){
947 as
.current_thrd
= curnum
;
948 maxnum
= as
.msgmap
->max_thrdno
;
951 as
.current_msg
= curnum
;
952 maxnum
= mn_get_total(as
.msgmap
);
955 titlecolor
= &as
.titlecontainer
.color
;
958 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
960 num_len
= strlen(comatose(maxnum
));
962 snprintf(buf
, sizeof(buf
), "%*.*s", num_len
, num_len
, comatose(curnum
));
964 PutLine0(0, as
.cur_mess_col
, buf
);
967 MoveCursor(0, ps_global
->ttyo
->screen_cols
); /* move to the last column */
970 (void)pico_set_colorp(lastc
, PSC_NONE
);
971 free_color_pair(&lastc
);
981 * Update titlebar line's message status field ("DEL", "NEW", etc)
983 * Args: None, operates on state set during most recent set_titlebar call
986 update_titlebar_status(void)
990 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
993 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
994 titlebar_is_dirty
= 1;
998 if(!as
.stream
|| as
.current_msg
<= 0L || as
.del_column
< 0)
1001 if(as
.current_msg
!= mn_get_cur(as
.msgmap
))
1002 update_titlebar_message();
1004 mc
= ((rawno
= mn_m2raw(as
.msgmap
, as
.current_msg
)) > 0L
1005 && rawno
<= as
.stream
->nmsgs
)
1006 ? mail_elt(as
.stream
, rawno
) : NULL
;
1008 if(!(mc
&& mc
->valid
))
1009 return(0); /* indeterminate */
1011 if(mc
->deleted
){ /* deleted takes precedence */
1012 if(as
.msg_state
& MS_DEL
)
1015 else if(mc
->answered
){ /* then answered */
1016 if(as
.msg_state
& MS_ANS
)
1018 } /* then forwarded */
1019 else if(user_flag_is_set(as
.stream
, mc
->msgno
, FORWARDED_FLAG
)){
1020 if(as
.msg_state
& MS_FWD
)
1023 else if(!mc
->seen
&& as
.stream
1024 && (!IS_NEWS(as
.stream
)
1025 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
))){
1026 if(as
.msg_state
& MS_NEW
) /* then seen */
1030 if(as
.msg_state
== 0) /* nothing to change */
1034 as
.msg_state
= STATUS_BITS(mc
);
1036 titlecolor
= &as
.titlecontainer
.color
;
1039 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
1041 PutLine0(0, as
.del_column
, BAR_STATUS(as
.msg_state
));
1043 if (ps_global
->ttyo
)
1044 MoveCursor(0, ps_global
->ttyo
->screen_cols
); /* move to the last column */
1047 (void)pico_set_colorp(lastc
, PSC_NONE
);
1048 free_color_pair(&lastc
);
1057 * Update the percentage shown in the titlebar line
1059 * Args: new_line_number -- line number to calculate new percentage
1062 update_titlebar_percent(long int new_line_number
)
1064 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
1066 if(as
.percent_column
< 0 || new_line_number
== as
.current_line
)
1070 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
1071 titlebar_is_dirty
= 1;
1075 as
.current_line
= new_line_number
;
1077 titlecolor
= &as
.titlecontainer
.color
;
1080 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
1082 PutLine0(0, as
.percent_column
,
1083 percentage(as
.current_line
, as
.total_lines
, 0));
1085 if (ps_global
->ttyo
)
1086 MoveCursor(0, ps_global
->ttyo
->screen_cols
); /* move to the last column */
1089 (void)pico_set_colorp(lastc
, PSC_NONE
);
1090 free_color_pair(&lastc
);
1098 * Update the percentage AND line number shown in the titlebar line
1100 * Args: new_line_number -- line number to calculate new percentage
1103 update_titlebar_lpercent(long int new_line_number
)
1105 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
1109 if(as
.page_column
< 0 || new_line_number
== as
.current_line
)
1113 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
1114 titlebar_is_dirty
= 1;
1118 as
.current_line
= new_line_number
;
1120 titlecolor
= &as
.titlecontainer
.color
;
1123 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
1125 num_len
= strlen(comatose(as
.total_lines
));
1126 snprintf(buf
, sizeof(buf
), "%*.*s", num_len
, num_len
, comatose(as
.current_line
));
1128 PutLine0(0, as
.page_column
, buf
);
1130 PutLine0(0, as
.percent_column
,
1131 percentage(as
.current_line
, as
.total_lines
, 0));
1133 if (ps_global
->ttyo
)
1134 MoveCursor(0, ps_global
->ttyo
->screen_cols
); /* move to the last column */
1137 (void)pico_set_colorp(lastc
, PSC_NONE
);
1138 free_color_pair(&lastc
);
1145 /*----------------------------------------------------------------------
1146 Return static buf containing portion of lines displayed
1148 Args: part -- how much so far
1149 total -- how many total
1153 percentage(long int part
, long int total
, int suppress_top
)
1155 static char percent
[4];
1157 if(total
== 0L || (total
<= ps_global
->ttyo
->screen_rows
1158 - HEADER_ROWS(ps_global
)
1159 - FOOTER_ROWS(ps_global
)))
1160 strncpy(percent
, "ALL", sizeof(percent
));
1161 else if(!suppress_top
&& part
<= ps_global
->ttyo
->screen_rows
1162 - HEADER_ROWS(ps_global
)
1163 - FOOTER_ROWS(ps_global
))
1164 strncpy(percent
, "TOP", sizeof(percent
));
1165 else if(part
>= total
)
1166 strncpy(percent
, "END", sizeof(percent
));
1168 snprintf(percent
, sizeof(percent
), "%2ld%%", (100L * part
)/total
);
1170 percent
[sizeof(percent
)-1] = '\0';
1177 * end_titlebar - free resources associated with titlebar state struct
1183 fs_give((void **)&as
.folder_name
);
1186 fs_give((void **)&as
.context_name
);
1190 /*----------------------------------------------------------------------
1191 Exported method to display status of mail check
1193 Args: putstr -- should be NO LONGER THAN 2 bytes
1195 Result: putstr displayed at upper-left-hand corner of screen
1198 check_cue_display(char *putstr
)
1200 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
1201 static char check_cue_char
;
1203 if(ps_global
->read_predicted
&&
1204 (check_cue_char
== putstr
[0]
1205 || (putstr
[0] == ' ' && putstr
[1] == '\0')))
1208 if(putstr
[0] == ' ')
1209 check_cue_char
= '\0';
1211 check_cue_char
= putstr
[0];
1214 titlecolor
= &as
.titlecontainer
.color
;
1217 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
1219 PutLine0(0, 0, putstr
); /* show delay cue */
1221 if (ps_global
->ttyo
)
1222 MoveCursor(0, ps_global
->ttyo
->screen_cols
); /* move to the last column */
1225 (void)pico_set_colorp(lastc
, PSC_NONE
);
1226 free_color_pair(&lastc
);
1233 /*----------------------------------------------------------------------
1234 Mandatory function of ../pith/newmail.c
1238 Result: newmail cue displayed at upper-left-hand corner of screen
1241 newmail_check_cue(int onoroff
)
1243 if(F_ON(F_SHOW_DELAY_CUE
, ps_global
) && !ps_global
->in_init_seq
){
1244 check_cue_display((onoroff
== TRUE
) ? " *" : " ");
1245 MoveCursor(ps_global
->ttyo
->screen_rows
-FOOTER_ROWS(ps_global
), 0);
1250 mswin_setcursor (MSWIN_CURSOR_BUSY
);
1252 mswin_setcursor (MSWIN_CURSOR_ARROW
);
1257 /*----------------------------------------------------------------------
1258 Mandatory function of ../pith/newmail.c
1262 Result: checkpoint delay cue displayed at upper-left-hand corner of screen
1265 newmail_check_point_cue(int onoroff
)
1267 if(F_ON(F_SHOW_DELAY_CUE
, ps_global
)){
1268 check_cue_display((onoroff
== TRUE
) ? "**" : " ");
1269 MoveCursor(ps_global
->ttyo
->screen_rows
-FOOTER_ROWS(ps_global
), 0);
1274 mswin_setcursor (MSWIN_CURSOR_BUSY
);
1276 mswin_setcursor (MSWIN_CURSOR_ARROW
);
1281 free_titlebar_globals(void)
1283 if(as_fname
) fs_give((void **) &as_fname
);
1284 if(as_cname
) fs_give((void **) &as_cname
);
1285 if(as
.folder_name
) fs_give((void **)&as
.folder_name
);
1286 if(as
.context_name
) fs_give((void **)&as
.context_name
);