source: window.c @ 402ed3d3

release-1.10release-1.7release-1.8release-1.9
Last change on this file since 402ed3d3 was 402ed3d3, checked in by David Benjamin <davidben@mit.edu>, 14 years ago
Use two functions: show/show_all instead of recurse flag It's more readable, and matches GTK.
  • Property mode set to 100644
File size: 11.9 KB
RevLine 
[449af72]1#include "owl.h"
2
3#include <assert.h>
4
5struct _owl_window { /*noproto*/
6  /* hierarchy information */
7  owl_window *parent;
8  owl_window *child;
9  owl_window *next, *prev;
10  /* flags */
11  int dirty : 1;
12  int dirty_subtree : 1;
[50031f0]13  int shown : 1;
[449af72]14  int is_screen : 1;
15  /* window information */
16  WINDOW *win;
17  PANEL *pan;
18  int nlines, ncols;
19  int begin_y, begin_x;
20  /* hooks */
21  void (*redraw_cb)(owl_window *, WINDOW *, void *);
[5d74b7c]22  void  *redraw_cbdata;
[449af72]23  void (*redraw_cbdata_destroy)(void *);
24
[d106110]25  void (*size_cb)(owl_window *, void *);
[5d74b7c]26  void  *size_cbdata;
[d106110]27  void (*size_cbdata_destroy)(void *);
[449af72]28
29  void (*destroy_cb)(owl_window *, void *);
[5d74b7c]30  void  *destroy_cbdata;
[449af72]31  void (*destroy_cbdata_destroy)(void *);
32};
33
34static owl_window *_owl_window_new(owl_window *parent, int nlines, int ncols, int begin_y, int begin_x);
35
36static void _owl_window_unlink(owl_window *w);
37static void _owl_window_link(owl_window *w, owl_window *parent);
38
39static void _owl_window_create_curses(owl_window *w);
40static void _owl_window_destroy_curses(owl_window *w);
41
[46e2e56]42static void _owl_window_realize(owl_window *w);
43static void _owl_window_unrealize(owl_window *w);
[449af72]44
45/** singletons **/
46
47static WINDOW *_dummy_window(void)
48{
49  static WINDOW *dummy = NULL;
50  if (!dummy) {
51    dummy = newwin(1, 1, 0, 0);
52  }
53  return dummy;
54}
55
[d39f68c]56static void _screen_calculate_size(owl_window *screen, void *user_data)
57{
58  owl_global *g = user_data;
59  int lines, cols;
60  owl_global_get_terminal_size(&lines, &cols);
61  if (!g->lines) g->lines = lines;
62  if (!g->cols) g->cols = cols;
63  owl_window_resize(screen, g->lines, g->cols);
64}
65
[449af72]66owl_window *owl_window_get_screen(void)
67{
68  static owl_window *screen = NULL;
69  if (!screen) {
[50031f0]70    /* The screen is magical. It's 'shown', but the only mean of it going
[449af72]71     * invisible is if we're tore down curses (i.e. screen resize) */
72    screen = _owl_window_new(NULL, g.lines, g.cols, 0, 0);
73    screen->is_screen = 1;
[d39f68c]74    owl_window_set_size_cb(screen, _screen_calculate_size, &g, 0);
[402ed3d3]75    owl_window_show(screen);
[449af72]76  }
77  return screen;
78}
79
80/** Creation and Destruction **/
81
[d106110]82owl_window *owl_window_new(owl_window *parent)
[449af72]83{
84  if (!parent)
85    parent = owl_window_get_screen();
[d106110]86  return _owl_window_new(parent, 0, 0, 0, 0);
[449af72]87}
88
89static owl_window *_owl_window_new(owl_window *parent, int nlines, int ncols, int begin_y, int begin_x)
90{
91  owl_window *w;
92
93  w = owl_malloc(sizeof(owl_window));
94  memset(w, 0, sizeof(*w));
95
96  w->nlines = nlines;
97  w->ncols = ncols;
98  w->begin_y = begin_y;
99  w->begin_x = begin_x;
100
101  _owl_window_link(w, parent);
102  if (parent && parent->is_screen) {
103    w->pan = new_panel(_dummy_window());
104    set_panel_userptr(w->pan, w);
105  }
106
107  return w;
108}
109
110void owl_window_delete(owl_window *w)
111{
112  /* destroy all children */
113  owl_window_children_foreach_onearg(w, owl_window_delete);
114
115  /* notify everyone of your imminent demise */
116  if (w->destroy_cb)
117    w->destroy_cb(w, w->destroy_cbdata);
118
119  /* clear all cbs */
120  owl_window_set_redraw_cb(w, 0, 0, 0);
[d106110]121  owl_window_set_size_cb(w, 0, 0, 0);
[449af72]122  owl_window_set_destroy_cb(w, 0, 0, 0);
123
124  /* destroy curses structures */
125  owl_window_unmap(w);
126  if (w->pan) {
127    del_panel(w->pan);
128  }
129
130  /* remove from hierarchy */
131  _owl_window_unlink(w);
132  owl_free(w);
133}
134
135/** Callbacks **/
136
137void owl_window_set_redraw_cb(owl_window *w, void (*cb)(owl_window*, WINDOW*, void*), void *cbdata, void (*cbdata_destroy)(void*))
138{
139  if (w->redraw_cbdata_destroy) {
140    w->redraw_cbdata_destroy(w->redraw_cbdata);
141    w->redraw_cbdata = 0;
142    w->redraw_cbdata_destroy = 0;
143  }
144
145  w->redraw_cb = cb;
146  w->redraw_cbdata = cbdata;
147  w->redraw_cbdata_destroy = cbdata_destroy;
148
149  /* mark the window as dirty, to take new cb in account */
150  owl_window_dirty(w);
151}
152
[d106110]153void owl_window_set_size_cb(owl_window *w, void (*cb)(owl_window*, void*), void *cbdata, void (*cbdata_destroy)(void*))
[449af72]154{
[d106110]155  if (w->size_cbdata_destroy) {
156    w->size_cbdata_destroy(w->size_cbdata);
157    w->size_cbdata = 0;
158    w->size_cbdata_destroy = 0;
[449af72]159  }
160
[d106110]161  w->size_cb = cb;
162  w->size_cbdata = cbdata;
163  w->size_cbdata_destroy = cbdata_destroy;
164
165  owl_window_recompute_position(w);
[449af72]166}
167
168void owl_window_set_destroy_cb(owl_window *w, void (*cb)(owl_window*, void*), void *cbdata, void (*cbdata_destroy)(void*))
169{
170  if (w->destroy_cbdata_destroy) {
171    w->destroy_cbdata_destroy(w->destroy_cbdata);
172    w->destroy_cbdata = 0;
173    w->destroy_cbdata_destroy = 0;
174  }
175
176  w->destroy_cb = cb;
177  w->destroy_cbdata = cbdata;
178  w->destroy_cbdata_destroy = cbdata_destroy;
179}
180
181/** Hierarchy **/
182
183static void _owl_window_unlink(owl_window *w)
184{
[ec4ccfc]185  /* make sure the window is unmapped first */
[46e2e56]186  _owl_window_unrealize(w);
[449af72]187  /* unlink parent/child information */
188  if (w->parent) {
189    if (w->prev)
190      w->prev->next = w->next;
191    if (w->next)
192      w->next->prev = w->prev;
193    if (w->parent->child == w)
194      w->parent->child = w->next;
[933aa7d]195    w->parent = NULL;
[449af72]196  }
197}
198
199static void _owl_window_link(owl_window *w, owl_window *parent)
200{
201  if (w->parent == parent)
202    return;
203
[933aa7d]204  owl_window_unlink(w);
[449af72]205  if (parent) {
[933aa7d]206    w->parent = parent;
[449af72]207    w->next = parent->child;
208    parent->child = w;
209  }
210}
211
212/* mimic g_list_foreach for consistency */
213void owl_window_children_foreach(owl_window *parent, GFunc func, gpointer user_data)
214{
215  owl_window *w;
216  for (w = parent->child;
217       w != NULL;
218       w = w->next) {
219    func(w, user_data);
220  }
221}
222
223void owl_window_children_foreach_onearg(owl_window *parent, void (*func)(owl_window*))
224{
225  owl_window *w;
226  for (w = parent->child;
227       w != NULL;
228       w = w->next) {
229    func(w);
230  }
231}
232
[b6cb985]233owl_window *owl_window_get_parent(owl_window *w)
234{
235  return w->parent;
236}
237
[449af72]238/** Internal window management **/
239
240static void _owl_window_create_curses(owl_window *w)
241{
242  if (w->is_screen) {
243    resizeterm(w->nlines, w->ncols);
[2dfccc7]244    w->win = stdscr;
[449af72]245  } else if (w->pan) {
246    w->win = newwin(w->nlines, w->ncols, w->begin_y, w->begin_x);
247    replace_panel(w->pan, w->win);
248  } else {
[fe49685]249    if (w->parent == NULL || w->parent->win == NULL)
250      return;
[449af72]251    w->win = derwin(w->parent->win, w->nlines, w->ncols, w->begin_y, w->begin_x);
252  }
253}
254
255static void _owl_window_destroy_curses(owl_window *w)
256{
257  if (w->is_screen) {
258    /* don't deallocate the dummy */
259    w->win = NULL;
260  } else {
261    if (w->pan) {
262      /* panels assume their windows always exist, so we put in a fake one */
263      replace_panel(w->pan, _dummy_window());
264    }
265    if (w->win) {
266      /* and destroy our own window */
267      delwin(w->win);
268      w->win = NULL;
269    }
270  }
271}
272
[402ed3d3]273void owl_window_show(owl_window *w)
[449af72]274{
[50031f0]275  w->shown = 1;
[46e2e56]276  _owl_window_realize(w);
[449af72]277  if (w->pan)
278    show_panel(w->pan);
[402ed3d3]279}
280
281void owl_window_show_all(owl_window *w)
282{
283  owl_window_show(w);
284  owl_window_children_foreach_onearg(w, owl_window_show);
[449af72]285}
286
[50031f0]287void owl_window_hide(owl_window *w)
[449af72]288{
289  /* you can't unmap the screen */
290  if (w->is_screen)
291    return;
[50031f0]292  w->shown = 0;
[449af72]293  if (w->pan)
294    hide_panel(w->pan);
[46e2e56]295  _owl_window_unrealize(w);
[449af72]296}
297
[50031f0]298int owl_window_is_shown(owl_window *w)
[449af72]299{
[50031f0]300  return w->shown;
[449af72]301}
302
[ce7c6c3]303int owl_window_is_realized(owl_window *w)
[449af72]304{
305  return w->win != NULL;
306}
307
308int owl_window_is_toplevel(owl_window *w)
309{
310  return w->pan != NULL;
311}
312
[46e2e56]313static void _owl_window_realize(owl_window *w)
[449af72]314{
315  /* check if we can create a window */
316  if ((w->parent && w->parent->win == NULL)
[50031f0]317      || !w->shown
[449af72]318      || w->win != NULL)
319    return;
320  _owl_window_create_curses(w);
[fe49685]321  if (w->win == NULL)
322    return;
[449af72]323  /* schedule a repaint */
324  owl_window_dirty(w);
[fe49685]325  /* map the children */
326  owl_window_children_foreach_onearg(w, _owl_window_realize);
[449af72]327}
328
[46e2e56]329static void _owl_window_unrealize(owl_window *w)
[449af72]330{
331  if (w->win == NULL)
332    return;
333  /* unmap all the children */
[46e2e56]334  owl_window_children_foreach_onearg(w, _owl_window_unrealize);
[449af72]335  _owl_window_destroy_curses(w);
336  /* subwins leave a mess in the parent; dirty it */
337  if (w->parent)
338    owl_window_dirty(w->parent);
339}
340
341/** Painting and book-keeping **/
342
343void owl_window_dirty(owl_window *w)
344{
345  /* don't put the screen on this list; pointless */
346  if (w->is_screen)
347    return;
[7cbef8c]348  if (!owl_window_is_realized(w))
349    return;
[449af72]350  if (!w->dirty) {
351    w->dirty = 1;
352    while (w && !w->dirty_subtree) {
353      w->dirty_subtree = 1;
354      w = w->parent;
355    }
356    owl_global_set_needrefresh(&g);
357  }
358}
359
360void owl_window_dirty_children(owl_window *w)
361{
362  owl_window_children_foreach_onearg(w, owl_window_dirty);
363}
364
365static void _owl_window_redraw(owl_window *w)
366{
367  if (!w->dirty) return;
368  if (w->win && w->redraw_cb) {
369    w->redraw_cb(w, w->win, w->redraw_cbdata);
[6eefb5e]370    wsyncup(w->win);
[449af72]371  }
372  w->dirty = 0;
373}
374
375static void _owl_window_redraw_subtree(owl_window *w)
376{
377  if (!w->dirty_subtree)
378    return;
379  _owl_window_redraw(w);
380  owl_window_children_foreach_onearg(w, _owl_window_redraw_subtree);
381}
382
383/*
384Redraw all the windows with scheduled redraws.
385NOTE: This function shouldn't be called outside the event loop
386*/
387void owl_window_redraw_scheduled(void)
388{
389  _owl_window_redraw_subtree(owl_window_get_screen());
390}
391
[7c8811c]392void owl_window_erase_cb(owl_window *w, WINDOW *win, void *user_data)
393{
394  werase(win);
395  owl_window_dirty_children(w);
396}
397
[449af72]398/** Window position **/
399
400void owl_window_get_position(owl_window *w, int *nlines, int *ncols, int *begin_y, int *begin_x)
401{
402  if (nlines)  *nlines  = w->nlines;
403  if (ncols)   *ncols   = w->ncols;
404  if (begin_y) *begin_y = w->begin_y;
405  if (begin_x) *begin_x = w->begin_x;
406}
407
[b0cbde4]408void owl_window_move(owl_window *w, int begin_y, int begin_x)
409{
[6b93305]410  if (w->is_screen) return; /* can't move the screen */
411  if (w->begin_y == begin_y && w->begin_x == begin_x) return;
[b0cbde4]412
413  w->begin_y = begin_y;
414  w->begin_x = begin_x;
[50031f0]415  if (w->shown) {
416    /* Window is shown, we must try to have a window at the end */
[b0cbde4]417    if (w->win) {
418      /* We actually do have a window; let's move it */
419      if (w->pan) {
420        if (move_panel(w->pan, begin_y, begin_x) == OK)
421          return;
422      } else {
423        if (mvderwin(w->win, begin_y, begin_x) == OK) {
424          /* now both we and the parent are dirty */
425          owl_window_dirty(w->parent);
426          owl_window_dirty(w);
427          return;
428        }
429      }
430    }
431    /* We don't have a window or failed to move it. Fine. Brute force. */
[46e2e56]432    _owl_window_unrealize(w);
433    _owl_window_realize(w);
[b0cbde4]434  }
435}
436
[449af72]437void owl_window_set_position(owl_window *w, int nlines, int ncols, int begin_y, int begin_x)
438{
439  /* can't move the screen */
440  if (w->is_screen) {
441    begin_y = begin_x = 0;
442  }
443
444  if (w->nlines == nlines && w->ncols == ncols) {
445    /* moving is easier */
446    owl_window_move(w, begin_y, begin_x);
447    return;
448  }
449
450  w->begin_y = begin_y;
451  w->begin_x = begin_x;
452  w->nlines = nlines;
453  w->ncols = ncols;
[50031f0]454  /* window is shown, we must try to have a window at the end */
455  if (w->shown) {
[46e2e56]456    /* resizing in ncurses is hard: give up do a unrealize/realize */
457    _owl_window_unrealize(w);
[449af72]458  }
[d106110]459  /* recalculate children sizes BEFORE remapping, so that everything can resize */
460  owl_window_children_foreach_onearg(w, owl_window_recompute_position);
[50031f0]461  if (w->shown) {
[46e2e56]462    _owl_window_realize(w);
[449af72]463  }
464}
465
466void owl_window_resize(owl_window *w, int nlines, int ncols)
467{
468  owl_window_set_position(w, nlines, ncols, w->begin_y, w->begin_x);
469}
470
[d106110]471void owl_window_recompute_position(owl_window *w)
472{
473  if (w->size_cb) {
474    /* TODO: size_cb probably wants to actually take four int*s */
475    w->size_cb(w, w->size_cbdata);
476  }
477}
478
479
[449af72]480/** Stacking order **/
481
482void owl_window_top(owl_window *w) {
483  if (!w->pan) {
[7c8811c]484    owl_function_debugmsg("Warning: owl_window_top only makes sense on top-level window");
[449af72]485    return;
486  }
487  top_panel(w->pan);
488}
489
490owl_window *owl_window_above(owl_window *w) {
491  PANEL *pan;
492
493  if (!w->pan) {
494    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
495    return NULL;
496  }
497  pan = panel_above(w->pan);
498  /* cast because panel_userptr pointlessly returns a const void * */
499  return pan ? (void*) panel_userptr(pan) : NULL;
500}
501
502owl_window *owl_window_below(owl_window *w) {
503  PANEL *pan;
504
505  if (!w->pan) {
506    owl_function_debugmsg("Warning: owl_window_above only makes sense on top-level window");
507    return NULL;
508  }
509  pan = panel_below(w->pan);
510  /* cast because panel_userptr pointlessly returns a const void * */
511  return pan ? (void*) panel_userptr(pan) : NULL;
512}
Note: See TracBrowser for help on using the repository browser.