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 (*handle
->h
.func
.f
)(handle
->h
.func
.args
.stream
,
983 handle
->h
.func
.args
.msgmap
,
984 handle
->h
.func
.args
.msgno
);
989 alpine_panic("Unexpected HANDLE type");
997 scroll_handle_obscured(HANDLE_S
*handle
)
999 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1001 return(handle_on_page(handle
, st
->top_text_line
,
1002 st
->top_text_line
+ st
->screen
.length
));
1008 * scroll_handle_in_frame -- return handle pointer to visible handle.
1011 scroll_handle_in_frame(long int top_line
)
1013 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1016 switch(handle_on_page(hp
= st
->parms
->text
.handles
, top_line
,
1017 top_line
+ st
->screen
.length
)){
1018 case -1 : /* handle above page */
1019 /* Find first handle from top of page */
1020 for(hp
= st
->parms
->text
.handles
->next
; hp
; hp
= hp
->next
)
1021 if(scroll_handle_selectable(hp
))
1022 switch(handle_on_page(hp
, top_line
, top_line
+ st
->screen
.length
)){
1023 case 0 : return(hp
);
1024 case 1 : return(NULL
);
1025 case -1 : default : break;
1030 case 1 : /* handle below page */
1031 /* Find first handle from top of page */
1032 for(hp
= st
->parms
->text
.handles
->prev
; hp
; hp
= hp
->prev
)
1033 if(scroll_handle_selectable(hp
))
1034 switch(handle_on_page(hp
, top_line
, top_line
+ st
->screen
.length
)){
1035 case 0 : return(hp
);
1036 case -1 : return(NULL
);
1037 case 1 : default : break;
1051 * scroll_handle_reframe -- adjust display params to display given handle
1054 scroll_handle_reframe(int key
, int center
)
1056 long l
, offset
, dlines
, start_line
;
1057 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1059 dlines
= PGSIZE(st
);
1060 offset
= (st
->parms
->text
.src
== FileStar
) ? st
->top_text_line
: 0;
1061 start_line
= st
->top_text_line
;
1064 key
= st
->parms
->text
.handles
->key
;
1066 /* Searc down from the top line */
1067 for(l
= start_line
; l
< st
->num_lines
; l
++) {
1068 if(st
->parms
->text
.src
== FileStar
&& l
> offset
+ dlines
)
1069 ScrollFile(offset
+= dlines
);
1071 if(handle_on_line(l
- offset
, key
))
1075 if(l
< st
->num_lines
){
1076 if(l
>= dlines
+ start_line
) /* bingo! */
1077 start_line
= l
- ((center
? (dlines
/ 2) : dlines
) - 1);
1080 if(st
->parms
->text
.src
== FileStar
) /* wrap offset */
1081 ScrollFile(offset
= 0);
1083 for(l
= 0; l
< start_line
; l
++) {
1084 if(st
->parms
->text
.src
== FileStar
&& l
> offset
+ dlines
)
1085 ScrollFile(offset
+= dlines
);
1087 if(handle_on_line(l
- offset
, key
))
1092 alpine_panic("Internal Error: no handle found");
1102 handle_on_line(long int line
, int goal
)
1105 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1107 for(i
= 0; i
< st
->line_lengths
[line
]; i
++)
1108 if(st
->text_lines
[line
][i
] == TAG_EMBED
1109 && st
->text_lines
[line
][++i
] == TAG_HANDLE
){
1110 for(key
= 0, n
= st
->text_lines
[line
][++i
]; n
; n
--)
1111 key
= (key
* 10) + (st
->text_lines
[line
][++i
] - '0');
1122 handle_on_page(HANDLE_S
*handle
, long int first_line
, long int last_line
)
1127 if(handle
&& (l
= handle
->loc
)){
1128 for( ; l
; l
= l
->next
)
1129 if(l
->where
.row
< first_line
){
1133 else if(l
->where
.row
>= last_line
){
1138 return(0); /* found! */
1146 scroll_handle_selectable(HANDLE_S
*handle
)
1148 return(handle
&& (handle
->type
!= URL
1149 || (handle
->h
.url
.path
&& *handle
->h
.url
.path
)));
1154 scroll_handle_next_sel(HANDLE_S
*handles
)
1156 while(handles
&& !scroll_handle_selectable(handles
= handles
->next
))
1164 scroll_handle_prev_sel(HANDLE_S
*handles
)
1166 while(handles
&& !scroll_handle_selectable(handles
= handles
->prev
))
1174 scroll_handle_next(HANDLE_S
*handles
)
1176 HANDLE_S
*next
= NULL
;
1178 if(scroll_handle_obscured(handles
) <= 0){
1180 while((next
= scroll_handle_next_sel(next
))
1181 && scroll_handle_obscured(next
))
1191 scroll_handle_prev(HANDLE_S
*handles
)
1193 HANDLE_S
*prev
= NULL
;
1195 if(scroll_handle_obscured(handles
) >= 0){
1197 while((prev
= scroll_handle_prev_sel(prev
))
1198 && scroll_handle_obscured(prev
))
1207 scroll_handle_boundary(HANDLE_S
*handle
, HANDLE_S
*(*f
) (HANDLE_S
*))
1209 HANDLE_S
*hp
, *whp
= NULL
;
1211 /* Multi-line handle? Punt! */
1212 if(handle
&& (!(hp
= handle
)->loc
->next
))
1213 while((hp
= (*f
)(hp
))
1214 && hp
->loc
->where
.row
== handle
->loc
->where
.row
)
1222 scroll_handle_column(int line
, int offset
)
1224 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1228 limit
= (offset
> -1) ? offset
: st
->line_lengths
[line
];
1230 for(i
= 0, col
= 0; i
< limit
;){
1231 if(st
&& st
->text_lines
&& st
->text_lines
[line
])
1232 switch(st
->text_lines
[line
][i
]){
1235 switch((i
< limit
) ? st
->text_lines
[line
][i
] : 0){
1237 for(key
= 0, n
= st
->text_lines
[line
][++i
]; n
> 0 && i
< limit
-1; n
--)
1238 key
= (key
* 10) + (st
->text_lines
[line
][++i
] - '0');
1245 i
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
1257 default: /* literal embed char */
1265 while(((++col
) & 0x07) != 0) /* add tab's spaces */
1271 col
+= width_at_this_position((unsigned char*) &st
->text_lines
[line
][i
],
1272 st
->line_lengths
[line
] - i
);
1283 scroll_handle_index(int row
, int column
)
1285 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1288 for(index
= 0; column
> 0;)
1289 switch(st
->text_lines
[row
][index
++]){
1291 switch(st
->text_lines
[row
][index
++]){
1293 index
+= st
->text_lines
[row
][index
] + 1;
1307 case TAB
: /* add tab's spaces */
1308 while(((--column
) & 0x07) != 0)
1323 scroll_handle_set_loc(POSLIST_S
**lpp
, int line
, int column
)
1327 lp
= (POSLIST_S
*) fs_get(sizeof(POSLIST_S
));
1328 lp
->where
.row
= line
;
1329 lp
->where
.col
= column
;
1331 for(; *lpp
; lpp
= &(*lpp
)->next
)
1339 dot_on_handle(long int line
, int goal
)
1341 int i
= 0, n
, key
= 0, column
= -1;
1342 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1345 if(i
>= st
->line_lengths
[line
])
1348 switch(st
->text_lines
[line
][i
++]){
1350 switch(st
->text_lines
[line
][i
++]){
1352 for(key
= 0, n
= st
->text_lines
[line
][i
++]; n
; n
--)
1353 key
= (key
* 10) + (st
->text_lines
[line
][i
++] - '0');
1359 i
+= RGBLEN
; /* advance past color setting */
1370 while(((++column
) & 0x07) != 0) /* add tab's spaces */
1376 column
+= width_at_this_position((unsigned char*) &st
->text_lines
[line
][i
-1],
1377 st
->line_lengths
[line
] - (i
-1));
1381 while(column
< goal
);
1388 * url_launch - Sniff the given url, see if we can do anything with
1389 * it. If not, hand off to user-defined app.
1393 url_launch(HANDLE_S
*handle
)
1397 #define URL_MAX_LAUNCH (2 * MAILTMPLEN)
1399 if(handle
->h
.url
.tool
){
1400 char *toolp
, *cmdp
, *p
, cmd
[URL_MAX_LAUNCH
+ 4];
1401 int mode
, copied
= 0;
1404 toolp
= handle
->h
.url
.tool
;
1406 /* This code used to quote a URL to prevent arbitrary command execution
1407 * through a URL. The plan was to quote the URL with single quotes,
1408 * and this used to work. BUT some shells do not care about quoting
1409 * and interpret some characters regardless of single quotes. The
1410 * simplest solution is to escape those characters, but then some
1411 * shells will see the escape characters and some others will not.
1412 * It's a mess. There are several ways to go around this mess,
1413 * including adding configuration options (one more!?), or forget
1414 * about shells. What we do is to forget about shells, and execute
1415 * the code as a PIPE_NOSHELL.
1419 while(cmdp
-cmd
< URL_MAX_LAUNCH
)
1420 if((!*toolp
&& !copied
)
1421 || (*toolp
== '_' && !strncmp(toolp
+ 1, "URL_", 4))){
1423 /* implicit _URL_ at end */
1427 if(cmdp
[-1] == '\'') /* unquote old '_URL_' */
1431 for(p
= handle
->h
.url
.path
;
1432 p
&& *p
&& cmdp
-cmd
< URL_MAX_LAUNCH
; p
++)
1438 toolp
+= 5; /* length of "_URL_" */
1443 if(!(*cmdp
++ = *toolp
++))
1446 if(cmdp
-cmd
>= URL_MAX_LAUNCH
)
1447 return(url_launch_too_long(rv
));
1449 mode
= PIPE_RESET
| PIPE_USER
| PIPE_RUNNOW
| PIPE_NOSHELL
;
1450 if((syspipe
= open_system_pipe(cmd
, NULL
, NULL
, mode
, 0, pipe_callback
, pipe_report_error
)) != NULL
){
1451 close_system_pipe(&syspipe
, NULL
, pipe_callback
);
1452 q_status_message(SM_ORDER
, 0, 4, _("VIEWER command completed"));
1455 q_status_message1(SM_ORDER
, 3, 4,
1456 /* TRANSLATORS: Cannot start command : <command name> */
1457 _("Cannot start command : %s"), cmd
);
1459 else if((f
= url_local_handler(handle
->h
.url
.path
)) != NULL
){
1460 if((*f
)(handle
->h
.url
.path
) > 1)
1464 q_status_message1(SM_ORDER
, 2, 2,
1465 _("\"URL-Viewer\" not defined: Can't open %s"),
1466 handle
->h
.url
.path
);
1473 url_launch_too_long(int return_value
)
1475 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
1476 "Can't spawn. Command too long.");
1477 return(return_value
);
1482 url_external_handler(HANDLE_S
*handle
, int specific
)
1484 char **l
, *test
, *cmd
, *p
, *q
, *ep
;
1485 int i
, specific_match
;
1487 for(l
= ps_global
->VAR_BROWSER
; l
&& *l
; l
++){
1488 get_pair(*l
, &test
, &cmd
, 0, 1);
1489 dprint((5, "TEST: \"%s\" CMD: \"%s\"\n",
1490 test
? test
: "<NULL>", cmd
? cmd
: "<NULL>"));
1491 removing_quotes(cmd
);
1492 if(valid_filter_command(&cmd
)){
1495 if((p
= test
) != NULL
){
1498 if(!strncmp(p
+1, "TEST(", 5)
1499 && (ep
= strstr(p
+6, ")_"))){
1502 if(exec_mailcap_test_cmd(p
+6) == 0){
1506 dprint((5,"failed handler TEST\n"));
1507 fs_give((void **) &cmd
);
1510 else if(!strncmp(p
+1, "SCHEME(", 7)
1511 && (ep
= strstr(p
+8, ")_"))){
1516 if((q
= strchr(p
, ',')) != NULL
)
1520 while(!((i
= strlen(p
))
1522 && handle
->h
.url
.path
[i
- 1] == ':')
1524 && handle
->h
.url
.path
[i
] == ':'))
1525 && !struncmp(handle
->h
.url
.path
, p
, i
))
1533 dprint((5,"failed handler SCHEME\n"));
1534 fs_give((void **) &cmd
);
1538 dprint((5, "UNKNOWN underscore test\n"));
1539 fs_give((void **) &cmd
);
1542 else if(isspace((unsigned char) *p
)){
1546 dprint((1,"bogus handler test: \"%s\"",
1547 test
? test
: "?"));
1548 fs_give((void **) &cmd
);
1551 fs_give((void **) &test
);
1554 if(cmd
&& (!specific
|| specific_match
))
1559 fs_give((void **) &test
);
1562 fs_give((void **) &cmd
);
1568 cmd
= url_os_specified_browser(handle
->h
.url
.path
);
1570 * Last chance, anything handling "text/html" in mailcap...
1572 if(!cmd
&& mailcap_can_display(TYPETEXT
, "html", NULL
, 0)){
1575 mc_cmd
= mailcap_build_command(TYPETEXT
, "html",
1576 NULL
, "_URL_", NULL
, 0);
1578 * right now URL viewing won't return anything requiring
1582 cmd
= mc_cmd
->command
;
1583 fs_give((void **)&mc_cmd
);
1593 url_local_handler(char *s
)
1596 static struct url_t
{
1601 {"mailto:", 7, url_local_mailto
}, /* see url_tool_t def's */
1602 {"imap://", 7, url_local_imap
}, /* for explanations */
1603 {"nntp://", 7, url_local_nntp
},
1604 {"file://", 7, url_local_file
},
1606 {"ldap://", 7, url_local_ldap
},
1608 {"news:", 5, url_local_news
},
1609 {"x-alpine-ical:", 14, ical_send_reply
},
1610 {"x-alpine-phone-home:", 20, url_local_phone_home
},
1611 {"x-alpine-gripe:", 15, gripe_gripe_to
},
1612 {"x-alpine-help:", 14, url_local_helper
},
1613 {"x-pine-help:", 12, url_local_helper
},
1614 {"x-alpine-config:", 16, url_local_config
},
1615 {"x-alpine-cert:", 14, url_local_certdetails
},
1616 {"#", 1, url_local_fragment
},
1620 for(i
= 0; handlers
[i
].url
; i
++)
1621 if(!struncmp(s
, handlers
[i
].url
, handlers
[i
].len
))
1622 return(handlers
[i
].f
);
1630 * mailto URL digester ala draft-hoffman-mailto-url-02.txt
1633 url_local_mailto(char *url
)
1635 return(url_local_mailto_and_atts(url
, NULL
));
1639 url_local_mailto_and_atts(char *url
, PATMT
*attachlist
)
1644 char *sig
, *urlp
, *p
, *hname
, *hvalue
;
1647 int was_a_body
= 0, impl
, template_len
= 0;
1650 REDRAFT_POS_S
*redraft_pos
= NULL
;
1651 ACTION_S
*role
= NULL
;
1653 outgoing
= mail_newenvelope();
1654 outgoing
->message_id
= generate_message_id();
1655 body
= mail_newbody();
1656 body
->type
= TYPETEXT
;
1657 if((body
->contents
.text
.data
= (void *) so_get(PicoText
,NULL
,EDIT_ACCESS
)) != NULL
){
1661 * mailtoURL = "mailto:" [ to ] [ headers ]
1663 * headers = "?" header *( "&" header )
1664 * header = hname "=" hvalue
1668 * NOTE2: "from" and "bcc" are intentionally excluded from
1669 * the list of understood "header" fields
1671 if((p
= strchr(urlp
= cpystr(url
+7), '?')) != NULL
)
1672 *p
++ = '\0'; /* headers? Tie off mailbox */
1674 /* grok mailbox as first "to", then roll thru specific headers */
1676 rfc822_parse_adrlist(&outgoing
->to
,
1678 ps_global
->maildomain
);
1681 /* Find next "header" */
1682 if((p
= strchr(hname
= p
, '&')) != NULL
)
1683 *p
++ = '\0'; /* tie off "header" */
1685 if((hvalue
= strchr(hname
, '=')) != NULL
)
1686 *hvalue
++ = '\0'; /* tie off hname */
1688 if(!hvalue
|| !strucmp(hname
, "subject")){
1689 char *sub
= rfc1738_str(hvalue
? hvalue
: hname
);
1691 if(outgoing
->subject
){
1692 int len
= strlen(outgoing
->subject
);
1694 fs_resize((void **) &outgoing
->subject
,
1695 (len
+ strlen(sub
) + 2) * sizeof(char));
1696 snprintf(outgoing
->subject
+ len
, strlen(sub
)+2, " %s", sub
);
1697 outgoing
->subject
[len
+ strlen(sub
) + 2 - 1] = '\0';
1700 outgoing
->subject
= cpystr(sub
);
1702 else if(!strucmp(hname
, "to")){
1703 url_mailto_addr(&outgoing
->to
, hvalue
);
1705 else if(!strucmp(hname
, "cc")){
1706 url_mailto_addr(&outgoing
->cc
, hvalue
);
1708 else if(!strucmp(hname
, "bcc")){
1709 q_status_message(SM_ORDER
, 3, 4,
1710 "\"Bcc\" header in mailto url ignored");
1712 else if(!strucmp(hname
, "from")){
1713 q_status_message(SM_ORDER
, 3, 4,
1714 "\"From\" header in mailto url ignored");
1716 else if(!strucmp(hname
, "body")){
1717 char *sub
= rfc1738_str(hvalue
? hvalue
: "");
1719 so_puts((STORE_S
*)body
->contents
.text
.data
, sub
);
1720 so_puts((STORE_S
*)body
->contents
.text
.data
, NEWLINE
);
1725 fs_give((void **) &urlp
);
1727 rflags
= ROLE_COMPOSE
;
1728 if(nonempty_patterns(rflags
, &dummy
)){
1729 role
= set_role_from_msg(ps_global
, rflags
, -1L, NULL
);
1730 if(confirm_role(rflags
, &role
))
1731 role
= combine_inherited_role(role
);
1734 cmd_cancelled("Composition");
1740 q_status_message1(SM_ORDER
, 3, 4, "Composing using role \"%s\"",
1743 if(!was_a_body
&& role
&& role
->template){
1746 impl
= 1; /* leave cursor in header if not explicit */
1747 filtered
= detoken(role
, NULL
, 0, 0, 0, &redraft_pos
, &impl
);
1750 so_puts((STORE_S
*)body
->contents
.text
.data
, filtered
);
1752 template_len
= strlen(filtered
);
1755 fs_give((void **)&filtered
);
1761 if(!was_a_body
&& (sig
= detoken(role
, NULL
, 2, 0, 1, &redraft_pos
,
1764 redraft_pos
->offset
+= template_len
;
1767 so_puts((STORE_S
*)body
->contents
.text
.data
, sig
);
1769 fs_give((void **)&sig
);
1772 memset((void *)&fake_reply
, 0, sizeof(fake_reply
));
1773 fake_reply
.pseudo
= 1;
1774 fake_reply
.data
.pico_flags
= (outgoing
->subject
) ? P_BODY
: P_HEADEND
;
1777 if(!(role
&& role
->fcc
))
1778 fcc
= get_fcc_based_on_to(outgoing
->to
);
1781 create_message_body(&body
, attachlist
, 0);
1783 pine_send(outgoing
, &body
, "\"MAILTO\" COMPOSE",
1784 role
, fcc
, &fake_reply
, redraft_pos
, NULL
, NULL
, PS_STICKY_TO
);
1786 ps_global
->mangled_screen
= 1;
1789 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1790 _("Can't create space for composer"));
1794 mail_free_envelope(&outgoing
);
1797 pine_free_body(&body
);
1800 fs_give((void **)&fcc
);
1802 free_redraft_pos(&redraft_pos
);
1810 url_mailto_addr(struct mail_address
**a
, char *a_raw
)
1812 char *p
= cpystr(rfc1738_str(a_raw
));
1814 while(*a
) /* append to address list */
1817 rfc822_parse_adrlist(a
, p
, ps_global
->maildomain
);
1818 fs_give((void **) &p
);
1823 * imap URL digester ala RFC 2192
1826 url_local_imap(char *url
)
1828 char *folder
, *mailbox
= NULL
, *errstr
= NULL
, *search
= NULL
,
1829 newfolder
[MAILTMPLEN
];
1832 imapuid_t uid
= 0L, uid_val
= 0L;
1833 CONTEXT_S
*fake_context
;
1836 rv
= url_imap_folder(url
, &folder
, &uid
, &uid_val
, &search
, 0);
1837 switch(rv
& URL_IMAP_MASK
){
1838 case URL_IMAP_IMAILBOXLIST
:
1839 /* BUG: deal with lsub tag */
1840 if((fake_context
= new_context(folder
, NULL
)) != NULL
){
1841 newfolder
[0] = '\0';
1842 if(display_folder_list(&fake_context
, newfolder
,
1843 0, folders_for_goto
))
1844 if(strlen(newfolder
) + 1 < MAILTMPLEN
)
1845 mailbox
= newfolder
;
1848 errstr
= "Problem building URL's folder list";
1850 fs_give((void **) &folder
);
1851 free_context(&fake_context
);
1857 q_status_message(SM_ORDER
|SM_DING
, 3, 3, errstr
);
1859 cmd_cancelled("URL Launch");
1863 case URL_IMAP_IMESSAGEPART
:
1864 case URL_IMAP_IMESSAGELIST
:
1865 if(ps_global
&& ps_global
->ttyo
){
1866 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
1867 ps_global
->mangled_footer
= 1;
1870 rv
= do_broach_folder(folder
, NULL
, NULL
, 0L);
1871 fs_give((void **) &folder
);
1873 case -1 : /* utter failure */
1874 ps_global
->next_screen
= main_menu_screen
;
1877 case 0 : /* same folder reopened */
1878 ps_global
->next_screen
= mail_index_screen
;
1881 case 1 : /* requested folder open */
1882 ps_global
->next_screen
= mail_index_screen
;
1884 if(uid_val
&& uid_val
!= ps_global
->mail_stream
->uid_validity
){
1886 q_status_message(SM_ORDER
|SM_DING
, 3, 3,
1887 "Warning! Referenced folder changed since URL recorded");
1892 * Make specified message the currently selected..
1894 for(i
= 1L; i
<= mn_get_total(ps_global
->msgmap
); i
++)
1895 if(mail_uid(ps_global
->mail_stream
, i
) == uid
){
1896 ps_global
->next_screen
= mail_view_screen
;
1897 mn_set_cur(ps_global
->msgmap
, i
);
1901 if(i
> mn_get_total(ps_global
->msgmap
))
1902 q_status_message(SM_ORDER
, 2, 3,
1903 "Couldn't find specified article number");
1907 * Select the specified messages
1908 * and present a zoom'd index...
1910 /* BUG: not dealing with CHARSET yet */
1912 /* ANOTHER BUG: mail_criteria is a compatibility routine for IMAP2BIS
1913 * so it doesn't know about IMAP4 search criteria, like SENTSINCE.
1914 * It also doesn't handle literals. */
1916 pine_mail_search_full(ps_global
->mail_stream
, NULL
,
1917 mail_criteria(search
),
1918 SE_NOPREFETCH
| SE_FREE
);
1920 for(i
= 1L; i
<= mn_get_total(ps_global
->msgmap
); i
++)
1921 if(ps_global
->mail_stream
1922 && i
<= ps_global
->mail_stream
->nmsgs
1923 && (mc
= mail_elt(ps_global
->mail_stream
, i
))
1925 set_lflag(ps_global
->mail_stream
,
1926 ps_global
->msgmap
, i
, MN_SLCT
, 1);
1928 if((i
= any_lflagged(ps_global
->msgmap
, MN_SLCT
)) != 0){
1930 q_status_message2(SM_ORDER
, 0, 3,
1931 "%s message%s selected",
1932 long2string(i
), plural(i
));
1933 /* Zoom the index! */
1934 zoom_index(ps_global
, ps_global
->mail_stream
,
1935 ps_global
->msgmap
, MN_SLCT
);
1943 case URL_IMAP_ERROR
:
1952 url_local_nntp(char *url
)
1954 char folder
[2*MAILTMPLEN
], *group
;
1956 long i
, article_num
;
1958 /* no hostport, no url, end of story */
1959 if((group
= strchr(url
+ 7, '/')) != 0){
1961 for(group_len
= 0; group
[group_len
] && group
[group_len
] != '/';
1963 if(!rfc1738_group(&group
[group_len
]))
1964 /* TRANSLATORS: these are errors in news group URLs */
1965 return(url_bogus(url
, _("Invalid newsgroup specified")));
1968 snprintf(folder
, sizeof(folder
), "{%.*s/nntp}#news.%.*s",
1969 (int) MIN((group
- 1) - (url
+ 7), MAILTMPLEN
-20), url
+ 7,
1970 (int) MIN(group_len
, MAILTMPLEN
-20), group
);
1971 folder
[sizeof(folder
)-1] = '\0';
1974 return(url_bogus(url
, _("No newsgroup specified")));
1977 return(url_bogus(url
, _("No server specified")));
1979 if(ps_global
&& ps_global
->ttyo
){
1980 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
1981 ps_global
->mangled_footer
= 1;
1984 switch(do_broach_folder(rfc1738_str(folder
), NULL
, NULL
, 0L)){
1985 case -1 : /* utter failure */
1986 ps_global
->next_screen
= main_menu_screen
;
1989 case 0 : /* same folder reopened */
1990 ps_global
->next_screen
= mail_index_screen
;
1993 case 1 : /* requested folder open */
1994 ps_global
->next_screen
= mail_index_screen
;
1996 /* grok article number --> c-client UID */
1997 if(group
[group_len
++] == '/'
1998 && (article_num
= atol(&group
[group_len
]))){
2000 * Make the requested article our current message
2002 for(i
= 1; i
<= mn_get_nmsgs(ps_global
->msgmap
); i
++)
2003 if(mail_uid(ps_global
->mail_stream
, i
) == article_num
){
2004 ps_global
->next_screen
= mail_view_screen
;
2005 if((i
= mn_raw2m(ps_global
->msgmap
, i
)) != 0)
2006 mn_set_cur(ps_global
->msgmap
, i
);
2010 if(i
== 0 || i
> mn_get_total(ps_global
->msgmap
))
2011 q_status_message(SM_ORDER
, 2, 3,
2012 _("Couldn't find specified article number"));
2018 ps_global
->redrawer
= (void(*)(void))NULL
;
2024 url_local_news(char *url
)
2026 char folder
[MAILTMPLEN
], *p
;
2027 CONTEXT_S
*cntxt
= NULL
;
2030 * NOTE: NO SUPPORT for '*' or message-id
2033 if(*(url
+5) == '/' && *(url
+6) == '/')
2034 return(url_local_nntp(url
)); /* really meant "nntp://"? */
2036 if(ps_global
->VAR_NNTP_SERVER
&& ps_global
->VAR_NNTP_SERVER
[0])
2038 * BUG: Only the first NNTP server is tried.
2040 snprintf(folder
, sizeof(folder
), "{%s/nntp}#news.", ps_global
->VAR_NNTP_SERVER
[0]);
2042 strncpy(folder
, "#news.", sizeof(folder
));
2044 folder
[sizeof(folder
)-1] = '\0';
2046 for(p
= strncpy(folder
+ strlen(folder
), url
+ 5, sizeof(folder
)-strlen(folder
)-1);
2047 *p
&& rfc1738_group(p
);
2052 return(url_bogus(url
, "Invalid newsgroup specified"));
2054 else{ /* fish first group from newsrc */
2060 /* Find first news context */
2061 for(cntxt
= ps_global
->context_list
;
2062 cntxt
&& !(cntxt
->use
& CNTXT_NEWS
);
2063 cntxt
= cntxt
->next
)
2067 if((alphaorder
= F_OFF(F_READ_IN_NEWSRC_ORDER
, ps_global
)) != 0)
2068 (void) F_SET(F_READ_IN_NEWSRC_ORDER
, ps_global
, 1);
2070 build_folder_list(NULL
, cntxt
, NULL
, NULL
, BFL_LSUB
);
2071 if((f
= folder_entry(0, FOLDERS(cntxt
))) != NULL
){
2072 strncpy(folder
, f
->name
, sizeof(folder
));
2073 folder
[sizeof(folder
)-1] = '\0';
2076 free_folder_list(cntxt
);
2079 (void) F_SET(F_READ_IN_NEWSRC_ORDER
, ps_global
, 0);
2082 if(folder
[0] == '\0'){
2083 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2084 "No default newsgroup");
2089 if(ps_global
&& ps_global
->ttyo
){
2090 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
2091 ps_global
->mangled_footer
= 1;
2094 if(do_broach_folder(rfc1738_str(folder
), cntxt
, NULL
, 0L) < 0)
2095 ps_global
->next_screen
= main_menu_screen
;
2097 ps_global
->next_screen
= mail_index_screen
;
2099 ps_global
->redrawer
= (void(*)(void))NULL
;
2106 url_local_file(char *file_url
)
2109 /* TRANSLATORS: this is a warning that the file URL can cause programs to run which may
2110 be a security problem. We are asking the user to confirm that they want to do this. */
2111 _("\"file\" URL may cause programs to be run on your system. Run anyway"),
2112 'n', 0, NO_HELP
, WT_NORM
) == 'y'){
2116 handle
.h
.url
.path
= file_url
;
2117 if((handle
.h
.url
.tool
= url_external_handler(&handle
, 1))
2118 || (handle
.h
.url
.tool
= url_external_handler(&handle
, 0))){
2119 url_launch(&handle
);
2123 q_status_message(SM_ORDER
, 0, 4,
2124 _("No viewer for \"file\" URL. VIEWER command cancelled"));
2128 q_status_message(SM_ORDER
, 0, 4, _("VIEWER command cancelled"));
2134 url_local_fragment(char *fragment
)
2136 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
2140 * find a handle with the fragment's name
2142 for(hp
= st
->parms
->text
.handles
; hp
; hp
= hp
->next
)
2143 if(hp
->type
== URL
&& hp
->h
.url
.name
2144 && !strcmp(hp
->h
.url
.name
, fragment
+ 1))
2148 for(hp
= st
->parms
->text
.handles
->prev
; hp
; hp
= hp
->prev
)
2149 if(hp
->type
== URL
&& hp
->h
.url
.name
2150 && !strcmp(hp
->h
.url
.name
, fragment
+ 1))
2154 * set the top line of the display to contain this line
2157 st
->top_text_line
= hp
->loc
->where
.row
;
2158 ps_global
->mangled_body
= 1;
2161 q_status_message1(SM_ORDER
| SM_DING
, 0, 3,
2162 "Can't find fragment: %s", fragment
);
2168 ical_send_reply(char *url
)
2170 // ical_compose_reply(url + strlen("x-alpine-ical:"));
2176 url_local_phone_home(char *URL
)
2178 phone_home(URL
+ strlen("x-alpine-phone-home:"));
2184 * Format editorial comment referencing screen offering
2185 * List-* header supplied commands
2188 rfc2369_editorial(long int msgno
, HANDLE_S
**handlesp
, int flags
, int width
, gf_io_t pc
)
2190 char *p
, *hdrp
, *hdrs
[MLCMD_COUNT
+ 1],
2191 color
[64], buf
[2048];
2192 int i
, n
, rv
= TRUE
;
2195 if((flags
& FM_DISPLAY
)
2196 && (hdrp
= pine_fetchheader_lines(ps_global
->mail_stream
, msgno
,
2197 NULL
, rfc2369_hdrs(hdrs
)))){
2199 snprintf(buf
, sizeof(buf
), "Note: This message contains ");
2200 buf
[sizeof(buf
)-1] = '\0';
2201 p
= buf
+ strlen(buf
);
2204 h
= new_handle(handlesp
);
2206 h
->h
.func
.f
= rfc2369_display
;
2207 h
->h
.func
.args
.stream
= ps_global
->mail_stream
;
2208 h
->h
.func
.args
.msgmap
= ps_global
->msgmap
;
2209 h
->h
.func
.args
.msgno
= msgno
;
2211 if(!(flags
& FM_NOCOLOR
)
2212 && handle_start_color(color
, sizeof(color
), &n
, 0)){
2213 if((p
-buf
)+n
< sizeof(buf
))
2214 for(i
= 0; i
< n
; i
++)
2217 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
2222 if((p
-buf
)+2 < sizeof(buf
)){
2227 snprintf(p
+ 1, sizeof(buf
)-(p
+1-buf
), "%d", h
->key
);
2228 buf
[sizeof(buf
)-1] = '\0';
2233 sstrncpy(&p
, "email list management information", sizeof(buf
)-(p
-buf
));
2234 buf
[sizeof(buf
)-1] = '\0';
2237 /* in case it was the current selection */
2238 if((p
-buf
)+2 < sizeof(buf
)){
2243 if(handle_end_color(color
, sizeof(color
), &n
)){
2244 if((p
-buf
)+n
< sizeof(buf
))
2245 for(i
= 0; i
< n
; i
++)
2249 if((p
-buf
)+2 < sizeof(buf
)){
2255 if(p
-buf
< sizeof(buf
))
2259 buf
[sizeof(buf
)-1] = '\0';
2261 rv
= (gf_puts(NEWLINE
, pc
)
2262 && format_editorial(buf
, width
, flags
, handlesp
, pc
) == NULL
2263 && gf_puts(NEWLINE
, pc
));
2266 fs_give((void **) &hdrp
);
2274 /*----------------------------------------------------------------------
2275 routine for displaying text on the screen.
2277 Args: sparms -- structure of args controlling what happens outside
2278 just the business of managing text scrolling
2280 This displays in three different kinds of text. One is an array of
2281 lines passed in in text_array. The other is a simple long string of
2282 characters passed in in text.
2284 The style determines what some of the error messages will be, and
2285 what commands are available as different things are appropriate for
2286 help text than for message text etc.
2291 scrolltool(SCROLL_S
*sparms
)
2293 register long cur_top_line
, num_display_lines
;
2295 int result
, done
, cmd
, found_on
, found_on_index
,
2296 first_view
, force
, scroll_lines
, km_size
,
2297 cursor_row
, cursor_col
, km_popped
;
2300 struct key_menu
*km
;
2301 HANDLE_S
*next_handle
;
2306 num_display_lines
= SCROLL_LINES(ps_global
);
2308 ps_global
->mangled_header
= 1;
2309 ps_global
->mangled_footer
= 1;
2310 ps_global
->mangled_body
= !sparms
->body_valid
;
2313 what
= sparms
->keys
.what
; /* which key menu to display */
2317 found_on_index
= -1;
2319 if(sparms
->quell_first_view
)
2323 ch
= 'x'; /* for first time through */
2324 whereis_pos
.row
= 0;
2325 whereis_pos
.col
= 0;
2326 next_handle
= sparms
->text
.handles
;
2328 set_scroll_text(sparms
, cur_top_line
, scroll_state(SS_NEW
));
2329 format_scroll_text();
2331 if((km
= sparms
->keys
.menu
) != NULL
){
2332 memcpy(bitmap
, sparms
->keys
.bitmap
, sizeof(bitmap_t
));
2336 km
= &simple_text_keymenu
;
2338 sparms
->mouse
.popup
= simple_text_popup
;
2342 if(!sparms
->bar
.title
)
2343 sparms
->bar
.title
= "Text";
2345 if(sparms
->bar
.style
== TitleBarNone
){
2346 if(THREADING() && sp_viewing_a_thread(ps_global
->mail_stream
))
2347 sparms
->bar
.style
= ThrdMsgPercent
;
2349 sparms
->bar
.style
= MsgTextPercent
;
2352 switch(sparms
->start
.on
){
2354 cur_top_line
= MAX(0, scroll_text_lines() - (num_display_lines
-2));
2355 if(F_ON(F_SHOW_CURSOR
, ps_global
)){
2356 whereis_pos
.row
= scroll_text_lines() - cur_top_line
;
2357 found_on
= scroll_text_lines() - 1;
2363 if(sparms
->start
.loc
.frag
){
2364 (void) url_local_fragment(sparms
->start
.loc
.frag
);
2366 cur_top_line
= scroll_state(SS_CUR
)->top_text_line
;
2368 if(F_ON(F_SHOW_CURSOR
, ps_global
)){
2369 whereis_pos
.row
= scroll_text_lines() - cur_top_line
;
2370 found_on
= scroll_text_lines() - 1;
2377 if(sparms
->start
.loc
.offset
){
2378 for(cur_top_line
= 0L;
2379 cur_top_line
+ 1 < scroll_text_lines()
2380 && (sparms
->start
.loc
.offset
2381 -= scroll_handle_column(cur_top_line
,
2390 if(scroll_handle_obscured(sparms
->text
.handles
))
2391 cur_top_line
= scroll_handle_reframe(-1, TRUE
);
2395 default : /* no-op */
2399 /* prepare for calls below to tell us where to go */
2400 ps_global
->next_screen
= SCREEN_FUN_NULL
;
2402 cancel_busy_cue(-1);
2405 ps_global
->user_says_cancel
= 0;
2409 clearfooter(ps_global
);
2410 ps_global
->mangled_body
= 1;
2414 if(ps_global
->mangled_screen
) {
2415 ps_global
->mangled_header
= 1;
2416 ps_global
->mangled_footer
= 1;
2417 ps_global
->mangled_body
= 1;
2420 if(!sparms
->quell_newmail
&& streams_died())
2421 ps_global
->mangled_header
= 1;
2423 dprint((9, "@@@@ current:%ld\n",
2424 mn_get_cur(ps_global
->msgmap
)));
2427 /*==================== All Screen painting ====================*/
2428 /*-------------- The title bar ---------------*/
2429 update_scroll_titlebar(cur_top_line
, ps_global
->mangled_header
);
2431 if(ps_global
->mangled_screen
){
2432 /* this is the only line not cleared by header, body or footer
2436 ps_global
->mangled_screen
= 0;
2439 /*---- Scroll or update the body of the text on the screen -------*/
2440 cur_top_line
= scroll_scroll_text(cur_top_line
, next_handle
,
2441 ps_global
->mangled_body
);
2442 ps_global
->redrawer
= redraw_scroll_text
;
2443 ps_global
->mangled_body
= 0;
2445 /*--- Check to see if keymenu might change based on next_handle --*/
2446 if(sparms
->text
.handles
!= next_handle
)
2447 ps_global
->mangled_footer
= 1;
2450 sparms
->text
.handles
= next_handle
;
2452 /*------------- The key menu footer --------------------*/
2453 if(ps_global
->mangled_footer
|| sparms
->keys
.each_cmd
){
2455 FOOTER_ROWS(ps_global
) = 3;
2456 clearfooter(ps_global
);
2459 if(F_ON(F_ARROW_NAV
, ps_global
)){
2460 menu_clear_binding(km
, KEY_LEFT
);
2461 if((cmd
= menu_clear_binding(km
, '<')) != MC_UNKNOWN
){
2462 menu_add_binding(km
, '<', cmd
);
2463 menu_add_binding(km
, KEY_LEFT
, cmd
);
2467 if(F_ON(F_ARROW_NAV
, ps_global
)){
2468 menu_clear_binding(km
, KEY_RIGHT
);
2469 if((cmd
= menu_clear_binding(km
, '>')) != MC_UNKNOWN
){
2470 menu_add_binding(km
, '>', cmd
);
2471 menu_add_binding(km
, KEY_RIGHT
, cmd
);
2475 if(sparms
->keys
.each_cmd
){
2476 (*sparms
->keys
.each_cmd
)(sparms
,
2477 scroll_handle_obscured(sparms
->text
.handles
));
2478 memcpy(bitmap
, sparms
->keys
.bitmap
, sizeof(bitmap_t
));
2481 if(menu_binding_index(km
, MC_JUMP
) >= 0){
2482 for(cmd
= 0; cmd
< 10; cmd
++)
2483 if(F_ON(F_ENABLE_JUMP
, ps_global
))
2484 (void) menu_add_binding(km
, '0' + cmd
, MC_JUMP
);
2486 (void) menu_clear_binding(km
, '0' + cmd
);
2489 draw_keymenu(km
, bitmap
, ps_global
->ttyo
->screen_cols
,
2490 1-FOOTER_ROWS(ps_global
), 0, what
);
2492 ps_global
->mangled_footer
= 0;
2494 FOOTER_ROWS(ps_global
) = 1;
2495 mark_keymenu_dirty();
2499 if((ps_global
->first_time_user
|| ps_global
->show_new_version
)
2500 && first_view
&& sparms
->text
.handles
2501 && (sparms
->text
.handles
->next
|| sparms
->text
.handles
->prev
)
2502 && !sparms
->quell_help
)
2503 q_status_message(SM_ORDER
, 0, 3, HANDLE_INIT_MSG
);
2505 /*============ Check for New Mail and CheckPoint ============*/
2506 if(!sparms
->quell_newmail
&&
2507 new_mail(force
, NM_TIMING(ch
), NM_STATUS_MSG
) >= 0){
2508 update_scroll_titlebar(cur_top_line
, 1);
2509 if(ps_global
->mangled_footer
)
2510 draw_keymenu(km
, bitmap
, ps_global
->ttyo
->screen_cols
,
2511 1-FOOTER_ROWS(ps_global
), 0, what
);
2513 ps_global
->mangled_footer
= 0;
2517 * If an expunge of the current message happened during the
2518 * new mail check we want to bail out of here. See mm_expunged.
2520 if(ps_global
->next_screen
!= SCREEN_FUN_NULL
){
2525 if(ps_global
->noticed_change_in_unseen
){
2526 ps_global
->noticed_change_in_unseen
= 0; /* redraw only once */
2527 cmd
= MC_RESIZE
; /* causes cursor to be saved in folder_lister */
2532 if(first_view
&& num_display_lines
>= scroll_text_lines())
2533 q_status_message1(SM_INFO
, 0, 1, "ALL of %s", STYLE_NAME(sparms
));
2536 force
= 0; /* may not need to next time around */
2537 first_view
= 0; /* check_point a priority any more? */
2539 /*==================== Output the status message ==============*/
2540 if(!sparms
->no_stat_msg
){
2542 FOOTER_ROWS(ps_global
) = 3;
2543 mark_status_unknown();
2546 display_message(ch
);
2548 FOOTER_ROWS(ps_global
) = 1;
2549 mark_status_unknown();
2553 if(F_ON(F_SHOW_CURSOR
, ps_global
)){
2555 if(cur_top_line
!= scroll_state(SS_CUR
)->top_text_line
)
2556 whereis_pos
.row
= 0;
2559 if(whereis_pos
.row
> 0){
2560 cursor_row
= SCROLL_LINES_ABOVE(ps_global
)
2561 + whereis_pos
.row
- 1;
2562 cursor_col
= whereis_pos
.col
;
2565 POSLIST_S
*lp
= NULL
;
2567 if(sparms
->text
.handles
&&
2568 !scroll_handle_obscured(sparms
->text
.handles
)){
2569 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
2571 for(lp
= sparms
->text
.handles
->loc
; lp
; lp
= lp
->next
)
2572 if(lp
->where
.row
>= st
->top_text_line
2573 && lp
->where
.row
< st
->top_text_line
2574 + st
->screen
.length
){
2575 cursor_row
= lp
->where
.row
- cur_top_line
2576 + SCROLL_LINES_ABOVE(ps_global
);
2577 cursor_col
= lp
->where
.col
;
2584 /* first new line of text */
2585 cursor_row
= SCROLL_LINES_ABOVE(ps_global
) +
2586 ((cur_top_line
== 0) ? 0 : ps_global
->viewer_overlap
);
2592 cursor_row
= ps_global
->ttyo
->screen_rows
2593 - SCROLL_LINES_BELOW(ps_global
);
2596 MoveCursor(cursor_row
, cursor_col
);
2598 /*================ Get command and validate =====================*/
2601 if(sparms
->text
.handles
)
2604 mouse_in_content(KEY_MOUSE
, -1, -1, 0x5, 0);
2605 register_mfunc(mouse_in_content
, HEADER_ROWS(ps_global
), 0,
2606 ps_global
->ttyo
->screen_rows
2607 - (FOOTER_ROWS(ps_global
) + 1),
2608 ps_global
->ttyo
->screen_cols
);
2612 mswin_allowcopy(mswin_readscrollbuf
);
2613 mswin_setscrollcallback(pcpine_do_scroll
);
2615 if(sparms
->help
.text
!= NO_HELP
)
2616 mswin_sethelptextcallback(pcpine_help_scroll
);
2618 if(sparms
->text
.handles
2619 && sparms
->text
.handles
->type
!= Folder
)
2620 mswin_mousetrackcallback(pcpine_view_cursor
);
2622 if(ps_global
->prev_screen
== mail_view_screen
)
2623 mswin_setviewinwindcallback(view_in_new_window
);
2625 ch
= (sparms
->quell_newmail
|| read_command_prep()) ? read_command(&utf8str
) : NO_OP_COMMAND
;
2628 if(sparms
->text
.handles
)
2630 clear_mfunc(mouse_in_content
);
2633 mswin_allowcopy(NULL
);
2634 mswin_setscrollcallback(NULL
);
2635 mswin_sethelptextcallback(NULL
);
2636 mswin_mousetrackcallback(NULL
);
2637 mswin_setviewinwindcallback(NULL
);
2638 cur_top_line
= scroll_state(SS_CUR
)->top_text_line
;
2641 cmd
= menu_command(ch
, km
);
2653 clearfooter(ps_global
);
2658 /*============= Execute command =======================*/
2661 /* ------ Help -------*/
2663 if(FOOTER_ROWS(ps_global
) == 1 && km_popped
== 0){
2665 ps_global
->mangled_footer
= 1;
2669 whereis_pos
.row
= 0;
2670 if(sparms
->help
.text
== NO_HELP
){
2671 q_status_message(SM_ORDER
, 0, 5,
2672 _("No help text currently available"));
2676 km_size
= FOOTER_ROWS(ps_global
);
2678 helper(sparms
->help
.text
, sparms
->help
.title
, 0);
2680 if(ps_global
->next_screen
!= main_menu_screen
2681 && km_size
== FOOTER_ROWS(ps_global
)) {
2682 /* Have to reset because helper uses scroll_text */
2683 num_display_lines
= SCROLL_LINES(ps_global
);
2684 ps_global
->mangled_screen
= 1;
2692 /*---------- Roll keymenu ------*/
2694 if(F_OFF(F_USE_FK
, ps_global
))
2698 ps_global
->mangled_footer
= 1;
2702 /* -------- Scroll back one page -----------*/
2704 whereis_pos
.row
= 0;
2706 scroll_lines
= MIN(MAX(num_display_lines
-
2707 ps_global
->viewer_overlap
, 1), num_display_lines
);
2708 cur_top_line
-= scroll_lines
;
2709 if(cur_top_line
<= 0){
2711 q_status_message1(SM_INFO
, 0, 1, "START of %s",
2712 STYLE_NAME(sparms
));
2716 /* hilite last available handle */
2718 if(sparms
->text
.handles
){
2719 HANDLE_S
*h
= sparms
->text
.handles
;
2721 while((h
= scroll_handle_prev_sel(h
))
2722 && !scroll_handle_obscured(h
))
2727 q_status_message1(SM_ORDER
, 0, 1, _("Already at start of %s"),
2728 STYLE_NAME(sparms
));
2736 /*---- Scroll down one page -------*/
2738 if(cur_top_line
+ num_display_lines
< scroll_text_lines()){
2739 whereis_pos
.row
= 0;
2740 scroll_lines
= MIN(MAX(num_display_lines
-
2741 ps_global
->viewer_overlap
, 1), num_display_lines
);
2742 cur_top_line
+= scroll_lines
;
2744 if(cur_top_line
+ num_display_lines
>= scroll_text_lines())
2745 q_status_message1(SM_INFO
, 0, 1, "END of %s",
2746 STYLE_NAME(sparms
));
2748 else if(!sparms
->end_scroll
2749 || !(done
= (*sparms
->end_scroll
)(sparms
))){
2750 q_status_message1(SM_ORDER
, 0, 1, _("Already at end of %s"),
2751 STYLE_NAME(sparms
));
2752 /* hilite last available handle */
2753 if(sparms
->text
.handles
){
2754 HANDLE_S
*h
= sparms
->text
.handles
;
2756 while((h
= scroll_handle_next_sel(h
)) != NULL
)
2763 /* scroll to the top page */
2767 q_status_message1(SM_INFO
, 0, 1, "START of %s",
2768 STYLE_NAME(sparms
));
2772 if(sparms
->text
.handles
){
2773 HANDLE_S
*h
= sparms
->text
.handles
;
2775 while((h
= scroll_handle_prev_sel(h
)) != NULL
)
2780 /* scroll to the bottom page */
2782 if(cur_top_line
+ num_display_lines
< scroll_text_lines()){
2783 cur_top_line
= scroll_text_lines() - MIN(5, num_display_lines
);
2784 q_status_message1(SM_INFO
, 0, 1, "END of %s",
2785 STYLE_NAME(sparms
));
2788 if(sparms
->text
.handles
){
2789 HANDLE_S
*h
= sparms
->text
.handles
;
2791 while((h
= scroll_handle_next_sel(h
)) != NULL
)
2796 /*------ Scroll down one line -----*/
2799 if(sparms
->text
.handles
){
2800 if(sparms
->vert_handle
){
2804 h2
= sparms
->text
.handles
;
2805 if(h2
->type
== Folder
&& h2
->prev
&& h2
->prev
->is_dual_do_open
)
2808 i
= h2
->loc
->where
.row
+ 1;
2809 j
= h2
->loc
->where
.col
;
2810 for(h
= NULL
, k
= h2
->key
;
2812 || (h
->loc
->where
.row
== h2
->loc
->where
.row
));
2814 /* must be different key */
2815 /* ... below current line */
2816 /* ... pref'bly to left */
2818 && h2
->loc
->where
.row
>= i
){
2819 if(h2
->loc
->where
.col
> j
){
2830 whereis_pos
.row
= 0;
2832 if((result
= scroll_handle_obscured(next_handle
)) != 0){
2835 if(scroll_handle_obscured(sparms
->text
.handles
)
2837 next_handle
= sparms
->text
.handles
;
2839 ps_global
->mangled_body
++;
2840 new_top
= scroll_handle_reframe(next_handle
->key
,0);
2842 cur_top_line
= new_top
;
2846 else if(!(ch
== ctrl('N') || F_ON(F_FORCE_ARROWS
, ps_global
)))
2847 next_handle
= scroll_handle_next(sparms
->text
.handles
);
2851 if(cur_top_line
+ num_display_lines
< scroll_text_lines()){
2852 whereis_pos
.row
= 0;
2854 if(cur_top_line
+ num_display_lines
>= scroll_text_lines())
2855 q_status_message1(SM_INFO
, 0, 1, "END of %s",
2856 STYLE_NAME(sparms
));
2859 q_status_message1(SM_ORDER
, 0, 1, _("Already at end of %s"),
2860 STYLE_NAME(sparms
));
2866 /* ------ Scroll back up one line -------*/
2869 if(sparms
->text
.handles
){
2870 if(sparms
->vert_handle
){
2874 h2
= sparms
->text
.handles
;
2875 if(h2
->type
== Folder
&& h2
->prev
&& h2
->prev
->is_dual_do_open
)
2878 i
= h2
->loc
->where
.row
- 1;
2879 j
= h2
->loc
->where
.col
;
2881 for(h
= NULL
, k
= h2
->key
;
2883 || (h
->loc
->where
.row
== h2
->loc
->where
.row
));
2885 /* must be new key, above current
2886 * line and pref'bly to right
2889 && h2
->loc
->where
.row
<= i
){
2890 if(h2
->loc
->where
.col
< j
){
2901 whereis_pos
.row
= 0;
2903 if((result
= scroll_handle_obscured(next_handle
)) != 0){
2906 if(scroll_handle_obscured(sparms
->text
.handles
)
2908 next_handle
= sparms
->text
.handles
;
2910 ps_global
->mangled_body
++;
2911 new_top
= scroll_handle_reframe(next_handle
->key
,0);
2913 cur_top_line
= new_top
;
2917 else if(!(ch
== ctrl('P') || F_ON(F_FORCE_ARROWS
, ps_global
)))
2918 next_handle
= scroll_handle_prev(sparms
->text
.handles
);
2922 whereis_pos
.row
= 0;
2925 if(cur_top_line
== 0)
2926 q_status_message1(SM_INFO
, 0, 1, "START of %s",
2927 STYLE_NAME(sparms
));
2930 q_status_message1(SM_ORDER
, 0, 1,
2931 _("Already at start of %s"),
2932 STYLE_NAME(sparms
));
2938 case MC_NEXT_HANDLE
:
2939 if((next_handle
= scroll_handle_next_sel(sparms
->text
.handles
)) != NULL
){
2940 whereis_pos
.row
= 0;
2941 if((result
= scroll_handle_obscured(next_handle
)) != 0){
2944 if(scroll_handle_obscured(sparms
->text
.handles
)
2946 next_handle
= sparms
->text
.handles
;
2948 ps_global
->mangled_body
++;
2949 new_top
= scroll_handle_reframe(next_handle
->key
, 0);
2951 cur_top_line
= new_top
;
2955 if(scroll_handle_obscured(sparms
->text
.handles
)){
2958 ps_global
->mangled_body
++;
2959 if((new_top
= scroll_handle_reframe(-1, 0)) >= 0){
2960 whereis_pos
.row
= 0;
2961 cur_top_line
= new_top
;
2965 q_status_message1(SM_ORDER
, 0, 1,
2966 _("Already on last item in %s"),
2967 STYLE_NAME(sparms
));
2973 case MC_PREV_HANDLE
:
2974 if((next_handle
= scroll_handle_prev_sel(sparms
->text
.handles
)) != NULL
){
2975 whereis_pos
.row
= 0;
2976 if((result
= scroll_handle_obscured(next_handle
)) != 0){
2979 if(scroll_handle_obscured(sparms
->text
.handles
)
2981 next_handle
= sparms
->text
.handles
;
2983 ps_global
->mangled_body
++;
2984 new_top
= scroll_handle_reframe(next_handle
->key
, 0);
2986 cur_top_line
= new_top
;
2990 if(scroll_handle_obscured(sparms
->text
.handles
)){
2993 ps_global
->mangled_body
++;
2994 if((new_top
= scroll_handle_reframe(-1, 0)) >= 0){
2995 whereis_pos
.row
= 0;
2996 cur_top_line
= new_top
;
3000 q_status_message1(SM_ORDER
, 0, 1,
3001 _("Already on first item in %s"),
3002 STYLE_NAME(sparms
));
3008 /*------ View the current handle ------*/
3009 case MC_VIEW_HANDLE
:
3010 switch(scroll_handle_obscured(sparms
->text
.handles
)){
3013 switch(scroll_handle_launch(sparms
->text
.handles
,
3014 sparms
->text
.handles
->force_display
)){
3016 cmd
= MC_EXIT
; /* propagate */
3021 cmd_cancelled(NULL
);
3028 cur_top_line
= scroll_state(SS_CUR
)->top_text_line
;
3032 q_status_message(SM_ORDER
, 0, 2, HANDLE_BELOW_ERR
);
3036 q_status_message(SM_ORDER
, 0, 2, HANDLE_ABOVE_ERR
);
3042 /*---------- Search text (where is) ----------*/
3044 ps_global
->mangled_footer
= 1;
3046 int start_index
, key
= 0;
3047 char *report
= NULL
;
3049 start_row
= cur_top_line
;
3052 if(F_ON(F_SHOW_CURSOR
,ps_global
)){
3054 || found_on
>= scroll_text_lines()
3055 || found_on
< cur_top_line
3056 || found_on
>= cur_top_line
+ num_display_lines
){
3057 start_row
= cur_top_line
;
3061 if(found_on_index
< 0){
3062 start_row
= found_on
+ 1;
3066 start_row
= found_on
;
3067 start_index
= found_on_index
+1;
3071 else if(sparms
->srch_handle
){
3074 if((h
= scroll_handle_next_sel(sparms
->text
.handles
)) != NULL
){
3076 * Translate the screen's column into the
3077 * line offset to start on...
3079 * This makes it so search_text never returns -3
3080 * so we don't know it is the same match. That's
3081 * because we start well after the current handle
3082 * (at the next handle) and that causes us to
3083 * think the one we just matched on is a different
3084 * one from before. Can't think of an easy way to
3085 * fix it, though, and it isn't a big deal. We still
3086 * match, we just don't say current line contains
3089 start_row
= h
->loc
->where
.row
;
3090 start_index
= scroll_handle_index(start_row
, h
->loc
->where
.col
);
3093 /* last handle, start over at top */
3094 start_row
= cur_top_line
;
3099 start_row
= (found_on
< 0
3100 || found_on
>= scroll_text_lines()
3101 || found_on
< cur_top_line
3102 || found_on
>= cur_top_line
+ num_display_lines
)
3103 ? cur_top_line
: found_on
+ 1,
3107 found_on
= search_text(-FOOTER_ROWS(ps_global
), start_row
,
3108 start_index
, &report
,
3109 &whereis_pos
, &found_on_index
);
3111 if(found_on
== -4){ /* search to top of text */
3112 whereis_pos
.row
= 0;
3113 whereis_pos
.col
= 0;
3115 if(sparms
->text
.handles
&& sparms
->srch_handle
)
3118 else if(found_on
== -5){ /* search to bottom of text */
3121 whereis_pos
.row
= MAX(scroll_text_lines() - 1, 0);
3122 whereis_pos
.col
= 0;
3123 found_on
= whereis_pos
.row
;
3124 if((h
= sparms
->text
.handles
) && sparms
->srch_handle
)
3127 while((h
= h
->next
) != NULL
);
3129 else if(found_on
== -3){
3130 whereis_pos
.row
= found_on
= start_row
;
3131 found_on_index
= start_index
- 1;
3132 q_status_message(SM_ORDER
, 1, 3,
3133 _("Current line contains the only match"));
3137 result
= found_on
< cur_top_line
;
3139 key
= (sparms
->text
.handles
)
3140 ? dot_on_handle(found_on
, whereis_pos
.col
) : 0;
3142 if(F_ON(F_FORCE_LOW_SPEED
,ps_global
)
3143 || ps_global
->low_speed
3144 || F_ON(F_SHOW_CURSOR
,ps_global
)
3146 if((found_on
>= cur_top_line
+ num_display_lines
||
3147 found_on
< cur_top_line
) &&
3148 num_display_lines
> ps_global
->viewer_overlap
){
3149 cur_top_line
= found_on
- ((found_on
> 0) ? 1 : 0);
3150 if(scroll_text_lines()-cur_top_line
< 5)
3151 cur_top_line
= MAX(0,
3152 scroll_text_lines()-MIN(5,num_display_lines
));
3154 /* else leave cur_top_line alone */
3157 cur_top_line
= found_on
- ((found_on
> 0) ? 1 : 0);
3158 if(scroll_text_lines()-cur_top_line
< 5)
3159 cur_top_line
= MAX(0,
3160 scroll_text_lines()-MIN(5,num_display_lines
));
3163 whereis_pos
.row
= whereis_pos
.row
- cur_top_line
+ 1;
3165 q_status_message(SM_ORDER
, 0, 3, report
);
3167 q_status_message2(SM_ORDER
, 0, 3,
3168 "%sFound on line %s on screen",
3169 result
? "Search wrapped to start. " : "",
3170 int2string(whereis_pos
.row
));
3173 if(sparms
->text
.handles
->key
< key
)
3174 for(next_handle
= sparms
->text
.handles
->next
;
3175 next_handle
->key
!= key
;
3176 next_handle
= next_handle
->next
)
3179 for(next_handle
= sparms
->text
.handles
;
3180 next_handle
->key
!= key
;
3181 next_handle
= next_handle
->prev
)
3185 else if(found_on
== -1)
3186 cmd_cancelled("Search");
3188 q_status_message(SM_ORDER
, 0, 3, _("Word not found"));
3194 /*-------------- jump command -------------*/
3195 /* NOTE: preempt the process_cmd() version because
3196 * we need to get at the number..
3199 jn
= jump_to(ps_global
->msgmap
, -FOOTER_ROWS(ps_global
), ch
,
3201 if(sparms
&& sparms
->jump_is_debug
)
3203 else if(jn
> 0 && jn
!= mn_get_cur(ps_global
->msgmap
)){
3205 if(mn_total_cur(ps_global
->msgmap
) > 1L)
3206 mn_reset_cur(ps_global
->msgmap
, jn
);
3208 mn_set_cur(ps_global
->msgmap
, jn
);
3213 ps_global
->mangled_footer
= 1;
3219 /*-------------- Mouse Event -------------*/
3226 mouse_get_last (NULL
, &mp
);
3229 /* The clicked line have anything special on it? */
3230 if((line
= cur_top_line
+ mp
.row
) < scroll_text_lines()
3231 && (key
= dot_on_handle(line
, mp
.col
))){
3233 case M_BUTTON_RIGHT
:
3235 if(sparms
->mouse
.popup
){
3236 if(sparms
->text
.handles
->key
< key
)
3237 for(next_handle
= sparms
->text
.handles
->next
;
3238 next_handle
->key
!= key
;
3239 next_handle
= next_handle
->next
)
3242 for(next_handle
= sparms
->text
.handles
;
3243 next_handle
->key
!= key
;
3244 next_handle
= next_handle
->prev
)
3247 if(sparms
->mouse
.popup
){
3248 cur_top_line
= scroll_scroll_text(cur_top_line
,
3250 ps_global
->mangled_body
);
3252 switch((*sparms
->mouse
.popup
)(sparms
, key
)){
3254 cur_top_line
= doubleclick_handle(sparms
, next_handle
, &cmd
, &done
);
3267 case M_BUTTON_LEFT
:
3268 if(sparms
->text
.handles
->key
< key
)
3269 for(next_handle
= sparms
->text
.handles
->next
;
3270 next_handle
->key
!= key
;
3271 next_handle
= next_handle
->next
)
3274 for(next_handle
= sparms
->text
.handles
;
3275 next_handle
->key
!= key
;
3276 next_handle
= next_handle
->prev
)
3279 if(mp
.doubleclick
) /* launch url */
3280 cur_top_line
= doubleclick_handle(sparms
, next_handle
, &cmd
, &done
);
3281 else if(sparms
->mouse
.click
)
3282 (*sparms
->mouse
.click
)(sparms
);
3286 case M_BUTTON_MIDDLE
: /* NO-OP for now */
3289 default: /* just ignore */
3294 else if(mp
.button
== M_BUTTON_RIGHT
){
3296 * Toss generic popup on to the screen
3298 if(sparms
->mouse
.popup
)
3299 if((*sparms
->mouse
.popup
)(sparms
, 0) == 2){
3310 /*-------------- Display Resize -------------*/
3312 if(sparms
->resize_exit
){
3316 * Figure out char offset of the char in the top left
3317 * corner of the display. Pass it back to the
3318 * fetcher/formatter and have it pass the offset
3321 sparms
->start
.on
= Offset
;
3322 for(sparms
->start
.loc
.offset
= line
= 0L;
3323 line
< cur_top_line
;
3325 sparms
->start
.loc
.offset
+= scroll_handle_column(line
, -1);
3331 /* else no reformatting neccessary, fall thru to repaint */
3334 /*-------------- refresh -------------*/
3336 num_display_lines
= SCROLL_LINES(ps_global
);
3337 mark_status_dirty();
3338 mark_keymenu_dirty();
3339 mark_titlebar_dirty();
3340 ps_global
->mangled_screen
= 1;
3345 /*------- no op timeout to check for new mail ------*/
3350 /*------- Forward displayed text ------*/
3352 forward_text(ps_global
, sparms
->text
.text
, sparms
->text
.src
);
3356 /*----------- Save the displayed text ------------*/
3358 (void)simple_export(ps_global
, sparms
->text
.text
,
3359 sparms
->text
.src
, "text", NULL
);
3363 /*----------- Exit this screen ------------*/
3369 /*----------- Pop back to the Main Menu ------------*/
3371 ps_global
->next_screen
= main_menu_screen
;
3376 /*----------- Print ------------*/
3378 print_to_printer(sparms
);
3382 /* ------- First handle on Line ------ */
3384 if(sparms
->text
.handles
){
3385 next_handle
= scroll_handle_boundary(sparms
->text
.handles
,
3386 scroll_handle_prev_sel
);
3390 /* fall thru as bogus */
3392 /* ------- Last handle on Line ------ */
3394 if(sparms
->text
.handles
){
3395 next_handle
= scroll_handle_boundary(sparms
->text
.handles
,
3396 scroll_handle_next_sel
);
3400 /* fall thru as bogus */
3402 /*------- BOGUS INPUT ------*/
3406 if(sparms
->bogus_input
)
3407 done
= (*sparms
->bogus_input
)(ch
);
3409 bogus_command(ch
, F_ON(F_USE_FK
,ps_global
) ? "F1" : "?");
3415 bogus_utf8_command(utf8str
, F_ON(F_USE_FK
, ps_global
) ? "F1" : "?");
3419 /*------- Standard commands ------*/
3421 whereis_pos
.row
= 0;
3422 if(sparms
->proc
.tool
)
3423 result
= (*sparms
->proc
.tool
)(cmd
, ps_global
->msgmap
, sparms
);
3425 result
= process_cmd(ps_global
, ps_global
->mail_stream
,
3426 ps_global
->msgmap
, cmd
, View
, &force
);
3428 dprint((7, "PROCESS_CMD return: %d\n", result
));
3430 if(ps_global
->next_screen
!= SCREEN_FUN_NULL
|| result
== 1){
3432 if(cmd
== MC_FULLHDR
){
3433 if(ps_global
->full_header
== 1){
3437 * Figure out char offset of the char in the top left
3438 * corner of the display. Pass it back to the
3439 * fetcher/formatter and have it pass the offset
3442 sparms
->start
.on
= Offset
;
3443 for(sparms
->start
.loc
.offset
= line
= 0L;
3444 line
< cur_top_line
;
3446 sparms
->start
.loc
.offset
+=
3447 scroll_handle_column(line
, -1);
3450 sparms
->start
.on
= 0;
3454 sparms
->keys
.what
= FirstMenu
;
3457 sparms
->keys
.what
= SecondMenu
;
3460 sparms
->keys
.what
= ThirdMenu
;
3463 sparms
->keys
.what
= FourthMenu
;
3468 else if(!scroll_state(SS_CUR
)){
3469 num_display_lines
= SCROLL_LINES(ps_global
);
3470 ps_global
->mangled_screen
= 1;
3475 } /* End of switch() */
3477 /* Need to frame some handles? */
3478 if(sparms
->text
.handles
3480 && handle_on_page(sparms
->text
.handles
, cur_top_line
,
3481 cur_top_line
+ num_display_lines
))
3483 && handle_on_page(next_handle
, cur_top_line
,
3484 cur_top_line
+ num_display_lines
))))
3485 next_handle
= scroll_handle_in_frame(cur_top_line
);
3487 } /* End of while() -- loop executing commands */
3489 ps_global
->redrawer
= NULL
; /* next statement makes this invalid! */
3490 zero_scroll_text(); /* very important to zero out on return!!! */
3491 scroll_state(SS_FREE
);
3492 if(sparms
->bar
.color
)
3493 free_color_pair(&sparms
->bar
.color
);
3496 scroll_setrange(0L, 0L);
3502 /*----------------------------------------------------------------------
3505 Args: text -- The text to print out
3506 source -- What type of source text is
3507 message -- Message for open_printer()
3508 Handling of error conditions is very poor.
3512 print_to_printer(SCROLL_S
*sparms
)
3516 snprintf(message
, sizeof(message
), "%s", STYLE_NAME(sparms
));
3517 message
[sizeof(message
)-1] = '\0';
3519 if(open_printer(message
) != 0)
3522 switch(sparms
->text
.src
){
3524 if(sparms
->text
.text
!= (char *)NULL
)
3525 print_text((char *)sparms
->text
.text
);
3530 if(sparms
->text
.text
!= (char **)NULL
){
3533 for(t
= sparms
->text
.text
; *t
!= NULL
; t
++){
3535 print_text(NEWLINE
);
3542 if(sparms
->text
.text
!= (FILE *)NULL
) {
3546 fseek((FILE *)sparms
->text
.text
, 0L, 0);
3547 n
= SIZEOF_20KBUF
- 1;
3548 while((i
= fread((void *)tmp_20k_buf
, sizeof(char),
3549 n
, (FILE *)sparms
->text
.text
)) != 0) {
3550 tmp_20k_buf
[i
] = '\0';
3551 print_text(tmp_20k_buf
);
3564 /*----------------------------------------------------------------------
3565 Search text being viewed (help or message)
3567 Args: q_line -- The screen line to prompt for search string on
3568 start_line -- Line number in text to begin search on
3569 start_index -- Where to begin search at in first line of text
3570 cursor_pos -- position of cursor is returned to caller here
3571 (Actually, this isn't really the position of the
3572 cursor because we don't know where we are on the
3573 screen. So row is set to the line number and col
3574 is set to the right column.)
3575 offset_in_line -- Offset where match was found.
3577 Result: returns line number string was found on
3580 -3 if only match is at start_index - 1
3581 -4 if search to first line
3582 -5 if search to last line
3585 search_text(int q_line
, long int start_line
, int start_index
, char **report
,
3586 Pos
*cursor_pos
, int *offset_in_line
)
3588 char prompt
[MAX_SEARCH
+50], nsearch_string
[MAX_SEARCH
+1], *p
;
3591 static HISTORY_S
*history
= NULL
;
3592 char search_string
[MAX_SEARCH
+1];
3593 static ESCKEY_S word_search_key
[] = { { 0, 0, "", "" },
3594 {ctrl('Y'), 10, "^Y", N_("First Line")},
3595 {ctrl('V'), 11, "^V", N_("Last Line")},
3596 {KEY_UP
, 30, "", ""},
3597 {KEY_DOWN
, 31, "", ""},
3600 #define KU_ST (3) /* index of KEY_UP */
3602 init_hist(&history
, HISTSIZE
);
3605 * Put the last one used in the default search_string,
3606 * not in nsearch_string.
3608 search_string
[0] = '\0';
3609 if((p
= get_prev_hist(history
, "", 0, NULL
)) != NULL
){
3610 strncpy(search_string
, p
, sizeof(search_string
));
3611 search_string
[sizeof(search_string
)-1] = '\0';
3614 snprintf(prompt
, sizeof(prompt
), _("Word to search for [%s] : "), search_string
);
3616 nsearch_string
[0] = '\0';
3619 flags
= OE_APPEND_CURRENT
| OE_SEQ_SENSITIVE
| OE_KEEP_TRAILING_SPACE
;
3622 * 2 is really 1 because there will be one real entry and
3623 * one entry of "" because of the get_prev_hist above.
3625 if(items_in_hist(history
) > 2){
3626 word_search_key
[KU_ST
].name
= HISTORY_UP_KEYNAME
;
3627 word_search_key
[KU_ST
].label
= HISTORY_KEYLABEL
;
3628 word_search_key
[KU_ST
+1].name
= HISTORY_DOWN_KEYNAME
;
3629 word_search_key
[KU_ST
+1].label
= HISTORY_KEYLABEL
;
3632 word_search_key
[KU_ST
].name
= "";
3633 word_search_key
[KU_ST
].label
= "";
3634 word_search_key
[KU_ST
+1].name
= "";
3635 word_search_key
[KU_ST
+1].label
= "";
3638 rc
= optionally_enter(nsearch_string
, q_line
, 0, sizeof(nsearch_string
),
3639 prompt
, word_search_key
, help
, &flags
);
3642 help
= help
== NO_HELP
? h_oe_searchview
: NO_HELP
;
3647 *report
= _("Searched to First Line.");
3653 *report
= _("Searched to Last Line.");
3658 if((p
= get_prev_hist(history
, nsearch_string
, 0, NULL
)) != NULL
){
3659 strncpy(nsearch_string
, p
, sizeof(nsearch_string
));
3660 nsearch_string
[sizeof(nsearch_string
)-1] = '\0';
3668 if((p
= get_next_hist(history
, nsearch_string
, 0, NULL
)) != NULL
){
3669 strncpy(nsearch_string
, p
, sizeof(nsearch_string
));
3670 nsearch_string
[sizeof(nsearch_string
)-1] = '\0';
3678 if(rc
!= 4){ /* 4 is redraw */
3679 save_hist(history
, nsearch_string
, 0, NULL
);
3684 if(rc
== 1 || (search_string
[0] == '\0' && nsearch_string
[0] == '\0'))
3687 if(nsearch_string
[0] != '\0'){
3688 strncpy(search_string
, nsearch_string
, sizeof(search_string
)-1);
3689 search_string
[sizeof(search_string
)-1] = '\0';
3692 rc
= search_scroll_text(start_line
, start_index
, search_string
, cursor_pos
,
3698 /*----------------------------------------------------------------------
3699 Update the scroll tool's titlebar
3701 Args: cur_top_line --
3702 redraw -- flag to force updating
3706 update_scroll_titlebar(long int cur_top_line
, int redraw
)
3708 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
3709 int num_display_lines
= SCROLL_LINES(ps_global
);
3710 long new_line
= (cur_top_line
+ num_display_lines
> st
->num_lines
)
3712 : cur_top_line
+ num_display_lines
;
3714 COLOR_PAIR
*returned_color
= NULL
;
3715 COLOR_PAIR
*titlecolor
= NULL
;
3717 SEARCHSET
*ss
= NULL
;
3719 if(st
->parms
->use_indexline_color
3720 && ps_global
->titlebar_color_style
!= TBAR_COLOR_DEFAULT
){
3721 raw_msgno
= mn_m2raw(ps_global
->msgmap
, mn_get_cur(ps_global
->msgmap
));
3722 if(raw_msgno
> 0L && ps_global
->mail_stream
3723 && raw_msgno
<= ps_global
->mail_stream
->nmsgs
){
3724 ss
= mail_newsearchset();
3725 ss
->first
= ss
->last
= (unsigned long) raw_msgno
;
3729 PAT_STATE
*pstate
= NULL
;
3731 colormatch
= get_index_line_color(ps_global
->mail_stream
,
3732 ss
, &pstate
, &returned_color
);
3733 mail_free_searchset(&ss
);
3736 * This is a bit tricky. If there is a colormatch but returned_color
3737 * is NULL, that means that the user explicitly wanted the
3738 * Normal color used in this index line, so that is what we
3739 * use. If no colormatch then we will use the TITLE color
3740 * instead of Normal.
3744 titlecolor
= returned_color
;
3746 titlecolor
= new_color_pair(ps_global
->VAR_NORM_FORE_COLOR
,
3747 ps_global
->VAR_NORM_BACK_COLOR
);
3751 && ps_global
->titlebar_color_style
== TBAR_COLOR_REV_INDEXLINE
){
3752 char cbuf
[MAXCOLORLEN
+1];
3754 strncpy(cbuf
, titlecolor
->fg
, MAXCOLORLEN
);
3755 strncpy(titlecolor
->fg
, titlecolor
->bg
, MAXCOLORLEN
);
3756 strncpy(titlecolor
->bg
, cbuf
, MAXCOLORLEN
);
3760 /* Did the color change? */
3761 if((!titlecolor
&& st
->parms
->bar
.color
)
3763 (titlecolor
&& !st
->parms
->bar
.color
)
3765 (titlecolor
&& st
->parms
->bar
.color
3766 && (strcmp(titlecolor
->fg
, st
->parms
->bar
.color
->fg
)
3767 || strcmp(titlecolor
->bg
, st
->parms
->bar
.color
->bg
)))){
3770 if(st
->parms
->bar
.color
)
3771 free_color_pair(&st
->parms
->bar
.color
);
3773 st
->parms
->bar
.color
= titlecolor
;
3778 free_color_pair(&titlecolor
);
3783 set_titlebar(st
->parms
->bar
.title
, ps_global
->mail_stream
,
3784 ps_global
->context_current
, ps_global
->cur_folder
,
3785 ps_global
->msgmap
, 1, st
->parms
->bar
.style
,
3786 new_line
, st
->num_lines
, st
->parms
->bar
.color
);
3787 ps_global
->mangled_header
= 0;
3789 else if(st
->parms
->bar
.style
== TextPercent
)
3790 update_titlebar_lpercent(new_line
);
3792 update_titlebar_percent(new_line
);
3796 /*----------------------------------------------------------------------
3797 manager of global (to this module, anyway) scroll state structures
3802 scroll_state(int func
)
3804 struct scrollstack
{
3806 struct scrollstack
*prev
;
3808 static struct scrollstack
*stack
= NULL
;
3811 case SS_CUR
: /* no op */
3814 s
= (struct scrollstack
*)fs_get(sizeof(struct scrollstack
));
3815 memset((void *)s
, 0, sizeof(struct scrollstack
));
3822 fs_give((void **)&stack
);
3826 default: /* BUG: should complain */
3830 return(stack
? &stack
->s
: NULL
);
3834 /*----------------------------------------------------------------------
3835 Save all the data for scrolling text and paint the screen
3840 set_scroll_text(SCROLL_S
*sparms
, long int current_line
, SCRLCTRL_S
*st
)
3842 /* save all the stuff for possible asynchronous redraws */
3844 st
->top_text_line
= current_line
;
3845 st
->screen
.start_line
= SCROLL_LINES_ABOVE(ps_global
);
3846 st
->screen
.other_lines
= SCROLL_LINES_ABOVE(ps_global
)
3847 + SCROLL_LINES_BELOW(ps_global
);
3848 st
->screen
.width
= -1; /* Force text formatting calculation */
3852 /*----------------------------------------------------------------------
3853 Redraw the text on the screen, possibly reformatting if necessary
3859 redraw_scroll_text(void)
3862 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
3864 format_scroll_text();
3866 offset
= (st
->parms
->text
.src
== FileStar
) ? 0 : st
->top_text_line
;
3869 mswin_beginupdate();
3871 /*---- Actually display the text on the screen ------*/
3872 for(i
= 0; i
< st
->screen
.length
; i
++){
3873 ClearLine(i
+ st
->screen
.start_line
);
3874 if((offset
+ i
) < st
->num_lines
)
3875 PutLine0n8b(i
+ st
->screen
.start_line
, 0, st
->text_lines
[offset
+ i
],
3876 st
->line_lengths
[offset
+ i
], st
->parms
->text
.handles
);
3887 /*----------------------------------------------------------------------
3888 Free memory used as scrolling buffers for text on disk. Also mark
3889 text_lines as available
3892 zero_scroll_text(void)
3894 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
3897 for(i
= 0; i
< st
->lines_allocated
; i
++)
3898 if(st
->parms
->text
.src
== FileStar
&& st
->text_lines
[i
])
3899 fs_give((void **)&st
->text_lines
[i
]);
3901 st
->text_lines
[i
] = NULL
;
3903 if(st
->parms
->text
.src
== FileStar
&& st
->findex
!= NULL
){
3907 our_unlink(st
->fname
);
3908 fs_give((void **)&st
->fname
);
3913 fs_give((void **)&st
->text_lines
);
3915 if(st
->line_lengths
)
3916 fs_give((void **) &st
->line_lengths
);
3920 /*----------------------------------------------------------------------
3922 Always format at least 20 chars wide. Wrapping lines would be crazy for
3923 screen widths of 1-20 characters
3926 format_scroll_text(void)
3930 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
3932 register char **tl
, **tl_end
;
3934 if(!st
|| (st
->screen
.width
== (i
= ps_global
->ttyo
->screen_cols
)
3935 && st
->screen
.length
== PGSIZE(st
)))
3938 st
->screen
.width
= MAX(20, i
);
3939 st
->screen
.length
= PGSIZE(st
);
3941 if(st
->lines_allocated
== 0) {
3942 st
->lines_allocated
= TYPICAL_BIG_MESSAGE_LINES
;
3943 st
->text_lines
= (char **)fs_get(st
->lines_allocated
*sizeof(char *));
3944 memset(st
->text_lines
, 0, st
->lines_allocated
* sizeof(char *));
3945 st
->line_lengths
= (short *)fs_get(st
->lines_allocated
*sizeof(short));
3948 tl
= st
->text_lines
;
3949 ll
= st
->line_lengths
;
3950 tl_end
= &st
->text_lines
[st
->lines_allocated
];
3952 if(st
->parms
->text
.src
== CharStarStar
) {
3953 /*---- original text is already list of lines -----*/
3954 /* The text could be wrapped nicely for narrow screens; for now
3955 it will get truncated as it is displayed */
3956 for(pp
= (char **)st
->parms
->text
.text
; *pp
!= NULL
;) {
3958 *ll
++ = st
->screen
.width
;
3960 i
= tl
- st
->text_lines
;
3961 st
->lines_allocated
*= 2;
3962 fs_resize((void **)&st
->text_lines
,
3963 st
->lines_allocated
* sizeof(char *));
3964 fs_resize((void **)&st
->line_lengths
,
3965 st
->lines_allocated
*sizeof(short));
3966 tl
= &st
->text_lines
[i
];
3967 ll
= &st
->line_lengths
[i
];
3968 tl_end
= &st
->text_lines
[st
->lines_allocated
];
3972 st
->num_lines
= tl
- st
->text_lines
;
3974 else if (st
->parms
->text
.src
== CharStar
) {
3975 /*------ Format the plain text ------*/
3976 for(p
= (char *)st
->parms
->text
.text
; *p
; ) {
3979 for(; *p
&& !(*p
== RETURN
|| *p
== LINE_FEED
); p
++)
3985 i
= tl
- st
->text_lines
;
3986 st
->lines_allocated
*= 2;
3987 fs_resize((void **)&st
->text_lines
,
3988 st
->lines_allocated
* sizeof(char *));
3989 fs_resize((void **)&st
->line_lengths
,
3990 st
->lines_allocated
*sizeof(short));
3991 tl
= &st
->text_lines
[i
];
3992 ll
= &st
->line_lengths
[i
];
3993 tl_end
= &st
->text_lines
[st
->lines_allocated
];
3996 if(*p
== '\r' && *(p
+1) == '\n')
3998 else if(*p
== '\n' || *p
== '\r')
4002 st
->num_lines
= tl
- st
->text_lines
;
4005 /*------ Display text is in a file --------*/
4008 * This is pretty much only useful under DOS where we can't fit
4009 * all of big messages in core at once. This scheme makes
4010 * some simplifying assumptions:
4011 * 1. Lines are on disk just the way we'll display them. That
4012 * is, line breaks and such are left to the function that
4013 * writes the disk file to catch and fix.
4014 * 2. We get away with this mainly because the DOS display isn't
4015 * going to be resized out from under us.
4017 * The idea is to use the already alloc'd array of char * as a
4018 * buffer for sections of what's on disk. We'll set up the first
4019 * few lines here, and read new ones in as needed in
4020 * scroll_scroll_text().
4022 * but first, make sure there are enough buffer lines allocated
4023 * to serve as a place to hold lines from the file.
4025 * Actually, this is also used under windows so the display will
4026 * be resized out from under us. So I changed the following
4028 * 1. free old text_lines, which may have been allocated
4029 * for a narrow screen.
4030 * 2. insure we have enough text_lines
4031 * 3. reallocate all text_lines that are needed.
4032 * (tom unger 10/26/94)
4035 /* free old text lines, which may be too short. */
4036 for(i
= 0; i
< st
->lines_allocated
; i
++)
4037 if(st
->text_lines
[i
]) /* clear alloc'd lines */
4038 fs_give((void **)&st
->text_lines
[i
]);
4040 /* Insure we have enough text lines. */
4041 if(st
->lines_allocated
< (2 * PGSIZE(st
)) + 1){
4042 st
->lines_allocated
= (2 * PGSIZE(st
)) + 1; /* resize */
4044 fs_resize((void **)&st
->text_lines
,
4045 st
->lines_allocated
* sizeof(char *));
4046 memset(st
->text_lines
, 0, st
->lines_allocated
* sizeof(char *));
4047 fs_resize((void **)&st
->line_lengths
,
4048 st
->lines_allocated
*sizeof(short));
4051 /* reallocate all text lines that are needed. */
4052 for(i
= 0; i
<= PGSIZE(st
); i
++)
4053 if(st
->text_lines
[i
] == NULL
)
4054 st
->text_lines
[i
] = (char *)fs_get((st
->screen
.width
+ 1)
4057 tl
= &st
->text_lines
[i
];
4059 st
->num_lines
= make_file_index();
4061 ScrollFile(st
->top_text_line
); /* then load them up */
4065 * Efficiency hack. If there are handles, fill in their
4066 * line number field for later...
4068 if(st
->parms
->text
.handles
){
4073 for(line
= 0; line
< st
->num_lines
; line
++)
4074 for(i
= 0, col
= 0; i
< st
->line_lengths
[line
];)
4075 switch(st
->text_lines
[line
][i
]){
4078 switch((i
< st
->line_lengths
[line
]) ? st
->text_lines
[line
][i
]
4081 for(key
= 0, n
= st
->text_lines
[line
][++i
]; n
> 0; n
--)
4082 key
= (key
* 10) + (st
->text_lines
[line
][++i
] - '0');
4085 for(h
= st
->parms
->text
.handles
; h
; h
= h
->next
)
4087 scroll_handle_set_loc(&h
->loc
, line
, col
);
4091 if(!h
) /* anything behind us? */
4092 for(h
= st
->parms
->text
.handles
->prev
; h
; h
= h
->prev
)
4094 scroll_handle_set_loc(&h
->loc
, line
, col
);
4102 i
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
4114 default: /* literal embed char */
4122 while(((++col
) & 0x07) != 0) /* add tab's spaces */
4128 col
+= width_at_this_position((unsigned char*) &st
->text_lines
[line
][i
],
4129 st
->line_lengths
[line
] - i
);
4130 i
++; /* character count */
4136 scroll_setrange (st
->screen
.length
, st
->num_lines
);
4144 * ScrollFile - scroll text into the st struct file making sure 'line'
4145 * of the file is the one first in the text_lines buffer.
4147 * NOTE: talk about massive potential for tuning...
4148 * Goes without saying this is still under constuction
4151 ScrollFile(long int line
)
4153 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4157 if(line
<= 0){ /* reset and load first couple of pages */
4158 fseek((FILE *) st
->parms
->text
.text
, 0L, 0);
4165 for(i
= 0; i
< PGSIZE(st
); i
++){
4166 /*** do stuff to get the file pointer into the right place ***/
4168 * BOGUS: this is painfully crude right now, but I just want to get
4171 * possibly in the near furture, an array of indexes into the
4172 * file that are the offset for the beginning of each line will
4173 * speed things up. Of course, this
4174 * will have limits, so maybe a disk file that is an array
4175 * of indexes is the answer.
4177 if(fseek(st
->findex
, (size_t)(line
++) * sizeof(SCRLFILE_S
), 0) < 0
4178 || fread(&sf
, sizeof(SCRLFILE_S
), (size_t)1, st
->findex
) != 1
4179 || fseek((FILE *) st
->parms
->text
.text
, sf
.offset
, 0) < 0
4180 || !st
->text_lines
[i
]
4181 || (sf
.len
&& !fgets(st
->text_lines
[i
], sf
.len
+ 1,
4182 (FILE *) st
->parms
->text
.text
)))
4185 st
->line_lengths
[i
] = sf
.len
;
4188 for(; i
< PGSIZE(st
); i
++)
4189 if(st
->text_lines
[i
]){ /* blank out any unused lines */
4190 *st
->text_lines
[i
] = '\0';
4191 st
->line_lengths
[i
] = 0;
4197 * make_file_index - do a single pass over the file containing the text
4198 * to display, recording line lengths and offsets.
4199 * NOTE: This is never really to be used on a real OS with virtual
4200 * memory. This is the whole reason st->findex exists. Don't
4201 * want to waste precious memory on a stupid array that could
4205 make_file_index(void)
4207 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4214 st
->fname
= temp_nam(NULL
, "pi");
4216 if(!st
->fname
|| (st
->findex
= our_fopen(st
->fname
,"w+b")) == NULL
){
4218 our_unlink(st
->fname
);
4219 fs_give((void **)&st
->fname
);
4226 fseek(st
->findex
, 0L, 0);
4228 fseek((FILE *)st
->parms
->text
.text
, 0L, 0);
4231 sf
.len
= st
->screen
.width
+ 1;
4232 if(scroll_file_line((FILE *) st
->parms
->text
.text
,
4233 tmp_20k_buf
, &sf
, &state
)){
4234 fwrite((void *) &sf
, sizeof(SCRLFILE_S
), (size_t)1, st
->findex
);
4241 fseek((FILE *)st
->parms
->text
.text
, 0L, 0);
4247 /*----------------------------------------------------------------------
4248 Get the next line to scroll from the given file
4252 scroll_file_line(FILE *fp
, char *buf
, SCRLFILE_S
*sfp
, int *wrapt
)
4254 register char *s
= NULL
;
4258 sfp
->offset
= ftell(fp
);
4259 if(!(s
= fgets(buf
, sfp
->len
, fp
)))
4260 return(NULL
); /* can't grab a line? */
4264 *wrapt
= 1; /* remember; that we wrapped */
4267 else if(*s
== NEWLINE
[0] && (!NEWLINE
[1] || *(s
+1) == NEWLINE
[1])){
4268 int empty
= (*wrapt
&& s
== buf
);
4270 *wrapt
= 0; /* turn off wrapped state */
4272 s
= NULL
; /* get a new line */
4285 /*----------------------------------------------------------------------
4286 Scroll the text on the screen
4288 Args: new_top_line -- The line to be displayed on top of the screen
4289 redraw -- Flag to force a redraw even if nothing changed
4291 Returns: resulting top line
4292 Note: the returned line number may be less than new_top_line if
4293 reformatting caused the total line count to change.
4297 scroll_scroll_text(long int new_top_line
, HANDLE_S
*handle
, int redraw
)
4299 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4300 int num_display_lines
, l
, top
;
4301 POSLIST_S
*lp
, *lp2
;
4303 /* When this is true, we're still on the same page of the display. */
4304 if(st
->top_text_line
== new_top_line
&& !redraw
){
4305 /* handle changed, so hilite the new handle and unhilite the old */
4306 if(handle
&& handle
!= st
->parms
->text
.handles
){
4307 top
= st
->screen
.start_line
- new_top_line
;
4308 /* hilite the new one */
4309 if(!scroll_handle_obscured(handle
))
4310 for(lp
= handle
->loc
; lp
; lp
= lp
->next
)
4311 if((l
= lp
->where
.row
) >= st
->top_text_line
4312 && l
< st
->top_text_line
+ st
->screen
.length
){
4313 if(st
->parms
->text
.src
== FileStar
)
4316 PutLine0n8b(top
+ lp
->where
.row
, 0, st
->text_lines
[l
],
4317 st
->line_lengths
[l
], handle
);
4320 /* unhilite the old one */
4321 if(!scroll_handle_obscured(st
->parms
->text
.handles
))
4322 for(lp
= st
->parms
->text
.handles
->loc
; lp
; lp
= lp
->next
)
4323 if((l
= lp
->where
.row
) >= st
->top_text_line
4324 && l
< st
->top_text_line
+ st
->screen
.length
){
4325 for(lp2
= handle
->loc
; lp2
; lp2
= lp2
->next
)
4326 if(l
== lp2
->where
.row
)
4330 if(st
->parms
->text
.src
== FileStar
)
4333 PutLine0n8b(top
+ lp
->where
.row
, 0, st
->text_lines
[l
],
4334 st
->line_lengths
[l
], handle
);
4338 st
->parms
->text
.handles
= handle
; /* update current */
4341 return(new_top_line
);
4344 num_display_lines
= PGSIZE(st
);
4346 format_scroll_text();
4348 if(st
->top_text_line
>= st
->num_lines
) /* don't pop line count */
4349 new_top_line
= st
->top_text_line
= MAX(st
->num_lines
- 1, 0);
4351 if(st
->parms
->text
.src
== FileStar
)
4352 ScrollFile(new_top_line
); /* set up new st->text_lines */
4355 scroll_setrange (st
->screen
.length
, st
->num_lines
);
4356 scroll_setpos (new_top_line
);
4360 Check out the scrolling situation. If we want to scroll, but BeginScroll
4361 says we can't then repaint, + 10 is so we repaint most of the time.
4364 (st
->top_text_line
- new_top_line
+ 10 >= num_display_lines
||
4365 new_top_line
- st
->top_text_line
+ 10 >= num_display_lines
) ||
4366 BeginScroll(st
->screen
.start_line
,
4367 st
->screen
.start_line
+ num_display_lines
- 1) != 0) {
4368 /* Too much text to scroll, or can't scroll -- just repaint */
4371 st
->parms
->text
.handles
= handle
;
4373 st
->top_text_line
= new_top_line
;
4374 redraw_scroll_text();
4378 * We're going to scroll the screen, but first we have to make sure
4379 * the old hilited handles are unhilited if they are going to remain
4382 top
= st
->screen
.start_line
- st
->top_text_line
;
4383 if(handle
&& handle
!= st
->parms
->text
.handles
4384 && st
->parms
->text
.handles
4385 && !scroll_handle_obscured(st
->parms
->text
.handles
))
4386 for(lp
= st
->parms
->text
.handles
->loc
; lp
; lp
= lp
->next
)
4387 if((l
= lp
->where
.row
) >= MAX(st
->top_text_line
,new_top_line
)
4388 && l
< MIN(st
->top_text_line
,new_top_line
) + st
->screen
.length
){
4389 if(st
->parms
->text
.src
== FileStar
)
4392 PutLine0n8b(top
+ lp
->where
.row
, 0, st
->text_lines
[l
],
4393 st
->line_lengths
[l
], handle
);
4396 if(new_top_line
> st
->top_text_line
){
4397 /*------ scroll down ------*/
4398 while(new_top_line
> st
->top_text_line
) {
4401 l
= (st
->parms
->text
.src
== FileStar
)
4402 ? num_display_lines
- (new_top_line
- st
->top_text_line
)
4403 : st
->top_text_line
+ num_display_lines
;
4405 if(l
< st
->num_lines
){
4406 PutLine0n8b(st
->screen
.start_line
+ num_display_lines
- 1,
4407 0, st
->text_lines
[l
], st
->line_lengths
[l
],
4408 handle
? handle
: st
->parms
->text
.handles
);
4410 * We clear to the end of line in the right background
4411 * color. If the line was exactly the width of the screen
4412 * then PutLine0n8b will have left _col and _row moved to
4413 * the start of the next row. We don't need or want to clear
4416 if(pico_usingcolor()
4417 && (st
->line_lengths
[l
] < ps_global
->ttyo
->screen_cols
4418 || visible_linelen(l
) < ps_global
->ttyo
->screen_cols
))
4422 st
->top_text_line
++;
4426 /*------ scroll up -----*/
4427 while(new_top_line
< st
->top_text_line
) {
4430 st
->top_text_line
--;
4431 l
= (st
->parms
->text
.src
== FileStar
)
4432 ? st
->top_text_line
- new_top_line
4433 : st
->top_text_line
;
4434 PutLine0n8b(st
->screen
.start_line
, 0, st
->text_lines
[l
],
4435 st
->line_lengths
[l
],
4436 handle
? handle
: st
->parms
->text
.handles
);
4438 * We clear to the end of line in the right background
4439 * color. If the line was exactly the width of the screen
4440 * then PutLine0n8b will have left _col and _row moved to
4441 * the start of the next row. We don't need or want to clear
4444 if(pico_usingcolor()
4445 && (st
->line_lengths
[l
] < ps_global
->ttyo
->screen_cols
4446 || visible_linelen(l
) < ps_global
->ttyo
->screen_cols
))
4453 if(handle
&& handle
!= st
->parms
->text
.handles
){
4456 for(lp
= handle
->loc
; lp
; lp
= lp
->next
)
4457 if(lp
->where
.row
>= st
->top_text_line
4458 && lp
->where
.row
< st
->top_text_line
+ st
->screen
.length
){
4459 PutLine0n8b(st
->screen
.start_line
4460 + (lp
->where
.row
- st
->top_text_line
),
4461 0, st
->text_lines
[lp
->where
.row
],
4462 st
->line_lengths
[lp
->where
.row
],
4467 st
->parms
->text
.handles
= handle
;
4473 return(new_top_line
);
4477 /*---------------------------------------------------------------------
4478 Edit individual char in text so that the entire text doesn't need
4479 to be completely reformatted.
4481 Returns 0 if there were no errors, 1 if we would like the entire
4482 text to be reformatted.
4485 ng_scroll_edit(CONTEXT_S
*context
, int index
)
4487 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4488 char *ngp
, tmp
[MAILTMPLEN
+10];
4492 if (!(f
= folder_entry(index
, FOLDERS(context
))))
4495 return 0; /* nothing in scroll needs to be changed */
4496 tmp
[0] = TAG_HANDLE
;
4497 snprintf(tmp
+2, sizeof(tmp
)-2, "%d", st
->parms
->text
.handles
->key
);
4498 tmp
[sizeof(tmp
)-1] = '\0';
4499 tmp
[1] = len
= strlen(tmp
+2);
4500 snprintf(tmp
+len
+2, sizeof(tmp
)-(len
+2), "%s ", f
->selected
? "[ ]" : "[X]");
4501 tmp
[sizeof(tmp
)-1] = '\0';
4502 snprintf(tmp
+len
+6, sizeof(tmp
)-(len
+6), "%.*s", MAILTMPLEN
, f
->name
);
4503 tmp
[sizeof(tmp
)-1] = '\0';
4505 ngp
= *(st
->text_lines
);
4507 ngp
= strstr(ngp
, tmp
);
4512 /* assumption that text is of form "[ ] xxx.xxx" */
4519 else if (*ngp
== ' '){
4528 /*---------------------------------------------------------------------
4529 Similar to ng_scroll_edit, but this is the more general case of
4530 selecting a folder, as opposed to selecting a newsgroup for
4531 subscription while in listmode.
4533 Returns 0 if there were no errors, 1 if we would like the entire
4534 text to be reformatted.
4537 folder_select_update(CONTEXT_S
*context
, int index
)
4539 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4541 char *ngp
, tmp
[MAILTMPLEN
+10];
4542 int len
, total
, fnum
, num_sel
= 0;
4544 if (!(f
= folder_entry(index
, FOLDERS(context
))))
4546 ngp
= *(st
->text_lines
);
4548 total
= folder_total(FOLDERS(context
));
4550 for (fnum
= 0; num_sel
< 2 && fnum
< total
; fnum
++)
4551 if(folder_entry(fnum
, FOLDERS(context
))->selected
)
4553 if(!num_sel
|| (f
->selected
&& num_sel
== 1))
4554 return 1; /* need to reformat the whole thing */
4556 tmp
[0] = TAG_HANDLE
;
4557 snprintf(tmp
+2, sizeof(tmp
)-2, "%d", st
->parms
->text
.handles
->key
);
4558 tmp
[sizeof(tmp
)-1] = '\0';
4559 tmp
[1] = len
= strlen(tmp
+2);
4561 ngp
= strstr(ngp
, tmp
);
4564 if(F_ON(F_SELECTED_SHOWN_BOLD
, ps_global
)){
4566 while(*ngp
&& ngp
[0] != TAG_EMBED
4567 && ngp
[1] != (f
->selected
? TAG_BOLDOFF
: TAG_BOLDON
)
4568 && *ngp
!= *(f
->name
))
4571 if (!(*ngp
) || (*ngp
== *(f
->name
)))
4575 *ngp
= (f
->selected
? TAG_BOLDON
: TAG_BOLDOFF
);
4580 while(*ngp
!= ' ' && *ngp
!= *(f
->name
) && *ngp
)
4582 if(!(*ngp
) || (*ngp
== *(f
->name
)))
4586 *ngp
= f
->selected
? 'X' : ' ';
4593 /*---------------------------------------------------------------------
4594 We gotta go through all of the formatted text and add "[ ] " in the right
4595 place. If we don't do this, we must completely reformat the whole text,
4596 which could take a very long time.
4598 Return 1 if we encountered some sort of error and we want to reformat the
4599 whole text, return 0 if everything went as planned.
4601 ASSUMPTION: for this to work, we assume that there are only total
4602 number of handles, numbered 1 through total.
4605 scroll_add_listmode(CONTEXT_S
*context
, int total
)
4607 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4609 char *ngp
, *ngname
, handle_str
[MAILTMPLEN
];
4613 ngp
= *(st
->text_lines
);
4614 h
= st
->parms
->text
.handles
;
4616 while(h
&& h
->key
!= 1 && h
->prev
)
4619 handle_str
[0] = TAG_EMBED
;
4620 handle_str
[1] = TAG_HANDLE
;
4621 for(i
= 1; i
<= total
&& h
; i
++, h
= h
->next
){
4622 snprintf(handle_str
+3, sizeof(handle_str
)-3, "%d", h
->key
);
4623 handle_str
[sizeof(handle_str
)-1] = '\0';
4624 handle_str
[2] = strlen(handle_str
+3);
4625 ngp
= strstr(ngp
, handle_str
);
4627 ngp
= *(st
->text_lines
);
4631 ngname
= ngp
+ strlen(handle_str
);
4632 while (strncmp(ngp
, " ", 4) && !(*ngp
== '\n')
4633 && !(ngp
== *(st
->text_lines
)))
4635 if (strncmp(ngp
, " ", 4))
4637 while(ngp
+4 != ngname
&& *ngp
){
4642 if(folder_entry(h
->h
.f
.index
, FOLDERS(context
))->subscribed
){
4660 /*----------------------------------------------------------------------
4661 Search the set scrolling text
4663 Args: start_line -- line to start searching on
4664 start_index -- column to start searching at in first line
4665 word -- string to search for
4666 cursor_pos -- position of cursor is returned to caller here
4667 (Actually, this isn't really the position of the
4668 cursor because we don't know where we are on the
4669 screen. So row is set to the line number and col
4670 is set to the right column.)
4671 offset_in_line -- Offset where match was found.
4673 Returns: the line the word was found on, or -2 if it wasn't found, or
4674 -3 if the only match is at column start_index - 1.
4678 search_scroll_text(long int start_line
, int start_index
, char *word
,
4679 Pos
*cursor_pos
, int *offset_in_line
)
4681 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4683 long l
, offset
, dlines
;
4684 #define SROW(N) ((N) - offset)
4685 #define SLINE(N) st->text_lines[SROW(N)]
4686 #define SLEN(N) st->line_lengths[SROW(N)]
4688 dlines
= PGSIZE(st
);
4689 offset
= (st
->parms
->text
.src
== FileStar
) ? st
->top_text_line
: 0;
4691 if(start_line
< st
->num_lines
){
4692 /* search first line starting at position start_index in */
4693 if((wh
= search_scroll_line(SLINE(start_line
) + start_index
,
4695 SLEN(start_line
) - start_index
,
4696 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4697 cursor_pos
->row
= start_line
;
4698 cursor_pos
->col
= scroll_handle_column(SROW(start_line
),
4699 *offset_in_line
= wh
- SLINE(start_line
));
4703 if(st
->parms
->text
.src
== FileStar
)
4706 for(l
= start_line
+ 1; l
< st
->num_lines
; l
++) {
4707 if(st
->parms
->text
.src
== FileStar
&& l
> offset
+ dlines
)
4708 ScrollFile(offset
+= dlines
);
4710 if((wh
= search_scroll_line(SLINE(l
), word
, SLEN(l
),
4711 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4712 cursor_pos
->row
= l
;
4713 cursor_pos
->col
= scroll_handle_column(SROW(l
),
4714 *offset_in_line
= wh
- SLINE(l
));
4720 start_line
= st
->num_lines
;
4722 if(st
->parms
->text
.src
== FileStar
) /* wrap offset */
4723 ScrollFile(offset
= 0);
4725 for(l
= 0; l
< start_line
; l
++) {
4726 if(st
->parms
->text
.src
== FileStar
&& l
> offset
+ dlines
)
4727 ScrollFile(offset
+= dlines
);
4729 if((wh
= search_scroll_line(SLINE(l
), word
, SLEN(l
),
4730 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4731 cursor_pos
->row
= l
;
4732 cursor_pos
->col
= scroll_handle_column(SROW(l
),
4733 *offset_in_line
= wh
- SLINE(l
));
4738 /* search in current line */
4739 if(start_line
< st
->num_lines
4740 && (wh
= search_scroll_line(SLINE(start_line
), word
,
4741 start_index
+ strlen(word
) - 2,
4742 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4743 cursor_pos
->row
= start_line
;
4744 cursor_pos
->col
= scroll_handle_column(SROW(start_line
),
4745 *offset_in_line
= wh
- SLINE(start_line
));
4750 /* see if the only match is a repeat */
4751 if(start_index
> 0 && start_line
< st
->num_lines
4752 && (wh
= search_scroll_line(
4753 SLINE(start_line
) + start_index
- 1,
4755 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4756 cursor_pos
->row
= start_line
;
4757 cursor_pos
->col
= scroll_handle_column(SROW(start_line
),
4758 *offset_in_line
= wh
- SLINE(start_line
));
4766 /*----------------------------------------------------------------------
4767 Search one line of scroll text for given string
4769 Args: haystack -- The string to search in, the larger string
4770 needle -- The string to search for, the smaller string
4771 n -- The max number of chars in haystack to search
4773 Search for first occurrence of needle in the haystack, and return a pointer
4774 into the string haystack when it is found. The search is case independent.
4777 search_scroll_line(char *haystack
, char *needle
, int n
, int handles
)
4779 char *return_ptr
= NULL
, *found_it
= NULL
;
4780 char *haystack_copy
, *p
, *free_this
= NULL
, *end
;
4782 int state
= 0, i
= 0;
4784 if(n
> 0 && haystack
){
4786 haystack_copy
= buf
;
4788 haystack_copy
= free_this
= (char *) fs_get((n
+1) * sizeof(char));
4790 strncpy(haystack_copy
, haystack
, n
);
4791 haystack_copy
[n
] = '\0';
4794 * We don't want to match text inside embedded tags.
4795 * Replace embedded octets with nulls and convert
4796 * uppercase ascii to lowercase. We should also do
4797 * some sort of canonicalization of UTF-8 but that
4800 for(i
= n
, p
= haystack_copy
; i
-- > 0 && *p
; p
++){
4804 if(*p
== TAG_EMBED
){
4810 /* lower case just ascii chars */
4811 if(!(*p
& 0x80) && isupper(*p
))
4818 state
= (*p
== TAG_HANDLE
)
4820 : (*p
== TAG_FGCOLOR
|| *p
== TAG_BGCOLOR
) ? RGBLEN
: 0;
4825 state
= *p
; /* length of handle's key */
4837 * The haystack_copy string now looks like
4839 * "chars\0\0\0\0\0\0chars...\0\0\0chars... \0"
4841 * with that final \0 at haystack_copy[n].
4842 * Search each piece one at a time.
4845 end
= haystack_copy
+ n
;
4848 while(p
< end
&& !return_ptr
){
4851 while(*p
== '\0' && p
< end
)
4855 found_it
= srchstr(p
, needle
);
4858 /* found it, make result relative to haystack */
4859 return_ptr
= haystack
+ (found_it
- haystack_copy
);
4862 /* skip to next null */
4863 while(*p
!= '\0' && p
< end
)
4868 fs_give((void **) &free_this
);
4876 * Returns the number of columns taken up by the visible part of the line.
4877 * That is, account for handles and color changes and so forth.
4880 visible_linelen(int line
)
4882 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4885 if(line
< 0 || line
>= st
->num_lines
)
4888 for(i
= 0, len
= 0; i
< st
->line_lengths
[line
];)
4889 switch(st
->text_lines
[line
][i
]){
4893 switch((i
< st
->line_lengths
[line
]) ? st
->text_lines
[line
][i
] : 0){
4896 /* skip the length byte plus <length> more bytes */
4897 if(i
< st
->line_lengths
[line
]){
4898 n
= st
->text_lines
[line
][i
];
4902 if(i
< st
->line_lengths
[line
] && n
> 0)
4909 i
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
4921 case TAG_EMBED
: /* escaped embed character */
4926 default: /* the embed char was literal */
4936 while(((++len
) & 0x07) != 0) /* add tab's spaces */
4951 /*----------------------------------------------------------------------
4952 Display the contents of the given file (likely output from some command)
4954 Args: filename -- name of file containing output
4955 title -- title to be used for screen displaying output
4956 alt_msg -- if no output, Q this message instead of the default
4957 mode -- non-zero to display short files in status line
4961 display_output_file(char *filename
, char *title
, char *alt_msg
, int mode
)
4963 STORE_S
*in_file
= NULL
, *out_store
= NULL
;
4965 if((in_file
= so_get(FileStar
, filename
, READ_ACCESS
|READ_FROM_LOCALE
))){
4966 if(mode
== DOF_BRIEF
){
4967 int msg_q
= 0, i
= 0;
4968 char buf
[512], *msg_p
[4];
4969 #define MAX_SINGLE_MSG_LEN 60
4975 * Might need to do something about CRLFs for Windows.
4977 while(so_fgets(in_file
, msg_p
[msg_q
], sizeof(buf
) - (msg_p
[msg_q
] - buf
))
4979 && (i
= strlen(msg_p
[msg_q
])) < MAX_SINGLE_MSG_LEN
){
4980 msg_p
[msg_q
+1] = msg_p
[msg_q
]+strlen(msg_p
[msg_q
]);
4981 if (*(msg_p
[++msg_q
] - 1) == '\n')
4982 *(msg_p
[msg_q
] - 1) = '\0';
4985 if(msg_q
< 3 && i
< MAX_SINGLE_MSG_LEN
){
4987 for(i
= 0; i
< msg_q
; i
++)
4988 q_status_message2(SM_ORDER
, 3, 4,
4989 "%s Result: %s", title
, msg_p
[i
]);
4991 q_status_message2(SM_ORDER
, 0, 4, "%s%s", title
,
4994 : " command completed with no output");
5000 else if(mode
== DOF_EMPTY
){
5003 if(so_readc(&c
, in_file
) < 1){
5004 q_status_message2(SM_ORDER
, 0, 4, "%s%s", title
,
5007 : " command completed with no output");
5014 * We need to translate the file contents from the user's locale
5015 * charset to UTF-8 for use in scrolltool. We get that translation
5016 * from the READ_FROM_LOCALE in the in_file storage object.
5017 * It would be nice to skip this step but scrolltool doesn't use
5018 * the storage object routines to read from the file, so would
5019 * skip the translation step.
5025 if(!(out_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
5027 our_unlink(filename
);
5028 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5029 _("Error allocating space."));
5033 so_seek(in_file
, 0L, 0);
5037 gf_link_filter(gf_wrap
,
5038 gf_wrap_filter_opt(ps_global
->ttyo
->screen_cols
- 4,
5039 ps_global
->ttyo
->screen_cols
,
5040 NULL
, 0, GFW_NONE
));
5042 gf_set_so_readc(&gc
, in_file
);
5043 gf_set_so_writec(&pc
, out_store
);
5045 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
5047 so_give(&out_store
);
5048 our_unlink(filename
);
5049 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5050 _("Error allocating space."));
5054 gf_clear_so_writec(out_store
);
5055 gf_clear_so_readc(in_file
);
5063 snprintf(title_buf
, sizeof(title_buf
), "HELP FOR %s VIEW", title
);
5064 title_buf
[sizeof(title_buf
)-1] = '\0';
5066 memset(&sargs
, 0, sizeof(SCROLL_S
));
5067 sargs
.text
.text
= so_text(out_store
);
5068 sargs
.text
.src
= CharStar
;
5069 sargs
.text
.desc
= "output";
5070 sargs
.bar
.title
= title
;
5071 sargs
.bar
.style
= TextPercent
;
5072 sargs
.help
.text
= h_simple_text_view
;
5073 sargs
.help
.title
= title_buf
;
5075 ps_global
->mangled_screen
= 1;
5076 so_give(&out_store
);
5079 our_unlink(filename
);
5082 dprint((2, "Error reopening %s to get results: %s\n",
5083 filename
? filename
: "?", error_description(errno
)));
5087 /*--------------------------------------------------------------------
5088 Call the function that will perform the double click operation
5090 Returns: the current top line
5093 doubleclick_handle(SCROLL_S
*sparms
, HANDLE_S
*next_handle
, int *cmd
, int *done
)
5095 if(sparms
->mouse
.clickclick
){
5096 if((*sparms
->mouse
.clickclick
)(sparms
))
5100 switch(scroll_handle_launch(next_handle
, TRUE
)){
5102 *cmd
= MC_EXIT
; /* propagate */
5107 cmd_cancelled("View");
5114 return(scroll_state(SS_CUR
)->top_text_line
);
5121 * Just a little something to simplify assignments
5123 #define VIEWPOPUP(p, c, s) { \
5124 (p)->type = tQueue; \
5125 (p)->data.val = c; \
5126 (p)->label.style = lNormal; \
5127 (p)->label.string = s; \
5135 format_message_popup(sparms
, in_handle
)
5139 MPopup fmp_menu
[32];
5145 /* Reason to offer per message ops? */
5146 if(mn_get_total(ps_global
->msgmap
) > 0L){
5148 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5150 switch((h
= get_handle(st
->parms
->text
.handles
, in_handle
))->type
){
5152 fmp_menu
[++i
].type
= tIndex
;
5153 fmp_menu
[i
].label
.string
= "View Attachment";
5154 fmp_menu
[i
].label
.style
= lNormal
;
5155 fmp_menu
[i
].data
.val
= 'X'; /* for local use */
5158 && dispatch_attachment(h
->h
.attach
) != MCD_NONE
5159 && !(h
->h
.attach
->can_display
& MCD_EXTERNAL
)
5160 && h
->h
.attach
->body
5161 && (h
->h
.attach
->body
->type
== TYPETEXT
5162 || (h
->h
.attach
->body
->type
== TYPEMESSAGE
5163 && h
->h
.attach
->body
->subtype
5164 && !strucmp(h
->h
.attach
->body
->subtype
,"rfc822")))){
5165 fmp_menu
[++i
].type
= tIndex
;
5166 fmp_menu
[i
].label
.string
= "View Attachment in New Window";
5167 fmp_menu
[i
].label
.style
= lNormal
;
5168 fmp_menu
[i
].data
.val
= 'Y'; /* for local use */
5171 fmp_menu
[++i
].type
= tIndex
;
5172 fmp_menu
[i
].label
.style
= lNormal
;
5173 fmp_menu
[i
].data
.val
= 'Z'; /* for local use */
5174 msgno_exceptions(ps_global
->mail_stream
,
5175 mn_m2raw(ps_global
->msgmap
,
5176 mn_get_cur(ps_global
->msgmap
)),
5177 h
->h
.attach
->number
, &n
, FALSE
);
5178 fmp_menu
[i
].label
.string
= (n
& MSG_EX_DELETE
)
5179 ? "Undelete Attachment"
5180 : "Delete Attachment";
5185 fmp_menu
[++i
].type
= tIndex
;
5186 fmp_menu
[i
].label
.string
= "View Link";
5187 fmp_menu
[i
].label
.style
= lNormal
;
5188 fmp_menu
[i
].data
.val
= 'X'; /* for local use */
5190 fmp_menu
[++i
].type
= tIndex
;
5191 fmp_menu
[i
].label
.string
= "Copy Link";
5192 fmp_menu
[i
].label
.style
= lNormal
;
5193 fmp_menu
[i
].data
.val
= 'W'; /* for local use */
5197 fmp_menu
[++i
].type
= tSeparator
;
5200 /* Delete or Undelete? That is the question. */
5201 fmp_menu
[++i
].type
= tQueue
;
5202 fmp_menu
[i
].label
.style
= lNormal
;
5203 mc
= ((rawno
= mn_m2raw(ps_global
->msgmap
,
5204 mn_get_cur(ps_global
->msgmap
))) > 0L
5205 && ps_global
->mail_stream
5206 && rawno
<= ps_global
->mail_stream
->nmsgs
)
5207 ? mail_elt(ps_global
->mail_stream
, rawno
) : NULL
;
5208 if(mc
&& mc
->deleted
){
5209 fmp_menu
[i
].data
.val
= 'U';
5210 fmp_menu
[i
].label
.string
= in_handle
5211 ? "&Undelete Message" : "&Undelete";
5214 fmp_menu
[i
].data
.val
= 'D';
5215 fmp_menu
[i
].label
.string
= in_handle
5216 ? "&Delete Message" : "&Delete";
5219 if(F_ON(F_ENABLE_FLAG
, ps_global
)){
5220 fmp_menu
[++i
].type
= tSubMenu
;
5221 fmp_menu
[i
].label
.string
= "Flag";
5222 fmp_menu
[i
].data
.submenu
= flag_submenu(mc
);
5226 VIEWPOPUP(&fmp_menu
[i
], 'S', in_handle
? "&Save Message" : "&Save");
5229 VIEWPOPUP(&fmp_menu
[i
], 'E', in_handle
? "&Export Message" : "&Export");
5232 VIEWPOPUP(&fmp_menu
[i
], '%', in_handle
? "Print Message" : "Print");
5235 VIEWPOPUP(&fmp_menu
[i
], 'R',
5236 in_handle
? "&Reply to Message" : "&Reply");
5239 VIEWPOPUP(&fmp_menu
[i
], 'F',
5240 in_handle
? "&Forward Message" : "&Forward");
5243 VIEWPOPUP(&fmp_menu
[i
], 'B',
5244 in_handle
? "&Bounce Message" : "&Bounce");
5247 VIEWPOPUP(&fmp_menu
[i
], 'T', "&Take Addresses");
5249 fmp_menu
[++i
].type
= tSeparator
;
5251 if(mn_get_cur(ps_global
->msgmap
) < mn_get_total(ps_global
->msgmap
)){
5253 VIEWPOPUP(&fmp_menu
[i
], 'N', "View &Next Message");
5256 if(mn_get_cur(ps_global
->msgmap
) > 0){
5258 VIEWPOPUP(&fmp_menu
[i
], 'P', "View &Prev Message");
5261 if(mn_get_cur(ps_global
->msgmap
) < mn_get_total(ps_global
->msgmap
)
5262 || mn_get_cur(ps_global
->msgmap
) > 0)
5263 fmp_menu
[++i
].type
= tSeparator
;
5265 /* Offer the attachment screen? */
5266 for(n
= 0; ps_global
->atmts
&& ps_global
->atmts
[n
].description
; n
++)
5271 VIEWPOPUP(&fmp_menu
[i
], 'V', "&View Attachment Index");
5276 VIEWPOPUP(&fmp_menu
[i
], 'I', "Message &Index");
5279 VIEWPOPUP(&fmp_menu
[i
], 'M', "&Main Menu");
5281 fmp_menu
[++i
].type
= tTail
;
5283 if((i
= mswin_popup(fmp_menu
)) >= 0 && in_handle
)
5284 switch(fmp_menu
[i
].data
.val
){
5285 case 'W' : /* Copy URL to clipboard */
5286 mswin_addclipboard(h
->h
.url
.path
);
5290 return(1); /* return like the user double-clicked */
5294 case 'Y' : /* popup the thing in another window */
5295 display_att_window(h
->h
.attach
);
5299 if(h
&& h
->type
== Attach
){
5300 msgno_exceptions(ps_global
->mail_stream
,
5301 mn_m2raw(ps_global
->msgmap
,
5302 mn_get_cur(ps_global
->msgmap
)),
5303 h
->h
.attach
->number
, &n
, FALSE
);
5305 msgno_exceptions(ps_global
->mail_stream
,
5306 mn_m2raw(ps_global
->msgmap
,
5307 mn_get_cur(ps_global
->msgmap
)),
5308 h
->h
.attach
->number
, &n
, TRUE
);
5309 q_status_message2(SM_ORDER
, 0, 3, "Attachment %s %s!",
5310 h
->h
.attach
->number
,
5311 (n
& MSG_EX_DELETE
) ? "deleted" : "undeleted");
5331 simple_text_popup(sparms
, in_handle
)
5335 MPopup simple_menu
[12];
5338 VIEWPOPUP(&simple_menu
[n
], '%', "Print");
5341 VIEWPOPUP(&simple_menu
[n
], 'S', "Save");
5344 VIEWPOPUP(&simple_menu
[n
], 'F', "Forward in Email");
5347 simple_menu
[n
++].type
= tSeparator
;
5349 VIEWPOPUP(&simple_menu
[n
], 'E', "Exit Viewer");
5352 simple_menu
[n
].type
= tTail
;
5354 (void) mswin_popup(simple_menu
);
5360 /*----------------------------------------------------------------------
5361 Return characters in scroll tool buffer serially
5363 Args: n -- index of char to return
5365 Returns: returns the character at index 'n', or -1 on error or
5370 mswin_readscrollbuf(n
)
5373 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5375 static char **orig
= NULL
, **l
, *p
;
5382 * All of these are mind-numbingly slow at the moment...
5384 switch(st
->parms
->text
.src
){
5386 return((n
>= strlen((char *)st
->parms
->text
.text
))
5387 ? -1 : ((char *)st
->parms
->text
.text
)[n
]);
5390 /* BUG? is this test rigorous enough? */
5391 if(orig
!= (char **)st
->parms
->text
.text
|| n
< lastn
){
5393 if(orig
= l
= (char **)st
->parms
->text
.text
) /* reset l and p */
5396 else{ /* use cached l and p */
5397 c
= n
; /* and adjust n */
5402 while(l
){ /* look for 'n' on each line */
5403 for(; n
&& *p
; n
--, p
++)
5406 if(n
--) /* 'n' found ? */
5412 return((l
&& *l
) ? *p
? *p
: '\n' : -1);
5415 return((fseek((FILE *)st
->parms
->text
.text
, (long) n
, 0) < 0
5416 || (c
= fgetc((FILE *)st
->parms
->text
.text
)) == EOF
) ? -1 : c
);
5425 /*----------------------------------------------------------------------
5426 MSWin scroll callback. Called during scroll message processing.
5430 Args: cmd - what type of scroll operation.
5431 scroll_pos - paramter for operation.
5432 used as position for SCROLL_TO operation.
5434 Returns: TRUE - did the scroll operation.
5435 FALSE - was not able to do the scroll operation.
5438 pcpine_do_scroll (cmd
, scroll_pos
)
5442 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5443 HANDLE_S
*next_handle
;
5445 int num_display_lines
;
5452 maxscroll
= st
->num_lines
;
5454 case MSWIN_KEY_SCROLLUPLINE
:
5455 if(st
->top_text_line
> 0) {
5456 st
->top_text_line
-= (int) scroll_pos
;
5458 if (st
->top_text_line
<= 0){
5459 snprintf(message
, sizeof(message
), "START of %.*s",
5460 32, STYLE_NAME(st
->parms
));
5461 message
[sizeof(message
)-1] = '\0';
5462 st
->top_text_line
= 0;
5467 case MSWIN_KEY_SCROLLDOWNLINE
:
5468 if(st
->top_text_line
< maxscroll
) {
5469 st
->top_text_line
+= (int) scroll_pos
;
5471 if (st
->top_text_line
>= maxscroll
){
5472 snprintf(message
, sizeof(message
), "END of %.*s", 32, STYLE_NAME(st
->parms
));
5473 message
[sizeof(message
)-1] = '\0';
5474 st
->top_text_line
= maxscroll
;
5479 case MSWIN_KEY_SCROLLUPPAGE
:
5480 if(st
->top_text_line
> 0) {
5481 num_display_lines
= SCROLL_LINES(ps_global
);
5482 scroll_lines
= MIN(MAX(num_display_lines
-
5483 ps_global
->viewer_overlap
, 1), num_display_lines
);
5484 if (st
->top_text_line
> scroll_lines
)
5485 st
->top_text_line
-= scroll_lines
;
5487 st
->top_text_line
= 0;
5488 snprintf(message
, sizeof(message
), "START of %.*s", 32, STYLE_NAME(st
->parms
));
5489 message
[sizeof(message
)-1] = '\0';
5495 case MSWIN_KEY_SCROLLDOWNPAGE
:
5496 num_display_lines
= SCROLL_LINES(ps_global
);
5497 if(st
->top_text_line
< maxscroll
) {
5498 scroll_lines
= MIN(MAX(num_display_lines
-
5499 ps_global
->viewer_overlap
, 1), num_display_lines
);
5500 st
->top_text_line
+= scroll_lines
;
5501 if (st
->top_text_line
>= maxscroll
) {
5502 st
->top_text_line
= maxscroll
;
5503 snprintf(message
, sizeof(message
), "END of %.*s", 32, STYLE_NAME(st
->parms
));
5504 message
[sizeof(message
)-1] = '\0';
5510 case MSWIN_KEY_SCROLLTO
:
5511 if (st
->top_text_line
!= scroll_pos
) {
5512 st
->top_text_line
= scroll_pos
;
5513 if (st
->top_text_line
== 0)
5514 snprintf(message
, sizeof(message
), "START of %.*s", 32, STYLE_NAME(st
->parms
));
5515 else if(st
->top_text_line
>= maxscroll
)
5516 snprintf(message
, sizeof(message
), "END of %.*s", 32, STYLE_NAME(st
->parms
));
5518 message
[sizeof(message
)-1] = '\0';
5524 /* Need to frame some handles? */
5525 if(st
->parms
->text
.handles
5526 && (next_handle
= scroll_handle_in_frame(st
->top_text_line
)))
5527 st
->parms
->text
.handles
= next_handle
;
5530 mswin_beginupdate();
5531 update_scroll_titlebar(st
->top_text_line
, 0);
5532 (void) scroll_scroll_text(st
->top_text_line
,
5533 st
->parms
->text
.handles
, 1);
5535 q_status_message(SM_INFO
, 0, 1, message
);
5537 /* Display is always called so that the "START(END) of message"
5538 * message gets removed when no longer at the start(end). */
5539 display_message (KEY_PGDN
);
5548 pcpine_help_scroll(title
)
5551 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5554 strncpy(title
, (st
->parms
->help
.title
)
5555 ? st
->parms
->help
.title
: "Alpine Help", 256);
5557 return(pcpine_help(st
->parms
->help
.text
));
5562 pcpine_view_cursor(col
, row
)
5566 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5570 return((row
>= HEADER_ROWS(ps_global
)
5571 && row
< HEADER_ROWS(ps_global
) + SCROLL_LINES(ps_global
)
5572 && (line
= (row
- 2) + st
->top_text_line
) < st
->num_lines
5573 && (key
= dot_on_handle(line
, col
))
5574 && scroll_handle_selectable(get_handle(st
->parms
->text
.handles
,key
)))
5576 : MSWIN_CURSOR_ARROW
);
5578 #endif /* _WINDOWS */