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-2016 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){
236 d_q_status_message();
242 * This relies on the global displayed_time being properly set
246 pause_for_and_mark_specific_msg(SMQ_T
*msg
)
251 w
= (int) (displayed_time
- time(0)) + msg
->min_display_time
;
259 msg
->pending_removal
= 1;
264 /*----------------------------------------------------------------------
265 Time remaining for current message's minimum display
268 status_message_remaining(void)
272 status_message_lock();
273 ret
= status_message_remaining_nolock();
274 status_message_unlock();
281 status_message_remaining_nolock(void)
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.
301 if((p
= message_queue
) != NULL
){
303 if(!p
->pending_removal
)
305 while((p
= p
->next
) != message_queue
);
313 new_info_msg_need_not_be_queued(void)
317 if(status_message_remaining_nolock() > 0)
320 if((q
= top_of_queue()) != NULL
&& (q
= q
->next
) != message_queue
){
322 if(!q
->pending_removal
&& !(q
->flags
& SM_INFO
))
324 while((q
= q
->next
) != message_queue
);
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.
340 messages_queued(long int *dtime
)
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();
357 /*----------------------------------------------------------------------
358 Return number of messages in queue
361 messages_in_queue(void)
366 if((p
= message_queue
) != NULL
){
368 if(!p
->pending_removal
)
370 while((p
= p
->next
) != message_queue
);
377 /*----------------------------------------------------------------------
378 Return last message queued
381 last_message_queued(void)
386 status_message_lock();
387 if((p
= message_queue
) != NULL
){
389 if(p
->flags
& SM_ORDER
&& !p
->pending_removal
)
391 while((p
= p
->next
) != message_queue
);
394 ret
= (r
&& r
->text
) ? cpystr(r
->text
) : NULL
;
396 status_message_unlock();
403 is_last_message(SMQ_T
*msg
)
407 if(msg
&& !msg
->pending_removal
){
408 if((p
= message_queue
) != NULL
){
410 if(!p
->pending_removal
)
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
;
454 if(ps_global
== NULL
|| ps_global
->ttyo
== NULL
455 || ps_global
->ttyo
->screen_rows
< 1 || ps_global
->in_init_seq
)
458 status_message_lock();
460 /*---- Deal with any previously displayed message ----*/
461 if((q
= top_of_queue()) != NULL
&& q
->shown
){
464 if(command
== ctrl('L')){ /* just repaint it, and go on */
465 mark_status_unknown();
466 mark_keymenu_dirty();
467 mark_titlebar_dirty();
470 else{ /* ensure sufficient time's passed */
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
);
480 "STATUS: diff:%d, displayed: %ld, now: %ld\n",
481 diff
, (long) displayed_time
, (long) now
));
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
);
497 status_message_unlock();
498 output_message_modal(copy_of_q
, ding
);
501 output_message(q
, ding
);
502 status_message_unlock();
506 status_message_unlock();
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
)){
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
));
530 (void)pico_set_bg_color(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();
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
);
553 status_message_unlock();
554 output_message_modal(copy_of_q
, ding
);
558 output_message(q
, ding
);
562 /* zero display time message, log it, delete it */
566 char *append
= " [not actually shown]";
570 len
= strlen(q
->text
) + strlen(append
);
571 if(len
< sizeof(buf
))
574 ptr
= (char *) fs_get((len
+1) * sizeof(char));
576 strncpy(ptr
, q
->text
, len
);
578 strncat(ptr
, append
, len
+1-1-strlen(ptr
));
580 add_review_message(ptr
, -1);
582 fs_give((void **) &ptr
);
585 d_q_status_message();
589 needs_clearing
= 0; /* always cleared or written */
593 status_message_unlock();
599 /*----------------------------------------------------------------------
600 Display all the messages on the queue as quickly as possible
603 flush_status_messages(int skip_last_pause
)
605 SMQ_T
*q
, *copy_of_q
;
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
)
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
);
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.
640 output_message(q
, ding
);
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.
661 flush_ordered_messages(void)
663 SMQ_T
*q
, *copy_of_q
;
667 status_message_lock();
669 set_saw_it_to_zero();
673 status_message_lock();
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
);
684 q
= (q
->next
!= message_queue
) ? q
->next
: NULL
;
687 /* find next one we need to show and show it */
689 if(q
->pending_removal
|| q
->saw_it
|| q
->shown
){
691 q
= (q
->next
!= message_queue
) ? q
->next
: NULL
;
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
);
703 status_message_unlock();
704 output_message_modal(copy_of_q
, ding
);
708 output_message(q
, ding
);
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
);
724 status_message_unlock();
733 /* set saw_it to zero */
734 if((q
= message_queue
) != NULL
){
737 q
= (q
->next
!= message_queue
) ? q
->next
: NULL
;
748 /* set shown to one */
749 if((q
= message_queue
) != NULL
){
751 if(q
->flags
& SM_MODAL
){
753 q
->pending_removal
= 1;
756 q
= (q
->next
!= message_queue
) ? q
->next
: NULL
;
763 * Caller needs to free the memory.
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
));
776 new->text
= cpystr(q
->text
);
782 new->prev
= head
->prev
;
783 new->prev
->next
= head
->prev
= new;
786 head
= new->next
= new->prev
= new;
788 q
= (q
->next
!= start
) ? q
->next
: NULL
;
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.
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;
813 /* flush out pending removals */
814 the_last_one
= 0; /* loop control */
816 last_one
= message_queue
->prev
;
817 while(!the_last_one
){
821 if(q
->pending_removal
){
823 if(q
== q
->next
){ /* last item in queue */
824 q
= message_queue
= NULL
;
827 p
->next
->prev
= p
->prev
;
828 q
= p
->prev
->next
= p
->next
; /* here's the increment */
829 if(message_queue
== p
)
835 fs_give((void **) &p
->text
);
837 fs_give((void **) &p
);
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.
858 status_message_write(char *message
, int from_alarm_handler
)
860 int col
, row
, max_width
, invert
;
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
;
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.
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
;
909 start_of_unequal
= newbuf
;
910 endnewbuf
= newbuf
+ plen
;
911 col
= column
= prevstartcol
;
913 while(start_of_unequal
< endnewbuf
&& (*start_of_unequal
) == (*pprev
)){
916 w
= wcellwidth(*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';
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();
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
);
948 /* PutLine wants UTF-8 string */
949 if(start_of_unequal
&& (*start_of_unequal
))
950 to_screen
= ucs4_to_utf8_cpystr(start_of_unequal
);
953 PutLine0(row
, column
, to_screen
);
954 fs_give((void **) &to_screen
);
956 strncpy(prevstatusbuf
, newstatusbuf
, sizeof(prevstatusbuf
));
957 prevstatusbuf
[sizeof(prevstatusbuf
)-1] = '\0';
961 (void)pico_set_colorp(lastc
, PSC_NONE
);
962 free_color_pair(&lastc
);
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 */
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
)){
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
);
992 free_color_pair(&lastc
);
997 col
= Centerline(row
, newstatusbuf
);
1000 (void)pico_set_colorp(lastc
, PSC_NONE
);
1001 free_color_pair(&lastc
);
1006 strncpy(prevstatusbuf
, newstatusbuf
, sizeof(prevstatusbuf
));
1007 prevstatusbuf
[sizeof(prevstatusbuf
)-1] = '\0';
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));
1021 fs_give((void **) &prevbuf
);
1024 fs_give((void **) &newbuf
);
1033 /*----------------------------------------------------------------------
1034 Write the given status message to the display.
1036 Args: mq_entry -- pointer to message queue entry to write.
1040 output_message(SMQ_T
*mq_entry
, int ding
)
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 */
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));
1060 MoveCursor(ps_global
->ttyo
->screen_rows
-FOOTER_ROWS(ps_global
), 0);
1063 sleep(ps_global
->status_msg_delay
);
1066 mq_entry
->shown
= 1;
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.
1081 output_message_modal(SMQ_T
*mq_entry
, int ding
)
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 */
1097 if(!mq_entry
->shown
){
1099 pad
= MAX(0, (ps_global
->ttyo
->screen_cols
- 59) / 2);
1100 char *p
, *q
, *s
, *t
;
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
){
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",
1111 /*23456789012345678901234567890123456789012345678901234567890*/
1113 "***********************************************************",
1115 "* What follows are advisory messages. After reading them *" :
1116 "* What follows is an advisory message. After reading it *",
1119 "* simply hit \"Return\" to continue your Alpine session. *",
1123 "* To review these messages later, press 'J' from the *" :
1124 "* To review this message later, press 'J' from the *",
1128 "***********************************************************");
1129 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1130 t
= tmp_20k_buf
+ strlen(tmp_20k_buf
);
1134 if((m
->flags
& SM_MODAL
) && !m
->shown
){
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';
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';
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';
1156 for(p
+= 7; *p
&& isspace((unsigned char)*p
); p
++)
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';
1175 snprintf(t
, SIZEOF_20KBUF
-(t
-tmp_20k_buf
), "\n%*s%s", indent
, "", p
);
1176 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
1180 snprintf(t
, SIZEOF_20KBUF
-(t
-tmp_20k_buf
), "\n\n%*s\n", pad
+ 30, "- - -");
1181 tmp_20k_buf
[SIZEOF_20KBUF
-1] = '\0';
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
);
1202 fs_give((void **)&s
);
1203 ps_global
->mangled_screen
= 1;
1206 /* free the passed in queue */
1210 fs_give((void **) &m
->text
);
1213 fs_give((void **) &m
);
1215 } while(m
!= mq_entry
);
1222 /*----------------------------------------------------------------------
1223 Write or clear delay cue
1225 Args: on -- whether to turn it on or not
1229 delay_cmd_cue(int on
)
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
,
1239 Write_to_screen(on
? (prevstartcol
? "[>" : ">")
1240 : (prevstartcol
? " [" : "["));
1242 MoveCursor(ps_global
->ttyo
->screen_rows
- FOOTER_ROWS(ps_global
),
1245 Write_to_screen(on
? (prevendcol
< ps_global
->ttyo
->screen_cols
-1 ? "<]" : "<")
1246 : (prevendcol
< ps_global
->ttyo
->screen_cols
-1 ? "] " : "]"));
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));
1257 MoveCursor(ps_global
->ttyo
->screen_rows
-FOOTER_ROWS(ps_global
), 0);
1262 mswin_setcursor ((on
) ? MSWIN_CURSOR_BUSY
: MSWIN_CURSOR_ARROW
);
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);