source: viewwin.c @ 95b52d1

release-1.8release-1.9
Last change on this file since 95b52d1 was d427f08, checked in by Nelson Elhage <nelhage@mit.edu>, 10 years ago
Use G_GNUC_WARN_UNUSED_RESULT Have gcc warn us when we ignore the result of a function that requires the caller to free the result, or an initilization function that can fail. This might help (slightly) with preventing leaks and segfaults. Additionally changed some functions that should never fail to not return values. (The owl_list_* functions changed only fail if list->size < 0, which we assume is not the case elsewhere.)
  • Property mode set to 100644
File size: 12.3 KB
Line 
1#include <string.h>
2#include "owl.h"
3
4#define BOTTOM_OFFSET 1
5
6static void owl_viewwin_redraw_content(owl_window *w, WINDOW *curswin, void *user_data);
7static void owl_viewwin_redraw_status(owl_window *w, WINDOW *curswin, void *user_data);
8static void owl_viewwin_layout(owl_viewwin *v);
9static void owl_viewwin_set_window(owl_viewwin *v, owl_window *w);
10
11/* Create a viewwin.  'win' is an already initialized owl_window that
12 * will be used by the viewwin
13 */
14G_GNUC_WARN_UNUSED_RESULT owl_viewwin *owl_viewwin_new_text(owl_window *win, const char *text)
15{
16  owl_viewwin *v = g_new0(owl_viewwin, 1);
17  owl_fmtext_init_null(&(v->fmtext));
18  if (text) {
19    owl_fmtext_append_normal(&(v->fmtext), text);
20    if (text[0] != '\0' && text[strlen(text) - 1] != '\n') {
21      owl_fmtext_append_normal(&(v->fmtext), "\n");
22    }
23    v->textlines=owl_fmtext_num_lines(&(v->fmtext));
24  }
25  v->topline=0;
26  v->rightshift=0;
27  v->onclose_hook = NULL;
28
29  owl_viewwin_set_window(v, win);
30  return v;
31}
32
33/* Create a viewwin.  'win' is an already initialized owl_window that
34 * will be used by the viewwin
35 */
36G_GNUC_WARN_UNUSED_RESULT owl_viewwin *owl_viewwin_new_fmtext(owl_window *win, const owl_fmtext *fmtext)
37{
38  char *text;
39  owl_viewwin *v = g_new0(owl_viewwin, 1);
40
41  owl_fmtext_copy(&(v->fmtext), fmtext);
42  text = owl_fmtext_print_plain(fmtext);
43  if (text[0] != '\0' && text[strlen(text) - 1] != '\n') {
44      owl_fmtext_append_normal(&(v->fmtext), "\n");
45  }
46  g_free(text);
47  v->textlines=owl_fmtext_num_lines(&(v->fmtext));
48  v->topline=0;
49  v->rightshift=0;
50
51  owl_viewwin_set_window(v, win);
52  return v;
53}
54
55static void owl_viewwin_set_window(owl_viewwin *v, owl_window *w)
56{
57  v->window = g_object_ref(w);
58  v->content = owl_window_new(v->window);
59  v->status = owl_window_new(v->window);
60  v->cmdwin = NULL;
61
62  v->sig_content_redraw_id =
63    g_signal_connect(v->content, "redraw", G_CALLBACK(owl_viewwin_redraw_content), v);
64  v->sig_status_redraw_id =
65    g_signal_connect(v->status, "redraw", G_CALLBACK(owl_viewwin_redraw_status), v);
66  v->sig_resize_id =
67    g_signal_connect_swapped(v->window, "resized", G_CALLBACK(owl_viewwin_layout), v);
68  owl_viewwin_layout(v);
69
70  owl_window_show(v->content);
71  owl_window_show(v->status);
72}
73
74void owl_viewwin_set_onclose_hook(owl_viewwin *v, void (*onclose_hook) (owl_viewwin *vwin, void *data), void *onclose_hook_data) {
75  v->onclose_hook = onclose_hook;
76  v->onclose_hook_data = onclose_hook_data;
77}
78
79static void owl_viewwin_layout(owl_viewwin *v)
80{
81  int lines, cols;
82  owl_window_get_position(v->window, &lines, &cols, NULL, NULL);
83  owl_window_set_position(v->content, lines - BOTTOM_OFFSET, cols, 0, 0);
84  /* Only one of these will be visible at a time: */
85  owl_window_set_position(v->status, BOTTOM_OFFSET, cols, lines - BOTTOM_OFFSET, 0);
86  if (v->cmdwin)
87    owl_window_set_position(v->cmdwin, BOTTOM_OFFSET, cols, lines - BOTTOM_OFFSET, 0);
88}
89
90/* regenerate text on the curses window. */
91static void owl_viewwin_redraw_content(owl_window *w, WINDOW *curswin, void *user_data)
92{
93  owl_fmtext fm1, fm2;
94  owl_viewwin *v = user_data;
95  int winlines, wincols;
96
97  owl_window_get_position(w, &winlines, &wincols, 0, 0);
98
99  werase(curswin);
100  wmove(curswin, 0, 0);
101
102  owl_fmtext_init_null(&fm1);
103  owl_fmtext_init_null(&fm2);
104
105  owl_fmtext_truncate_lines(&(v->fmtext), v->topline, winlines, &fm1);
106  owl_fmtext_truncate_cols(&fm1, v->rightshift, wincols-1+v->rightshift, &fm2);
107
108  owl_fmtext_curs_waddstr(&fm2, curswin, OWL_FMTEXT_ATTR_NONE, OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
109
110  owl_fmtext_cleanup(&fm1);
111  owl_fmtext_cleanup(&fm2);
112}
113
114static void owl_viewwin_redraw_status(owl_window *w, WINDOW *curswin, void *user_data)
115{
116  owl_viewwin *v = user_data;
117  int winlines, wincols;
118
119  owl_window_get_position(v->content, &winlines, &wincols, 0, 0);
120
121  werase(curswin);
122  wmove(curswin, 0, 0);
123  wattrset(curswin, A_REVERSE);
124  if (v->textlines - v->topline > winlines) {
125    waddstr(curswin, "--More-- (Space to see more, 'q' to quit)");
126  } else {
127    waddstr(curswin, "--End-- (Press 'q' to quit)");
128  }
129  wattroff(curswin, A_REVERSE);
130}
131
132char *owl_viewwin_command_search(owl_viewwin *v, int argc, const char *const *argv, const char *buff)
133{
134  int direction, consider_current;
135  const char *buffstart;
136
137  direction=OWL_DIRECTION_DOWNWARDS;
138  buffstart=skiptokens(buff, 1);
139  if (argc>1 && !strcmp(argv[1], "-r")) {
140    direction=OWL_DIRECTION_UPWARDS;
141    buffstart=skiptokens(buff, 2);
142  }
143
144  if (argc==1 || (argc==2 && !strcmp(argv[1], "-r"))) {
145    consider_current = false;
146  } else {
147    owl_function_set_search(buffstart);
148    consider_current = true;
149  }
150
151  if (!owl_viewwin_search(v, owl_global_get_search_re(&g), consider_current, direction))
152    owl_function_makemsg("No more matches");
153  return NULL;
154}
155
156typedef struct _owl_viewwin_search_data { /*noproto*/
157  owl_viewwin *v;
158  int direction;
159} owl_viewwin_search_data;
160
161static void owl_viewwin_callback_search(owl_editwin *e)
162{
163  int consider_current = false;
164  const char *line = owl_editwin_get_text(e);
165  owl_viewwin_search_data *data = owl_editwin_get_cbdata(e);
166
167  /* Given an empty string, just continue the current search. */
168  if (line && *line) {
169    owl_function_set_search(line);
170    consider_current = true;
171  }
172  if (!owl_viewwin_search(data->v, owl_global_get_search_re(&g),
173                          consider_current, data->direction))
174    owl_function_makemsg("No matches");
175}
176
177char *owl_viewwin_command_start_search(owl_viewwin *v, int argc, const char *const *argv, const char *buff)
178{
179  int direction;
180  const char *buffstart;
181  owl_editwin *tw;
182  owl_context *ctx;
183  owl_viewwin_search_data *data;
184
185  direction=OWL_DIRECTION_DOWNWARDS;
186  buffstart=skiptokens(buff, 1);
187  if (argc>1 && !strcmp(argv[1], "-r")) {
188    direction=OWL_DIRECTION_UPWARDS;
189    buffstart=skiptokens(buff, 2);
190  }
191
192  /* TODO: Add a search history? */
193  tw = owl_viewwin_set_typwin_active(v, NULL);
194  owl_editwin_set_locktext(tw, (direction == OWL_DIRECTION_DOWNWARDS) ? "/" : "?");
195  owl_editwin_insert_string(tw, buffstart);
196
197  data = g_new(owl_viewwin_search_data, 1);
198  data->v = v;
199  data->direction = direction;
200
201  ctx = owl_editcontext_new(OWL_CTX_EDITLINE, tw, "editline",
202                            owl_viewwin_deactivate_editcontext, v);
203  ctx->cbdata = v;
204  owl_global_push_context_obj(&g, ctx);
205  owl_editwin_set_callback(tw, owl_viewwin_callback_search);
206  owl_editwin_set_cbdata(tw, data, g_free);
207  /* We aren't saving tw, so release the reference we were given. */
208  owl_editwin_unref(tw);
209  return NULL;
210}
211
212char *owl_viewwin_start_command(owl_viewwin *v, int argc, const char *const *argv, const char *buff)
213{
214  owl_editwin *tw;
215  owl_context *ctx;
216
217  buff = skiptokens(buff, 1);
218
219  tw = owl_viewwin_set_typwin_active(v, owl_global_get_cmd_history(&g));
220  owl_editwin_set_locktext(tw, ":");
221
222  owl_editwin_insert_string(tw, buff);
223
224  ctx = owl_editcontext_new(OWL_CTX_EDITLINE, tw, "editline",
225                            owl_viewwin_deactivate_editcontext, v);
226  owl_global_push_context_obj(&g, ctx);
227  owl_editwin_set_callback(tw, owl_callback_command);
228  /* We aren't saving tw, so release the reference we were given. */
229  owl_editwin_unref(tw);
230
231  return NULL;
232}
233
234void owl_viewwin_deactivate_editcontext(owl_context *ctx) {
235  owl_viewwin *v = ctx->cbdata;
236  owl_viewwin_set_typwin_inactive(v);
237}
238
239G_GNUC_WARN_UNUSED_RESULT owl_editwin *owl_viewwin_set_typwin_active(owl_viewwin *v, owl_history *hist) {
240  int lines, cols;
241  owl_editwin *cmdline;
242  if (v->cmdwin)
243    return NULL;
244  /* Create the command line. */
245  v->cmdwin = owl_window_new(v->window);
246  owl_viewwin_layout(v);
247  owl_window_get_position(v->cmdwin, &lines, &cols, NULL, NULL);
248  cmdline = owl_editwin_new(v->cmdwin, lines, cols, OWL_EDITWIN_STYLE_ONELINE, hist);
249  /* Swap out the bottom window. */
250  owl_window_hide(v->status);
251  owl_window_show(v->cmdwin);
252  return cmdline;
253}
254
255void owl_viewwin_set_typwin_inactive(owl_viewwin *v) {
256  if (v->cmdwin) {
257    /* Swap out the bottom window. */
258    owl_window_hide(v->cmdwin);
259    owl_window_show(v->status);
260    /* Destroy the window itself. */
261    owl_window_unlink(v->cmdwin);
262    g_object_unref(v->cmdwin);
263    v->cmdwin = NULL;
264  }
265}
266
267void owl_viewwin_append_text(owl_viewwin *v, const char *text) {
268    owl_fmtext_append_normal(&(v->fmtext), text);
269    v->textlines=owl_fmtext_num_lines(&(v->fmtext));
270    owl_viewwin_dirty(v);
271}
272
273/* Schedule a redraw of 'v'. Exported for hooking into the search
274   string; when we have some way of listening for changes, this can be
275   removed. */
276void owl_viewwin_dirty(owl_viewwin *v)
277{
278  owl_window_dirty(v->content);
279  owl_window_dirty(v->status);
280}
281
282void owl_viewwin_down(owl_viewwin *v, int amount) {
283  int winlines;
284  owl_window_get_position(v->content, &winlines, 0, 0, 0);
285  /* Don't scroll past the bottom. */
286  amount = MIN(amount, v->textlines - (v->topline + winlines));
287  /* But if we're already past the bottom, don't back up either. */
288  if (amount > 0) {
289    v->topline += amount;
290    owl_viewwin_dirty(v);
291  }
292}
293
294void owl_viewwin_up(owl_viewwin *v, int amount)
295{
296  v->topline -= amount;
297  if (v->topline<0) v->topline=0;
298  owl_viewwin_dirty(v);
299}
300
301void owl_viewwin_pagedown(owl_viewwin *v)
302{
303  int winlines;
304  owl_window_get_position(v->content, &winlines, 0, 0, 0);
305  owl_viewwin_down(v, winlines);
306}
307
308void owl_viewwin_linedown(owl_viewwin *v)
309{
310  owl_viewwin_down(v, 1);
311}
312
313void owl_viewwin_pageup(owl_viewwin *v)
314{
315  int winlines;
316  owl_window_get_position(v->content, &winlines, 0, 0, 0);
317  owl_viewwin_up(v, winlines);
318}
319
320void owl_viewwin_lineup(owl_viewwin *v)
321{
322  owl_viewwin_up(v, 1);
323}
324
325void owl_viewwin_right(owl_viewwin *v, int n)
326{
327  v->rightshift+=n;
328  owl_viewwin_dirty(v);
329}
330
331void owl_viewwin_left(owl_viewwin *v, int n)
332{
333  v->rightshift-=n;
334  if (v->rightshift<0) v->rightshift=0;
335  owl_viewwin_dirty(v);
336}
337
338void owl_viewwin_top(owl_viewwin *v)
339{
340  v->topline=0;
341  v->rightshift=0;
342  owl_viewwin_dirty(v);
343}
344
345void owl_viewwin_bottom(owl_viewwin *v)
346{
347  int winlines;
348  owl_window_get_position(v->content, &winlines, 0, 0, 0);
349  v->topline = v->textlines - winlines;
350  owl_viewwin_dirty(v);
351}
352
353/* This is a bit of a hack, because regexec doesn't have an 'n'
354 * version. */
355static int _re_memcompare(const owl_regex *re, const char *string, int start, int end)
356{
357  int ans;
358  char *tmp = g_strndup(string + start, end - start);
359  ans = owl_regex_compare(re, tmp, NULL, NULL);
360  g_free(tmp);
361  return !ans;
362}
363
364/* Scroll in 'direction' to the next line containing 're' in 'v',
365 * starting from the current line. Returns 0 if no occurrence is
366 * found.
367 *
368 * If consider_current is true then stay on the current line
369 * if it matches.
370 */
371int owl_viewwin_search(owl_viewwin *v, const owl_regex *re, int consider_current, int direction)
372{
373  int start, end, offset;
374  int lineend, linestart;
375  const char *buf, *linestartp;
376  owl_fmtext_line_extents(&v->fmtext, v->topline, &start, &end);
377  if (direction == OWL_DIRECTION_DOWNWARDS) {
378    offset = owl_fmtext_search(&v->fmtext, re,
379                               consider_current ? start : end);
380    if (offset < 0)
381      return 0;
382    v->topline = owl_fmtext_line_number(&v->fmtext, offset);
383    owl_viewwin_dirty(v);
384    return 1;
385  } else {
386    /* TODO: This is a hack. Really, we should have an owl_fmlines or
387     * something containing an array of owl_fmtext split into
388     * lines. Also, it cannot handle multi-line regex, if we ever care about
389     * them. */
390    buf = owl_fmtext_get_text(&v->fmtext);
391    lineend = consider_current ? end : start;
392    while (lineend > 0) {
393      linestartp = memrchr(buf, '\n', lineend - 1);
394      linestart = linestartp ? linestartp - buf + 1 : 0;
395      if (_re_memcompare(re, buf, linestart, lineend)) {
396        v->topline = owl_fmtext_line_number(&v->fmtext, linestart);
397        owl_viewwin_dirty(v);
398        return 1;
399      }
400      lineend = linestart;
401    }
402    return 0;
403  }
404}
405
406void owl_viewwin_delete(owl_viewwin *v)
407{
408  if (v->onclose_hook) {
409    v->onclose_hook(v, v->onclose_hook_data);
410    v->onclose_hook = NULL;
411    v->onclose_hook_data = NULL;
412  }
413  owl_viewwin_set_typwin_inactive(v);
414  /* TODO: This is far too tedious. owl_viewwin should own v->window
415   * and just unlink it in one go. Signals should also go away for
416   * free. */
417  g_signal_handler_disconnect(v->window, v->sig_resize_id);
418  g_signal_handler_disconnect(v->content, v->sig_content_redraw_id);
419  g_signal_handler_disconnect(v->status, v->sig_status_redraw_id);
420  owl_window_unlink(v->content);
421  owl_window_unlink(v->status);
422  g_object_unref(v->window);
423  g_object_unref(v->content);
424  g_object_unref(v->status);
425  owl_fmtext_cleanup(&(v->fmtext));
426  g_free(v);
427}
Note: See TracBrowser for help on using the repository browser.