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 2006-2008 University of Washington
8 * Copyright 2013-2014 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 * ========================================================================
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
{
87 enum {Normal
, OnlyRead
, Closed
} stream_status
;
89 TITLE_S titlecontainer
;
93 static int titlebar_is_dirty
= 1;
97 push_titlebar_state(void)
100 as
.folder_name
= NULL
; /* erase knowledge of malloc'd data */
101 as
.context_name
= NULL
;
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
));
118 digit_count(long int n
)
123 ? (1 + (((i
= digit_count(n
/ 10L)) == 3 || i
== 7) ? i
+1 : i
))
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
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
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.
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
;
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;
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';
206 memset(&as
.titlecontainer
.color
, 0, sizeof(as
.titlecontainer
.color
));
208 strncpy(as
.titlecontainer
.color
.fg
, color
->fg
, MAXCOLORLEN
);
209 as
.titlecontainer
.color
.fg
[MAXCOLORLEN
] = '\0';
213 strncpy(as
.titlecontainer
.color
.bg
, color
->bg
, MAXCOLORLEN
);
214 as
.titlecontainer
.color
.bg
[MAXCOLORLEN
] = '\0';
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';
234 fs_give((void **)&as
.folder_name
);
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
));
242 as
.folder_name
= cpystr(fname
? (char *) fname
: folder
);
243 if(fname
) fs_give((void **)&fname
);
247 as
.folder_name
= cpystr("");
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
267 as
.context_name
= cpystr("");
269 if(as
.stream
&& style
!= FolderName
270 && style
!= ThrdIndex
&& as
.current_msg
> 0L) {
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
);
289 as
.current_thrd
= thrd
->thrdno
;
294 as
.total_lines
= msgmap
->max_thrdno
;
299 case FileTextPercent
:
301 as
.total_lines
= total_pl
;
302 as
.current_line
= current_pl
;
303 /* fall through to set state */
306 as
.msg_state
= STATUS_BITS(mc
);
308 case FolderName
: /* nothing more to do for this one */
314 tc
= format_titlebar();
315 if(display_on_screen
)
318 return(tc
->titlebar_line
);
323 redraw_titlebar(void)
325 output_titlebar(format_titlebar());
330 output_titlebar(TITLE_S
*tc
)
332 COLOR_PAIR
*lastc
= NULL
, *newcolor
;
335 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
336 titlebar_is_dirty
= 1;
340 newcolor
= tc
? &tc
->color
: NULL
;
343 lastc
= pico_set_colorp(newcolor
, PSC_REV
| PSC_RET
);
345 if(tc
&& tc
->titlebar_line
)
346 PutLine0(0, 0, tc
->titlebar_line
);
349 (void)pico_set_colorp(lastc
, PSC_NONE
);
350 free_color_pair(&lastc
);
358 titlebar_stream_closing(MAILSTREAM
*stream
)
360 if(stream
== as
.stream
)
365 /* caller should free returned color pair */
367 current_titlebar_color(void)
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
);
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
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.
400 Returns - Formatted title bar
403 format_titlebar(void)
405 char version
[50], fold_tmp
[6*MAXPATH
+1], *titlebar_line
,
406 loc1
[200], loc_label
[10], *thd_label
, *ss_string
, *q
,
407 *plus
, *loc2
= "", title
[200];
408 int title_len
= 0, ver_len
, loc1_len
= 0, loc2_len
= 0, fold_len
= 0, num_len
,
409 s1
= 0, s2
= 0, s3
= 0, s4
= 0, s5
= 0, s6
= 0, tryloc
= 1,
410 cur_mess_col_offset
= -1, percent_column_offset
= -1, page_column_offset
= -1,
411 ss_len
, thd_len
, is_context
, avail
, extra
;
413 titlebar_is_dirty
= 0;
419 as
.folder_name
= cpystr("");
422 as
.context_name
= cpystr("");
425 * Full blown title looks like:
427 * | LV vers VT title TF folder FL location LR |
429 #define LV 2 /* space between Left edge and Version, must be >= 2 */
430 #define VT 3 /* space between Version and Title */
431 #define TF 1 /* space between Title and Folder */
432 #define FL 2 /* space between Folder and Location */
433 #define LR 2 /* space between Location and Right edge, >= 2 */
434 /* half of n but round up */
435 #define HRU(n) (((n) <= 0) ? 0 : (((n)%2) ? ((n)+1)/2 : (n)/2))
436 /* half of n but round down */
437 #define HRD(n) (((n) <= 0) ? 0 : ((n)/2))
439 titlebar_line
= as
.titlecontainer
.titlebar_line
;
441 avail
= MIN(ps_global
->ttyo
->screen_cols
, MAX_SCREEN_COLS
);
445 as
.cur_mess_col
= -1;
446 as
.percent_column
= -1;
449 is_context
= as
.context_name
? strlen(as
.context_name
) : 0;
451 snprintf(version
, sizeof(version
), "ALPINE %s", ALPINE_VERSION
);
452 version
[sizeof(version
)-1] = '\0';
453 ver_len
= (int) utf8_width(version
); /* fixed version field width */
457 strncpy(title
, as
.title
, sizeof(title
));
458 title
[sizeof(title
)-1] = '\0';
461 /* Add Sort indicator to title */
462 if(F_ON(F_SHOW_SORT
, ps_global
) &&
463 !(as
.style
== FileTextPercent
|| as
.style
== TextPercent
)){
464 SortOrder current_sort
;
468 current_sort
= mn_get_sort(ps_global
->msgmap
);
469 current_rev
= mn_get_revsort(ps_global
->msgmap
);
471 /* turn current_sort into a letter */
472 let
= sort_letter(current_sort
);
473 if(let
== 'A' && current_rev
){
478 snprintf(title
+strlen(title
), sizeof(title
)-strlen(title
),
479 " [%s%c]", current_rev
? "R" : "", let
);
480 title
[sizeof(title
)-1] = '\0';
483 title_len
= (int) utf8_width(title
); /* title field width */
485 s1
= MAX(MIN(LV
, avail
), 0); /* left two spaces */
488 s6
= MAX(MIN(LR
, avail
), 0); /* right two spaces */
491 title_len
= MAX(MIN(title_len
, avail
), 0);
494 if(ver_len
+ VT
> avail
){ /* can only fit title */
497 s2
= MAX(MIN(HRD(avail
), avail
), 0);
503 s2
= MAX(MIN(VT
, avail
), 0);
506 ver_len
= MAX(MIN(ver_len
, avail
), 0);
512 * set location field's length and value based on requested style
514 if(as
.style
== ThrdIndex
)
515 /* TRANSLATORS: In titlebar, Thd is an abbreviation for Thread, Msg for Message.
516 They are used when there isn't enough space so need to be short.
517 The formatting isn't very flexible. These come before the number
518 of the message or thread, as in
520 when reading message number 17. */
521 snprintf(loc_label
, sizeof(loc_label
), "%s ", (is_context
|| tryloc
==2) ? _("Thd") : _("Thread"));
523 snprintf(loc_label
, sizeof(loc_label
), "%s ", (is_context
|| tryloc
==2) ? _("Msg") : _("Message"));
525 if(tryloc
== 3 && as
.style
!= FolderName
&& mn_get_total(as
.msgmap
))
528 loc_label
[sizeof(loc_label
)-1] = '\0';
530 if(as
.style
== ThrdMsgNum
|| as
.style
== ThrdMsgPercent
){
531 thd_label
= is_context
? _("Thd") : _("Thread");
532 thd_len
= (int) utf8_width(thd_label
);
535 loc1_len
= (int) utf8_width(loc_label
); /* initial length */
537 if(!mn_get_total(as
.msgmap
)){
538 loc_label
[strlen(loc_label
)-1] = 's';
539 snprintf(loc1
, sizeof(loc1
), "%s %s", _("No"), loc_label
);
540 loc1
[sizeof(loc1
)-1]= '\0';
544 case FolderName
: /* "x,xxx <loc_label>s" */
546 if(mn_get_total(as
.msgmap
) != 1)
547 loc_label
[strlen(loc_label
)-1] = 's';
549 loc_label
[strlen(loc_label
)-1] = '\0';
552 snprintf(loc1
, sizeof(loc1
), "%s %s", comatose(mn_get_total(as
.msgmap
)), loc_label
);
553 loc1
[sizeof(loc1
)-1]= '\0';
556 case MessageNumber
: /* "<loc_label> xxx of xxx DEL" */
557 num_len
= strlen(comatose(mn_get_total(as
.msgmap
)));
558 cur_mess_col_offset
= loc1_len
;
559 snprintf(loc1
, sizeof(loc1
), "%s%*.*s of %s", loc_label
,
561 comatose(as
.current_msg
),
562 comatose(mn_get_total(as
.msgmap
)));
563 loc1
[sizeof(loc1
)-1]= '\0';
564 loc2
= BAR_STATUS(as
.msg_state
);
568 case ThrdIndex
: /* "<loc_label> xxx of xxx" */
569 num_len
= strlen(comatose(as
.total_lines
));
570 cur_mess_col_offset
= loc1_len
;
571 snprintf(loc1
, sizeof(loc1
), "%s%*.*s of %s", loc_label
,
573 comatose(as
.current_thrd
),
574 comatose(as
.total_lines
));
575 loc1
[sizeof(loc1
)-1]= '\0';
578 case ThrdMsgNum
: /* "<loc_label> xxx in Thd xxx DEL" */
579 num_len
= strlen(comatose(mn_get_total(as
.msgmap
)));
580 cur_mess_col_offset
= loc1_len
;
581 snprintf(loc1
, sizeof(loc1
), "%s%*.*s in %s %s", loc_label
,
583 comatose(as
.current_msg
),
585 comatose(as
.current_thrd
));
586 loc1
[sizeof(loc1
)-1]= '\0';
587 loc2
= BAR_STATUS(as
.msg_state
);
591 case MsgTextPercent
: /* "<loc_label> xxx of xxx xx% DEL" */
592 num_len
= strlen(comatose(mn_get_total(as
.msgmap
)));
593 cur_mess_col_offset
= loc1_len
;
594 percent_column_offset
= 3;
595 snprintf(loc1
, sizeof(loc1
), "%s%*.*s of %s %s", loc_label
,
597 comatose(as
.current_msg
),
598 comatose(mn_get_total(as
.msgmap
)),
599 percentage(as
.current_line
, as
.total_lines
, 1));
600 loc1
[sizeof(loc1
)-1]= '\0';
601 loc2
= BAR_STATUS(as
.msg_state
);
605 case ThrdMsgPercent
: /* "<loc_label> xxx in Thd xxx xx% DEL" */
606 num_len
= strlen(comatose(mn_get_total(as
.msgmap
)));
607 cur_mess_col_offset
= loc1_len
;
608 percent_column_offset
= 3;
609 snprintf(loc1
, sizeof(loc1
), "%s%*.*s in %s %s %s", loc_label
,
611 comatose(as
.current_msg
),
613 comatose(as
.current_thrd
),
614 percentage(as
.current_line
, as
.total_lines
, 1));
615 loc1
[sizeof(loc1
)-1]= '\0';
616 loc2
= BAR_STATUS(as
.msg_state
);
621 /* NOTE: no fold_tmp setup below for TextPercent style */
622 case FileTextPercent
: /* "Line xxx of xxx xx%" */
623 num_len
= strlen(comatose(as
.total_lines
));
624 page_column_offset
= 5;
625 percent_column_offset
= 3;
626 snprintf(loc1
, sizeof(loc1
), "Line %*.*s of %s %s",
628 comatose(as
.current_line
),
629 comatose(as
.total_lines
),
630 percentage(as
.current_line
, as
.total_lines
, 1));
631 loc1
[sizeof(loc1
)-1]= '\0';
638 loc1_len
= utf8_width(loc1
);
640 if(loc1_len
+ loc2_len
+ ((loc2_len
> 0) ? 1 : 0) >= avail
){ /* can't fit location in */
643 goto try_smaller_loc
;
646 loc1_len
= loc2_len
= 0;
648 avail
+= s2
; /* re-allocate s2 to center title */
650 s2
= MAX(MIN(HRD(avail
), avail
), 0);
656 loc1_len
= MAX(MIN(loc1_len
, avail
), 0);
659 loc2_len
= MAX(MIN(loc2_len
, avail
), 0);
663 s5
= MAX(MIN(1, avail
), 0);
669 s3
= MAX(MIN(TF
, avail
), 0);
672 s4
= MAX(MIN(FL
, avail
), 0);
676 /* TRANSLATORS: it might say READONLY or CLOSED in the titlebar, referring to
677 the current folder. */
678 ss_string
= as
.stream_status
== Closed
? _("(CLOSED)") :
679 (as
.stream_status
== OnlyRead
680 && !IS_NEWS(as
.stream
))
681 ? _("(READONLY)") : "";
682 ss_len
= (int) utf8_width(ss_string
);
684 /* figure folder_length and what's to be displayed */
686 if(as
.style
== FileTextPercent
|| as
.style
== TextPercent
){
687 if(as
.style
== FileTextPercent
){
688 extra
= (int) utf8_width("File: ");
689 fold_len
= (int) utf8_width(as
.folder_name
);
690 if(fold_len
+ extra
<= avail
){ /* all of folder fit? */
691 strncpy(fold_tmp
, "File: ", sizeof(fold_tmp
));
692 q
= fold_tmp
+ strlen(fold_tmp
);
693 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
695 else if(HRU(fold_len
) + extra
+3 <= avail
){
697 * fold_tmp = ...partial_folder_name
699 strncpy(fold_tmp
, "File: ...", sizeof(fold_tmp
));
700 q
= fold_tmp
+ strlen(fold_tmp
);
701 (void) utf8_to_width_rhs(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
-(extra
+3));
704 /* else leave folder/file name blank */
709 fold_len
= (int) utf8_width(as
.folder_name
);
712 && as
.stream_status
!= Closed
713 && (ct_len
= (int) utf8_width(as
.context_name
))){
715 extra
= 3; /* length from "<" ">" and SPACE */
717 if(ct_len
+ fold_len
+ ss_len
+ extra
<= avail
){
720 strncpy(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
724 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
726 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
728 else if(ct_len
+ fold_len
+ ss_len
+ extra
<= avail
){
731 strncpy(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
735 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
737 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
739 else if(HRU(ct_len
) + fold_len
+ ss_len
+ extra
<= avail
){
742 q
+= utf8_pad_to_width(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
-(fold_len
+ss_len
+extra
), 1);
745 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
747 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
749 else if(HRU(ct_len
) + HRU(fold_len
) + ss_len
+ extra
<= avail
){
752 q
+= utf8_pad_to_width(q
, as
.context_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), HRU(ct_len
), 1);
755 q
+= utf8_to_width_rhs(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
-(HRU(ct_len
)+ss_len
+extra
));
756 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
758 else if(ss_len
> 0 && ss_len
<= avail
){
760 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
762 /* else leave it out */
765 /* TRANSLATORS: the name of the open folder follows this in the titlebar */
766 extra
= strlen(_("Folder: "));
767 if(fold_len
+ ss_len
+ extra
<= avail
){
769 strncpy(q
, "Folder: ", sizeof(fold_tmp
)-(q
-fold_tmp
));
771 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
773 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
776 if(fold_len
+ ss_len
<= avail
){
778 strncpy(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
));
780 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
782 else if(((HRU(fold_len
) + ss_len
<= avail
)
783 || (avail
> ss_len
+3 && avail
> 10)) && fold_len
> 5){
785 strncpy(q
, "...", sizeof(fold_tmp
));
787 q
+= utf8_to_width_rhs(q
, as
.folder_name
, sizeof(fold_tmp
)-(q
-fold_tmp
), avail
-(3+ss_len
));
788 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
790 else if(ss_len
> 0 && ss_len
<= avail
){
792 strncpy(q
, ss_string
, sizeof(fold_tmp
)-(q
-fold_tmp
));
794 /* else leave it out */
799 fold_tmp
[sizeof(fold_tmp
)-1] = '\0';
801 /* write title, location and, optionally, the folder name */
802 fold_len
= (int) utf8_width(fold_tmp
);
805 fold_len
= MAX(MIN(fold_len
, avail
), 0);
808 /* center folder in its space */
824 if(as
.style
!= FileTextPercent
&& as
.style
!= TextPercent
){
828 && as
.stream
->mailbox
829 && mail_valid_net_parse(as
.stream
->mailbox
, &mb
)
830 && (mb
.sslflag
|| mb
.tlsflag
))
835 if(loc1_len
> 0 && cur_mess_col_offset
>= 0)
836 as
.cur_mess_col
= s1
+ver_len
+s2
+title_len
+s3
+fold_len
+s4
+ cur_mess_col_offset
;
838 if(loc1_len
> 0 && page_column_offset
>= 0)
839 as
.page_column
= s1
+ver_len
+s2
+title_len
+s3
+fold_len
+s4
+ page_column_offset
;
842 as
.del_column
= s1
+ver_len
+s2
+title_len
+s3
+fold_len
+s4
+loc1_len
+s5
;
844 if(loc1_len
> 0 && percent_column_offset
> 0)
845 as
.percent_column
= s1
+ver_len
+s2
+title_len
+s3
+fold_len
+s4
+loc1_len
- percent_column_offset
;
848 utf8_snprintf(titlebar_line
, 6*MAX_SCREEN_COLS
, "%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s",
850 ver_len
, ver_len
, version
,
852 title_len
, title_len
, title
,
854 fold_len
, fold_len
, fold_tmp
,
856 loc1_len
, loc1_len
, loc1
,
858 loc2_len
, loc2_len
, loc2
,
861 return(&as
.titlecontainer
);
866 sort_letter(SortOrder sort
)
870 if((p
= sort_name(sort
)) != NULL
&& *p
){
871 while(*(p
+1) && islower((unsigned char) *p
))
883 * Update the titlebar line if the message number changed
885 * Args: None, uses state setup on previous call to set_titlebar.
888 update_titlebar_message(void)
890 long curnum
, maxnum
, oldnum
;
891 PINETHRD_S
*thrd
= NULL
;
892 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
898 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
899 titlebar_is_dirty
= 1;
903 if(as
.style
== ThrdIndex
){
905 oldnum
= as
.current_thrd
;
907 if(as
.stream
&& (rawno
=mn_m2raw(as
.msgmap
, mn_get_cur(as
.msgmap
))))
908 thrd
= fetch_thread(as
.stream
, rawno
);
910 if(thrd
&& thrd
->top
&& (thrd
=fetch_thread(as
.stream
, thrd
->top
)))
911 curnum
= thrd
->thrdno
;
913 else if(as
.cur_mess_col
>= 0){
914 curnum
= mn_get_cur(as
.msgmap
);
915 oldnum
= as
.current_msg
;
918 if(as
.cur_mess_col
>= 0 && oldnum
!= curnum
){
920 if(as
.style
== ThrdIndex
){
921 as
.current_thrd
= curnum
;
922 maxnum
= as
.msgmap
->max_thrdno
;
925 as
.current_msg
= curnum
;
926 maxnum
= mn_get_total(as
.msgmap
);
929 titlecolor
= &as
.titlecontainer
.color
;
932 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
934 num_len
= strlen(comatose(maxnum
));
936 snprintf(buf
, sizeof(buf
), "%*.*s", num_len
, num_len
, comatose(curnum
));
938 PutLine0(0, as
.cur_mess_col
, buf
);
941 (void)pico_set_colorp(lastc
, PSC_NONE
);
942 free_color_pair(&lastc
);
952 * Update titlebar line's message status field ("DEL", "NEW", etc)
954 * Args: None, operates on state set during most recent set_titlebar call
957 update_titlebar_status(void)
961 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
964 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
965 titlebar_is_dirty
= 1;
969 if(!as
.stream
|| as
.current_msg
<= 0L || as
.del_column
< 0)
972 if(as
.current_msg
!= mn_get_cur(as
.msgmap
))
973 update_titlebar_message();
975 mc
= ((rawno
= mn_m2raw(as
.msgmap
, as
.current_msg
)) > 0L
976 && rawno
<= as
.stream
->nmsgs
)
977 ? mail_elt(as
.stream
, rawno
) : NULL
;
979 if(!(mc
&& mc
->valid
))
980 return(0); /* indeterminate */
982 if(mc
->deleted
){ /* deleted takes precedence */
983 if(as
.msg_state
& MS_DEL
)
986 else if(mc
->answered
){ /* then answered */
987 if(as
.msg_state
& MS_ANS
)
989 } /* then forwarded */
990 else if(user_flag_is_set(as
.stream
, mc
->msgno
, FORWARDED_FLAG
)){
991 if(as
.msg_state
& MS_FWD
)
994 else if(!mc
->seen
&& as
.stream
995 && (!IS_NEWS(as
.stream
)
996 || F_ON(F_FAKE_NEW_IN_NEWS
, ps_global
))){
997 if(as
.msg_state
& MS_NEW
) /* then seen */
1001 if(as
.msg_state
== 0) /* nothing to change */
1005 as
.msg_state
= STATUS_BITS(mc
);
1007 titlecolor
= &as
.titlecontainer
.color
;
1010 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
1012 PutLine0(0, as
.del_column
, BAR_STATUS(as
.msg_state
));
1015 (void)pico_set_colorp(lastc
, PSC_NONE
);
1016 free_color_pair(&lastc
);
1025 * Update the percentage shown in the titlebar line
1027 * Args: new_line_number -- line number to calculate new percentage
1030 update_titlebar_percent(long int new_line_number
)
1032 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
1034 if(as
.percent_column
< 0 || new_line_number
== as
.current_line
)
1038 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
1039 titlebar_is_dirty
= 1;
1043 as
.current_line
= new_line_number
;
1045 titlecolor
= &as
.titlecontainer
.color
;
1048 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
1050 PutLine0(0, as
.percent_column
,
1051 percentage(as
.current_line
, as
.total_lines
, 0));
1054 (void)pico_set_colorp(lastc
, PSC_NONE
);
1055 free_color_pair(&lastc
);
1063 * Update the percentage AND line number shown in the titlebar line
1065 * Args: new_line_number -- line number to calculate new percentage
1068 update_titlebar_lpercent(long int new_line_number
)
1070 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
1074 if(as
.page_column
< 0 || new_line_number
== as
.current_line
)
1078 && (ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
)) < 1){
1079 titlebar_is_dirty
= 1;
1083 as
.current_line
= new_line_number
;
1085 titlecolor
= &as
.titlecontainer
.color
;
1088 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
1090 num_len
= strlen(comatose(as
.total_lines
));
1091 snprintf(buf
, sizeof(buf
), "%*.*s", num_len
, num_len
, comatose(as
.current_line
));
1093 PutLine0(0, as
.page_column
, buf
);
1095 PutLine0(0, as
.percent_column
,
1096 percentage(as
.current_line
, as
.total_lines
, 0));
1099 (void)pico_set_colorp(lastc
, PSC_NONE
);
1100 free_color_pair(&lastc
);
1107 /*----------------------------------------------------------------------
1108 Return static buf containing portion of lines displayed
1110 Args: part -- how much so far
1111 total -- how many total
1115 percentage(long int part
, long int total
, int suppress_top
)
1117 static char percent
[4];
1119 if(total
== 0L || (total
<= ps_global
->ttyo
->screen_rows
1120 - HEADER_ROWS(ps_global
)
1121 - FOOTER_ROWS(ps_global
)))
1122 strncpy(percent
, "ALL", sizeof(percent
));
1123 else if(!suppress_top
&& part
<= ps_global
->ttyo
->screen_rows
1124 - HEADER_ROWS(ps_global
)
1125 - FOOTER_ROWS(ps_global
))
1126 strncpy(percent
, "TOP", sizeof(percent
));
1127 else if(part
>= total
)
1128 strncpy(percent
, "END", sizeof(percent
));
1130 snprintf(percent
, sizeof(percent
), "%2ld%%", (100L * part
)/total
);
1132 percent
[sizeof(percent
)-1] = '\0';
1139 * end_titlebar - free resources associated with titlebar state struct
1145 fs_give((void **)&as
.folder_name
);
1148 fs_give((void **)&as
.context_name
);
1152 /*----------------------------------------------------------------------
1153 Exported method to display status of mail check
1155 Args: putstr -- should be NO LONGER THAN 2 bytes
1157 Result: putstr displayed at upper-left-hand corner of screen
1160 check_cue_display(char *putstr
)
1162 COLOR_PAIR
*lastc
= NULL
, *titlecolor
;
1163 static char check_cue_char
;
1165 if(ps_global
->read_predicted
&&
1166 (check_cue_char
== putstr
[0]
1167 || (putstr
[0] == ' ' && putstr
[1] == '\0')))
1170 if(putstr
[0] == ' ')
1171 check_cue_char
= '\0';
1173 check_cue_char
= putstr
[0];
1176 titlecolor
= &as
.titlecontainer
.color
;
1179 lastc
= pico_set_colorp(titlecolor
, PSC_REV
|PSC_RET
);
1181 PutLine0(0, 0, putstr
); /* show delay cue */
1183 (void)pico_set_colorp(lastc
, PSC_NONE
);
1184 free_color_pair(&lastc
);
1191 /*----------------------------------------------------------------------
1192 Mandatory function of ../pith/newmail.c
1196 Result: newmail cue displayed at upper-left-hand corner of screen
1199 newmail_check_cue(int onoroff
)
1201 if(F_ON(F_SHOW_DELAY_CUE
, ps_global
) && !ps_global
->in_init_seq
){
1202 check_cue_display((onoroff
== TRUE
) ? " *" : " ");
1203 MoveCursor(ps_global
->ttyo
->screen_rows
-FOOTER_ROWS(ps_global
), 0);
1208 mswin_setcursor (MSWIN_CURSOR_BUSY
);
1210 mswin_setcursor (MSWIN_CURSOR_ARROW
);
1215 /*----------------------------------------------------------------------
1216 Mandatory function of ../pith/newmail.c
1220 Result: checkpoint delay cue displayed at upper-left-hand corner of screen
1223 newmail_check_point_cue(int onoroff
)
1225 if(F_ON(F_SHOW_DELAY_CUE
, ps_global
)){
1226 check_cue_display((onoroff
== TRUE
) ? "**" : " ");
1227 MoveCursor(ps_global
->ttyo
->screen_rows
-FOOTER_ROWS(ps_global
), 0);
1232 mswin_setcursor (MSWIN_CURSOR_BUSY
);
1234 mswin_setcursor (MSWIN_CURSOR_ARROW
);