* Update to version 2.19.5
[alpine.git] / alpine / titlebar.c
blob06730ddbf01ea615bcea039d53ff7758945dbbf8
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: titlebar.c 1075 2008-06-04 00:19:39Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-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 * ========================================================================
19 #include "headers.h"
20 #include "titlebar.h"
21 #include "folder.h"
22 #include "../pith/state.h"
23 #include "../pith/bitmap.h"
24 #include "../pith/msgno.h"
25 #include "../pith/thread.h"
26 #include "../pith/conf.h"
27 #include "../pith/init.h"
28 #include "../pith/sort.h"
29 #include "../pith/news.h"
30 #include "../pith/util.h"
31 #include "../pith/folder.h"
34 * Internal prototypes
36 int digit_count(long);
37 void output_titlebar(TITLE_S *);
38 char sort_letter(SortOrder);
39 char *percentage(long, long, int);
43 * some useful macros...
45 #define MS_DEL (0x01)
46 #define MS_NEW (0x02)
47 #define MS_ANS (0x04)
48 #define MS_FWD (0x08)
50 #define STATUS_BITS(X) (!(X && (X)->valid) ? 0 \
51 : (X)->deleted ? MS_DEL \
52 : (X)->answered ? MS_ANS \
53 : (as.stream && user_flag_is_set(as.stream, (X)->msgno, FORWARDED_FLAG)) ? MS_FWD \
54 : (as.stream \
55 && (ps_global->unseen_in_view \
56 || (!(X)->seen \
57 && (!IS_NEWS(as.stream) \
58 || F_ON(F_FAKE_NEW_IN_NEWS, \
59 ps_global))))) \
60 ? MS_NEW : 0)
62 #define BAR_STATUS(X) (((X) & MS_DEL) ? "DEL" \
63 : ((X) & MS_ANS) ? "ANS" \
64 : ((X) & MS_FWD) ? "FWD" \
65 : (as.stream \
66 && (!IS_NEWS(as.stream) \
67 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)) \
68 && ((X) & MS_NEW)) ? "NEW" : " ")
71 static struct titlebar_state {
72 MAILSTREAM *stream;
73 MSGNO_S *msgmap;
74 char *title,
75 *folder_name,
76 *context_name;
77 long current_msg,
78 current_line,
79 current_thrd,
80 total_lines;
81 int msg_state,
82 cur_mess_col,
83 del_column,
84 percent_column,
85 page_column,
86 screen_cols;
87 enum {Normal, OnlyRead, Closed} stream_status;
88 TitleBarType style;
89 TITLE_S titlecontainer;
90 } as, titlebar_stack;
93 static int titlebar_is_dirty = 1;
96 void
97 push_titlebar_state(void)
99 titlebar_stack = as;
100 as.folder_name = NULL; /* erase knowledge of malloc'd data */
101 as.context_name = NULL;
105 void
106 pop_titlebar_state(void)
108 /* guard against case where push pushed no state */
109 if(titlebar_stack.style != TitleBarNone){
110 fs_give((void **)&(as.folder_name)); /* free malloc'd values */
111 fs_give((void **)&(as.context_name));
112 as = titlebar_stack;
118 digit_count(long int n)
120 int i;
122 return((n > 9)
123 ? (1 + (((i = digit_count(n / 10L)) == 3 || i == 7) ? i+1 : i))
124 : 1);
128 void
129 mark_titlebar_dirty(void)
131 titlebar_is_dirty = 1;
135 /*----------------------------------------------------------------------
136 Sets up style and contents of current titlebar line
138 All of the text is assumed to be UTF-8 text, which probably isn't
139 true yet.
141 Args: title -- The title that appears in the center of the line
142 This title is in UTF-8 characters.
143 display_on_screen -- flag whether to display on screen or generate
144 string
145 style -- The format/style of the titlebar line
146 msgmap -- MSGNO_S * to selected message map
147 current_pl -- The current page or line number
148 total_pl -- The total line or page count
150 Set the contents of the anchor line. It's called an anchor line
151 because it's always present and titlebars the user. This accesses a
152 number of global variables, but doesn't change any. There are several
153 different styles of the titlebar line.
155 It's OK to call this with a bogus current message - it is only used
156 to look up status of current message
158 Formats only change the right section (part3).
159 FolderName: "<folder>" xx Messages
160 MessageNumber: "<folder>" message x,xxx of x,xxx XXX
161 TextPercent: line xxx of xxx xx%
162 MsgTextPercent: "<folder>" message x,xxx of x,xxx xx% XXX
163 FileTextPercent: "<filename>" line xxx of xxx xx%
165 Several strings and column numbers are saved so later updates to the status
166 line for changes in message number or percentage can be done efficiently.
167 ----*/
169 char *
170 set_titlebar(char *title, MAILSTREAM *stream, CONTEXT_S *cntxt, char *folder,
171 MSGNO_S *msgmap, int display_on_screen, TitleBarType style,
172 long int current_pl, long int total_pl, COLOR_PAIR *color)
174 struct variable *vars = ps_global->vars;
175 MESSAGECACHE *mc = NULL;
176 PINETHRD_S *thrd = NULL;
177 TITLE_S *tc;
179 dprint((9, "set_titlebar - style: %d current message cnt:%ld",
180 style, mn_total_cur(msgmap)));
181 dprint((9, " current_pl: %ld total_pl: %ld\n",
182 current_pl, total_pl));
184 as.current_msg = (mn_get_total(msgmap) > 0L)
185 ? MAX(0, mn_get_cur(msgmap)) : 0L;
186 as.msgmap = msgmap;
187 as.style = style;
188 as.title = title;
189 as.stream = stream;
190 as.stream_status = (!as.stream || (sp_dead_stream(as.stream)))
191 ? Closed : as.stream->rdonly ? OnlyRead : Normal;
193 if(ps_global->first_open_was_attempted
194 && as.stream_status == Closed
195 && VAR_TITLECLOSED_FORE_COLOR && VAR_TITLECLOSED_BACK_COLOR){
196 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
197 strncpy(as.titlecontainer.color.fg,
198 VAR_TITLECLOSED_FORE_COLOR, MAXCOLORLEN);
199 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
200 strncpy(as.titlecontainer.color.bg,
201 VAR_TITLECLOSED_BACK_COLOR, MAXCOLORLEN);
202 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
204 else{
205 if(color){
206 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
207 if(color->fg){
208 strncpy(as.titlecontainer.color.fg, color->fg, MAXCOLORLEN);
209 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
212 if(color->bg){
213 strncpy(as.titlecontainer.color.bg, color->bg, MAXCOLORLEN);
214 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
217 else{
218 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
219 if(VAR_TITLE_FORE_COLOR){
220 strncpy(as.titlecontainer.color.fg,
221 VAR_TITLE_FORE_COLOR, MAXCOLORLEN);
222 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
225 if(VAR_TITLE_BACK_COLOR){
226 strncpy(as.titlecontainer.color.bg,
227 VAR_TITLE_BACK_COLOR, MAXCOLORLEN);
228 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
233 if(as.folder_name)
234 fs_give((void **)&as.folder_name);
236 if(folder){
237 unsigned char *fname;
238 fname = folder_name_decoded((unsigned char *) folder);
239 if(!strucmp(folder, ps_global->inbox_name) && cntxt == ps_global->context_list)
240 as.folder_name = cpystr(pretty_fn(fname ? (char *) fname : folder));
241 else
242 as.folder_name = cpystr(fname ? (char *) fname : folder);
243 if(fname) fs_give((void **)&fname);
246 if(!as.folder_name)
247 as.folder_name = cpystr("");
249 if(as.context_name)
250 fs_give((void **)&as.context_name);
253 * Handle setting up the context if appropriate.
255 if(cntxt && context_isambig(folder) && ps_global->context_list->next
256 && (strucmp(as.folder_name, ps_global->inbox_name) || cntxt != ps_global->context_list)){
258 * if there's more than one context and the current folder
259 * is in it (ambiguous name), set the context name...
261 as.context_name = cpystr(cntxt->nickname
262 ? cntxt->nickname
263 : cntxt->context);
266 if(!as.context_name)
267 as.context_name = cpystr("");
269 if(as.stream && style != FolderName
270 && style != ThrdIndex && as.current_msg > 0L) {
271 long rawno;
273 if((rawno = mn_m2raw(msgmap, as.current_msg)) > 0L
274 && rawno <= as.stream->nmsgs
275 && !((mc = mail_elt(as.stream, rawno)) && mc->valid)){
276 pine_mail_fetch_flags(as.stream, long2string(rawno), NIL);
277 mc = mail_elt(as.stream, rawno);
281 if(style == ThrdIndex || style == ThrdMsgNum || style == ThrdMsgPercent){
282 if(mn_get_total(msgmap) > 0L)
283 thrd = fetch_thread(stream, mn_m2raw(msgmap, mn_get_cur(msgmap)));
285 if(thrd && thrd->top && thrd->top != thrd->rawno)
286 thrd = fetch_thread(stream, thrd->top);
288 if(thrd)
289 as.current_thrd = thrd->thrdno;
292 switch(style) {
293 case ThrdIndex:
294 as.total_lines = msgmap->max_thrdno;
295 break;
297 case TextPercent:
298 case MsgTextPercent:
299 case FileTextPercent :
300 case ThrdMsgPercent:
301 as.total_lines = total_pl;
302 as.current_line = current_pl;
303 /* fall through to set state */
304 case ThrdMsgNum:
305 case MessageNumber:
306 as.msg_state = STATUS_BITS(mc);
308 case FolderName: /* nothing more to do for this one */
309 break;
310 default:
311 break;
314 tc = format_titlebar();
315 if(display_on_screen)
316 output_titlebar(tc);
318 return(tc->titlebar_line);
322 void
323 redraw_titlebar(void)
325 output_titlebar(format_titlebar());
329 void
330 output_titlebar(TITLE_S *tc)
332 COLOR_PAIR *lastc = NULL, *newcolor;
334 if(ps_global->ttyo
335 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
336 titlebar_is_dirty = 1;
337 return;
340 newcolor = tc ? &tc->color : NULL;
342 if(newcolor)
343 lastc = pico_set_colorp(newcolor, PSC_REV | PSC_RET);
345 if(tc && tc->titlebar_line)
346 PutLine0(0, 0, tc->titlebar_line);
348 if(lastc){
349 (void)pico_set_colorp(lastc, PSC_NONE);
350 free_color_pair(&lastc);
353 fflush(stdout);
357 void
358 titlebar_stream_closing(MAILSTREAM *stream)
360 if(stream == as.stream)
361 as.stream = NULL;
365 /* caller should free returned color pair */
366 COLOR_PAIR *
367 current_titlebar_color(void)
369 TITLE_S *tc;
370 COLOR_PAIR *col;
371 COLOR_PAIR *the_color = NULL;
373 tc = format_titlebar();
374 col = tc ? &tc->color : NULL;
376 if(col && col->fg && col->fg[0] && col->bg && col->bg[0])
377 the_color = new_color_pair(col->fg, col->bg);
379 return(the_color);
383 /*----------------------------------------------------------------------
384 Redraw or draw the top line, the title bar
386 The titlebar has Four fields:
387 1) "Version" of fixed length and is always positioned two spaces
388 in from left display edge.
389 2) "Location" which is fixed for each style of titlebar and
390 is positioned two spaces from the right display edge
391 3) "Title" which is of fixed length, and is centered if
392 there's space
393 4) "Folder" whose existence depends on style and which can
394 have it's length adjusted (within limits) so it will
395 equally share the space between 1) and 2) with the
396 "Title". The rule for existence is that in the
397 space between 1) and 2) there must be one space between
398 3) and 4) AND at least 50% of 4) must be displayed.
400 Returns - Formatted title bar
401 ----*/
402 TITLE_S *
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;
415 if(!as.title)
416 return NULL;
418 if(!as.folder_name)
419 as.folder_name = cpystr("");
421 if(!as.context_name)
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);
443 /* initialize */
444 as.del_column = -1;
445 as.cur_mess_col = -1;
446 as.percent_column = -1;
447 as.page_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 */
455 title[0] = '\0';
456 if(as.title){
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;
465 int current_rev;
466 char let;
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){
474 let = 'R';
475 current_rev = 0;
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 */
486 avail -= s1;
488 s6 = MAX(MIN(LR, avail), 0); /* right two spaces */
489 avail -= s6;
491 title_len = MAX(MIN(title_len, avail), 0);
492 avail -= title_len;
494 if(ver_len + VT > avail){ /* can only fit title */
495 ver_len = 0;
497 s2 = MAX(MIN(HRD(avail), avail), 0);
498 avail -= s2;
500 s3 = MAX(0, avail);
502 else{
503 s2 = MAX(MIN(VT, avail), 0);
504 avail -= s2;
506 ver_len = MAX(MIN(ver_len, avail), 0);
507 avail -= ver_len;
509 try_smaller_loc:
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
519 Message 17
520 when reading message number 17. */
521 snprintf(loc_label, sizeof(loc_label), "%s ", (is_context || tryloc==2) ? _("Thd") : _("Thread"));
522 else
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))
526 loc_label[0] = '\0';
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';
542 else{
543 switch(as.style){
544 case FolderName : /* "x,xxx <loc_label>s" */
545 if(*loc_label){
546 if(mn_get_total(as.msgmap) != 1)
547 loc_label[strlen(loc_label)-1] = 's';
548 else
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';
554 break;
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,
560 num_len, num_len,
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);
565 loc2_len = 3;
566 break;
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,
572 num_len, num_len,
573 comatose(as.current_thrd),
574 comatose(as.total_lines));
575 loc1[sizeof(loc1)-1]= '\0';
576 break;
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,
582 num_len, num_len,
583 comatose(as.current_msg),
584 thd_label,
585 comatose(as.current_thrd));
586 loc1[sizeof(loc1)-1]= '\0';
587 loc2 = BAR_STATUS(as.msg_state);
588 loc2_len = 3;
589 break;
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,
596 num_len, num_len,
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);
602 loc2_len = 3;
603 break;
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,
610 num_len, num_len,
611 comatose(as.current_msg),
612 thd_label,
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);
617 loc2_len = 3;
618 break;
620 case TextPercent :
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",
627 num_len, num_len,
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';
632 break;
633 default:
634 break;
638 loc1_len = utf8_width(loc1);
640 if(loc1_len + loc2_len + ((loc2_len > 0) ? 1 : 0) >= avail){ /* can't fit location in */
641 if(tryloc < 3){
642 tryloc++;
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);
651 avail -= s2;
653 s3 = MAX(0, avail);
655 else{
656 loc1_len = MAX(MIN(loc1_len, avail), 0);
657 avail -= loc1_len;
659 loc2_len = MAX(MIN(loc2_len, avail), 0);
660 avail -= loc2_len;
662 if(loc2_len > 0){
663 s5 = MAX(MIN(1, avail), 0);
664 avail -= s5;
666 else
667 s5 = 0;
669 s3 = MAX(MIN(TF, avail), 0);
670 avail -= s3;
672 s4 = MAX(MIN(FL, avail), 0);
673 avail -= s4;
675 if(avail){
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 */
685 fold_tmp[0] = '\0';
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 */
706 else{
707 int ct_len;
709 fold_len = (int) utf8_width(as.folder_name);
711 if(is_context
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){
718 q = fold_tmp;
719 *q++ = '<';
720 strncpy(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp));
721 q += strlen(q);
722 *q++ = '>';
723 *q++ = ' ';
724 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
725 q += strlen(q);
726 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
728 else if(ct_len + fold_len + ss_len + extra <= avail){
729 q = fold_tmp;
730 *q++ = '<';
731 strncpy(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp));
732 q += strlen(q);
733 *q++ = '>';
734 *q++ = ' ';
735 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
736 q += strlen(q);
737 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
739 else if(HRU(ct_len) + fold_len + ss_len + extra <= avail){
740 q = fold_tmp;
741 *q++ = '<';
742 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(fold_len+ss_len+extra), 1);
743 *q++ = '>';
744 *q++ = ' ';
745 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
746 q += strlen(q);
747 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
749 else if(HRU(ct_len) + HRU(fold_len) + ss_len + extra <= avail){
750 q = fold_tmp;
751 *q++ = '<';
752 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), HRU(ct_len), 1);
753 *q++ = '>';
754 *q++ = ' ';
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){
759 q = fold_tmp;
760 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
762 /* else leave it out */
764 else{
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){
768 q = fold_tmp;
769 strncpy(q, "Folder: ", sizeof(fold_tmp)-(q-fold_tmp));
770 q += strlen(q);
771 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
772 q += strlen(q);
773 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
775 else{
776 if(fold_len + ss_len <= avail){
777 q = fold_tmp;
778 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
779 q += strlen(q);
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){
784 q = fold_tmp;
785 strncpy(q, "...", sizeof(fold_tmp));
786 q += strlen(q);
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){
791 q = fold_tmp;
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);
806 avail -= fold_len;
808 /* center folder in its space */
809 if(avail){
810 int inc;
812 inc = HRU(avail);
814 s3 += inc;
815 avail -= inc;
817 s4 += avail;
822 plus = "";
824 if(as.style != FileTextPercent && as.style != TextPercent){
825 NETMBX mb;
827 if(as.stream
828 && as.stream->mailbox
829 && mail_valid_net_parse(as.stream->mailbox, &mb)
830 && (mb.sslflag || mb.tlsflag))
831 plus = "+";
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;
841 if(loc2_len > 0)
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",
849 s1, s1, "",
850 ver_len, ver_len, version,
851 s2, s2, "",
852 title_len, title_len, title,
853 s3, s3, "",
854 fold_len, fold_len, fold_tmp,
855 s4, s4, "",
856 loc1_len, loc1_len, loc1,
857 s5, s5, "",
858 loc2_len, loc2_len, loc2,
859 s6, s6, plus);
861 return(&as.titlecontainer);
865 char
866 sort_letter(SortOrder sort)
868 char let = 'A', *p;
870 if((p = sort_name(sort)) != NULL && *p){
871 while(*(p+1) && islower((unsigned char) *p))
872 p++;
874 if(*p)
875 let = *p;
878 return(let);
883 * Update the titlebar line if the message number changed
885 * Args: None, uses state setup on previous call to set_titlebar.
887 void
888 update_titlebar_message(void)
890 long curnum, maxnum, oldnum;
891 PINETHRD_S *thrd = NULL;
892 COLOR_PAIR *lastc = NULL, *titlecolor;
893 char buf[50];
894 int num_len;
895 unsigned long rawno;
897 if(ps_global->ttyo
898 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
899 titlebar_is_dirty = 1;
900 return;
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;
924 else{
925 as.current_msg = curnum;
926 maxnum = mn_get_total(as.msgmap);
929 titlecolor = &as.titlecontainer.color;
931 if(titlecolor)
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);
940 if(lastc){
941 (void)pico_set_colorp(lastc, PSC_NONE);
942 free_color_pair(&lastc);
945 fflush(stdout);
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)
959 unsigned long rawno;
960 MESSAGECACHE *mc;
961 COLOR_PAIR *lastc = NULL, *titlecolor;
963 if(ps_global->ttyo
964 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
965 titlebar_is_dirty = 1;
966 return(0);
969 if(!as.stream || as.current_msg <= 0L || as.del_column < 0)
970 return(1);
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)
984 return(1);
986 else if(mc->answered){ /* then answered */
987 if(as.msg_state & MS_ANS)
988 return(1);
989 } /* then forwarded */
990 else if(user_flag_is_set(as.stream, mc->msgno, FORWARDED_FLAG)){
991 if(as.msg_state & MS_FWD)
992 return(1);
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 */
998 return(1);
1000 else{
1001 if(as.msg_state == 0) /* nothing to change */
1002 return(1);
1005 as.msg_state = STATUS_BITS(mc);
1007 titlecolor = &as.titlecontainer.color;
1009 if(titlecolor)
1010 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1012 PutLine0(0, as.del_column, BAR_STATUS(as.msg_state));
1014 if(lastc){
1015 (void)pico_set_colorp(lastc, PSC_NONE);
1016 free_color_pair(&lastc);
1019 fflush(stdout);
1020 return(1);
1025 * Update the percentage shown in the titlebar line
1027 * Args: new_line_number -- line number to calculate new percentage
1029 void
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)
1035 return;
1037 if(ps_global->ttyo
1038 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
1039 titlebar_is_dirty = 1;
1040 return;
1043 as.current_line = new_line_number;
1045 titlecolor = &as.titlecontainer.color;
1047 if(titlecolor)
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));
1053 if(lastc){
1054 (void)pico_set_colorp(lastc, PSC_NONE);
1055 free_color_pair(&lastc);
1058 fflush(stdout);
1063 * Update the percentage AND line number shown in the titlebar line
1065 * Args: new_line_number -- line number to calculate new percentage
1067 void
1068 update_titlebar_lpercent(long int new_line_number)
1070 COLOR_PAIR *lastc = NULL, *titlecolor;
1071 int num_len;
1072 char buf[50];
1074 if(as.page_column < 0 || new_line_number == as.current_line)
1075 return;
1077 if(ps_global->ttyo
1078 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
1079 titlebar_is_dirty = 1;
1080 return;
1083 as.current_line = new_line_number;
1085 titlecolor = &as.titlecontainer.color;
1087 if(titlecolor)
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));
1098 if(lastc){
1099 (void)pico_set_colorp(lastc, PSC_NONE);
1100 free_color_pair(&lastc);
1103 fflush(stdout);
1107 /*----------------------------------------------------------------------
1108 Return static buf containing portion of lines displayed
1110 Args: part -- how much so far
1111 total -- how many total
1113 ---*/
1114 char *
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));
1129 else
1130 snprintf(percent, sizeof(percent), "%2ld%%", (100L * part)/total);
1132 percent[sizeof(percent)-1] = '\0';
1134 return(percent);
1139 * end_titlebar - free resources associated with titlebar state struct
1141 void
1142 end_titlebar(void)
1144 if(as.folder_name)
1145 fs_give((void **)&as.folder_name);
1147 if(as.context_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
1158 ----*/
1159 void
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')))
1168 return;
1169 else{
1170 if(putstr[0] == ' ')
1171 check_cue_char = '\0';
1172 else
1173 check_cue_char = putstr[0];
1176 titlecolor = &as.titlecontainer.color;
1178 if(titlecolor)
1179 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1181 PutLine0(0, 0, putstr); /* show delay cue */
1182 if(lastc){
1183 (void)pico_set_colorp(lastc, PSC_NONE);
1184 free_color_pair(&lastc);
1187 fflush(stdout);
1191 /*----------------------------------------------------------------------
1192 Mandatory function of ../pith/newmail.c
1194 Args: none
1196 Result: newmail cue displayed at upper-left-hand corner of screen
1197 ----*/
1198 void
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);
1206 #ifdef _WINDOWS
1207 if(onoroff)
1208 mswin_setcursor (MSWIN_CURSOR_BUSY);
1209 else
1210 mswin_setcursor (MSWIN_CURSOR_ARROW);
1211 #endif
1215 /*----------------------------------------------------------------------
1216 Mandatory function of ../pith/newmail.c
1218 Args: none
1220 Result: checkpoint delay cue displayed at upper-left-hand corner of screen
1221 ----*/
1222 void
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);
1230 #ifdef _WINDOWS
1231 if(onoroff)
1232 mswin_setcursor (MSWIN_CURSOR_BUSY);
1233 else
1234 mswin_setcursor (MSWIN_CURSOR_ARROW);
1235 #endif