source: viewwin.c @ ca1fb26a

release-1.9
Last change on this file since ca1fb26a was 7803326, checked in by Jason Gross <jgross@mit.edu>, 10 years ago
editwin callback for canceling the editwin The code for editwin callbacks (called when the editwin is created) has been extended so that callbacks are called when the editwin is canceled. The old (perl) editwin callbacks still exist and have the same functionality that they always have. They are now deprecated.
  • Property mode set to 100644
File size: 12.3 KB
Line 
1#include "owl.h"
2
3#define BOTTOM_OFFSET 1
4
5static void owl_viewwin_redraw_content(owl_window *w, WINDOW *curswin, void *user_data);
6static void owl_viewwin_redraw_status(owl_window *w, WINDOW *curswin, void *user_data);
7static void owl_viewwin_layout(owl_viewwin *v);
8static void owl_viewwin_set_window(owl_viewwin *v, owl_window *w);
9
10/* Create a viewwin.  'win' is an already initialized owl_window that
11 * will be used by the viewwin
12 */
13CALLER_OWN owl_viewwin *owl_viewwin_new_text(owl_window *win, const char *text)
14{
15  owl_viewwin *v = g_new0(owl_viewwin, 1);
16  owl_fmtext_init_null(&(v->fmtext));
17  if (text) {
18    owl_fmtext_append_normal(&(v->fmtext), text);
19    if (text[0] != '\0' && text[strlen(text) - 1] != '\n') {
20      owl_fmtext_append_normal(&(v->fmtext), "\n");
21    }
22    v->textlines=owl_fmtext_num_lines(&(v->fmtext));
23  }
24  v->topline=0;
25  v->rightshift=0;
26  v->onclose_hook = NULL;
27
28  owl_viewwin_set_window(v, win);
29  return v;
30}
31
32/* Create a viewwin.  'win' is an already initialized owl_window that
33 * will be used by the viewwin
34 */
35CALLER_OWN owl_viewwin *owl_viewwin_new_fmtext(owl_window *win, const owl_fmtext *fmtext)
36{
37  char *text;
38  owl_viewwin *v = g_new0(owl_viewwin, 1);
39
40  owl_fmtext_copy(&(v->fmtext), fmtext);
41  text = owl_fmtext_print_plain(fmtext);
42  if (text[0] != '\0' && text[strlen(text) - 1] != '\n') {
43      owl_fmtext_append_normal(&(v->fmtext), "\n");
44  }
45  g_free(text);
46  v->textlines=owl_fmtext_num_lines(&(v->fmtext));
47  v->topline=0;
48  v->rightshift=0;
49
50  owl_viewwin_set_window(v, win);
51  return v;
52}
53
54static void owl_viewwin_set_window(owl_viewwin *v, owl_window *w)
55{
56  v->window = g_object_ref(w);
57  v->content = owl_window_new(v->window);
58  v->status = owl_window_new(v->window);
59  v->cmdwin = NULL;
60
61  v->sig_content_redraw_id =
62    g_signal_connect(v->content, "redraw", G_CALLBACK(owl_viewwin_redraw_content), v);
63  v->sig_status_redraw_id =
64    g_signal_connect(v->status, "redraw", G_CALLBACK(owl_viewwin_redraw_status), v);
65  v->sig_resize_id =
66    g_signal_connect_swapped(v->window, "resized", G_CALLBACK(owl_viewwin_layout), v);
67  owl_viewwin_layout(v);
68
69  owl_window_show(v->content);
70  owl_window_show(v->status);
71}
72
73void owl_viewwin_set_onclose_hook(owl_viewwin *v, void (*onclose_hook) (owl_viewwin *vwin, void *data), void *onclose_hook_data) {
74  v->onclose_hook = onclose_hook;
75  v->onclose_hook_data = onclose_hook_data;
76}
77
78static void owl_viewwin_layout(owl_viewwin *v)
79{
80  int lines, cols;
81  owl_window_get_position(v->window, &lines, &cols, NULL, NULL);
82  owl_window_set_position(v->content, lines - BOTTOM_OFFSET, cols, 0, 0);
83  /* Only one of these will be visible at a time: */
84  owl_window_set_position(v->status, BOTTOM_OFFSET, cols, lines - BOTTOM_OFFSET, 0);
85  if (v->cmdwin)
86    owl_window_set_position(v->cmdwin, BOTTOM_OFFSET, cols, lines - BOTTOM_OFFSET, 0);
87}
88
89/* regenerate text on the curses window. */
90static void owl_viewwin_redraw_content(owl_window *w, WINDOW *curswin, void *user_data)
91{
92  owl_fmtext fm1, fm2;
93  owl_viewwin *v = user_data;
94  int winlines, wincols;
95
96  owl_window_get_position(w, &winlines, &wincols, 0, 0);
97
98  werase(curswin);
99  wmove(curswin, 0, 0);
100
101  owl_fmtext_init_null(&fm1);
102  owl_fmtext_init_null(&fm2);
103
104  owl_fmtext_truncate_lines(&(v->fmtext), v->topline, winlines, &fm1);
105  owl_fmtext_truncate_cols(&fm1, v->rightshift, wincols-1+v->rightshift, &fm2);
106
107  owl_fmtext_curs_waddstr(&fm2, curswin, OWL_FMTEXT_ATTR_NONE, OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
108
109  owl_fmtext_cleanup(&fm1);
110  owl_fmtext_cleanup(&fm2);
111}
112
113static void owl_viewwin_redraw_status(owl_window *w, WINDOW *curswin, void *user_data)
114{
115  owl_viewwin *v = user_data;
116  int winlines, wincols;
117
118  owl_window_get_position(v->content, &winlines, &wincols, 0, 0);
119
120  werase(curswin);
121  wmove(curswin, 0, 0);
122  wattrset(curswin, A_REVERSE);
123  if (v->textlines - v->topline > winlines) {
124    waddstr(curswin, "--More-- (Space to see more, 'q' to quit)");
125  } else {
126    waddstr(curswin, "--End-- (Press 'q' to quit)");
127  }
128  wattroff(curswin, A_REVERSE);
129}
130
131char *owl_viewwin_command_search(owl_viewwin *v, int argc, const char *const *argv, const char *buff)
132{
133  int direction, consider_current;
134  const char *buffstart;
135
136  direction=OWL_DIRECTION_DOWNWARDS;
137  buffstart=skiptokens(buff, 1);
138  if (argc>1 && !strcmp(argv[1], "-r")) {
139    direction=OWL_DIRECTION_UPWARDS;
140    buffstart=skiptokens(buff, 2);
141  }
142
143  if (argc==1 || (argc==2 && !strcmp(argv[1], "-r"))) {
144    consider_current = false;
145  } else {
146    owl_function_set_search(buffstart);
147    consider_current = true;
148  }
149
150  if (!owl_viewwin_search(v, owl_global_get_search_re(&g), consider_current, direction))
151    owl_function_makemsg("No more matches");
152  return NULL;
153}
154
155typedef struct _owl_viewwin_search_data { /*noproto*/
156  owl_viewwin *v;
157  int direction;
158} owl_viewwin_search_data;
159
160static void owl_viewwin_callback_search(owl_editwin *e, bool success)
161{
162  if (!success) return;
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
239CALLER_OWN 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.