source: viewwin.c @ 353719a

release-1.10release-1.9
Last change on this file since 353719a was 7803326, checked in by Jason Gross <jgross@mit.edu>, 13 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
RevLine 
[7d4fbcd]1#include "owl.h"
2
3#define BOTTOM_OFFSET 1
4
[60c1386]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);
[61c1f19]8static void owl_viewwin_set_window(owl_viewwin *v, owl_window *w);
[0b9e607]9
[9eb38bb]10/* Create a viewwin.  'win' is an already initialized owl_window that
11 * will be used by the viewwin
[b2b0773]12 */
[6829afc]13CALLER_OWN owl_viewwin *owl_viewwin_new_text(owl_window *win, const char *text)
[b2b0773]14{
[96828e4]15  owl_viewwin *v = g_new0(owl_viewwin, 1);
[7d4fbcd]16  owl_fmtext_init_null(&(v->fmtext));
17  if (text) {
18    owl_fmtext_append_normal(&(v->fmtext), text);
[4083c49]19    if (text[0] != '\0' && text[strlen(text) - 1] != '\n') {
[7d4fbcd]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;
[afbf668]26  v->onclose_hook = NULL;
[68f63a2]27
28  owl_viewwin_set_window(v, win);
[9eb38bb]29  return v;
[afbf668]30}
31
[9eb38bb]32/* Create a viewwin.  'win' is an already initialized owl_window that
33 * will be used by the viewwin
[b2b0773]34 */
[6829afc]35CALLER_OWN owl_viewwin *owl_viewwin_new_fmtext(owl_window *win, const owl_fmtext *fmtext)
[b2b0773]36{
[dd6af02]37  char *text;
[96828e4]38  owl_viewwin *v = g_new0(owl_viewwin, 1);
[dd6af02]39
[7d4fbcd]40  owl_fmtext_copy(&(v->fmtext), fmtext);
[dd6af02]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  }
[ddbbcffa]45  g_free(text);
[7d4fbcd]46  v->textlines=owl_fmtext_num_lines(&(v->fmtext));
47  v->topline=0;
48  v->rightshift=0;
[68f63a2]49
50  owl_viewwin_set_window(v, win);
[9eb38bb]51  return v;
[7d4fbcd]52}
53
[61c1f19]54static void owl_viewwin_set_window(owl_viewwin *v, owl_window *w)
[b2b0773]55{
[60c1386]56  v->window = g_object_ref(w);
57  v->content = owl_window_new(v->window);
58  v->status = owl_window_new(v->window);
[09ceee3]59  v->cmdwin = NULL;
[60c1386]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);
[7d4fbcd]71}
72
[afbf668]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
[60c1386]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);
[09ceee3]83  /* Only one of these will be visible at a time: */
[60c1386]84  owl_window_set_position(v->status, BOTTOM_OFFSET, cols, lines - BOTTOM_OFFSET, 0);
[09ceee3]85  if (v->cmdwin)
86    owl_window_set_position(v->cmdwin, BOTTOM_OFFSET, cols, lines - BOTTOM_OFFSET, 0);
[60c1386]87}
88
[b2b0773]89/* regenerate text on the curses window. */
[60c1386]90static void owl_viewwin_redraw_content(owl_window *w, WINDOW *curswin, void *user_data)
[b2b0773]91{
[7d4fbcd]92  owl_fmtext fm1, fm2;
[68f63a2]93  owl_viewwin *v = user_data;
[c447d9c]94  int winlines, wincols;
95
96  owl_window_get_position(w, &winlines, &wincols, 0, 0);
[60c1386]97
[68f63a2]98  werase(curswin);
99  wmove(curswin, 0, 0);
[7d4fbcd]100
[af2ca19]101  owl_fmtext_init_null(&fm1);
102  owl_fmtext_init_null(&fm2);
[60c1386]103
104  owl_fmtext_truncate_lines(&(v->fmtext), v->topline, winlines, &fm1);
[c447d9c]105  owl_fmtext_truncate_cols(&fm1, v->rightshift, wincols-1+v->rightshift, &fm2);
[7d4fbcd]106
[237d02c]107  owl_fmtext_curs_waddstr(&fm2, curswin, OWL_FMTEXT_ATTR_NONE, OWL_COLOR_DEFAULT, OWL_COLOR_DEFAULT);
[7d4fbcd]108
[60c1386]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);
[68f63a2]122  wattrset(curswin, A_REVERSE);
[60c1386]123  if (v->textlines - v->topline > winlines) {
[68f63a2]124    waddstr(curswin, "--More-- (Space to see more, 'q' to quit)");
[7d4fbcd]125  } else {
[68f63a2]126    waddstr(curswin, "--End-- (Press 'q' to quit)");
[7d4fbcd]127  }
[68f63a2]128  wattroff(curswin, A_REVERSE);
[7d4fbcd]129}
130
[b6cf72f]131char *owl_viewwin_command_search(owl_viewwin *v, int argc, const char *const *argv, const char *buff)
132{
[118c919]133  int direction, consider_current;
[b6cf72f]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"))) {
[118c919]144    consider_current = false;
[b6cf72f]145  } else {
146    owl_function_set_search(buffstart);
[118c919]147    consider_current = true;
[b6cf72f]148  }
149
[118c919]150  if (!owl_viewwin_search(v, owl_global_get_search_re(&g), consider_current, direction))
[4fd211f]151    owl_function_makemsg("No more matches");
[b6cf72f]152  return NULL;
153}
154
[d2fd2f7]155typedef struct _owl_viewwin_search_data { /*noproto*/
156  owl_viewwin *v;
157  int direction;
158} owl_viewwin_search_data;
159
[7803326]160static void owl_viewwin_callback_search(owl_editwin *e, bool success)
[d2fd2f7]161{
[7803326]162  if (!success) return;
[09065ed]163  int consider_current = false;
[d2fd2f7]164  const char *line = owl_editwin_get_text(e);
165  owl_viewwin_search_data *data = owl_editwin_get_cbdata(e);
[09065ed]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  }
[d2fd2f7]172  if (!owl_viewwin_search(data->v, owl_global_get_search_re(&g),
[09065ed]173                          consider_current, data->direction))
[4fd211f]174    owl_function_makemsg("No matches");
[d2fd2f7]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
[96828e4]197  data = g_new(owl_viewwin_search_data, 1);
[d2fd2f7]198  data->v = v;
199  data->direction = direction;
200
[4a41f16]201  ctx = owl_editcontext_new(OWL_CTX_EDITLINE, tw, "editline",
202                            owl_viewwin_deactivate_editcontext, v);
[d2fd2f7]203  ctx->cbdata = v;
204  owl_global_push_context_obj(&g, ctx);
205  owl_editwin_set_callback(tw, owl_viewwin_callback_search);
[ddbbcffa]206  owl_editwin_set_cbdata(tw, data, g_free);
[4c0ac34]207  /* We aren't saving tw, so release the reference we were given. */
208  owl_editwin_unref(tw);
[d2fd2f7]209  return NULL;
210}
211
[09ceee3]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
[4a41f16]224  ctx = owl_editcontext_new(OWL_CTX_EDITLINE, tw, "editline",
225                            owl_viewwin_deactivate_editcontext, v);
[09ceee3]226  owl_global_push_context_obj(&g, ctx);
227  owl_editwin_set_callback(tw, owl_callback_command);
[4c0ac34]228  /* We aren't saving tw, so release the reference we were given. */
229  owl_editwin_unref(tw);
[09ceee3]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
[6829afc]239CALLER_OWN owl_editwin *owl_viewwin_set_typwin_active(owl_viewwin *v, owl_history *hist) {
[09ceee3]240  int lines, cols;
[1b8c3f8]241  owl_editwin *cmdline;
242  if (v->cmdwin)
[09ceee3]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);
[1b8c3f8]248  cmdline = owl_editwin_new(v->cmdwin, lines, cols, OWL_EDITWIN_STYLE_ONELINE, hist);
[09ceee3]249  /* Swap out the bottom window. */
250  owl_window_hide(v->status);
251  owl_window_show(v->cmdwin);
[1b8c3f8]252  return cmdline;
[09ceee3]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
[2640b63]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{
[60c1386]278  owl_window_dirty(v->content);
279  owl_window_dirty(v->status);
[2640b63]280}
281
[d574d61]282void owl_viewwin_down(owl_viewwin *v, int amount) {
[c447d9c]283  int winlines;
[60c1386]284  owl_window_get_position(v->content, &winlines, 0, 0, 0);
[fa23002]285  /* Don't scroll past the bottom. */
[60c1386]286  amount = MIN(amount, v->textlines - (v->topline + winlines));
[fa23002]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);
[7d4fbcd]291  }
292}
293
[d574d61]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)
[b2b0773]302{
[c447d9c]303  int winlines;
[60c1386]304  owl_window_get_position(v->content, &winlines, 0, 0, 0);
305  owl_viewwin_down(v, winlines);
[d574d61]306}
307
308void owl_viewwin_linedown(owl_viewwin *v)
309{
310  owl_viewwin_down(v, 1);
[7d4fbcd]311}
312
[b2b0773]313void owl_viewwin_pageup(owl_viewwin *v)
314{
[c447d9c]315  int winlines;
[60c1386]316  owl_window_get_position(v->content, &winlines, 0, 0, 0);
317  owl_viewwin_up(v, winlines);
[7d4fbcd]318}
319
[b2b0773]320void owl_viewwin_lineup(owl_viewwin *v)
321{
[d574d61]322  owl_viewwin_up(v, 1);
[7d4fbcd]323}
324
[b2b0773]325void owl_viewwin_right(owl_viewwin *v, int n)
326{
[7d4fbcd]327  v->rightshift+=n;
[5b68c05]328  owl_viewwin_dirty(v);
[7d4fbcd]329}
330
[b2b0773]331void owl_viewwin_left(owl_viewwin *v, int n)
332{
[7d4fbcd]333  v->rightshift-=n;
334  if (v->rightshift<0) v->rightshift=0;
[5b68c05]335  owl_viewwin_dirty(v);
[7d4fbcd]336}
337
[b2b0773]338void owl_viewwin_top(owl_viewwin *v)
339{
[7d4fbcd]340  v->topline=0;
341  v->rightshift=0;
[5b68c05]342  owl_viewwin_dirty(v);
[7d4fbcd]343}
344
[b2b0773]345void owl_viewwin_bottom(owl_viewwin *v)
346{
[c447d9c]347  int winlines;
[60c1386]348  owl_window_get_position(v->content, &winlines, 0, 0, 0);
349  v->topline = v->textlines - winlines;
[5b68c05]350  owl_viewwin_dirty(v);
[7d4fbcd]351}
352
[6a35938]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
[b8742ba]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 *
[118c919]368 * If consider_current is true then stay on the current line
[9c1e61d4]369 * if it matches.
[b8742ba]370 */
[118c919]371int owl_viewwin_search(owl_viewwin *v, const owl_regex *re, int consider_current, int direction)
[b8742ba]372{
373  int start, end, offset;
[6a35938]374  int lineend, linestart;
375  const char *buf, *linestartp;
[b8742ba]376  owl_fmtext_line_extents(&v->fmtext, v->topline, &start, &end);
377  if (direction == OWL_DIRECTION_DOWNWARDS) {
[9c1e61d4]378    offset = owl_fmtext_search(&v->fmtext, re,
[118c919]379                               consider_current ? start : end);
[b8742ba]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 {
[6a35938]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);
[118c919]391    lineend = consider_current ? end : start;
[6a35938]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;
[b8742ba]403  }
404}
405
[9eb38bb]406void owl_viewwin_delete(owl_viewwin *v)
[b2b0773]407{
[afbf668]408  if (v->onclose_hook) {
409    v->onclose_hook(v, v->onclose_hook_data);
[8721756]410    v->onclose_hook = NULL;
411    v->onclose_hook_data = NULL;
[afbf668]412  }
[09ceee3]413  owl_viewwin_set_typwin_inactive(v);
[60c1386]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);
[7ab0020]425  owl_fmtext_cleanup(&(v->fmtext));
[ddbbcffa]426  g_free(v);
[7d4fbcd]427}
Note: See TracBrowser for help on using the repository browser.