* Update configure script according to previous change to configure.ac
[alpine.git] / alpine / titlebar.c
blob88cd5d65031560d8951913eb7dd35d73e15517d5
1 /*
2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
15 #include "headers.h"
16 #include "titlebar.h"
17 #include "folder.h"
18 #include "../pith/state.h"
19 #include "../pith/bitmap.h"
20 #include "../pith/msgno.h"
21 #include "../pith/thread.h"
22 #include "../pith/conf.h"
23 #include "../pith/init.h"
24 #include "../pith/sort.h"
25 #include "../pith/news.h"
26 #include "../pith/util.h"
27 #include "../pith/folder.h"
30 * Internal prototypes
32 int digit_count(long);
33 void output_titlebar(TITLE_S *);
34 char sort_letter(SortOrder);
35 char *percentage(long, long, int);
39 * some useful macros...
41 #define MS_DEL (0x01)
42 #define MS_NEW (0x02)
43 #define MS_ANS (0x04)
44 #define MS_FWD (0x08)
46 #define STATUS_BITS(X) (!(X && (X)->valid) ? 0 \
47 : (X)->deleted ? MS_DEL \
48 : (X)->answered ? MS_ANS \
49 : (as.stream && user_flag_is_set(as.stream, (X)->msgno, FORWARDED_FLAG)) ? MS_FWD \
50 : (as.stream \
51 && (ps_global->unseen_in_view \
52 || (!(X)->seen \
53 && (!IS_NEWS(as.stream) \
54 || F_ON(F_FAKE_NEW_IN_NEWS, \
55 ps_global))))) \
56 ? MS_NEW : 0)
58 #define BAR_STATUS(X) (((X) & MS_DEL) ? "DEL" \
59 : ((X) & MS_ANS) ? "ANS" \
60 : ((X) & MS_FWD) ? "FWD" \
61 : (as.stream \
62 && (!IS_NEWS(as.stream) \
63 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global)) \
64 && ((X) & MS_NEW)) ? "NEW" : " ")
67 static struct titlebar_state {
68 MAILSTREAM *stream;
69 MSGNO_S *msgmap;
70 char *title,
71 *folder_name,
72 *context_name;
73 long current_msg,
74 current_line,
75 current_thrd,
76 total_lines;
77 int msg_state,
78 cur_mess_col,
79 del_column,
80 percent_column,
81 page_column,
82 screen_cols,
83 pushed;
84 enum {Normal, OnlyRead, Closed} stream_status;
85 TitleBarType style;
86 TITLE_S titlecontainer;
87 } as, titlebar_stack;
90 static int titlebar_is_dirty = 1;
92 char *as_fname; /* folder name */
93 char *as_cname; /* context name */
95 void
96 push_titlebar_state(void)
98 as.pushed = 1;
99 titlebar_stack = as;
100 if(as_fname) fs_give((void **)&as_fname);
101 as_fname = cpystr(as.folder_name);
102 if(as_cname) fs_give((void **)&as_cname);
103 as_cname = cpystr(as.context_name);
104 if(as.folder_name) fs_give((void **)&as.folder_name);
105 if(as.context_name) fs_give((void **)&as.context_name);
109 void
110 pop_titlebar_state(void)
112 /* guard against case where push pushed no state */
113 if(titlebar_stack.style != TitleBarNone){
114 fs_give((void **)&(as.folder_name)); /* free malloc'd values */
115 fs_give((void **)&(as.context_name));
116 as = titlebar_stack;
117 if(as.pushed){
118 as.folder_name = as_fname ? cpystr(as_fname) : NULL;
119 as.context_name = as_cname ? cpystr(as_cname): NULL;
121 as.pushed = 0;
127 digit_count(long int n)
129 int i;
131 return((n > 9)
132 ? (1 + (((i = digit_count(n / 10L)) == 3 || i == 7) ? i+1 : i))
133 : 1);
137 void
138 mark_titlebar_dirty(void)
140 titlebar_is_dirty = 1;
144 /*----------------------------------------------------------------------
145 Sets up style and contents of current titlebar line
147 All of the text is assumed to be UTF-8 text, which probably isn't
148 true yet.
150 Args: title -- The title that appears in the center of the line
151 This title is in UTF-8 characters.
152 display_on_screen -- flag whether to display on screen or generate
153 string
154 style -- The format/style of the titlebar line
155 msgmap -- MSGNO_S * to selected message map
156 current_pl -- The current page or line number
157 total_pl -- The total line or page count
159 Set the contents of the anchor line. It's called an anchor line
160 because it's always present and titlebars the user. This accesses a
161 number of global variables, but doesn't change any. There are several
162 different styles of the titlebar line.
164 It's OK to call this with a bogus current message - it is only used
165 to look up status of current message
167 Formats only change the right section (part3).
168 FolderName: "<folder>" xx Messages
169 MessageNumber: "<folder>" message x,xxx of x,xxx XXX
170 TextPercent: line xxx of xxx xx%
171 MsgTextPercent: "<folder>" message x,xxx of x,xxx xx% XXX
172 FileTextPercent: "<filename>" line xxx of xxx xx%
174 Several strings and column numbers are saved so later updates to the status
175 line for changes in message number or percentage can be done efficiently.
176 ----*/
178 char *
179 set_titlebar(char *title, MAILSTREAM *stream, CONTEXT_S *cntxt, char *folder,
180 MSGNO_S *msgmap, int display_on_screen, TitleBarType style,
181 long int current_pl, long int total_pl, COLOR_PAIR *color)
183 struct variable *vars = ps_global->vars;
184 MESSAGECACHE *mc = NULL;
185 PINETHRD_S *thrd = NULL;
186 TITLE_S *tc;
188 dprint((9, "set_titlebar - style: %d current message cnt:%ld",
189 style, mn_total_cur(msgmap)));
190 dprint((9, " current_pl: %ld total_pl: %ld\n",
191 current_pl, total_pl));
193 as.current_msg = (mn_get_total(msgmap) > 0L)
194 ? MAX(0, mn_get_cur(msgmap)) : 0L;
195 as.msgmap = msgmap;
196 as.style = style;
197 as.title = title;
198 as.stream = stream;
199 as.stream_status = (!as.stream || (sp_dead_stream(as.stream)))
200 ? Closed : as.stream->rdonly ? OnlyRead : Normal;
202 if(ps_global->first_open_was_attempted
203 && as.stream_status == Closed
204 && VAR_TITLECLOSED_FORE_COLOR && VAR_TITLECLOSED_BACK_COLOR){
205 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
206 strncpy(as.titlecontainer.color.fg,
207 VAR_TITLECLOSED_FORE_COLOR, MAXCOLORLEN);
208 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
209 strncpy(as.titlecontainer.color.bg,
210 VAR_TITLECLOSED_BACK_COLOR, MAXCOLORLEN);
211 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
213 else{
214 if(color){
215 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
216 if(color->fg){
217 strncpy(as.titlecontainer.color.fg, color->fg, MAXCOLORLEN);
218 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
221 if(color->bg){
222 strncpy(as.titlecontainer.color.bg, color->bg, MAXCOLORLEN);
223 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
226 else{
227 memset(&as.titlecontainer.color, 0, sizeof(as.titlecontainer.color));
228 if(VAR_TITLE_FORE_COLOR){
229 strncpy(as.titlecontainer.color.fg,
230 VAR_TITLE_FORE_COLOR, MAXCOLORLEN);
231 as.titlecontainer.color.fg[MAXCOLORLEN] = '\0';
234 if(VAR_TITLE_BACK_COLOR){
235 strncpy(as.titlecontainer.color.bg,
236 VAR_TITLE_BACK_COLOR, MAXCOLORLEN);
237 as.titlecontainer.color.bg[MAXCOLORLEN] = '\0';
242 if(as.folder_name)
243 fs_give((void **)&as.folder_name);
245 if(folder){
246 unsigned char *fname;
247 fname = folder_name_decoded((unsigned char *) folder);
248 if(!strucmp(folder, ps_global->inbox_name) && cntxt == ps_global->context_list)
249 as.folder_name = cpystr(pretty_fn(fname ? (char *) fname : folder));
250 else
251 as.folder_name = cpystr(fname ? (char *) fname : folder);
252 if(fname) fs_give((void **)&fname);
255 if(!as.folder_name)
256 as.folder_name = cpystr("");
258 if(as.context_name)
259 fs_give((void **)&as.context_name);
262 * Handle setting up the context if appropriate.
264 if(cntxt && context_isambig(folder) && ps_global->context_list->next
265 && (strucmp(as.folder_name, ps_global->inbox_name) || cntxt != ps_global->context_list)){
267 * if there's more than one context and the current folder
268 * is in it (ambiguous name), set the context name...
270 as.context_name = cpystr(cntxt->nickname
271 ? cntxt->nickname
272 : cntxt->context);
275 if(!as.context_name)
276 as.context_name = cpystr("");
278 if(as.stream && style != FolderName
279 && style != ThrdIndex && as.current_msg > 0L) {
280 long rawno;
282 if((rawno = mn_m2raw(msgmap, as.current_msg)) > 0L
283 && rawno <= as.stream->nmsgs
284 && !((mc = mail_elt(as.stream, rawno)) && mc->valid)){
285 pine_mail_fetch_flags(as.stream, long2string(rawno), NIL);
286 if(rawno <= as.stream->nmsgs && as.stream && rawno > 0L)
287 mc = mail_elt(as.stream, rawno);
288 else
289 mc = NULL;
290 if(mc && !mc->valid)
291 mc = NULL;
295 if(style == ThrdIndex || style == ThrdMsgNum || style == ThrdMsgPercent){
296 if(mn_get_total(msgmap) > 0L)
297 thrd = fetch_thread(stream, mn_m2raw(msgmap, mn_get_cur(msgmap)));
299 if(thrd && thrd->top && thrd->top != thrd->rawno)
300 thrd = fetch_thread(stream, thrd->top);
302 if(thrd)
303 as.current_thrd = thrd->thrdno;
306 switch(style) {
307 case ThrdIndex:
308 as.total_lines = msgmap->max_thrdno;
309 break;
311 case TextPercent:
312 case MsgTextPercent:
313 case FileTextPercent :
314 case ThrdMsgPercent:
315 as.total_lines = total_pl;
316 as.current_line = current_pl;
317 /* fall through to set state */
318 case ThrdMsgNum:
319 case MessageNumber:
320 as.msg_state = STATUS_BITS(mc);
322 case FolderName: /* nothing more to do for this one */
323 break;
324 default:
325 break;
328 tc = format_titlebar();
329 if(display_on_screen)
330 output_titlebar(tc);
332 return(tc->titlebar_line);
336 void
337 redraw_titlebar(void)
339 output_titlebar(format_titlebar());
343 void
344 output_titlebar(TITLE_S *tc)
346 COLOR_PAIR *lastc = NULL, *newcolor;
348 if(ps_global->ttyo
349 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
350 titlebar_is_dirty = 1;
351 return;
354 newcolor = tc ? &tc->color : NULL;
356 if(newcolor)
357 lastc = pico_set_colorp(newcolor, PSC_REV | PSC_RET);
359 if(tc && tc->titlebar_line)
360 PutLine0(0, 0, tc->titlebar_line);
362 if (ps_global->ttyo)
363 MoveCursor(0, ps_global->ttyo->screen_cols); /* move to the last column */
365 if(lastc){
366 (void)pico_set_colorp(lastc, PSC_NONE);
367 free_color_pair(&lastc);
370 fflush(stdout);
374 void
375 titlebar_stream_closing(MAILSTREAM *stream)
377 if(stream == as.stream)
378 as.stream = NULL;
382 /* caller should free returned color pair */
383 COLOR_PAIR *
384 current_titlebar_color(void)
386 COLOR_PAIR *col;
387 COLOR_PAIR *the_color = NULL;
389 col = as.title ? &as.titlecontainer.color : NULL;
391 if(col && col->fg && col->fg[0] && col->bg && col->bg[0])
392 the_color = new_color_pair(col->fg, col->bg);
394 return(the_color);
398 /*----------------------------------------------------------------------
399 Redraw or draw the top line, the title bar
401 The titlebar has Four fields:
402 1) "Version" of fixed length and is always positioned two spaces
403 in from left display edge.
404 2) "Location" which is fixed for each style of titlebar and
405 is positioned two spaces from the right display edge
406 3) "Title" which is of fixed length, and is centered if
407 there's space
408 4) "Folder" whose existence depends on style and which can
409 have it's length adjusted (within limits) so it will
410 equally share the space between 1) and 2) with the
411 "Title". The rule for existence is that in the
412 space between 1) and 2) there must be one space between
413 3) and 4) AND at least 50% of 4) must be displayed.
414 However, if the folder name can be displayed, then do
415 so, and display as much as possible of the collection name.
417 Returns - Formatted title bar
418 ----*/
419 TITLE_S *
420 format_titlebar(void)
422 char version[50], fold_tmp[6*MAXPATH+1], *titlebar_line,
423 loc1[200], loc_label[10], *thd_label, *ss_string, *q,
424 *plus, *loc2 = "", title[200];
425 int title_len = 0, ver_len, loc1_len = 0, loc2_len = 0, fold_len = 0, num_len,
426 s1 = 0, s2 = 0, s3 = 0, s4 = 0, s5 = 0, s6 = 0, tryloc = 1,
427 cur_mess_col_offset = -1, percent_column_offset = -1, page_column_offset = -1,
428 ss_len, thd_len, is_context, avail, extra;
430 titlebar_is_dirty = 0;
432 if(!as.title)
433 return NULL;
435 if(!as.folder_name)
436 as.folder_name = cpystr("");
438 if(!as.context_name)
439 as.context_name = cpystr("");
442 * Full blown title looks like:
444 * | LV vers VT title TF folder FL location LR |
446 #define LV 2 /* space between Left edge and Version, must be >= 2 */
447 #define VT 3 /* space between Version and Title */
448 #define TF 1 /* space between Title and Folder */
449 #define FL 2 /* space between Folder and Location */
450 #define LR 2 /* space between Location and Right edge, >= 2 */
451 /* half of n but round up */
452 #define HRU(n) (((n) <= 0) ? 0 : (((n)%2) ? ((n)+1)/2 : (n)/2))
453 /* half of n but round down */
454 #define HRD(n) (((n) <= 0) ? 0 : ((n)/2))
456 titlebar_line = as.titlecontainer.titlebar_line;
458 avail = MIN(ps_global->ttyo->screen_cols, MAX_SCREEN_COLS);
460 /* initialize */
461 as.del_column = -1;
462 as.cur_mess_col = -1;
463 as.percent_column = -1;
464 as.page_column = -1;
466 is_context = as.context_name ? strlen(as.context_name) : 0;
468 snprintf(version, sizeof(version), "ALPINE %s", ALPINE_VERSION);
469 version[sizeof(version)-1] = '\0';
470 ver_len = (int) utf8_width(version); /* fixed version field width */
472 title[0] = '\0';
473 if(as.title){
474 strncpy(title, as.title, sizeof(title));
475 title[sizeof(title)-1] = '\0';
478 /* Add Sort indicator to title */
479 if(F_ON(F_SHOW_SORT, ps_global) &&
480 !(as.style == FileTextPercent || as.style == TextPercent)){
481 SortOrder current_sort;
482 int current_rev;
483 char let;
485 current_sort = mn_get_sort(ps_global->msgmap);
486 current_rev = mn_get_revsort(ps_global->msgmap);
488 /* turn current_sort into a letter */
489 let = sort_letter(current_sort);
490 if(let == 'A' && current_rev){
491 let = 'R';
492 current_rev = 0;
495 snprintf(title+strlen(title), sizeof(title)-strlen(title),
496 " [%s%c]", current_rev ? "R" : "", let);
497 title[sizeof(title)-1] = '\0';
500 title_len = (int) utf8_width(title); /* title field width */
502 s1 = MAX(MIN(LV, avail), 0); /* left two spaces */
503 avail -= s1;
505 s6 = MAX(MIN(LR, avail), 0); /* right two spaces */
506 avail -= s6;
508 title_len = MAX(MIN(title_len, avail), 0);
509 avail -= title_len;
511 if(ver_len + VT > avail){ /* can only fit title */
512 ver_len = 0;
514 s2 = MAX(MIN(HRD(avail), avail), 0);
515 avail -= s2;
517 s3 = MAX(0, avail);
519 else{
520 s2 = MAX(MIN(VT, avail), 0);
521 avail -= s2;
523 ver_len = MAX(MIN(ver_len, avail), 0);
524 avail -= ver_len;
526 try_smaller_loc:
529 * set location field's length and value based on requested style
531 if(as.style == ThrdIndex)
532 /* TRANSLATORS: In titlebar, Thd is an abbreviation for Thread, Msg for Message.
533 They are used when there isn't enough space so need to be short.
534 The formatting isn't very flexible. These come before the number
535 of the message or thread, as in
536 Message 17
537 when reading message number 17. */
538 snprintf(loc_label, sizeof(loc_label), "%s ", (is_context || tryloc==2) ? _("Thd") : _("Thread"));
539 else
540 snprintf(loc_label, sizeof(loc_label), "%s ", (is_context || tryloc==2) ? _("Msg") : _("Message"));
542 if(tryloc == 3 && as.style != FolderName && mn_get_total(as.msgmap))
543 loc_label[0] = '\0';
545 loc_label[sizeof(loc_label)-1] = '\0';
547 if(as.style == ThrdMsgNum || as.style == ThrdMsgPercent){
548 thd_label = is_context ? _("Thd") : _("Thread");
549 thd_len = (int) utf8_width(thd_label);
552 loc1_len = (int) utf8_width(loc_label); /* initial length */
554 if(!mn_get_total(as.msgmap)){
555 loc_label[strlen(loc_label)-1] = 's';
556 snprintf(loc1, sizeof(loc1), "%s %s", _("No"), loc_label);
557 loc1[sizeof(loc1)-1]= '\0';
559 else{
560 switch(as.style){
561 case FolderName : /* "x,xxx <loc_label>s" */
562 if(*loc_label){
563 if(mn_get_total(as.msgmap) != 1)
564 loc_label[strlen(loc_label)-1] = 's';
565 else
566 loc_label[strlen(loc_label)-1] = '\0';
569 snprintf(loc1, sizeof(loc1), "%s %s", comatose(mn_get_total(as.msgmap)), loc_label);
570 loc1[sizeof(loc1)-1]= '\0';
571 break;
573 case MessageNumber : /* "<loc_label> xxx of xxx DEL" */
574 num_len = strlen(comatose(mn_get_total(as.msgmap)));
575 cur_mess_col_offset = loc1_len;
576 snprintf(loc1, sizeof(loc1), "%s%*.*s of %s", loc_label,
577 num_len, num_len,
578 comatose(as.current_msg),
579 comatose(mn_get_total(as.msgmap)));
580 loc1[sizeof(loc1)-1]= '\0';
581 loc2 = BAR_STATUS(as.msg_state);
582 loc2_len = 3;
583 break;
585 case ThrdIndex : /* "<loc_label> xxx of xxx" */
586 num_len = strlen(comatose(as.total_lines));
587 cur_mess_col_offset = loc1_len;
588 snprintf(loc1, sizeof(loc1), "%s%*.*s of %s", loc_label,
589 num_len, num_len,
590 comatose(as.current_thrd),
591 comatose(as.total_lines));
592 loc1[sizeof(loc1)-1]= '\0';
593 break;
595 case ThrdMsgNum : /* "<loc_label> xxx in Thd xxx DEL" */
596 num_len = strlen(comatose(mn_get_total(as.msgmap)));
597 cur_mess_col_offset = loc1_len;
598 snprintf(loc1, sizeof(loc1), "%s%*.*s in %s %s", loc_label,
599 num_len, num_len,
600 comatose(as.current_msg),
601 thd_label,
602 comatose(as.current_thrd));
603 loc1[sizeof(loc1)-1]= '\0';
604 loc2 = BAR_STATUS(as.msg_state);
605 loc2_len = 3;
606 break;
608 case MsgTextPercent : /* "<loc_label> xxx of xxx xx% DEL" */
609 num_len = strlen(comatose(mn_get_total(as.msgmap)));
610 cur_mess_col_offset = loc1_len;
611 percent_column_offset = 3;
612 snprintf(loc1, sizeof(loc1), "%s%*.*s of %s %s", loc_label,
613 num_len, num_len,
614 comatose(as.current_msg),
615 comatose(mn_get_total(as.msgmap)),
616 percentage(as.current_line, as.total_lines, 1));
617 loc1[sizeof(loc1)-1]= '\0';
618 loc2 = BAR_STATUS(as.msg_state);
619 loc2_len = 3;
620 break;
622 case ThrdMsgPercent : /* "<loc_label> xxx in Thd xxx xx% DEL" */
623 num_len = strlen(comatose(mn_get_total(as.msgmap)));
624 cur_mess_col_offset = loc1_len;
625 percent_column_offset = 3;
626 snprintf(loc1, sizeof(loc1), "%s%*.*s in %s %s %s", loc_label,
627 num_len, num_len,
628 comatose(as.current_msg),
629 thd_label,
630 comatose(as.current_thrd),
631 percentage(as.current_line, as.total_lines, 1));
632 loc1[sizeof(loc1)-1]= '\0';
633 loc2 = BAR_STATUS(as.msg_state);
634 loc2_len = 3;
635 break;
637 case TextPercent :
638 /* NOTE: no fold_tmp setup below for TextPercent style */
639 case FileTextPercent : /* "Line xxx of xxx xx%" */
640 num_len = strlen(comatose(as.total_lines));
641 page_column_offset = 5;
642 percent_column_offset = 3;
643 snprintf(loc1, sizeof(loc1), "Line %*.*s of %s %s",
644 num_len, num_len,
645 comatose(as.current_line),
646 comatose(as.total_lines),
647 percentage(as.current_line, as.total_lines, 1));
648 loc1[sizeof(loc1)-1]= '\0';
649 break;
650 default:
651 break;
655 loc1_len = utf8_width(loc1);
657 if(loc1_len + loc2_len + ((loc2_len > 0) ? 1 : 0) >= avail){ /* can't fit location in */
658 if(tryloc < 3){
659 tryloc++;
660 goto try_smaller_loc;
663 loc1_len = loc2_len = 0;
665 avail += s2; /* re-allocate s2 to center title */
667 s2 = MAX(MIN(HRD(avail), avail), 0);
668 avail -= s2;
670 s3 = MAX(0, avail);
672 else{
673 loc1_len = MAX(MIN(loc1_len, avail), 0);
674 avail -= loc1_len;
676 loc2_len = MAX(MIN(loc2_len, avail), 0);
677 avail -= loc2_len;
679 if(loc2_len > 0){
680 s5 = MAX(MIN(1, avail), 0);
681 avail -= s5;
683 else
684 s5 = 0;
686 s3 = MAX(MIN(TF, avail), 0);
687 avail -= s3;
689 s4 = MAX(MIN(FL, avail), 0);
690 avail -= s4;
692 if(avail){
693 /* TRANSLATORS: it might say READONLY or CLOSED in the titlebar, referring to
694 the current folder. */
695 ss_string = as.stream_status == Closed ? _("(CLOSED)") :
696 (as.stream_status == OnlyRead
697 && !IS_NEWS(as.stream))
698 ? _("(READONLY)") : "";
699 ss_len = (int) utf8_width(ss_string);
701 /* figure folder_length and what's to be displayed */
702 fold_tmp[0] = '\0';
703 if(as.style == FileTextPercent || as.style == TextPercent){
704 if(as.style == FileTextPercent){
705 extra = (int) utf8_width("File: ");
706 fold_len = (int) utf8_width(as.folder_name);
707 if(fold_len + extra <= avail){ /* all of folder fit? */
708 strncpy(fold_tmp, "File: ", sizeof(fold_tmp));
709 q = fold_tmp + strlen(fold_tmp);
710 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
712 else if(HRU(fold_len) + extra+3 <= avail){
714 * fold_tmp = ...partial_folder_name
716 strncpy(fold_tmp, "File: ...", sizeof(fold_tmp));
717 q = fold_tmp + strlen(fold_tmp);
718 (void) utf8_to_width_rhs(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(extra+3));
721 /* else leave folder/file name blank */
723 else{
724 int ct_len;
726 fold_len = (int) utf8_width(as.folder_name);
728 if(is_context
729 && as.stream_status != Closed
730 && (ct_len = (int) utf8_width(as.context_name))){
732 extra = 3; /* length from "<" ">" and SPACE */
734 if(ct_len + fold_len + ss_len + extra <= avail){
735 q = fold_tmp;
736 *q++ = '<';
737 strncpy(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp));
738 q += strlen(q);
739 *q++ = '>';
740 *q++ = ' ';
741 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
742 q += strlen(q);
743 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
745 else if(ct_len + fold_len + ss_len + extra <= avail){
746 q = fold_tmp;
747 *q++ = '<';
748 strncpy(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp));
749 q += strlen(q);
750 *q++ = '>';
751 *q++ = ' ';
752 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
753 q += strlen(q);
754 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
756 else if(HRU(ct_len) + fold_len + ss_len + extra <= avail){
757 q = fold_tmp;
758 *q++ = '<';
759 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(fold_len+ss_len+extra), 1);
760 *q++ = '>';
761 *q++ = ' ';
762 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
763 q += strlen(q);
764 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
766 else if(HRU(ct_len) + HRU(fold_len) + ss_len + extra <= avail){
767 q = fold_tmp;
768 *q++ = '<';
769 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), HRU(ct_len), 1);
770 *q++ = '>';
771 *q++ = ' ';
772 q += utf8_to_width_rhs(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(HRU(ct_len)+ss_len+extra));
773 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
775 else if(ss_len > 0 && ss_len <= avail){
776 q = fold_tmp;
777 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
778 } else if(fold_len < avail){
779 q = fold_tmp;
780 if(fold_len + 7 < avail){
781 *q++ = '<';
782 q += utf8_pad_to_width(q, as.context_name, sizeof(fold_tmp)-(q-fold_tmp), avail - fold_len - 3, 1);
783 *q++ = '>';
784 *q++ = ' ';
786 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
788 /* else leave it out */
790 else{
791 /* TRANSLATORS: the name of the open folder follows this in the titlebar */
792 extra = strlen(_("Folder: "));
793 if(fold_len + ss_len + extra <= avail){
794 q = fold_tmp;
795 strncpy(q, "Folder: ", sizeof(fold_tmp)-(q-fold_tmp));
796 q += strlen(q);
797 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
798 q += strlen(q);
799 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
801 else{
802 if(fold_len + ss_len <= avail){
803 q = fold_tmp;
804 strncpy(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp));
805 q += strlen(q);
806 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
808 else if(((HRU(fold_len) + ss_len <= avail)
809 || (avail > ss_len+3 && avail > 10)) && fold_len > 5){
810 q = fold_tmp;
811 strncpy(q, "...", sizeof(fold_tmp));
812 q += strlen(q);
813 q += utf8_to_width_rhs(q, as.folder_name, sizeof(fold_tmp)-(q-fold_tmp), avail-(3+ss_len));
814 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
816 else if(ss_len > 0 && ss_len <= avail){
817 q = fold_tmp;
818 strncpy(q, ss_string, sizeof(fold_tmp)-(q-fold_tmp));
820 /* else leave it out */
825 fold_tmp[sizeof(fold_tmp)-1] = '\0';
827 /* write title, location and, optionally, the folder name */
828 fold_len = (int) utf8_width(fold_tmp);
831 fold_len = MAX(MIN(fold_len, avail), 0);
832 avail -= fold_len;
834 /* center folder in its space */
835 if(avail){
836 int inc;
838 inc = HRU(avail);
840 s3 += inc;
841 avail -= inc;
843 s4 += avail;
848 plus = "";
850 if(as.style != FileTextPercent && as.style != TextPercent){
851 NETMBX mb;
853 if(as.stream
854 && as.stream->mailbox
855 && mail_valid_net_parse(as.stream->mailbox, &mb)
856 && (mb.sslflag || mb.tlsflag))
857 plus = "+";
861 if(loc1_len > 0 && cur_mess_col_offset >= 0)
862 as.cur_mess_col = s1+ver_len+s2+title_len+s3+fold_len+s4 + cur_mess_col_offset;
864 if(loc1_len > 0 && page_column_offset >= 0)
865 as.page_column = s1+ver_len+s2+title_len+s3+fold_len+s4 + page_column_offset;
867 if(loc2_len > 0)
868 as.del_column = s1+ver_len+s2+title_len+s3+fold_len+s4+loc1_len+s5;
870 if(loc1_len > 0 && percent_column_offset > 0)
871 as.percent_column = s1+ver_len+s2+title_len+s3+fold_len+s4+loc1_len - percent_column_offset;
874 utf8_snprintf(titlebar_line, 6*MAX_SCREEN_COLS, "%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s%-*.*w%*.*s",
875 s1, s1, "",
876 ver_len, ver_len, version,
877 s2, s2, "",
878 title_len, title_len, title,
879 s3, s3, "",
880 fold_len, fold_len, fold_tmp,
881 s4, s4, "",
882 loc1_len, loc1_len, loc1,
883 s5, s5, "",
884 loc2_len, loc2_len, loc2,
885 s6, s6, plus);
887 return(&as.titlecontainer);
891 char
892 sort_letter(SortOrder sort)
894 char let = 'A', *p;
896 if((p = sort_name(sort)) != NULL && *p){
897 while(*(p+1) && islower((unsigned char) *p))
898 p++;
900 if(*p)
901 let = *p;
904 return(let);
909 * Update the titlebar line if the message number changed
911 * Args: None, uses state setup on previous call to set_titlebar.
913 void
914 update_titlebar_message(void)
916 long curnum = 0, maxnum, oldnum = 0;
917 PINETHRD_S *thrd = NULL;
918 COLOR_PAIR *lastc = NULL, *titlecolor;
919 char buf[50];
920 int num_len;
921 unsigned long rawno;
923 if(ps_global->ttyo
924 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
925 titlebar_is_dirty = 1;
926 return;
929 if(as.style == ThrdIndex){
931 oldnum = as.current_thrd;
933 if(as.stream && (rawno=mn_m2raw(as.msgmap, mn_get_cur(as.msgmap))))
934 thrd = fetch_thread(as.stream, rawno);
936 if(thrd && thrd->top && (thrd=fetch_thread(as.stream, thrd->top)))
937 curnum = thrd->thrdno;
939 else if(as.cur_mess_col >= 0){
940 curnum = mn_get_cur(as.msgmap);
941 oldnum = as.current_msg;
944 if(as.cur_mess_col >= 0 && oldnum != curnum){
946 if(as.style == ThrdIndex){
947 as.current_thrd = curnum;
948 maxnum = as.msgmap->max_thrdno;
950 else{
951 as.current_msg = curnum;
952 maxnum = mn_get_total(as.msgmap);
955 titlecolor = &as.titlecontainer.color;
957 if(titlecolor)
958 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
960 num_len = strlen(comatose(maxnum));
962 snprintf(buf, sizeof(buf), "%*.*s", num_len, num_len, comatose(curnum));
964 PutLine0(0, as.cur_mess_col, buf);
966 if (ps_global->ttyo)
967 MoveCursor(0, ps_global->ttyo->screen_cols); /* move to the last column */
969 if(lastc){
970 (void)pico_set_colorp(lastc, PSC_NONE);
971 free_color_pair(&lastc);
974 fflush(stdout);
981 * Update titlebar line's message status field ("DEL", "NEW", etc)
983 * Args: None, operates on state set during most recent set_titlebar call
986 update_titlebar_status(void)
988 unsigned long rawno;
989 MESSAGECACHE *mc;
990 COLOR_PAIR *lastc = NULL, *titlecolor;
992 if(ps_global->ttyo
993 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
994 titlebar_is_dirty = 1;
995 return(0);
998 if(!as.stream || as.current_msg <= 0L || as.del_column < 0)
999 return(1);
1001 if(as.current_msg != mn_get_cur(as.msgmap))
1002 update_titlebar_message();
1004 mc = ((rawno = mn_m2raw(as.msgmap, as.current_msg)) > 0L
1005 && rawno <= as.stream->nmsgs)
1006 ? mail_elt(as.stream, rawno) : NULL;
1008 if(!(mc && mc->valid))
1009 return(0); /* indeterminate */
1011 if(mc->deleted){ /* deleted takes precedence */
1012 if(as.msg_state & MS_DEL)
1013 return(1);
1015 else if(mc->answered){ /* then answered */
1016 if(as.msg_state & MS_ANS)
1017 return(1);
1018 } /* then forwarded */
1019 else if(user_flag_is_set(as.stream, mc->msgno, FORWARDED_FLAG)){
1020 if(as.msg_state & MS_FWD)
1021 return(1);
1023 else if(!mc->seen && as.stream
1024 && (!IS_NEWS(as.stream)
1025 || F_ON(F_FAKE_NEW_IN_NEWS, ps_global))){
1026 if(as.msg_state & MS_NEW) /* then seen */
1027 return(1);
1029 else{
1030 if(as.msg_state == 0) /* nothing to change */
1031 return(1);
1034 as.msg_state = STATUS_BITS(mc);
1036 titlecolor = &as.titlecontainer.color;
1038 if(titlecolor)
1039 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1041 PutLine0(0, as.del_column, BAR_STATUS(as.msg_state));
1043 if (ps_global->ttyo)
1044 MoveCursor(0, ps_global->ttyo->screen_cols); /* move to the last column */
1046 if(lastc){
1047 (void)pico_set_colorp(lastc, PSC_NONE);
1048 free_color_pair(&lastc);
1051 fflush(stdout);
1052 return(1);
1057 * Update the percentage shown in the titlebar line
1059 * Args: new_line_number -- line number to calculate new percentage
1061 void
1062 update_titlebar_percent(long int new_line_number)
1064 COLOR_PAIR *lastc = NULL, *titlecolor;
1066 if(as.percent_column < 0 || new_line_number == as.current_line)
1067 return;
1069 if(ps_global->ttyo
1070 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
1071 titlebar_is_dirty = 1;
1072 return;
1075 as.current_line = new_line_number;
1077 titlecolor = &as.titlecontainer.color;
1079 if(titlecolor)
1080 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1082 PutLine0(0, as.percent_column,
1083 percentage(as.current_line, as.total_lines, 0));
1085 if (ps_global->ttyo)
1086 MoveCursor(0, ps_global->ttyo->screen_cols); /* move to the last column */
1088 if(lastc){
1089 (void)pico_set_colorp(lastc, PSC_NONE);
1090 free_color_pair(&lastc);
1093 fflush(stdout);
1098 * Update the percentage AND line number shown in the titlebar line
1100 * Args: new_line_number -- line number to calculate new percentage
1102 void
1103 update_titlebar_lpercent(long int new_line_number)
1105 COLOR_PAIR *lastc = NULL, *titlecolor;
1106 int num_len;
1107 char buf[50];
1109 if(as.page_column < 0 || new_line_number == as.current_line)
1110 return;
1112 if(ps_global->ttyo
1113 && (ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global)) < 1){
1114 titlebar_is_dirty = 1;
1115 return;
1118 as.current_line = new_line_number;
1120 titlecolor = &as.titlecontainer.color;
1122 if(titlecolor)
1123 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1125 num_len = strlen(comatose(as.total_lines));
1126 snprintf(buf, sizeof(buf), "%*.*s", num_len, num_len, comatose(as.current_line));
1128 PutLine0(0, as.page_column, buf);
1130 PutLine0(0, as.percent_column,
1131 percentage(as.current_line, as.total_lines, 0));
1133 if (ps_global->ttyo)
1134 MoveCursor(0, ps_global->ttyo->screen_cols); /* move to the last column */
1136 if(lastc){
1137 (void)pico_set_colorp(lastc, PSC_NONE);
1138 free_color_pair(&lastc);
1141 fflush(stdout);
1145 /*----------------------------------------------------------------------
1146 Return static buf containing portion of lines displayed
1148 Args: part -- how much so far
1149 total -- how many total
1151 ---*/
1152 char *
1153 percentage(long int part, long int total, int suppress_top)
1155 static char percent[4];
1157 if(total == 0L || (total <= ps_global->ttyo->screen_rows
1158 - HEADER_ROWS(ps_global)
1159 - FOOTER_ROWS(ps_global)))
1160 strncpy(percent, "ALL", sizeof(percent));
1161 else if(!suppress_top && part <= ps_global->ttyo->screen_rows
1162 - HEADER_ROWS(ps_global)
1163 - FOOTER_ROWS(ps_global))
1164 strncpy(percent, "TOP", sizeof(percent));
1165 else if(part >= total)
1166 strncpy(percent, "END", sizeof(percent));
1167 else
1168 snprintf(percent, sizeof(percent), "%2ld%%", (100L * part)/total);
1170 percent[sizeof(percent)-1] = '\0';
1172 return(percent);
1177 * end_titlebar - free resources associated with titlebar state struct
1179 void
1180 end_titlebar(void)
1182 if(as.folder_name)
1183 fs_give((void **)&as.folder_name);
1185 if(as.context_name)
1186 fs_give((void **)&as.context_name);
1190 /*----------------------------------------------------------------------
1191 Exported method to display status of mail check
1193 Args: putstr -- should be NO LONGER THAN 2 bytes
1195 Result: putstr displayed at upper-left-hand corner of screen
1196 ----*/
1197 void
1198 check_cue_display(char *putstr)
1200 COLOR_PAIR *lastc = NULL, *titlecolor;
1201 static char check_cue_char;
1203 if(ps_global->read_predicted &&
1204 (check_cue_char == putstr[0]
1205 || (putstr[0] == ' ' && putstr[1] == '\0')))
1206 return;
1207 else{
1208 if(putstr[0] == ' ')
1209 check_cue_char = '\0';
1210 else
1211 check_cue_char = putstr[0];
1214 titlecolor = &as.titlecontainer.color;
1216 if(titlecolor)
1217 lastc = pico_set_colorp(titlecolor, PSC_REV|PSC_RET);
1219 PutLine0(0, 0, putstr); /* show delay cue */
1221 if (ps_global->ttyo)
1222 MoveCursor(0, ps_global->ttyo->screen_cols); /* move to the last column */
1224 if(lastc){
1225 (void)pico_set_colorp(lastc, PSC_NONE);
1226 free_color_pair(&lastc);
1229 fflush(stdout);
1233 /*----------------------------------------------------------------------
1234 Mandatory function of ../pith/newmail.c
1236 Args: none
1238 Result: newmail cue displayed at upper-left-hand corner of screen
1239 ----*/
1240 void
1241 newmail_check_cue(int onoroff)
1243 if(F_ON(F_SHOW_DELAY_CUE, ps_global) && !ps_global->in_init_seq){
1244 check_cue_display((onoroff == TRUE) ? " *" : " ");
1245 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
1248 #ifdef _WINDOWS
1249 if(onoroff)
1250 mswin_setcursor (MSWIN_CURSOR_BUSY);
1251 else
1252 mswin_setcursor (MSWIN_CURSOR_ARROW);
1253 #endif
1257 /*----------------------------------------------------------------------
1258 Mandatory function of ../pith/newmail.c
1260 Args: none
1262 Result: checkpoint delay cue displayed at upper-left-hand corner of screen
1263 ----*/
1264 void
1265 newmail_check_point_cue(int onoroff)
1267 if(F_ON(F_SHOW_DELAY_CUE, ps_global)){
1268 check_cue_display((onoroff == TRUE) ? "**" : " ");
1269 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
1272 #ifdef _WINDOWS
1273 if(onoroff)
1274 mswin_setcursor (MSWIN_CURSOR_BUSY);
1275 else
1276 mswin_setcursor (MSWIN_CURSOR_ARROW);
1277 #endif
1280 void
1281 free_titlebar_globals(void)
1283 if(as_fname) fs_give((void **) &as_fname);
1284 if(as_cname) fs_give((void **) &as_cname);
1285 if(as.folder_name) fs_give((void **)&as.folder_name);
1286 if(as.context_name) fs_give((void **)&as.context_name);