* When there are time changes in the clock, Alpine might go to sleep
[alpine.git] / alpine / status.c
blobfcfcdfda74a0075b58483aa767b3ac8bb2f3f0cd
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-2018 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 /* protect user from changes in the clock. If the clock
233 * changes during this process (for example, going from
234 * Standard time to Delayed time) this may result in big
235 * values for w. In those cases, reset w.
237 if(w < 0 || w > 5){ /* maximum wait time is 5 seconds */
238 w = 5;
239 displayed_time = time(0);
241 sleep(w);
242 delay_cmd_cue(0);
245 d_q_status_message();
251 * This relies on the global displayed_time being properly set
252 * for this message.
254 void
255 pause_for_and_mark_specific_msg(SMQ_T *msg)
257 if(msg){
258 int w;
260 w = (int) (displayed_time - time(0)) + msg->min_display_time;
261 w = (w > 0) ? w : 0;
262 if(w){
263 delay_cmd_cue(1);
264 sleep(w);
265 delay_cmd_cue(0);
268 msg->pending_removal = 1;
273 /*----------------------------------------------------------------------
274 Time remaining for current message's minimum display
275 ----*/
277 status_message_remaining(void)
279 int ret;
281 status_message_lock();
282 ret = status_message_remaining_nolock();
283 status_message_unlock();
285 return(ret);
290 status_message_remaining_nolock(void)
292 SMQ_T *q;
293 int d = 0;
295 if((q = top_of_queue()) != NULL)
296 d = (int) (displayed_time - time(0)) + q->min_display_time;
298 return((d > 0) ? d : 0);
303 * Return first message in queue that isn't pending_removal.
305 SMQ_T *
306 top_of_queue(void)
308 SMQ_T *p;
310 if((p = message_queue) != NULL){
312 if(!p->pending_removal)
313 return(p);
314 while((p = p->next) != message_queue);
317 return(NULL);
322 new_info_msg_need_not_be_queued(void)
324 SMQ_T *q;
326 if(status_message_remaining_nolock() > 0)
327 return 1;
329 if((q = top_of_queue()) != NULL && (q = q->next) != message_queue){
331 if(!q->pending_removal && !(q->flags & SM_INFO))
332 return 1;
333 while((q = q->next) != message_queue);
336 return 0;
340 /*----------------------------------------------------------------------
341 Find out how many messages are queued for display
343 Args: dtime -- will get set to minimum display time for current message
345 Result: number of messages in the queue.
347 ---------*/
349 messages_queued(long int *dtime)
351 SMQ_T *q;
352 int ret;
354 status_message_lock();
355 if(dtime && (q = top_of_queue()) != NULL)
356 *dtime = (long) MAX(q->min_display_time, 1L);
358 ret = ps_global->in_init_seq ? 0 : messages_in_queue();
360 status_message_unlock();
362 return(ret);
366 /*----------------------------------------------------------------------
367 Return number of messages in queue
368 ---------*/
370 messages_in_queue(void)
372 int n = 0;
373 SMQ_T *p;
375 if((p = message_queue) != NULL){
377 if(!p->pending_removal)
378 n++;
379 while((p = p->next) != message_queue);
382 return(n);
386 /*----------------------------------------------------------------------
387 Return last message queued
388 ---------*/
389 char *
390 last_message_queued(void)
392 SMQ_T *p, *r = NULL;
393 char *ret = NULL;
395 status_message_lock();
396 if((p = message_queue) != NULL){
398 if(p->flags & SM_ORDER && !p->pending_removal)
399 r = p;
400 while((p = p->next) != message_queue);
403 ret = (r && r->text) ? cpystr(r->text) : NULL;
405 status_message_unlock();
407 return(ret);
412 is_last_message(SMQ_T *msg)
414 SMQ_T *p, *r = NULL;
416 if(msg && !msg->pending_removal){
417 if((p = message_queue) != NULL){
419 if(!p->pending_removal)
420 r = p;
421 while((p = p->next) != message_queue);
425 return(r && msg && (r == msg));
429 /*----------------------------------------------------------------------
430 Update status line, clearing or displaying a message
432 Arg: command -- The command that is about to be executed
434 Result: status line cleared or
435 next message queued is displayed or
436 current message is redisplayed.
437 if next message displayed, it's min display time
438 is returned else if message already displayed, it's
439 time remaining on the display is returned, else 0.
441 This is called when ready to display the next message, usually just
442 before reading the next command from the user. We pass in the nature
443 of the command because it affects what we do here. If the command just
444 executed by the user is a redraw screen, we don't want to reset or go to
445 next message because it might not have been seen. Also if the command
446 is just a noop, which are usually executed when checking for new mail
447 and happen every few minutes, we don't clear the message.
449 If it was really a command and there's nothing more to show, then we
450 clear, because we know the user has seen the message. In this case the
451 user might be typing commands very quickly and miss a message, so
452 there is a time stamp and time check that each message has been on the
453 screen for a few seconds. If it hasn't we just return and let it be
454 taken care of next time.
455 ----------------------------------------------------------------------*/
457 display_message(UCS command)
459 SMQ_T *q, *copy_of_q;
460 int need_to_unlock;
461 int ding;
463 if(ps_global == NULL || ps_global->ttyo == NULL
464 || ps_global->ttyo->screen_rows < 1 || ps_global->in_init_seq)
465 return(0);
467 status_message_lock();
469 /*---- Deal with any previously displayed message ----*/
470 if((q = top_of_queue()) != NULL && q->shown){
471 int rv = -1;
473 if(command == ctrl('L')){ /* just repaint it, and go on */
474 mark_status_unknown();
475 mark_keymenu_dirty();
476 mark_titlebar_dirty();
477 rv = 0;
479 else{ /* ensure sufficient time's passed */
480 time_t now;
481 int diff;
483 now = time(0);
484 diff = (int)(displayed_time - now)
485 + ((command == NO_OP_COMMAND || command == NO_OP_IDLE)
486 ? q->max_display_time
487 : q->min_display_time);
488 dprint((9,
489 "STATUS: diff:%d, displayed: %ld, now: %ld\n",
490 diff, (long) displayed_time, (long) now));
491 if(diff > 0)
492 rv = diff; /* check again next time */
493 else if(is_last_message(q)
494 && (command == NO_OP_COMMAND || command == NO_OP_IDLE)
495 && q->max_display_time)
496 rv = 0; /* last msg, no cmd, has max */
499 if(rv >= 0){ /* leave message displayed? */
500 if(prevstartcol < 0){ /* need to redisplay it? */
501 ding = q->flags & SM_DING;
502 q->flags &= ~SM_DING;
503 if(q->flags & SM_MODAL && !q->shown){
504 copy_of_q = copy_status_queue(q);
505 mark_modals_done();
506 status_message_unlock();
507 output_message_modal(copy_of_q, ding);
509 else{
510 output_message(q, ding);
511 status_message_unlock();
514 else
515 status_message_unlock();
517 return(rv);
520 d_q_status_message(); /* remove it from queue and */
521 needs_clearing++; /* clear the line if needed */
524 if(!top_of_queue() && (command == ctrl('L') || needs_clearing)){
525 int inverse;
526 struct variable *vars = ps_global->vars;
527 char *last_bg = NULL;
529 dprint((9, "Clearing status line\n"));
530 inverse = InverseState(); /* already in inverse? */
531 if(inverse && pico_usingcolor() && VAR_STATUS_FORE_COLOR &&
532 VAR_STATUS_BACK_COLOR){
533 last_bg = pico_get_last_bg_color();
534 pico_set_nbg_color(); /* so ClearLine will clear in bg color */
537 ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
538 if(last_bg){
539 (void)pico_set_bg_color(last_bg);
540 if(last_bg)
541 fs_give((void **)&last_bg);
544 mark_status_unknown();
545 if(command == ctrl('L')){
546 mark_keymenu_dirty();
547 mark_titlebar_dirty();
551 need_to_unlock = 1;
553 /* display next message, weeding out 0 display times */
554 for(q = top_of_queue(); q && !q->shown; q = top_of_queue()){
555 if(q->min_display_time || is_last_message(q)){
556 displayed_time = time(0);
557 ding = q->flags & SM_DING;
558 q->flags &= ~SM_DING;
559 if(q->flags & SM_MODAL && !q->shown){
560 copy_of_q = copy_status_queue(q);
561 mark_modals_done();
562 status_message_unlock();
563 output_message_modal(copy_of_q, ding);
564 need_to_unlock = 0;
566 else
567 output_message(q, ding);
569 break;
571 /* zero display time message, log it, delete it */
572 else{
573 if(q->text){
574 char buf[1000];
575 char *append = " [not actually shown]";
576 char *ptr;
577 size_t len;
579 len = strlen(q->text) + strlen(append);
580 if(len < sizeof(buf))
581 ptr = buf;
582 else
583 ptr = (char *) fs_get((len+1) * sizeof(char));
585 strncpy(ptr, q->text, len);
586 ptr[len] = '\0';
587 strncat(ptr, append, len+1-1-strlen(ptr));
588 ptr[len] = '\0';
589 add_review_message(ptr, -1);
590 if(ptr != buf)
591 fs_give((void **) &ptr);
594 d_q_status_message();
598 needs_clearing = 0; /* always cleared or written */
599 fflush(stdout);
601 if(need_to_unlock)
602 status_message_unlock();
604 return(0);
608 /*----------------------------------------------------------------------
609 Display all the messages on the queue as quickly as possible
610 ----*/
611 void
612 flush_status_messages(int skip_last_pause)
614 SMQ_T *q, *copy_of_q;
615 int ding;
617 start_over:
618 status_message_lock();
620 for(q = top_of_queue(); q; q = top_of_queue()){
621 /* don't have to wait for this one */
622 if(is_last_message(q) && skip_last_pause && q->shown)
623 break;
625 if(q->shown)
626 pause_for_and_dq_cur_msg();
628 /* find next one we need to show and show it */
629 for(q = top_of_queue(); q && !q->shown; q = top_of_queue()){
630 if((q->min_display_time || is_last_message(q))){
631 displayed_time = time(0);
632 ding = q->flags & SM_DING;
633 q->flags &= ~SM_DING;
634 if(q->flags & SM_MODAL){
635 copy_of_q = copy_status_queue(q);
636 mark_modals_done();
637 status_message_unlock();
638 output_message_modal(copy_of_q, ding);
641 * Because we unlock the message queue in order
642 * to display modal messages we have to worry
643 * about the queue being changed while we have
644 * it unlocked. So start the whole process over.
646 goto start_over;
648 else
649 output_message(q, ding);
651 else{
652 d_q_status_message();
657 status_message_unlock();
661 /*----------------------------------------------------------------------
662 Make sure any and all SM_ORDER messages get displayed.
664 Note: This flags the message line as having nothing displayed.
665 The idea is that it's a function called by routines that want
666 the message line for a prompt or something, and that they're
667 going to obliterate the message anyway.
668 ----*/
669 void
670 flush_ordered_messages(void)
672 SMQ_T *q, *copy_of_q;
673 int firsttime = 1;
674 int ding;
676 status_message_lock();
678 set_saw_it_to_zero();
680 start_over2:
681 if(!firsttime)
682 status_message_lock();
683 else
684 firsttime = 0;
686 if((q = message_queue) != NULL){
688 if(q->pending_removal || q->saw_it || q->shown){
689 if(!q->pending_removal && q->shown)
690 pause_for_and_mark_specific_msg(q);
692 q->saw_it = 1;
693 q = (q->next != message_queue) ? q->next : NULL;
695 else{
696 /* find next one we need to show and show it */
698 if(q->pending_removal || q->saw_it || q->shown){
699 q->saw_it = 1;
700 q = (q->next != message_queue) ? q->next : NULL;
702 else{
703 if((q->flags & (SM_ORDER | SM_MODAL))
704 && q->min_display_time){
705 displayed_time = time(0);
706 ding = q->flags & SM_DING;
707 q->flags &= ~SM_DING;
708 if(q->flags & SM_MODAL){
709 copy_of_q = copy_status_queue(q);
710 mark_modals_done();
711 q->saw_it = 1;
712 status_message_unlock();
713 output_message_modal(copy_of_q, ding);
714 goto start_over2;
716 else{
717 output_message(q, ding);
720 else{
721 q->saw_it = 1;
722 if(!(q->flags & SM_ASYNC))
723 q->pending_removal = 1;
725 q = (q->next != message_queue) ? q->next : NULL;
728 }while(q && !q->shown);
730 }while(q);
733 status_message_unlock();
737 void
738 set_saw_it_to_zero()
740 SMQ_T *q;
742 /* set saw_it to zero */
743 if((q = message_queue) != NULL){
745 q->saw_it = 0;
746 q = (q->next != message_queue) ? q->next : NULL;
747 }while(q);
752 void
753 mark_modals_done()
755 SMQ_T *q;
757 /* set shown to one */
758 if((q = message_queue) != NULL){
760 if(q->flags & SM_MODAL){
761 q->shown = 1;
762 q->pending_removal = 1;
765 q = (q->next != message_queue) ? q->next : NULL;
766 }while(q);
772 * Caller needs to free the memory.
774 SMQ_T *
775 copy_status_queue(SMQ_T *start)
777 SMQ_T *q, *new, *head = NULL;
779 if((q = start) != NULL){
781 new = (SMQ_T *) fs_get(sizeof(SMQ_T));
782 *new = *q;
784 if(q->text)
785 new->text = cpystr(q->text);
786 else
787 new->text = NULL;
789 if(head){
790 new->next = head;
791 new->prev = head->prev;
792 new->prev->next = head->prev = new;
794 else
795 head = new->next = new->prev = new;
797 q = (q->next != start) ? q->next : NULL;
798 }while(q);
801 return(head);
805 /*----------------------------------------------------------------------
806 Removes the first message from the message queue.
807 Returns 0 if all was well, -1 if had to skip the removal and
808 message_queue is unchanged.
809 ----*/
810 void
811 d_q_status_message(void)
813 int the_last_one = 0;
814 SMQ_T *q, *p, *last_one;
816 /* mark the top one for removal */
817 if((p = top_of_queue()) != NULL)
818 p->pending_removal = 1;
820 if(message_queue){
822 /* flush out pending removals */
823 the_last_one = 0; /* loop control */
824 q = message_queue;
825 last_one = message_queue->prev;
826 while(!the_last_one){
827 if(q == last_one)
828 the_last_one++;
830 if(q->pending_removal){
831 p = q;
832 if(q == q->next){ /* last item in queue */
833 q = message_queue = NULL;
835 else{
836 p->next->prev = p->prev;
837 q = p->prev->next = p->next; /* here's the increment */
838 if(message_queue == p)
839 message_queue = q;
842 if(p){
843 if(p->text)
844 fs_give((void **) &p->text);
846 fs_give((void **) &p);
849 else
850 q = q->next;
856 /*----------------------------------------------------------------------
857 Actually output the message to the screen
859 Args: message -- The message to output
860 from_alarm_handler -- Called from alarm signal handler.
861 We don't want to add this message to the review
862 message list since we may mess with the malloc
863 arena here, and the interrupt may be from
864 the middle of something malloc'ing.
865 ----*/
867 status_message_write(char *message, int from_alarm_handler)
869 int col, row, max_width, invert;
870 int bytes;
871 char newstatusbuf[6*MAX_SCREEN_COLS + 1];
872 struct variable *vars = ps_global->vars;
873 COLOR_PAIR *lastc = NULL, *newc;
875 if(!from_alarm_handler)
876 add_review_message(message, -1);
878 invert = !InverseState(); /* already in inverse? */
879 row = MAX(0, ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
881 /* Put [] around message and truncate to screen width */
882 max_width = ps_global->ttyo != NULL ? ps_global->ttyo->screen_cols : 80;
883 max_width = MIN(max_width, MAX_SCREEN_COLS);
884 newstatusbuf[0] = '[';
885 newstatusbuf[1] = '\0';
887 bytes = utf8_to_width(newstatusbuf+1, message, sizeof(newstatusbuf)-1, max_width-2, NULL);
888 newstatusbuf[1+bytes] = ']';
889 newstatusbuf[1+bytes+1] = '\0';
891 if(prevstartcol == -1 || strcmp(newstatusbuf, prevstatusbuf)){
892 UCS *prevbuf = NULL, *newbuf = NULL;
893 size_t plen;
895 if(prevstartcol != -1){
896 prevbuf = utf8_to_ucs4_cpystr(prevstatusbuf);
897 newbuf = utf8_to_ucs4_cpystr(newstatusbuf);
901 * Simple optimization. If the strings are the same length
902 * and width just skip leading and trailing strings of common
903 * characters and only write whatever's in between. Otherwise,
904 * write out the whole thing.
905 * We could do something more complicated but the odds of
906 * getting any optimization goes way down if they aren't the
907 * same length and width and the complexity goes way up.
909 if(prevbuf && newbuf
910 && (plen=ucs4_strlen(prevbuf)) == ucs4_strlen(newbuf)
911 && ucs4_str_width(prevbuf) == ucs4_str_width(newbuf)){
912 UCS *start_of_unequal, *end_of_unequal;
913 UCS *pprev, *endnewbuf;
914 char *to_screen = NULL;
915 int column;
917 pprev = prevbuf;
918 start_of_unequal = newbuf;
919 endnewbuf = newbuf + plen;
920 col = column = prevstartcol;
922 while(start_of_unequal < endnewbuf && (*start_of_unequal) == (*pprev)){
923 int w;
925 w = wcellwidth(*start_of_unequal);
926 if(w >= 0)
927 column += w;
929 pprev++;
930 start_of_unequal++;
933 end_of_unequal = endnewbuf-1;
934 pprev = prevbuf + plen - 1;
936 while(end_of_unequal > start_of_unequal && (*end_of_unequal) == (*pprev)){
937 *end_of_unequal = '\0';
938 pprev--;
939 end_of_unequal--;
942 if(pico_usingcolor() && VAR_STATUS_FORE_COLOR &&
943 VAR_STATUS_BACK_COLOR &&
944 pico_is_good_color(VAR_STATUS_FORE_COLOR) &&
945 pico_is_good_color(VAR_STATUS_BACK_COLOR)){
946 lastc = pico_get_cur_color();
947 if(lastc){
948 newc = new_color_pair(VAR_STATUS_FORE_COLOR,
949 VAR_STATUS_BACK_COLOR);
950 (void)pico_set_colorp(newc, PSC_NONE);
951 free_color_pair(&newc);
954 else if(invert)
955 StartInverse();
957 /* PutLine wants UTF-8 string */
958 if(start_of_unequal && (*start_of_unequal))
959 to_screen = ucs4_to_utf8_cpystr(start_of_unequal);
961 if(to_screen){
962 PutLine0(row, column, to_screen);
963 fs_give((void **) &to_screen);
965 strncpy(prevstatusbuf, newstatusbuf, sizeof(prevstatusbuf));
966 prevstatusbuf[sizeof(prevstatusbuf)-1] = '\0';
969 if(lastc){
970 (void)pico_set_colorp(lastc, PSC_NONE);
971 free_color_pair(&lastc);
973 else if(invert)
974 EndInverse();
976 else{
977 if(pico_usingcolor())
978 lastc = pico_get_cur_color();
980 if(!invert && pico_usingcolor() && VAR_STATUS_FORE_COLOR &&
981 VAR_STATUS_BACK_COLOR &&
982 pico_is_good_color(VAR_STATUS_FORE_COLOR) &&
983 pico_is_good_color(VAR_STATUS_BACK_COLOR))
984 pico_set_nbg_color(); /* so ClearLine uses bg color */
986 ClearLine(row);
988 if(pico_usingcolor() && VAR_STATUS_FORE_COLOR &&
989 VAR_STATUS_BACK_COLOR &&
990 pico_is_good_color(VAR_STATUS_FORE_COLOR) &&
991 pico_is_good_color(VAR_STATUS_BACK_COLOR)){
992 if(lastc){
993 newc = new_color_pair(VAR_STATUS_FORE_COLOR,
994 VAR_STATUS_BACK_COLOR);
995 (void)pico_set_colorp(newc, PSC_NONE);
996 free_color_pair(&newc);
999 else if(invert){
1000 if(lastc)
1001 free_color_pair(&lastc);
1003 StartInverse();
1006 col = Centerline(row, newstatusbuf);
1008 if(lastc){
1009 (void)pico_set_colorp(lastc, PSC_NONE);
1010 free_color_pair(&lastc);
1012 else if(invert)
1013 EndInverse();
1015 strncpy(prevstatusbuf, newstatusbuf, sizeof(prevstatusbuf));
1016 prevstatusbuf[sizeof(prevstatusbuf)-1] = '\0';
1017 prevstartcol = col;
1018 prevendcol = col + utf8_width(prevstatusbuf) - 1;
1021 /* move cursor to a consistent position */
1022 if(F_ON(F_SHOW_CURSOR, ps_global))
1023 MoveCursor(row, MIN(MAX(0,col+1),ps_global->ttyo->screen_cols-1));
1024 else
1025 MoveCursor(row, 0);
1027 fflush(stdout);
1029 if(prevbuf)
1030 fs_give((void **) &prevbuf);
1032 if(newbuf)
1033 fs_give((void **) &newbuf);
1035 else
1036 col = prevstartcol;
1038 return(col);
1042 /*----------------------------------------------------------------------
1043 Write the given status message to the display.
1045 Args: mq_entry -- pointer to message queue entry to write.
1047 ----*/
1048 int
1049 output_message(SMQ_T *mq_entry, int ding)
1051 int col = 0;
1053 dprint((9, "output_message(%s)\n",
1054 (mq_entry && mq_entry->text) ? mq_entry->text : "?"));
1056 if(ding && F_OFF(F_QUELL_BEEPS, ps_global)){
1057 Writechar(BELL, 0); /* ring bell */
1058 fflush(stdout);
1061 if(!(mq_entry->flags & SM_MODAL)){
1062 col = status_message_write(mq_entry->text, 0);
1063 if(ps_global->status_msg_delay > 0){
1064 if(F_ON(F_SHOW_CURSOR, ps_global))
1065 /* col+1 because col is "[" character */
1066 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global),
1067 MIN(MAX(0,col+1),ps_global->ttyo->screen_cols-1));
1068 else
1069 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
1071 fflush(stdout);
1072 sleep(ps_global->status_msg_delay);
1075 mq_entry->shown = 1;
1078 return(col);
1083 * This is split off from output_message due to the locking
1084 * on the status message queue data. This calls scrolltool
1085 * which can call back into q_status and so on. So instead of
1086 * keeping the queue locked for a long time we copy the
1087 * data, unlock the queue, and display the data.
1089 int
1090 output_message_modal(SMQ_T *mq_entry, int ding)
1092 int rv = 0;
1093 SMQ_T *m, *mnext;
1095 if(!mq_entry)
1096 return(rv);
1098 dprint((9, "output_message_modal(%s)\n",
1099 (mq_entry && mq_entry->text) ? mq_entry->text : "?"));
1101 if(ding && F_OFF(F_QUELL_BEEPS, ps_global)){
1102 Writechar(BELL, 0); /* ring bell */
1103 fflush(stdout);
1106 if(!mq_entry->shown){
1107 int i = 0,
1108 pad = MAX(0, (ps_global->ttyo->screen_cols - 59) / 2);
1109 char *p, *q, *s, *t;
1110 SCROLL_S sargs;
1112 /* Count the number of modal messsages and add up their lengths. */
1113 for(m = mq_entry->next; m != mq_entry; m = m->next)
1114 if((m->flags & SM_MODAL) && !m->shown){
1115 i++;
1118 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",
1119 /* 1 2 3 4 5 6*/
1120 /*23456789012345678901234567890123456789012345678901234567890*/
1121 pad, "",
1122 "***********************************************************",
1123 pad, "", i ?
1124 "* What follows are advisory messages. After reading them *" :
1125 "* What follows is an advisory message. After reading it *",
1127 pad, "",
1128 "* simply hit \"Return\" to continue your Alpine session. *",
1129 pad, "",
1130 "* *",
1131 pad, "", i ?
1132 "* To review these messages later, press 'J' from the *" :
1133 "* To review this message later, press 'J' from the *",
1134 pad, "",
1135 "* MAIN MENU. *",
1136 pad, "",
1137 "***********************************************************");
1138 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1139 t = tmp_20k_buf + strlen(tmp_20k_buf);
1141 m = mq_entry;
1143 if((m->flags & SM_MODAL) && !m->shown){
1144 int indent;
1146 indent = ps_global->ttyo->screen_cols > 80
1147 ? (ps_global->ttyo->screen_cols - 80) / 3 : 0;
1149 if(t - tmp_20k_buf > 19000){
1150 snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n%*s* * * Running out of buffer space * * *", indent, "");
1151 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1152 t += strlen(t);
1153 snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n%*s* * * Press RETURN for more messages * * *", indent, "");
1154 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1155 break;
1158 add_review_message(m->text, -1);
1160 if((p = strstr(m->text, "[ALERT]")) != NULL){
1161 snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "%*.*s\n", (int)(indent + p - m->text), (int) (p - m->text), m->text);
1162 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1163 t += strlen(t);
1165 for(p += 7; *p && isspace((unsigned char)*p); p++)
1167 indent += 8;
1169 else{
1170 p = m->text;
1173 while(strlen(p) > ps_global->ttyo->screen_cols - 2 * indent){
1174 for(q = p + ps_global->ttyo->screen_cols - 2 * indent;
1175 q > p && !isspace((unsigned char)*q); q--)
1178 snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n%*.*s", (int) (indent + q - p), (int) (q - p), p);
1179 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1180 t += strlen(t);
1181 p = q + 1;
1184 snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n%*s%s", indent, "", p);
1185 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1186 t += strlen(t);
1188 if(i--){
1189 snprintf(t, SIZEOF_20KBUF-(t-tmp_20k_buf), "\n\n%*s\n", pad + 30, "- - -");
1190 tmp_20k_buf[SIZEOF_20KBUF-1] = '\0';
1191 t += strlen(t);
1194 m = m->next;
1195 } while(m != mq_entry);
1197 s = cpystr(tmp_20k_buf);
1198 ClearLine(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global));
1200 memset(&sargs, 0, sizeof(SCROLL_S));
1201 sargs.text.text = s;
1202 sargs.text.src = CharStar;
1203 sargs.bar.title = "Status Message";
1204 sargs.bogus_input = modal_bogus_input;
1205 sargs.no_stat_msg = 1;
1206 sargs.keys.menu = &modal_message_keymenu;
1207 setbitmap(sargs.keys.bitmap);
1209 scrolltool(&sargs);
1211 fs_give((void **)&s);
1212 ps_global->mangled_screen = 1;
1215 /* free the passed in queue */
1216 m = mq_entry;
1218 if(m->text)
1219 fs_give((void **) &m->text);
1221 mnext = m->next;
1222 fs_give((void **) &m);
1223 m = mnext;
1224 } while(m != mq_entry);
1226 return(rv);
1231 /*----------------------------------------------------------------------
1232 Write or clear delay cue
1234 Args: on -- whether to turn it on or not
1236 ----*/
1237 void
1238 delay_cmd_cue(int on)
1240 COLOR_PAIR *lastc;
1241 struct variable *vars = ps_global->vars;
1243 if(prevstartcol >= 0 && prevendcol < ps_global->ttyo->screen_cols){
1244 MoveCursor(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global),
1245 MAX(prevstartcol - 1, 0));
1246 lastc = pico_set_colors(VAR_STATUS_FORE_COLOR, VAR_STATUS_BACK_COLOR,
1247 PSC_REV|PSC_RET);
1248 Write_to_screen(on ? (prevstartcol ? "[>" : ">")
1249 : (prevstartcol ? " [" : "["));
1251 MoveCursor(ps_global->ttyo->screen_rows - FOOTER_ROWS(ps_global),
1252 prevendcol);
1254 Write_to_screen(on ? (prevendcol < ps_global->ttyo->screen_cols-1 ? "<]" : "<")
1255 : (prevendcol < ps_global->ttyo->screen_cols-1 ? "] " : "]"));
1257 if(lastc){
1258 (void)pico_set_colorp(lastc, PSC_NONE);
1259 free_color_pair(&lastc);
1262 if(F_ON(F_SHOW_CURSOR, ps_global))
1263 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global),
1264 MIN(MAX(0,(prevstartcol + on ? 2 : 1)),ps_global->ttyo->screen_cols-1));
1265 else
1266 MoveCursor(ps_global->ttyo->screen_rows-FOOTER_ROWS(ps_global), 0);
1269 fflush(stdout);
1270 #ifdef _WINDOWS
1271 mswin_setcursor ((on) ? MSWIN_CURSOR_BUSY : MSWIN_CURSOR_ARROW);
1272 #endif
1277 * modal_bogus_input - used by scrolltool to complain about
1278 * invalid user input.
1281 modal_bogus_input(UCS ch)
1283 char s[MAX_SCREEN_COLS+1];
1285 snprintf(s, sizeof(s), _("Command \"%s\" not allowed. Press RETURN to continue Alpine."),
1286 pretty_command(ch));
1287 s[sizeof(s)-1] = '\0';
1288 status_message_write(s, 0);
1289 Writechar(BELL, 0);
1290 return(0);
1293 void
1294 free_message_queue(void)
1296 SMQ_T *m, *mnext;
1298 if(message_queue == NULL)
1299 return;
1301 m = message_queue;
1303 if(m->text)
1304 fs_give((void **) &m->text);
1306 mnext = m->next;
1307 fs_give((void **) &m);
1308 m = mnext;
1309 } while(m != message_queue);
1310 message_queue = NULL;