1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2005-2021 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include "mail.h"
18
19 static size_t top_of_page = 1; /* Number of the topmost message on the page */
20 static size_t cursor; /* Number of current message */
21
22 static size_t *page_map; /* Array of message numbers. page_map[N] holds
23 number of the message occupying Nth line on
24 the screen */
25 static unsigned page_size; /* Capacity of page_map */
26 static unsigned page_avail; /* First non-used entry in page map. Can be
27 equal to page_size */
28
29 /* Auxiliary function: Store number of message from mspec into page_map */
30 static int
_fill_map(msgset_t * mspec,mu_message_t msg,void * data)31 _fill_map (msgset_t *mspec, mu_message_t msg, void *data)
32 {
33 unsigned *pos = data;
34 page_map[*pos] = msgset_msgno (mspec);
35 ++*pos;
36 return 0;
37 }
38
39 /* Fill page_map.
40 page_avail must be set to zero before calling this function */
41 static void
fill_page_map()42 fill_page_map ()
43 {
44 util_range_msg (top_of_page, page_size,
45 MSG_COUNT|MSG_NODELETED|MSG_SILENT, _fill_map,
46 &page_avail);
47 if (cursor >= page_avail)
48 cursor = page_avail - 1;
49 }
50
51 /* Check if the page_map is valid. If not, fill it.
52 Page_avail can be set to zero to force re-filling page_map. In particular
53 this happens when deleting current message or when SIGWINCH is delivered
54 to the program */
55 static void
check_page_map()56 check_page_map ()
57 {
58 if (!page_map)
59 {
60 page_size = util_screen_lines ();
61 page_map = mu_realloc (page_map, sizeof (page_map[0]) * page_size);
62 page_avail = 0;
63 }
64 if (page_avail == 0)
65 fill_page_map ();
66 }
67
68 /* Invalidate page_map. HARD=1 means 'hard invalidation', implying
69 the need to reallocate the array */
70 void
page_invalidate(int hard)71 page_invalidate (int hard)
72 {
73 page_avail = 0;
74 if (hard)
75 {
76 free (page_map);
77 page_map = NULL;
78 }
79 }
80
81 /* Invalidate page_map if VALUE is number of the current message */
82 void
cond_page_invalidate(size_t value)83 cond_page_invalidate (size_t value)
84 {
85 unsigned i;
86
87 if (page_map == NULL || page_avail == 0)
88 return;
89 if (page_avail)
90 {
91 if (page_map[page_avail-1] == value)
92 page_invalidate (0);
93 else
94 {
95 if (page_avail > 1)
96 {
97 for (i = 0; i < page_avail-1; i++)
98 if (page_map[i] >= value && value <= page_map[i+1])
99 {
100 page_invalidate (0);
101 return;
102 }
103 }
104 if (page_avail < page_size
105 && (value > page_map[page_avail-1] || value < page_map[0]))
106 page_invalidate (0);
107 }
108 }
109 }
110
111 /* Return a 1-based index of page_map entry occupied by number VALUE.
112 Return 0 if VALUE is not found in page_map */
113 static int
page_line(size_t value)114 page_line (size_t value)
115 {
116 unsigned i;
117
118 for (i = 0; i < page_avail; i++)
119 if (page_map[i] == value)
120 return i+1;
121 return 0;
122 }
123
124 /* Return number of the current message.
125 Zero is returned if page_map is empty (i.e. mailbox is empy) */
126 size_t
get_cursor()127 get_cursor ()
128 {
129 check_page_map ();
130 if (page_avail == 0)
131 return 0;
132 return page_map[cursor];
133 }
134
135 /* Move cursor to message number VALUE */
136 void
set_cursor(unsigned value)137 set_cursor (unsigned value)
138 {
139 int n;
140
141 if (total == 0)
142 {
143 cursor = 0;
144 return;
145 }
146
147 check_page_map ();
148 n = page_line (value);
149 if (n == 0)
150 {
151 top_of_page = value;
152 cursor = 0;
153 page_avail = 0;
154 page_move (0);
155 }
156 else
157 cursor = n - 1;
158 }
159
160 /* Return T if the cursor points to message number N */
161 int
is_current_message(size_t n)162 is_current_message (size_t n)
163 {
164 check_page_map ();
165 return page_map[cursor] == n;
166 }
167
168 /* Apply function FUNC to each message from the page. DATA supplies
169 call-specific data to the function. */
170 void
page_do(msg_handler_t func,void * data)171 page_do (msg_handler_t func, void *data)
172 {
173 unsigned i;
174
175 check_page_map ();
176 for (i = 0; i < page_avail; i++)
177 {
178 mu_message_t msg;
179 msgset_t *set = msgset_make_1 (page_map[i]);
180 mu_mailbox_get_message (mbox, page_map[i], &msg);
181 func (set, msg, data);
182 msgset_free (set);
183 }
184 }
185
186 /* Move current page OFFSET lines forward, if OFFSET > 0, or backward,
187 if OFFSET < 0.
188 Return number of items that will be displayed */
189 size_t
page_move(off_t offset)190 page_move (off_t offset)
191 {
192 size_t start;
193 size_t count = 0;
194
195 check_page_map ();
196
197 if (offset < 0 && -offset > page_map[0])
198 start = 1;
199 else
200 start = page_map[0] + offset;
201
202 util_range_msg (start, page_size,
203 MSG_COUNT|MSG_NODELETED|MSG_SILENT, _fill_map, &count);
204
205 if (offset < 0 && top_of_page == page_map[0])
206 {
207 page_avail = count;
208 return 0;
209 }
210
211 if (count)
212 {
213 top_of_page = page_map[0];
214
215 if (count < page_size && top_of_page > 1)
216 {
217 for (start = top_of_page - 1; count < page_size && start > 1;
218 start--)
219 {
220 if (!util_isdeleted (start))
221 {
222 top_of_page = start;
223 count++;
224 cursor++;
225 }
226 }
227
228 page_avail = 0;
229 fill_page_map ();
230 if (cursor >= page_avail)
231 cursor = page_avail - 1;
232 }
233 else
234 page_avail = count;
235 }
236 return count;
237 }
238