* Rewrite support for specific SSL encryption protocols, including
[alpine.git] / alpine / titlebar.c
blob4b51cc6a3f0a85abef775a807bdbd16577c48511
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 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 * ========================================================================
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 pushed;
88 enum {Normal, OnlyRead, Closed} stream_status;
89 TitleBarType style;
90 TITLE_S titlecontainer;
91 } as, titlebar_stack;
94 static int titlebar_is_dirty = 1;
96 char *as_fname; /* folder name */
97 char *as_cname; /* context name */
99 void
100 push_titlebar_state(void)
102 as.pushed = 1;
103 titlebar_stack = as;
104 if(as_fname) fs_give((void **)&as_fname);
105 as_fname = cpystr(as.folder_name);
106 if(as_cname) fs_give((void **)&as_cname);
107 as_cname = cpystr(as.context_name);
108 if(as.folder_name) fs_give((void **)&as.folder_name);
109 if(as.context_name) fs_give((void **)&as.context_name);
113 void
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));
120 as = titlebar_stack;
121 if(as.pushed){
122 as.folder_name = as_fname ? cpystr(as_fname) : NULL;
123 as.context_name = as_cname ? cpystr(as_cname): NULL;
125 as.pushed = 0;
131 digit_count(long int n)
133 int i;
135 return((n > 9)
136 ? (1 + (((i = digit_count(n / 10L)) == 3 || i == 7) ? i+1 : i))
137 : 1);
141 void
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
152 true yet.
154 Args: title -- The title that appears in the center of the line
155 This title is in UTF-8 characters.
156 display_on_screen -- flag whether to display on screen or generate
157 string
158 style -- The format/style of the titlebar line
159 msgmap -- MSGNO_S * to selected message map
160 current_pl -- The current page or line number
161 total_pl -- The total line or page count
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.
180 ----*/
182 char *
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;
190 TITLE_S *tc;
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;
199 as.msgmap = msgmap;
200 as.style = style;
201 as.title = title;
202 as.stream = stream;
203 as.stream_status = (!as.stream || (sp_dead_stream(as.stream)))
204 ? Closed : as.stream->rdonly ? OnlyRead : Normal;
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';
217 else{
218 if(color){
219 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
220 if(color->fg){
221 strncpy(as.titlecontainer.color.fg, color->fg, MAXCOLORLEN);
222 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
225 if(color->bg){
226 strncpy(as.titlecontainer.color.bg, color->bg, MAXCOLORLEN);
227 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
230 else{
231 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
232 if(VAR_TITLE_FORE_COLOR){
233 strncpy(as.titlecontainer.color.fg,
234 VAR_TITLE_FORE_COLOR, MAXCOLORLEN);
235 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
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';
246 if(as.folder_name)
247 fs_give((void **)&as.folder_name);
249 if(folder){
250 unsigned char *fname;
251 fname = folder_name_decoded((unsigned char *) folder);
252 if(!strucmp(folder, ps_global->inbox_name) && cntxt == ps_global->context_list)
253 as.folder_name = cpystr(pretty_fn(fname ? (char *) fname : folder));
254 else
255 as.folder_name = cpystr(fname ? (char *) fname : folder);
256 if(fname) fs_give((void **)&fname);
259 if(!as.folder_name)
260 as.folder_name = cpystr("");
262 if(as.context_name)
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
275 ? cntxt->nickname
276 : cntxt->context);
279 if(!as.context_name)
280 as.context_name = cpystr("");
282 if(as.stream && style != FolderName
283 && style != ThrdIndex && as.current_msg > 0L) {
284 long rawno;
286 if((rawno = mn_m2raw(msgmap, as.current_msg)) > 0L
287 && rawno <= as.stream->nmsgs
288 && !((mc = mail_elt(as.stream, rawno)) && mc->valid)){
289 pine_mail_fetch_flags(as.stream, long2string(rawno), NIL);
290 if(rawno <= as.stream->nmsgs && as.stream && rawno > 0L)
291 mc = mail_elt(as.stream, rawno);
292 else
293 mc = NULL;
294 if(mc && !mc->valid)
295 mc = NULL;
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);
306 if(thrd)
307 as.current_thrd = thrd->thrdno;
310 switch(style) {
311 case ThrdIndex:
312 as.total_lines = msgmap->max_thrdno;
313 break;
315 case TextPercent:
316 case MsgTextPercent:
317 case FileTextPercent :
318 case ThrdMsgPercent:
319 as.total_lines = total_pl;
320 as.current_line = current_pl;
321 /* fall through to set state */
322 case ThrdMsgNum:
323 case MessageNumber:
324 as.msg_state = STATUS_BITS(mc);
326 case FolderName: /* nothing more to do for this one */
327 break;
328 default:
329 break;
332 tc = format_titlebar();
333 if(display_on_screen)
334 output_titlebar(tc);
336 return(tc->titlebar_line);
340 void
341 redraw_titlebar(void)
343 output_titlebar(format_titlebar());
347 void
348 output_titlebar(TITLE_S *tc)
350 COLOR_PAIR *lastc = NULL, *newcolor;
352 if(ps_global->ttyo
353 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
354 titlebar_is_dirty = 1;
355 return;
358 newcolor = tc ? &tc->color : NULL;
360 if(newcolor)
361 lastc = pico_set_colorp(newcolor, PSC_REV | PSC_RET);
363 if(tc && tc->titlebar_line)
364 PutLine0(0, 0, tc->titlebar_line);
366 if(lastc){
367 (void)pico_set_colorp(lastc, PSC_NONE);
368 free_color_pair(&lastc);
371 fflush(stdout);
375 void
376 titlebar_stream_closing(MAILSTREAM *stream)
378 if(stream == as.stream)
379 as.stream = NULL;
383 /* caller should free returned color pair */
384 COLOR_PAIR *
385 current_titlebar_color(void)
387 COLOR_PAIR *col;
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);
395 return(the_color);
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
408 there's space
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
419 ----*/
420 TITLE_S *
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;
433 if(!as.title)
434 return NULL;
436 if(!as.folder_name)
437 as.folder_name = cpystr("");
439 if(!as.context_name)
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);
461 /* initialize */
462 as.del_column = -1;
463 as.cur_mess_col = -1;
464 as.percent_column = -1;
465 as.page_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 */
473 title[0] = '\0';
474 if(as.title){
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;
483 int current_rev;
484 char let;
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){
492 let = 'R';
493 current_rev = 0;
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 */
504 avail -= s1;
506 s6 = MAX(MIN(LR, avail), 0); /* right two spaces */
507 avail -= s6;
509 title_len = MAX(MIN(title_len, avail), 0);
510 avail -= title_len;
512 if(ver_len + VT > avail){ /* can only fit title */
513 ver_len = 0;
515 s2 = MAX(MIN(HRD(avail), avail), 0);
516 avail -= s2;
518 s3 = MAX(0, avail);
520 else{
521 s2 = MAX(MIN(VT, avail), 0);
522 avail -= s2;
524 ver_len = MAX(MIN(ver_len, avail), 0);
525 avail -= ver_len;
527 try_smaller_loc:
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
537 Message 17
538 when reading message number 17. */
539 snprintf(loc_label, sizeof(loc_label), "%s ", (is_context || tryloc==2) ? _("Thd") : _("Thread"));
540 else
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))
544 loc_label[0] = '\0';
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';
560 else{
561 switch(as.style){
562 case FolderName : /* "x,xxx <loc_label>s" */
563 if(*loc_label){
564 if(mn_get_total(as.msgmap) != 1)
565 loc_label[strlen(loc_label)-1] = 's';
566 else
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';
572 break;
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,
578 num_len, num_len,
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);
583 loc2_len = 3;
584 break;
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,
590 num_len, num_len,
591 comatose(as.current_thrd),
592 comatose(as.total_lines));
593 loc1[sizeof(loc1)-1]= '\0';
594 break;
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,
600 num_len, num_len,
601 comatose(as.current_msg),
602 thd_label,
603 comatose(as.current_thrd));
604 loc1[sizeof(loc1)-1]= '\0';
605 loc2 = BAR_STATUS(as.msg_state);
606 loc2_len = 3;
607 break;
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,
614 num_len, num_len,
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);
620 loc2_len = 3;
621 break;
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,
628 num_len, num_len,
629 comatose(as.current_msg),
630 thd_label,
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);
635 loc2_len = 3;
636 break;
638 case TextPercent :
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",
645 num_len, num_len,
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';
650 break;
651 default:
652 break;
656 loc1_len = utf8_width(loc1);
658 if(loc1_len + loc2_len + ((loc2_len > 0) ? 1 : 0) >= avail){ /* can't fit location in */
659 if(tryloc < 3){
660 tryloc++;
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);
669 avail -= s2;
671 s3 = MAX(0, avail);
673 else{
674 loc1_len = MAX(MIN(loc1_len, avail), 0);
675 avail -= loc1_len;
677 loc2_len = MAX(MIN(loc2_len, avail), 0);
678 avail -= loc2_len;
680 if(loc2_len > 0){
681 s5 = MAX(MIN(1, avail), 0);
682 avail -= s5;
684 else
685 s5 = 0;
687 s3 = MAX(MIN(TF, avail), 0);
688 avail -= s3;
690 s4 = MAX(MIN(FL, avail), 0);
691 avail -= s4;
693 if(avail){
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 */
703 fold_tmp[0] = '\0';
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 */
724 else{
725 int ct_len;
727 fold_len = (int) utf8_width(as.folder_name);
729 if(is_context
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){
736 q = fold_tmp;
737 *q++ = '<';
738 strncpy(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp));
739 q += strlen(q);
740 *q++ = '>';
741 *q++ = ' ';
742 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
743 q += strlen(q);
744 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
746 else if(ct_len + fold_len + ss_len + extra <= avail){
747 q = fold_tmp;
748 *q++ = '<';
749 strncpy(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp));
750 q += strlen(q);
751 *q++ = '>';
752 *q++ = ' ';
753 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
754 q += strlen(q);
755 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
757 else if(HRU(ct_len) + fold_len + ss_len + extra <= avail){
758 q = fold_tmp;
759 *q++ = '<';
760 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(fold_len+ss_len+extra), 1);
761 *q++ = '>';
762 *q++ = ' ';
763 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
764 q += strlen(q);
765 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
767 else if(HRU(ct_len) + HRU(fold_len) + ss_len + extra <= avail){
768 q = fold_tmp;
769 *q++ = '<';
770 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), HRU(ct_len), 1);
771 *q++ = '>';
772 *q++ = ' ';
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){
777 q = fold_tmp;
778 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
779 } else if(fold_len < avail){
780 q = fold_tmp;
781 if(fold_len + 7 < avail){
782 *q++ = '<';
783 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), avail - fold_len - 3, 1);
784 *q++ = '>';
785 *q++ = ' ';
787 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
789 /* else leave it out */
791 else{
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){
795 q = fold_tmp;
796 strncpy(q, "Folder: ", sizeof(fold_tmp)-(q-fold_tmp));
797 q += strlen(q);
798 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
799 q += strlen(q);
800 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
802 else{
803 if(fold_len + ss_len <= avail){
804 q = fold_tmp;
805 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
806 q += strlen(q);
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){
811 q = fold_tmp;
812 strncpy(q, "...", sizeof(fold_tmp));
813 q += strlen(q);
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){
818 q = fold_tmp;
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);
833 avail -= fold_len;
835 /* center folder in its space */
836 if(avail){
837 int inc;
839 inc = HRU(avail);
841 s3 += inc;
842 avail -= inc;
844 s4 += avail;
849 plus = "";
851 if(as.style != FileTextPercent && as.style != TextPercent){
852 NETMBX mb;
854 if(as.stream
855 && as.stream->mailbox
856 && mail_valid_net_parse(as.stream->mailbox, &mb)
857 && (mb.sslflag || mb.tlsflag))
858 plus = "+";
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;
868 if(loc2_len > 0)
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",
876 s1, s1, "",
877 ver_len, ver_len, version,
878 s2, s2, "",
879 title_len, title_len, title,
880 s3, s3, "",
881 fold_len, fold_len, fold_tmp,
882 s4, s4, "",
883 loc1_len, loc1_len, loc1,
884 s5, s5, "",
885 loc2_len, loc2_len, loc2,
886 s6, s6, plus);
888 return(&as.titlecontainer);
892 char
893 sort_letter(SortOrder sort)
895 char let = 'A', *p;
897 if((p = sort_name(sort)) != NULL && *p){
898 while(*(p+1) && islower((unsigned char) *p))
899 p++;
901 if(*p)
902 let = *p;
905 return(let);
910 * Update the titlebar line if the message number changed
912 * Args: None, uses state setup on previous call to set_titlebar.
914 void
915 update_titlebar_message(void)
917 long curnum, maxnum, oldnum;
918 PINETHRD_S *thrd = NULL;
919 COLOR_PAIR *lastc = NULL, *titlecolor;
920 char buf[50];
921 int num_len;
922 unsigned long rawno;
924 if(ps_global->ttyo
925 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
926 titlebar_is_dirty = 1;
927 return;
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;
951 else{
952 as.current_msg = curnum;
953 maxnum = mn_get_total(as.msgmap);
956 titlecolor = &as.titlecontainer.color;
958 if(titlecolor)
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);
967 if(lastc){
968 (void)pico_set_colorp(lastc, PSC_NONE);
969 free_color_pair(&lastc);
972 fflush(stdout);
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)
986 unsigned long rawno;
987 MESSAGECACHE *mc;
988 COLOR_PAIR *lastc = NULL, *titlecolor;
990 if(ps_global->ttyo
991 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
992 titlebar_is_dirty = 1;
993 return(0);
996 if(!as.stream || as.current_msg <= 0L || as.del_column < 0)
997 return(1);
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)
1011 return(1);
1013 else if(mc->answered){ /* then answered */
1014 if(as.msg_state & MS_ANS)
1015 return(1);
1016 } /* then forwarded */
1017 else if(user_flag_is_set(as.stream, mc->msgno, FORWARDED_FLAG)){
1018 if(as.msg_state & MS_FWD)
1019 return(1);
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 */
1025 return(1);
1027 else{
1028 if(as.msg_state == 0) /* nothing to change */
1029 return(1);
1032 as.msg_state = STATUS_BITS(mc);
1034 titlecolor = &as.titlecontainer.color;
1036 if(titlecolor)
1037 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1039 PutLine0(0, as.del_column, BAR_STATUS(as.msg_state));
1041 if(lastc){
1042 (void)pico_set_colorp(lastc, PSC_NONE);
1043 free_color_pair(&lastc);
1046 fflush(stdout);
1047 return(1);
1052 * Update the percentage shown in the titlebar line
1054 * Args: new_line_number -- line number to calculate new percentage
1056 void
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)
1062 return;
1064 if(ps_global->ttyo
1065 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
1066 titlebar_is_dirty = 1;
1067 return;
1070 as.current_line = new_line_number;
1072 titlecolor = &as.titlecontainer.color;
1074 if(titlecolor)
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));
1080 if(lastc){
1081 (void)pico_set_colorp(lastc, PSC_NONE);
1082 free_color_pair(&lastc);
1085 fflush(stdout);
1090 * Update the percentage AND line number shown in the titlebar line
1092 * Args: new_line_number -- line number to calculate new percentage
1094 void
1095 update_titlebar_lpercent(long int new_line_number)
1097 COLOR_PAIR *lastc = NULL, *titlecolor;
1098 int num_len;
1099 char buf[50];
1101 if(as.page_column < 0 || new_line_number == as.current_line)
1102 return;
1104 if(ps_global->ttyo
1105 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
1106 titlebar_is_dirty = 1;
1107 return;
1110 as.current_line = new_line_number;
1112 titlecolor = &as.titlecontainer.color;
1114 if(titlecolor)
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));
1125 if(lastc){
1126 (void)pico_set_colorp(lastc, PSC_NONE);
1127 free_color_pair(&lastc);
1130 fflush(stdout);
1134 /*----------------------------------------------------------------------
1135 Return static buf containing portion of lines displayed
1137 Args: part -- how much so far
1138 total -- how many total
1140 ---*/
1141 char *
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));
1156 else
1157 snprintf(percent, sizeof(percent), "%2ld%%", (100L * part)/total);
1159 percent[sizeof(percent)-1] = '\0';
1161 return(percent);
1166 * end_titlebar - free resources associated with titlebar state struct
1168 void
1169 end_titlebar(void)
1171 if(as.folder_name)
1172 fs_give((void **)&as.folder_name);
1174 if(as.context_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
1185 ----*/
1186 void
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')))
1195 return;
1196 else{
1197 if(putstr[0] == ' ')
1198 check_cue_char = '\0';
1199 else
1200 check_cue_char = putstr[0];
1203 titlecolor = &as.titlecontainer.color;
1205 if(titlecolor)
1206 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1208 PutLine0(0, 0, putstr); /* show delay cue */
1209 if(lastc){
1210 (void)pico_set_colorp(lastc, PSC_NONE);
1211 free_color_pair(&lastc);
1214 fflush(stdout);
1218 /*----------------------------------------------------------------------
1219 Mandatory function of ../pith/newmail.c
1221 Args: none
1223 Result: newmail cue displayed at upper-left-hand corner of screen
1224 ----*/
1225 void
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);
1233 #ifdef _WINDOWS
1234 if(onoroff)
1235 mswin_setcursor (MSWIN_CURSOR_BUSY);
1236 else
1237 mswin_setcursor (MSWIN_CURSOR_ARROW);
1238 #endif
1242 /*----------------------------------------------------------------------
1243 Mandatory function of ../pith/newmail.c
1245 Args: none
1247 Result: checkpoint delay cue displayed at upper-left-hand corner of screen
1248 ----*/
1249 void
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);
1257 #ifdef _WINDOWS
1258 if(onoroff)
1259 mswin_setcursor (MSWIN_CURSOR_BUSY);
1260 else
1261 mswin_setcursor (MSWIN_CURSOR_ARROW);
1262 #endif
1265 void
1266 free_titlebar_globals(void)
1268 if(as_fname) fs_give((void **) &as_fname);
1269 if(as_cname) fs_give((void **) &as_cname);
1270 if(as.folder_name) fs_give((void **)&as.folder_name);
1271 if(as.context_name) fs_give((void **)&as.context_name);