1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 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 /*======================================================================
23 Implements the mailview screen
24 Also includes scrolltool used to display help text
48 #include "../pith/conf.h"
49 #include "../pith/filter.h"
50 #include "../pith/msgno.h"
51 #include "../pith/escapes.h"
52 #include "../pith/flag.h"
53 #include "../pith/mimedesc.h"
54 #include "../pith/url.h"
55 #include "../pith/bldaddr.h"
56 #include "../pith/mailcmd.h"
57 #include "../pith/newmail.h"
58 #include "../pith/pipe.h"
59 #include "../pith/thread.h"
60 #include "../pith/util.h"
61 #include "../pith/detoken.h"
62 #include "../pith/editorial.h"
63 #include "../pith/maillist.h"
64 #include "../pith/hist.h"
65 #include "../pith/busy.h"
66 #include "../pith/list.h"
67 #include "../pith/detach.h"
70 /*----------------------------------------------------------------------
71 Saved state for scrolling text
73 typedef struct scroll_text
{
74 SCROLL_S
*parms
; /* Original text (file, char *, char **) */
75 char **text_lines
, /* Lines to display */
76 *fname
; /* filename of line offsets in "text" */
77 FILE *findex
; /* file pointer to line offsets in "text" */
78 short *line_lengths
; /* Length of each line in "text_lines" */
79 long top_text_line
, /* index in "text_lines" top displayed line */
80 num_lines
; /* number of valid pointers in "text_lines" */
81 int lines_allocated
; /* size of "text_lines" array */
83 int length
, /* count of displayable lines (== PGSIZE) */
84 width
, /* width of displayable lines */
85 start_line
, /* line number to start painting on */
86 other_lines
; /* # of lines not for scroll text */
87 } screen
; /* screen parameters */
91 typedef struct scroll_file
{
98 * Struct to help write lines do display as they're decoded
100 struct view_write_s
{
112 #define LINEBUFSIZ (4096)
114 #define MAX_FUDGE (1024*1024)
117 * Definitions to help scrolltool
119 #define SCROLL_LINES_ABOVE(X) HEADER_ROWS(X)
120 #define SCROLL_LINES_BELOW(X) FOOTER_ROWS(X)
121 #define SCROLL_LINES(X) MAX(((X)->ttyo->screen_rows \
122 - SCROLL_LINES_ABOVE(X) - SCROLL_LINES_BELOW(X)), 0)
123 #define scroll_text_lines() (scroll_state(SS_CUR)->num_lines)
127 * Definitions for various scroll state manager's functions
137 #define HANDLE_INIT_MSG \
138 _("Selectable items in text -- Use Up/Down Arrows to choose, Return to view")
139 #define HANDLE_ABOVE_ERR \
140 _("No selected item displayed -- Use PrevPage to bring choice into view")
141 #define HANDLE_BELOW_ERR \
142 _("No selected item displayed -- Use NextPage to bring choice into view")
145 #define PGSIZE(X) (ps_global->ttyo->screen_rows - (X)->screen.other_lines)
147 #define TYPICAL_BIG_MESSAGE_LINES 200
151 * Internal prototypes
153 void view_writec_killbuf(void);
154 int view_end_scroll(SCROLL_S
*);
155 long format_size_guess(BODY
*);
156 int scroll_handle_prompt(HANDLE_S
*, int);
157 int scroll_handle_launch(HANDLE_S
*, int);
158 int scroll_handle_obscured(HANDLE_S
*);
159 HANDLE_S
*scroll_handle_in_frame(long);
160 long scroll_handle_reframe(int, int);
161 int handle_on_line(long, int);
162 int handle_on_page(HANDLE_S
*, long, long);
163 int scroll_handle_selectable(HANDLE_S
*);
164 HANDLE_S
*scroll_handle_next_sel(HANDLE_S
*);
165 HANDLE_S
*scroll_handle_prev_sel(HANDLE_S
*);
166 HANDLE_S
*scroll_handle_next(HANDLE_S
*);
167 HANDLE_S
*scroll_handle_prev(HANDLE_S
*);
168 HANDLE_S
*scroll_handle_boundary(HANDLE_S
*, HANDLE_S
*(*)(HANDLE_S
*));
169 int scroll_handle_column(int, int);
170 int scroll_handle_index(int, int);
171 void scroll_handle_set_loc(POSLIST_S
**, int, int);
172 int dot_on_handle(long, int);
173 int url_launch(HANDLE_S
*);
174 int url_launch_too_long(int);
175 char *url_external_handler(HANDLE_S
*, int);
176 void url_mailto_addr(ADDRESS
**, char *);
177 int ical_send_reply(char *);
178 int url_local_phone_home(char *);
179 int url_local_imap(char *);
180 int url_local_nntp(char *);
181 int url_local_news(char *);
182 int url_local_file(char *);
183 static int print_to_printer(SCROLL_S
*);
184 int search_text(int, long, int, char **, Pos
*, int *);
185 void update_scroll_titlebar(long, int);
186 SCRLCTRL_S
*scroll_state(int);
187 void set_scroll_text(SCROLL_S
*, long, SCRLCTRL_S
*);
188 void redraw_scroll_text(void);
189 void zero_scroll_text(void);
190 void format_scroll_text(void);
191 void ScrollFile(long);
192 long make_file_index(void);
193 char *scroll_file_line(FILE *, char *, SCRLFILE_S
*, int *);
194 long scroll_scroll_text(long, HANDLE_S
*, int);
195 int search_scroll_text(long, int, char *, Pos
*, int *);
196 char *search_scroll_line(char *, char *, int, int);
197 int visible_linelen(int);
198 long doubleclick_handle(SCROLL_S
*, HANDLE_S
*, int *, int *);
200 int format_message_popup(SCROLL_S
*, int);
201 int simple_text_popup(SCROLL_S
*, int);
202 int mswin_readscrollbuf(int);
203 int pcpine_do_scroll(int, long);
204 char *pcpine_help_scroll(char *);
205 int pcpine_view_cursor(int, long);
210 /*----------------------------------------------------------------------
211 Format a buffer with the text of the current message for browser
213 Args: ps - pine state structure
215 Result: The scrolltool is called to display the message
217 Loop here viewing mail until the folder changed or a command takes
218 us to another screen. Inside the loop the message text is fetched and
219 formatted into a buffer allocated for it. These are passed to the
220 scrolltool(), that displays the message and executes commands. It
221 returns when it's time to display a different message, when we
222 change folders, when it's time for a different screen, or when
223 there are no more messages available.
227 mail_view_screen(struct pine
*ps
)
229 char last_was_full_header
= 0;
230 long last_message_viewed
= -1L, raw_msgno
, offset
= 0L;
231 OtherMenu save_what
= FirstMenu
;
232 int we_cancel
= 0, flags
, cmd
= 0;
233 int force_prefer
= 0;
238 HANDLE_S
*handles
= NULL
;
240 SourceType src
= CharStar
;
242 dprint((1, "\n\n ----- MAIL VIEW -----\n"));
244 ps
->prev_screen
= mail_view_screen
;
245 ps
->force_prefer_plain
= ps
->force_no_prefer_plain
= 0;
247 if(ps
->ttyo
->screen_rows
- HEADER_ROWS(ps
) - FOOTER_ROWS(ps
) < 1){
248 q_status_message(SM_ORDER
| SM_DING
, 0, 3,
249 _("Screen too small to view message"));
250 ps
->next_screen
= mail_index_screen
;
254 /*----------------- Loop viewing messages ------------------*/
257 ps
->user_says_cancel
= 0;
258 ps
->some_quoting_was_suppressed
= 0;
261 * Check total to make sure there's something to view. Check it
262 * inside the loop to make sure everything wasn't expunged while
263 * we were viewing. If so, make sure we don't just come back.
265 if(mn_get_total(ps
->msgmap
) <= 0L || !ps
->mail_stream
){
266 q_status_message(SM_ORDER
, 0, 3, _("No messages to read!"));
267 ps
->next_screen
= mail_index_screen
;
271 we_cancel
= busy_cue(NULL
, NULL
, 1);
273 if(mn_get_cur(ps
->msgmap
) <= 0L)
274 mn_set_cur(ps
->msgmap
,
275 THREADING() ? first_sorted_flagged(F_NONE
,
280 raw_msgno
= mn_m2raw(ps
->msgmap
, mn_get_cur(ps
->msgmap
));
283 || !(env
= pine_mail_fetchstructure(ps
->mail_stream
,raw_msgno
,&body
))
284 || !(raw_msgno
> 0L && ps
->mail_stream
285 && raw_msgno
<= ps
->mail_stream
->nmsgs
286 && (mc
= mail_elt(ps
->mail_stream
, raw_msgno
)))){
287 q_status_message1(SM_ORDER
, 3, 3,
288 "Error getting message %s data",
289 comatose(mn_get_cur(ps
->msgmap
)));
290 dprint((1, "!!!! ERROR fetching %s of msg %ld\n",
291 env
? "elt" : "env", mn_get_cur(ps
->msgmap
)));
293 ps
->next_screen
= mail_index_screen
;
297 ps
->unseen_in_view
= !mc
->seen
;
299 init_handles(&handles
);
301 store
= so_get(src
, NULL
, EDIT_ACCESS
);
302 so_truncate(store
, format_size_guess(body
) + 2048);
304 view_writec_init(store
, &handles
, SCROLL_LINES_ABOVE(ps
),
305 SCROLL_LINES_ABOVE(ps
) +
306 ps
->ttyo
->screen_rows
- (SCROLL_LINES_ABOVE(ps
)
307 + SCROLL_LINES_BELOW(ps
)));
310 if(last_message_viewed
!= mn_get_cur(ps
->msgmap
)
311 || last_was_full_header
== 2 || cmd
== MC_TOGGLE
)
312 flags
|= FM_NEW_MESS
;
314 if(F_OFF(F_QUELL_FULL_HDR_RESET
, ps_global
)
315 && last_message_viewed
!= -1L
316 && last_message_viewed
!= mn_get_cur(ps
->msgmap
))
319 if(offset
) /* no pre-paint during resize */
320 view_writec_killbuf();
323 /* Attempt to handle S/MIME bodies */
325 ps
->smime
->need_passphrase
= 0;
327 if(F_OFF(F_DONT_DO_SMIME
, ps_global
) && fiddle_smime_message(body
, raw_msgno
))
328 flags
|= FM_NEW_MESS
; /* body was changed, force a reload */
332 mswin_noscrollupdate(1);
334 ps
->cur_uid_stream
= ps
->mail_stream
;
335 ps
->cur_uid
= mail_uid(ps
->mail_stream
, raw_msgno
);
336 (void) format_message(raw_msgno
, env
, body
, &handles
, flags
| force_prefer
,
339 mswin_noscrollupdate(0);
342 view_writec_destroy();
344 last_message_viewed
= mn_get_cur(ps
->msgmap
);
345 last_was_full_header
= ps
->full_header
;
347 ps
->next_screen
= SCREEN_FUN_NULL
;
349 memset(&scrollargs
, 0, sizeof(SCROLL_S
));
350 scrollargs
.text
.text
= so_text(store
);
351 scrollargs
.text
.src
= src
;
352 scrollargs
.text
.desc
= "message";
355 * make first selectable handle the default
361 while(!scroll_handle_selectable(hp
) && hp
!= NULL
)
364 if((scrollargs
.text
.handles
= hp
) != NULL
)
365 scrollargs
.body_valid
= (hp
== handles
);
367 free_handles(&handles
);
370 scrollargs
.body_valid
= 1;
372 if(offset
){ /* resize? preserve paging! */
373 scrollargs
.start
.on
= Offset
;
374 scrollargs
.start
.loc
.offset
= offset
;
375 scrollargs
.body_valid
= 0;
379 scrollargs
.use_indexline_color
= 1;
381 /* TRANSLATORS: a screen title */
382 scrollargs
.bar
.title
= _("MESSAGE TEXT");
383 scrollargs
.end_scroll
= view_end_scroll
;
384 scrollargs
.resize_exit
= 1;
385 scrollargs
.help
.text
= h_mail_view
;
386 scrollargs
.help
.title
= _("HELP FOR MESSAGE TEXT VIEW");
387 scrollargs
.keys
.menu
= &view_keymenu
;
388 scrollargs
.keys
.what
= save_what
;
389 setbitmap(scrollargs
.keys
.bitmap
);
390 if(F_OFF(F_ENABLE_PIPE
, ps_global
))
391 clrbitn(VIEW_PIPE_KEY
, scrollargs
.keys
.bitmap
);
394 * turn off attachment viewing for raw msg txt, atts
395 * haven't been set up at this point
397 if(ps_global
->full_header
== 2
398 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))
399 clrbitn(VIEW_ATT_KEY
, scrollargs
.keys
.bitmap
);
401 if(F_OFF(F_ENABLE_BOUNCE
, ps_global
))
402 clrbitn(BOUNCE_KEY
, scrollargs
.keys
.bitmap
);
404 if(F_OFF(F_ENABLE_FLAG
, ps_global
))
405 clrbitn(FLAG_KEY
, scrollargs
.keys
.bitmap
);
407 if(F_OFF(F_ENABLE_AGG_OPS
, ps_global
))
408 clrbitn(VIEW_SELECT_KEY
, scrollargs
.keys
.bitmap
);
410 if(F_OFF(F_ENABLE_FULL_HDR
, ps_global
))
411 clrbitn(VIEW_FULL_HEADERS_KEY
, scrollargs
.keys
.bitmap
);
414 if(!(ps
->smime
&& ps
->smime
->need_passphrase
))
415 clrbitn(DECRYPT_KEY
, scrollargs
.keys
.bitmap
);
417 if(F_ON(F_DONT_DO_SMIME
, ps_global
)){
418 clrbitn(DECRYPT_KEY
, scrollargs
.keys
.bitmap
);
419 clrbitn(SECURITY_KEY
, scrollargs
.keys
.bitmap
);
425 * NOTE: the comment below only really makes sense if we
426 * decide to replace the "Attachment Index" with
427 * a model that lets you highlight the attachments
428 * in the header. In a way its consistent, but
429 * would mean more keymenu monkeybusiness since not
430 * all things would be available all the time. No wait.
431 * Then what would "Save" mean; the attachment, url or
432 * message you're currently viewing? Would Save
433 * of a message then only be possible from the message
434 * index? Clumsy. what about arrow-navigation. isn't
435 * that now inconsistent? Maybe next/prev url shouldn't
436 * be bound to the arrow/^N/^P navigation?
438 clrbitn(VIEW_VIEW_HANDLE
, scrollargs
.keys
.bitmap
);
439 clrbitn(VIEW_PREV_HANDLE
, scrollargs
.keys
.bitmap
);
440 clrbitn(VIEW_NEXT_HANDLE
, scrollargs
.keys
.bitmap
);
444 scrollargs
.mouse
.popup
= format_message_popup
;
447 if(((cmd
= scrolltool(&scrollargs
)) == MC_RESIZE
448 || (cmd
== MC_FULLHDR
&& ps_global
->full_header
== 1))
449 && scrollargs
.start
.on
== Offset
)
450 offset
= scrollargs
.start
.loc
.offset
;
452 if(cmd
== MC_TOGGLE
&& ps
->force_prefer_plain
== 0 && ps
->force_no_prefer_plain
== 0){
453 if(F_ON(F_PREFER_PLAIN_TEXT
, ps_global
))
454 ps
->force_no_prefer_plain
= 1;
456 ps
->force_prefer_plain
= 1;
459 ps
->force_prefer_plain
= ps
->force_no_prefer_plain
= 0;
463 * We could use the values directly but this is the way it
464 * already worked with the flags, so leave it alone.
466 if(ps
->force_prefer_plain
== 0 && ps
->force_no_prefer_plain
== 0)
468 else if(ps
->force_prefer_plain
)
469 force_prefer
= FM_FORCEPREFPLN
;
470 else if(ps
->force_no_prefer_plain
)
471 force_prefer
= FM_FORCENOPREFPLN
;
473 save_what
= scrollargs
.keys
.what
;
474 ps_global
->unseen_in_view
= 0;
475 so_give(&store
); /* free resources associated with store */
476 free_handles(&handles
);
478 mswin_destroyicons();
481 while(ps
->next_screen
== SCREEN_FUN_NULL
);
486 ps
->force_prefer_plain
= ps
->force_no_prefer_plain
= 0;
488 ps
->cur_uid_stream
= NULL
;
492 * Unless we're going into attachment screen,
493 * start over with full_header.
495 if(F_OFF(F_QUELL_FULL_HDR_RESET
, ps_global
)
496 && ps
->next_screen
!= attachment_screen
)
503 * view_writec_init - function to create and init struct that manages
504 * writing to the display what we can as soon
508 view_writec_init(STORE_S
*store
, HANDLE_S
**handlesp
, int first_line
, int last_line
)
510 char tmp
[MAILTMPLEN
];
512 g_view_write
= (struct view_write_s
*)fs_get(sizeof(struct view_write_s
));
513 memset(g_view_write
, 0, sizeof(struct view_write_s
));
514 g_view_write
->store
= store
;
515 g_view_write
->handles
= handlesp
;
516 g_view_write
->screen_line
= first_line
;
517 g_view_write
->last_screen_line
= last_line
;
519 if(!dfilter_trigger(NULL
, tmp
, sizeof(tmp
))){
520 g_view_write
->line
= (char *) fs_get(LINEBUFSIZ
*sizeof(char));
523 scroll_setrange(0L, 0L);
526 if(ps_global
->VAR_DISPLAY_FILTERS
)
527 ClearLines(first_line
, last_line
- 1);
534 view_writec_destroy(void)
537 if(g_view_write
->line
&& g_view_write
->index
)
538 view_writec('\n'); /* flush pending output! */
540 while(g_view_write
->screen_line
< g_view_write
->last_screen_line
)
541 ClearLine(g_view_write
->screen_line
++);
543 view_writec_killbuf();
545 fs_give((void **) &g_view_write
);
556 view_writec_killbuf(void)
558 if(g_view_write
->line
)
559 fs_give((void **) &g_view_write
->line
);
565 * view_screen_pc - write chars into the final buffer
570 static int in_color
= 0;
572 if(g_view_write
->line
){
574 * This only works if the 2nd and 3rd parts of the || don't happen.
575 * The only way it breaks is if we get a really, really long line
576 * because there are oodles of tags in it. In that case we will
577 * wrap incorrectly or split a tag across lines (losing color perhaps)
578 * but we won't crash.
582 (char)c
!= TAG_EMBED
&&
583 g_view_write
->index
>= (LINEBUFSIZ
- 20 * (RGBLEN
+2))) ||
584 (g_view_write
->index
>= LINEBUFSIZ
- 1)){
589 ClearLine(g_view_write
->screen_line
);
591 g_view_write
->line
[g_view_write
->index
++] = (char) c
;
593 PutLine0n8b(g_view_write
->screen_line
++, 0,
594 g_view_write
->line
, g_view_write
->index
,
595 g_view_write
->handles
? *g_view_write
->handles
: NULL
);
598 rv
= so_nputs(g_view_write
->store
,
600 g_view_write
->index
);
601 g_view_write
->index
= 0;
603 if(g_view_write
->screen_line
>= g_view_write
->last_screen_line
){
604 fs_give((void **) &g_view_write
->line
);
615 * Don't split embedded things over multiple lines. Colors are
616 * the longest tags so use their length.
618 if((char)c
== TAG_EMBED
)
623 g_view_write
->line
[g_view_write
->index
++] = (char) c
;
628 else if(c
== '\n' && g_view_write
->lines
++ > SCROLL_LINES(ps_global
))
629 scroll_setrange(SCROLL_LINES(ps_global
),
630 g_view_write
->lines
+ SCROLL_LINES(ps_global
));
633 return(so_writec(c
, g_view_write
->store
));
637 view_end_scroll(SCROLL_S
*sparms
)
639 int done
= 0, result
, force
;
641 if(F_ON(F_ENABLE_SPACE_AS_TAB
, ps_global
)){
643 if(F_ON(F_ENABLE_TAB_DELETES
, ps_global
)){
646 /* Let the TAB advance cur msgno for us */
647 save_msgno
= mn_get_cur(ps_global
->msgmap
);
648 (void) cmd_delete(ps_global
, ps_global
->msgmap
, MCMD_NONE
, NULL
);
649 mn_set_cur(ps_global
->msgmap
, save_msgno
);
652 /* act like the user hit a TAB */
653 result
= process_cmd(ps_global
, ps_global
->mail_stream
,
654 ps_global
->msgmap
, MC_TAB
, View
, &force
);
665 * format_size_guess -- Run down the given body summing the text/plain
666 * pieces we're likely to display. It need only
667 * be a guess since this is intended for preallocating
668 * our display buffer...
671 format_size_guess(struct mail_bodystruct
*body
)
675 char *free_me
= NULL
;
678 if(body
->type
== TYPEMULTIPART
){
681 for(part
= body
->nested
.part
; part
; part
= part
->next
)
682 size
+= format_size_guess(&part
->body
);
684 else if(body
->type
== TYPEMESSAGE
685 && body
->subtype
&& !strucmp(body
->subtype
, "rfc822"))
686 size
= format_size_guess(body
->nested
.msg
->body
);
687 else if((!body
->type
|| body
->type
== TYPETEXT
)
688 && (!body
->subtype
|| !strucmp(body
->subtype
, "plain"))
689 && ((body
->disposition
.type
690 && !strucmp(body
->disposition
.type
, "inline"))
691 || !(free_me
= parameter_val(body
->parameter
, "name")))){
693 * Handles and colored quotes cause memory overhead. Figure about
694 * 100 bytes per level of quote per line and about 100 bytes per
695 * handle. Make a guess of 1 handle or colored quote per
696 * 10 lines of message. If we guess too low, we'll do a resize in
697 * so_cs_writec. Most of the overhead comes from the colors, so
698 * if we can see we don't do colors or don't have these features
699 * turned on, skip it.
701 if(pico_usingcolor() &&
702 ((ps_global
->VAR_QUOTE1_FORE_COLOR
&&
703 ps_global
->VAR_QUOTE1_BACK_COLOR
) ||
704 F_ON(F_VIEW_SEL_URL
, ps_global
) ||
705 F_ON(F_VIEW_SEL_URL_HOST
, ps_global
) ||
706 F_ON(F_SCAN_ADDR
, ps_global
)))
707 extra
= MIN(100/10 * body
->size
.lines
, MAX_FUDGE
);
709 size
= body
->size
.bytes
+ extra
;
713 fs_give((void **) &free_me
);
721 scroll_handle_prompt(HANDLE_S
*handle
, int force
)
723 char prompt
[256], tmp
[MAILTMPLEN
];
724 int rc
, flags
, local_h
;
725 static ESCKEY_S launch_opts
[] = {
726 /* TRANSLATORS: command names, editURL means user gets to edit a URL if they
727 want, editApp is edit application where they edit the application used to
729 {'y', 'y', "Y", N_("Yes")},
730 {'n', 'n', "N", N_("No")},
733 {0, 'u', "U", N_("editURL")},
734 {0, 'a', "A", N_("editApp")},
735 {-1, 0, NULL
, NULL
}};
737 if(handle
->type
== URL
){
738 launch_opts
[4].ch
= 'u';
740 if((!(local_h
= !struncmp(handle
->h
.url
.path
, "x-alpine-", 9))
741 || !(local_h
= !struncmp(handle
->h
.url
.path
, "x-pine-help", 11)))
742 && (handle
->h
.url
.tool
743 || ((local_h
= url_local_handler(handle
->h
.url
.path
) != NULL
)
744 && (handle
->h
.url
.tool
= url_external_handler(handle
,1)))
746 && (handle
->h
.url
.tool
= url_external_handler(handle
,0))))){
748 /* if NOT special DDE hack */
749 if(handle
->h
.url
.tool
[0] != '*')
751 if(ps_global
->vars
[V_BROWSER
].is_fixed
)
752 launch_opts
[5].ch
= -1;
754 launch_opts
[5].ch
= 'a';
757 launch_opts
[5].ch
= -1;
759 if(ps_global
->vars
[V_BROWSER
].is_fixed
){
760 q_status_message(SM_ORDER
, 3, 4,
761 _("URL-Viewer is disabled by sys-admin"));
765 /* TRANSLATORS: a question */
766 if(want_to(_("No URL-Viewer application defined. Define now"),
767 'y', 0, NO_HELP
, WT_SEQ_SENSITIVE
) == 'y'){
768 /* Prompt for the displayer? */
771 flags
= OE_APPEND_CURRENT
|
773 OE_KEEP_TRAILING_SPACE
;
775 rc
= optionally_enter(tmp
, -FOOTER_ROWS(ps_global
), 0,
778 NULL
, NO_HELP
, &flags
);
780 if((flags
& OE_USER_MODIFIED
) && *tmp
){
781 if(can_access(tmp
, EXECUTE_ACCESS
) == 0){
786 * Save it for next time...
788 for(l
= ps_global
->VAR_BROWSER
, n
= 0;
793 l
= (char **) fs_get((n
+2)*sizeof(char *));
795 ps_global
->VAR_BROWSER
796 && ps_global
->VAR_BROWSER
[n
];
798 l
[n
] = cpystr(ps_global
->VAR_BROWSER
[n
]);
800 l
[n
++] = cpystr(tmp
);
803 set_variable_list(V_BROWSER
, l
, TRUE
, Main
);
806 handle
->h
.url
.tool
= cpystr(tmp
);
810 q_status_message1(SM_ORDER
| SM_DING
, 2, 2,
811 _("Browser not found: %s"),
812 error_description(errno
));
819 else if(rc
== 1 || rc
== -1){
823 if(ps_global
->redrawer
)
824 (*ps_global
->redrawer
)();
835 launch_opts
[4].ch
= -1;
838 || (handle
->type
== URL
839 && (!struncmp(handle
->h
.url
.path
, "x-alpine-", 9)
840 || !struncmp(handle
->h
.url
.path
, "x-pine-help", 11))))
844 int sc
= ps_global
->ttyo
->screen_cols
;
847 * Customize the prompt for mailto, all the other schemes make
848 * sense if you just say View selected URL ...
850 if(handle
->type
== URL
&&
851 !struncmp(handle
->h
.url
.path
, "mailto:", 7))
852 snprintf(prompt
, sizeof(prompt
), "Compose mail to \"%.*s%s\" ? ",
853 (int) MIN(MAX(0,sc
- 25), sizeof(prompt
)-50), handle
->h
.url
.path
+7,
854 (strlen(handle
->h
.url
.path
+7) > MAX(0,sc
-25)) ? "..." : "");
856 snprintf(prompt
, sizeof(prompt
), "View selected %s %s%.*s%s ? ",
857 (handle
->type
== URL
) ? "URL" : "Attachment",
858 (handle
->type
== URL
) ? "\"" : "",
859 (int) MIN(MAX(0,sc
-27), sizeof(prompt
)-50),
860 (handle
->type
== URL
) ? handle
->h
.url
.path
: "",
861 (handle
->type
== URL
)
862 ? ((strlen(handle
->h
.url
.path
) > MAX(0,sc
-27))
863 ? "...\"" : "\"") : "");
865 prompt
[sizeof(prompt
)-1] = '\0';
867 switch(radio_buttons(prompt
, -FOOTER_ROWS(ps_global
),
868 launch_opts
, 'y', 'n', NO_HELP
, RB_SEQ_SENSITIVE
)){
873 strncpy(tmp
, handle
->h
.url
.path
, sizeof(tmp
)-1);
874 tmp
[sizeof(tmp
)-1] = '\0';
876 flags
= OE_APPEND_CURRENT
|
878 OE_KEEP_TRAILING_SPACE
;
880 rc
= optionally_enter(tmp
, -FOOTER_ROWS(ps_global
), 0,
881 sizeof(tmp
), _("Edit URL: "),
882 NULL
, NO_HELP
, &flags
);
884 if(flags
& OE_USER_MODIFIED
){
885 if(handle
->h
.url
.path
)
886 fs_give((void **) &handle
->h
.url
.path
);
888 handle
->h
.url
.path
= cpystr(tmp
);
893 else if(rc
== 1 || rc
== -1){
897 if(ps_global
->redrawer
)
898 (*ps_global
->redrawer
)();
905 if(handle
->h
.url
.tool
){
906 strncpy(tmp
, handle
->h
.url
.tool
, sizeof(tmp
)-1);
907 tmp
[sizeof(tmp
)-1] = '\0';
913 flags
= OE_APPEND_CURRENT
|
915 OE_KEEP_TRAILING_SPACE
|
918 rc
= optionally_enter(tmp
, -FOOTER_ROWS(ps_global
), 0,
919 sizeof(tmp
), _("Viewer Command: "),
920 NULL
, NO_HELP
, &flags
);
922 if(flags
& OE_USER_MODIFIED
){
923 if(handle
->h
.url
.tool
)
924 fs_give((void **) &handle
->h
.url
.tool
);
926 handle
->h
.url
.tool
= cpystr(tmp
);
931 else if(rc
== 1 || rc
== -1){
935 if(ps_global
->redrawer
)
936 (*ps_global
->redrawer
)();
952 scroll_handle_launch(HANDLE_S
*handle
, int force
)
954 switch(handle
->type
){
956 if(handle
->h
.url
.path
){
957 if(scroll_handle_prompt(handle
, force
)){
958 if(url_launch(handle
)
959 || ps_global
->next_screen
!= SCREEN_FUN_NULL
)
960 return(1); /* done with this screen */
969 if(scroll_handle_prompt(handle
, force
))
970 display_attachment(mn_m2raw(ps_global
->msgmap
,
971 mn_get_cur(ps_global
->msgmap
)),
972 handle
->h
.attach
, DA_FROM_VIEW
| DA_DIDPROMPT
);
982 display_vevent_summary(mn_m2raw(ps_global
->msgmap
, mn_get_cur(ps_global
->msgmap
)),
983 handle
->h
.ical
.attach
,
984 DA_FROM_VIEW
| DA_DIDPROMPT
, handle
->h
.ical
.depth
);
988 (*handle
->h
.func
.f
)(handle
->h
.func
.args
.stream
,
989 handle
->h
.func
.args
.msgmap
,
990 handle
->h
.func
.args
.msgno
);
995 alpine_panic("Unexpected HANDLE type");
1003 scroll_handle_obscured(HANDLE_S
*handle
)
1005 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1007 return(handle_on_page(handle
, st
->top_text_line
,
1008 st
->top_text_line
+ st
->screen
.length
));
1014 * scroll_handle_in_frame -- return handle pointer to visible handle.
1017 scroll_handle_in_frame(long int top_line
)
1019 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1022 switch(handle_on_page(hp
= st
->parms
->text
.handles
, top_line
,
1023 top_line
+ st
->screen
.length
)){
1024 case -1 : /* handle above page */
1025 /* Find first handle from top of page */
1026 for(hp
= st
->parms
->text
.handles
->next
; hp
; hp
= hp
->next
)
1027 if(scroll_handle_selectable(hp
))
1028 switch(handle_on_page(hp
, top_line
, top_line
+ st
->screen
.length
)){
1029 case 0 : return(hp
);
1030 case 1 : return(NULL
);
1031 case -1 : default : break;
1036 case 1 : /* handle below page */
1037 /* Find first handle from top of page */
1038 for(hp
= st
->parms
->text
.handles
->prev
; hp
; hp
= hp
->prev
)
1039 if(scroll_handle_selectable(hp
))
1040 switch(handle_on_page(hp
, top_line
, top_line
+ st
->screen
.length
)){
1041 case 0 : return(hp
);
1042 case -1 : return(NULL
);
1043 case 1 : default : break;
1057 * scroll_handle_reframe -- adjust display params to display given handle
1060 scroll_handle_reframe(int key
, int center
)
1062 long l
, offset
, dlines
, start_line
;
1063 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1065 dlines
= PGSIZE(st
);
1066 offset
= (st
->parms
->text
.src
== FileStar
) ? st
->top_text_line
: 0;
1067 start_line
= st
->top_text_line
;
1070 key
= st
->parms
->text
.handles
->key
;
1072 /* Searc down from the top line */
1073 for(l
= start_line
; l
< st
->num_lines
; l
++) {
1074 if(st
->parms
->text
.src
== FileStar
&& l
> offset
+ dlines
)
1075 ScrollFile(offset
+= dlines
);
1077 if(handle_on_line(l
- offset
, key
))
1081 if(l
< st
->num_lines
){
1082 if(l
>= dlines
+ start_line
) /* bingo! */
1083 start_line
= l
- ((center
? (dlines
/ 2) : dlines
) - 1);
1086 if(st
->parms
->text
.src
== FileStar
) /* wrap offset */
1087 ScrollFile(offset
= 0);
1089 for(l
= 0; l
< start_line
; l
++) {
1090 if(st
->parms
->text
.src
== FileStar
&& l
> offset
+ dlines
)
1091 ScrollFile(offset
+= dlines
);
1093 if(handle_on_line(l
- offset
, key
))
1098 alpine_panic("Internal Error: no handle found");
1108 handle_on_line(long int line
, int goal
)
1111 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1113 for(i
= 0; i
< st
->line_lengths
[line
]; i
++)
1114 if(st
->text_lines
[line
][i
] == TAG_EMBED
1115 && st
->text_lines
[line
][++i
] == TAG_HANDLE
){
1116 for(key
= 0, n
= st
->text_lines
[line
][++i
]; n
; n
--)
1117 key
= (key
* 10) + (st
->text_lines
[line
][++i
] - '0');
1128 handle_on_page(HANDLE_S
*handle
, long int first_line
, long int last_line
)
1133 if(handle
&& (l
= handle
->loc
)){
1134 for( ; l
; l
= l
->next
)
1135 if(l
->where
.row
< first_line
){
1139 else if(l
->where
.row
>= last_line
){
1144 return(0); /* found! */
1152 scroll_handle_selectable(HANDLE_S
*handle
)
1154 return(handle
&& (handle
->type
!= URL
1155 || (handle
->h
.url
.path
&& *handle
->h
.url
.path
)));
1160 scroll_handle_next_sel(HANDLE_S
*handles
)
1162 while(handles
&& !scroll_handle_selectable(handles
= handles
->next
))
1170 scroll_handle_prev_sel(HANDLE_S
*handles
)
1172 while(handles
&& !scroll_handle_selectable(handles
= handles
->prev
))
1180 scroll_handle_next(HANDLE_S
*handles
)
1182 HANDLE_S
*next
= NULL
;
1184 if(scroll_handle_obscured(handles
) <= 0){
1186 while((next
= scroll_handle_next_sel(next
))
1187 && scroll_handle_obscured(next
))
1197 scroll_handle_prev(HANDLE_S
*handles
)
1199 HANDLE_S
*prev
= NULL
;
1201 if(scroll_handle_obscured(handles
) >= 0){
1203 while((prev
= scroll_handle_prev_sel(prev
))
1204 && scroll_handle_obscured(prev
))
1213 scroll_handle_boundary(HANDLE_S
*handle
, HANDLE_S
*(*f
) (HANDLE_S
*))
1215 HANDLE_S
*hp
, *whp
= NULL
;
1217 /* Multi-line handle? Punt! */
1218 if(handle
&& (!(hp
= handle
)->loc
->next
))
1219 while((hp
= (*f
)(hp
))
1220 && hp
->loc
->where
.row
== handle
->loc
->where
.row
)
1228 scroll_handle_column(int line
, int offset
)
1230 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1234 limit
= (offset
> -1) ? offset
: st
->line_lengths
[line
];
1236 for(i
= 0, col
= 0; i
< limit
;){
1237 if(st
&& st
->text_lines
&& st
->text_lines
[line
])
1238 switch(st
->text_lines
[line
][i
]){
1241 switch((i
< limit
) ? st
->text_lines
[line
][i
] : 0){
1243 for(key
= 0, n
= st
->text_lines
[line
][++i
]; n
> 0 && i
< limit
-1; n
--)
1244 key
= (key
* 10) + (st
->text_lines
[line
][++i
] - '0');
1251 i
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
1263 default: /* literal embed char */
1271 while(((++col
) & 0x07) != 0) /* add tab's spaces */
1277 col
+= width_at_this_position((unsigned char*) &st
->text_lines
[line
][i
],
1278 st
->line_lengths
[line
] - i
);
1289 scroll_handle_index(int row
, int column
)
1291 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1294 for(index
= 0; column
> 0;)
1295 switch(st
->text_lines
[row
][index
++]){
1297 switch(st
->text_lines
[row
][index
++]){
1299 index
+= st
->text_lines
[row
][index
] + 1;
1313 case TAB
: /* add tab's spaces */
1314 while(((--column
) & 0x07) != 0)
1329 scroll_handle_set_loc(POSLIST_S
**lpp
, int line
, int column
)
1333 lp
= (POSLIST_S
*) fs_get(sizeof(POSLIST_S
));
1334 lp
->where
.row
= line
;
1335 lp
->where
.col
= column
;
1337 for(; *lpp
; lpp
= &(*lpp
)->next
)
1345 dot_on_handle(long int line
, int goal
)
1347 int i
= 0, n
, key
= 0, column
= -1;
1348 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1351 if(i
>= st
->line_lengths
[line
])
1354 switch(st
->text_lines
[line
][i
++]){
1356 switch(st
->text_lines
[line
][i
++]){
1358 for(key
= 0, n
= st
->text_lines
[line
][i
++]; n
; n
--)
1359 key
= (key
* 10) + (st
->text_lines
[line
][i
++] - '0');
1365 i
+= RGBLEN
; /* advance past color setting */
1376 while(((++column
) & 0x07) != 0) /* add tab's spaces */
1382 column
+= width_at_this_position((unsigned char*) &st
->text_lines
[line
][i
-1],
1383 st
->line_lengths
[line
] - (i
-1));
1387 while(column
< goal
);
1394 * url_launch - Sniff the given url, see if we can do anything with
1395 * it. If not, hand off to user-defined app.
1399 url_launch(HANDLE_S
*handle
)
1403 #define URL_MAX_LAUNCH (2 * MAILTMPLEN)
1405 if(handle
->h
.url
.tool
){
1406 char *toolp
, *cmdp
, *p
, cmd
[URL_MAX_LAUNCH
+ 4];
1407 int mode
, copied
= 0;
1410 toolp
= handle
->h
.url
.tool
;
1412 /* This code used to quote a URL to prevent arbitrary command execution
1413 * through a URL. The plan was to quote the URL with single quotes,
1414 * and this used to work. BUT some shells do not care about quoting
1415 * and interpret some characters regardless of single quotes. The
1416 * simplest solution is to escape those characters, but then some
1417 * shells will see the escape characters and some others will not.
1418 * It's a mess. There are several ways to go around this mess,
1419 * including adding configuration options (one more!?), or forget
1420 * about shells. What we do is to forget about shells, and execute
1421 * the code as a PIPE_NOSHELL.
1425 while(cmdp
-cmd
< URL_MAX_LAUNCH
)
1426 if((!*toolp
&& !copied
)
1427 || (*toolp
== '_' && !strncmp(toolp
+ 1, "URL_", 4))){
1429 /* implicit _URL_ at end */
1433 if(cmdp
[-1] == '\'') /* unquote old '_URL_' */
1437 for(p
= handle
->h
.url
.path
;
1438 p
&& *p
&& cmdp
-cmd
< URL_MAX_LAUNCH
; p
++)
1444 toolp
+= 5; /* length of "_URL_" */
1449 if(!(*cmdp
++ = *toolp
++))
1452 if(cmdp
-cmd
>= URL_MAX_LAUNCH
)
1453 return(url_launch_too_long(rv
));
1455 mode
= PIPE_RESET
| PIPE_USER
| PIPE_RUNNOW
| PIPE_NOSHELL
;
1456 if((syspipe
= open_system_pipe(cmd
, NULL
, NULL
, mode
, 0, pipe_callback
, pipe_report_error
)) != NULL
){
1457 close_system_pipe(&syspipe
, NULL
, pipe_callback
);
1458 q_status_message(SM_ORDER
, 0, 4, _("VIEWER command completed"));
1461 q_status_message1(SM_ORDER
, 3, 4,
1462 /* TRANSLATORS: Cannot start command : <command name> */
1463 _("Cannot start command : %s"), cmd
);
1465 else if((f
= url_local_handler(handle
->h
.url
.path
)) != NULL
){
1466 if((*f
)(handle
->h
.url
.path
) > 1)
1470 q_status_message1(SM_ORDER
, 2, 2,
1471 _("\"URL-Viewer\" not defined: Can't open %s"),
1472 handle
->h
.url
.path
);
1479 url_launch_too_long(int return_value
)
1481 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
1482 "Can't spawn. Command too long.");
1483 return(return_value
);
1488 url_external_handler(HANDLE_S
*handle
, int specific
)
1490 char **l
, *test
, *cmd
, *p
, *q
, *ep
;
1491 int i
, specific_match
;
1493 for(l
= ps_global
->VAR_BROWSER
; l
&& *l
; l
++){
1494 get_pair(*l
, &test
, &cmd
, 0, 1);
1495 dprint((5, "TEST: \"%s\" CMD: \"%s\"\n",
1496 test
? test
: "<NULL>", cmd
? cmd
: "<NULL>"));
1497 removing_quotes(cmd
);
1498 if(valid_filter_command(&cmd
)){
1501 if((p
= test
) != NULL
){
1504 if(!strncmp(p
+1, "TEST(", 5)
1505 && (ep
= strstr(p
+6, ")_"))){
1508 if(exec_mailcap_test_cmd(p
+6) == 0){
1512 dprint((5,"failed handler TEST\n"));
1513 fs_give((void **) &cmd
);
1516 else if(!strncmp(p
+1, "SCHEME(", 7)
1517 && (ep
= strstr(p
+8, ")_"))){
1522 if((q
= strchr(p
, ',')) != NULL
)
1526 while(!((i
= strlen(p
))
1528 && handle
->h
.url
.path
[i
- 1] == ':')
1530 && handle
->h
.url
.path
[i
] == ':'))
1531 && !struncmp(handle
->h
.url
.path
, p
, i
))
1539 dprint((5,"failed handler SCHEME\n"));
1540 fs_give((void **) &cmd
);
1544 dprint((5, "UNKNOWN underscore test\n"));
1545 fs_give((void **) &cmd
);
1548 else if(isspace((unsigned char) *p
)){
1552 dprint((1,"bogus handler test: \"%s\"",
1553 test
? test
: "?"));
1554 fs_give((void **) &cmd
);
1557 fs_give((void **) &test
);
1560 if(cmd
&& (!specific
|| specific_match
))
1565 fs_give((void **) &test
);
1568 fs_give((void **) &cmd
);
1574 cmd
= url_os_specified_browser(handle
->h
.url
.path
);
1576 * Last chance, anything handling "text/html" in mailcap...
1578 if(!cmd
&& mailcap_can_display(TYPETEXT
, "html", NULL
, 0)){
1581 mc_cmd
= mailcap_build_command(TYPETEXT
, "html",
1582 NULL
, "_URL_", NULL
, 0);
1584 * right now URL viewing won't return anything requiring
1588 cmd
= mc_cmd
->command
;
1589 fs_give((void **)&mc_cmd
);
1599 url_local_handler(char *s
)
1602 static struct url_t
{
1607 {"mailto:", 7, url_local_mailto
}, /* see url_tool_t def's */
1608 {"imap://", 7, url_local_imap
}, /* for explanations */
1609 {"nntp://", 7, url_local_nntp
},
1610 {"file://", 7, url_local_file
},
1612 {"ldap://", 7, url_local_ldap
},
1614 {"news:", 5, url_local_news
},
1615 {"x-alpine-ical:", 14, ical_send_reply
},
1616 {"x-alpine-phone-home:", 20, url_local_phone_home
},
1617 {"x-alpine-gripe:", 15, gripe_gripe_to
},
1618 {"x-alpine-help:", 14, url_local_helper
},
1619 {"x-pine-help:", 12, url_local_helper
},
1620 {"x-alpine-config:", 16, url_local_config
},
1621 {"x-alpine-cert:", 14, url_local_certdetails
},
1622 {"#", 1, url_local_fragment
},
1626 for(i
= 0; handlers
[i
].url
; i
++)
1627 if(!struncmp(s
, handlers
[i
].url
, handlers
[i
].len
))
1628 return(handlers
[i
].f
);
1636 * mailto URL digester ala draft-hoffman-mailto-url-02.txt
1639 url_local_mailto(char *url
)
1641 return(url_local_mailto_and_atts(url
, NULL
));
1645 url_local_mailto_and_atts(char *url
, PATMT
*attachlist
)
1650 char *sig
, *urlp
, *p
, *hname
, *hvalue
;
1653 int was_a_body
= 0, impl
, template_len
= 0;
1656 REDRAFT_POS_S
*redraft_pos
= NULL
;
1657 ACTION_S
*role
= NULL
;
1659 outgoing
= mail_newenvelope();
1660 outgoing
->message_id
= generate_message_id();
1661 body
= mail_newbody();
1662 body
->type
= TYPETEXT
;
1663 if((body
->contents
.text
.data
= (void *) so_get(PicoText
,NULL
,EDIT_ACCESS
)) != NULL
){
1667 * mailtoURL = "mailto:" [ to ] [ headers ]
1669 * headers = "?" header *( "&" header )
1670 * header = hname "=" hvalue
1674 * NOTE2: "from" and "bcc" are intentionally excluded from
1675 * the list of understood "header" fields
1677 if((p
= strchr(urlp
= cpystr(url
+7), '?')) != NULL
)
1678 *p
++ = '\0'; /* headers? Tie off mailbox */
1680 /* grok mailbox as first "to", then roll thru specific headers */
1682 rfc822_parse_adrlist(&outgoing
->to
,
1684 ps_global
->maildomain
);
1687 /* Find next "header" */
1688 if((p
= strchr(hname
= p
, '&')) != NULL
)
1689 *p
++ = '\0'; /* tie off "header" */
1691 if((hvalue
= strchr(hname
, '=')) != NULL
)
1692 *hvalue
++ = '\0'; /* tie off hname */
1694 if(!hvalue
|| !strucmp(hname
, "subject")){
1695 char *sub
= rfc1738_str(hvalue
? hvalue
: hname
);
1697 if(outgoing
->subject
){
1698 int len
= strlen(outgoing
->subject
);
1700 fs_resize((void **) &outgoing
->subject
,
1701 (len
+ strlen(sub
) + 2) * sizeof(char));
1702 snprintf(outgoing
->subject
+ len
, strlen(sub
)+2, " %s", sub
);
1703 outgoing
->subject
[len
+ strlen(sub
) + 2 - 1] = '\0';
1706 outgoing
->subject
= cpystr(sub
);
1708 else if(!strucmp(hname
, "to")){
1709 url_mailto_addr(&outgoing
->to
, hvalue
);
1711 else if(!strucmp(hname
, "cc")){
1712 url_mailto_addr(&outgoing
->cc
, hvalue
);
1714 else if(!strucmp(hname
, "bcc")){
1715 q_status_message(SM_ORDER
, 3, 4,
1716 "\"Bcc\" header in mailto url ignored");
1718 else if(!strucmp(hname
, "from")){
1719 q_status_message(SM_ORDER
, 3, 4,
1720 "\"From\" header in mailto url ignored");
1722 else if(!strucmp(hname
, "body")){
1723 char *sub
= rfc1738_str(hvalue
? hvalue
: "");
1725 so_puts((STORE_S
*)body
->contents
.text
.data
, sub
);
1726 so_puts((STORE_S
*)body
->contents
.text
.data
, NEWLINE
);
1731 fs_give((void **) &urlp
);
1733 rflags
= ROLE_COMPOSE
;
1734 if(nonempty_patterns(rflags
, &dummy
)){
1735 role
= set_role_from_msg(ps_global
, rflags
, -1L, NULL
);
1736 if(confirm_role(rflags
, &role
))
1737 role
= combine_inherited_role(role
);
1740 cmd_cancelled("Composition");
1746 q_status_message1(SM_ORDER
, 3, 4, "Composing using role \"%s\"",
1749 if(!was_a_body
&& role
&& role
->template){
1752 impl
= 1; /* leave cursor in header if not explicit */
1753 filtered
= detoken(role
, NULL
, 0, 0, 0, &redraft_pos
, &impl
);
1756 so_puts((STORE_S
*)body
->contents
.text
.data
, filtered
);
1758 template_len
= strlen(filtered
);
1761 fs_give((void **)&filtered
);
1767 if(!was_a_body
&& (sig
= detoken(role
, NULL
, 2, 0, 1, &redraft_pos
,
1770 redraft_pos
->offset
+= template_len
;
1773 so_puts((STORE_S
*)body
->contents
.text
.data
, sig
);
1775 fs_give((void **)&sig
);
1778 memset((void *)&fake_reply
, 0, sizeof(fake_reply
));
1779 fake_reply
.pseudo
= 1;
1780 fake_reply
.data
.pico_flags
= (outgoing
->subject
) ? P_BODY
: P_HEADEND
;
1783 if(!(role
&& role
->fcc
))
1784 fcc
= get_fcc_based_on_to(outgoing
->to
);
1787 create_message_body(&body
, attachlist
, 0);
1789 pine_send(outgoing
, &body
, "\"MAILTO\" COMPOSE",
1790 role
, fcc
, &fake_reply
, redraft_pos
, NULL
, NULL
, PS_STICKY_TO
);
1792 ps_global
->mangled_screen
= 1;
1795 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1796 _("Can't create space for composer"));
1800 mail_free_envelope(&outgoing
);
1803 pine_free_body(&body
);
1806 fs_give((void **)&fcc
);
1808 free_redraft_pos(&redraft_pos
);
1816 url_mailto_addr(struct mail_address
**a
, char *a_raw
)
1818 char *p
= cpystr(rfc1738_str(a_raw
));
1820 while(*a
) /* append to address list */
1823 rfc822_parse_adrlist(a
, p
, ps_global
->maildomain
);
1824 fs_give((void **) &p
);
1829 * imap URL digester ala RFC 2192
1832 url_local_imap(char *url
)
1834 char *folder
, *mailbox
= NULL
, *errstr
= NULL
, *search
= NULL
,
1835 newfolder
[MAILTMPLEN
];
1838 imapuid_t uid
= 0L, uid_val
= 0L;
1839 CONTEXT_S
*fake_context
;
1842 rv
= url_imap_folder(url
, &folder
, &uid
, &uid_val
, &search
, 0);
1843 switch(rv
& URL_IMAP_MASK
){
1844 case URL_IMAP_IMAILBOXLIST
:
1845 /* BUG: deal with lsub tag */
1846 if((fake_context
= new_context(folder
, NULL
)) != NULL
){
1847 newfolder
[0] = '\0';
1848 if(display_folder_list(&fake_context
, newfolder
,
1849 0, folders_for_goto
))
1850 if(strlen(newfolder
) + 1 < MAILTMPLEN
)
1851 mailbox
= newfolder
;
1854 errstr
= "Problem building URL's folder list";
1856 fs_give((void **) &folder
);
1857 free_context(&fake_context
);
1863 q_status_message(SM_ORDER
|SM_DING
, 3, 3, errstr
);
1865 cmd_cancelled("URL Launch");
1869 case URL_IMAP_IMESSAGEPART
:
1870 case URL_IMAP_IMESSAGELIST
:
1871 if(ps_global
&& ps_global
->ttyo
){
1872 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
1873 ps_global
->mangled_footer
= 1;
1876 rv
= do_broach_folder(folder
, NULL
, NULL
, 0L);
1877 fs_give((void **) &folder
);
1879 case -1 : /* utter failure */
1880 ps_global
->next_screen
= main_menu_screen
;
1883 case 0 : /* same folder reopened */
1884 ps_global
->next_screen
= mail_index_screen
;
1887 case 1 : /* requested folder open */
1888 ps_global
->next_screen
= mail_index_screen
;
1890 if(uid_val
&& uid_val
!= ps_global
->mail_stream
->uid_validity
){
1892 q_status_message(SM_ORDER
|SM_DING
, 3, 3,
1893 "Warning! Referenced folder changed since URL recorded");
1898 * Make specified message the currently selected..
1900 for(i
= 1L; i
<= mn_get_total(ps_global
->msgmap
); i
++)
1901 if(mail_uid(ps_global
->mail_stream
, i
) == uid
){
1902 ps_global
->next_screen
= mail_view_screen
;
1903 mn_set_cur(ps_global
->msgmap
, i
);
1907 if(i
> mn_get_total(ps_global
->msgmap
))
1908 q_status_message(SM_ORDER
, 2, 3,
1909 "Couldn't find specified article number");
1913 * Select the specified messages
1914 * and present a zoom'd index...
1916 /* BUG: not dealing with CHARSET yet */
1918 /* ANOTHER BUG: mail_criteria is a compatibility routine for IMAP2BIS
1919 * so it doesn't know about IMAP4 search criteria, like SENTSINCE.
1920 * It also doesn't handle literals. */
1922 pine_mail_search_full(ps_global
->mail_stream
, NULL
,
1923 mail_criteria(search
),
1924 SE_NOPREFETCH
| SE_FREE
);
1926 for(i
= 1L; i
<= mn_get_total(ps_global
->msgmap
); i
++)
1927 if(ps_global
->mail_stream
1928 && i
<= ps_global
->mail_stream
->nmsgs
1929 && (mc
= mail_elt(ps_global
->mail_stream
, i
))
1931 set_lflag(ps_global
->mail_stream
,
1932 ps_global
->msgmap
, i
, MN_SLCT
, 1);
1934 if((i
= any_lflagged(ps_global
->msgmap
, MN_SLCT
)) != 0){
1936 q_status_message2(SM_ORDER
, 0, 3,
1937 "%s message%s selected",
1938 long2string(i
), plural(i
));
1939 /* Zoom the index! */
1940 zoom_index(ps_global
, ps_global
->mail_stream
,
1941 ps_global
->msgmap
, MN_SLCT
);
1949 case URL_IMAP_ERROR
:
1958 url_local_nntp(char *url
)
1960 char folder
[2*MAILTMPLEN
], *group
;
1962 long i
, article_num
;
1964 /* no hostport, no url, end of story */
1965 if((group
= strchr(url
+ 7, '/')) != 0){
1967 for(group_len
= 0; group
[group_len
] && group
[group_len
] != '/';
1969 if(!rfc1738_group(&group
[group_len
]))
1970 /* TRANSLATORS: these are errors in news group URLs */
1971 return(url_bogus(url
, _("Invalid newsgroup specified")));
1974 snprintf(folder
, sizeof(folder
), "{%.*s/nntp}#news.%.*s",
1975 (int) MIN((group
- 1) - (url
+ 7), MAILTMPLEN
-20), url
+ 7,
1976 (int) MIN(group_len
, MAILTMPLEN
-20), group
);
1977 folder
[sizeof(folder
)-1] = '\0';
1980 return(url_bogus(url
, _("No newsgroup specified")));
1983 return(url_bogus(url
, _("No server specified")));
1985 if(ps_global
&& ps_global
->ttyo
){
1986 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
1987 ps_global
->mangled_footer
= 1;
1990 switch(do_broach_folder(rfc1738_str(folder
), NULL
, NULL
, 0L)){
1991 case -1 : /* utter failure */
1992 ps_global
->next_screen
= main_menu_screen
;
1995 case 0 : /* same folder reopened */
1996 ps_global
->next_screen
= mail_index_screen
;
1999 case 1 : /* requested folder open */
2000 ps_global
->next_screen
= mail_index_screen
;
2002 /* grok article number --> c-client UID */
2003 if(group
[group_len
++] == '/'
2004 && (article_num
= atol(&group
[group_len
]))){
2006 * Make the requested article our current message
2008 for(i
= 1; i
<= mn_get_nmsgs(ps_global
->msgmap
); i
++)
2009 if(mail_uid(ps_global
->mail_stream
, i
) == article_num
){
2010 ps_global
->next_screen
= mail_view_screen
;
2011 if((i
= mn_raw2m(ps_global
->msgmap
, i
)) != 0)
2012 mn_set_cur(ps_global
->msgmap
, i
);
2016 if(i
== 0 || i
> mn_get_total(ps_global
->msgmap
))
2017 q_status_message(SM_ORDER
, 2, 3,
2018 _("Couldn't find specified article number"));
2024 ps_global
->redrawer
= (void(*)(void))NULL
;
2030 url_local_news(char *url
)
2032 char folder
[MAILTMPLEN
], *p
;
2033 CONTEXT_S
*cntxt
= NULL
;
2036 * NOTE: NO SUPPORT for '*' or message-id
2039 if(*(url
+5) == '/' && *(url
+6) == '/')
2040 return(url_local_nntp(url
)); /* really meant "nntp://"? */
2042 if(ps_global
->VAR_NNTP_SERVER
&& ps_global
->VAR_NNTP_SERVER
[0])
2044 * BUG: Only the first NNTP server is tried.
2046 snprintf(folder
, sizeof(folder
), "{%s/nntp}#news.", ps_global
->VAR_NNTP_SERVER
[0]);
2048 strncpy(folder
, "#news.", sizeof(folder
));
2050 folder
[sizeof(folder
)-1] = '\0';
2052 for(p
= strncpy(folder
+ strlen(folder
), url
+ 5, sizeof(folder
)-strlen(folder
)-1);
2053 *p
&& rfc1738_group(p
);
2058 return(url_bogus(url
, "Invalid newsgroup specified"));
2060 else{ /* fish first group from newsrc */
2066 /* Find first news context */
2067 for(cntxt
= ps_global
->context_list
;
2068 cntxt
&& !(cntxt
->use
& CNTXT_NEWS
);
2069 cntxt
= cntxt
->next
)
2073 if((alphaorder
= F_OFF(F_READ_IN_NEWSRC_ORDER
, ps_global
)) != 0)
2074 (void) F_SET(F_READ_IN_NEWSRC_ORDER
, ps_global
, 1);
2076 build_folder_list(NULL
, cntxt
, NULL
, NULL
, BFL_LSUB
);
2077 if((f
= folder_entry(0, FOLDERS(cntxt
))) != NULL
){
2078 strncpy(folder
, f
->name
, sizeof(folder
));
2079 folder
[sizeof(folder
)-1] = '\0';
2082 free_folder_list(cntxt
);
2085 (void) F_SET(F_READ_IN_NEWSRC_ORDER
, ps_global
, 0);
2088 if(folder
[0] == '\0'){
2089 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2090 "No default newsgroup");
2095 if(ps_global
&& ps_global
->ttyo
){
2096 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
2097 ps_global
->mangled_footer
= 1;
2100 if(do_broach_folder(rfc1738_str(folder
), cntxt
, NULL
, 0L) < 0)
2101 ps_global
->next_screen
= main_menu_screen
;
2103 ps_global
->next_screen
= mail_index_screen
;
2105 ps_global
->redrawer
= (void(*)(void))NULL
;
2112 url_local_file(char *file_url
)
2115 /* TRANSLATORS: this is a warning that the file URL can cause programs to run which may
2116 be a security problem. We are asking the user to confirm that they want to do this. */
2117 _("\"file\" URL may cause programs to be run on your system. Run anyway"),
2118 'n', 0, NO_HELP
, WT_NORM
) == 'y'){
2122 handle
.h
.url
.path
= file_url
;
2123 if((handle
.h
.url
.tool
= url_external_handler(&handle
, 1))
2124 || (handle
.h
.url
.tool
= url_external_handler(&handle
, 0))){
2125 url_launch(&handle
);
2129 q_status_message(SM_ORDER
, 0, 4,
2130 _("No viewer for \"file\" URL. VIEWER command cancelled"));
2134 q_status_message(SM_ORDER
, 0, 4, _("VIEWER command cancelled"));
2140 url_local_fragment(char *fragment
)
2142 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
2146 * find a handle with the fragment's name
2148 for(hp
= st
->parms
->text
.handles
; hp
; hp
= hp
->next
)
2149 if(hp
->type
== URL
&& hp
->h
.url
.name
2150 && !strcmp(hp
->h
.url
.name
, fragment
+ 1))
2154 for(hp
= st
->parms
->text
.handles
->prev
; hp
; hp
= hp
->prev
)
2155 if(hp
->type
== URL
&& hp
->h
.url
.name
2156 && !strcmp(hp
->h
.url
.name
, fragment
+ 1))
2160 * set the top line of the display to contain this line
2163 st
->top_text_line
= hp
->loc
->where
.row
;
2164 ps_global
->mangled_body
= 1;
2167 q_status_message1(SM_ORDER
| SM_DING
, 0, 3,
2168 "Can't find fragment: %s", fragment
);
2174 ical_send_reply(char *url
)
2176 // ical_compose_reply(url + strlen("x-alpine-ical:"));
2182 url_local_phone_home(char *URL
)
2184 phone_home(URL
+ strlen("x-alpine-phone-home:"));
2190 * Format editorial comment referencing screen offering
2191 * List-* header supplied commands
2194 rfc2369_editorial(long int msgno
, HANDLE_S
**handlesp
, int flags
, int width
, gf_io_t pc
)
2196 char *p
, *hdrp
, *hdrs
[MLCMD_COUNT
+ 1],
2197 color
[64], buf
[2048];
2198 int i
, n
, rv
= TRUE
;
2201 if((flags
& FM_DISPLAY
)
2202 && (hdrp
= pine_fetchheader_lines(ps_global
->mail_stream
, msgno
,
2203 NULL
, rfc2369_hdrs(hdrs
)))){
2205 snprintf(buf
, sizeof(buf
), "Note: This message contains ");
2206 buf
[sizeof(buf
)-1] = '\0';
2207 p
= buf
+ strlen(buf
);
2210 h
= new_handle(handlesp
);
2212 h
->h
.func
.f
= rfc2369_display
;
2213 h
->h
.func
.args
.stream
= ps_global
->mail_stream
;
2214 h
->h
.func
.args
.msgmap
= ps_global
->msgmap
;
2215 h
->h
.func
.args
.msgno
= msgno
;
2217 if(!(flags
& FM_NOCOLOR
)
2218 && handle_start_color(color
, sizeof(color
), &n
, 0)){
2219 if((p
-buf
)+n
< sizeof(buf
))
2220 for(i
= 0; i
< n
; i
++)
2223 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
2228 if((p
-buf
)+2 < sizeof(buf
)){
2233 snprintf(p
+ 1, sizeof(buf
)-(p
+1-buf
), "%d", h
->key
);
2234 buf
[sizeof(buf
)-1] = '\0';
2239 sstrncpy(&p
, "email list management information", sizeof(buf
)-(p
-buf
));
2240 buf
[sizeof(buf
)-1] = '\0';
2243 /* in case it was the current selection */
2244 if((p
-buf
)+2 < sizeof(buf
)){
2249 if(handle_end_color(color
, sizeof(color
), &n
)){
2250 if((p
-buf
)+n
< sizeof(buf
))
2251 for(i
= 0; i
< n
; i
++)
2255 if((p
-buf
)+2 < sizeof(buf
)){
2261 if(p
-buf
< sizeof(buf
))
2265 buf
[sizeof(buf
)-1] = '\0';
2267 rv
= (gf_puts(NEWLINE
, pc
)
2268 && format_editorial(buf
, width
, flags
, handlesp
, pc
) == NULL
2269 && gf_puts(NEWLINE
, pc
));
2272 fs_give((void **) &hdrp
);
2280 /*----------------------------------------------------------------------
2281 routine for displaying text on the screen.
2283 Args: sparms -- structure of args controlling what happens outside
2284 just the business of managing text scrolling
2286 This displays in three different kinds of text. One is an array of
2287 lines passed in in text_array. The other is a simple long string of
2288 characters passed in in text.
2290 The style determines what some of the error messages will be, and
2291 what commands are available as different things are appropriate for
2292 help text than for message text etc.
2297 scrolltool(SCROLL_S
*sparms
)
2299 register long cur_top_line
, num_display_lines
;
2301 int result
, done
, cmd
, found_on
, found_on_index
,
2302 first_view
, force
, scroll_lines
, km_size
,
2303 cursor_row
, cursor_col
, km_popped
;
2306 struct key_menu
*km
;
2307 HANDLE_S
*next_handle
;
2312 num_display_lines
= SCROLL_LINES(ps_global
);
2314 ps_global
->mangled_header
= 1;
2315 ps_global
->mangled_footer
= 1;
2316 ps_global
->mangled_body
= !sparms
->body_valid
;
2319 what
= sparms
->keys
.what
; /* which key menu to display */
2323 found_on_index
= -1;
2325 if(sparms
->quell_first_view
)
2329 ch
= 'x'; /* for first time through */
2330 whereis_pos
.row
= 0;
2331 whereis_pos
.col
= 0;
2332 next_handle
= sparms
->text
.handles
;
2334 set_scroll_text(sparms
, cur_top_line
, scroll_state(SS_NEW
));
2335 format_scroll_text();
2337 if((km
= sparms
->keys
.menu
) != NULL
){
2338 memcpy(bitmap
, sparms
->keys
.bitmap
, sizeof(bitmap_t
));
2342 km
= &simple_text_keymenu
;
2344 sparms
->mouse
.popup
= simple_text_popup
;
2348 if(!sparms
->bar
.title
)
2349 sparms
->bar
.title
= "Text";
2351 if(sparms
->bar
.style
== TitleBarNone
){
2352 if(THREADING() && sp_viewing_a_thread(ps_global
->mail_stream
))
2353 sparms
->bar
.style
= ThrdMsgPercent
;
2355 sparms
->bar
.style
= MsgTextPercent
;
2358 switch(sparms
->start
.on
){
2360 cur_top_line
= MAX(0, scroll_text_lines() - (num_display_lines
-2));
2361 if(F_ON(F_SHOW_CURSOR
, ps_global
)){
2362 whereis_pos
.row
= scroll_text_lines() - cur_top_line
;
2363 found_on
= scroll_text_lines() - 1;
2369 if(sparms
->start
.loc
.frag
){
2370 (void) url_local_fragment(sparms
->start
.loc
.frag
);
2372 cur_top_line
= scroll_state(SS_CUR
)->top_text_line
;
2374 if(F_ON(F_SHOW_CURSOR
, ps_global
)){
2375 whereis_pos
.row
= scroll_text_lines() - cur_top_line
;
2376 found_on
= scroll_text_lines() - 1;
2383 if(sparms
->start
.loc
.offset
){
2384 for(cur_top_line
= 0L;
2385 cur_top_line
+ 1 < scroll_text_lines()
2386 && (sparms
->start
.loc
.offset
2387 -= scroll_handle_column(cur_top_line
,
2396 if(scroll_handle_obscured(sparms
->text
.handles
))
2397 cur_top_line
= scroll_handle_reframe(-1, TRUE
);
2401 default : /* no-op */
2405 /* prepare for calls below to tell us where to go */
2406 ps_global
->next_screen
= SCREEN_FUN_NULL
;
2408 cancel_busy_cue(-1);
2411 ps_global
->user_says_cancel
= 0;
2415 clearfooter(ps_global
);
2416 ps_global
->mangled_body
= 1;
2420 if(ps_global
->mangled_screen
) {
2421 ps_global
->mangled_header
= 1;
2422 ps_global
->mangled_footer
= 1;
2423 ps_global
->mangled_body
= 1;
2426 if(!sparms
->quell_newmail
&& streams_died())
2427 ps_global
->mangled_header
= 1;
2429 dprint((9, "@@@@ current:%ld\n",
2430 mn_get_cur(ps_global
->msgmap
)));
2433 /*==================== All Screen painting ====================*/
2434 /*-------------- The title bar ---------------*/
2435 update_scroll_titlebar(cur_top_line
, ps_global
->mangled_header
);
2437 if(ps_global
->mangled_screen
){
2438 /* this is the only line not cleared by header, body or footer
2442 ps_global
->mangled_screen
= 0;
2445 /*---- Scroll or update the body of the text on the screen -------*/
2446 cur_top_line
= scroll_scroll_text(cur_top_line
, next_handle
,
2447 ps_global
->mangled_body
);
2448 ps_global
->redrawer
= redraw_scroll_text
;
2449 ps_global
->mangled_body
= 0;
2451 /*--- Check to see if keymenu might change based on next_handle --*/
2452 if(sparms
->text
.handles
!= next_handle
)
2453 ps_global
->mangled_footer
= 1;
2456 sparms
->text
.handles
= next_handle
;
2458 /*------------- The key menu footer --------------------*/
2459 if(ps_global
->mangled_footer
|| sparms
->keys
.each_cmd
){
2461 FOOTER_ROWS(ps_global
) = 3;
2462 clearfooter(ps_global
);
2465 if(F_ON(F_ARROW_NAV
, ps_global
)){
2466 menu_clear_binding(km
, KEY_LEFT
);
2467 if((cmd
= menu_clear_binding(km
, '<')) != MC_UNKNOWN
){
2468 menu_add_binding(km
, '<', cmd
);
2469 menu_add_binding(km
, KEY_LEFT
, cmd
);
2473 if(F_ON(F_ARROW_NAV
, ps_global
)){
2474 menu_clear_binding(km
, KEY_RIGHT
);
2475 if((cmd
= menu_clear_binding(km
, '>')) != MC_UNKNOWN
){
2476 menu_add_binding(km
, '>', cmd
);
2477 menu_add_binding(km
, KEY_RIGHT
, cmd
);
2481 if(sparms
->keys
.each_cmd
){
2482 (*sparms
->keys
.each_cmd
)(sparms
,
2483 scroll_handle_obscured(sparms
->text
.handles
));
2484 memcpy(bitmap
, sparms
->keys
.bitmap
, sizeof(bitmap_t
));
2487 if(menu_binding_index(km
, MC_JUMP
) >= 0){
2488 for(cmd
= 0; cmd
< 10; cmd
++)
2489 if(F_ON(F_ENABLE_JUMP
, ps_global
))
2490 (void) menu_add_binding(km
, '0' + cmd
, MC_JUMP
);
2492 (void) menu_clear_binding(km
, '0' + cmd
);
2495 draw_keymenu(km
, bitmap
, ps_global
->ttyo
->screen_cols
,
2496 1-FOOTER_ROWS(ps_global
), 0, what
);
2498 ps_global
->mangled_footer
= 0;
2500 FOOTER_ROWS(ps_global
) = 1;
2501 mark_keymenu_dirty();
2505 if((ps_global
->first_time_user
|| ps_global
->show_new_version
)
2506 && first_view
&& sparms
->text
.handles
2507 && (sparms
->text
.handles
->next
|| sparms
->text
.handles
->prev
)
2508 && !sparms
->quell_help
)
2509 q_status_message(SM_ORDER
, 0, 3, HANDLE_INIT_MSG
);
2511 /*============ Check for New Mail and CheckPoint ============*/
2512 if(!sparms
->quell_newmail
&&
2513 new_mail(force
, NM_TIMING(ch
), NM_STATUS_MSG
) >= 0){
2514 update_scroll_titlebar(cur_top_line
, 1);
2515 if(ps_global
->mangled_footer
)
2516 draw_keymenu(km
, bitmap
, ps_global
->ttyo
->screen_cols
,
2517 1-FOOTER_ROWS(ps_global
), 0, what
);
2519 ps_global
->mangled_footer
= 0;
2523 * If an expunge of the current message happened during the
2524 * new mail check we want to bail out of here. See mm_expunged.
2526 if(ps_global
->next_screen
!= SCREEN_FUN_NULL
){
2531 if(ps_global
->noticed_change_in_unseen
){
2532 ps_global
->noticed_change_in_unseen
= 0; /* redraw only once */
2533 cmd
= MC_RESIZE
; /* causes cursor to be saved in folder_lister */
2538 if(first_view
&& num_display_lines
>= scroll_text_lines())
2539 q_status_message1(SM_INFO
, 0, 1, "ALL of %s", STYLE_NAME(sparms
));
2542 force
= 0; /* may not need to next time around */
2543 first_view
= 0; /* check_point a priority any more? */
2545 /*==================== Output the status message ==============*/
2546 if(!sparms
->no_stat_msg
){
2548 FOOTER_ROWS(ps_global
) = 3;
2549 mark_status_unknown();
2552 display_message(ch
);
2554 FOOTER_ROWS(ps_global
) = 1;
2555 mark_status_unknown();
2559 if(F_ON(F_SHOW_CURSOR
, ps_global
)){
2561 if(cur_top_line
!= scroll_state(SS_CUR
)->top_text_line
)
2562 whereis_pos
.row
= 0;
2565 if(whereis_pos
.row
> 0){
2566 cursor_row
= SCROLL_LINES_ABOVE(ps_global
)
2567 + whereis_pos
.row
- 1;
2568 cursor_col
= whereis_pos
.col
;
2571 POSLIST_S
*lp
= NULL
;
2573 if(sparms
->text
.handles
&&
2574 !scroll_handle_obscured(sparms
->text
.handles
)){
2575 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
2577 for(lp
= sparms
->text
.handles
->loc
; lp
; lp
= lp
->next
)
2578 if(lp
->where
.row
>= st
->top_text_line
2579 && lp
->where
.row
< st
->top_text_line
2580 + st
->screen
.length
){
2581 cursor_row
= lp
->where
.row
- cur_top_line
2582 + SCROLL_LINES_ABOVE(ps_global
);
2583 cursor_col
= lp
->where
.col
;
2590 /* first new line of text */
2591 cursor_row
= SCROLL_LINES_ABOVE(ps_global
) +
2592 ((cur_top_line
== 0) ? 0 : ps_global
->viewer_overlap
);
2598 cursor_row
= ps_global
->ttyo
->screen_rows
2599 - SCROLL_LINES_BELOW(ps_global
);
2602 MoveCursor(cursor_row
, cursor_col
);
2604 /*================ Get command and validate =====================*/
2607 if(sparms
->text
.handles
)
2610 mouse_in_content(KEY_MOUSE
, -1, -1, 0x5, 0);
2611 register_mfunc(mouse_in_content
, HEADER_ROWS(ps_global
), 0,
2612 ps_global
->ttyo
->screen_rows
2613 - (FOOTER_ROWS(ps_global
) + 1),
2614 ps_global
->ttyo
->screen_cols
);
2618 mswin_allowcopy(mswin_readscrollbuf
);
2619 mswin_setscrollcallback(pcpine_do_scroll
);
2621 if(sparms
->help
.text
!= NO_HELP
)
2622 mswin_sethelptextcallback(pcpine_help_scroll
);
2624 if(sparms
->text
.handles
2625 && sparms
->text
.handles
->type
!= Folder
)
2626 mswin_mousetrackcallback(pcpine_view_cursor
);
2628 if(ps_global
->prev_screen
== mail_view_screen
)
2629 mswin_setviewinwindcallback(view_in_new_window
);
2631 ch
= (sparms
->quell_newmail
|| read_command_prep()) ? read_command(&utf8str
) : NO_OP_COMMAND
;
2634 if(sparms
->text
.handles
)
2636 clear_mfunc(mouse_in_content
);
2639 mswin_allowcopy(NULL
);
2640 mswin_setscrollcallback(NULL
);
2641 mswin_sethelptextcallback(NULL
);
2642 mswin_mousetrackcallback(NULL
);
2643 mswin_setviewinwindcallback(NULL
);
2644 cur_top_line
= scroll_state(SS_CUR
)->top_text_line
;
2647 cmd
= menu_command(ch
, km
);
2659 clearfooter(ps_global
);
2664 /*============= Execute command =======================*/
2667 /* ------ Help -------*/
2669 if(FOOTER_ROWS(ps_global
) == 1 && km_popped
== 0){
2671 ps_global
->mangled_footer
= 1;
2675 whereis_pos
.row
= 0;
2676 if(sparms
->help
.text
== NO_HELP
){
2677 q_status_message(SM_ORDER
, 0, 5,
2678 _("No help text currently available"));
2682 km_size
= FOOTER_ROWS(ps_global
);
2684 helper(sparms
->help
.text
, sparms
->help
.title
, 0);
2686 if(ps_global
->next_screen
!= main_menu_screen
2687 && km_size
== FOOTER_ROWS(ps_global
)) {
2688 /* Have to reset because helper uses scroll_text */
2689 num_display_lines
= SCROLL_LINES(ps_global
);
2690 ps_global
->mangled_screen
= 1;
2698 /*---------- Roll keymenu ------*/
2700 if(F_OFF(F_USE_FK
, ps_global
))
2704 ps_global
->mangled_footer
= 1;
2708 /* -------- Scroll back one page -----------*/
2710 whereis_pos
.row
= 0;
2712 scroll_lines
= MIN(MAX(num_display_lines
-
2713 ps_global
->viewer_overlap
, 1), num_display_lines
);
2714 cur_top_line
-= scroll_lines
;
2715 if(cur_top_line
<= 0){
2717 q_status_message1(SM_INFO
, 0, 1, "START of %s",
2718 STYLE_NAME(sparms
));
2722 /* hilite last available handle */
2724 if(sparms
->text
.handles
){
2725 HANDLE_S
*h
= sparms
->text
.handles
;
2727 while((h
= scroll_handle_prev_sel(h
))
2728 && !scroll_handle_obscured(h
))
2733 q_status_message1(SM_ORDER
, 0, 1, _("Already at start of %s"),
2734 STYLE_NAME(sparms
));
2742 /*---- Scroll down one page -------*/
2744 if(cur_top_line
+ num_display_lines
< scroll_text_lines()){
2745 whereis_pos
.row
= 0;
2746 scroll_lines
= MIN(MAX(num_display_lines
-
2747 ps_global
->viewer_overlap
, 1), num_display_lines
);
2748 cur_top_line
+= scroll_lines
;
2750 if(cur_top_line
+ num_display_lines
>= scroll_text_lines())
2751 q_status_message1(SM_INFO
, 0, 1, "END of %s",
2752 STYLE_NAME(sparms
));
2754 else if(!sparms
->end_scroll
2755 || !(done
= (*sparms
->end_scroll
)(sparms
))){
2756 q_status_message1(SM_ORDER
, 0, 1, _("Already at end of %s"),
2757 STYLE_NAME(sparms
));
2758 /* hilite last available handle */
2759 if(sparms
->text
.handles
){
2760 HANDLE_S
*h
= sparms
->text
.handles
;
2762 while((h
= scroll_handle_next_sel(h
)) != NULL
)
2769 /* scroll to the top page */
2773 q_status_message1(SM_INFO
, 0, 1, "START of %s",
2774 STYLE_NAME(sparms
));
2778 if(sparms
->text
.handles
){
2779 HANDLE_S
*h
= sparms
->text
.handles
;
2781 while((h
= scroll_handle_prev_sel(h
)) != NULL
)
2786 /* scroll to the bottom page */
2788 if(cur_top_line
+ num_display_lines
< scroll_text_lines()){
2789 cur_top_line
= scroll_text_lines() - MIN(5, num_display_lines
);
2790 q_status_message1(SM_INFO
, 0, 1, "END of %s",
2791 STYLE_NAME(sparms
));
2794 if(sparms
->text
.handles
){
2795 HANDLE_S
*h
= sparms
->text
.handles
;
2797 while((h
= scroll_handle_next_sel(h
)) != NULL
)
2802 /*------ Scroll down one line -----*/
2805 if(sparms
->text
.handles
){
2806 if(sparms
->vert_handle
){
2810 h2
= sparms
->text
.handles
;
2811 if(h2
->type
== Folder
&& h2
->prev
&& h2
->prev
->is_dual_do_open
)
2814 i
= h2
->loc
->where
.row
+ 1;
2815 j
= h2
->loc
->where
.col
;
2816 for(h
= NULL
, k
= h2
->key
;
2818 || (h
->loc
->where
.row
== h2
->loc
->where
.row
));
2820 /* must be different key */
2821 /* ... below current line */
2822 /* ... pref'bly to left */
2824 && h2
->loc
->where
.row
>= i
){
2825 if(h2
->loc
->where
.col
> j
){
2836 whereis_pos
.row
= 0;
2838 if((result
= scroll_handle_obscured(next_handle
)) != 0){
2841 if(scroll_handle_obscured(sparms
->text
.handles
)
2843 next_handle
= sparms
->text
.handles
;
2845 ps_global
->mangled_body
++;
2846 new_top
= scroll_handle_reframe(next_handle
->key
,0);
2848 cur_top_line
= new_top
;
2852 else if(!(ch
== ctrl('N') || F_ON(F_FORCE_ARROWS
, ps_global
)))
2853 next_handle
= scroll_handle_next(sparms
->text
.handles
);
2857 if(cur_top_line
+ num_display_lines
< scroll_text_lines()){
2858 whereis_pos
.row
= 0;
2860 if(cur_top_line
+ num_display_lines
>= scroll_text_lines())
2861 q_status_message1(SM_INFO
, 0, 1, "END of %s",
2862 STYLE_NAME(sparms
));
2865 q_status_message1(SM_ORDER
, 0, 1, _("Already at end of %s"),
2866 STYLE_NAME(sparms
));
2872 /* ------ Scroll back up one line -------*/
2875 if(sparms
->text
.handles
){
2876 if(sparms
->vert_handle
){
2880 h2
= sparms
->text
.handles
;
2881 if(h2
->type
== Folder
&& h2
->prev
&& h2
->prev
->is_dual_do_open
)
2884 i
= h2
->loc
->where
.row
- 1;
2885 j
= h2
->loc
->where
.col
;
2887 for(h
= NULL
, k
= h2
->key
;
2889 || (h
->loc
->where
.row
== h2
->loc
->where
.row
));
2891 /* must be new key, above current
2892 * line and pref'bly to right
2895 && h2
->loc
->where
.row
<= i
){
2896 if(h2
->loc
->where
.col
< j
){
2907 whereis_pos
.row
= 0;
2909 if((result
= scroll_handle_obscured(next_handle
)) != 0){
2912 if(scroll_handle_obscured(sparms
->text
.handles
)
2914 next_handle
= sparms
->text
.handles
;
2916 ps_global
->mangled_body
++;
2917 new_top
= scroll_handle_reframe(next_handle
->key
,0);
2919 cur_top_line
= new_top
;
2923 else if(!(ch
== ctrl('P') || F_ON(F_FORCE_ARROWS
, ps_global
)))
2924 next_handle
= scroll_handle_prev(sparms
->text
.handles
);
2928 whereis_pos
.row
= 0;
2931 if(cur_top_line
== 0)
2932 q_status_message1(SM_INFO
, 0, 1, "START of %s",
2933 STYLE_NAME(sparms
));
2936 q_status_message1(SM_ORDER
, 0, 1,
2937 _("Already at start of %s"),
2938 STYLE_NAME(sparms
));
2944 case MC_NEXT_HANDLE
:
2945 if((next_handle
= scroll_handle_next_sel(sparms
->text
.handles
)) != NULL
){
2946 whereis_pos
.row
= 0;
2947 if((result
= scroll_handle_obscured(next_handle
)) != 0){
2950 if(scroll_handle_obscured(sparms
->text
.handles
)
2952 next_handle
= sparms
->text
.handles
;
2954 ps_global
->mangled_body
++;
2955 new_top
= scroll_handle_reframe(next_handle
->key
, 0);
2957 cur_top_line
= new_top
;
2961 if(scroll_handle_obscured(sparms
->text
.handles
)){
2964 ps_global
->mangled_body
++;
2965 if((new_top
= scroll_handle_reframe(-1, 0)) >= 0){
2966 whereis_pos
.row
= 0;
2967 cur_top_line
= new_top
;
2971 q_status_message1(SM_ORDER
, 0, 1,
2972 _("Already on last item in %s"),
2973 STYLE_NAME(sparms
));
2979 case MC_PREV_HANDLE
:
2980 if((next_handle
= scroll_handle_prev_sel(sparms
->text
.handles
)) != NULL
){
2981 whereis_pos
.row
= 0;
2982 if((result
= scroll_handle_obscured(next_handle
)) != 0){
2985 if(scroll_handle_obscured(sparms
->text
.handles
)
2987 next_handle
= sparms
->text
.handles
;
2989 ps_global
->mangled_body
++;
2990 new_top
= scroll_handle_reframe(next_handle
->key
, 0);
2992 cur_top_line
= new_top
;
2996 if(scroll_handle_obscured(sparms
->text
.handles
)){
2999 ps_global
->mangled_body
++;
3000 if((new_top
= scroll_handle_reframe(-1, 0)) >= 0){
3001 whereis_pos
.row
= 0;
3002 cur_top_line
= new_top
;
3006 q_status_message1(SM_ORDER
, 0, 1,
3007 _("Already on first item in %s"),
3008 STYLE_NAME(sparms
));
3014 /*------ View the current handle ------*/
3015 case MC_VIEW_HANDLE
:
3016 switch(scroll_handle_obscured(sparms
->text
.handles
)){
3019 switch(scroll_handle_launch(sparms
->text
.handles
,
3020 sparms
->text
.handles
->force_display
)){
3022 cmd
= MC_EXIT
; /* propagate */
3027 cmd_cancelled(NULL
);
3034 cur_top_line
= scroll_state(SS_CUR
)->top_text_line
;
3038 q_status_message(SM_ORDER
, 0, 2, HANDLE_BELOW_ERR
);
3042 q_status_message(SM_ORDER
, 0, 2, HANDLE_ABOVE_ERR
);
3048 /*---------- Search text (where is) ----------*/
3050 ps_global
->mangled_footer
= 1;
3052 int start_index
, key
= 0;
3053 char *report
= NULL
;
3055 start_row
= cur_top_line
;
3058 if(F_ON(F_SHOW_CURSOR
,ps_global
)){
3060 || found_on
>= scroll_text_lines()
3061 || found_on
< cur_top_line
3062 || found_on
>= cur_top_line
+ num_display_lines
){
3063 start_row
= cur_top_line
;
3067 if(found_on_index
< 0){
3068 start_row
= found_on
+ 1;
3072 start_row
= found_on
;
3073 start_index
= found_on_index
+1;
3077 else if(sparms
->srch_handle
){
3080 if((h
= scroll_handle_next_sel(sparms
->text
.handles
)) != NULL
){
3082 * Translate the screen's column into the
3083 * line offset to start on...
3085 * This makes it so search_text never returns -3
3086 * so we don't know it is the same match. That's
3087 * because we start well after the current handle
3088 * (at the next handle) and that causes us to
3089 * think the one we just matched on is a different
3090 * one from before. Can't think of an easy way to
3091 * fix it, though, and it isn't a big deal. We still
3092 * match, we just don't say current line contains
3095 start_row
= h
->loc
->where
.row
;
3096 start_index
= scroll_handle_index(start_row
, h
->loc
->where
.col
);
3099 /* last handle, start over at top */
3100 start_row
= cur_top_line
;
3105 start_row
= (found_on
< 0
3106 || found_on
>= scroll_text_lines()
3107 || found_on
< cur_top_line
3108 || found_on
>= cur_top_line
+ num_display_lines
)
3109 ? cur_top_line
: found_on
+ 1,
3113 found_on
= search_text(-FOOTER_ROWS(ps_global
), start_row
,
3114 start_index
, &report
,
3115 &whereis_pos
, &found_on_index
);
3117 if(found_on
== -4){ /* search to top of text */
3118 whereis_pos
.row
= 0;
3119 whereis_pos
.col
= 0;
3121 if(sparms
->text
.handles
&& sparms
->srch_handle
)
3124 else if(found_on
== -5){ /* search to bottom of text */
3127 whereis_pos
.row
= MAX(scroll_text_lines() - 1, 0);
3128 whereis_pos
.col
= 0;
3129 found_on
= whereis_pos
.row
;
3130 if((h
= sparms
->text
.handles
) && sparms
->srch_handle
)
3133 while((h
= h
->next
) != NULL
);
3135 else if(found_on
== -3){
3136 whereis_pos
.row
= found_on
= start_row
;
3137 found_on_index
= start_index
- 1;
3138 q_status_message(SM_ORDER
, 1, 3,
3139 _("Current line contains the only match"));
3143 result
= found_on
< cur_top_line
;
3145 key
= (sparms
->text
.handles
)
3146 ? dot_on_handle(found_on
, whereis_pos
.col
) : 0;
3148 if(F_ON(F_FORCE_LOW_SPEED
,ps_global
)
3149 || ps_global
->low_speed
3150 || F_ON(F_SHOW_CURSOR
,ps_global
)
3152 if((found_on
>= cur_top_line
+ num_display_lines
||
3153 found_on
< cur_top_line
) &&
3154 num_display_lines
> ps_global
->viewer_overlap
){
3155 cur_top_line
= found_on
- ((found_on
> 0) ? 1 : 0);
3156 if(scroll_text_lines()-cur_top_line
< 5)
3157 cur_top_line
= MAX(0,
3158 scroll_text_lines()-MIN(5,num_display_lines
));
3160 /* else leave cur_top_line alone */
3163 cur_top_line
= found_on
- ((found_on
> 0) ? 1 : 0);
3164 if(scroll_text_lines()-cur_top_line
< 5)
3165 cur_top_line
= MAX(0,
3166 scroll_text_lines()-MIN(5,num_display_lines
));
3169 whereis_pos
.row
= whereis_pos
.row
- cur_top_line
+ 1;
3171 q_status_message(SM_ORDER
, 0, 3, report
);
3173 q_status_message2(SM_ORDER
, 0, 3,
3174 "%sFound on line %s on screen",
3175 result
? "Search wrapped to start. " : "",
3176 int2string(whereis_pos
.row
));
3179 if(sparms
->text
.handles
->key
< key
)
3180 for(next_handle
= sparms
->text
.handles
->next
;
3181 next_handle
->key
!= key
;
3182 next_handle
= next_handle
->next
)
3185 for(next_handle
= sparms
->text
.handles
;
3186 next_handle
->key
!= key
;
3187 next_handle
= next_handle
->prev
)
3191 else if(found_on
== -1)
3192 cmd_cancelled("Search");
3194 q_status_message(SM_ORDER
, 0, 3, _("Word not found"));
3200 /*-------------- jump command -------------*/
3201 /* NOTE: preempt the process_cmd() version because
3202 * we need to get at the number..
3205 jn
= jump_to(ps_global
->msgmap
, -FOOTER_ROWS(ps_global
), ch
,
3207 if(sparms
&& sparms
->jump_is_debug
)
3209 else if(jn
> 0 && jn
!= mn_get_cur(ps_global
->msgmap
)){
3211 if(mn_total_cur(ps_global
->msgmap
) > 1L)
3212 mn_reset_cur(ps_global
->msgmap
, jn
);
3214 mn_set_cur(ps_global
->msgmap
, jn
);
3219 ps_global
->mangled_footer
= 1;
3225 /*-------------- Mouse Event -------------*/
3232 mouse_get_last (NULL
, &mp
);
3235 /* The clicked line have anything special on it? */
3236 if((line
= cur_top_line
+ mp
.row
) < scroll_text_lines()
3237 && (key
= dot_on_handle(line
, mp
.col
))){
3239 case M_BUTTON_RIGHT
:
3241 if(sparms
->mouse
.popup
){
3242 if(sparms
->text
.handles
->key
< key
)
3243 for(next_handle
= sparms
->text
.handles
->next
;
3244 next_handle
->key
!= key
;
3245 next_handle
= next_handle
->next
)
3248 for(next_handle
= sparms
->text
.handles
;
3249 next_handle
->key
!= key
;
3250 next_handle
= next_handle
->prev
)
3253 if(sparms
->mouse
.popup
){
3254 cur_top_line
= scroll_scroll_text(cur_top_line
,
3256 ps_global
->mangled_body
);
3258 switch((*sparms
->mouse
.popup
)(sparms
, key
)){
3260 cur_top_line
= doubleclick_handle(sparms
, next_handle
, &cmd
, &done
);
3273 case M_BUTTON_LEFT
:
3274 if(sparms
->text
.handles
->key
< key
)
3275 for(next_handle
= sparms
->text
.handles
->next
;
3276 next_handle
->key
!= key
;
3277 next_handle
= next_handle
->next
)
3280 for(next_handle
= sparms
->text
.handles
;
3281 next_handle
->key
!= key
;
3282 next_handle
= next_handle
->prev
)
3285 if(mp
.doubleclick
) /* launch url */
3286 cur_top_line
= doubleclick_handle(sparms
, next_handle
, &cmd
, &done
);
3287 else if(sparms
->mouse
.click
)
3288 (*sparms
->mouse
.click
)(sparms
);
3292 case M_BUTTON_MIDDLE
: /* NO-OP for now */
3295 default: /* just ignore */
3300 else if(mp
.button
== M_BUTTON_RIGHT
){
3302 * Toss generic popup on to the screen
3304 if(sparms
->mouse
.popup
)
3305 if((*sparms
->mouse
.popup
)(sparms
, 0) == 2){
3316 /*-------------- Display Resize -------------*/
3318 if(sparms
->resize_exit
){
3322 * Figure out char offset of the char in the top left
3323 * corner of the display. Pass it back to the
3324 * fetcher/formatter and have it pass the offset
3327 sparms
->start
.on
= Offset
;
3328 for(sparms
->start
.loc
.offset
= line
= 0L;
3329 line
< cur_top_line
;
3331 sparms
->start
.loc
.offset
+= scroll_handle_column(line
, -1);
3337 /* else no reformatting neccessary, fall thru to repaint */
3340 /*-------------- refresh -------------*/
3342 num_display_lines
= SCROLL_LINES(ps_global
);
3343 mark_status_dirty();
3344 mark_keymenu_dirty();
3345 mark_titlebar_dirty();
3346 ps_global
->mangled_screen
= 1;
3351 /*------- no op timeout to check for new mail ------*/
3356 /*------- Forward displayed text ------*/
3358 forward_text(ps_global
, sparms
->text
.text
, sparms
->text
.src
);
3362 /*----------- Save the displayed text ------------*/
3364 (void)simple_export(ps_global
, sparms
->text
.text
,
3365 sparms
->text
.src
, "text", NULL
);
3369 /*----------- Exit this screen ------------*/
3375 /*----------- Pop back to the Main Menu ------------*/
3377 ps_global
->next_screen
= main_menu_screen
;
3382 /*----------- Print ------------*/
3384 print_to_printer(sparms
);
3388 /* ------- First handle on Line ------ */
3390 if(sparms
->text
.handles
){
3391 next_handle
= scroll_handle_boundary(sparms
->text
.handles
,
3392 scroll_handle_prev_sel
);
3396 /* fall thru as bogus */
3398 /* ------- Last handle on Line ------ */
3400 if(sparms
->text
.handles
){
3401 next_handle
= scroll_handle_boundary(sparms
->text
.handles
,
3402 scroll_handle_next_sel
);
3406 /* fall thru as bogus */
3408 /*------- BOGUS INPUT ------*/
3412 if(sparms
->bogus_input
)
3413 done
= (*sparms
->bogus_input
)(ch
);
3415 bogus_command(ch
, F_ON(F_USE_FK
,ps_global
) ? "F1" : "?");
3421 bogus_utf8_command(utf8str
, F_ON(F_USE_FK
, ps_global
) ? "F1" : "?");
3425 /*------- Standard commands ------*/
3427 whereis_pos
.row
= 0;
3428 if(sparms
->proc
.tool
)
3429 result
= (*sparms
->proc
.tool
)(cmd
, ps_global
->msgmap
, sparms
);
3431 result
= process_cmd(ps_global
, ps_global
->mail_stream
,
3432 ps_global
->msgmap
, cmd
, View
, &force
);
3434 dprint((7, "PROCESS_CMD return: %d\n", result
));
3436 if(ps_global
->next_screen
!= SCREEN_FUN_NULL
|| result
== 1){
3438 if(cmd
== MC_FULLHDR
){
3439 if(ps_global
->full_header
== 1){
3443 * Figure out char offset of the char in the top left
3444 * corner of the display. Pass it back to the
3445 * fetcher/formatter and have it pass the offset
3448 sparms
->start
.on
= Offset
;
3449 for(sparms
->start
.loc
.offset
= line
= 0L;
3450 line
< cur_top_line
;
3452 sparms
->start
.loc
.offset
+=
3453 scroll_handle_column(line
, -1);
3456 sparms
->start
.on
= 0;
3460 sparms
->keys
.what
= FirstMenu
;
3463 sparms
->keys
.what
= SecondMenu
;
3466 sparms
->keys
.what
= ThirdMenu
;
3469 sparms
->keys
.what
= FourthMenu
;
3474 else if(!scroll_state(SS_CUR
)){
3475 num_display_lines
= SCROLL_LINES(ps_global
);
3476 ps_global
->mangled_screen
= 1;
3481 } /* End of switch() */
3483 /* Need to frame some handles? */
3484 if(sparms
->text
.handles
3486 && handle_on_page(sparms
->text
.handles
, cur_top_line
,
3487 cur_top_line
+ num_display_lines
))
3489 && handle_on_page(next_handle
, cur_top_line
,
3490 cur_top_line
+ num_display_lines
))))
3491 next_handle
= scroll_handle_in_frame(cur_top_line
);
3493 } /* End of while() -- loop executing commands */
3495 ps_global
->redrawer
= NULL
; /* next statement makes this invalid! */
3496 zero_scroll_text(); /* very important to zero out on return!!! */
3497 scroll_state(SS_FREE
);
3498 if(sparms
->bar
.color
)
3499 free_color_pair(&sparms
->bar
.color
);
3502 scroll_setrange(0L, 0L);
3508 /*----------------------------------------------------------------------
3511 Args: text -- The text to print out
3512 source -- What type of source text is
3513 message -- Message for open_printer()
3514 Handling of error conditions is very poor.
3518 print_to_printer(SCROLL_S
*sparms
)
3522 snprintf(message
, sizeof(message
), "%s", STYLE_NAME(sparms
));
3523 message
[sizeof(message
)-1] = '\0';
3525 if(open_printer(message
) != 0)
3528 switch(sparms
->text
.src
){
3530 if(sparms
->text
.text
!= (char *)NULL
)
3531 print_text((char *)sparms
->text
.text
);
3536 if(sparms
->text
.text
!= (char **)NULL
){
3539 for(t
= sparms
->text
.text
; *t
!= NULL
; t
++){
3541 print_text(NEWLINE
);
3548 if(sparms
->text
.text
!= (FILE *)NULL
) {
3552 fseek((FILE *)sparms
->text
.text
, 0L, 0);
3553 n
= SIZEOF_20KBUF
- 1;
3554 while((i
= fread((void *)tmp_20k_buf
, sizeof(char),
3555 n
, (FILE *)sparms
->text
.text
)) != 0) {
3556 tmp_20k_buf
[i
] = '\0';
3557 print_text(tmp_20k_buf
);
3570 /*----------------------------------------------------------------------
3571 Search text being viewed (help or message)
3573 Args: q_line -- The screen line to prompt for search string on
3574 start_line -- Line number in text to begin search on
3575 start_index -- Where to begin search at in first line of text
3576 cursor_pos -- position of cursor is returned to caller here
3577 (Actually, this isn't really the position of the
3578 cursor because we don't know where we are on the
3579 screen. So row is set to the line number and col
3580 is set to the right column.)
3581 offset_in_line -- Offset where match was found.
3583 Result: returns line number string was found on
3586 -3 if only match is at start_index - 1
3587 -4 if search to first line
3588 -5 if search to last line
3591 search_text(int q_line
, long int start_line
, int start_index
, char **report
,
3592 Pos
*cursor_pos
, int *offset_in_line
)
3594 char prompt
[MAX_SEARCH
+50], nsearch_string
[MAX_SEARCH
+1], *p
;
3597 static HISTORY_S
*history
= NULL
;
3598 char search_string
[MAX_SEARCH
+1];
3599 static ESCKEY_S word_search_key
[] = { { 0, 0, "", "" },
3600 {ctrl('Y'), 10, "^Y", N_("First Line")},
3601 {ctrl('V'), 11, "^V", N_("Last Line")},
3602 {KEY_UP
, 30, "", ""},
3603 {KEY_DOWN
, 31, "", ""},
3606 #define KU_ST (3) /* index of KEY_UP */
3608 init_hist(&history
, HISTSIZE
);
3611 * Put the last one used in the default search_string,
3612 * not in nsearch_string.
3614 search_string
[0] = '\0';
3615 if((p
= get_prev_hist(history
, "", 0, NULL
)) != NULL
){
3616 strncpy(search_string
, p
, sizeof(search_string
));
3617 search_string
[sizeof(search_string
)-1] = '\0';
3620 snprintf(prompt
, sizeof(prompt
), _("Word to search for [%s] : "), search_string
);
3622 nsearch_string
[0] = '\0';
3625 flags
= OE_APPEND_CURRENT
| OE_SEQ_SENSITIVE
| OE_KEEP_TRAILING_SPACE
;
3628 * 2 is really 1 because there will be one real entry and
3629 * one entry of "" because of the get_prev_hist above.
3631 if(items_in_hist(history
) > 2){
3632 word_search_key
[KU_ST
].name
= HISTORY_UP_KEYNAME
;
3633 word_search_key
[KU_ST
].label
= HISTORY_KEYLABEL
;
3634 word_search_key
[KU_ST
+1].name
= HISTORY_DOWN_KEYNAME
;
3635 word_search_key
[KU_ST
+1].label
= HISTORY_KEYLABEL
;
3638 word_search_key
[KU_ST
].name
= "";
3639 word_search_key
[KU_ST
].label
= "";
3640 word_search_key
[KU_ST
+1].name
= "";
3641 word_search_key
[KU_ST
+1].label
= "";
3644 rc
= optionally_enter(nsearch_string
, q_line
, 0, sizeof(nsearch_string
),
3645 prompt
, word_search_key
, help
, &flags
);
3648 help
= help
== NO_HELP
? h_oe_searchview
: NO_HELP
;
3653 *report
= _("Searched to First Line.");
3659 *report
= _("Searched to Last Line.");
3664 if((p
= get_prev_hist(history
, nsearch_string
, 0, NULL
)) != NULL
){
3665 strncpy(nsearch_string
, p
, sizeof(nsearch_string
));
3666 nsearch_string
[sizeof(nsearch_string
)-1] = '\0';
3674 if((p
= get_next_hist(history
, nsearch_string
, 0, NULL
)) != NULL
){
3675 strncpy(nsearch_string
, p
, sizeof(nsearch_string
));
3676 nsearch_string
[sizeof(nsearch_string
)-1] = '\0';
3684 if(rc
!= 4){ /* 4 is redraw */
3685 save_hist(history
, nsearch_string
, 0, NULL
);
3690 if(rc
== 1 || (search_string
[0] == '\0' && nsearch_string
[0] == '\0'))
3693 if(nsearch_string
[0] != '\0'){
3694 strncpy(search_string
, nsearch_string
, sizeof(search_string
)-1);
3695 search_string
[sizeof(search_string
)-1] = '\0';
3698 rc
= search_scroll_text(start_line
, start_index
, search_string
, cursor_pos
,
3704 /*----------------------------------------------------------------------
3705 Update the scroll tool's titlebar
3707 Args: cur_top_line --
3708 redraw -- flag to force updating
3712 update_scroll_titlebar(long int cur_top_line
, int redraw
)
3714 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
3715 int num_display_lines
= SCROLL_LINES(ps_global
);
3716 long new_line
= (cur_top_line
+ num_display_lines
> st
->num_lines
)
3718 : cur_top_line
+ num_display_lines
;
3720 COLOR_PAIR
*returned_color
= NULL
;
3721 COLOR_PAIR
*titlecolor
= NULL
;
3723 SEARCHSET
*ss
= NULL
;
3725 if(st
->parms
->use_indexline_color
3726 && ps_global
->titlebar_color_style
!= TBAR_COLOR_DEFAULT
){
3727 raw_msgno
= mn_m2raw(ps_global
->msgmap
, mn_get_cur(ps_global
->msgmap
));
3728 if(raw_msgno
> 0L && ps_global
->mail_stream
3729 && raw_msgno
<= ps_global
->mail_stream
->nmsgs
){
3730 ss
= mail_newsearchset();
3731 ss
->first
= ss
->last
= (unsigned long) raw_msgno
;
3735 PAT_STATE
*pstate
= NULL
;
3737 colormatch
= get_index_line_color(ps_global
->mail_stream
,
3738 ss
, &pstate
, &returned_color
);
3739 mail_free_searchset(&ss
);
3742 * This is a bit tricky. If there is a colormatch but returned_color
3743 * is NULL, that means that the user explicitly wanted the
3744 * Normal color used in this index line, so that is what we
3745 * use. If no colormatch then we will use the TITLE color
3746 * instead of Normal.
3750 titlecolor
= returned_color
;
3752 titlecolor
= new_color_pair(ps_global
->VAR_NORM_FORE_COLOR
,
3753 ps_global
->VAR_NORM_BACK_COLOR
);
3757 && ps_global
->titlebar_color_style
== TBAR_COLOR_REV_INDEXLINE
){
3758 char cbuf
[MAXCOLORLEN
+1];
3760 strncpy(cbuf
, titlecolor
->fg
, MAXCOLORLEN
);
3761 strncpy(titlecolor
->fg
, titlecolor
->bg
, MAXCOLORLEN
);
3762 strncpy(titlecolor
->bg
, cbuf
, MAXCOLORLEN
);
3766 /* Did the color change? */
3767 if((!titlecolor
&& st
->parms
->bar
.color
)
3769 (titlecolor
&& !st
->parms
->bar
.color
)
3771 (titlecolor
&& st
->parms
->bar
.color
3772 && (strcmp(titlecolor
->fg
, st
->parms
->bar
.color
->fg
)
3773 || strcmp(titlecolor
->bg
, st
->parms
->bar
.color
->bg
)))){
3776 if(st
->parms
->bar
.color
)
3777 free_color_pair(&st
->parms
->bar
.color
);
3779 st
->parms
->bar
.color
= titlecolor
;
3784 free_color_pair(&titlecolor
);
3789 set_titlebar(st
->parms
->bar
.title
, ps_global
->mail_stream
,
3790 ps_global
->context_current
, ps_global
->cur_folder
,
3791 ps_global
->msgmap
, 1, st
->parms
->bar
.style
,
3792 new_line
, st
->num_lines
, st
->parms
->bar
.color
);
3793 ps_global
->mangled_header
= 0;
3795 else if(st
->parms
->bar
.style
== TextPercent
)
3796 update_titlebar_lpercent(new_line
);
3798 update_titlebar_percent(new_line
);
3802 /*----------------------------------------------------------------------
3803 manager of global (to this module, anyway) scroll state structures
3808 scroll_state(int func
)
3810 struct scrollstack
{
3812 struct scrollstack
*prev
;
3814 static struct scrollstack
*stack
= NULL
;
3817 case SS_CUR
: /* no op */
3820 s
= (struct scrollstack
*)fs_get(sizeof(struct scrollstack
));
3821 memset((void *)s
, 0, sizeof(struct scrollstack
));
3828 fs_give((void **)&stack
);
3832 default: /* BUG: should complain */
3836 return(stack
? &stack
->s
: NULL
);
3840 /*----------------------------------------------------------------------
3841 Save all the data for scrolling text and paint the screen
3846 set_scroll_text(SCROLL_S
*sparms
, long int current_line
, SCRLCTRL_S
*st
)
3848 /* save all the stuff for possible asynchronous redraws */
3850 st
->top_text_line
= current_line
;
3851 st
->screen
.start_line
= SCROLL_LINES_ABOVE(ps_global
);
3852 st
->screen
.other_lines
= SCROLL_LINES_ABOVE(ps_global
)
3853 + SCROLL_LINES_BELOW(ps_global
);
3854 st
->screen
.width
= -1; /* Force text formatting calculation */
3858 /*----------------------------------------------------------------------
3859 Redraw the text on the screen, possibly reformatting if necessary
3865 redraw_scroll_text(void)
3868 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
3870 format_scroll_text();
3872 offset
= (st
->parms
->text
.src
== FileStar
) ? 0 : st
->top_text_line
;
3875 mswin_beginupdate();
3877 /*---- Actually display the text on the screen ------*/
3878 for(i
= 0; i
< st
->screen
.length
; i
++){
3879 ClearLine(i
+ st
->screen
.start_line
);
3880 if((offset
+ i
) < st
->num_lines
)
3881 PutLine0n8b(i
+ st
->screen
.start_line
, 0, st
->text_lines
[offset
+ i
],
3882 st
->line_lengths
[offset
+ i
], st
->parms
->text
.handles
);
3893 /*----------------------------------------------------------------------
3894 Free memory used as scrolling buffers for text on disk. Also mark
3895 text_lines as available
3898 zero_scroll_text(void)
3900 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
3903 for(i
= 0; i
< st
->lines_allocated
; i
++)
3904 if(st
->parms
->text
.src
== FileStar
&& st
->text_lines
[i
])
3905 fs_give((void **)&st
->text_lines
[i
]);
3907 st
->text_lines
[i
] = NULL
;
3909 if(st
->parms
->text
.src
== FileStar
&& st
->findex
!= NULL
){
3913 our_unlink(st
->fname
);
3914 fs_give((void **)&st
->fname
);
3919 fs_give((void **)&st
->text_lines
);
3921 if(st
->line_lengths
)
3922 fs_give((void **) &st
->line_lengths
);
3926 /*----------------------------------------------------------------------
3928 Always format at least 20 chars wide. Wrapping lines would be crazy for
3929 screen widths of 1-20 characters
3932 format_scroll_text(void)
3936 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
3938 register char **tl
, **tl_end
;
3940 if(!st
|| (st
->screen
.width
== (i
= ps_global
->ttyo
->screen_cols
)
3941 && st
->screen
.length
== PGSIZE(st
)))
3944 st
->screen
.width
= MAX(20, i
);
3945 st
->screen
.length
= PGSIZE(st
);
3947 if(st
->lines_allocated
== 0) {
3948 st
->lines_allocated
= TYPICAL_BIG_MESSAGE_LINES
;
3949 st
->text_lines
= (char **)fs_get(st
->lines_allocated
*sizeof(char *));
3950 memset(st
->text_lines
, 0, st
->lines_allocated
* sizeof(char *));
3951 st
->line_lengths
= (short *)fs_get(st
->lines_allocated
*sizeof(short));
3954 tl
= st
->text_lines
;
3955 ll
= st
->line_lengths
;
3956 tl_end
= &st
->text_lines
[st
->lines_allocated
];
3958 if(st
->parms
->text
.src
== CharStarStar
) {
3959 /*---- original text is already list of lines -----*/
3960 /* The text could be wrapped nicely for narrow screens; for now
3961 it will get truncated as it is displayed */
3962 for(pp
= (char **)st
->parms
->text
.text
; *pp
!= NULL
;) {
3964 *ll
++ = st
->screen
.width
;
3966 i
= tl
- st
->text_lines
;
3967 st
->lines_allocated
*= 2;
3968 fs_resize((void **)&st
->text_lines
,
3969 st
->lines_allocated
* sizeof(char *));
3970 fs_resize((void **)&st
->line_lengths
,
3971 st
->lines_allocated
*sizeof(short));
3972 tl
= &st
->text_lines
[i
];
3973 ll
= &st
->line_lengths
[i
];
3974 tl_end
= &st
->text_lines
[st
->lines_allocated
];
3978 st
->num_lines
= tl
- st
->text_lines
;
3980 else if (st
->parms
->text
.src
== CharStar
) {
3981 /*------ Format the plain text ------*/
3982 for(p
= (char *)st
->parms
->text
.text
; *p
; ) {
3985 for(; *p
&& !(*p
== RETURN
|| *p
== LINE_FEED
); p
++)
3991 i
= tl
- st
->text_lines
;
3992 st
->lines_allocated
*= 2;
3993 fs_resize((void **)&st
->text_lines
,
3994 st
->lines_allocated
* sizeof(char *));
3995 fs_resize((void **)&st
->line_lengths
,
3996 st
->lines_allocated
*sizeof(short));
3997 tl
= &st
->text_lines
[i
];
3998 ll
= &st
->line_lengths
[i
];
3999 tl_end
= &st
->text_lines
[st
->lines_allocated
];
4002 if(*p
== '\r' && *(p
+1) == '\n')
4004 else if(*p
== '\n' || *p
== '\r')
4008 st
->num_lines
= tl
- st
->text_lines
;
4011 /*------ Display text is in a file --------*/
4014 * This is pretty much only useful under DOS where we can't fit
4015 * all of big messages in core at once. This scheme makes
4016 * some simplifying assumptions:
4017 * 1. Lines are on disk just the way we'll display them. That
4018 * is, line breaks and such are left to the function that
4019 * writes the disk file to catch and fix.
4020 * 2. We get away with this mainly because the DOS display isn't
4021 * going to be resized out from under us.
4023 * The idea is to use the already alloc'd array of char * as a
4024 * buffer for sections of what's on disk. We'll set up the first
4025 * few lines here, and read new ones in as needed in
4026 * scroll_scroll_text().
4028 * but first, make sure there are enough buffer lines allocated
4029 * to serve as a place to hold lines from the file.
4031 * Actually, this is also used under windows so the display will
4032 * be resized out from under us. So I changed the following
4034 * 1. free old text_lines, which may have been allocated
4035 * for a narrow screen.
4036 * 2. insure we have enough text_lines
4037 * 3. reallocate all text_lines that are needed.
4038 * (tom unger 10/26/94)
4041 /* free old text lines, which may be too short. */
4042 for(i
= 0; i
< st
->lines_allocated
; i
++)
4043 if(st
->text_lines
[i
]) /* clear alloc'd lines */
4044 fs_give((void **)&st
->text_lines
[i
]);
4046 /* Insure we have enough text lines. */
4047 if(st
->lines_allocated
< (2 * PGSIZE(st
)) + 1){
4048 st
->lines_allocated
= (2 * PGSIZE(st
)) + 1; /* resize */
4050 fs_resize((void **)&st
->text_lines
,
4051 st
->lines_allocated
* sizeof(char *));
4052 memset(st
->text_lines
, 0, st
->lines_allocated
* sizeof(char *));
4053 fs_resize((void **)&st
->line_lengths
,
4054 st
->lines_allocated
*sizeof(short));
4057 /* reallocate all text lines that are needed. */
4058 for(i
= 0; i
<= PGSIZE(st
); i
++)
4059 if(st
->text_lines
[i
] == NULL
)
4060 st
->text_lines
[i
] = (char *)fs_get((st
->screen
.width
+ 1)
4063 tl
= &st
->text_lines
[i
];
4065 st
->num_lines
= make_file_index();
4067 ScrollFile(st
->top_text_line
); /* then load them up */
4071 * Efficiency hack. If there are handles, fill in their
4072 * line number field for later...
4074 if(st
->parms
->text
.handles
){
4079 for(line
= 0; line
< st
->num_lines
; line
++)
4080 for(i
= 0, col
= 0; i
< st
->line_lengths
[line
];)
4081 switch(st
->text_lines
[line
][i
]){
4084 switch((i
< st
->line_lengths
[line
]) ? st
->text_lines
[line
][i
]
4087 for(key
= 0, n
= st
->text_lines
[line
][++i
]; n
> 0; n
--)
4088 key
= (key
* 10) + (st
->text_lines
[line
][++i
] - '0');
4091 for(h
= st
->parms
->text
.handles
; h
; h
= h
->next
)
4093 scroll_handle_set_loc(&h
->loc
, line
, col
);
4097 if(!h
) /* anything behind us? */
4098 for(h
= st
->parms
->text
.handles
->prev
; h
; h
= h
->prev
)
4100 scroll_handle_set_loc(&h
->loc
, line
, col
);
4108 i
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
4120 default: /* literal embed char */
4128 while(((++col
) & 0x07) != 0) /* add tab's spaces */
4134 col
+= width_at_this_position((unsigned char*) &st
->text_lines
[line
][i
],
4135 st
->line_lengths
[line
] - i
);
4136 i
++; /* character count */
4142 scroll_setrange (st
->screen
.length
, st
->num_lines
);
4150 * ScrollFile - scroll text into the st struct file making sure 'line'
4151 * of the file is the one first in the text_lines buffer.
4153 * NOTE: talk about massive potential for tuning...
4154 * Goes without saying this is still under constuction
4157 ScrollFile(long int line
)
4159 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4163 if(line
<= 0){ /* reset and load first couple of pages */
4164 fseek((FILE *) st
->parms
->text
.text
, 0L, 0);
4171 for(i
= 0; i
< PGSIZE(st
); i
++){
4172 /*** do stuff to get the file pointer into the right place ***/
4174 * BOGUS: this is painfully crude right now, but I just want to get
4177 * possibly in the near furture, an array of indexes into the
4178 * file that are the offset for the beginning of each line will
4179 * speed things up. Of course, this
4180 * will have limits, so maybe a disk file that is an array
4181 * of indexes is the answer.
4183 if(fseek(st
->findex
, (size_t)(line
++) * sizeof(SCRLFILE_S
), 0) < 0
4184 || fread(&sf
, sizeof(SCRLFILE_S
), (size_t)1, st
->findex
) != 1
4185 || fseek((FILE *) st
->parms
->text
.text
, sf
.offset
, 0) < 0
4186 || !st
->text_lines
[i
]
4187 || (sf
.len
&& !fgets(st
->text_lines
[i
], sf
.len
+ 1,
4188 (FILE *) st
->parms
->text
.text
)))
4191 st
->line_lengths
[i
] = sf
.len
;
4194 for(; i
< PGSIZE(st
); i
++)
4195 if(st
->text_lines
[i
]){ /* blank out any unused lines */
4196 *st
->text_lines
[i
] = '\0';
4197 st
->line_lengths
[i
] = 0;
4203 * make_file_index - do a single pass over the file containing the text
4204 * to display, recording line lengths and offsets.
4205 * NOTE: This is never really to be used on a real OS with virtual
4206 * memory. This is the whole reason st->findex exists. Don't
4207 * want to waste precious memory on a stupid array that could
4211 make_file_index(void)
4213 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4220 st
->fname
= temp_nam(NULL
, "pi");
4222 if(!st
->fname
|| (st
->findex
= our_fopen(st
->fname
,"w+b")) == NULL
){
4224 our_unlink(st
->fname
);
4225 fs_give((void **)&st
->fname
);
4232 fseek(st
->findex
, 0L, 0);
4234 fseek((FILE *)st
->parms
->text
.text
, 0L, 0);
4237 sf
.len
= st
->screen
.width
+ 1;
4238 if(scroll_file_line((FILE *) st
->parms
->text
.text
,
4239 tmp_20k_buf
, &sf
, &state
)){
4240 fwrite((void *) &sf
, sizeof(SCRLFILE_S
), (size_t)1, st
->findex
);
4247 fseek((FILE *)st
->parms
->text
.text
, 0L, 0);
4253 /*----------------------------------------------------------------------
4254 Get the next line to scroll from the given file
4258 scroll_file_line(FILE *fp
, char *buf
, SCRLFILE_S
*sfp
, int *wrapt
)
4260 register char *s
= NULL
;
4264 sfp
->offset
= ftell(fp
);
4265 if(!(s
= fgets(buf
, sfp
->len
, fp
)))
4266 return(NULL
); /* can't grab a line? */
4270 *wrapt
= 1; /* remember; that we wrapped */
4273 else if(*s
== NEWLINE
[0] && (!NEWLINE
[1] || *(s
+1) == NEWLINE
[1])){
4274 int empty
= (*wrapt
&& s
== buf
);
4276 *wrapt
= 0; /* turn off wrapped state */
4278 s
= NULL
; /* get a new line */
4291 /*----------------------------------------------------------------------
4292 Scroll the text on the screen
4294 Args: new_top_line -- The line to be displayed on top of the screen
4295 redraw -- Flag to force a redraw even if nothing changed
4297 Returns: resulting top line
4298 Note: the returned line number may be less than new_top_line if
4299 reformatting caused the total line count to change.
4303 scroll_scroll_text(long int new_top_line
, HANDLE_S
*handle
, int redraw
)
4305 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4306 int num_display_lines
, l
, top
;
4307 POSLIST_S
*lp
, *lp2
;
4309 /* When this is true, we're still on the same page of the display. */
4310 if(st
->top_text_line
== new_top_line
&& !redraw
){
4311 /* handle changed, so hilite the new handle and unhilite the old */
4312 if(handle
&& handle
!= st
->parms
->text
.handles
){
4313 top
= st
->screen
.start_line
- new_top_line
;
4314 /* hilite the new one */
4315 if(!scroll_handle_obscured(handle
))
4316 for(lp
= handle
->loc
; lp
; lp
= lp
->next
)
4317 if((l
= lp
->where
.row
) >= st
->top_text_line
4318 && l
< st
->top_text_line
+ st
->screen
.length
){
4319 if(st
->parms
->text
.src
== FileStar
)
4322 PutLine0n8b(top
+ lp
->where
.row
, 0, st
->text_lines
[l
],
4323 st
->line_lengths
[l
], handle
);
4326 /* unhilite the old one */
4327 if(!scroll_handle_obscured(st
->parms
->text
.handles
))
4328 for(lp
= st
->parms
->text
.handles
->loc
; lp
; lp
= lp
->next
)
4329 if((l
= lp
->where
.row
) >= st
->top_text_line
4330 && l
< st
->top_text_line
+ st
->screen
.length
){
4331 for(lp2
= handle
->loc
; lp2
; lp2
= lp2
->next
)
4332 if(l
== lp2
->where
.row
)
4336 if(st
->parms
->text
.src
== FileStar
)
4339 PutLine0n8b(top
+ lp
->where
.row
, 0, st
->text_lines
[l
],
4340 st
->line_lengths
[l
], handle
);
4344 st
->parms
->text
.handles
= handle
; /* update current */
4347 return(new_top_line
);
4350 num_display_lines
= PGSIZE(st
);
4352 format_scroll_text();
4354 if(st
->top_text_line
>= st
->num_lines
) /* don't pop line count */
4355 new_top_line
= st
->top_text_line
= MAX(st
->num_lines
- 1, 0);
4357 if(st
->parms
->text
.src
== FileStar
)
4358 ScrollFile(new_top_line
); /* set up new st->text_lines */
4361 scroll_setrange (st
->screen
.length
, st
->num_lines
);
4362 scroll_setpos (new_top_line
);
4366 Check out the scrolling situation. If we want to scroll, but BeginScroll
4367 says we can't then repaint, + 10 is so we repaint most of the time.
4370 (st
->top_text_line
- new_top_line
+ 10 >= num_display_lines
||
4371 new_top_line
- st
->top_text_line
+ 10 >= num_display_lines
) ||
4372 BeginScroll(st
->screen
.start_line
,
4373 st
->screen
.start_line
+ num_display_lines
- 1) != 0) {
4374 /* Too much text to scroll, or can't scroll -- just repaint */
4377 st
->parms
->text
.handles
= handle
;
4379 st
->top_text_line
= new_top_line
;
4380 redraw_scroll_text();
4384 * We're going to scroll the screen, but first we have to make sure
4385 * the old hilited handles are unhilited if they are going to remain
4388 top
= st
->screen
.start_line
- st
->top_text_line
;
4389 if(handle
&& handle
!= st
->parms
->text
.handles
4390 && st
->parms
->text
.handles
4391 && !scroll_handle_obscured(st
->parms
->text
.handles
))
4392 for(lp
= st
->parms
->text
.handles
->loc
; lp
; lp
= lp
->next
)
4393 if((l
= lp
->where
.row
) >= MAX(st
->top_text_line
,new_top_line
)
4394 && l
< MIN(st
->top_text_line
,new_top_line
) + st
->screen
.length
){
4395 if(st
->parms
->text
.src
== FileStar
)
4398 PutLine0n8b(top
+ lp
->where
.row
, 0, st
->text_lines
[l
],
4399 st
->line_lengths
[l
], handle
);
4402 if(new_top_line
> st
->top_text_line
){
4403 /*------ scroll down ------*/
4404 while(new_top_line
> st
->top_text_line
) {
4407 l
= (st
->parms
->text
.src
== FileStar
)
4408 ? num_display_lines
- (new_top_line
- st
->top_text_line
)
4409 : st
->top_text_line
+ num_display_lines
;
4411 if(l
< st
->num_lines
){
4412 PutLine0n8b(st
->screen
.start_line
+ num_display_lines
- 1,
4413 0, st
->text_lines
[l
], st
->line_lengths
[l
],
4414 handle
? handle
: st
->parms
->text
.handles
);
4416 * We clear to the end of line in the right background
4417 * color. If the line was exactly the width of the screen
4418 * then PutLine0n8b will have left _col and _row moved to
4419 * the start of the next row. We don't need or want to clear
4422 if(pico_usingcolor()
4423 && (st
->line_lengths
[l
] < ps_global
->ttyo
->screen_cols
4424 || visible_linelen(l
) < ps_global
->ttyo
->screen_cols
))
4428 st
->top_text_line
++;
4432 /*------ scroll up -----*/
4433 while(new_top_line
< st
->top_text_line
) {
4436 st
->top_text_line
--;
4437 l
= (st
->parms
->text
.src
== FileStar
)
4438 ? st
->top_text_line
- new_top_line
4439 : st
->top_text_line
;
4440 PutLine0n8b(st
->screen
.start_line
, 0, st
->text_lines
[l
],
4441 st
->line_lengths
[l
],
4442 handle
? handle
: st
->parms
->text
.handles
);
4444 * We clear to the end of line in the right background
4445 * color. If the line was exactly the width of the screen
4446 * then PutLine0n8b will have left _col and _row moved to
4447 * the start of the next row. We don't need or want to clear
4450 if(pico_usingcolor()
4451 && (st
->line_lengths
[l
] < ps_global
->ttyo
->screen_cols
4452 || visible_linelen(l
) < ps_global
->ttyo
->screen_cols
))
4459 if(handle
&& handle
!= st
->parms
->text
.handles
){
4462 for(lp
= handle
->loc
; lp
; lp
= lp
->next
)
4463 if(lp
->where
.row
>= st
->top_text_line
4464 && lp
->where
.row
< st
->top_text_line
+ st
->screen
.length
){
4465 PutLine0n8b(st
->screen
.start_line
4466 + (lp
->where
.row
- st
->top_text_line
),
4467 0, st
->text_lines
[lp
->where
.row
],
4468 st
->line_lengths
[lp
->where
.row
],
4473 st
->parms
->text
.handles
= handle
;
4479 return(new_top_line
);
4483 /*---------------------------------------------------------------------
4484 Edit individual char in text so that the entire text doesn't need
4485 to be completely reformatted.
4487 Returns 0 if there were no errors, 1 if we would like the entire
4488 text to be reformatted.
4491 ng_scroll_edit(CONTEXT_S
*context
, int index
)
4493 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4494 char *ngp
, tmp
[MAILTMPLEN
+10];
4498 if (!(f
= folder_entry(index
, FOLDERS(context
))))
4501 return 0; /* nothing in scroll needs to be changed */
4502 tmp
[0] = TAG_HANDLE
;
4503 snprintf(tmp
+2, sizeof(tmp
)-2, "%d", st
->parms
->text
.handles
->key
);
4504 tmp
[sizeof(tmp
)-1] = '\0';
4505 tmp
[1] = len
= strlen(tmp
+2);
4506 snprintf(tmp
+len
+2, sizeof(tmp
)-(len
+2), "%s ", f
->selected
? "[ ]" : "[X]");
4507 tmp
[sizeof(tmp
)-1] = '\0';
4508 snprintf(tmp
+len
+6, sizeof(tmp
)-(len
+6), "%.*s", MAILTMPLEN
, f
->name
);
4509 tmp
[sizeof(tmp
)-1] = '\0';
4511 ngp
= *(st
->text_lines
);
4513 ngp
= strstr(ngp
, tmp
);
4518 /* assumption that text is of form "[ ] xxx.xxx" */
4525 else if (*ngp
== ' '){
4534 /*---------------------------------------------------------------------
4535 Similar to ng_scroll_edit, but this is the more general case of
4536 selecting a folder, as opposed to selecting a newsgroup for
4537 subscription while in listmode.
4539 Returns 0 if there were no errors, 1 if we would like the entire
4540 text to be reformatted.
4543 folder_select_update(CONTEXT_S
*context
, int index
)
4545 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4547 char *ngp
, tmp
[MAILTMPLEN
+10];
4548 int len
, total
, fnum
, num_sel
= 0;
4550 if (!(f
= folder_entry(index
, FOLDERS(context
))))
4552 ngp
= *(st
->text_lines
);
4554 total
= folder_total(FOLDERS(context
));
4556 for (fnum
= 0; num_sel
< 2 && fnum
< total
; fnum
++)
4557 if(folder_entry(fnum
, FOLDERS(context
))->selected
)
4559 if(!num_sel
|| (f
->selected
&& num_sel
== 1))
4560 return 1; /* need to reformat the whole thing */
4562 tmp
[0] = TAG_HANDLE
;
4563 snprintf(tmp
+2, sizeof(tmp
)-2, "%d", st
->parms
->text
.handles
->key
);
4564 tmp
[sizeof(tmp
)-1] = '\0';
4565 tmp
[1] = len
= strlen(tmp
+2);
4567 ngp
= strstr(ngp
, tmp
);
4570 if(F_ON(F_SELECTED_SHOWN_BOLD
, ps_global
)){
4572 while(*ngp
&& ngp
[0] != TAG_EMBED
4573 && ngp
[1] != (f
->selected
? TAG_BOLDOFF
: TAG_BOLDON
)
4574 && *ngp
!= *(f
->name
))
4577 if (!(*ngp
) || (*ngp
== *(f
->name
)))
4581 *ngp
= (f
->selected
? TAG_BOLDON
: TAG_BOLDOFF
);
4586 while(*ngp
!= ' ' && *ngp
!= *(f
->name
) && *ngp
)
4588 if(!(*ngp
) || (*ngp
== *(f
->name
)))
4592 *ngp
= f
->selected
? 'X' : ' ';
4599 /*---------------------------------------------------------------------
4600 We gotta go through all of the formatted text and add "[ ] " in the right
4601 place. If we don't do this, we must completely reformat the whole text,
4602 which could take a very long time.
4604 Return 1 if we encountered some sort of error and we want to reformat the
4605 whole text, return 0 if everything went as planned.
4607 ASSUMPTION: for this to work, we assume that there are only total
4608 number of handles, numbered 1 through total.
4611 scroll_add_listmode(CONTEXT_S
*context
, int total
)
4613 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4615 char *ngp
, *ngname
, handle_str
[MAILTMPLEN
];
4619 ngp
= *(st
->text_lines
);
4620 h
= st
->parms
->text
.handles
;
4622 while(h
&& h
->key
!= 1 && h
->prev
)
4625 handle_str
[0] = TAG_EMBED
;
4626 handle_str
[1] = TAG_HANDLE
;
4627 for(i
= 1; i
<= total
&& h
; i
++, h
= h
->next
){
4628 snprintf(handle_str
+3, sizeof(handle_str
)-3, "%d", h
->key
);
4629 handle_str
[sizeof(handle_str
)-1] = '\0';
4630 handle_str
[2] = strlen(handle_str
+3);
4631 ngp
= strstr(ngp
, handle_str
);
4633 ngp
= *(st
->text_lines
);
4637 ngname
= ngp
+ strlen(handle_str
);
4638 while (strncmp(ngp
, " ", 4) && !(*ngp
== '\n')
4639 && !(ngp
== *(st
->text_lines
)))
4641 if (strncmp(ngp
, " ", 4))
4643 while(ngp
+4 != ngname
&& *ngp
){
4648 if(folder_entry(h
->h
.f
.index
, FOLDERS(context
))->subscribed
){
4666 /*----------------------------------------------------------------------
4667 Search the set scrolling text
4669 Args: start_line -- line to start searching on
4670 start_index -- column to start searching at in first line
4671 word -- string to search for
4672 cursor_pos -- position of cursor is returned to caller here
4673 (Actually, this isn't really the position of the
4674 cursor because we don't know where we are on the
4675 screen. So row is set to the line number and col
4676 is set to the right column.)
4677 offset_in_line -- Offset where match was found.
4679 Returns: the line the word was found on, or -2 if it wasn't found, or
4680 -3 if the only match is at column start_index - 1.
4684 search_scroll_text(long int start_line
, int start_index
, char *word
,
4685 Pos
*cursor_pos
, int *offset_in_line
)
4687 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4689 long l
, offset
, dlines
;
4690 #define SROW(N) ((N) - offset)
4691 #define SLINE(N) st->text_lines[SROW(N)]
4692 #define SLEN(N) st->line_lengths[SROW(N)]
4694 dlines
= PGSIZE(st
);
4695 offset
= (st
->parms
->text
.src
== FileStar
) ? st
->top_text_line
: 0;
4697 if(start_line
< st
->num_lines
){
4698 /* search first line starting at position start_index in */
4699 if((wh
= search_scroll_line(SLINE(start_line
) + start_index
,
4701 SLEN(start_line
) - start_index
,
4702 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4703 cursor_pos
->row
= start_line
;
4704 cursor_pos
->col
= scroll_handle_column(SROW(start_line
),
4705 *offset_in_line
= wh
- SLINE(start_line
));
4709 if(st
->parms
->text
.src
== FileStar
)
4712 for(l
= start_line
+ 1; l
< st
->num_lines
; l
++) {
4713 if(st
->parms
->text
.src
== FileStar
&& l
> offset
+ dlines
)
4714 ScrollFile(offset
+= dlines
);
4716 if((wh
= search_scroll_line(SLINE(l
), word
, SLEN(l
),
4717 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4718 cursor_pos
->row
= l
;
4719 cursor_pos
->col
= scroll_handle_column(SROW(l
),
4720 *offset_in_line
= wh
- SLINE(l
));
4726 start_line
= st
->num_lines
;
4728 if(st
->parms
->text
.src
== FileStar
) /* wrap offset */
4729 ScrollFile(offset
= 0);
4731 for(l
= 0; l
< start_line
; l
++) {
4732 if(st
->parms
->text
.src
== FileStar
&& l
> offset
+ dlines
)
4733 ScrollFile(offset
+= dlines
);
4735 if((wh
= search_scroll_line(SLINE(l
), word
, SLEN(l
),
4736 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4737 cursor_pos
->row
= l
;
4738 cursor_pos
->col
= scroll_handle_column(SROW(l
),
4739 *offset_in_line
= wh
- SLINE(l
));
4744 /* search in current line */
4745 if(start_line
< st
->num_lines
4746 && (wh
= search_scroll_line(SLINE(start_line
), word
,
4747 start_index
+ strlen(word
) - 2,
4748 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4749 cursor_pos
->row
= start_line
;
4750 cursor_pos
->col
= scroll_handle_column(SROW(start_line
),
4751 *offset_in_line
= wh
- SLINE(start_line
));
4756 /* see if the only match is a repeat */
4757 if(start_index
> 0 && start_line
< st
->num_lines
4758 && (wh
= search_scroll_line(
4759 SLINE(start_line
) + start_index
- 1,
4761 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4762 cursor_pos
->row
= start_line
;
4763 cursor_pos
->col
= scroll_handle_column(SROW(start_line
),
4764 *offset_in_line
= wh
- SLINE(start_line
));
4772 /*----------------------------------------------------------------------
4773 Search one line of scroll text for given string
4775 Args: haystack -- The string to search in, the larger string
4776 needle -- The string to search for, the smaller string
4777 n -- The max number of chars in haystack to search
4779 Search for first occurrence of needle in the haystack, and return a pointer
4780 into the string haystack when it is found. The search is case independent.
4783 search_scroll_line(char *haystack
, char *needle
, int n
, int handles
)
4785 char *return_ptr
= NULL
, *found_it
= NULL
;
4786 char *haystack_copy
, *p
, *free_this
= NULL
, *end
;
4788 int state
= 0, i
= 0;
4790 if(n
> 0 && haystack
){
4792 haystack_copy
= buf
;
4794 haystack_copy
= free_this
= (char *) fs_get((n
+1) * sizeof(char));
4796 strncpy(haystack_copy
, haystack
, n
);
4797 haystack_copy
[n
] = '\0';
4800 * We don't want to match text inside embedded tags.
4801 * Replace embedded octets with nulls and convert
4802 * uppercase ascii to lowercase. We should also do
4803 * some sort of canonicalization of UTF-8 but that
4806 for(i
= n
, p
= haystack_copy
; i
-- > 0 && *p
; p
++){
4810 if(*p
== TAG_EMBED
){
4816 /* lower case just ascii chars */
4817 if(!(*p
& 0x80) && isupper(*p
))
4824 state
= (*p
== TAG_HANDLE
)
4826 : (*p
== TAG_FGCOLOR
|| *p
== TAG_BGCOLOR
) ? RGBLEN
: 0;
4831 state
= *p
; /* length of handle's key */
4843 * The haystack_copy string now looks like
4845 * "chars\0\0\0\0\0\0chars...\0\0\0chars... \0"
4847 * with that final \0 at haystack_copy[n].
4848 * Search each piece one at a time.
4851 end
= haystack_copy
+ n
;
4854 while(p
< end
&& !return_ptr
){
4857 while(*p
== '\0' && p
< end
)
4861 found_it
= srchstr(p
, needle
);
4864 /* found it, make result relative to haystack */
4865 return_ptr
= haystack
+ (found_it
- haystack_copy
);
4868 /* skip to next null */
4869 while(*p
!= '\0' && p
< end
)
4874 fs_give((void **) &free_this
);
4882 * Returns the number of columns taken up by the visible part of the line.
4883 * That is, account for handles and color changes and so forth.
4886 visible_linelen(int line
)
4888 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4891 if(line
< 0 || line
>= st
->num_lines
)
4894 for(i
= 0, len
= 0; i
< st
->line_lengths
[line
];)
4895 switch(st
->text_lines
[line
][i
]){
4899 switch((i
< st
->line_lengths
[line
]) ? st
->text_lines
[line
][i
] : 0){
4902 /* skip the length byte plus <length> more bytes */
4903 if(i
< st
->line_lengths
[line
]){
4904 n
= st
->text_lines
[line
][i
];
4908 if(i
< st
->line_lengths
[line
] && n
> 0)
4915 i
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
4927 case TAG_EMBED
: /* escaped embed character */
4932 default: /* the embed char was literal */
4942 while(((++len
) & 0x07) != 0) /* add tab's spaces */
4957 /*----------------------------------------------------------------------
4958 Display the contents of the given file (likely output from some command)
4960 Args: filename -- name of file containing output
4961 title -- title to be used for screen displaying output
4962 alt_msg -- if no output, Q this message instead of the default
4963 mode -- non-zero to display short files in status line
4967 display_output_file(char *filename
, char *title
, char *alt_msg
, int mode
)
4969 STORE_S
*in_file
= NULL
, *out_store
= NULL
;
4971 if((in_file
= so_get(FileStar
, filename
, READ_ACCESS
|READ_FROM_LOCALE
))){
4972 if(mode
== DOF_BRIEF
){
4973 int msg_q
= 0, i
= 0;
4974 char buf
[512], *msg_p
[4];
4975 #define MAX_SINGLE_MSG_LEN 60
4981 * Might need to do something about CRLFs for Windows.
4983 while(so_fgets(in_file
, msg_p
[msg_q
], sizeof(buf
) - (msg_p
[msg_q
] - buf
))
4985 && (i
= strlen(msg_p
[msg_q
])) < MAX_SINGLE_MSG_LEN
){
4986 msg_p
[msg_q
+1] = msg_p
[msg_q
]+strlen(msg_p
[msg_q
]);
4987 if (*(msg_p
[++msg_q
] - 1) == '\n')
4988 *(msg_p
[msg_q
] - 1) = '\0';
4991 if(msg_q
< 3 && i
< MAX_SINGLE_MSG_LEN
){
4993 for(i
= 0; i
< msg_q
; i
++)
4994 q_status_message2(SM_ORDER
, 3, 4,
4995 "%s Result: %s", title
, msg_p
[i
]);
4997 q_status_message2(SM_ORDER
, 0, 4, "%s%s", title
,
5000 : " command completed with no output");
5006 else if(mode
== DOF_EMPTY
){
5009 if(so_readc(&c
, in_file
) < 1){
5010 q_status_message2(SM_ORDER
, 0, 4, "%s%s", title
,
5013 : " command completed with no output");
5020 * We need to translate the file contents from the user's locale
5021 * charset to UTF-8 for use in scrolltool. We get that translation
5022 * from the READ_FROM_LOCALE in the in_file storage object.
5023 * It would be nice to skip this step but scrolltool doesn't use
5024 * the storage object routines to read from the file, so would
5025 * skip the translation step.
5031 if(!(out_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
5033 our_unlink(filename
);
5034 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5035 _("Error allocating space."));
5039 so_seek(in_file
, 0L, 0);
5043 gf_link_filter(gf_wrap
,
5044 gf_wrap_filter_opt(ps_global
->ttyo
->screen_cols
- 4,
5045 ps_global
->ttyo
->screen_cols
,
5046 NULL
, 0, GFW_NONE
));
5048 gf_set_so_readc(&gc
, in_file
);
5049 gf_set_so_writec(&pc
, out_store
);
5051 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
5053 so_give(&out_store
);
5054 our_unlink(filename
);
5055 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5056 _("Error allocating space."));
5060 gf_clear_so_writec(out_store
);
5061 gf_clear_so_readc(in_file
);
5069 snprintf(title_buf
, sizeof(title_buf
), "HELP FOR %s VIEW", title
);
5070 title_buf
[sizeof(title_buf
)-1] = '\0';
5072 memset(&sargs
, 0, sizeof(SCROLL_S
));
5073 sargs
.text
.text
= so_text(out_store
);
5074 sargs
.text
.src
= CharStar
;
5075 sargs
.text
.desc
= "output";
5076 sargs
.bar
.title
= title
;
5077 sargs
.bar
.style
= TextPercent
;
5078 sargs
.help
.text
= h_simple_text_view
;
5079 sargs
.help
.title
= title_buf
;
5081 ps_global
->mangled_screen
= 1;
5082 so_give(&out_store
);
5085 our_unlink(filename
);
5088 dprint((2, "Error reopening %s to get results: %s\n",
5089 filename
? filename
: "?", error_description(errno
)));
5093 /*--------------------------------------------------------------------
5094 Call the function that will perform the double click operation
5096 Returns: the current top line
5099 doubleclick_handle(SCROLL_S
*sparms
, HANDLE_S
*next_handle
, int *cmd
, int *done
)
5101 if(sparms
->mouse
.clickclick
){
5102 if((*sparms
->mouse
.clickclick
)(sparms
))
5106 switch(scroll_handle_launch(next_handle
, TRUE
)){
5108 *cmd
= MC_EXIT
; /* propagate */
5113 cmd_cancelled("View");
5120 return(scroll_state(SS_CUR
)->top_text_line
);
5127 * Just a little something to simplify assignments
5129 #define VIEWPOPUP(p, c, s) { \
5130 (p)->type = tQueue; \
5131 (p)->data.val = c; \
5132 (p)->label.style = lNormal; \
5133 (p)->label.string = s; \
5141 format_message_popup(sparms
, in_handle
)
5145 MPopup fmp_menu
[32];
5151 /* Reason to offer per message ops? */
5152 if(mn_get_total(ps_global
->msgmap
) > 0L){
5154 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5156 switch((h
= get_handle(st
->parms
->text
.handles
, in_handle
))->type
){
5158 fmp_menu
[++i
].type
= tIndex
;
5159 fmp_menu
[i
].label
.string
= "View Attachment";
5160 fmp_menu
[i
].label
.style
= lNormal
;
5161 fmp_menu
[i
].data
.val
= 'X'; /* for local use */
5164 && dispatch_attachment(h
->h
.attach
) != MCD_NONE
5165 && !(h
->h
.attach
->can_display
& MCD_EXTERNAL
)
5166 && h
->h
.attach
->body
5167 && (h
->h
.attach
->body
->type
== TYPETEXT
5168 || (h
->h
.attach
->body
->type
== TYPEMESSAGE
5169 && h
->h
.attach
->body
->subtype
5170 && !strucmp(h
->h
.attach
->body
->subtype
,"rfc822")))){
5171 fmp_menu
[++i
].type
= tIndex
;
5172 fmp_menu
[i
].label
.string
= "View Attachment in New Window";
5173 fmp_menu
[i
].label
.style
= lNormal
;
5174 fmp_menu
[i
].data
.val
= 'Y'; /* for local use */
5177 fmp_menu
[++i
].type
= tIndex
;
5178 fmp_menu
[i
].label
.style
= lNormal
;
5179 fmp_menu
[i
].data
.val
= 'Z'; /* for local use */
5180 msgno_exceptions(ps_global
->mail_stream
,
5181 mn_m2raw(ps_global
->msgmap
,
5182 mn_get_cur(ps_global
->msgmap
)),
5183 h
->h
.attach
->number
, &n
, FALSE
);
5184 fmp_menu
[i
].label
.string
= (n
& MSG_EX_DELETE
)
5185 ? "Undelete Attachment"
5186 : "Delete Attachment";
5191 fmp_menu
[++i
].type
= tIndex
;
5192 fmp_menu
[i
].label
.string
= "View Link";
5193 fmp_menu
[i
].label
.style
= lNormal
;
5194 fmp_menu
[i
].data
.val
= 'X'; /* for local use */
5196 fmp_menu
[++i
].type
= tIndex
;
5197 fmp_menu
[i
].label
.string
= "Copy Link";
5198 fmp_menu
[i
].label
.style
= lNormal
;
5199 fmp_menu
[i
].data
.val
= 'W'; /* for local use */
5203 fmp_menu
[++i
].type
= tSeparator
;
5206 /* Delete or Undelete? That is the question. */
5207 fmp_menu
[++i
].type
= tQueue
;
5208 fmp_menu
[i
].label
.style
= lNormal
;
5209 mc
= ((rawno
= mn_m2raw(ps_global
->msgmap
,
5210 mn_get_cur(ps_global
->msgmap
))) > 0L
5211 && ps_global
->mail_stream
5212 && rawno
<= ps_global
->mail_stream
->nmsgs
)
5213 ? mail_elt(ps_global
->mail_stream
, rawno
) : NULL
;
5214 if(mc
&& mc
->deleted
){
5215 fmp_menu
[i
].data
.val
= 'U';
5216 fmp_menu
[i
].label
.string
= in_handle
5217 ? "&Undelete Message" : "&Undelete";
5220 fmp_menu
[i
].data
.val
= 'D';
5221 fmp_menu
[i
].label
.string
= in_handle
5222 ? "&Delete Message" : "&Delete";
5225 if(F_ON(F_ENABLE_FLAG
, ps_global
)){
5226 fmp_menu
[++i
].type
= tSubMenu
;
5227 fmp_menu
[i
].label
.string
= "Flag";
5228 fmp_menu
[i
].data
.submenu
= flag_submenu(mc
);
5232 VIEWPOPUP(&fmp_menu
[i
], 'S', in_handle
? "&Save Message" : "&Save");
5235 VIEWPOPUP(&fmp_menu
[i
], 'E', in_handle
? "&Export Message" : "&Export");
5238 VIEWPOPUP(&fmp_menu
[i
], '%', in_handle
? "Print Message" : "Print");
5241 VIEWPOPUP(&fmp_menu
[i
], 'R',
5242 in_handle
? "&Reply to Message" : "&Reply");
5245 VIEWPOPUP(&fmp_menu
[i
], 'F',
5246 in_handle
? "&Forward Message" : "&Forward");
5249 VIEWPOPUP(&fmp_menu
[i
], 'B',
5250 in_handle
? "&Bounce Message" : "&Bounce");
5253 VIEWPOPUP(&fmp_menu
[i
], 'T', "&Take Addresses");
5255 fmp_menu
[++i
].type
= tSeparator
;
5257 if(mn_get_cur(ps_global
->msgmap
) < mn_get_total(ps_global
->msgmap
)){
5259 VIEWPOPUP(&fmp_menu
[i
], 'N', "View &Next Message");
5262 if(mn_get_cur(ps_global
->msgmap
) > 0){
5264 VIEWPOPUP(&fmp_menu
[i
], 'P', "View &Prev Message");
5267 if(mn_get_cur(ps_global
->msgmap
) < mn_get_total(ps_global
->msgmap
)
5268 || mn_get_cur(ps_global
->msgmap
) > 0)
5269 fmp_menu
[++i
].type
= tSeparator
;
5271 /* Offer the attachment screen? */
5272 for(n
= 0; ps_global
->atmts
&& ps_global
->atmts
[n
].description
; n
++)
5277 VIEWPOPUP(&fmp_menu
[i
], 'V', "&View Attachment Index");
5282 VIEWPOPUP(&fmp_menu
[i
], 'I', "Message &Index");
5285 VIEWPOPUP(&fmp_menu
[i
], 'M', "&Main Menu");
5287 fmp_menu
[++i
].type
= tTail
;
5289 if((i
= mswin_popup(fmp_menu
)) >= 0 && in_handle
)
5290 switch(fmp_menu
[i
].data
.val
){
5291 case 'W' : /* Copy URL to clipboard */
5292 mswin_addclipboard(h
->h
.url
.path
);
5296 return(1); /* return like the user double-clicked */
5300 case 'Y' : /* popup the thing in another window */
5301 display_att_window(h
->h
.attach
);
5305 if(h
&& h
->type
== Attach
){
5306 msgno_exceptions(ps_global
->mail_stream
,
5307 mn_m2raw(ps_global
->msgmap
,
5308 mn_get_cur(ps_global
->msgmap
)),
5309 h
->h
.attach
->number
, &n
, FALSE
);
5311 msgno_exceptions(ps_global
->mail_stream
,
5312 mn_m2raw(ps_global
->msgmap
,
5313 mn_get_cur(ps_global
->msgmap
)),
5314 h
->h
.attach
->number
, &n
, TRUE
);
5315 q_status_message2(SM_ORDER
, 0, 3, "Attachment %s %s!",
5316 h
->h
.attach
->number
,
5317 (n
& MSG_EX_DELETE
) ? "deleted" : "undeleted");
5337 simple_text_popup(sparms
, in_handle
)
5341 MPopup simple_menu
[12];
5344 VIEWPOPUP(&simple_menu
[n
], '%', "Print");
5347 VIEWPOPUP(&simple_menu
[n
], 'S', "Save");
5350 VIEWPOPUP(&simple_menu
[n
], 'F', "Forward in Email");
5353 simple_menu
[n
++].type
= tSeparator
;
5355 VIEWPOPUP(&simple_menu
[n
], 'E', "Exit Viewer");
5358 simple_menu
[n
].type
= tTail
;
5360 (void) mswin_popup(simple_menu
);
5366 /*----------------------------------------------------------------------
5367 Return characters in scroll tool buffer serially
5369 Args: n -- index of char to return
5371 Returns: returns the character at index 'n', or -1 on error or
5376 mswin_readscrollbuf(n
)
5379 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5381 static char **orig
= NULL
, **l
, *p
;
5388 * All of these are mind-numbingly slow at the moment...
5390 switch(st
->parms
->text
.src
){
5392 return((n
>= strlen((char *)st
->parms
->text
.text
))
5393 ? -1 : ((char *)st
->parms
->text
.text
)[n
]);
5396 /* BUG? is this test rigorous enough? */
5397 if(orig
!= (char **)st
->parms
->text
.text
|| n
< lastn
){
5399 if(orig
= l
= (char **)st
->parms
->text
.text
) /* reset l and p */
5402 else{ /* use cached l and p */
5403 c
= n
; /* and adjust n */
5408 while(l
){ /* look for 'n' on each line */
5409 for(; n
&& *p
; n
--, p
++)
5412 if(n
--) /* 'n' found ? */
5418 return((l
&& *l
) ? *p
? *p
: '\n' : -1);
5421 return((fseek((FILE *)st
->parms
->text
.text
, (long) n
, 0) < 0
5422 || (c
= fgetc((FILE *)st
->parms
->text
.text
)) == EOF
) ? -1 : c
);
5431 /*----------------------------------------------------------------------
5432 MSWin scroll callback. Called during scroll message processing.
5436 Args: cmd - what type of scroll operation.
5437 scroll_pos - paramter for operation.
5438 used as position for SCROLL_TO operation.
5440 Returns: TRUE - did the scroll operation.
5441 FALSE - was not able to do the scroll operation.
5444 pcpine_do_scroll (cmd
, scroll_pos
)
5448 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5449 HANDLE_S
*next_handle
;
5451 int num_display_lines
;
5458 maxscroll
= st
->num_lines
;
5460 case MSWIN_KEY_SCROLLUPLINE
:
5461 if(st
->top_text_line
> 0) {
5462 st
->top_text_line
-= (int) scroll_pos
;
5464 if (st
->top_text_line
<= 0){
5465 snprintf(message
, sizeof(message
), "START of %.*s",
5466 32, STYLE_NAME(st
->parms
));
5467 message
[sizeof(message
)-1] = '\0';
5468 st
->top_text_line
= 0;
5473 case MSWIN_KEY_SCROLLDOWNLINE
:
5474 if(st
->top_text_line
< maxscroll
) {
5475 st
->top_text_line
+= (int) scroll_pos
;
5477 if (st
->top_text_line
>= maxscroll
){
5478 snprintf(message
, sizeof(message
), "END of %.*s", 32, STYLE_NAME(st
->parms
));
5479 message
[sizeof(message
)-1] = '\0';
5480 st
->top_text_line
= maxscroll
;
5485 case MSWIN_KEY_SCROLLUPPAGE
:
5486 if(st
->top_text_line
> 0) {
5487 num_display_lines
= SCROLL_LINES(ps_global
);
5488 scroll_lines
= MIN(MAX(num_display_lines
-
5489 ps_global
->viewer_overlap
, 1), num_display_lines
);
5490 if (st
->top_text_line
> scroll_lines
)
5491 st
->top_text_line
-= scroll_lines
;
5493 st
->top_text_line
= 0;
5494 snprintf(message
, sizeof(message
), "START of %.*s", 32, STYLE_NAME(st
->parms
));
5495 message
[sizeof(message
)-1] = '\0';
5501 case MSWIN_KEY_SCROLLDOWNPAGE
:
5502 num_display_lines
= SCROLL_LINES(ps_global
);
5503 if(st
->top_text_line
< maxscroll
) {
5504 scroll_lines
= MIN(MAX(num_display_lines
-
5505 ps_global
->viewer_overlap
, 1), num_display_lines
);
5506 st
->top_text_line
+= scroll_lines
;
5507 if (st
->top_text_line
>= maxscroll
) {
5508 st
->top_text_line
= maxscroll
;
5509 snprintf(message
, sizeof(message
), "END of %.*s", 32, STYLE_NAME(st
->parms
));
5510 message
[sizeof(message
)-1] = '\0';
5516 case MSWIN_KEY_SCROLLTO
:
5517 if (st
->top_text_line
!= scroll_pos
) {
5518 st
->top_text_line
= scroll_pos
;
5519 if (st
->top_text_line
== 0)
5520 snprintf(message
, sizeof(message
), "START of %.*s", 32, STYLE_NAME(st
->parms
));
5521 else if(st
->top_text_line
>= maxscroll
)
5522 snprintf(message
, sizeof(message
), "END of %.*s", 32, STYLE_NAME(st
->parms
));
5524 message
[sizeof(message
)-1] = '\0';
5530 /* Need to frame some handles? */
5531 if(st
->parms
->text
.handles
5532 && (next_handle
= scroll_handle_in_frame(st
->top_text_line
)))
5533 st
->parms
->text
.handles
= next_handle
;
5536 mswin_beginupdate();
5537 update_scroll_titlebar(st
->top_text_line
, 0);
5538 (void) scroll_scroll_text(st
->top_text_line
,
5539 st
->parms
->text
.handles
, 1);
5541 q_status_message(SM_INFO
, 0, 1, message
);
5543 /* Display is always called so that the "START(END) of message"
5544 * message gets removed when no longer at the start(end). */
5545 display_message (KEY_PGDN
);
5554 pcpine_help_scroll(title
)
5557 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5560 strncpy(title
, (st
->parms
->help
.title
)
5561 ? st
->parms
->help
.title
: "Alpine Help", 256);
5563 return(pcpine_help(st
->parms
->help
.text
));
5568 pcpine_view_cursor(col
, row
)
5572 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5576 return((row
>= HEADER_ROWS(ps_global
)
5577 && row
< HEADER_ROWS(ps_global
) + SCROLL_LINES(ps_global
)
5578 && (line
= (row
- 2) + st
->top_text_line
) < st
->num_lines
5579 && (key
= dot_on_handle(line
, col
))
5580 && scroll_handle_selectable(get_handle(st
->parms
->text
.handles
,key
)))
5582 : MSWIN_CURSOR_ARROW
);
5584 #endif /* _WINDOWS */