4 #include <stdint.h> /* for uint16_t, uint32_t */
5 #include <ncurses.h> /* for typedefs WINDOW, chtype, wresize(), getmaxx(), */
6 /* getmaxy(), supbad(), delwin(), mvwaddch(), */
7 /* mvwaddstr(), newpad(), wnoutrefres(), erase(), */
8 /* werase(), pnoutrefresh(), doupdate() */
9 #include <stdlib.h> /* for malloc(), free() */
10 #include <string.h> /* for strlen(), strnlen(), memcpy() */
11 #include "yx_uint16.h" /* for yx_uint16 coordinates */
15 /* Fit virtual screen's width to minimum width demanded by current windows'
18 static void refit_pad(struct WinMeta * wmeta);
22 /* Update geometry (sizes, positions) of window "w" and its successors in the
23 * window chain. For the positioning algorithm place_win() is used.
25 static void update_wins(struct WinMeta * wmeta, struct Win * w);
26 static void place_win(struct WinMeta * wmeta, struct Win * w);
30 /* Destroy window "w"'s ncurses window (and set w.Frame.curses_win to 0). */
31 static void destroy_win(struct Win * w);
35 /* Draw contents of all windows in window chain from window "w" onwards. */
36 static void draw_wins(struct Win * w);
40 /* draw_win_borderlines() draws the vertical and horizontal borders of window
41 * "w" sans corners into the virtual screen "pad", and draws the top border
42 * line as the windows' title bar (highlighted if the window is described
43 * active by "active" being set). draw_wins_borderlines().
45 * draw_wins_borderlines() calls draw_win_borderlines() recursively on all
46 * windows from "w" on. "w_active" is a pointer to the one window that
47 * draw_win_borderlines() is supposed to handle as the active window.
49 * Finally, draw_wins_bordercorners draws into "pad" the borders of window "w"
50 * and all its successors.
52 static void draw_win_borderlines(struct Win * w, char active, WINDOW * pad);
53 static void draw_wins_borderlines(struct Win * w, struct Win * w_active,
55 static void draw_wins_bordercorners(struct Win * w, WINDOW * pad);
59 static void refit_pad(struct WinMeta * wmeta)
61 /* Determine rightmost window column. */
62 uint16_t lastwincol = 0;
63 struct Win * w_p = wmeta->chain_start;
66 if (w_p->start.x + w_p->frame.size.x > lastwincol + 1)
68 lastwincol = w_p->start.x + w_p->frame.size.x - 1;
73 /* Only resize the pad if the rightmost window column has changed. */
74 if (getmaxx(wmeta->padframe.curses_win) != lastwincol)
76 wresize(wmeta->padframe.curses_win,
77 getmaxy(wmeta->padframe.curses_win), lastwincol + 2);
83 static void update_wins(struct WinMeta * wmeta, struct Win * w)
85 if (0 != w->frame.curses_win)
91 w->frame.curses_win = subpad(wmeta->padframe.curses_win,
92 w->frame.size.y, w->frame.size.x,
93 w->start.y, w->start.x);
96 update_wins(wmeta, w->next);
102 static void place_win(struct WinMeta * wmeta, struct Win * w)
104 /* First window goes into the upper-left corner. */
106 w->start.y = 1; /* Leave space for title bar. */
110 /* Non-first window fallbacks to: fit rightwards of rightmost border. */
111 struct Win * w_top = w->prev;
112 while (w_top->start.y != 1)
116 w->start.x = w_top->start.x + w_top->frame.size.x + 1;
118 /* Fit window below its predecessor if that one directly thrones over
119 * empty space wide and high enough.
121 uint16_t w_prev_maxy = w->prev->start.y
122 + getmaxy(w->prev->frame.curses_win);
123 if ( w->frame.size.x <= w->prev->frame.size.x
124 && w->frame.size.y < wmeta->padframe.size.y - w_prev_maxy)
126 w->start.x = w->prev->start.x;
127 w->start.y = w_prev_maxy + 1;
130 /* Failing that, try to open a new sub column below the nearest
131 * predecessor window that thrones over enough empty space.
135 struct Win * w_up = w->prev;
136 struct Win * w_upup = w_up;
138 while (w_up != w_top)
143 if (w_up->start.y != w_upup->start.y)
147 w_upup = w_upup->prev;
149 w_prev_maxy = w_upup->start.y
150 + getmaxy(w_upup->frame.curses_win);
151 widthdiff = (w_upup->start.x + w_upup->frame.size.x)
152 - (w_up->start.x + w_up->frame.size.x);
153 if ( w->frame.size.y < wmeta->padframe.size.y - w_prev_maxy
154 && w->frame.size.x < widthdiff)
156 w->start.x = w_up->start.x + w_up->frame.size.x + 1 ;
157 w->start.y = w_prev_maxy + 1;
168 static void destroy_win (struct Win * w)
170 delwin(w->frame.curses_win);
171 w->frame.curses_win = 0;
176 static void draw_wins (struct Win * w)
187 static void draw_win_borderlines(struct Win * w, char active, WINDOW * pad)
189 /* Draw vertical and horizontal border lines. */
191 for (y = w->start.y; y <= w->start.y + w->frame.size.y; y++)
193 mvwaddch(pad, y, w->start.x - 1, '|');
194 mvwaddch(pad, y, w->start.x + w->frame.size.x, '|');
196 for (x = w->start.x; x <= w->start.x + w->frame.size.x; x++)
198 mvwaddch(pad, w->start.y - 1, x, '-');
199 mvwaddch(pad, w->start.y + w->frame.size.y, x, '-');
202 /* Draw as much as possible of the title into center of top border line. */
203 char min_title_length_visible = 3; /* min. 1 char + 2 padding/decoration */
204 if (w->frame.size.x >= min_title_length_visible)
206 uint16_t title_offset = 0;
207 if (w->frame.size.x > strlen(w->title) + 2)
209 title_offset = (w->frame.size.x - (strlen(w->title) + 2)) / 2;
210 } /* +2 is for padding/decoration */
211 uint16_t length_visible = strnlen(w->title, w->frame.size.x - 2);
212 char title[length_visible + 3];
213 char decoration = ' ';
218 memcpy(title + 1, w->title, length_visible);
219 title[0] = title[length_visible + 1] = decoration;
220 title[length_visible + 2] = '\0';
221 mvwaddstr(pad, w->start.y - 1, w->start.x + title_offset, title);
227 static void draw_wins_borderlines(struct Win * w, struct Win * w_active,
235 draw_win_borderlines(w, active, pad);
238 draw_wins_borderlines(w->next, w_active, pad);
244 static void draw_wins_bordercorners(struct Win * w, WINDOW * pad)
246 mvwaddch(pad, w->start.y - 1, w->start.x - 1, '+');
247 mvwaddch(pad, w->start.y - 1, w->start.x + w->frame.size.x, '+');
248 mvwaddch(pad, w->start.y + w->frame.size.y, w->start.x - 1, '+');
250 w->start.y + w->frame.size.y, w->start.x + w->frame.size.x, '+');
253 draw_wins_bordercorners(w->next, pad);
259 extern struct WinMeta init_win_meta(WINDOW * screen)
261 struct WinMeta wmeta;
262 wmeta.screen = screen;
263 wmeta.padframe.size.y = getmaxy(screen);
264 wmeta.padframe.size.x = getmaxx(screen);
265 wmeta.chain_start = 0;
267 wmeta.pad_offset = 0;
268 wmeta.padframe.curses_win = newpad(wmeta.padframe.size.y, 1);
275 extern struct Win init_win(struct WinMeta * wmeta, char * title,
276 uint16_t height, uint16_t width,
277 void * data, void * func)
282 w.frame.curses_win = 0;
288 w.frame.size.x = width;
294 if (height > 0 && height <= wmeta->padframe.size.y - 1)
296 w.frame.size.y = height;
300 w.frame.size.y = wmeta->padframe.size.y - 1;
307 extern void append_win(struct WinMeta * wmeta, struct Win * w)
309 if (0 != wmeta->chain_start)
311 w->prev = wmeta->chain_end;
312 wmeta->chain_end->next = w;
317 wmeta->chain_start = w;
319 wmeta->chain_end = w;
320 update_wins(wmeta, w);
325 extern void suspend_win(struct WinMeta * wmeta, struct Win * w)
329 if (wmeta->chain_start != w)
331 w->prev->next = w->next;
335 wmeta->chain_start = w->next;
337 char pad_refitted = 0;
338 if (wmeta->chain_end != w)
340 w->next->prev = w->prev;
341 if (wmeta->active == w)
343 wmeta->active = w->next;
345 update_wins(wmeta, w->next); /* Positioning of successor windows may */
346 pad_refitted = 1; /* be affected / need correction. Note */
347 } /* that update_wins() already refits the */
348 else /* pad, voiding later need for that. */
350 wmeta->chain_end = w->prev;
351 if (wmeta->active == w)
353 wmeta->active = w->prev;
360 if (0 == pad_refitted)
368 extern void reset_pad_offset(struct WinMeta * wmeta, uint16_t new_offset)
371 && (new_offset < wmeta->pad_offset
372 || new_offset + wmeta->padframe.size.x
373 < getmaxx(wmeta->padframe.curses_win)))
375 wmeta->pad_offset = new_offset;
381 extern void resize_active_win(struct WinMeta * wmeta, struct yx_uint16 size)
383 if (0 != wmeta->active
384 && size.x > 0 && size.y > 0
385 && size.y < wmeta->padframe.size.y)
387 wmeta->active->frame.size = size;
388 update_wins(wmeta, wmeta->chain_start); /* Positioning of successor */
389 } /* windows may be affected. */
394 extern void cycle_active_win(struct WinMeta * wmeta, char dir)
396 if (0 != wmeta->active)
400 if (wmeta->active->next != 0)
402 wmeta->active = wmeta->active->next;
406 wmeta->active = wmeta->chain_start;
411 if (wmeta->active->prev != 0)
413 wmeta->active = wmeta->active->prev;
417 wmeta->active = wmeta->chain_end;
425 extern void shift_active_win(struct WinMeta * wmeta, char dir)
427 if (0 != wmeta->active /* No shifting with less */
428 && wmeta->chain_start != wmeta->chain_end /* than one window visible. */
429 && (dir == 'f' || dir == 'b'))
431 struct Win * w_shift = wmeta->active, * w_p, * w_p_next;
433 /* Check if shifting will lead to wrapping. */
435 if ( (dir == 'f' && w_shift == wmeta->chain_end)
436 || (dir == 'b' && w_shift == wmeta->chain_start))
441 /* Suspend all visible windows, remember their order in wins[]. */
443 for (w_p = wmeta->chain_start, i_max = 1;
444 w_p != wmeta->chain_end;
449 struct Win ** wins = malloc(i_max * sizeof(struct Win *));
450 for (i = 0, w_p = wmeta->chain_start;
454 w_p_next = w_p->next;
455 suspend_win(wmeta, w_p);
460 /* Re-append all previously visible windows in the new order. */
465 append_win(wmeta, w_shift);
466 for (i = 0; i < i_max - 1; i++)
468 append_win(wmeta, wins[i]);
473 for (i = 1; i < i_max; i++)
475 append_win(wmeta, wins[i]);
477 append_win(wmeta, w_shift);
482 for (i = 0; i < i_max; i++)
484 if ( (dir == 'f' && w_shift == wins[i])
485 || (dir == 'b' && w_shift == wins[i+1]))
487 append_win(wmeta, wins[i+1]);
488 append_win(wmeta, wins[i]);
493 append_win(wmeta, wins[i]);
499 wmeta->active = w_shift; /* Otherwise lastly appended win is active. */
505 extern void draw_all_wins(struct WinMeta * wmeta)
507 /* Empty everything before filling it a-new. */
509 wnoutrefresh(wmeta->screen);
510 werase(wmeta->padframe.curses_win);
511 if (wmeta->chain_start)
514 /* Draw windows' contents first, then their borders. */
515 draw_wins(wmeta->chain_start);
516 draw_wins_borderlines(wmeta->chain_start, wmeta->active,
517 wmeta->padframe.curses_win);
518 draw_wins_bordercorners(wmeta->chain_start, wmeta->padframe.curses_win);
520 /* Draw virtual screen scroll hints. */
521 if (wmeta->pad_offset > 0)
523 draw_scroll_hint(&wmeta->padframe,
524 wmeta->pad_offset, wmeta->pad_offset + 1, '<');
526 if (wmeta->pad_offset + wmeta->padframe.size.x
527 < getmaxx(wmeta->padframe.curses_win) - 1)
529 draw_scroll_hint(&wmeta->padframe,
530 wmeta->pad_offset + wmeta->padframe.size.x - 1,
531 getmaxx(wmeta->padframe.curses_win)
532 - (wmeta->pad_offset + wmeta->padframe.size.x),
536 /* Write virtual screen segment to be shown on physical screen into */
537 /* ncurses screen buffer. */
538 pnoutrefresh(wmeta->padframe.curses_win, 0, wmeta->pad_offset, 0, 0,
539 wmeta->padframe.size.y, wmeta->padframe.size.x-1);
542 /* Only at the end write accumulated changes to the physical screen. */
548 extern void draw_scroll_hint(struct Frame * frame, uint16_t pos, uint32_t dist,
551 /* Decide on alignment (vertical/horizontal?), thereby scroll hint text. */
552 char * more = "more";
553 char * unit_cols = "columns";
554 char * unit_rows = "lines";
555 uint16_t dsc_space = frame->size.x;
556 char * unit = unit_rows;
557 if ('<' == dir || '>' == dir)
559 dsc_space = frame->size.y;
562 char * scrolldsc = malloc((4 * sizeof(char)) + strlen(more) + strlen(unit)
563 + 10); /* 10 = uint32 max strlen */
564 sprintf(scrolldsc, " %d %s %s ", dist, more, unit);
566 /* Decide on offset of the description text inside the scroll hint line. */
568 if (dsc_space > strlen(scrolldsc) + 1)
570 offset = (dsc_space - strlen(scrolldsc)) / 2;
573 /* Draw scroll hint line as dir symbols bracketing description text. */
575 for (q = 0; q < dsc_space; q++)
577 if (q >= offset && q < strlen(scrolldsc) + offset)
579 symbol = scrolldsc[q - offset] | A_REVERSE;
583 symbol = dir | A_REVERSE;
585 if ('<' == dir || '>' == dir)
587 mvwaddch(frame->curses_win, q, pos, symbol);
591 mvwaddch(frame->curses_win, pos, q, symbol);