1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: status.c 840 2007-12-01 01:34:49Z hubert@u.washington.edu $";
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 /*======================================================================
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
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
{
51 unsigned pending_removal
:1;
52 int min_display_time
, max_display_time
;
53 struct message
*next
, *prev
;
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.
108 q_status_message(int flags
, int min_time
, int max_time
, char *message
)
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();
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)
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.
153 if(!q
->pending_removal
&& q
->text
&& !strcmp(q
->text
, clean_msg
)){
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
: "?"));
165 fs_give((void **)&clean_msg
);
167 status_message_unlock();
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
;
183 new->next
= message_queue
;
184 new->prev
= message_queue
->prev
;
185 new->prev
->next
= message_queue
->prev
= new;
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
201 mark_status_dirty(void)
203 mark_status_unknown();
208 /*----------------------------------------------------------------------
209 Cause status line drawing optimization to be turned off, because we
210 don't know what the status line looks like.
213 mark_status_unknown(void)
217 prevstatusbuf
[0] = '\0';
221 /*----------------------------------------------------------------------
222 Wait a suitable amount of time for the currently displayed message
225 pause_for_and_dq_cur_msg(void)
230 if((w
= status_message_remaining_nolock()) != 0){
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 */
239 displayed_time
= time(0);
245 d_q_status_message();
251 * This relies on the global displayed_time being properly set
255 pause_for_and_mark_specific_msg(SMQ_T
*msg
)
260 w
= (int) (displayed_time
- time(0)) + msg
->min_display_time
;
268 msg
->pending_removal
= 1;
273 /*----------------------------------------------------------------------
274 Time remaining for current message's minimum display
277 status_message_remaining(void)
281 status_message_lock();
282 ret
= status_message_remaining_nolock();
283 status_message_unlock();
290 status_message_remaining_nolock(void)
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.
310 if((p
= message_queue
) != NULL
){
312 if(!p
->pending_removal
)
314 while((p
= p
->next
) != message_queue
);
322 new_info_msg_need_not_be_queued(void)
326 if(status_message_remaining_nolock() > 0)
329 if((q
= top_of_queue()) != NULL
&& (q
= q
->next
) != message_queue
){
331 if(!q
->pending_removal
&& !(q
->flags
& SM_INFO
))
333 while((q
= q
->next
) != message_queue
);
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.
349 messages_queued(long int *dtime
)
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();
366 /*----------------------------------------------------------------------
367 Return number of messages in queue
370 messages_in_queue(void)
375 if((p
= message_queue
) != NULL
){
377 if(!p
->pending_removal
)
379 while((p
= p
->next
) != message_queue
);
386 /*----------------------------------------------------------------------
387 Return last message queued
390 last_message_queued(void)
395 status_message_lock();
396 if((p
= message_queue
) != NULL
){
398 if(p
->flags
& SM_ORDER
&& !p
->pending_removal
)
400 while((p
= p
->next
) != message_queue
);
403 ret
= (r
&& r
->text
) ? cpystr(r
->text
) : NULL
;
405 status_message_unlock();
412 is_last_message(SMQ_T
*msg
)
416 if(msg
&& !msg
->pending_removal
){
417 if((p
= message_queue
) != NULL
){
419 if(!p
->pending_removal
)
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
;
463 if(ps_global
== NULL
|| ps_global
->ttyo
== NULL
464 || ps_global
->ttyo
->screen_rows
< 1 || ps_global
->in_init_seq
)
467 status_message_lock();
469 /*---- Deal with any previously displayed message ----*/
470 if((q
= top_of_queue()) != NULL
&& q
->shown
){
473 if(command
== ctrl('L')){ /* just repaint it, and go on */
474 mark_status_unknown();
475 mark_keymenu_dirty();
476 mark_titlebar_dirty();
479 else{ /* ensure sufficient time's passed */
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
);
489 "STATUS: diff:%d, displayed: %ld, now: %ld\n",
490 diff
, (long) displayed_time
, (long) now
));
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
);
506 status_message_unlock();
507 output_message_modal(copy_of_q
, ding
);
510 output_message(q
, ding
);
511 status_message_unlock();
515 status_message_unlock();
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
)){
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
));
539 (void)pico_set_bg_color(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();
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
);
562 status_message_unlock();
563 output_message_modal(copy_of_q
, ding
);
567 output_message(q
, ding
);
571 /* zero display time message, log it, delete it */
575 char *append
= " [not actually shown]";
579 len
= strlen(q
->text
) + strlen(append
);
580 if(len
< sizeof(buf
))
583 ptr
= (char *) fs_get((len
+1) * sizeof(char));
585 strncpy(ptr
, q
->text
, len
);
587 strncat(ptr
, append
, len
+1-1-strlen(ptr
));
589 add_review_message(ptr
, -1);
591 fs_give((void **) &ptr
);
594 d_q_status_message();
598 needs_clearing
= 0; /* always cleared or written */
602 status_message_unlock();
608 /*----------------------------------------------------------------------
609 Display all the messages on the queue as quickly as possible
612 flush_status_messages(int skip_last_pause
)
614 SMQ_T
*q
, *copy_of_q
;
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
)
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
);
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.
649 output_message(q
, ding
);
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.
670 flush_ordered_messages(void)
672 SMQ_T
*q
, *copy_of_q
;
676 status_message_lock();
678 set_saw_it_to_zero();
682 status_message_lock();
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
);
693 q
= (q
->next
!= message_queue
) ? q
->next
: NULL
;
696 /* find next one we need to show and show it */
698 if(q
->pending_removal
|| q
->saw_it
|| q
->shown
){
700 q
= (q
->next
!= message_queue
) ? q
->next
: NULL
;
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
);
712 status_message_unlock();
713 output_message_modal(copy_of_q
, ding
);
717 output_message(q
, ding
);
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
);
733 status_message_unlock();
742 /* set saw_it to zero */
743 if((q
= message_queue
) != NULL
){
746 q
= (q
->next
!= message_queue
) ? q
->next
: NULL
;
757 /* set shown to one */
758 if((q
= message_queue
) != NULL
){
760 if(q
->flags
& SM_MODAL
){
762 q
->pending_removal
= 1;
765 q
= (q
->next
!= message_queue
) ? q
->next
: NULL
;
772 * Caller needs to free the memory.
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
));
785 new->text
= cpystr(q
->text
);
791 new->prev
= head
->prev
;
792 new->prev
->next
= head
->prev
= new;
795 head
= new->next
= new->prev
= new;
797 q
= (q
->next
!= start
) ? q
->next
: NULL
;
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.
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;
822 /* flush out pending removals */
823 the_last_one
= 0; /* loop control */
825 last_one
= message_queue
->prev
;
826 while(!the_last_one
){
830 if(q
->pending_removal
){
832 if(q
== q
->next
){ /* last item in queue */
833 q
= message_queue
= NULL
;
836 p
->next
->prev
= p
->prev
;
837 q
= p
->prev
->next
= p
->next
; /* here's the increment */
838 if(message_queue
== p
)
844 fs_give((void **) &p
->text
);
846 fs_give((void **) &p
);
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.
867 status_message_write(char *message
, int from_alarm_handler
)
869 int col
, row
, max_width
, invert
;
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
;
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.
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
;
918 start_of_unequal
= newbuf
;
919 endnewbuf
= newbuf
+ plen
;
920 col
= column
= prevstartcol
;
922 while(start_of_unequal
< endnewbuf
&& (*start_of_unequal
) == (*pprev
)){
925 w
= wcellwidth(*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';
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();
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
);
957 /* PutLine wants UTF-8 string */
958 if(start_of_unequal
&& (*start_of_unequal
))
959 to_screen
= ucs4_to_utf8_cpystr(start_of_unequal
);
962 PutLine0(row
, column
, to_screen
);
963 fs_give((void **) &to_screen
);
965 strncpy(prevstatusbuf
, newstatusbuf
, sizeof(prevstatusbuf
));
966 prevstatusbuf
[sizeof(prevstatusbuf
)-1] = '\0';
970 (void)pico_set_colorp(lastc
, PSC_NONE
);
971 free_color_pair(&lastc
);
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 */
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
)){
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
);
1001 free_color_pair(&lastc
);
1006 col
= Centerline(row
, newstatusbuf
);
1009 (void)pico_set_colorp(lastc
, PSC_NONE
);
1010 free_color_pair(&lastc
);
1015 strncpy(prevstatusbuf
, newstatusbuf
, sizeof(prevstatusbuf
));
1016 prevstatusbuf
[sizeof(prevstatusbuf
)-1] = '\0';
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));
1030 fs_give((void **) &prevbuf
);
1033 fs_give((void **) &newbuf
);
1042 /*----------------------------------------------------------------------
1043 Write the given status message to the display.
1045 Args: mq_entry -- pointer to message queue entry to write.
1049 output_message(SMQ_T
*mq_entry
, int ding
)
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 */
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));
1069 MoveCursor(ps_global
->ttyo
->screen_rows
-FOOTER_ROWS(ps_global
), 0);
1072 sleep(ps_global
->status_msg_delay
);
1075 mq_entry
->shown
= 1;
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.
1090 output_message_modal(SMQ_T
*mq_entry
, int ding
)
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 */
1106 if(!mq_entry
->shown
){
1108 pad
= MAX(0, (ps_global
->ttyo
->screen_cols
- 59) / 2);
1109 char *p
, *q
, *s
, *t
;
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
){
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",
1120 /*23456789012345678901234567890123456789012345678901234567890*/
1122 "***********************************************************",
1124 "* What follows are advisory messages. After reading them *" :
1125 "* What follows is an advisory message. After reading it *",
1128 "* simply hit \"Return\" to continue your Alpine session. *",
1132 "* To review these messages later, press 'J' from the *" :
1133 "* To review this message later, press 'J' from the *",
1137 "***********************************************************");
1138 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1139 t
= tmp_20k_buf
+ strlen(tmp_20k_buf
);
1143 if((m
->flags
& SM_MODAL
) && !m
->shown
){
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';
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';
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';
1165 for(p
+= 7; *p
&& isspace((unsigned char)*p
); p
++)
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';
1184 snprintf(t
, SIZEOF_20KBUF
-(t
-tmp_20k_buf
), "\n%*s%s", indent
, "", p
);
1185 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1189 snprintf(t
, SIZEOF_20KBUF
-(t
-tmp_20k_buf
), "\n\n%*s\n", pad
+ 30, "- - -");
1190 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
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
);
1211 fs_give((void **)&s
);
1212 ps_global
->mangled_screen
= 1;
1215 /* free the passed in queue */
1219 fs_give((void **) &m
->text
);
1222 fs_give((void **) &m
);
1224 } while(m
!= mq_entry
);
1231 /*----------------------------------------------------------------------
1232 Write or clear delay cue
1234 Args: on -- whether to turn it on or not
1238 delay_cmd_cue(int on
)
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
,
1248 Write_to_screen(on
? (prevstartcol
? "[>" : ">")
1249 : (prevstartcol
? " [" : "["));
1251 MoveCursor(ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
),
1254 Write_to_screen(on
? (prevendcol
< ps_global
->ttyo
->screen_cols
-1 ? "<]" : "<")
1255 : (prevendcol
< ps_global
->ttyo
->screen_cols
-1 ? "] " : "]"));
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));
1266 MoveCursor(ps_global
->ttyo
->screen_rows
-FOOTER_ROWS(ps_global
), 0);
1271 mswin_setcursor ((on
) ? MSWIN_CURSOR_BUSY
: MSWIN_CURSOR_ARROW
);
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);
1294 free_message_queue(void)
1298 if(message_queue
== NULL
)
1304 fs_give((void **) &m
->text
);
1307 fs_give((void **) &m
);
1309 } while(m
!= message_queue
);
1310 message_queue
= NULL
;