* Deactivate some color code from Pico (as standalone editor) until
[alpine.git] / alpine / status.c
blobf8dc87aa19c4ac5392e66845c04c66652173ef3e
1 #if !defined(lint) && !defined(DOS)
2 static char rcsid[] = "$Id: status.c 840 2007-12-01 01:34:49Z hubert@u.washington.edu $";
3 #endif
5 /*
6 * ========================================================================
7 * Copyright 2006-2007 University of Washington
8 * Copyright 2013-2017 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 /*======================================================================
20 status.c
21 Functions that manage the status line (third from the bottom)
22 - put messages on the queue to be displayed
23 - display messages on the queue with timers
24 - check queue to figure out next timeout
25 - prompt for yes/no type of questions
26 ====*/
28 #include "headers.h"
29 #include "status.h"
30 #include "keymenu.h"
31 #include "mailview.h"
32 #include "mailcmd.h"
33 #include "busy.h"
34 #include "after.h"
36 #include "../pith/charconv/utf8.h"
38 #include "../pith/conf.h"
39 #include "../pith/bitmap.h"
43 * Internal queue of messages. The circular, double-linked list's
44 * allocated on demand, and cleared as each message is displayed.
46 typedef struct message {
47 char *text;
48 unsigned flags:8;
49 unsigned shown:1;
50 unsigned saw_it:1;
51 unsigned pending_removal:1;
52 int min_display_time, max_display_time;
53 struct message *next, *prev;
54 } SMQ_T;
58 * Internal prototypes
60 void pause_for_and_dq_cur_msg(void);
61 SMQ_T *top_of_queue(void);
62 int new_info_msg_need_not_be_queued(void);
63 int is_last_message(SMQ_T *);
64 void d_q_status_message(void);
65 int output_message(SMQ_T *, int);
66 int output_message_modal(SMQ_T *, int);
67 void delay_cmd_cue(int);
68 int modal_bogus_input(UCS);
69 int messages_in_queue(void);
70 int status_message_remaining_nolock(void);
71 void set_saw_it_to_zero();
72 void mark_modals_done();
73 SMQ_T *copy_status_queue(SMQ_T *);
77 /*----------------------------------------------------------------------
78 Manage the second line from the bottom where status and error messages
79 are displayed. A small queue is set up and messages are put on the queue
80 by calling one of the q_status_message routines. Even though this is a queue
81 most of the time message will go right on through. The messages are
82 displayed just before the read for the next command, or when a read times
83 out. Read timeouts occur every minute or so for new mail checking and every
84 few seconds when there are still messages on the queue. Hopefully, this scheme
85 will not let messages fly past that the user can't see.
86 ----------------------------------------------------------------------*/
89 static SMQ_T *message_queue = NULL;
90 static short needs_clearing = 0, /* Flag set by want_to()
91 and optionally_enter() */
92 prevstartcol, prevendcol;
93 static char prevstatusbuf[6*MAX_SCREEN_COLS+1];
94 static time_t displayed_time;
97 /*----------------------------------------------------------------------
98 Put a message for the status line on the queue
100 Args: time -- the min time in seconds to display the message
101 message -- message string
103 Result: queues message on queue represented by static variables
105 This puts a single message on the queue to be shown.
106 ----------*/
107 void
108 q_status_message(int flags, int min_time, int max_time, char *message)
110 SMQ_T *new, *q;
111 char *clean_msg;
112 size_t mlen;
114 status_message_lock();
117 * If there is already a message queued and
118 * new message is just informational, discard it.
120 if(flags & SM_INFO && new_info_msg_need_not_be_queued()){
121 status_message_unlock();
122 return;
126 * By convention, we have min_time equal to zero in messages which we
127 * think are not as important, so-called comfort messages. We have
128 * min_time >= 3 for messages which we think the user should see for
129 * sure. Some users don't like to wait so we've provided a way for them
130 * to live on the edge.
131 * status_msg_delay == -1 => min time == MIN(0, min_time)
132 * status_msg_delay == -2 => min time == MIN(1, min_time)
133 * status_msg_delay == -3 => min time == MIN(2, min_time)
134 * ...
136 if(ps_global->status_msg_delay < 0)
137 min_time = MIN(-1 - ps_global->status_msg_delay, min_time);
139 /* The 40 is room for 40 escaped control characters */
140 mlen = strlen(message) + 40;
141 clean_msg = (char *)fs_get(mlen + 1);
142 iutf8ncpy(clean_msg, message, mlen); /* does the cleaning */
144 clean_msg[mlen] = '\0';
146 if((q = message_queue) != NULL){ /* the queue exists */
149 * Scan through all of the messages currently in the queue.
150 * If the new message is already queued, don't add it again.
152 do {
153 if(!q->pending_removal && q->text && !strcmp(q->text, clean_msg)){
154 q->shown = 0;
155 if(q->min_display_time < min_time)
156 q->min_display_time = min_time;
158 if(q->max_display_time < max_time)
159 q->max_display_time = max_time;
161 dprint((9, "q_status_message(%s): skipping duplicate msg\n",
162 clean_msg ? clean_msg : "?"));
164 if(clean_msg)
165 fs_give((void **)&clean_msg);
167 status_message_unlock();
168 return;
171 q = q->next;
173 } while(q != message_queue);
176 new = (SMQ_T *)fs_get(sizeof(SMQ_T));
177 memset(new, 0, sizeof(SMQ_T));
178 new->text = clean_msg;
179 new->min_display_time = min_time;
180 new->max_display_time = max_time;
181 new->flags = flags;
182 if(message_queue){
183 new->next = message_queue;
184 new->prev = message_queue->prev;
185 new->prev->next = message_queue->prev = new;
187 else
188 message_queue = new->next = new->prev = new;
190 status_message_unlock();
192 dprint((9, "q_status_message(%s)\n",
193 clean_msg ? clean_msg : "?"));
197 /*----------------------------------------------------------------------
198 Mark the status line as dirty so it gets cleared next chance
199 ----*/
200 void
201 mark_status_dirty(void)
203 mark_status_unknown();
204 needs_clearing++;
208 /*----------------------------------------------------------------------
209 Cause status line drawing optimization to be turned off, because we
210 don't know what the status line looks like.
211 ----*/
212 void
213 mark_status_unknown(void)
215 prevstartcol = -1;
216 prevendcol = -1;
217 prevstatusbuf[0] = '\0';
221 /*----------------------------------------------------------------------
222 Wait a suitable amount of time for the currently displayed message
223 ----*/
224 void
225 pause_for_and_dq_cur_msg(void)
227 if(top_of_queue()){
228 int w;
230 if((w = status_message_remaining_nolock()) != 0){
231 delay_cmd_cue(1);
232 sleep(w);
233 delay_cmd_cue(0);
236 d_q_status_message();
242 * This relies on the global displayed_time being properly set
243 * for this message.
245 void
246 pause_for_and_mark_specific_msg(SMQ_T *msg)
248 if(msg){
249 int w;
251 w = (int) (displayed_time - time(0)) + msg->min_display_time;
252 w = (w > 0) ? w : 0;
253 if(w){
254 delay_cmd_cue(1);
255 sleep(w);
256 delay_cmd_cue(0);
259 msg->pending_removal = 1;
264 /*----------------------------------------------------------------------
265 Time remaining for current message's minimum display
266 ----*/
268 status_message_remaining(void)
270 int ret;
272 status_message_lock();
273 ret = status_message_remaining_nolock();
274 status_message_unlock();
276 return(ret);
281 status_message_remaining_nolock(void)
283 SMQ_T *q;
284 int d = 0;
286 if((q = top_of_queue()) != NULL)
287 d = (int) (displayed_time - time(0)) + q->min_display_time;
289 return((d > 0) ? d : 0);
294 * Return first message in queue that isn't pending_removal.
296 SMQ_T *
297 top_of_queue(void)
299 SMQ_T *p;
301 if((p = message_queue) != NULL){
303 if(!p->pending_removal)
304 return(p);
305 while((p = p->next) != message_queue);
308 return(NULL);
313 new_info_msg_need_not_be_queued(void)
315 SMQ_T *q;
317 if(status_message_remaining_nolock() > 0)
318 return 1;
320 if((q = top_of_queue()) != NULL && (q = q->next) != message_queue){
322 if(!q->pending_removal && !(q->flags & SM_INFO))
323 return 1;
324 while((q = q->next) != message_queue);
327 return 0;
331 /*----------------------------------------------------------------------
332 Find out how many messages are queued for display
334 Args: dtime -- will get set to minimum display time for current message
336 Result: number of messages in the queue.
338 ---------*/
340 messages_queued(long int *dtime)
342 SMQ_T *q;
343 int ret;
345 status_message_lock();
346 if(dtime && (q = top_of_queue()) != NULL)
347 *dtime = (long) MAX(q->min_display_time, 1L);
349 ret = ps_global->in_init_seq ? 0 : messages_in_queue();
351 status_message_unlock();
353 return(ret);
357 /*----------------------------------------------------------------------
358 Return number of messages in queue
359 ---------*/
361 messages_in_queue(void)
363 int n = 0;
364 SMQ_T *p;
366 if((p = message_queue) != NULL){
368 if(!p->pending_removal)
369 n++;
370 while((p = p->next) != message_queue);
373 return(n);
377 /*----------------------------------------------------------------------
378 Return last message queued
379 ---------*/
380 char *
381 last_message_queued(void)
383 SMQ_T *p, *r = NULL;
384 char *ret = NULL;
386 status_message_lock();
387 if((p = message_queue) != NULL){
389 if(p->flags & SM_ORDER && !p->pending_removal)
390 r = p;
391 while((p = p->next) != message_queue);
394 ret = (r && r->text) ? cpystr(r->text) : NULL;
396 status_message_unlock();
398 return(ret);
403 is_last_message(SMQ_T *msg)
405 SMQ_T *p, *r = NULL;
407 if(msg && !msg->pending_removal){
408 if((p = message_queue) != NULL){
410 if(!p->pending_removal)
411 r = p;
412 while((p = p->next) != message_queue);
416 return(r && msg && (r == msg));
420 /*----------------------------------------------------------------------
421 Update status line, clearing or displaying a message
423 Arg: command -- The command that is about to be executed
425 Result: status line cleared or
426 next message queued is displayed or
427 current message is redisplayed.
428 if next message displayed, it's min display time
429 is returned else if message already displayed, it's
430 time remaining on the display is returned, else 0.
432 This is called when ready to display the next message, usually just
433 before reading the next command from the user. We pass in the nature
434 of the command because it affects what we do here. If the command just
435 executed by the user is a redraw screen, we don't want to reset or go to
436 next message because it might not have been seen. Also if the command
437 is just a noop, which are usually executed when checking for new mail
438 and happen every few minutes, we don't clear the message.
440 If it was really a command and there's nothing more to show, then we
441 clear, because we know the user has seen the message. In this case the
442 user might be typing commands very quickly and miss a message, so
443 there is a time stamp and time check that each message has been on the
444 screen for a few seconds. If it hasn't we just return and let it be
445 taken care of next time.
446 ----------------------------------------------------------------------*/
448 display_message(UCS command)
450 SMQ_T *q, *copy_of_q;
451 int need_to_unlock;
452 int ding;
454 if(ps_global == NULL || ps_global->ttyo == NULL
455 || ps_global->ttyo->screen_rows < 1 || ps_global->in_init_seq)
456 return(0);
458 status_message_lock();
460 /*---- Deal with any previously displayed message ----*/
461 if((q = top_of_queue()) != NULL && q->shown){
462 int rv = -1;
464 if(command == ctrl('L')){ /* just repaint it, and go on */
465 mark_status_unknown();
466 mark_keymenu_dirty();
467 mark_titlebar_dirty();
468 rv = 0;
470 else{ /* ensure sufficient time's passed */
471 time_t now;
472 int diff;
474 now = time(0);
475 diff = (int)(displayed_time - now)
476 + ((command == NO_OP_COMMAND || command == NO_OP_IDLE)
477 ? q->max_display_time
478 : q->min_display_time);
479 dprint((9,
480 "STATUS: diff:%d, displayed: %ld, now: %ld\n",
481 diff, (long) displayed_time, (long) now));
482 if(diff > 0)
483 rv = diff; /* check again next time */
484 else if(is_last_message(q)
485 && (command == NO_OP_COMMAND || command == NO_OP_IDLE)
486 && q->max_display_time)
487 rv = 0; /* last msg, no cmd, has max */
490 if(rv >= 0){ /* leave message displayed? */
491 if(prevstartcol < 0){ /* need to redisplay it? */
492 ding = q->flags & SM_DING;
493 q->flags &= ~SM_DING;
494 if(q->flags & SM_MODAL && !q->shown){
495 copy_of_q = copy_status_queue(q);
496 mark_modals_done();
497 status_message_unlock();
498 output_message_modal(copy_of_q, ding);
500 else{
501 output_message(q, ding);
502 status_message_unlock();
505 else
506 status_message_unlock();
508 return(rv);
511 d_q_status_message(); /* remove it from queue and */
512 needs_clearing++; /* clear the line if needed */
515 if(!top_of_queue() && (command == ctrl('L') || needs_clearing)){
516 int inverse;
517 struct variable *vars = ps_global->vars;
518 char *last_bg = NULL;
520 dprint((9, "Clearing status line\n"));
521 inverse = InverseState(); /* already in inverse? */
522 if(inverse && pico_usingcolor() && VAR_STATUS_FORE_COLOR &&
523 VAR_STATUS_BACK_COLOR){
524 last_bg = pico_get_last_bg_color();
525 pico_set_nbg_color(); /* so ClearLine will clear in bg color */
528 ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
529 if(last_bg){
530 (void)pico_set_bg_color(last_bg);
531 if(last_bg)
532 fs_give((void **)&last_bg);
535 mark_status_unknown();
536 if(command == ctrl('L')){
537 mark_keymenu_dirty();
538 mark_titlebar_dirty();
542 need_to_unlock = 1;
544 /* display next message, weeding out 0 display times */
545 for(q = top_of_queue(); q && !q->shown; q = top_of_queue()){
546 if(q->min_display_time || is_last_message(q)){
547 displayed_time = time(0);
548 ding = q->flags & SM_DING;
549 q->flags &= ~SM_DING;
550 if(q->flags & SM_MODAL && !q->shown){
551 copy_of_q = copy_status_queue(q);
552 mark_modals_done();
553 status_message_unlock();
554 output_message_modal(copy_of_q, ding);
555 need_to_unlock = 0;
557 else
558 output_message(q, ding);
560 break;
562 /* zero display time message, log it, delete it */
563 else{
564 if(q->text){
565 char buf[1000];
566 char *append = " [not actually shown]";
567 char *ptr;
568 size_t len;
570 len = strlen(q->text) + strlen(append);
571 if(len < sizeof(buf))
572 ptr = buf;
573 else
574 ptr = (char *) fs_get((len+1) * sizeof(char));
576 strncpy(ptr, q->text, len);
577 ptr[len] = '\0';
578 strncat(ptr, append, len+1-1-strlen(ptr));
579 ptr[len] = '\0';
580 add_review_message(ptr, -1);
581 if(ptr != buf)
582 fs_give((void **) &ptr);
585 d_q_status_message();
589 needs_clearing = 0; /* always cleared or written */
590 fflush(stdout);
592 if(need_to_unlock)
593 status_message_unlock();
595 return(0);
599 /*----------------------------------------------------------------------
600 Display all the messages on the queue as quickly as possible
601 ----*/
602 void
603 flush_status_messages(int skip_last_pause)
605 SMQ_T *q, *copy_of_q;
606 int ding;
608 start_over:
609 status_message_lock();
611 for(q = top_of_queue(); q; q = top_of_queue()){
612 /* don't have to wait for this one */
613 if(is_last_message(q) && skip_last_pause && q->shown)
614 break;
616 if(q->shown)
617 pause_for_and_dq_cur_msg();
619 /* find next one we need to show and show it */
620 for(q = top_of_queue(); q && !q->shown; q = top_of_queue()){
621 if((q->min_display_time || is_last_message(q))){
622 displayed_time = time(0);
623 ding = q->flags & SM_DING;
624 q->flags &= ~SM_DING;
625 if(q->flags & SM_MODAL){
626 copy_of_q = copy_status_queue(q);
627 mark_modals_done();
628 status_message_unlock();
629 output_message_modal(copy_of_q, ding);
632 * Because we unlock the message queue in order
633 * to display modal messages we have to worry
634 * about the queue being changed while we have
635 * it unlocked. So start the whole process over.
637 goto start_over;
639 else
640 output_message(q, ding);
642 else{
643 d_q_status_message();
648 status_message_unlock();
652 /*----------------------------------------------------------------------
653 Make sure any and all SM_ORDER messages get displayed.
655 Note: This flags the message line as having nothing displayed.
656 The idea is that it's a function called by routines that want
657 the message line for a prompt or something, and that they're
658 going to obliterate the message anyway.
659 ----*/
660 void
661 flush_ordered_messages(void)
663 SMQ_T *q, *copy_of_q;
664 int firsttime = 1;
665 int ding;
667 status_message_lock();
669 set_saw_it_to_zero();
671 start_over2:
672 if(!firsttime)
673 status_message_lock();
674 else
675 firsttime = 0;
677 if((q = message_queue) != NULL){
679 if(q->pending_removal || q->saw_it || q->shown){
680 if(!q->pending_removal && q->shown)
681 pause_for_and_mark_specific_msg(q);
683 q->saw_it = 1;
684 q = (q->next != message_queue) ? q->next : NULL;
686 else{
687 /* find next one we need to show and show it */
689 if(q->pending_removal || q->saw_it || q->shown){
690 q->saw_it = 1;
691 q = (q->next != message_queue) ? q->next : NULL;
693 else{
694 if((q->flags & (SM_ORDER | SM_MODAL))
695 && q->min_display_time){
696 displayed_time = time(0);
697 ding = q->flags & SM_DING;
698 q->flags &= ~SM_DING;
699 if(q->flags & SM_MODAL){
700 copy_of_q = copy_status_queue(q);
701 mark_modals_done();
702 q->saw_it = 1;
703 status_message_unlock();
704 output_message_modal(copy_of_q, ding);
705 goto start_over2;
707 else{
708 output_message(q, ding);
711 else{
712 q->saw_it = 1;
713 if(!(q->flags & SM_ASYNC))
714 q->pending_removal = 1;
716 q = (q->next != message_queue) ? q->next : NULL;
719 }while(q && !q->shown);
721 }while(q);
724 status_message_unlock();
728 void
729 set_saw_it_to_zero()
731 SMQ_T *q;
733 /* set saw_it to zero */
734 if((q = message_queue) != NULL){
736 q->saw_it = 0;
737 q = (q->next != message_queue) ? q->next : NULL;
738 }while(q);
743 void
744 mark_modals_done()
746 SMQ_T *q;
748 /* set shown to one */
749 if((q = message_queue) != NULL){
751 if(q->flags & SM_MODAL){
752 q->shown = 1;
753 q->pending_removal = 1;
756 q = (q->next != message_queue) ? q->next : NULL;
757 }while(q);
763 * Caller needs to free the memory.
765 SMQ_T *
766 copy_status_queue(SMQ_T *start)
768 SMQ_T *q, *new, *head = NULL;
770 if((q = start) != NULL){
772 new = (SMQ_T *) fs_get(sizeof(SMQ_T));
773 *new = *q;
775 if(q->text)
776 new->text = cpystr(q->text);
777 else
778 new->text = NULL;
780 if(head){
781 new->next = head;
782 new->prev = head->prev;
783 new->prev->next = head->prev = new;
785 else
786 head = new->next = new->prev = new;
788 q = (q->next != start) ? q->next : NULL;
789 }while(q);
792 return(head);
796 /*----------------------------------------------------------------------
797 Removes the first message from the message queue.
798 Returns 0 if all was well, -1 if had to skip the removal and
799 message_queue is unchanged.
800 ----*/
801 void
802 d_q_status_message(void)
804 int the_last_one = 0;
805 SMQ_T *q, *p, *last_one;
807 /* mark the top one for removal */
808 if((p = top_of_queue()) != NULL)
809 p->pending_removal = 1;
811 if(message_queue){
813 /* flush out pending removals */
814 the_last_one = 0; /* loop control */
815 q = message_queue;
816 last_one = message_queue->prev;
817 while(!the_last_one){
818 if(q == last_one)
819 the_last_one++;
821 if(q->pending_removal){
822 p = q;
823 if(q == q->next){ /* last item in queue */
824 q = message_queue = NULL;
826 else{
827 p->next->prev = p->prev;
828 q = p->prev->next = p->next; /* here's the increment */
829 if(message_queue == p)
830 message_queue = q;
833 if(p){
834 if(p->text)
835 fs_give((void **) &p->text);
837 fs_give((void **) &p);
840 else
841 q = q->next;
847 /*----------------------------------------------------------------------
848 Actually output the message to the screen
850 Args: message -- The message to output
851 from_alarm_handler -- Called from alarm signal handler.
852 We don't want to add this message to the review
853 message list since we may mess with the malloc
854 arena here, and the interrupt may be from
855 the middle of something malloc'ing.
856 ----*/
858 status_message_write(char *message, int from_alarm_handler)
860 int col, row, max_width, invert;
861 int bytes;
862 char newstatusbuf[6*MAX_SCREEN_COLS + 1];
863 struct variable *vars = ps_global->vars;
864 COLOR_PAIR *lastc = NULL, *newc;
866 if(!from_alarm_handler)
867 add_review_message(message, -1);
869 invert = !InverseState(); /* already in inverse? */
870 row = MAX(0, ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
872 /* Put [] around message and truncate to screen width */
873 max_width = ps_global->ttyo != NULL ? ps_global->ttyo->screen_cols : 80;
874 max_width = MIN(max_width, MAX_SCREEN_COLS);
875 newstatusbuf[0] = '[';
876 newstatusbuf[1] = '\0';
878 bytes = utf8_to_width(newstatusbuf+1, message, sizeof(newstatusbuf)-1, max_width-2, NULL);
879 newstatusbuf[1+bytes] = ']';
880 newstatusbuf[1+bytes+1] = '\0';
882 if(prevstartcol == -1 || strcmp(newstatusbuf, prevstatusbuf)){
883 UCS *prevbuf = NULL, *newbuf = NULL;
884 size_t plen;
886 if(prevstartcol != -1){
887 prevbuf = utf8_to_ucs4_cpystr(prevstatusbuf);
888 newbuf = utf8_to_ucs4_cpystr(newstatusbuf);
892 * Simple optimization. If the strings are the same length
893 * and width just skip leading and trailing strings of common
894 * characters and only write whatever's in between. Otherwise,
895 * write out the whole thing.
896 * We could do something more complicated but the odds of
897 * getting any optimization goes way down if they aren't the
898 * same length and width and the complexity goes way up.
900 if(prevbuf && newbuf
901 && (plen=ucs4_strlen(prevbuf)) == ucs4_strlen(newbuf)
902 && ucs4_str_width(prevbuf) == ucs4_str_width(newbuf)){
903 UCS *start_of_unequal, *end_of_unequal;
904 UCS *pprev, *endnewbuf;
905 char *to_screen = NULL;
906 int column;
908 pprev = prevbuf;
909 start_of_unequal = newbuf;
910 endnewbuf = newbuf + plen;
911 col = column = prevstartcol;
913 while(start_of_unequal < endnewbuf && (*start_of_unequal) == (*pprev)){
914 int w;
916 w = wcellwidth(*start_of_unequal);
917 if(w >= 0)
918 column += w;
920 pprev++;
921 start_of_unequal++;
924 end_of_unequal = endnewbuf-1;
925 pprev = prevbuf + plen - 1;
927 while(end_of_unequal > start_of_unequal && (*end_of_unequal) == (*pprev)){
928 *end_of_unequal = '\0';
929 pprev--;
930 end_of_unequal--;
933 if(pico_usingcolor() && VAR_STATUS_FORE_COLOR &&
934 VAR_STATUS_BACK_COLOR &&
935 pico_is_good_color(VAR_STATUS_FORE_COLOR) &&
936 pico_is_good_color(VAR_STATUS_BACK_COLOR)){
937 lastc = pico_get_cur_color();
938 if(lastc){
939 newc = new_color_pair(VAR_STATUS_FORE_COLOR,
940 VAR_STATUS_BACK_COLOR);
941 (void)pico_set_colorp(newc, PSC_NONE);
942 free_color_pair(&newc);
945 else if(invert)
946 StartInverse();
948 /* PutLine wants UTF-8 string */
949 if(start_of_unequal && (*start_of_unequal))
950 to_screen = ucs4_to_utf8_cpystr(start_of_unequal);
952 if(to_screen){
953 PutLine0(row, column, to_screen);
954 fs_give((void **) &to_screen);
956 strncpy(prevstatusbuf, newstatusbuf, sizeof(prevstatusbuf));
957 prevstatusbuf[sizeof(prevstatusbuf)-1] = '\0';
960 if(lastc){
961 (void)pico_set_colorp(lastc, PSC_NONE);
962 free_color_pair(&lastc);
964 else if(invert)
965 EndInverse();
967 else{
968 if(pico_usingcolor())
969 lastc = pico_get_cur_color();
971 if(!invert && pico_usingcolor() && VAR_STATUS_FORE_COLOR &&
972 VAR_STATUS_BACK_COLOR &&
973 pico_is_good_color(VAR_STATUS_FORE_COLOR) &&
974 pico_is_good_color(VAR_STATUS_BACK_COLOR))
975 pico_set_nbg_color(); /* so ClearLine uses bg color */
977 ClearLine(row);
979 if(pico_usingcolor() && VAR_STATUS_FORE_COLOR &&
980 VAR_STATUS_BACK_COLOR &&
981 pico_is_good_color(VAR_STATUS_FORE_COLOR) &&
982 pico_is_good_color(VAR_STATUS_BACK_COLOR)){
983 if(lastc){
984 newc = new_color_pair(VAR_STATUS_FORE_COLOR,
985 VAR_STATUS_BACK_COLOR);
986 (void)pico_set_colorp(newc, PSC_NONE);
987 free_color_pair(&newc);
990 else if(invert){
991 if(lastc)
992 free_color_pair(&lastc);
994 StartInverse();
997 col = Centerline(row, newstatusbuf);
999 if(lastc){
1000 (void)pico_set_colorp(lastc, PSC_NONE);
1001 free_color_pair(&lastc);
1003 else if(invert)
1004 EndInverse();
1006 strncpy(prevstatusbuf, newstatusbuf, sizeof(prevstatusbuf));
1007 prevstatusbuf[sizeof(prevstatusbuf)-1] = '\0';
1008 prevstartcol = col;
1009 prevendcol = col + utf8_width(prevstatusbuf) - 1;
1012 /* move cursor to a consistent position */
1013 if(F_ON(F_SHOW_CURSOR, ps_global))
1014 MoveCursor(row, MIN(MAX(0,col+1),ps_global->ttyo->screen_cols-1));
1015 else
1016 MoveCursor(row, 0);
1018 fflush(stdout);
1020 if(prevbuf)
1021 fs_give((void **) &prevbuf);
1023 if(newbuf)
1024 fs_give((void **) &newbuf);
1026 else
1027 col = prevstartcol;
1029 return(col);
1033 /*----------------------------------------------------------------------
1034 Write the given status message to the display.
1036 Args: mq_entry -- pointer to message queue entry to write.
1038 ----*/
1039 int
1040 output_message(SMQ_T *mq_entry, int ding)
1042 int col = 0;
1044 dprint((9, "output_message(%s)\n",
1045 (mq_entry && mq_entry->text) ? mq_entry->text : "?"));
1047 if(ding && F_OFF(F_QUELL_BEEPS, ps_global)){
1048 Writechar(BELL, 0); /* ring bell */
1049 fflush(stdout);
1052 if(!(mq_entry->flags & SM_MODAL)){
1053 col = status_message_write(mq_entry->text, 0);
1054 if(ps_global->status_msg_delay > 0){
1055 if(F_ON(F_SHOW_CURSOR, ps_global))
1056 /* col+1 because col is "[" character */
1057 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global),
1058 MIN(MAX(0,col+1),ps_global->ttyo->screen_cols-1));
1059 else
1060 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
1062 fflush(stdout);
1063 sleep(ps_global->status_msg_delay);
1066 mq_entry->shown = 1;
1069 return(col);
1074 * This is split off from output_message due to the locking
1075 * on the status message queue data. This calls scrolltool
1076 * which can call back into q_status and so on. So instead of
1077 * keeping the queue locked for a long time we copy the
1078 * data, unlock the queue, and display the data.
1080 int
1081 output_message_modal(SMQ_T *mq_entry, int ding)
1083 int rv = 0;
1084 SMQ_T *m, *mnext;
1086 if(!mq_entry)
1087 return(rv);
1089 dprint((9, "output_message_modal(%s)\n",
1090 (mq_entry && mq_entry->text) ? mq_entry->text : "?"));
1092 if(ding && F_OFF(F_QUELL_BEEPS, ps_global)){
1093 Writechar(BELL, 0); /* ring bell */
1094 fflush(stdout);
1097 if(!mq_entry->shown){
1098 int i = 0,
1099 pad = MAX(0, (ps_global->ttyo->screen_cols - 59) / 2);
1100 char *p, *q, *s, *t;
1101 SCROLL_S sargs;
1103 /* Count the number of modal messsages and add up their lengths. */
1104 for(m = mq_entry->next; m != mq_entry; m = m->next)
1105 if((m->flags & SM_MODAL) && !m->shown){
1106 i++;
1109 snprintf(tmp_20k_buf, SIZEOF_20KBUF, "%*s%s\n%*s%s\n%*s%s\n%*s%s\n%*s%s\n%*s%s\n%*s%s\n\n",
1110 /* 1 2 3 4 5 6*/
1111 /*23456789012345678901234567890123456789012345678901234567890*/
1112 pad, "",
1113 "***********************************************************",
1114 pad, "", i ?
1115 "* What follows are advisory messages. After reading them *" :
1116 "* What follows is an advisory message. After reading it *",
1118 pad, "",
1119 "* simply hit \"Return\" to continue your Alpine session. *",
1120 pad, "",
1121 "* *",
1122 pad, "", i ?
1123 "* To review these messages later, press 'J' from the *" :
1124 "* To review this message later, press 'J' from the *",
1125 pad, "",
1126 "* MAIN MENU. *",
1127 pad, "",
1128 "***********************************************************");
1129 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1130 t = tmp_20k_buf + strlen(tmp_20k_buf);
1132 m = mq_entry;
1134 if((m->flags & SM_MODAL) && !m->shown){
1135 int indent;
1137 indent = ps_global->ttyo->screen_cols > 80
1138 ? (ps_global->ttyo->screen_cols - 80) / 3 : 0;
1140 if(t - tmp_20k_buf > 19000){
1141 snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n%*s* * * Running out of buffer space * * *", indent, "");
1142 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1143 t += strlen(t);
1144 snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n%*s* * * Press RETURN for more messages * * *", indent, "");
1145 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1146 break;
1149 add_review_message(m->text, -1);
1151 if((p = strstr(m->text, "[ALERT]")) != NULL){
1152 snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "%*.*s\n", (int)(indent + p - m->text), (int) (p - m->text), m->text);
1153 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1154 t += strlen(t);
1156 for(p += 7; *p && isspace((unsigned char)*p); p++)
1158 indent += 8;
1160 else{
1161 p = m->text;
1164 while(strlen(p) > ps_global->ttyo->screen_cols - 2 * indent){
1165 for(q = p + ps_global->ttyo->screen_cols - 2 * indent;
1166 q > p && !isspace((unsigned char)*q); q--)
1169 snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n%*.*s", (int) (indent + q - p), (int) (q - p), p);
1170 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1171 t += strlen(t);
1172 p = q + 1;
1175 snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n%*s%s", indent, "", p);
1176 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1177 t += strlen(t);
1179 if(i--){
1180 snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n\n%*s\n", pad + 30, "- - -");
1181 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1182 t += strlen(t);
1185 m = m->next;
1186 } while(m != mq_entry);
1188 s = cpystr(tmp_20k_buf);
1189 ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
1191 memset(&sargs, 0, sizeof(SCROLL_S));
1192 sargs.text.text = s;
1193 sargs.text.src = CharStar;
1194 sargs.bar.title = "Status Message";
1195 sargs.bogus_input = modal_bogus_input;
1196 sargs.no_stat_msg = 1;
1197 sargs.keys.menu = &modal_message_keymenu;
1198 setbitmap(sargs.keys.bitmap);
1200 scrolltool(&sargs);
1202 fs_give((void **)&s);
1203 ps_global->mangled_screen = 1;
1206 /* free the passed in queue */
1207 m = mq_entry;
1209 if(m->text)
1210 fs_give((void **) &m->text);
1212 mnext = m->next;
1213 fs_give((void **) &m);
1214 m = mnext;
1215 } while(m != mq_entry);
1217 return(rv);
1222 /*----------------------------------------------------------------------
1223 Write or clear delay cue
1225 Args: on -- whether to turn it on or not
1227 ----*/
1228 void
1229 delay_cmd_cue(int on)
1231 COLOR_PAIR *lastc;
1232 struct variable *vars = ps_global->vars;
1234 if(prevstartcol >= 0 && prevendcol < ps_global->ttyo->screen_cols){
1235 MoveCursor(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global),
1236 MAX(prevstartcol - 1, 0));
1237 lastc = pico_set_colors(VAR_STATUS_FORE_COLOR, VAR_STATUS_BACK_COLOR,
1238 PSC_REV|PSC_RET);
1239 Write_to_screen(on ? (prevstartcol ? "[>" : ">")
1240 : (prevstartcol ? " [" : "["));
1242 MoveCursor(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global),
1243 prevendcol);
1245 Write_to_screen(on ? (prevendcol < ps_global->ttyo->screen_cols-1 ? "<]" : "<")
1246 : (prevendcol < ps_global->ttyo->screen_cols-1 ? "] " : "]"));
1248 if(lastc){
1249 (void)pico_set_colorp(lastc, PSC_NONE);
1250 free_color_pair(&lastc);
1253 if(F_ON(F_SHOW_CURSOR, ps_global))
1254 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global),
1255 MIN(MAX(0,(prevstartcol + on ? 2 : 1)),ps_global->ttyo->screen_cols-1));
1256 else
1257 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
1260 fflush(stdout);
1261 #ifdef _WINDOWS
1262 mswin_setcursor ((on) ? MSWIN_CURSOR_BUSY : MSWIN_CURSOR_ARROW);
1263 #endif
1268 * modal_bogus_input - used by scrolltool to complain about
1269 * invalid user input.
1272 modal_bogus_input(UCS ch)
1274 char s[MAX_SCREEN_COLS+1];
1276 snprintf(s, sizeof(s), _("Command \"%s\" not allowed. Press RETURN to continue Alpine."),
1277 pretty_command(ch));
1278 s[sizeof(s)-1] = '\0';
1279 status_message_write(s, 0);
1280 Writechar(BELL, 0);
1281 return(0);