source: window.c @ d106110

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