1 #if !defined(lint) && !defined(DOS)
2 static char rcsid
[] = "$Id: mailview.c 1266 2009-07-14 18:39:12Z hubert@u.washington.edu $";
6 * ========================================================================
7 * Copyright 2006-2008 University of Washington
8 * Copyright 2013-2018 Eduardo Chappa
10 * Licensed under the Apache License, Version 2.0 (the "License");
11 * you may not use this file except in compliance with the License.
12 * You may obtain a copy of the License at
14 * http://www.apache.org/licenses/LICENSE-2.0
16 * ========================================================================
19 /*======================================================================
23 Implements the mailview screen
24 Also includes scrolltool used to display help text
48 #include "../pith/conf.h"
49 #include "../pith/filter.h"
50 #include "../pith/msgno.h"
51 #include "../pith/escapes.h"
52 #include "../pith/flag.h"
53 #include "../pith/mimedesc.h"
54 #include "../pith/url.h"
55 #include "../pith/bldaddr.h"
56 #include "../pith/mailcmd.h"
57 #include "../pith/newmail.h"
58 #include "../pith/pipe.h"
59 #include "../pith/thread.h"
60 #include "../pith/util.h"
61 #include "../pith/detoken.h"
62 #include "../pith/editorial.h"
63 #include "../pith/maillist.h"
64 #include "../pith/hist.h"
65 #include "../pith/busy.h"
66 #include "../pith/list.h"
67 #include "../pith/detach.h"
70 /*----------------------------------------------------------------------
71 Saved state for scrolling text
73 typedef struct scroll_text
{
74 SCROLL_S
*parms
; /* Original text (file, char *, char **) */
75 char **text_lines
, /* Lines to display */
76 *fname
; /* filename of line offsets in "text" */
77 FILE *findex
; /* file pointer to line offsets in "text" */
78 short *line_lengths
; /* Length of each line in "text_lines" */
79 long top_text_line
, /* index in "text_lines" top displayed line */
80 num_lines
; /* number of valid pointers in "text_lines" */
81 int lines_allocated
; /* size of "text_lines" array */
83 int length
, /* count of displayable lines (== PGSIZE) */
84 width
, /* width of displayable lines */
85 start_line
, /* line number to start painting on */
86 other_lines
; /* # of lines not for scroll text */
87 } screen
; /* screen parameters */
91 typedef struct scroll_file
{
98 * Struct to help write lines do display as they're decoded
100 struct view_write_s
{
112 #define LINEBUFSIZ (4096)
114 #define MAX_FUDGE (1024*1024)
117 * Definitions to help scrolltool
119 #define SCROLL_LINES_ABOVE(X) HEADER_ROWS(X)
120 #define SCROLL_LINES_BELOW(X) FOOTER_ROWS(X)
121 #define SCROLL_LINES(X) MAX(((X)->ttyo->screen_rows \
122 - SCROLL_LINES_ABOVE(X) - SCROLL_LINES_BELOW(X)), 0)
123 #define scroll_text_lines() (scroll_state(SS_CUR)->num_lines)
127 * Definitions for various scroll state manager's functions
137 #define HANDLE_INIT_MSG \
138 _("Selectable items in text -- Use Up/Down Arrows to choose, Return to view")
139 #define HANDLE_ABOVE_ERR \
140 _("No selected item displayed -- Use PrevPage to bring choice into view")
141 #define HANDLE_BELOW_ERR \
142 _("No selected item displayed -- Use NextPage to bring choice into view")
145 #define PGSIZE(X) (ps_global->ttyo->screen_rows - (X)->screen.other_lines)
147 #define TYPICAL_BIG_MESSAGE_LINES 200
151 * Internal prototypes
153 void view_writec_killbuf(void);
154 int view_end_scroll(SCROLL_S
*);
155 long format_size_guess(BODY
*);
156 int scroll_handle_prompt(HANDLE_S
*, int);
157 int scroll_handle_launch(HANDLE_S
*, int);
158 int scroll_handle_obscured(HANDLE_S
*);
159 HANDLE_S
*scroll_handle_in_frame(long);
160 long scroll_handle_reframe(int, int);
161 int handle_on_line(long, int);
162 int handle_on_page(HANDLE_S
*, long, long);
163 int scroll_handle_selectable(HANDLE_S
*);
164 HANDLE_S
*scroll_handle_next_sel(HANDLE_S
*);
165 HANDLE_S
*scroll_handle_prev_sel(HANDLE_S
*);
166 HANDLE_S
*scroll_handle_next(HANDLE_S
*);
167 HANDLE_S
*scroll_handle_prev(HANDLE_S
*);
168 HANDLE_S
*scroll_handle_boundary(HANDLE_S
*, HANDLE_S
*(*)(HANDLE_S
*));
169 int scroll_handle_column(int, int);
170 int scroll_handle_index(int, int);
171 void scroll_handle_set_loc(POSLIST_S
**, int, int);
172 int dot_on_handle(long, int);
173 int url_launch(HANDLE_S
*);
174 int url_launch_too_long(int);
175 char *url_external_handler(HANDLE_S
*, int);
176 void url_mailto_addr(ADDRESS
**, char *);
177 int ical_send_reply(char *);
178 int url_local_phone_home(char *);
179 int url_local_imap(char *);
180 int url_local_nntp(char *);
181 int url_local_news(char *);
182 int url_local_file(char *);
183 static int print_to_printer(SCROLL_S
*);
184 int search_text(int, long, int, char **, Pos
*, int *);
185 void update_scroll_titlebar(long, int);
186 SCRLCTRL_S
*scroll_state(int);
187 void set_scroll_text(SCROLL_S
*, long, SCRLCTRL_S
*);
188 void redraw_scroll_text(void);
189 void zero_scroll_text(void);
190 void format_scroll_text(void);
191 void ScrollFile(long);
192 long make_file_index(void);
193 char *scroll_file_line(FILE *, char *, SCRLFILE_S
*, int *);
194 long scroll_scroll_text(long, HANDLE_S
*, int);
195 int search_scroll_text(long, int, char *, Pos
*, int *);
196 char *search_scroll_line(char *, char *, int, int);
197 int visible_linelen(int);
198 long doubleclick_handle(SCROLL_S
*, HANDLE_S
*, int *, int *);
200 int format_message_popup(SCROLL_S
*, int);
201 int simple_text_popup(SCROLL_S
*, int);
202 int mswin_readscrollbuf(int);
203 int pcpine_do_scroll(int, long);
204 char *pcpine_help_scroll(char *);
205 int pcpine_view_cursor(int, long);
210 /*----------------------------------------------------------------------
211 Format a buffer with the text of the current message for browser
213 Args: ps - pine state structure
215 Result: The scrolltool is called to display the message
217 Loop here viewing mail until the folder changed or a command takes
218 us to another screen. Inside the loop the message text is fetched and
219 formatted into a buffer allocated for it. These are passed to the
220 scrolltool(), that displays the message and executes commands. It
221 returns when it's time to display a different message, when we
222 change folders, when it's time for a different screen, or when
223 there are no more messages available.
227 mail_view_screen(struct pine
*ps
)
229 char last_was_full_header
= 0;
230 long last_message_viewed
= -1L, raw_msgno
, offset
= 0L;
231 OtherMenu save_what
= FirstMenu
;
232 int we_cancel
= 0, flags
, cmd
= 0;
233 int force_prefer
= 0;
238 HANDLE_S
*handles
= NULL
;
240 SourceType src
= CharStar
;
242 dprint((1, "\n\n ----- MAIL VIEW -----\n"));
244 ps
->prev_screen
= mail_view_screen
;
245 ps
->force_prefer_plain
= ps
->force_no_prefer_plain
= 0;
247 if(ps
->ttyo
->screen_rows
- HEADER_ROWS(ps
) - FOOTER_ROWS(ps
) < 1){
248 q_status_message(SM_ORDER
| SM_DING
, 0, 3,
249 _("Screen too small to view message"));
250 ps
->next_screen
= mail_index_screen
;
254 /*----------------- Loop viewing messages ------------------*/
257 ps
->user_says_cancel
= 0;
258 ps
->some_quoting_was_suppressed
= 0;
261 * Check total to make sure there's something to view. Check it
262 * inside the loop to make sure everything wasn't expunged while
263 * we were viewing. If so, make sure we don't just come back.
265 if(mn_get_total(ps
->msgmap
) <= 0L || !ps
->mail_stream
){
266 q_status_message(SM_ORDER
, 0, 3, _("No messages to read!"));
267 ps
->next_screen
= mail_index_screen
;
271 we_cancel
= busy_cue(NULL
, NULL
, 1);
273 if(mn_get_cur(ps
->msgmap
) <= 0L)
274 mn_set_cur(ps
->msgmap
,
275 THREADING() ? first_sorted_flagged(F_NONE
,
280 raw_msgno
= mn_m2raw(ps
->msgmap
, mn_get_cur(ps
->msgmap
));
283 || !(env
= pine_mail_fetchstructure(ps
->mail_stream
,raw_msgno
,&body
))
284 || !(raw_msgno
> 0L && ps
->mail_stream
285 && raw_msgno
<= ps
->mail_stream
->nmsgs
286 && (mc
= mail_elt(ps
->mail_stream
, raw_msgno
)))){
287 q_status_message1(SM_ORDER
, 3, 3,
288 "Error getting message %s data",
289 comatose(mn_get_cur(ps
->msgmap
)));
290 dprint((1, "!!!! ERROR fetching %s of msg %ld\n",
291 env
? "elt" : "env", mn_get_cur(ps
->msgmap
)));
293 ps
->next_screen
= mail_index_screen
;
297 ps
->unseen_in_view
= !mc
->seen
;
299 init_handles(&handles
);
301 store
= so_get(src
, NULL
, EDIT_ACCESS
);
302 so_truncate(store
, format_size_guess(body
) + 2048);
304 view_writec_init(store
, &handles
, SCROLL_LINES_ABOVE(ps
),
305 SCROLL_LINES_ABOVE(ps
) +
306 ps
->ttyo
->screen_rows
- (SCROLL_LINES_ABOVE(ps
)
307 + SCROLL_LINES_BELOW(ps
)));
310 if(last_message_viewed
!= mn_get_cur(ps
->msgmap
)
311 || last_was_full_header
== 2 || cmd
== MC_TOGGLE
)
312 flags
|= FM_NEW_MESS
;
314 if(F_OFF(F_QUELL_FULL_HDR_RESET
, ps_global
)
315 && last_message_viewed
!= -1L
316 && last_message_viewed
!= mn_get_cur(ps
->msgmap
))
319 if(offset
) /* no pre-paint during resize */
320 view_writec_killbuf();
323 /* Attempt to handle S/MIME bodies */
325 ps
->smime
->need_passphrase
= 0;
327 if(F_OFF(F_DONT_DO_SMIME
, ps_global
) && fiddle_smime_message(body
, raw_msgno
))
328 flags
|= FM_NEW_MESS
; /* body was changed, force a reload */
332 mswin_noscrollupdate(1);
334 ps
->cur_uid_stream
= ps
->mail_stream
;
335 ps
->cur_uid
= mail_uid(ps
->mail_stream
, raw_msgno
);
336 (void) format_message(raw_msgno
, env
, body
, &handles
, flags
| force_prefer
,
339 mswin_noscrollupdate(0);
342 view_writec_destroy();
344 last_message_viewed
= mn_get_cur(ps
->msgmap
);
345 last_was_full_header
= ps
->full_header
;
347 ps
->next_screen
= SCREEN_FUN_NULL
;
349 memset(&scrollargs
, 0, sizeof(SCROLL_S
));
350 scrollargs
.text
.text
= so_text(store
);
351 scrollargs
.text
.src
= src
;
352 scrollargs
.text
.desc
= "message";
355 * make first selectable handle the default
361 while(!scroll_handle_selectable(hp
) && hp
!= NULL
)
364 if((scrollargs
.text
.handles
= hp
) != NULL
)
365 scrollargs
.body_valid
= (hp
== handles
);
367 free_handles(&handles
);
370 scrollargs
.body_valid
= 1;
372 if(offset
){ /* resize? preserve paging! */
373 scrollargs
.start
.on
= Offset
;
374 scrollargs
.start
.loc
.offset
= offset
;
375 scrollargs
.body_valid
= 0;
379 scrollargs
.use_indexline_color
= 1;
381 /* TRANSLATORS: a screen title */
382 scrollargs
.bar
.title
= _("MESSAGE TEXT");
383 scrollargs
.end_scroll
= view_end_scroll
;
384 scrollargs
.resize_exit
= 1;
385 scrollargs
.help
.text
= h_mail_view
;
386 scrollargs
.help
.title
= _("HELP FOR MESSAGE TEXT VIEW");
387 scrollargs
.keys
.menu
= &view_keymenu
;
388 scrollargs
.keys
.what
= save_what
;
389 setbitmap(scrollargs
.keys
.bitmap
);
390 if(F_OFF(F_ENABLE_PIPE
, ps_global
))
391 clrbitn(VIEW_PIPE_KEY
, scrollargs
.keys
.bitmap
);
394 * turn off attachment viewing for raw msg txt, atts
395 * haven't been set up at this point
397 if(ps_global
->full_header
== 2
398 && F_ON(F_ENABLE_FULL_HDR_AND_TEXT
, ps_global
))
399 clrbitn(VIEW_ATT_KEY
, scrollargs
.keys
.bitmap
);
401 if(F_OFF(F_ENABLE_BOUNCE
, ps_global
))
402 clrbitn(BOUNCE_KEY
, scrollargs
.keys
.bitmap
);
404 if(F_OFF(F_ENABLE_FLAG
, ps_global
))
405 clrbitn(FLAG_KEY
, scrollargs
.keys
.bitmap
);
407 if(F_OFF(F_ENABLE_AGG_OPS
, ps_global
))
408 clrbitn(VIEW_SELECT_KEY
, scrollargs
.keys
.bitmap
);
410 if(F_OFF(F_ENABLE_FULL_HDR
, ps_global
))
411 clrbitn(VIEW_FULL_HEADERS_KEY
, scrollargs
.keys
.bitmap
);
414 if(!(ps
->smime
&& ps
->smime
->need_passphrase
))
415 clrbitn(DECRYPT_KEY
, scrollargs
.keys
.bitmap
);
417 if(F_ON(F_DONT_DO_SMIME
, ps_global
)){
418 clrbitn(DECRYPT_KEY
, scrollargs
.keys
.bitmap
);
419 clrbitn(SECURITY_KEY
, scrollargs
.keys
.bitmap
);
425 * NOTE: the comment below only really makes sense if we
426 * decide to replace the "Attachment Index" with
427 * a model that lets you highlight the attachments
428 * in the header. In a way its consistent, but
429 * would mean more keymenu monkeybusiness since not
430 * all things would be available all the time. No wait.
431 * Then what would "Save" mean; the attachment, url or
432 * message you're currently viewing? Would Save
433 * of a message then only be possible from the message
434 * index? Clumsy. what about arrow-navigation. isn't
435 * that now inconsistent? Maybe next/prev url shouldn't
436 * be bound to the arrow/^N/^P navigation?
438 clrbitn(VIEW_VIEW_HANDLE
, scrollargs
.keys
.bitmap
);
439 clrbitn(VIEW_PREV_HANDLE
, scrollargs
.keys
.bitmap
);
440 clrbitn(VIEW_NEXT_HANDLE
, scrollargs
.keys
.bitmap
);
444 scrollargs
.mouse
.popup
= format_message_popup
;
447 if(((cmd
= scrolltool(&scrollargs
)) == MC_RESIZE
448 || (cmd
== MC_FULLHDR
&& ps_global
->full_header
== 1))
449 && scrollargs
.start
.on
== Offset
)
450 offset
= scrollargs
.start
.loc
.offset
;
452 if(cmd
== MC_TOGGLE
&& ps
->force_prefer_plain
== 0 && ps
->force_no_prefer_plain
== 0){
453 if(F_ON(F_PREFER_PLAIN_TEXT
, ps_global
))
454 ps
->force_no_prefer_plain
= 1;
456 ps
->force_prefer_plain
= 1;
459 ps
->force_prefer_plain
= ps
->force_no_prefer_plain
= 0;
463 * We could use the values directly but this is the way it
464 * already worked with the flags, so leave it alone.
466 if(ps
->force_prefer_plain
== 0 && ps
->force_no_prefer_plain
== 0)
468 else if(ps
->force_prefer_plain
)
469 force_prefer
= FM_FORCEPREFPLN
;
470 else if(ps
->force_no_prefer_plain
)
471 force_prefer
= FM_FORCENOPREFPLN
;
473 save_what
= scrollargs
.keys
.what
;
474 ps_global
->unseen_in_view
= 0;
475 so_give(&store
); /* free resources associated with store */
476 free_handles(&handles
);
478 mswin_destroyicons();
481 while(ps
->next_screen
== SCREEN_FUN_NULL
);
486 ps
->force_prefer_plain
= ps
->force_no_prefer_plain
= 0;
488 ps
->cur_uid_stream
= NULL
;
492 * Unless we're going into attachment screen,
493 * start over with full_header.
495 if(F_OFF(F_QUELL_FULL_HDR_RESET
, ps_global
)
496 && ps
->next_screen
!= attachment_screen
)
503 * view_writec_init - function to create and init struct that manages
504 * writing to the display what we can as soon
508 view_writec_init(STORE_S
*store
, HANDLE_S
**handlesp
, int first_line
, int last_line
)
510 char tmp
[MAILTMPLEN
];
512 g_view_write
= (struct view_write_s
*)fs_get(sizeof(struct view_write_s
));
513 memset(g_view_write
, 0, sizeof(struct view_write_s
));
514 g_view_write
->store
= store
;
515 g_view_write
->handles
= handlesp
;
516 g_view_write
->screen_line
= first_line
;
517 g_view_write
->last_screen_line
= last_line
;
519 if(!dfilter_trigger(NULL
, tmp
, sizeof(tmp
))){
520 g_view_write
->line
= (char *) fs_get(LINEBUFSIZ
*sizeof(char));
523 scroll_setrange(0L, 0L);
526 if(ps_global
->VAR_DISPLAY_FILTERS
)
527 ClearLines(first_line
, last_line
- 1);
534 view_writec_destroy(void)
537 if(g_view_write
->line
&& g_view_write
->index
)
538 view_writec('\n'); /* flush pending output! */
540 while(g_view_write
->screen_line
< g_view_write
->last_screen_line
)
541 ClearLine(g_view_write
->screen_line
++);
543 view_writec_killbuf();
545 fs_give((void **) &g_view_write
);
556 view_writec_killbuf(void)
558 if(g_view_write
->line
)
559 fs_give((void **) &g_view_write
->line
);
565 * view_screen_pc - write chars into the final buffer
570 static int in_color
= 0;
572 if(g_view_write
->line
){
574 * This only works if the 2nd and 3rd parts of the || don't happen.
575 * The only way it breaks is if we get a really, really long line
576 * because there are oodles of tags in it. In that case we will
577 * wrap incorrectly or split a tag across lines (losing color perhaps)
578 * but we won't crash.
582 (char)c
!= TAG_EMBED
&&
583 g_view_write
->index
>= (LINEBUFSIZ
- 20 * (RGBLEN
+2))) ||
584 (g_view_write
->index
>= LINEBUFSIZ
- 1)){
589 ClearLine(g_view_write
->screen_line
);
591 g_view_write
->line
[g_view_write
->index
++] = (char) c
;
593 PutLine0n8b(g_view_write
->screen_line
++, 0,
594 g_view_write
->line
, g_view_write
->index
,
595 g_view_write
->handles
? *g_view_write
->handles
: NULL
);
598 rv
= so_nputs(g_view_write
->store
,
600 g_view_write
->index
);
601 g_view_write
->index
= 0;
603 if(g_view_write
->screen_line
>= g_view_write
->last_screen_line
){
604 fs_give((void **) &g_view_write
->line
);
615 * Don't split embedded things over multiple lines. Colors are
616 * the longest tags so use their length.
618 if((char)c
== TAG_EMBED
)
623 g_view_write
->line
[g_view_write
->index
++] = (char) c
;
628 else if(c
== '\n' && g_view_write
->lines
++ > SCROLL_LINES(ps_global
))
629 scroll_setrange(SCROLL_LINES(ps_global
),
630 g_view_write
->lines
+ SCROLL_LINES(ps_global
));
633 return(so_writec(c
, g_view_write
->store
));
637 view_end_scroll(SCROLL_S
*sparms
)
639 int done
= 0, result
, force
;
641 if(F_ON(F_ENABLE_SPACE_AS_TAB
, ps_global
)){
643 if(F_ON(F_ENABLE_TAB_DELETES
, ps_global
)){
646 /* Let the TAB advance cur msgno for us */
647 save_msgno
= mn_get_cur(ps_global
->msgmap
);
648 (void) cmd_delete(ps_global
, ps_global
->msgmap
, MCMD_NONE
, NULL
);
649 mn_set_cur(ps_global
->msgmap
, save_msgno
);
652 /* act like the user hit a TAB */
653 result
= process_cmd(ps_global
, ps_global
->mail_stream
,
654 ps_global
->msgmap
, MC_TAB
, View
, &force
);
665 * format_size_guess -- Run down the given body summing the text/plain
666 * pieces we're likely to display. It need only
667 * be a guess since this is intended for preallocating
668 * our display buffer...
671 format_size_guess(struct mail_bodystruct
*body
)
675 char *free_me
= NULL
;
678 if(body
->type
== TYPEMULTIPART
){
681 for(part
= body
->nested
.part
; part
; part
= part
->next
)
682 size
+= format_size_guess(&part
->body
);
684 else if(body
->type
== TYPEMESSAGE
685 && body
->subtype
&& !strucmp(body
->subtype
, "rfc822"))
686 size
= format_size_guess(body
->nested
.msg
->body
);
687 else if((!body
->type
|| body
->type
== TYPETEXT
)
688 && (!body
->subtype
|| !strucmp(body
->subtype
, "plain"))
689 && ((body
->disposition
.type
690 && !strucmp(body
->disposition
.type
, "inline"))
691 || !(free_me
= parameter_val(body
->parameter
, "name")))){
693 * Handles and colored quotes cause memory overhead. Figure about
694 * 100 bytes per level of quote per line and about 100 bytes per
695 * handle. Make a guess of 1 handle or colored quote per
696 * 10 lines of message. If we guess too low, we'll do a resize in
697 * so_cs_writec. Most of the overhead comes from the colors, so
698 * if we can see we don't do colors or don't have these features
699 * turned on, skip it.
701 if(pico_usingcolor() &&
702 ((ps_global
->VAR_QUOTE1_FORE_COLOR
&&
703 ps_global
->VAR_QUOTE1_BACK_COLOR
) ||
704 F_ON(F_VIEW_SEL_URL
, ps_global
) ||
705 F_ON(F_VIEW_SEL_URL_HOST
, ps_global
) ||
706 F_ON(F_SCAN_ADDR
, ps_global
)))
707 extra
= MIN(100/10 * body
->size
.lines
, MAX_FUDGE
);
709 size
= body
->size
.bytes
+ extra
;
713 fs_give((void **) &free_me
);
721 scroll_handle_prompt(HANDLE_S
*handle
, int force
)
723 char prompt
[256], tmp
[MAILTMPLEN
];
724 int rc
, flags
, local_h
;
725 static ESCKEY_S launch_opts
[] = {
726 /* TRANSLATORS: command names, editURL means user gets to edit a URL if they
727 want, editApp is edit application where they edit the application used to
729 {'y', 'y', "Y", N_("Yes")},
730 {'n', 'n', "N", N_("No")},
733 {0, 'u', "U", N_("editURL")},
734 {0, 'a', "A", N_("editApp")},
735 {-1, 0, NULL
, NULL
}};
737 if(handle
->type
== URL
){
738 launch_opts
[4].ch
= 'u';
740 if((!(local_h
= !struncmp(handle
->h
.url
.path
, "x-alpine-", 9))
741 || !(local_h
= !struncmp(handle
->h
.url
.path
, "x-pine-help", 11)))
742 && (handle
->h
.url
.tool
743 || ((local_h
= url_local_handler(handle
->h
.url
.path
) != NULL
)
744 && (handle
->h
.url
.tool
= url_external_handler(handle
,1)))
746 && (handle
->h
.url
.tool
= url_external_handler(handle
,0))))){
748 /* if NOT special DDE hack */
749 if(handle
->h
.url
.tool
[0] != '*')
751 if(ps_global
->vars
[V_BROWSER
].is_fixed
)
752 launch_opts
[5].ch
= -1;
754 launch_opts
[5].ch
= 'a';
757 launch_opts
[5].ch
= -1;
759 if(ps_global
->vars
[V_BROWSER
].is_fixed
){
760 q_status_message(SM_ORDER
, 3, 4,
761 _("URL-Viewer is disabled by sys-admin"));
765 /* TRANSLATORS: a question */
766 if(want_to(_("No URL-Viewer application defined. Define now"),
767 'y', 0, NO_HELP
, WT_SEQ_SENSITIVE
) == 'y'){
768 /* Prompt for the displayer? */
771 flags
= OE_APPEND_CURRENT
|
773 OE_KEEP_TRAILING_SPACE
;
775 rc
= optionally_enter(tmp
, -FOOTER_ROWS(ps_global
), 0,
778 NULL
, NO_HELP
, &flags
);
780 if((flags
& OE_USER_MODIFIED
) && *tmp
){
781 if(can_access(tmp
, EXECUTE_ACCESS
) == 0){
786 * Save it for next time...
788 for(l
= ps_global
->VAR_BROWSER
, n
= 0;
793 l
= (char **) fs_get((n
+2)*sizeof(char *));
795 ps_global
->VAR_BROWSER
796 && ps_global
->VAR_BROWSER
[n
];
798 l
[n
] = cpystr(ps_global
->VAR_BROWSER
[n
]);
800 l
[n
++] = cpystr(tmp
);
803 set_variable_list(V_BROWSER
, l
, TRUE
, Main
);
806 handle
->h
.url
.tool
= cpystr(tmp
);
810 q_status_message1(SM_ORDER
| SM_DING
, 2, 2,
811 _("Browser not found: %s"),
812 error_description(errno
));
819 else if(rc
== 1 || rc
== -1){
823 if(ps_global
->redrawer
)
824 (*ps_global
->redrawer
)();
835 launch_opts
[4].ch
= -1;
838 || (handle
->type
== URL
839 && (!struncmp(handle
->h
.url
.path
, "x-alpine-", 9)
840 || !struncmp(handle
->h
.url
.path
, "x-pine-help", 11))))
844 int sc
= ps_global
->ttyo
->screen_cols
;
847 * Customize the prompt for mailto, all the other schemes make
848 * sense if you just say View selected URL ...
850 if(handle
->type
== URL
&&
851 !struncmp(handle
->h
.url
.path
, "mailto:", 7))
852 snprintf(prompt
, sizeof(prompt
), "Compose mail to \"%.*s%s\" ? ",
853 (int) MIN(MAX(0,sc
- 25), sizeof(prompt
)-50), handle
->h
.url
.path
+7,
854 (strlen(handle
->h
.url
.path
+7) > MAX(0,sc
-25)) ? "..." : "");
856 snprintf(prompt
, sizeof(prompt
), "View selected %s %s%.*s%s ? ",
857 (handle
->type
== URL
) ? "URL" : "Attachment",
858 (handle
->type
== URL
) ? "\"" : "",
859 (int) MIN(MAX(0,sc
-27), sizeof(prompt
)-50),
860 (handle
->type
== URL
) ? handle
->h
.url
.path
: "",
861 (handle
->type
== URL
)
862 ? ((strlen(handle
->h
.url
.path
) > MAX(0,sc
-27))
863 ? "...\"" : "\"") : "");
865 prompt
[sizeof(prompt
)-1] = '\0';
867 switch(radio_buttons(prompt
, -FOOTER_ROWS(ps_global
),
868 launch_opts
, 'y', 'n', NO_HELP
, RB_SEQ_SENSITIVE
)){
873 strncpy(tmp
, handle
->h
.url
.path
, sizeof(tmp
)-1);
874 tmp
[sizeof(tmp
)-1] = '\0';
876 flags
= OE_APPEND_CURRENT
|
878 OE_KEEP_TRAILING_SPACE
;
880 rc
= optionally_enter(tmp
, -FOOTER_ROWS(ps_global
), 0,
881 sizeof(tmp
), _("Edit URL: "),
882 NULL
, NO_HELP
, &flags
);
884 if(flags
& OE_USER_MODIFIED
){
885 if(handle
->h
.url
.path
)
886 fs_give((void **) &handle
->h
.url
.path
);
888 handle
->h
.url
.path
= cpystr(tmp
);
893 else if(rc
== 1 || rc
== -1){
897 if(ps_global
->redrawer
)
898 (*ps_global
->redrawer
)();
905 if(handle
->h
.url
.tool
){
906 strncpy(tmp
, handle
->h
.url
.tool
, sizeof(tmp
)-1);
907 tmp
[sizeof(tmp
)-1] = '\0';
913 flags
= OE_APPEND_CURRENT
|
915 OE_KEEP_TRAILING_SPACE
|
918 rc
= optionally_enter(tmp
, -FOOTER_ROWS(ps_global
), 0,
919 sizeof(tmp
), _("Viewer Command: "),
920 NULL
, NO_HELP
, &flags
);
922 if(flags
& OE_USER_MODIFIED
){
923 if(handle
->h
.url
.tool
)
924 fs_give((void **) &handle
->h
.url
.tool
);
926 handle
->h
.url
.tool
= cpystr(tmp
);
931 else if(rc
== 1 || rc
== -1){
935 if(ps_global
->redrawer
)
936 (*ps_global
->redrawer
)();
952 scroll_handle_launch(HANDLE_S
*handle
, int force
)
954 switch(handle
->type
){
956 if(handle
->h
.url
.path
){
957 if(scroll_handle_prompt(handle
, force
)){
958 if(url_launch(handle
)
959 || ps_global
->next_screen
!= SCREEN_FUN_NULL
)
960 return(1); /* done with this screen */
969 if(scroll_handle_prompt(handle
, force
))
970 display_attachment(mn_m2raw(ps_global
->msgmap
,
971 mn_get_cur(ps_global
->msgmap
)),
972 handle
->h
.attach
, DA_FROM_VIEW
| DA_DIDPROMPT
);
982 display_vevent_summary(mn_m2raw(ps_global
->msgmap
, mn_get_cur(ps_global
->msgmap
)),
983 handle
->h
.ical
.attach
,
984 DA_FROM_VIEW
| DA_DIDPROMPT
, handle
->h
.ical
.depth
);
988 (*handle
->h
.func
.f
)(handle
->h
.func
.args
.stream
,
989 handle
->h
.func
.args
.msgmap
,
990 handle
->h
.func
.args
.msgno
);
995 alpine_panic("Unexpected HANDLE type");
1003 scroll_handle_obscured(HANDLE_S
*handle
)
1005 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1007 return(handle_on_page(handle
, st
->top_text_line
,
1008 st
->top_text_line
+ st
->screen
.length
));
1014 * scroll_handle_in_frame -- return handle pointer to visible handle.
1017 scroll_handle_in_frame(long int top_line
)
1019 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1022 switch(handle_on_page(hp
= st
->parms
->text
.handles
, top_line
,
1023 top_line
+ st
->screen
.length
)){
1024 case -1 : /* handle above page */
1025 /* Find first handle from top of page */
1026 for(hp
= st
->parms
->text
.handles
->next
; hp
; hp
= hp
->next
)
1027 if(scroll_handle_selectable(hp
))
1028 switch(handle_on_page(hp
, top_line
, top_line
+ st
->screen
.length
)){
1029 case 0 : return(hp
);
1030 case 1 : return(NULL
);
1031 case -1 : default : break;
1036 case 1 : /* handle below page */
1037 /* Find first handle from top of page */
1038 for(hp
= st
->parms
->text
.handles
->prev
; hp
; hp
= hp
->prev
)
1039 if(scroll_handle_selectable(hp
))
1040 switch(handle_on_page(hp
, top_line
, top_line
+ st
->screen
.length
)){
1041 case 0 : return(hp
);
1042 case -1 : return(NULL
);
1043 case 1 : default : break;
1057 * scroll_handle_reframe -- adjust display params to display given handle
1060 scroll_handle_reframe(int key
, int center
)
1062 long l
, offset
, dlines
, start_line
;
1063 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1065 dlines
= PGSIZE(st
);
1066 offset
= (st
->parms
->text
.src
== FileStar
) ? st
->top_text_line
: 0;
1067 start_line
= st
->top_text_line
;
1070 key
= st
->parms
->text
.handles
->key
;
1072 /* Searc down from the top line */
1073 for(l
= start_line
; l
< st
->num_lines
; l
++) {
1074 if(st
->parms
->text
.src
== FileStar
&& l
> offset
+ dlines
)
1075 ScrollFile(offset
+= dlines
);
1077 if(handle_on_line(l
- offset
, key
))
1081 if(l
< st
->num_lines
){
1082 if(l
>= dlines
+ start_line
) /* bingo! */
1083 start_line
= l
- ((center
? (dlines
/ 2) : dlines
) - 1);
1086 if(st
->parms
->text
.src
== FileStar
) /* wrap offset */
1087 ScrollFile(offset
= 0);
1089 for(l
= 0; l
< start_line
; l
++) {
1090 if(st
->parms
->text
.src
== FileStar
&& l
> offset
+ dlines
)
1091 ScrollFile(offset
+= dlines
);
1093 if(handle_on_line(l
- offset
, key
))
1098 alpine_panic("Internal Error: no handle found");
1108 handle_on_line(long int line
, int goal
)
1111 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1113 for(i
= 0; i
< st
->line_lengths
[line
]; i
++)
1114 if(st
->text_lines
[line
][i
] == TAG_EMBED
1115 && st
->text_lines
[line
][++i
] == TAG_HANDLE
){
1116 for(key
= 0, n
= st
->text_lines
[line
][++i
]; n
; n
--)
1117 key
= (key
* 10) + (st
->text_lines
[line
][++i
] - '0');
1128 handle_on_page(HANDLE_S
*handle
, long int first_line
, long int last_line
)
1133 if(handle
&& (l
= handle
->loc
)){
1134 for( ; l
; l
= l
->next
)
1135 if(l
->where
.row
< first_line
){
1139 else if(l
->where
.row
>= last_line
){
1144 return(0); /* found! */
1152 scroll_handle_selectable(HANDLE_S
*handle
)
1154 return(handle
&& (handle
->type
!= URL
1155 || (handle
->h
.url
.path
&& *handle
->h
.url
.path
)));
1160 scroll_handle_next_sel(HANDLE_S
*handles
)
1162 while(handles
&& !scroll_handle_selectable(handles
= handles
->next
))
1170 scroll_handle_prev_sel(HANDLE_S
*handles
)
1172 while(handles
&& !scroll_handle_selectable(handles
= handles
->prev
))
1180 scroll_handle_next(HANDLE_S
*handles
)
1182 HANDLE_S
*next
= NULL
;
1184 if(scroll_handle_obscured(handles
) <= 0){
1186 while((next
= scroll_handle_next_sel(next
))
1187 && scroll_handle_obscured(next
))
1197 scroll_handle_prev(HANDLE_S
*handles
)
1199 HANDLE_S
*prev
= NULL
;
1201 if(scroll_handle_obscured(handles
) >= 0){
1203 while((prev
= scroll_handle_prev_sel(prev
))
1204 && scroll_handle_obscured(prev
))
1213 scroll_handle_boundary(HANDLE_S
*handle
, HANDLE_S
*(*f
) (HANDLE_S
*))
1215 HANDLE_S
*hp
, *whp
= NULL
;
1217 /* Multi-line handle? Punt! */
1218 if(handle
&& (!(hp
= handle
)->loc
->next
))
1219 while((hp
= (*f
)(hp
))
1220 && hp
->loc
->where
.row
== handle
->loc
->where
.row
)
1228 scroll_handle_column(int line
, int offset
)
1230 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1234 limit
= (offset
> -1) ? offset
: st
->line_lengths
[line
];
1236 for(i
= 0, col
= 0; i
< limit
;){
1237 if(st
&& st
->text_lines
&& st
->text_lines
[line
])
1238 switch(st
->text_lines
[line
][i
]){
1241 switch((i
< limit
) ? st
->text_lines
[line
][i
] : 0){
1243 for(key
= 0, n
= st
->text_lines
[line
][++i
]; n
> 0 && i
< limit
-1; n
--)
1244 key
= (key
* 10) + (st
->text_lines
[line
][++i
] - '0');
1251 i
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
1263 default: /* literal embed char */
1271 while(((++col
) & 0x07) != 0) /* add tab's spaces */
1277 col
+= width_at_this_position((unsigned char*) &st
->text_lines
[line
][i
],
1278 st
->line_lengths
[line
] - i
);
1289 scroll_handle_index(int row
, int column
)
1291 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1294 for(index
= 0; column
> 0;)
1295 switch(st
->text_lines
[row
][index
++]){
1297 switch(st
->text_lines
[row
][index
++]){
1299 index
+= st
->text_lines
[row
][index
] + 1;
1313 case TAB
: /* add tab's spaces */
1314 while(((--column
) & 0x07) != 0)
1329 scroll_handle_set_loc(POSLIST_S
**lpp
, int line
, int column
)
1333 lp
= (POSLIST_S
*) fs_get(sizeof(POSLIST_S
));
1334 lp
->where
.row
= line
;
1335 lp
->where
.col
= column
;
1337 for(; *lpp
; lpp
= &(*lpp
)->next
)
1345 dot_on_handle(long int line
, int goal
)
1347 int i
= 0, n
, key
= 0, column
= -1;
1348 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
1351 if(i
>= st
->line_lengths
[line
])
1354 switch(st
->text_lines
[line
][i
++]){
1356 switch(st
->text_lines
[line
][i
++]){
1358 for(key
= 0, n
= st
->text_lines
[line
][i
++]; n
; n
--)
1359 key
= (key
* 10) + (st
->text_lines
[line
][i
++] - '0');
1365 i
+= RGBLEN
; /* advance past color setting */
1376 while(((++column
) & 0x07) != 0) /* add tab's spaces */
1382 column
+= width_at_this_position((unsigned char*) &st
->text_lines
[line
][i
-1],
1383 st
->line_lengths
[line
] - (i
-1));
1387 while(column
< goal
);
1394 * url_launch - Sniff the given url, see if we can do anything with
1395 * it. If not, hand off to user-defined app.
1399 url_launch(HANDLE_S
*handle
)
1403 #define URL_MAX_LAUNCH (2 * MAILTMPLEN)
1405 if(handle
->h
.url
.tool
){
1406 char *toolp
, *cmdp
, *p
, cmd
[URL_MAX_LAUNCH
+ 4];
1407 int mode
, copied
= 0;
1410 toolp
= handle
->h
.url
.tool
;
1412 /* This code used to quote a URL to prevent arbitrary command execution
1413 * through a URL. The plan was to quote the URL with single quotes,
1414 * and this used to work. BUT some shells do not care about quoting
1415 * and interpret some characters regardless of single quotes. The
1416 * simplest solution is to escape those characters, but then some
1417 * shells will see the escape characters and some others will not.
1418 * It's a mess. There are several ways to go around this mess,
1419 * including adding configuration options (one more!?), or forget
1420 * about shells. What we do is to forget about shells, and execute
1421 * the code as a PIPE_NOSHELL.
1425 while(cmdp
-cmd
< URL_MAX_LAUNCH
)
1426 if((!*toolp
&& !copied
)
1427 || (*toolp
== '_' && !strncmp(toolp
+ 1, "URL_", 4))){
1429 /* implicit _URL_ at end */
1433 if(cmdp
[-1] == '\'') /* unquote old '_URL_' */
1437 for(p
= handle
->h
.url
.path
;
1438 p
&& *p
&& cmdp
-cmd
< URL_MAX_LAUNCH
; p
++)
1444 toolp
+= 5; /* length of "_URL_" */
1449 if(!(*cmdp
++ = *toolp
++))
1452 if(cmdp
-cmd
>= URL_MAX_LAUNCH
)
1453 return(url_launch_too_long(rv
));
1455 mode
= PIPE_RESET
| PIPE_USER
| PIPE_RUNNOW
| PIPE_NOSHELL
;
1456 if((syspipe
= open_system_pipe(cmd
, NULL
, NULL
, mode
, 0, pipe_callback
, pipe_report_error
)) != NULL
){
1457 close_system_pipe(&syspipe
, NULL
, pipe_callback
);
1458 q_status_message(SM_ORDER
, 0, 4, _("VIEWER command completed"));
1461 q_status_message1(SM_ORDER
, 3, 4,
1462 /* TRANSLATORS: Cannot start command : <command name> */
1463 _("Cannot start command : %s"), cmd
);
1465 else if((f
= url_local_handler(handle
->h
.url
.path
)) != NULL
){
1466 if((*f
)(handle
->h
.url
.path
) > 1)
1470 q_status_message1(SM_ORDER
, 2, 2,
1471 _("\"URL-Viewer\" not defined: Can't open %s"),
1472 handle
->h
.url
.path
);
1479 url_launch_too_long(int return_value
)
1481 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
1482 "Can't spawn. Command too long.");
1483 return(return_value
);
1488 url_external_handler(HANDLE_S
*handle
, int specific
)
1490 char **l
, *test
, *cmd
, *p
, *q
, *ep
;
1491 int i
, specific_match
;
1493 for(l
= ps_global
->VAR_BROWSER
; l
&& *l
; l
++){
1494 get_pair(*l
, &test
, &cmd
, 0, 1);
1495 dprint((5, "TEST: \"%s\" CMD: \"%s\"\n",
1496 test
? test
: "<NULL>", cmd
? cmd
: "<NULL>"));
1497 removing_quotes(cmd
);
1498 if(valid_filter_command(&cmd
)){
1501 if((p
= test
) != NULL
){
1504 if(!strncmp(p
+1, "TEST(", 5)
1505 && (ep
= strstr(p
+6, ")_"))){
1508 if(exec_mailcap_test_cmd(p
+6) == 0){
1512 dprint((5,"failed handler TEST\n"));
1513 fs_give((void **) &cmd
);
1516 else if(!strncmp(p
+1, "SCHEME(", 7)
1517 && (ep
= strstr(p
+8, ")_"))){
1522 if((q
= strchr(p
, ',')) != NULL
)
1526 while(!((i
= strlen(p
))
1528 && handle
->h
.url
.path
[i
- 1] == ':')
1530 && handle
->h
.url
.path
[i
] == ':'))
1531 && !struncmp(handle
->h
.url
.path
, p
, i
))
1539 dprint((5,"failed handler SCHEME\n"));
1540 fs_give((void **) &cmd
);
1544 dprint((5, "UNKNOWN underscore test\n"));
1545 fs_give((void **) &cmd
);
1548 else if(isspace((unsigned char) *p
)){
1552 dprint((1,"bogus handler test: \"%s\"",
1553 test
? test
: "?"));
1554 fs_give((void **) &cmd
);
1557 fs_give((void **) &test
);
1560 if(cmd
&& (!specific
|| specific_match
))
1565 fs_give((void **) &test
);
1568 fs_give((void **) &cmd
);
1574 cmd
= url_os_specified_browser(handle
->h
.url
.path
);
1576 * Last chance, anything handling "text/html" in mailcap...
1578 if(!cmd
&& mailcap_can_display(TYPETEXT
, "html", NULL
, 0)){
1581 mc_cmd
= mailcap_build_command(TYPETEXT
, "html",
1582 NULL
, "_URL_", NULL
, 0);
1584 * right now URL viewing won't return anything requiring
1588 cmd
= mc_cmd
->command
;
1589 fs_give((void **)&mc_cmd
);
1599 url_local_handler(char *s
)
1602 static struct url_t
{
1607 {"mailto:", 7, url_local_mailto
}, /* see url_tool_t def's */
1608 {"imap://", 7, url_local_imap
}, /* for explanations */
1609 {"nntp://", 7, url_local_nntp
},
1610 {"file://", 7, url_local_file
},
1612 {"ldap://", 7, url_local_ldap
},
1614 {"news:", 5, url_local_news
},
1615 {"x-alpine-ical:", 14, ical_send_reply
},
1616 {"x-alpine-phone-home:", 20, url_local_phone_home
},
1617 {"x-alpine-gripe:", 15, gripe_gripe_to
},
1618 {"x-alpine-help:", 14, url_local_helper
},
1619 {"x-pine-help:", 12, url_local_helper
},
1620 {"x-alpine-config:", 16, url_local_config
},
1621 {"x-alpine-cert:", 14, url_local_certdetails
},
1622 {"#", 1, url_local_fragment
},
1626 for(i
= 0; handlers
[i
].url
; i
++)
1627 if(!struncmp(s
, handlers
[i
].url
, handlers
[i
].len
))
1628 return(handlers
[i
].f
);
1636 * mailto URL digester ala draft-hoffman-mailto-url-02.txt
1639 url_local_mailto(char *url
)
1641 return(url_local_mailto_and_atts(url
, NULL
));
1645 url_local_mailto_and_atts(char *url
, PATMT
*attachlist
)
1650 char *sig
, *urlp
, *p
, *hname
, *hvalue
;
1653 int was_a_body
= 0, impl
, template_len
= 0;
1656 REDRAFT_POS_S
*redraft_pos
= NULL
;
1657 ACTION_S
*role
= NULL
;
1659 outgoing
= mail_newenvelope();
1660 outgoing
->message_id
= generate_message_id();
1661 body
= mail_newbody();
1662 body
->type
= TYPETEXT
;
1663 if((body
->contents
.text
.data
= (void *) so_get(PicoText
,NULL
,EDIT_ACCESS
)) != NULL
){
1667 * mailtoURL = "mailto:" [ to ] [ headers ]
1669 * headers = "?" header *( "&" header )
1670 * header = hname "=" hvalue
1674 * NOTE2: "from" and "bcc" are intentionally excluded from
1675 * the list of understood "header" fields
1677 if((p
= strchr(urlp
= cpystr(url
+7), '?')) != NULL
)
1678 *p
++ = '\0'; /* headers? Tie off mailbox */
1680 /* grok mailbox as first "to", then roll thru specific headers */
1682 rfc822_parse_adrlist(&outgoing
->to
,
1684 ps_global
->maildomain
);
1687 /* Find next "header" */
1688 if((p
= strchr(hname
= p
, '&')) != NULL
)
1689 *p
++ = '\0'; /* tie off "header" */
1691 if((hvalue
= strchr(hname
, '=')) != NULL
)
1692 *hvalue
++ = '\0'; /* tie off hname */
1694 if(!hvalue
|| !strucmp(hname
, "subject")){
1695 char *sub
= rfc1738_str(hvalue
? hvalue
: hname
);
1697 if(outgoing
->subject
){
1698 int len
= strlen(outgoing
->subject
);
1700 fs_resize((void **) &outgoing
->subject
,
1701 (len
+ strlen(sub
) + 2) * sizeof(char));
1702 snprintf(outgoing
->subject
+ len
, strlen(sub
)+2, " %s", sub
);
1703 outgoing
->subject
[len
+ strlen(sub
) + 2 - 1] = '\0';
1706 outgoing
->subject
= cpystr(sub
);
1708 else if(!strucmp(hname
, "to")){
1709 url_mailto_addr(&outgoing
->to
, hvalue
);
1711 else if(!strucmp(hname
, "cc")){
1712 url_mailto_addr(&outgoing
->cc
, hvalue
);
1714 else if(!strucmp(hname
, "bcc")){
1715 q_status_message(SM_ORDER
, 3, 4,
1716 "\"Bcc\" header in mailto url ignored");
1718 else if(!strucmp(hname
, "from")){
1719 q_status_message(SM_ORDER
, 3, 4,
1720 "\"From\" header in mailto url ignored");
1722 else if(!strucmp(hname
, "body")){
1723 char *sub
= rfc1738_str(hvalue
? hvalue
: "");
1725 so_puts((STORE_S
*)body
->contents
.text
.data
, sub
);
1726 so_puts((STORE_S
*)body
->contents
.text
.data
, NEWLINE
);
1731 fs_give((void **) &urlp
);
1733 rflags
= ROLE_COMPOSE
;
1734 if(nonempty_patterns(rflags
, &dummy
)){
1735 role
= set_role_from_msg(ps_global
, rflags
, -1L, NULL
);
1736 if(confirm_role(rflags
, &role
))
1737 role
= combine_inherited_role(role
);
1740 cmd_cancelled("Composition");
1746 q_status_message1(SM_ORDER
, 3, 4, "Composing using role \"%s\"",
1749 if(!was_a_body
&& role
&& role
->template){
1752 impl
= 1; /* leave cursor in header if not explicit */
1753 filtered
= detoken(role
, NULL
, 0, 0, 0, &redraft_pos
, &impl
);
1756 so_puts((STORE_S
*)body
->contents
.text
.data
, filtered
);
1758 template_len
= strlen(filtered
);
1761 fs_give((void **)&filtered
);
1767 if(!was_a_body
&& (sig
= detoken(role
, NULL
, 2, 0, 1, &redraft_pos
,
1770 redraft_pos
->offset
+= template_len
;
1773 so_puts((STORE_S
*)body
->contents
.text
.data
, sig
);
1775 fs_give((void **)&sig
);
1778 memset((void *)&fake_reply
, 0, sizeof(fake_reply
));
1779 fake_reply
.pseudo
= 1;
1780 fake_reply
.data
.pico_flags
= (outgoing
->subject
) ? P_BODY
: P_HEADEND
;
1783 if(!(role
&& role
->fcc
))
1784 fcc
= get_fcc_based_on_to(outgoing
->to
);
1787 create_message_body(&body
, attachlist
, 0);
1789 pine_send(outgoing
, &body
, "\"MAILTO\" COMPOSE",
1790 role
, fcc
, &fake_reply
, redraft_pos
, NULL
, NULL
, PS_STICKY_TO
);
1792 ps_global
->mangled_screen
= 1;
1795 q_status_message(SM_ORDER
| SM_DING
, 3, 4,
1796 _("Can't create space for composer"));
1800 mail_free_envelope(&outgoing
);
1803 pine_free_body(&body
);
1806 fs_give((void **)&fcc
);
1808 free_redraft_pos(&redraft_pos
);
1816 url_mailto_addr(struct mail_address
**a
, char *a_raw
)
1818 char *p
= cpystr(rfc1738_str(a_raw
));
1820 while(*a
) /* append to address list */
1823 rfc822_parse_adrlist(a
, p
, ps_global
->maildomain
);
1824 fs_give((void **) &p
);
1829 * imap URL digester ala RFC 2192
1832 url_local_imap(char *url
)
1834 char *folder
, *mailbox
= NULL
, *errstr
= NULL
, *search
= NULL
,
1835 newfolder
[MAILTMPLEN
];
1838 imapuid_t uid
= 0L, uid_val
= 0L;
1839 CONTEXT_S
*fake_context
;
1842 rv
= url_imap_folder(url
, &folder
, &uid
, &uid_val
, &search
, 0);
1843 switch(rv
& URL_IMAP_MASK
){
1844 case URL_IMAP_IMAILBOXLIST
:
1845 /* BUG: deal with lsub tag */
1846 if((fake_context
= new_context(folder
, NULL
)) != NULL
){
1847 newfolder
[0] = '\0';
1848 if(display_folder_list(&fake_context
, newfolder
,
1849 0, folders_for_goto
))
1850 if(strlen(newfolder
) + 1 < MAILTMPLEN
)
1851 mailbox
= newfolder
;
1854 errstr
= "Problem building URL's folder list";
1856 fs_give((void **) &folder
);
1857 free_context(&fake_context
);
1863 q_status_message(SM_ORDER
|SM_DING
, 3, 3, errstr
);
1865 cmd_cancelled("URL Launch");
1869 case URL_IMAP_IMESSAGEPART
:
1870 case URL_IMAP_IMESSAGELIST
:
1871 if(ps_global
&& ps_global
->ttyo
){
1872 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
1873 ps_global
->mangled_footer
= 1;
1876 rv
= do_broach_folder(folder
, NULL
, NULL
, 0L);
1877 fs_give((void **) &folder
);
1879 case -1 : /* utter failure */
1880 ps_global
->next_screen
= main_menu_screen
;
1883 case 0 : /* same folder reopened */
1884 ps_global
->next_screen
= mail_index_screen
;
1887 case 1 : /* requested folder open */
1888 ps_global
->next_screen
= mail_index_screen
;
1890 if(uid_val
&& uid_val
!= ps_global
->mail_stream
->uid_validity
){
1892 q_status_message(SM_ORDER
|SM_DING
, 3, 3,
1893 "Warning! Referenced folder changed since URL recorded");
1898 * Make specified message the currently selected..
1900 for(i
= 1L; i
<= mn_get_total(ps_global
->msgmap
); i
++)
1901 if(mail_uid(ps_global
->mail_stream
, i
) == uid
){
1902 ps_global
->next_screen
= mail_view_screen
;
1903 mn_set_cur(ps_global
->msgmap
, i
);
1907 if(i
> mn_get_total(ps_global
->msgmap
))
1908 q_status_message(SM_ORDER
, 2, 3,
1909 "Couldn't find specified article number");
1913 * Select the specified messages
1914 * and present a zoom'd index...
1916 /* BUG: not dealing with CHARSET yet */
1918 /* ANOTHER BUG: mail_criteria is a compatibility routine for IMAP2BIS
1919 * so it doesn't know about IMAP4 search criteria, like SENTSINCE.
1920 * It also doesn't handle literals. */
1922 pine_mail_search_full(ps_global
->mail_stream
, NULL
,
1923 mail_criteria(search
),
1924 SE_NOPREFETCH
| SE_FREE
);
1926 for(i
= 1L; i
<= mn_get_total(ps_global
->msgmap
); i
++)
1927 if(ps_global
->mail_stream
1928 && i
<= ps_global
->mail_stream
->nmsgs
1929 && (mc
= mail_elt(ps_global
->mail_stream
, i
))
1931 set_lflag(ps_global
->mail_stream
,
1932 ps_global
->msgmap
, i
, MN_SLCT
, 1);
1934 if((i
= any_lflagged(ps_global
->msgmap
, MN_SLCT
)) != 0){
1936 q_status_message2(SM_ORDER
, 0, 3,
1937 "%s message%s selected",
1938 long2string(i
), plural(i
));
1939 /* Zoom the index! */
1940 zoom_index(ps_global
, ps_global
->mail_stream
,
1941 ps_global
->msgmap
, MN_SLCT
);
1949 case URL_IMAP_ERROR
:
1958 url_local_nntp(char *url
)
1960 char folder
[2*MAILTMPLEN
], *group
;
1962 long i
, article_num
;
1964 /* no hostport, no url, end of story */
1965 if((group
= strchr(url
+ 7, '/')) != 0){
1967 for(group_len
= 0; group
[group_len
] && group
[group_len
] != '/';
1969 if(!rfc1738_group(&group
[group_len
]))
1970 /* TRANSLATORS: these are errors in news group URLs */
1971 return(url_bogus(url
, _("Invalid newsgroup specified")));
1974 snprintf(folder
, sizeof(folder
), "{%.*s/nntp}#news.%.*s",
1975 (int) MIN((group
- 1) - (url
+ 7), MAILTMPLEN
-20), url
+ 7,
1976 (int) MIN(group_len
, MAILTMPLEN
-20), group
);
1977 folder
[sizeof(folder
)-1] = '\0';
1980 return(url_bogus(url
, _("No newsgroup specified")));
1983 return(url_bogus(url
, _("No server specified")));
1985 if(ps_global
&& ps_global
->ttyo
){
1986 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
1987 ps_global
->mangled_footer
= 1;
1990 switch(do_broach_folder(rfc1738_str(folder
), NULL
, NULL
, 0L)){
1991 case -1 : /* utter failure */
1992 ps_global
->next_screen
= main_menu_screen
;
1995 case 0 : /* same folder reopened */
1996 ps_global
->next_screen
= mail_index_screen
;
1999 case 1 : /* requested folder open */
2000 ps_global
->next_screen
= mail_index_screen
;
2002 /* grok article number --> c-client UID */
2003 if(group
[group_len
++] == '/'
2004 && (article_num
= atol(&group
[group_len
]))){
2006 * Make the requested article our current message
2008 for(i
= 1; i
<= mn_get_nmsgs(ps_global
->msgmap
); i
++)
2009 if(mail_uid(ps_global
->mail_stream
, i
) == article_num
){
2010 ps_global
->next_screen
= mail_view_screen
;
2011 if((i
= mn_raw2m(ps_global
->msgmap
, i
)) != 0)
2012 mn_set_cur(ps_global
->msgmap
, i
);
2016 if(i
== 0 || i
> mn_get_total(ps_global
->msgmap
))
2017 q_status_message(SM_ORDER
, 2, 3,
2018 _("Couldn't find specified article number"));
2024 ps_global
->redrawer
= (void(*)(void))NULL
;
2030 url_local_news(char *url
)
2032 char folder
[MAILTMPLEN
], *p
;
2033 CONTEXT_S
*cntxt
= NULL
;
2036 * NOTE: NO SUPPORT for '*' or message-id
2039 if(*(url
+5) == '/' && *(url
+6) == '/')
2040 return(url_local_nntp(url
)); /* really meant "nntp://"? */
2042 if(ps_global
->VAR_NNTP_SERVER
&& ps_global
->VAR_NNTP_SERVER
[0])
2044 * BUG: Only the first NNTP server is tried.
2046 snprintf(folder
, sizeof(folder
), "{%s/nntp}#news.", ps_global
->VAR_NNTP_SERVER
[0]);
2048 strncpy(folder
, "#news.", sizeof(folder
));
2050 folder
[sizeof(folder
)-1] = '\0';
2052 for(p
= strncpy(folder
+ strlen(folder
), url
+ 5, sizeof(folder
)-strlen(folder
)-1);
2053 *p
&& rfc1738_group(p
);
2058 return(url_bogus(url
, "Invalid newsgroup specified"));
2060 else{ /* fish first group from newsrc */
2066 /* Find first news context */
2067 for(cntxt
= ps_global
->context_list
;
2068 cntxt
&& !(cntxt
->use
& CNTXT_NEWS
);
2069 cntxt
= cntxt
->next
)
2073 if((alphaorder
= F_OFF(F_READ_IN_NEWSRC_ORDER
, ps_global
)) != 0)
2074 (void) F_SET(F_READ_IN_NEWSRC_ORDER
, ps_global
, 1);
2076 build_folder_list(NULL
, cntxt
, NULL
, NULL
, BFL_LSUB
);
2077 if((f
= folder_entry(0, FOLDERS(cntxt
))) != NULL
){
2078 strncpy(folder
, f
->name
, sizeof(folder
));
2079 folder
[sizeof(folder
)-1] = '\0';
2082 free_folder_list(cntxt
);
2085 (void) F_SET(F_READ_IN_NEWSRC_ORDER
, ps_global
, 0);
2088 if(folder
[0] == '\0'){
2089 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
2090 "No default newsgroup");
2095 if(ps_global
&& ps_global
->ttyo
){
2096 blank_keymenu(ps_global
->ttyo
->screen_rows
- 2, 0);
2097 ps_global
->mangled_footer
= 1;
2100 if(do_broach_folder(rfc1738_str(folder
), cntxt
, NULL
, 0L) < 0)
2101 ps_global
->next_screen
= main_menu_screen
;
2103 ps_global
->next_screen
= mail_index_screen
;
2105 ps_global
->redrawer
= (void(*)(void))NULL
;
2112 url_local_file(char *file_url
)
2115 /* TRANSLATORS: this is a warning that the file URL can cause programs to run which may
2116 be a security problem. We are asking the user to confirm that they want to do this. */
2117 _("\"file\" URL may cause programs to be run on your system. Run anyway"),
2118 'n', 0, NO_HELP
, WT_NORM
) == 'y'){
2122 handle
.h
.url
.path
= file_url
;
2123 if((handle
.h
.url
.tool
= url_external_handler(&handle
, 1))
2124 || (handle
.h
.url
.tool
= url_external_handler(&handle
, 0))){
2125 url_launch(&handle
);
2129 q_status_message(SM_ORDER
, 0, 4,
2130 _("No viewer for \"file\" URL. VIEWER command cancelled"));
2134 q_status_message(SM_ORDER
, 0, 4, _("VIEWER command cancelled"));
2140 url_local_fragment(char *fragment
)
2142 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
2143 HANDLE_S
*hp
= NULL
;
2146 * find a handle with the fragment's name
2149 for(hp
= st
->parms
->text
.handles
; hp
; hp
= hp
->next
)
2150 if(hp
->type
== URL
&& hp
->h
.url
.name
2151 && !strcmp(hp
->h
.url
.name
, fragment
+ 1))
2155 for(hp
= st
->parms
->text
.handles
->prev
; hp
; hp
= hp
->prev
)
2156 if(hp
->type
== URL
&& hp
->h
.url
.name
2157 && !strcmp(hp
->h
.url
.name
, fragment
+ 1))
2161 * set the top line of the display to contain this line
2164 st
->top_text_line
= hp
->loc
->where
.row
;
2165 ps_global
->mangled_body
= 1;
2168 q_status_message1(SM_ORDER
| SM_DING
, 0, 3,
2169 "Can't find fragment: %s", fragment
);
2175 ical_send_reply(char *url
)
2177 // ical_compose_reply(url + strlen("x-alpine-ical:"));
2183 url_local_phone_home(char *URL
)
2185 phone_home(URL
+ strlen("x-alpine-phone-home:"));
2191 * Format editorial comment referencing screen offering
2192 * List-* header supplied commands
2195 rfc2369_editorial(long int msgno
, HANDLE_S
**handlesp
, int flags
, int width
, gf_io_t pc
)
2197 char *p
, *hdrp
, *hdrs
[MLCMD_COUNT
+ 1],
2198 color
[64], buf
[2048];
2199 int i
, n
, rv
= TRUE
;
2202 if((flags
& FM_DISPLAY
)
2203 && (hdrp
= pine_fetchheader_lines(ps_global
->mail_stream
, msgno
,
2204 NULL
, rfc2369_hdrs(hdrs
)))){
2206 snprintf(buf
, sizeof(buf
), "Note: This message contains ");
2207 buf
[sizeof(buf
)-1] = '\0';
2208 p
= buf
+ strlen(buf
);
2211 h
= new_handle(handlesp
);
2213 h
->h
.func
.f
= rfc2369_display
;
2214 h
->h
.func
.args
.stream
= ps_global
->mail_stream
;
2215 h
->h
.func
.args
.msgmap
= ps_global
->msgmap
;
2216 h
->h
.func
.args
.msgno
= msgno
;
2218 if(!(flags
& FM_NOCOLOR
)
2219 && handle_start_color(color
, sizeof(color
), &n
, 0)){
2220 if((p
-buf
)+n
< sizeof(buf
))
2221 for(i
= 0; i
< n
; i
++)
2224 else if(F_OFF(F_SLCTBL_ITEM_NOBOLD
, ps_global
)){
2229 if((p
-buf
)+2 < sizeof(buf
)){
2234 snprintf(p
+ 1, sizeof(buf
)-(p
+1-buf
), "%d", h
->key
);
2235 buf
[sizeof(buf
)-1] = '\0';
2240 sstrncpy(&p
, "email list management information", sizeof(buf
)-(p
-buf
));
2241 buf
[sizeof(buf
)-1] = '\0';
2244 /* in case it was the current selection */
2245 if((p
-buf
)+2 < sizeof(buf
)){
2250 if(handle_end_color(color
, sizeof(color
), &n
)){
2251 if((p
-buf
)+n
< sizeof(buf
))
2252 for(i
= 0; i
< n
; i
++)
2256 if((p
-buf
)+2 < sizeof(buf
)){
2262 if(p
-buf
< sizeof(buf
))
2266 buf
[sizeof(buf
)-1] = '\0';
2268 rv
= (gf_puts(NEWLINE
, pc
)
2269 && format_editorial(buf
, width
, flags
, handlesp
, pc
) == NULL
2270 && gf_puts(NEWLINE
, pc
));
2273 fs_give((void **) &hdrp
);
2281 /*----------------------------------------------------------------------
2282 routine for displaying text on the screen.
2284 Args: sparms -- structure of args controlling what happens outside
2285 just the business of managing text scrolling
2287 This displays in three different kinds of text. One is an array of
2288 lines passed in in text_array. The other is a simple long string of
2289 characters passed in in text.
2291 The style determines what some of the error messages will be, and
2292 what commands are available as different things are appropriate for
2293 help text than for message text etc.
2298 scrolltool(SCROLL_S
*sparms
)
2300 register long cur_top_line
, num_display_lines
;
2302 int result
, done
, cmd
, found_on
, found_on_index
,
2303 first_view
, force
, scroll_lines
, km_size
,
2304 cursor_row
, cursor_col
, km_popped
;
2307 struct key_menu
*km
;
2308 HANDLE_S
*next_handle
;
2313 num_display_lines
= SCROLL_LINES(ps_global
);
2315 ps_global
->mangled_header
= 1;
2316 ps_global
->mangled_footer
= 1;
2317 ps_global
->mangled_body
= !sparms
->body_valid
;
2320 what
= sparms
->keys
.what
; /* which key menu to display */
2324 found_on_index
= -1;
2326 if(sparms
->quell_first_view
)
2330 ch
= 'x'; /* for first time through */
2331 whereis_pos
.row
= 0;
2332 whereis_pos
.col
= 0;
2333 next_handle
= sparms
->text
.handles
;
2335 set_scroll_text(sparms
, cur_top_line
, scroll_state(SS_NEW
));
2336 format_scroll_text();
2338 if((km
= sparms
->keys
.menu
) != NULL
){
2339 memcpy(bitmap
, sparms
->keys
.bitmap
, sizeof(bitmap_t
));
2343 km
= &simple_text_keymenu
;
2345 sparms
->mouse
.popup
= simple_text_popup
;
2349 if(!sparms
->bar
.title
)
2350 sparms
->bar
.title
= "Text";
2352 if(sparms
->bar
.style
== TitleBarNone
){
2353 if(THREADING() && sp_viewing_a_thread(ps_global
->mail_stream
))
2354 sparms
->bar
.style
= ThrdMsgPercent
;
2356 sparms
->bar
.style
= MsgTextPercent
;
2359 switch(sparms
->start
.on
){
2361 cur_top_line
= MAX(0, scroll_text_lines() - (num_display_lines
-2));
2362 if(F_ON(F_SHOW_CURSOR
, ps_global
)){
2363 whereis_pos
.row
= scroll_text_lines() - cur_top_line
;
2364 found_on
= scroll_text_lines() - 1;
2370 if(sparms
->start
.loc
.frag
){
2371 (void) url_local_fragment(sparms
->start
.loc
.frag
);
2373 cur_top_line
= scroll_state(SS_CUR
)->top_text_line
;
2375 if(F_ON(F_SHOW_CURSOR
, ps_global
)){
2376 whereis_pos
.row
= scroll_text_lines() - cur_top_line
;
2377 found_on
= scroll_text_lines() - 1;
2384 if(sparms
->start
.loc
.offset
){
2385 for(cur_top_line
= 0L;
2386 cur_top_line
+ 1 < scroll_text_lines()
2387 && (sparms
->start
.loc
.offset
2388 -= scroll_handle_column(cur_top_line
,
2397 if(scroll_handle_obscured(sparms
->text
.handles
))
2398 cur_top_line
= scroll_handle_reframe(-1, TRUE
);
2402 default : /* no-op */
2406 /* prepare for calls below to tell us where to go */
2407 ps_global
->next_screen
= SCREEN_FUN_NULL
;
2409 cancel_busy_cue(-1);
2412 ps_global
->user_says_cancel
= 0;
2416 clearfooter(ps_global
);
2417 ps_global
->mangled_body
= 1;
2421 if(ps_global
->mangled_screen
) {
2422 ps_global
->mangled_header
= 1;
2423 ps_global
->mangled_footer
= 1;
2424 ps_global
->mangled_body
= 1;
2427 if(!sparms
->quell_newmail
&& streams_died())
2428 ps_global
->mangled_header
= 1;
2430 dprint((9, "@@@@ current:%ld\n",
2431 mn_get_cur(ps_global
->msgmap
)));
2434 /*==================== All Screen painting ====================*/
2435 /*-------------- The title bar ---------------*/
2436 update_scroll_titlebar(cur_top_line
, ps_global
->mangled_header
);
2438 if(ps_global
->mangled_screen
){
2439 /* this is the only line not cleared by header, body or footer
2443 ps_global
->mangled_screen
= 0;
2446 /*---- Scroll or update the body of the text on the screen -------*/
2447 cur_top_line
= scroll_scroll_text(cur_top_line
, next_handle
,
2448 ps_global
->mangled_body
);
2449 ps_global
->redrawer
= redraw_scroll_text
;
2450 ps_global
->mangled_body
= 0;
2452 /*--- Check to see if keymenu might change based on next_handle --*/
2453 if(sparms
->text
.handles
!= next_handle
)
2454 ps_global
->mangled_footer
= 1;
2457 sparms
->text
.handles
= next_handle
;
2459 /*------------- The key menu footer --------------------*/
2460 if(ps_global
->mangled_footer
|| sparms
->keys
.each_cmd
){
2462 FOOTER_ROWS(ps_global
) = 3;
2463 clearfooter(ps_global
);
2466 if(F_ON(F_ARROW_NAV
, ps_global
)){
2467 menu_clear_binding(km
, KEY_LEFT
);
2468 if((cmd
= menu_clear_binding(km
, '<')) != MC_UNKNOWN
){
2469 menu_add_binding(km
, '<', cmd
);
2470 menu_add_binding(km
, KEY_LEFT
, cmd
);
2474 if(F_ON(F_ARROW_NAV
, ps_global
)){
2475 menu_clear_binding(km
, KEY_RIGHT
);
2476 if((cmd
= menu_clear_binding(km
, '>')) != MC_UNKNOWN
){
2477 menu_add_binding(km
, '>', cmd
);
2478 menu_add_binding(km
, KEY_RIGHT
, cmd
);
2482 if(sparms
->keys
.each_cmd
){
2483 (*sparms
->keys
.each_cmd
)(sparms
,
2484 scroll_handle_obscured(sparms
->text
.handles
));
2485 memcpy(bitmap
, sparms
->keys
.bitmap
, sizeof(bitmap_t
));
2488 if(menu_binding_index(km
, MC_JUMP
) >= 0){
2489 for(cmd
= 0; cmd
< 10; cmd
++)
2490 if(F_ON(F_ENABLE_JUMP
, ps_global
))
2491 (void) menu_add_binding(km
, '0' + cmd
, MC_JUMP
);
2493 (void) menu_clear_binding(km
, '0' + cmd
);
2496 draw_keymenu(km
, bitmap
, ps_global
->ttyo
->screen_cols
,
2497 1-FOOTER_ROWS(ps_global
), 0, what
);
2499 ps_global
->mangled_footer
= 0;
2501 FOOTER_ROWS(ps_global
) = 1;
2502 mark_keymenu_dirty();
2506 if((ps_global
->first_time_user
|| ps_global
->show_new_version
)
2507 && first_view
&& sparms
->text
.handles
2508 && (sparms
->text
.handles
->next
|| sparms
->text
.handles
->prev
)
2509 && !sparms
->quell_help
)
2510 q_status_message(SM_ORDER
, 0, 3, HANDLE_INIT_MSG
);
2512 /*============ Check for New Mail and CheckPoint ============*/
2513 if(!sparms
->quell_newmail
&&
2514 new_mail(force
, NM_TIMING(ch
), NM_STATUS_MSG
) >= 0){
2515 update_scroll_titlebar(cur_top_line
, 1);
2516 if(ps_global
->mangled_footer
)
2517 draw_keymenu(km
, bitmap
, ps_global
->ttyo
->screen_cols
,
2518 1-FOOTER_ROWS(ps_global
), 0, what
);
2520 ps_global
->mangled_footer
= 0;
2524 * If an expunge of the current message happened during the
2525 * new mail check we want to bail out of here. See mm_expunged.
2527 if(ps_global
->next_screen
!= SCREEN_FUN_NULL
){
2532 if(ps_global
->noticed_change_in_unseen
){
2533 ps_global
->noticed_change_in_unseen
= 0; /* redraw only once */
2534 cmd
= MC_RESIZE
; /* causes cursor to be saved in folder_lister */
2539 if(first_view
&& num_display_lines
>= scroll_text_lines())
2540 q_status_message1(SM_INFO
, 0, 1, "ALL of %s", STYLE_NAME(sparms
));
2543 force
= 0; /* may not need to next time around */
2544 first_view
= 0; /* check_point a priority any more? */
2546 /*==================== Output the status message ==============*/
2547 if(!sparms
->no_stat_msg
){
2549 FOOTER_ROWS(ps_global
) = 3;
2550 mark_status_unknown();
2553 display_message(ch
);
2555 FOOTER_ROWS(ps_global
) = 1;
2556 mark_status_unknown();
2560 if(F_ON(F_SHOW_CURSOR
, ps_global
)){
2562 if(cur_top_line
!= scroll_state(SS_CUR
)->top_text_line
)
2563 whereis_pos
.row
= 0;
2566 if(whereis_pos
.row
> 0){
2567 cursor_row
= SCROLL_LINES_ABOVE(ps_global
)
2568 + whereis_pos
.row
- 1;
2569 cursor_col
= whereis_pos
.col
;
2572 POSLIST_S
*lp
= NULL
;
2574 if(sparms
->text
.handles
&&
2575 !scroll_handle_obscured(sparms
->text
.handles
)){
2576 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
2578 for(lp
= sparms
->text
.handles
->loc
; lp
; lp
= lp
->next
)
2579 if(lp
->where
.row
>= st
->top_text_line
2580 && lp
->where
.row
< st
->top_text_line
2581 + st
->screen
.length
){
2582 cursor_row
= lp
->where
.row
- cur_top_line
2583 + SCROLL_LINES_ABOVE(ps_global
);
2584 cursor_col
= lp
->where
.col
;
2591 /* first new line of text */
2592 cursor_row
= SCROLL_LINES_ABOVE(ps_global
) +
2593 ((cur_top_line
== 0) ? 0 : ps_global
->viewer_overlap
);
2599 cursor_row
= ps_global
->ttyo
->screen_rows
2600 - SCROLL_LINES_BELOW(ps_global
);
2603 MoveCursor(cursor_row
, cursor_col
);
2605 /*================ Get command and validate =====================*/
2608 if(sparms
->text
.handles
)
2611 mouse_in_content(KEY_MOUSE
, -1, -1, 0x5, 0);
2612 register_mfunc(mouse_in_content
, HEADER_ROWS(ps_global
), 0,
2613 ps_global
->ttyo
->screen_rows
2614 - (FOOTER_ROWS(ps_global
) + 1),
2615 ps_global
->ttyo
->screen_cols
);
2619 mswin_allowcopy(mswin_readscrollbuf
);
2620 mswin_setscrollcallback(pcpine_do_scroll
);
2622 if(sparms
->help
.text
!= NO_HELP
)
2623 mswin_sethelptextcallback(pcpine_help_scroll
);
2625 if(sparms
->text
.handles
2626 && sparms
->text
.handles
->type
!= Folder
)
2627 mswin_mousetrackcallback(pcpine_view_cursor
);
2629 if(ps_global
->prev_screen
== mail_view_screen
)
2630 mswin_setviewinwindcallback(view_in_new_window
);
2632 ch
= (sparms
->quell_newmail
|| read_command_prep()) ? read_command(&utf8str
) : NO_OP_COMMAND
;
2635 if(sparms
->text
.handles
)
2637 clear_mfunc(mouse_in_content
);
2640 mswin_allowcopy(NULL
);
2641 mswin_setscrollcallback(NULL
);
2642 mswin_sethelptextcallback(NULL
);
2643 mswin_mousetrackcallback(NULL
);
2644 mswin_setviewinwindcallback(NULL
);
2645 cur_top_line
= scroll_state(SS_CUR
)->top_text_line
;
2648 cmd
= menu_command(ch
, km
);
2660 clearfooter(ps_global
);
2665 /*============= Execute command =======================*/
2668 /* ------ Help -------*/
2670 if(FOOTER_ROWS(ps_global
) == 1 && km_popped
== 0){
2672 ps_global
->mangled_footer
= 1;
2676 whereis_pos
.row
= 0;
2677 if(sparms
->help
.text
== NO_HELP
){
2678 q_status_message(SM_ORDER
, 0, 5,
2679 _("No help text currently available"));
2683 km_size
= FOOTER_ROWS(ps_global
);
2685 helper(sparms
->help
.text
, sparms
->help
.title
, 0);
2687 if(ps_global
->next_screen
!= main_menu_screen
2688 && km_size
== FOOTER_ROWS(ps_global
)) {
2689 /* Have to reset because helper uses scroll_text */
2690 num_display_lines
= SCROLL_LINES(ps_global
);
2691 ps_global
->mangled_screen
= 1;
2699 /*---------- Roll keymenu ------*/
2701 if(F_OFF(F_USE_FK
, ps_global
))
2705 ps_global
->mangled_footer
= 1;
2709 /* -------- Scroll back one page -----------*/
2711 whereis_pos
.row
= 0;
2713 scroll_lines
= MIN(MAX(num_display_lines
-
2714 ps_global
->viewer_overlap
, 1), num_display_lines
);
2715 cur_top_line
-= scroll_lines
;
2716 if(cur_top_line
<= 0){
2718 q_status_message1(SM_INFO
, 0, 1, "START of %s",
2719 STYLE_NAME(sparms
));
2723 /* hilite last available handle */
2725 if(sparms
->text
.handles
){
2726 HANDLE_S
*h
= sparms
->text
.handles
;
2728 while((h
= scroll_handle_prev_sel(h
))
2729 && !scroll_handle_obscured(h
))
2734 q_status_message1(SM_ORDER
, 0, 1, _("Already at start of %s"),
2735 STYLE_NAME(sparms
));
2743 /*---- Scroll down one page -------*/
2745 if(cur_top_line
+ num_display_lines
< scroll_text_lines()){
2746 whereis_pos
.row
= 0;
2747 scroll_lines
= MIN(MAX(num_display_lines
-
2748 ps_global
->viewer_overlap
, 1), num_display_lines
);
2749 cur_top_line
+= scroll_lines
;
2751 if(cur_top_line
+ num_display_lines
>= scroll_text_lines())
2752 q_status_message1(SM_INFO
, 0, 1, "END of %s",
2753 STYLE_NAME(sparms
));
2755 else if(!sparms
->end_scroll
2756 || !(done
= (*sparms
->end_scroll
)(sparms
))){
2757 q_status_message1(SM_ORDER
, 0, 1, _("Already at end of %s"),
2758 STYLE_NAME(sparms
));
2759 /* hilite last available handle */
2760 if(sparms
->text
.handles
){
2761 HANDLE_S
*h
= sparms
->text
.handles
;
2763 while((h
= scroll_handle_next_sel(h
)) != NULL
)
2770 /* scroll to the top page */
2774 q_status_message1(SM_INFO
, 0, 1, "START of %s",
2775 STYLE_NAME(sparms
));
2779 if(sparms
->text
.handles
){
2780 HANDLE_S
*h
= sparms
->text
.handles
;
2782 while((h
= scroll_handle_prev_sel(h
)) != NULL
)
2787 /* scroll to the bottom page */
2789 if(cur_top_line
+ num_display_lines
< scroll_text_lines()){
2790 cur_top_line
= scroll_text_lines() - MIN(5, num_display_lines
);
2791 q_status_message1(SM_INFO
, 0, 1, "END of %s",
2792 STYLE_NAME(sparms
));
2795 if(sparms
->text
.handles
){
2796 HANDLE_S
*h
= sparms
->text
.handles
;
2798 while((h
= scroll_handle_next_sel(h
)) != NULL
)
2803 /*------ Scroll down one line -----*/
2806 if(sparms
->text
.handles
){
2807 if(sparms
->vert_handle
){
2811 h2
= sparms
->text
.handles
;
2812 if(h2
->type
== Folder
&& h2
->prev
&& h2
->prev
->is_dual_do_open
)
2815 i
= h2
->loc
->where
.row
+ 1;
2816 j
= h2
->loc
->where
.col
;
2817 for(h
= NULL
, k
= h2
->key
;
2819 || (h
->loc
->where
.row
== h2
->loc
->where
.row
));
2821 /* must be different key */
2822 /* ... below current line */
2823 /* ... pref'bly to left */
2825 && h2
->loc
->where
.row
>= i
){
2826 if(h2
->loc
->where
.col
> j
){
2837 whereis_pos
.row
= 0;
2839 if((result
= scroll_handle_obscured(next_handle
)) != 0){
2842 if(scroll_handle_obscured(sparms
->text
.handles
)
2844 next_handle
= sparms
->text
.handles
;
2846 ps_global
->mangled_body
++;
2847 new_top
= scroll_handle_reframe(next_handle
->key
,0);
2849 cur_top_line
= new_top
;
2853 else if(!(ch
== ctrl('N') || F_ON(F_FORCE_ARROWS
, ps_global
)))
2854 next_handle
= scroll_handle_next(sparms
->text
.handles
);
2858 if(cur_top_line
+ num_display_lines
< scroll_text_lines()){
2859 whereis_pos
.row
= 0;
2861 if(cur_top_line
+ num_display_lines
>= scroll_text_lines())
2862 q_status_message1(SM_INFO
, 0, 1, "END of %s",
2863 STYLE_NAME(sparms
));
2866 q_status_message1(SM_ORDER
, 0, 1, _("Already at end of %s"),
2867 STYLE_NAME(sparms
));
2873 /* ------ Scroll back up one line -------*/
2876 if(sparms
->text
.handles
){
2877 if(sparms
->vert_handle
){
2881 h2
= sparms
->text
.handles
;
2882 if(h2
->type
== Folder
&& h2
->prev
&& h2
->prev
->is_dual_do_open
)
2885 i
= h2
->loc
->where
.row
- 1;
2886 j
= h2
->loc
->where
.col
;
2888 for(h
= NULL
, k
= h2
->key
;
2890 || (h
->loc
->where
.row
== h2
->loc
->where
.row
));
2892 /* must be new key, above current
2893 * line and pref'bly to right
2896 && h2
->loc
->where
.row
<= i
){
2897 if(h2
->loc
->where
.col
< j
){
2908 whereis_pos
.row
= 0;
2910 if((result
= scroll_handle_obscured(next_handle
)) != 0){
2913 if(scroll_handle_obscured(sparms
->text
.handles
)
2915 next_handle
= sparms
->text
.handles
;
2917 ps_global
->mangled_body
++;
2918 new_top
= scroll_handle_reframe(next_handle
->key
,0);
2920 cur_top_line
= new_top
;
2924 else if(!(ch
== ctrl('P') || F_ON(F_FORCE_ARROWS
, ps_global
)))
2925 next_handle
= scroll_handle_prev(sparms
->text
.handles
);
2929 whereis_pos
.row
= 0;
2932 if(cur_top_line
== 0)
2933 q_status_message1(SM_INFO
, 0, 1, "START of %s",
2934 STYLE_NAME(sparms
));
2937 q_status_message1(SM_ORDER
, 0, 1,
2938 _("Already at start of %s"),
2939 STYLE_NAME(sparms
));
2945 case MC_NEXT_HANDLE
:
2946 if((next_handle
= scroll_handle_next_sel(sparms
->text
.handles
)) != NULL
){
2947 whereis_pos
.row
= 0;
2948 if((result
= scroll_handle_obscured(next_handle
)) != 0){
2951 if(scroll_handle_obscured(sparms
->text
.handles
)
2953 next_handle
= sparms
->text
.handles
;
2955 ps_global
->mangled_body
++;
2956 new_top
= scroll_handle_reframe(next_handle
->key
, 0);
2958 cur_top_line
= new_top
;
2962 if(scroll_handle_obscured(sparms
->text
.handles
)){
2965 ps_global
->mangled_body
++;
2966 if((new_top
= scroll_handle_reframe(-1, 0)) >= 0){
2967 whereis_pos
.row
= 0;
2968 cur_top_line
= new_top
;
2972 q_status_message1(SM_ORDER
, 0, 1,
2973 _("Already on last item in %s"),
2974 STYLE_NAME(sparms
));
2980 case MC_PREV_HANDLE
:
2981 if((next_handle
= scroll_handle_prev_sel(sparms
->text
.handles
)) != NULL
){
2982 whereis_pos
.row
= 0;
2983 if((result
= scroll_handle_obscured(next_handle
)) != 0){
2986 if(scroll_handle_obscured(sparms
->text
.handles
)
2988 next_handle
= sparms
->text
.handles
;
2990 ps_global
->mangled_body
++;
2991 new_top
= scroll_handle_reframe(next_handle
->key
, 0);
2993 cur_top_line
= new_top
;
2997 if(scroll_handle_obscured(sparms
->text
.handles
)){
3000 ps_global
->mangled_body
++;
3001 if((new_top
= scroll_handle_reframe(-1, 0)) >= 0){
3002 whereis_pos
.row
= 0;
3003 cur_top_line
= new_top
;
3007 q_status_message1(SM_ORDER
, 0, 1,
3008 _("Already on first item in %s"),
3009 STYLE_NAME(sparms
));
3015 /*------ View the current handle ------*/
3016 case MC_VIEW_HANDLE
:
3017 switch(scroll_handle_obscured(sparms
->text
.handles
)){
3020 switch(scroll_handle_launch(sparms
->text
.handles
,
3021 sparms
->text
.handles
->force_display
)){
3023 cmd
= MC_EXIT
; /* propagate */
3028 cmd_cancelled(NULL
);
3035 cur_top_line
= scroll_state(SS_CUR
)->top_text_line
;
3039 q_status_message(SM_ORDER
, 0, 2, HANDLE_BELOW_ERR
);
3043 q_status_message(SM_ORDER
, 0, 2, HANDLE_ABOVE_ERR
);
3049 /*---------- Search text (where is) ----------*/
3051 ps_global
->mangled_footer
= 1;
3053 int start_index
, key
= 0;
3054 char *report
= NULL
;
3056 start_row
= cur_top_line
;
3059 if(F_ON(F_SHOW_CURSOR
,ps_global
)){
3061 || found_on
>= scroll_text_lines()
3062 || found_on
< cur_top_line
3063 || found_on
>= cur_top_line
+ num_display_lines
){
3064 start_row
= cur_top_line
;
3068 if(found_on_index
< 0){
3069 start_row
= found_on
+ 1;
3073 start_row
= found_on
;
3074 start_index
= found_on_index
+1;
3078 else if(sparms
->srch_handle
){
3081 if((h
= scroll_handle_next_sel(sparms
->text
.handles
)) != NULL
){
3083 * Translate the screen's column into the
3084 * line offset to start on...
3086 * This makes it so search_text never returns -3
3087 * so we don't know it is the same match. That's
3088 * because we start well after the current handle
3089 * (at the next handle) and that causes us to
3090 * think the one we just matched on is a different
3091 * one from before. Can't think of an easy way to
3092 * fix it, though, and it isn't a big deal. We still
3093 * match, we just don't say current line contains
3096 start_row
= h
->loc
->where
.row
;
3097 start_index
= scroll_handle_index(start_row
, h
->loc
->where
.col
);
3100 /* last handle, start over at top */
3101 start_row
= cur_top_line
;
3106 start_row
= (found_on
< 0
3107 || found_on
>= scroll_text_lines()
3108 || found_on
< cur_top_line
3109 || found_on
>= cur_top_line
+ num_display_lines
)
3110 ? cur_top_line
: found_on
+ 1,
3114 found_on
= search_text(-FOOTER_ROWS(ps_global
), start_row
,
3115 start_index
, &report
,
3116 &whereis_pos
, &found_on_index
);
3118 if(found_on
== -4){ /* search to top of text */
3119 whereis_pos
.row
= 0;
3120 whereis_pos
.col
= 0;
3122 if(sparms
->text
.handles
&& sparms
->srch_handle
)
3125 else if(found_on
== -5){ /* search to bottom of text */
3128 whereis_pos
.row
= MAX(scroll_text_lines() - 1, 0);
3129 whereis_pos
.col
= 0;
3130 found_on
= whereis_pos
.row
;
3131 if((h
= sparms
->text
.handles
) && sparms
->srch_handle
)
3134 while((h
= h
->next
) != NULL
);
3136 else if(found_on
== -3){
3137 whereis_pos
.row
= found_on
= start_row
;
3138 found_on_index
= start_index
- 1;
3139 q_status_message(SM_ORDER
, 1, 3,
3140 _("Current line contains the only match"));
3144 result
= found_on
< cur_top_line
;
3146 key
= (sparms
->text
.handles
)
3147 ? dot_on_handle(found_on
, whereis_pos
.col
) : 0;
3149 if(F_ON(F_FORCE_LOW_SPEED
,ps_global
)
3150 || ps_global
->low_speed
3151 || F_ON(F_SHOW_CURSOR
,ps_global
)
3153 if((found_on
>= cur_top_line
+ num_display_lines
||
3154 found_on
< cur_top_line
) &&
3155 num_display_lines
> ps_global
->viewer_overlap
){
3156 cur_top_line
= found_on
- ((found_on
> 0) ? 1 : 0);
3157 if(scroll_text_lines()-cur_top_line
< 5)
3158 cur_top_line
= MAX(0,
3159 scroll_text_lines()-MIN(5,num_display_lines
));
3161 /* else leave cur_top_line alone */
3164 cur_top_line
= found_on
- ((found_on
> 0) ? 1 : 0);
3165 if(scroll_text_lines()-cur_top_line
< 5)
3166 cur_top_line
= MAX(0,
3167 scroll_text_lines()-MIN(5,num_display_lines
));
3170 whereis_pos
.row
= whereis_pos
.row
- cur_top_line
+ 1;
3172 q_status_message(SM_ORDER
, 0, 3, report
);
3174 q_status_message2(SM_ORDER
, 0, 3,
3175 "%sFound on line %s on screen",
3176 result
? "Search wrapped to start. " : "",
3177 int2string(whereis_pos
.row
));
3180 if(sparms
->text
.handles
->key
< key
)
3181 for(next_handle
= sparms
->text
.handles
->next
;
3182 next_handle
->key
!= key
;
3183 next_handle
= next_handle
->next
)
3186 for(next_handle
= sparms
->text
.handles
;
3187 next_handle
->key
!= key
;
3188 next_handle
= next_handle
->prev
)
3192 else if(found_on
== -1)
3193 cmd_cancelled("Search");
3195 q_status_message(SM_ORDER
, 0, 3, _("Word not found"));
3201 /*-------------- jump command -------------*/
3202 /* NOTE: preempt the process_cmd() version because
3203 * we need to get at the number..
3206 jn
= jump_to(ps_global
->msgmap
, -FOOTER_ROWS(ps_global
), ch
,
3208 if(sparms
&& sparms
->jump_is_debug
)
3210 else if(jn
> 0 && jn
!= mn_get_cur(ps_global
->msgmap
)){
3212 if(mn_total_cur(ps_global
->msgmap
) > 1L)
3213 mn_reset_cur(ps_global
->msgmap
, jn
);
3215 mn_set_cur(ps_global
->msgmap
, jn
);
3220 ps_global
->mangled_footer
= 1;
3226 /*-------------- Mouse Event -------------*/
3233 mouse_get_last (NULL
, &mp
);
3236 /* The clicked line have anything special on it? */
3237 if((line
= cur_top_line
+ mp
.row
) < scroll_text_lines()
3238 && (key
= dot_on_handle(line
, mp
.col
))){
3240 case M_BUTTON_RIGHT
:
3242 if(sparms
->mouse
.popup
){
3243 if(sparms
->text
.handles
->key
< key
)
3244 for(next_handle
= sparms
->text
.handles
->next
;
3245 next_handle
->key
!= key
;
3246 next_handle
= next_handle
->next
)
3249 for(next_handle
= sparms
->text
.handles
;
3250 next_handle
->key
!= key
;
3251 next_handle
= next_handle
->prev
)
3254 if(sparms
->mouse
.popup
){
3255 cur_top_line
= scroll_scroll_text(cur_top_line
,
3257 ps_global
->mangled_body
);
3259 switch((*sparms
->mouse
.popup
)(sparms
, key
)){
3261 cur_top_line
= doubleclick_handle(sparms
, next_handle
, &cmd
, &done
);
3274 case M_BUTTON_LEFT
:
3275 if(sparms
->text
.handles
->key
< key
)
3276 for(next_handle
= sparms
->text
.handles
->next
;
3277 next_handle
->key
!= key
;
3278 next_handle
= next_handle
->next
)
3281 for(next_handle
= sparms
->text
.handles
;
3282 next_handle
->key
!= key
;
3283 next_handle
= next_handle
->prev
)
3286 if(mp
.doubleclick
) /* launch url */
3287 cur_top_line
= doubleclick_handle(sparms
, next_handle
, &cmd
, &done
);
3288 else if(sparms
->mouse
.click
)
3289 (*sparms
->mouse
.click
)(sparms
);
3293 case M_BUTTON_MIDDLE
: /* NO-OP for now */
3296 default: /* just ignore */
3301 else if(mp
.button
== M_BUTTON_RIGHT
){
3303 * Toss generic popup on to the screen
3305 if(sparms
->mouse
.popup
)
3306 if((*sparms
->mouse
.popup
)(sparms
, 0) == 2){
3317 /*-------------- Display Resize -------------*/
3319 if(sparms
->resize_exit
){
3323 * Figure out char offset of the char in the top left
3324 * corner of the display. Pass it back to the
3325 * fetcher/formatter and have it pass the offset
3328 sparms
->start
.on
= Offset
;
3329 for(sparms
->start
.loc
.offset
= line
= 0L;
3330 line
< cur_top_line
;
3332 sparms
->start
.loc
.offset
+= scroll_handle_column(line
, -1);
3338 /* else no reformatting neccessary, fall thru to repaint */
3341 /*-------------- refresh -------------*/
3343 num_display_lines
= SCROLL_LINES(ps_global
);
3344 mark_status_dirty();
3345 mark_keymenu_dirty();
3346 mark_titlebar_dirty();
3347 ps_global
->mangled_screen
= 1;
3352 /*------- no op timeout to check for new mail ------*/
3357 /*------- Forward displayed text ------*/
3359 forward_text(ps_global
, sparms
->text
.text
, sparms
->text
.src
);
3363 /*----------- Save the displayed text ------------*/
3365 (void)simple_export(ps_global
, sparms
->text
.text
,
3366 sparms
->text
.src
, "text", NULL
);
3370 /*----------- Exit this screen ------------*/
3376 /*----------- Pop back to the Main Menu ------------*/
3378 ps_global
->next_screen
= main_menu_screen
;
3383 /*----------- Print ------------*/
3385 print_to_printer(sparms
);
3389 /* ------- First handle on Line ------ */
3391 if(sparms
->text
.handles
){
3392 next_handle
= scroll_handle_boundary(sparms
->text
.handles
,
3393 scroll_handle_prev_sel
);
3397 /* fall thru as bogus */
3399 /* ------- Last handle on Line ------ */
3401 if(sparms
->text
.handles
){
3402 next_handle
= scroll_handle_boundary(sparms
->text
.handles
,
3403 scroll_handle_next_sel
);
3407 /* fall thru as bogus */
3409 /*------- BOGUS INPUT ------*/
3413 if(sparms
->bogus_input
)
3414 done
= (*sparms
->bogus_input
)(ch
);
3416 bogus_command(ch
, F_ON(F_USE_FK
,ps_global
) ? "F1" : "?");
3422 bogus_utf8_command(utf8str
, F_ON(F_USE_FK
, ps_global
) ? "F1" : "?");
3426 /*------- Standard commands ------*/
3428 whereis_pos
.row
= 0;
3429 if(sparms
->proc
.tool
)
3430 result
= (*sparms
->proc
.tool
)(cmd
, ps_global
->msgmap
, sparms
);
3432 result
= process_cmd(ps_global
, ps_global
->mail_stream
,
3433 ps_global
->msgmap
, cmd
, View
, &force
);
3435 dprint((7, "PROCESS_CMD return: %d\n", result
));
3437 if(ps_global
->next_screen
!= SCREEN_FUN_NULL
|| result
== 1){
3439 if(cmd
== MC_FULLHDR
){
3440 if(ps_global
->full_header
== 1){
3444 * Figure out char offset of the char in the top left
3445 * corner of the display. Pass it back to the
3446 * fetcher/formatter and have it pass the offset
3449 sparms
->start
.on
= Offset
;
3450 for(sparms
->start
.loc
.offset
= line
= 0L;
3451 line
< cur_top_line
;
3453 sparms
->start
.loc
.offset
+=
3454 scroll_handle_column(line
, -1);
3457 sparms
->start
.on
= 0;
3461 sparms
->keys
.what
= FirstMenu
;
3464 sparms
->keys
.what
= SecondMenu
;
3467 sparms
->keys
.what
= ThirdMenu
;
3470 sparms
->keys
.what
= FourthMenu
;
3475 else if(!scroll_state(SS_CUR
)){
3476 num_display_lines
= SCROLL_LINES(ps_global
);
3477 ps_global
->mangled_screen
= 1;
3482 } /* End of switch() */
3484 /* Need to frame some handles? */
3485 if(sparms
->text
.handles
3487 && handle_on_page(sparms
->text
.handles
, cur_top_line
,
3488 cur_top_line
+ num_display_lines
))
3490 && handle_on_page(next_handle
, cur_top_line
,
3491 cur_top_line
+ num_display_lines
))))
3492 next_handle
= scroll_handle_in_frame(cur_top_line
);
3494 } /* End of while() -- loop executing commands */
3496 ps_global
->redrawer
= NULL
; /* next statement makes this invalid! */
3497 zero_scroll_text(); /* very important to zero out on return!!! */
3498 scroll_state(SS_FREE
);
3499 if(sparms
->bar
.color
)
3500 free_color_pair(&sparms
->bar
.color
);
3503 scroll_setrange(0L, 0L);
3509 /*----------------------------------------------------------------------
3512 Args: text -- The text to print out
3513 source -- What type of source text is
3514 message -- Message for open_printer()
3515 Handling of error conditions is very poor.
3519 print_to_printer(SCROLL_S
*sparms
)
3523 snprintf(message
, sizeof(message
), "%s", STYLE_NAME(sparms
));
3524 message
[sizeof(message
)-1] = '\0';
3526 if(open_printer(message
) != 0)
3529 switch(sparms
->text
.src
){
3531 if(sparms
->text
.text
!= (char *)NULL
)
3532 print_text((char *)sparms
->text
.text
);
3537 if(sparms
->text
.text
!= (char **)NULL
){
3540 for(t
= sparms
->text
.text
; *t
!= NULL
; t
++){
3542 print_text(NEWLINE
);
3549 if(sparms
->text
.text
!= (FILE *)NULL
) {
3553 fseek((FILE *)sparms
->text
.text
, 0L, 0);
3554 n
= SIZEOF_20KBUF
- 1;
3555 while((i
= fread((void *)tmp_20k_buf
, sizeof(char),
3556 n
, (FILE *)sparms
->text
.text
)) != 0) {
3557 tmp_20k_buf
[i
] = '\0';
3558 print_text(tmp_20k_buf
);
3571 /*----------------------------------------------------------------------
3572 Search text being viewed (help or message)
3574 Args: q_line -- The screen line to prompt for search string on
3575 start_line -- Line number in text to begin search on
3576 start_index -- Where to begin search at in first line of text
3577 cursor_pos -- position of cursor is returned to caller here
3578 (Actually, this isn't really the position of the
3579 cursor because we don't know where we are on the
3580 screen. So row is set to the line number and col
3581 is set to the right column.)
3582 offset_in_line -- Offset where match was found.
3584 Result: returns line number string was found on
3587 -3 if only match is at start_index - 1
3588 -4 if search to first line
3589 -5 if search to last line
3592 search_text(int q_line
, long int start_line
, int start_index
, char **report
,
3593 Pos
*cursor_pos
, int *offset_in_line
)
3595 char prompt
[MAX_SEARCH
+50], nsearch_string
[MAX_SEARCH
+1], *p
;
3598 static HISTORY_S
*history
= NULL
;
3599 char search_string
[MAX_SEARCH
+1];
3600 static ESCKEY_S word_search_key
[] = { { 0, 0, "", "" },
3601 {ctrl('Y'), 10, "^Y", N_("First Line")},
3602 {ctrl('V'), 11, "^V", N_("Last Line")},
3603 {KEY_UP
, 30, "", ""},
3604 {KEY_DOWN
, 31, "", ""},
3607 #define KU_ST (3) /* index of KEY_UP */
3609 init_hist(&history
, HISTSIZE
);
3612 * Put the last one used in the default search_string,
3613 * not in nsearch_string.
3615 search_string
[0] = '\0';
3616 if((p
= get_prev_hist(history
, "", 0, NULL
)) != NULL
){
3617 strncpy(search_string
, p
, sizeof(search_string
));
3618 search_string
[sizeof(search_string
)-1] = '\0';
3621 snprintf(prompt
, sizeof(prompt
), _("Word to search for [%s] : "), search_string
);
3623 nsearch_string
[0] = '\0';
3626 flags
= OE_APPEND_CURRENT
| OE_SEQ_SENSITIVE
| OE_KEEP_TRAILING_SPACE
;
3629 * 2 is really 1 because there will be one real entry and
3630 * one entry of "" because of the get_prev_hist above.
3632 if(items_in_hist(history
) > 2){
3633 word_search_key
[KU_ST
].name
= HISTORY_UP_KEYNAME
;
3634 word_search_key
[KU_ST
].label
= HISTORY_KEYLABEL
;
3635 word_search_key
[KU_ST
+1].name
= HISTORY_DOWN_KEYNAME
;
3636 word_search_key
[KU_ST
+1].label
= HISTORY_KEYLABEL
;
3639 word_search_key
[KU_ST
].name
= "";
3640 word_search_key
[KU_ST
].label
= "";
3641 word_search_key
[KU_ST
+1].name
= "";
3642 word_search_key
[KU_ST
+1].label
= "";
3645 rc
= optionally_enter(nsearch_string
, q_line
, 0, sizeof(nsearch_string
),
3646 prompt
, word_search_key
, help
, &flags
);
3649 help
= help
== NO_HELP
? h_oe_searchview
: NO_HELP
;
3654 *report
= _("Searched to First Line.");
3660 *report
= _("Searched to Last Line.");
3665 if((p
= get_prev_hist(history
, nsearch_string
, 0, NULL
)) != NULL
){
3666 strncpy(nsearch_string
, p
, sizeof(nsearch_string
));
3667 nsearch_string
[sizeof(nsearch_string
)-1] = '\0';
3675 if((p
= get_next_hist(history
, nsearch_string
, 0, NULL
)) != NULL
){
3676 strncpy(nsearch_string
, p
, sizeof(nsearch_string
));
3677 nsearch_string
[sizeof(nsearch_string
)-1] = '\0';
3685 if(rc
!= 4){ /* 4 is redraw */
3686 save_hist(history
, nsearch_string
, 0, NULL
);
3691 if(rc
== 1 || (search_string
[0] == '\0' && nsearch_string
[0] == '\0'))
3694 if(nsearch_string
[0] != '\0'){
3695 strncpy(search_string
, nsearch_string
, sizeof(search_string
)-1);
3696 search_string
[sizeof(search_string
)-1] = '\0';
3699 rc
= search_scroll_text(start_line
, start_index
, search_string
, cursor_pos
,
3705 /*----------------------------------------------------------------------
3706 Update the scroll tool's titlebar
3708 Args: cur_top_line --
3709 redraw -- flag to force updating
3713 update_scroll_titlebar(long int cur_top_line
, int redraw
)
3715 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
3716 int num_display_lines
= SCROLL_LINES(ps_global
);
3717 long new_line
= (cur_top_line
+ num_display_lines
> st
->num_lines
)
3719 : cur_top_line
+ num_display_lines
;
3721 COLOR_PAIR
*returned_color
= NULL
;
3722 COLOR_PAIR
*titlecolor
= NULL
;
3724 SEARCHSET
*ss
= NULL
;
3726 if(st
->parms
->use_indexline_color
3727 && ps_global
->titlebar_color_style
!= TBAR_COLOR_DEFAULT
){
3728 raw_msgno
= mn_m2raw(ps_global
->msgmap
, mn_get_cur(ps_global
->msgmap
));
3729 if(raw_msgno
> 0L && ps_global
->mail_stream
3730 && raw_msgno
<= ps_global
->mail_stream
->nmsgs
){
3731 ss
= mail_newsearchset();
3732 ss
->first
= ss
->last
= (unsigned long) raw_msgno
;
3736 PAT_STATE
*pstate
= NULL
;
3738 colormatch
= get_index_line_color(ps_global
->mail_stream
,
3739 ss
, &pstate
, &returned_color
);
3740 mail_free_searchset(&ss
);
3743 * This is a bit tricky. If there is a colormatch but returned_color
3744 * is NULL, that means that the user explicitly wanted the
3745 * Normal color used in this index line, so that is what we
3746 * use. If no colormatch then we will use the TITLE color
3747 * instead of Normal.
3751 titlecolor
= returned_color
;
3753 titlecolor
= new_color_pair(ps_global
->VAR_NORM_FORE_COLOR
,
3754 ps_global
->VAR_NORM_BACK_COLOR
);
3758 && ps_global
->titlebar_color_style
== TBAR_COLOR_REV_INDEXLINE
){
3759 char cbuf
[MAXCOLORLEN
+1];
3761 strncpy(cbuf
, titlecolor
->fg
, MAXCOLORLEN
);
3762 strncpy(titlecolor
->fg
, titlecolor
->bg
, MAXCOLORLEN
);
3763 strncpy(titlecolor
->bg
, cbuf
, MAXCOLORLEN
);
3767 /* Did the color change? */
3768 if((!titlecolor
&& st
->parms
->bar
.color
)
3770 (titlecolor
&& !st
->parms
->bar
.color
)
3772 (titlecolor
&& st
->parms
->bar
.color
3773 && (strcmp(titlecolor
->fg
, st
->parms
->bar
.color
->fg
)
3774 || strcmp(titlecolor
->bg
, st
->parms
->bar
.color
->bg
)))){
3777 if(st
->parms
->bar
.color
)
3778 free_color_pair(&st
->parms
->bar
.color
);
3780 st
->parms
->bar
.color
= titlecolor
;
3785 free_color_pair(&titlecolor
);
3790 set_titlebar(st
->parms
->bar
.title
, ps_global
->mail_stream
,
3791 ps_global
->context_current
, ps_global
->cur_folder
,
3792 ps_global
->msgmap
, 1, st
->parms
->bar
.style
,
3793 new_line
, st
->num_lines
, st
->parms
->bar
.color
);
3794 ps_global
->mangled_header
= 0;
3796 else if(st
->parms
->bar
.style
== TextPercent
)
3797 update_titlebar_lpercent(new_line
);
3799 update_titlebar_percent(new_line
);
3803 /*----------------------------------------------------------------------
3804 manager of global (to this module, anyway) scroll state structures
3809 scroll_state(int func
)
3811 struct scrollstack
{
3813 struct scrollstack
*prev
;
3815 static struct scrollstack
*stack
= NULL
;
3818 case SS_CUR
: /* no op */
3821 s
= (struct scrollstack
*)fs_get(sizeof(struct scrollstack
));
3822 memset((void *)s
, 0, sizeof(struct scrollstack
));
3829 fs_give((void **)&stack
);
3833 default: /* BUG: should complain */
3837 return(stack
? &stack
->s
: NULL
);
3841 /*----------------------------------------------------------------------
3842 Save all the data for scrolling text and paint the screen
3847 set_scroll_text(SCROLL_S
*sparms
, long int current_line
, SCRLCTRL_S
*st
)
3849 /* save all the stuff for possible asynchronous redraws */
3851 st
->top_text_line
= current_line
;
3852 st
->screen
.start_line
= SCROLL_LINES_ABOVE(ps_global
);
3853 st
->screen
.other_lines
= SCROLL_LINES_ABOVE(ps_global
)
3854 + SCROLL_LINES_BELOW(ps_global
);
3855 st
->screen
.width
= -1; /* Force text formatting calculation */
3859 /*----------------------------------------------------------------------
3860 Redraw the text on the screen, possibly reformatting if necessary
3866 redraw_scroll_text(void)
3869 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
3871 format_scroll_text();
3873 offset
= (st
->parms
->text
.src
== FileStar
) ? 0 : st
->top_text_line
;
3876 mswin_beginupdate();
3878 /*---- Actually display the text on the screen ------*/
3879 for(i
= 0; i
< st
->screen
.length
; i
++){
3880 ClearLine(i
+ st
->screen
.start_line
);
3881 if((offset
+ i
) < st
->num_lines
)
3882 PutLine0n8b(i
+ st
->screen
.start_line
, 0, st
->text_lines
[offset
+ i
],
3883 st
->line_lengths
[offset
+ i
], st
->parms
->text
.handles
);
3894 /*----------------------------------------------------------------------
3895 Free memory used as scrolling buffers for text on disk. Also mark
3896 text_lines as available
3899 zero_scroll_text(void)
3901 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
3904 for(i
= 0; i
< st
->lines_allocated
; i
++)
3905 if(st
->parms
->text
.src
== FileStar
&& st
->text_lines
[i
])
3906 fs_give((void **)&st
->text_lines
[i
]);
3908 st
->text_lines
[i
] = NULL
;
3910 if(st
->parms
->text
.src
== FileStar
&& st
->findex
!= NULL
){
3914 our_unlink(st
->fname
);
3915 fs_give((void **)&st
->fname
);
3920 fs_give((void **)&st
->text_lines
);
3922 if(st
->line_lengths
)
3923 fs_give((void **) &st
->line_lengths
);
3927 /*----------------------------------------------------------------------
3929 Always format at least 20 chars wide. Wrapping lines would be crazy for
3930 screen widths of 1-20 characters
3933 format_scroll_text(void)
3937 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
3939 register char **tl
, **tl_end
;
3941 if(!st
|| (st
->screen
.width
== (i
= ps_global
->ttyo
->screen_cols
)
3942 && st
->screen
.length
== PGSIZE(st
)))
3945 st
->screen
.width
= MAX(20, i
);
3946 st
->screen
.length
= PGSIZE(st
);
3948 if(st
->lines_allocated
== 0) {
3949 st
->lines_allocated
= TYPICAL_BIG_MESSAGE_LINES
;
3950 st
->text_lines
= (char **)fs_get(st
->lines_allocated
*sizeof(char *));
3951 memset(st
->text_lines
, 0, st
->lines_allocated
* sizeof(char *));
3952 st
->line_lengths
= (short *)fs_get(st
->lines_allocated
*sizeof(short));
3955 tl
= st
->text_lines
;
3956 ll
= st
->line_lengths
;
3957 tl_end
= &st
->text_lines
[st
->lines_allocated
];
3959 if(st
->parms
->text
.src
== CharStarStar
) {
3960 /*---- original text is already list of lines -----*/
3961 /* The text could be wrapped nicely for narrow screens; for now
3962 it will get truncated as it is displayed */
3963 for(pp
= (char **)st
->parms
->text
.text
; *pp
!= NULL
;) {
3965 *ll
++ = st
->screen
.width
;
3967 i
= tl
- st
->text_lines
;
3968 st
->lines_allocated
*= 2;
3969 fs_resize((void **)&st
->text_lines
,
3970 st
->lines_allocated
* sizeof(char *));
3971 fs_resize((void **)&st
->line_lengths
,
3972 st
->lines_allocated
*sizeof(short));
3973 tl
= &st
->text_lines
[i
];
3974 ll
= &st
->line_lengths
[i
];
3975 tl_end
= &st
->text_lines
[st
->lines_allocated
];
3979 st
->num_lines
= tl
- st
->text_lines
;
3981 else if (st
->parms
->text
.src
== CharStar
) {
3982 /*------ Format the plain text ------*/
3983 for(p
= (char *)st
->parms
->text
.text
; *p
; ) {
3986 for(; *p
&& !(*p
== RETURN
|| *p
== LINE_FEED
); p
++)
3992 i
= tl
- st
->text_lines
;
3993 st
->lines_allocated
*= 2;
3994 fs_resize((void **)&st
->text_lines
,
3995 st
->lines_allocated
* sizeof(char *));
3996 fs_resize((void **)&st
->line_lengths
,
3997 st
->lines_allocated
*sizeof(short));
3998 tl
= &st
->text_lines
[i
];
3999 ll
= &st
->line_lengths
[i
];
4000 tl_end
= &st
->text_lines
[st
->lines_allocated
];
4003 if(*p
== '\r' && *(p
+1) == '\n')
4005 else if(*p
== '\n' || *p
== '\r')
4009 st
->num_lines
= tl
- st
->text_lines
;
4012 /*------ Display text is in a file --------*/
4015 * This is pretty much only useful under DOS where we can't fit
4016 * all of big messages in core at once. This scheme makes
4017 * some simplifying assumptions:
4018 * 1. Lines are on disk just the way we'll display them. That
4019 * is, line breaks and such are left to the function that
4020 * writes the disk file to catch and fix.
4021 * 2. We get away with this mainly because the DOS display isn't
4022 * going to be resized out from under us.
4024 * The idea is to use the already alloc'd array of char * as a
4025 * buffer for sections of what's on disk. We'll set up the first
4026 * few lines here, and read new ones in as needed in
4027 * scroll_scroll_text().
4029 * but first, make sure there are enough buffer lines allocated
4030 * to serve as a place to hold lines from the file.
4032 * Actually, this is also used under windows so the display will
4033 * be resized out from under us. So I changed the following
4035 * 1. free old text_lines, which may have been allocated
4036 * for a narrow screen.
4037 * 2. insure we have enough text_lines
4038 * 3. reallocate all text_lines that are needed.
4039 * (tom unger 10/26/94)
4042 /* free old text lines, which may be too short. */
4043 for(i
= 0; i
< st
->lines_allocated
; i
++)
4044 if(st
->text_lines
[i
]) /* clear alloc'd lines */
4045 fs_give((void **)&st
->text_lines
[i
]);
4047 /* Insure we have enough text lines. */
4048 if(st
->lines_allocated
< (2 * PGSIZE(st
)) + 1){
4049 st
->lines_allocated
= (2 * PGSIZE(st
)) + 1; /* resize */
4051 fs_resize((void **)&st
->text_lines
,
4052 st
->lines_allocated
* sizeof(char *));
4053 memset(st
->text_lines
, 0, st
->lines_allocated
* sizeof(char *));
4054 fs_resize((void **)&st
->line_lengths
,
4055 st
->lines_allocated
*sizeof(short));
4058 /* reallocate all text lines that are needed. */
4059 for(i
= 0; i
<= PGSIZE(st
); i
++)
4060 if(st
->text_lines
[i
] == NULL
)
4061 st
->text_lines
[i
] = (char *)fs_get((st
->screen
.width
+ 1)
4064 tl
= &st
->text_lines
[i
];
4066 st
->num_lines
= make_file_index();
4068 ScrollFile(st
->top_text_line
); /* then load them up */
4072 * Efficiency hack. If there are handles, fill in their
4073 * line number field for later...
4075 if(st
->parms
->text
.handles
){
4080 for(line
= 0; line
< st
->num_lines
; line
++)
4081 for(i
= 0, col
= 0; i
< st
->line_lengths
[line
];)
4082 switch(st
->text_lines
[line
][i
]){
4085 switch((i
< st
->line_lengths
[line
]) ? st
->text_lines
[line
][i
]
4088 for(key
= 0, n
= st
->text_lines
[line
][++i
]; n
> 0; n
--)
4089 key
= (key
* 10) + (st
->text_lines
[line
][++i
] - '0');
4092 for(h
= st
->parms
->text
.handles
; h
; h
= h
->next
)
4094 scroll_handle_set_loc(&h
->loc
, line
, col
);
4098 if(!h
) /* anything behind us? */
4099 for(h
= st
->parms
->text
.handles
->prev
; h
; h
= h
->prev
)
4101 scroll_handle_set_loc(&h
->loc
, line
, col
);
4109 i
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
4121 default: /* literal embed char */
4129 while(((++col
) & 0x07) != 0) /* add tab's spaces */
4135 col
+= width_at_this_position((unsigned char*) &st
->text_lines
[line
][i
],
4136 st
->line_lengths
[line
] - i
);
4137 i
++; /* character count */
4143 scroll_setrange (st
->screen
.length
, st
->num_lines
);
4151 * ScrollFile - scroll text into the st struct file making sure 'line'
4152 * of the file is the one first in the text_lines buffer.
4154 * NOTE: talk about massive potential for tuning...
4155 * Goes without saying this is still under constuction
4158 ScrollFile(long int line
)
4160 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4164 if(line
<= 0){ /* reset and load first couple of pages */
4165 fseek((FILE *) st
->parms
->text
.text
, 0L, 0);
4172 for(i
= 0; i
< PGSIZE(st
); i
++){
4173 /*** do stuff to get the file pointer into the right place ***/
4175 * BOGUS: this is painfully crude right now, but I just want to get
4178 * possibly in the near furture, an array of indexes into the
4179 * file that are the offset for the beginning of each line will
4180 * speed things up. Of course, this
4181 * will have limits, so maybe a disk file that is an array
4182 * of indexes is the answer.
4184 if(fseek(st
->findex
, (size_t)(line
++) * sizeof(SCRLFILE_S
), 0) < 0
4185 || fread(&sf
, sizeof(SCRLFILE_S
), (size_t)1, st
->findex
) != 1
4186 || fseek((FILE *) st
->parms
->text
.text
, sf
.offset
, 0) < 0
4187 || !st
->text_lines
[i
]
4188 || (sf
.len
&& !fgets(st
->text_lines
[i
], sf
.len
+ 1,
4189 (FILE *) st
->parms
->text
.text
)))
4192 st
->line_lengths
[i
] = sf
.len
;
4195 for(; i
< PGSIZE(st
); i
++)
4196 if(st
->text_lines
[i
]){ /* blank out any unused lines */
4197 *st
->text_lines
[i
] = '\0';
4198 st
->line_lengths
[i
] = 0;
4204 * make_file_index - do a single pass over the file containing the text
4205 * to display, recording line lengths and offsets.
4206 * NOTE: This is never really to be used on a real OS with virtual
4207 * memory. This is the whole reason st->findex exists. Don't
4208 * want to waste precious memory on a stupid array that could
4212 make_file_index(void)
4214 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4221 st
->fname
= temp_nam(NULL
, "pi");
4223 if(!st
->fname
|| (st
->findex
= our_fopen(st
->fname
,"w+b")) == NULL
){
4225 our_unlink(st
->fname
);
4226 fs_give((void **)&st
->fname
);
4233 fseek(st
->findex
, 0L, 0);
4235 fseek((FILE *)st
->parms
->text
.text
, 0L, 0);
4238 sf
.len
= st
->screen
.width
+ 1;
4239 if(scroll_file_line((FILE *) st
->parms
->text
.text
,
4240 tmp_20k_buf
, &sf
, &state
)){
4241 fwrite((void *) &sf
, sizeof(SCRLFILE_S
), (size_t)1, st
->findex
);
4248 fseek((FILE *)st
->parms
->text
.text
, 0L, 0);
4254 /*----------------------------------------------------------------------
4255 Get the next line to scroll from the given file
4259 scroll_file_line(FILE *fp
, char *buf
, SCRLFILE_S
*sfp
, int *wrapt
)
4261 register char *s
= NULL
;
4265 sfp
->offset
= ftell(fp
);
4266 if(!(s
= fgets(buf
, sfp
->len
, fp
)))
4267 return(NULL
); /* can't grab a line? */
4271 *wrapt
= 1; /* remember; that we wrapped */
4274 else if(*s
== NEWLINE
[0] && (!NEWLINE
[1] || *(s
+1) == NEWLINE
[1])){
4275 int empty
= (*wrapt
&& s
== buf
);
4277 *wrapt
= 0; /* turn off wrapped state */
4279 s
= NULL
; /* get a new line */
4292 /*----------------------------------------------------------------------
4293 Scroll the text on the screen
4295 Args: new_top_line -- The line to be displayed on top of the screen
4296 redraw -- Flag to force a redraw even if nothing changed
4298 Returns: resulting top line
4299 Note: the returned line number may be less than new_top_line if
4300 reformatting caused the total line count to change.
4304 scroll_scroll_text(long int new_top_line
, HANDLE_S
*handle
, int redraw
)
4306 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4307 int num_display_lines
, l
, top
;
4308 POSLIST_S
*lp
, *lp2
;
4310 /* When this is true, we're still on the same page of the display. */
4311 if(st
->top_text_line
== new_top_line
&& !redraw
){
4312 /* handle changed, so hilite the new handle and unhilite the old */
4313 if(handle
&& handle
!= st
->parms
->text
.handles
){
4314 top
= st
->screen
.start_line
- new_top_line
;
4315 /* hilite the new one */
4316 if(!scroll_handle_obscured(handle
))
4317 for(lp
= handle
->loc
; lp
; lp
= lp
->next
)
4318 if((l
= lp
->where
.row
) >= st
->top_text_line
4319 && l
< st
->top_text_line
+ st
->screen
.length
){
4320 if(st
->parms
->text
.src
== FileStar
)
4323 PutLine0n8b(top
+ lp
->where
.row
, 0, st
->text_lines
[l
],
4324 st
->line_lengths
[l
], handle
);
4327 /* unhilite the old one */
4328 if(!scroll_handle_obscured(st
->parms
->text
.handles
))
4329 for(lp
= st
->parms
->text
.handles
->loc
; lp
; lp
= lp
->next
)
4330 if((l
= lp
->where
.row
) >= st
->top_text_line
4331 && l
< st
->top_text_line
+ st
->screen
.length
){
4332 for(lp2
= handle
->loc
; lp2
; lp2
= lp2
->next
)
4333 if(l
== lp2
->where
.row
)
4337 if(st
->parms
->text
.src
== FileStar
)
4340 PutLine0n8b(top
+ lp
->where
.row
, 0, st
->text_lines
[l
],
4341 st
->line_lengths
[l
], handle
);
4345 st
->parms
->text
.handles
= handle
; /* update current */
4348 return(new_top_line
);
4351 num_display_lines
= PGSIZE(st
);
4353 format_scroll_text();
4355 if(st
->top_text_line
>= st
->num_lines
) /* don't pop line count */
4356 new_top_line
= st
->top_text_line
= MAX(st
->num_lines
- 1, 0);
4358 if(st
->parms
->text
.src
== FileStar
)
4359 ScrollFile(new_top_line
); /* set up new st->text_lines */
4362 scroll_setrange (st
->screen
.length
, st
->num_lines
);
4363 scroll_setpos (new_top_line
);
4367 Check out the scrolling situation. If we want to scroll, but BeginScroll
4368 says we can't then repaint, + 10 is so we repaint most of the time.
4371 (st
->top_text_line
- new_top_line
+ 10 >= num_display_lines
||
4372 new_top_line
- st
->top_text_line
+ 10 >= num_display_lines
) ||
4373 BeginScroll(st
->screen
.start_line
,
4374 st
->screen
.start_line
+ num_display_lines
- 1) != 0) {
4375 /* Too much text to scroll, or can't scroll -- just repaint */
4378 st
->parms
->text
.handles
= handle
;
4380 st
->top_text_line
= new_top_line
;
4381 redraw_scroll_text();
4385 * We're going to scroll the screen, but first we have to make sure
4386 * the old hilited handles are unhilited if they are going to remain
4389 top
= st
->screen
.start_line
- st
->top_text_line
;
4390 if(handle
&& handle
!= st
->parms
->text
.handles
4391 && st
->parms
->text
.handles
4392 && !scroll_handle_obscured(st
->parms
->text
.handles
))
4393 for(lp
= st
->parms
->text
.handles
->loc
; lp
; lp
= lp
->next
)
4394 if((l
= lp
->where
.row
) >= MAX(st
->top_text_line
,new_top_line
)
4395 && l
< MIN(st
->top_text_line
,new_top_line
) + st
->screen
.length
){
4396 if(st
->parms
->text
.src
== FileStar
)
4399 PutLine0n8b(top
+ lp
->where
.row
, 0, st
->text_lines
[l
],
4400 st
->line_lengths
[l
], handle
);
4403 if(new_top_line
> st
->top_text_line
){
4404 /*------ scroll down ------*/
4405 while(new_top_line
> st
->top_text_line
) {
4408 l
= (st
->parms
->text
.src
== FileStar
)
4409 ? num_display_lines
- (new_top_line
- st
->top_text_line
)
4410 : st
->top_text_line
+ num_display_lines
;
4412 if(l
< st
->num_lines
){
4413 PutLine0n8b(st
->screen
.start_line
+ num_display_lines
- 1,
4414 0, st
->text_lines
[l
], st
->line_lengths
[l
],
4415 handle
? handle
: st
->parms
->text
.handles
);
4417 * We clear to the end of line in the right background
4418 * color. If the line was exactly the width of the screen
4419 * then PutLine0n8b will have left _col and _row moved to
4420 * the start of the next row. We don't need or want to clear
4423 if(pico_usingcolor()
4424 && (st
->line_lengths
[l
] < ps_global
->ttyo
->screen_cols
4425 || visible_linelen(l
) < ps_global
->ttyo
->screen_cols
))
4429 st
->top_text_line
++;
4433 /*------ scroll up -----*/
4434 while(new_top_line
< st
->top_text_line
) {
4437 st
->top_text_line
--;
4438 l
= (st
->parms
->text
.src
== FileStar
)
4439 ? st
->top_text_line
- new_top_line
4440 : st
->top_text_line
;
4441 PutLine0n8b(st
->screen
.start_line
, 0, st
->text_lines
[l
],
4442 st
->line_lengths
[l
],
4443 handle
? handle
: st
->parms
->text
.handles
);
4445 * We clear to the end of line in the right background
4446 * color. If the line was exactly the width of the screen
4447 * then PutLine0n8b will have left _col and _row moved to
4448 * the start of the next row. We don't need or want to clear
4451 if(pico_usingcolor()
4452 && (st
->line_lengths
[l
] < ps_global
->ttyo
->screen_cols
4453 || visible_linelen(l
) < ps_global
->ttyo
->screen_cols
))
4460 if(handle
&& handle
!= st
->parms
->text
.handles
){
4463 for(lp
= handle
->loc
; lp
; lp
= lp
->next
)
4464 if(lp
->where
.row
>= st
->top_text_line
4465 && lp
->where
.row
< st
->top_text_line
+ st
->screen
.length
){
4466 PutLine0n8b(st
->screen
.start_line
4467 + (lp
->where
.row
- st
->top_text_line
),
4468 0, st
->text_lines
[lp
->where
.row
],
4469 st
->line_lengths
[lp
->where
.row
],
4474 st
->parms
->text
.handles
= handle
;
4480 return(new_top_line
);
4484 /*---------------------------------------------------------------------
4485 Edit individual char in text so that the entire text doesn't need
4486 to be completely reformatted.
4488 Returns 0 if there were no errors, 1 if we would like the entire
4489 text to be reformatted.
4492 ng_scroll_edit(CONTEXT_S
*context
, int index
)
4494 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4495 char *ngp
, tmp
[MAILTMPLEN
+10];
4499 if (!(f
= folder_entry(index
, FOLDERS(context
))))
4502 return 0; /* nothing in scroll needs to be changed */
4503 tmp
[0] = TAG_HANDLE
;
4504 snprintf(tmp
+2, sizeof(tmp
)-2, "%d", st
->parms
->text
.handles
->key
);
4505 tmp
[sizeof(tmp
)-1] = '\0';
4506 tmp
[1] = len
= strlen(tmp
+2);
4507 snprintf(tmp
+len
+2, sizeof(tmp
)-(len
+2), "%s ", f
->selected
? "[ ]" : "[X]");
4508 tmp
[sizeof(tmp
)-1] = '\0';
4509 snprintf(tmp
+len
+6, sizeof(tmp
)-(len
+6), "%.*s", MAILTMPLEN
, f
->name
);
4510 tmp
[sizeof(tmp
)-1] = '\0';
4512 ngp
= *(st
->text_lines
);
4514 ngp
= strstr(ngp
, tmp
);
4519 /* assumption that text is of form "[ ] xxx.xxx" */
4526 else if (*ngp
== ' '){
4535 /*---------------------------------------------------------------------
4536 Similar to ng_scroll_edit, but this is the more general case of
4537 selecting a folder, as opposed to selecting a newsgroup for
4538 subscription while in listmode.
4540 Returns 0 if there were no errors, 1 if we would like the entire
4541 text to be reformatted.
4544 folder_select_update(CONTEXT_S
*context
, int index
)
4546 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4548 char *ngp
, tmp
[MAILTMPLEN
+10];
4549 int len
, total
, fnum
, num_sel
= 0;
4551 if (!(f
= folder_entry(index
, FOLDERS(context
))))
4553 ngp
= *(st
->text_lines
);
4555 total
= folder_total(FOLDERS(context
));
4557 for (fnum
= 0; num_sel
< 2 && fnum
< total
; fnum
++)
4558 if(folder_entry(fnum
, FOLDERS(context
))->selected
)
4560 if(!num_sel
|| (f
->selected
&& num_sel
== 1))
4561 return 1; /* need to reformat the whole thing */
4563 tmp
[0] = TAG_HANDLE
;
4564 snprintf(tmp
+2, sizeof(tmp
)-2, "%d", st
->parms
->text
.handles
->key
);
4565 tmp
[sizeof(tmp
)-1] = '\0';
4566 tmp
[1] = len
= strlen(tmp
+2);
4568 ngp
= strstr(ngp
, tmp
);
4571 if(F_ON(F_SELECTED_SHOWN_BOLD
, ps_global
)){
4573 while(*ngp
&& ngp
[0] != TAG_EMBED
4574 && ngp
[1] != (f
->selected
? TAG_BOLDOFF
: TAG_BOLDON
)
4575 && *ngp
!= *(f
->name
))
4578 if (!(*ngp
) || (*ngp
== *(f
->name
)))
4582 *ngp
= (f
->selected
? TAG_BOLDON
: TAG_BOLDOFF
);
4587 while(*ngp
!= ' ' && *ngp
!= *(f
->name
) && *ngp
)
4589 if(!(*ngp
) || (*ngp
== *(f
->name
)))
4593 *ngp
= f
->selected
? 'X' : ' ';
4600 /*---------------------------------------------------------------------
4601 We gotta go through all of the formatted text and add "[ ] " in the right
4602 place. If we don't do this, we must completely reformat the whole text,
4603 which could take a very long time.
4605 Return 1 if we encountered some sort of error and we want to reformat the
4606 whole text, return 0 if everything went as planned.
4608 ASSUMPTION: for this to work, we assume that there are only total
4609 number of handles, numbered 1 through total.
4612 scroll_add_listmode(CONTEXT_S
*context
, int total
)
4614 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4616 char *ngp
, *ngname
, handle_str
[MAILTMPLEN
];
4620 ngp
= *(st
->text_lines
);
4621 h
= st
->parms
->text
.handles
;
4623 while(h
&& h
->key
!= 1 && h
->prev
)
4626 handle_str
[0] = TAG_EMBED
;
4627 handle_str
[1] = TAG_HANDLE
;
4628 for(i
= 1; i
<= total
&& h
; i
++, h
= h
->next
){
4629 snprintf(handle_str
+3, sizeof(handle_str
)-3, "%d", h
->key
);
4630 handle_str
[sizeof(handle_str
)-1] = '\0';
4631 handle_str
[2] = strlen(handle_str
+3);
4632 ngp
= strstr(ngp
, handle_str
);
4634 ngp
= *(st
->text_lines
);
4638 ngname
= ngp
+ strlen(handle_str
);
4639 while (strncmp(ngp
, " ", 4) && !(*ngp
== '\n')
4640 && !(ngp
== *(st
->text_lines
)))
4642 if (strncmp(ngp
, " ", 4))
4644 while(ngp
+4 != ngname
&& *ngp
){
4649 if(folder_entry(h
->h
.f
.index
, FOLDERS(context
))->subscribed
){
4667 /*----------------------------------------------------------------------
4668 Search the set scrolling text
4670 Args: start_line -- line to start searching on
4671 start_index -- column to start searching at in first line
4672 word -- string to search for
4673 cursor_pos -- position of cursor is returned to caller here
4674 (Actually, this isn't really the position of the
4675 cursor because we don't know where we are on the
4676 screen. So row is set to the line number and col
4677 is set to the right column.)
4678 offset_in_line -- Offset where match was found.
4680 Returns: the line the word was found on, or -2 if it wasn't found, or
4681 -3 if the only match is at column start_index - 1.
4685 search_scroll_text(long int start_line
, int start_index
, char *word
,
4686 Pos
*cursor_pos
, int *offset_in_line
)
4688 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4690 long l
, offset
, dlines
;
4691 #define SROW(N) ((N) - offset)
4692 #define SLINE(N) st->text_lines[SROW(N)]
4693 #define SLEN(N) st->line_lengths[SROW(N)]
4695 dlines
= PGSIZE(st
);
4696 offset
= (st
->parms
->text
.src
== FileStar
) ? st
->top_text_line
: 0;
4698 if(start_line
< st
->num_lines
){
4699 /* search first line starting at position start_index in */
4700 if((wh
= search_scroll_line(SLINE(start_line
) + start_index
,
4702 SLEN(start_line
) - start_index
,
4703 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4704 cursor_pos
->row
= start_line
;
4705 cursor_pos
->col
= scroll_handle_column(SROW(start_line
),
4706 *offset_in_line
= wh
- SLINE(start_line
));
4710 if(st
->parms
->text
.src
== FileStar
)
4713 for(l
= start_line
+ 1; l
< st
->num_lines
; l
++) {
4714 if(st
->parms
->text
.src
== FileStar
&& l
> offset
+ dlines
)
4715 ScrollFile(offset
+= dlines
);
4717 if((wh
= search_scroll_line(SLINE(l
), word
, SLEN(l
),
4718 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4719 cursor_pos
->row
= l
;
4720 cursor_pos
->col
= scroll_handle_column(SROW(l
),
4721 *offset_in_line
= wh
- SLINE(l
));
4727 start_line
= st
->num_lines
;
4729 if(st
->parms
->text
.src
== FileStar
) /* wrap offset */
4730 ScrollFile(offset
= 0);
4732 for(l
= 0; l
< start_line
; l
++) {
4733 if(st
->parms
->text
.src
== FileStar
&& l
> offset
+ dlines
)
4734 ScrollFile(offset
+= dlines
);
4736 if((wh
= search_scroll_line(SLINE(l
), word
, SLEN(l
),
4737 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4738 cursor_pos
->row
= l
;
4739 cursor_pos
->col
= scroll_handle_column(SROW(l
),
4740 *offset_in_line
= wh
- SLINE(l
));
4745 /* search in current line */
4746 if(start_line
< st
->num_lines
4747 && (wh
= search_scroll_line(SLINE(start_line
), word
,
4748 start_index
+ strlen(word
) - 2,
4749 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4750 cursor_pos
->row
= start_line
;
4751 cursor_pos
->col
= scroll_handle_column(SROW(start_line
),
4752 *offset_in_line
= wh
- SLINE(start_line
));
4757 /* see if the only match is a repeat */
4758 if(start_index
> 0 && start_line
< st
->num_lines
4759 && (wh
= search_scroll_line(
4760 SLINE(start_line
) + start_index
- 1,
4762 st
->parms
->text
.handles
!= NULL
)) != NULL
){
4763 cursor_pos
->row
= start_line
;
4764 cursor_pos
->col
= scroll_handle_column(SROW(start_line
),
4765 *offset_in_line
= wh
- SLINE(start_line
));
4773 /*----------------------------------------------------------------------
4774 Search one line of scroll text for given string
4776 Args: haystack -- The string to search in, the larger string
4777 needle -- The string to search for, the smaller string
4778 n -- The max number of chars in haystack to search
4780 Search for first occurrence of needle in the haystack, and return a pointer
4781 into the string haystack when it is found. The search is case independent.
4784 search_scroll_line(char *haystack
, char *needle
, int n
, int handles
)
4786 char *return_ptr
= NULL
, *found_it
= NULL
;
4787 char *haystack_copy
, *p
, *free_this
= NULL
, *end
;
4789 int state
= 0, i
= 0;
4791 if(n
> 0 && haystack
){
4793 haystack_copy
= buf
;
4795 haystack_copy
= free_this
= (char *) fs_get((n
+1) * sizeof(char));
4797 strncpy(haystack_copy
, haystack
, n
);
4798 haystack_copy
[n
] = '\0';
4801 * We don't want to match text inside embedded tags.
4802 * Replace embedded octets with nulls and convert
4803 * uppercase ascii to lowercase. We should also do
4804 * some sort of canonicalization of UTF-8 but that
4807 for(i
= n
, p
= haystack_copy
; i
-- > 0 && *p
; p
++){
4811 if(*p
== TAG_EMBED
){
4817 /* lower case just ascii chars */
4818 if(!(*p
& 0x80) && isupper(*p
))
4825 state
= (*p
== TAG_HANDLE
)
4827 : (*p
== TAG_FGCOLOR
|| *p
== TAG_BGCOLOR
) ? RGBLEN
: 0;
4832 state
= *p
; /* length of handle's key */
4844 * The haystack_copy string now looks like
4846 * "chars\0\0\0\0\0\0chars...\0\0\0chars... \0"
4848 * with that final \0 at haystack_copy[n].
4849 * Search each piece one at a time.
4852 end
= haystack_copy
+ n
;
4855 while(p
< end
&& !return_ptr
){
4858 while(*p
== '\0' && p
< end
)
4862 found_it
= srchstr(p
, needle
);
4865 /* found it, make result relative to haystack */
4866 return_ptr
= haystack
+ (found_it
- haystack_copy
);
4869 /* skip to next null */
4870 while(*p
!= '\0' && p
< end
)
4875 fs_give((void **) &free_this
);
4883 * Returns the number of columns taken up by the visible part of the line.
4884 * That is, account for handles and color changes and so forth.
4887 visible_linelen(int line
)
4889 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
4892 if(line
< 0 || line
>= st
->num_lines
)
4895 for(i
= 0, len
= 0; i
< st
->line_lengths
[line
];)
4896 switch(st
->text_lines
[line
][i
]){
4900 switch((i
< st
->line_lengths
[line
]) ? st
->text_lines
[line
][i
] : 0){
4903 /* skip the length byte plus <length> more bytes */
4904 if(i
< st
->line_lengths
[line
]){
4905 n
= st
->text_lines
[line
][i
];
4909 if(i
< st
->line_lengths
[line
] && n
> 0)
4916 i
+= (RGBLEN
+ 1); /* 1 for TAG, RGBLEN for color */
4928 case TAG_EMBED
: /* escaped embed character */
4933 default: /* the embed char was literal */
4943 while(((++len
) & 0x07) != 0) /* add tab's spaces */
4958 /*----------------------------------------------------------------------
4959 Display the contents of the given file (likely output from some command)
4961 Args: filename -- name of file containing output
4962 title -- title to be used for screen displaying output
4963 alt_msg -- if no output, Q this message instead of the default
4964 mode -- non-zero to display short files in status line
4968 display_output_file(char *filename
, char *title
, char *alt_msg
, int mode
)
4970 STORE_S
*in_file
= NULL
, *out_store
= NULL
;
4972 if((in_file
= so_get(FileStar
, filename
, READ_ACCESS
|READ_FROM_LOCALE
))){
4973 if(mode
== DOF_BRIEF
){
4974 int msg_q
= 0, i
= 0;
4975 char buf
[512], *msg_p
[4];
4976 #define MAX_SINGLE_MSG_LEN 60
4982 * Might need to do something about CRLFs for Windows.
4984 while(so_fgets(in_file
, msg_p
[msg_q
], sizeof(buf
) - (msg_p
[msg_q
] - buf
))
4986 && (i
= strlen(msg_p
[msg_q
])) < MAX_SINGLE_MSG_LEN
){
4987 msg_p
[msg_q
+1] = msg_p
[msg_q
]+strlen(msg_p
[msg_q
]);
4988 if (*(msg_p
[++msg_q
] - 1) == '\n')
4989 *(msg_p
[msg_q
] - 1) = '\0';
4992 if(msg_q
< 3 && i
< MAX_SINGLE_MSG_LEN
){
4994 for(i
= 0; i
< msg_q
; i
++)
4995 q_status_message2(SM_ORDER
, 3, 4,
4996 "%s Result: %s", title
, msg_p
[i
]);
4998 q_status_message2(SM_ORDER
, 0, 4, "%s%s", title
,
5001 : " command completed with no output");
5007 else if(mode
== DOF_EMPTY
){
5010 if(so_readc(&c
, in_file
) < 1){
5011 q_status_message2(SM_ORDER
, 0, 4, "%s%s", title
,
5014 : " command completed with no output");
5021 * We need to translate the file contents from the user's locale
5022 * charset to UTF-8 for use in scrolltool. We get that translation
5023 * from the READ_FROM_LOCALE in the in_file storage object.
5024 * It would be nice to skip this step but scrolltool doesn't use
5025 * the storage object routines to read from the file, so would
5026 * skip the translation step.
5032 if(!(out_store
= so_get(CharStar
, NULL
, EDIT_ACCESS
))){
5034 our_unlink(filename
);
5035 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5036 _("Error allocating space."));
5040 so_seek(in_file
, 0L, 0);
5044 gf_link_filter(gf_wrap
,
5045 gf_wrap_filter_opt(ps_global
->ttyo
->screen_cols
- 4,
5046 ps_global
->ttyo
->screen_cols
,
5047 NULL
, 0, GFW_NONE
));
5049 gf_set_so_readc(&gc
, in_file
);
5050 gf_set_so_writec(&pc
, out_store
);
5052 if((errstr
= gf_pipe(gc
, pc
)) != NULL
){
5054 so_give(&out_store
);
5055 our_unlink(filename
);
5056 q_status_message(SM_ORDER
| SM_DING
, 3, 3,
5057 _("Error allocating space."));
5061 gf_clear_so_writec(out_store
);
5062 gf_clear_so_readc(in_file
);
5070 snprintf(title_buf
, sizeof(title_buf
), "HELP FOR %s VIEW", title
);
5071 title_buf
[sizeof(title_buf
)-1] = '\0';
5073 memset(&sargs
, 0, sizeof(SCROLL_S
));
5074 sargs
.text
.text
= so_text(out_store
);
5075 sargs
.text
.src
= CharStar
;
5076 sargs
.text
.desc
= "output";
5077 sargs
.bar
.title
= title
;
5078 sargs
.bar
.style
= TextPercent
;
5079 sargs
.help
.text
= h_simple_text_view
;
5080 sargs
.help
.title
= title_buf
;
5082 ps_global
->mangled_screen
= 1;
5083 so_give(&out_store
);
5086 our_unlink(filename
);
5089 dprint((2, "Error reopening %s to get results: %s\n",
5090 filename
? filename
: "?", error_description(errno
)));
5094 /*--------------------------------------------------------------------
5095 Call the function that will perform the double click operation
5097 Returns: the current top line
5100 doubleclick_handle(SCROLL_S
*sparms
, HANDLE_S
*next_handle
, int *cmd
, int *done
)
5102 if(sparms
->mouse
.clickclick
){
5103 if((*sparms
->mouse
.clickclick
)(sparms
))
5107 switch(scroll_handle_launch(next_handle
, TRUE
)){
5109 *cmd
= MC_EXIT
; /* propagate */
5114 cmd_cancelled("View");
5121 return(scroll_state(SS_CUR
)->top_text_line
);
5128 * Just a little something to simplify assignments
5130 #define VIEWPOPUP(p, c, s) { \
5131 (p)->type = tQueue; \
5132 (p)->data.val = c; \
5133 (p)->label.style = lNormal; \
5134 (p)->label.string = s; \
5142 format_message_popup(sparms
, in_handle
)
5146 MPopup fmp_menu
[32];
5152 /* Reason to offer per message ops? */
5153 if(mn_get_total(ps_global
->msgmap
) > 0L){
5155 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5157 switch((h
= get_handle(st
->parms
->text
.handles
, in_handle
))->type
){
5159 fmp_menu
[++i
].type
= tIndex
;
5160 fmp_menu
[i
].label
.string
= "View Attachment";
5161 fmp_menu
[i
].label
.style
= lNormal
;
5162 fmp_menu
[i
].data
.val
= 'X'; /* for local use */
5165 && dispatch_attachment(h
->h
.attach
) != MCD_NONE
5166 && !(h
->h
.attach
->can_display
& MCD_EXTERNAL
)
5167 && h
->h
.attach
->body
5168 && (h
->h
.attach
->body
->type
== TYPETEXT
5169 || (h
->h
.attach
->body
->type
== TYPEMESSAGE
5170 && h
->h
.attach
->body
->subtype
5171 && !strucmp(h
->h
.attach
->body
->subtype
,"rfc822")))){
5172 fmp_menu
[++i
].type
= tIndex
;
5173 fmp_menu
[i
].label
.string
= "View Attachment in New Window";
5174 fmp_menu
[i
].label
.style
= lNormal
;
5175 fmp_menu
[i
].data
.val
= 'Y'; /* for local use */
5178 fmp_menu
[++i
].type
= tIndex
;
5179 fmp_menu
[i
].label
.style
= lNormal
;
5180 fmp_menu
[i
].data
.val
= 'Z'; /* for local use */
5181 msgno_exceptions(ps_global
->mail_stream
,
5182 mn_m2raw(ps_global
->msgmap
,
5183 mn_get_cur(ps_global
->msgmap
)),
5184 h
->h
.attach
->number
, &n
, FALSE
);
5185 fmp_menu
[i
].label
.string
= (n
& MSG_EX_DELETE
)
5186 ? "Undelete Attachment"
5187 : "Delete Attachment";
5192 fmp_menu
[++i
].type
= tIndex
;
5193 fmp_menu
[i
].label
.string
= "View Link";
5194 fmp_menu
[i
].label
.style
= lNormal
;
5195 fmp_menu
[i
].data
.val
= 'X'; /* for local use */
5197 fmp_menu
[++i
].type
= tIndex
;
5198 fmp_menu
[i
].label
.string
= "Copy Link";
5199 fmp_menu
[i
].label
.style
= lNormal
;
5200 fmp_menu
[i
].data
.val
= 'W'; /* for local use */
5204 fmp_menu
[++i
].type
= tSeparator
;
5207 /* Delete or Undelete? That is the question. */
5208 fmp_menu
[++i
].type
= tQueue
;
5209 fmp_menu
[i
].label
.style
= lNormal
;
5210 mc
= ((rawno
= mn_m2raw(ps_global
->msgmap
,
5211 mn_get_cur(ps_global
->msgmap
))) > 0L
5212 && ps_global
->mail_stream
5213 && rawno
<= ps_global
->mail_stream
->nmsgs
)
5214 ? mail_elt(ps_global
->mail_stream
, rawno
) : NULL
;
5215 if(mc
&& mc
->deleted
){
5216 fmp_menu
[i
].data
.val
= 'U';
5217 fmp_menu
[i
].label
.string
= in_handle
5218 ? "&Undelete Message" : "&Undelete";
5221 fmp_menu
[i
].data
.val
= 'D';
5222 fmp_menu
[i
].label
.string
= in_handle
5223 ? "&Delete Message" : "&Delete";
5226 if(F_ON(F_ENABLE_FLAG
, ps_global
)){
5227 fmp_menu
[++i
].type
= tSubMenu
;
5228 fmp_menu
[i
].label
.string
= "Flag";
5229 fmp_menu
[i
].data
.submenu
= flag_submenu(mc
);
5233 VIEWPOPUP(&fmp_menu
[i
], 'S', in_handle
? "&Save Message" : "&Save");
5236 VIEWPOPUP(&fmp_menu
[i
], 'E', in_handle
? "&Export Message" : "&Export");
5239 VIEWPOPUP(&fmp_menu
[i
], '%', in_handle
? "Print Message" : "Print");
5242 VIEWPOPUP(&fmp_menu
[i
], 'R',
5243 in_handle
? "&Reply to Message" : "&Reply");
5246 VIEWPOPUP(&fmp_menu
[i
], 'F',
5247 in_handle
? "&Forward Message" : "&Forward");
5250 VIEWPOPUP(&fmp_menu
[i
], 'B',
5251 in_handle
? "&Bounce Message" : "&Bounce");
5254 VIEWPOPUP(&fmp_menu
[i
], 'T', "&Take Addresses");
5256 fmp_menu
[++i
].type
= tSeparator
;
5258 if(mn_get_cur(ps_global
->msgmap
) < mn_get_total(ps_global
->msgmap
)){
5260 VIEWPOPUP(&fmp_menu
[i
], 'N', "View &Next Message");
5263 if(mn_get_cur(ps_global
->msgmap
) > 0){
5265 VIEWPOPUP(&fmp_menu
[i
], 'P', "View &Prev Message");
5268 if(mn_get_cur(ps_global
->msgmap
) < mn_get_total(ps_global
->msgmap
)
5269 || mn_get_cur(ps_global
->msgmap
) > 0)
5270 fmp_menu
[++i
].type
= tSeparator
;
5272 /* Offer the attachment screen? */
5273 for(n
= 0; ps_global
->atmts
&& ps_global
->atmts
[n
].description
; n
++)
5278 VIEWPOPUP(&fmp_menu
[i
], 'V', "&View Attachment Index");
5283 VIEWPOPUP(&fmp_menu
[i
], 'I', "Message &Index");
5286 VIEWPOPUP(&fmp_menu
[i
], 'M', "&Main Menu");
5288 fmp_menu
[++i
].type
= tTail
;
5290 if((i
= mswin_popup(fmp_menu
)) >= 0 && in_handle
)
5291 switch(fmp_menu
[i
].data
.val
){
5292 case 'W' : /* Copy URL to clipboard */
5293 mswin_addclipboard(h
->h
.url
.path
);
5297 return(1); /* return like the user double-clicked */
5301 case 'Y' : /* popup the thing in another window */
5302 display_att_window(h
->h
.attach
);
5306 if(h
&& h
->type
== Attach
){
5307 msgno_exceptions(ps_global
->mail_stream
,
5308 mn_m2raw(ps_global
->msgmap
,
5309 mn_get_cur(ps_global
->msgmap
)),
5310 h
->h
.attach
->number
, &n
, FALSE
);
5312 msgno_exceptions(ps_global
->mail_stream
,
5313 mn_m2raw(ps_global
->msgmap
,
5314 mn_get_cur(ps_global
->msgmap
)),
5315 h
->h
.attach
->number
, &n
, TRUE
);
5316 q_status_message2(SM_ORDER
, 0, 3, "Attachment %s %s!",
5317 h
->h
.attach
->number
,
5318 (n
& MSG_EX_DELETE
) ? "deleted" : "undeleted");
5338 simple_text_popup(sparms
, in_handle
)
5342 MPopup simple_menu
[12];
5345 VIEWPOPUP(&simple_menu
[n
], '%', "Print");
5348 VIEWPOPUP(&simple_menu
[n
], 'S', "Save");
5351 VIEWPOPUP(&simple_menu
[n
], 'F', "Forward in Email");
5354 simple_menu
[n
++].type
= tSeparator
;
5356 VIEWPOPUP(&simple_menu
[n
], 'E', "Exit Viewer");
5359 simple_menu
[n
].type
= tTail
;
5361 (void) mswin_popup(simple_menu
);
5367 /*----------------------------------------------------------------------
5368 Return characters in scroll tool buffer serially
5370 Args: n -- index of char to return
5372 Returns: returns the character at index 'n', or -1 on error or
5377 mswin_readscrollbuf(n
)
5380 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5382 static char **orig
= NULL
, **l
, *p
;
5389 * All of these are mind-numbingly slow at the moment...
5391 switch(st
->parms
->text
.src
){
5393 return((n
>= strlen((char *)st
->parms
->text
.text
))
5394 ? -1 : ((char *)st
->parms
->text
.text
)[n
]);
5397 /* BUG? is this test rigorous enough? */
5398 if(orig
!= (char **)st
->parms
->text
.text
|| n
< lastn
){
5400 if(orig
= l
= (char **)st
->parms
->text
.text
) /* reset l and p */
5403 else{ /* use cached l and p */
5404 c
= n
; /* and adjust n */
5409 while(l
){ /* look for 'n' on each line */
5410 for(; n
&& *p
; n
--, p
++)
5413 if(n
--) /* 'n' found ? */
5419 return((l
&& *l
) ? *p
? *p
: '\n' : -1);
5422 return((fseek((FILE *)st
->parms
->text
.text
, (long) n
, 0) < 0
5423 || (c
= fgetc((FILE *)st
->parms
->text
.text
)) == EOF
) ? -1 : c
);
5432 /*----------------------------------------------------------------------
5433 MSWin scroll callback. Called during scroll message processing.
5437 Args: cmd - what type of scroll operation.
5438 scroll_pos - paramter for operation.
5439 used as position for SCROLL_TO operation.
5441 Returns: TRUE - did the scroll operation.
5442 FALSE - was not able to do the scroll operation.
5445 pcpine_do_scroll (cmd
, scroll_pos
)
5449 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5450 HANDLE_S
*next_handle
;
5452 int num_display_lines
;
5459 maxscroll
= st
->num_lines
;
5461 case MSWIN_KEY_SCROLLUPLINE
:
5462 if(st
->top_text_line
> 0) {
5463 st
->top_text_line
-= (int) scroll_pos
;
5465 if (st
->top_text_line
<= 0){
5466 snprintf(message
, sizeof(message
), "START of %.*s",
5467 32, STYLE_NAME(st
->parms
));
5468 message
[sizeof(message
)-1] = '\0';
5469 st
->top_text_line
= 0;
5474 case MSWIN_KEY_SCROLLDOWNLINE
:
5475 if(st
->top_text_line
< maxscroll
) {
5476 st
->top_text_line
+= (int) scroll_pos
;
5478 if (st
->top_text_line
>= maxscroll
){
5479 snprintf(message
, sizeof(message
), "END of %.*s", 32, STYLE_NAME(st
->parms
));
5480 message
[sizeof(message
)-1] = '\0';
5481 st
->top_text_line
= maxscroll
;
5486 case MSWIN_KEY_SCROLLUPPAGE
:
5487 if(st
->top_text_line
> 0) {
5488 num_display_lines
= SCROLL_LINES(ps_global
);
5489 scroll_lines
= MIN(MAX(num_display_lines
-
5490 ps_global
->viewer_overlap
, 1), num_display_lines
);
5491 if (st
->top_text_line
> scroll_lines
)
5492 st
->top_text_line
-= scroll_lines
;
5494 st
->top_text_line
= 0;
5495 snprintf(message
, sizeof(message
), "START of %.*s", 32, STYLE_NAME(st
->parms
));
5496 message
[sizeof(message
)-1] = '\0';
5502 case MSWIN_KEY_SCROLLDOWNPAGE
:
5503 num_display_lines
= SCROLL_LINES(ps_global
);
5504 if(st
->top_text_line
< maxscroll
) {
5505 scroll_lines
= MIN(MAX(num_display_lines
-
5506 ps_global
->viewer_overlap
, 1), num_display_lines
);
5507 st
->top_text_line
+= scroll_lines
;
5508 if (st
->top_text_line
>= maxscroll
) {
5509 st
->top_text_line
= maxscroll
;
5510 snprintf(message
, sizeof(message
), "END of %.*s", 32, STYLE_NAME(st
->parms
));
5511 message
[sizeof(message
)-1] = '\0';
5517 case MSWIN_KEY_SCROLLTO
:
5518 if (st
->top_text_line
!= scroll_pos
) {
5519 st
->top_text_line
= scroll_pos
;
5520 if (st
->top_text_line
== 0)
5521 snprintf(message
, sizeof(message
), "START of %.*s", 32, STYLE_NAME(st
->parms
));
5522 else if(st
->top_text_line
>= maxscroll
)
5523 snprintf(message
, sizeof(message
), "END of %.*s", 32, STYLE_NAME(st
->parms
));
5525 message
[sizeof(message
)-1] = '\0';
5531 /* Need to frame some handles? */
5532 if(st
->parms
->text
.handles
5533 && (next_handle
= scroll_handle_in_frame(st
->top_text_line
)))
5534 st
->parms
->text
.handles
= next_handle
;
5537 mswin_beginupdate();
5538 update_scroll_titlebar(st
->top_text_line
, 0);
5539 (void) scroll_scroll_text(st
->top_text_line
,
5540 st
->parms
->text
.handles
, 1);
5542 q_status_message(SM_INFO
, 0, 1, message
);
5544 /* Display is always called so that the "START(END) of message"
5545 * message gets removed when no longer at the start(end). */
5546 display_message (KEY_PGDN
);
5555 pcpine_help_scroll(title
)
5558 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5561 strncpy(title
, (st
->parms
->help
.title
)
5562 ? st
->parms
->help
.title
: "Alpine Help", 256);
5564 return(pcpine_help(st
->parms
->help
.text
));
5569 pcpine_view_cursor(col
, row
)
5573 SCRLCTRL_S
*st
= scroll_state(SS_CUR
);
5577 return((row
>= HEADER_ROWS(ps_global
)
5578 && row
< HEADER_ROWS(ps_global
) + SCROLL_LINES(ps_global
)
5579 && (line
= (row
- 2) + st
->top_text_line
) < st
->num_lines
5580 && (key
= dot_on_handle(line
, col
))
5581 && scroll_handle_selectable(get_handle(st
->parms
->text
.handles
,key
)))
5583 : MSWIN_CURSOR_ARROW
);
5585 #endif /* _WINDOWS */