home · contact · privacy
draw_all_windows() only starts drawing cycle if any window is actually visible.
[plomrogue] / windows.c
1 #include <stdlib.h>
2 #include <ncurses.h>
3 #include <string.h>
4 #include "windows.h"
5
6 struct WinMeta init_win_meta (WINDOW * screen) {
7 // Create and populate WinMeta struct with sane default values.
8   struct WinMeta win_meta;
9   win_meta.height = screen->_maxy + 1;
10   win_meta.chain_start = 0;
11   win_meta.chain_end = 0;
12   return win_meta; }
13
14 struct Win init_window (struct WinMeta * win_meta, char * title) {
15 // Create and populate Win struct with sane default values.
16   struct Win win;
17   win.prev = 0;
18   win.next = 0;
19   win.curses_win = 0;
20   win.title = title;
21   win.width = 20;
22   win.height = win_meta->height;
23   return win; }
24
25 void append_window (struct WinMeta * win_meta, struct Win * win) {
26 // Append win to window chain. Set active, if first window. Update geometry of windows from new window on.
27   if (0 != win_meta->chain_start) {
28     win->prev = win_meta->chain_end;
29     win_meta->chain_end->next = win; }
30   else {
31     win_meta->active = win;
32     win_meta->chain_start = win; }
33   win_meta->chain_end = win;
34   update_windows(win_meta, win);
35   draw_all_windows(win_meta); }
36
37 void suspend_window (struct WinMeta * win_meta, struct Win * win) {
38 // Destroy win, suspend from window chain. Update geometry of following rows, as well as activity selection.
39   destroy_window(win);
40   if (win_meta->chain_start != win) // Give win's position in the chain to element next to it in the chain.
41     win->prev->next = win->next;
42   else
43     win_meta->chain_start = win->next;
44   if (win_meta->chain_end != win) { // Let chain element next to win know its new predecessor.
45     win->next->prev = win->prev;
46     if (win_meta->active == win)    // If win was active, shift active window pointer to ...
47       win_meta->active = win->next;                     // ... the next chain element, if that is a window ...
48     update_windows(win_meta, win->next); }
49   else {
50     win_meta->chain_end = win->prev;
51     if (win_meta->active == win)                                   // ... or else to the previous element.
52       win_meta->active = win->prev; }
53   win->prev = 0;
54   win->next = 0;
55   if (0 != win_meta->chain_start)
56     draw_all_windows(win_meta); }
57
58 void place_window (struct WinMeta * win_meta, struct Win * win) {
59 // Based on position and sizes of previous window, find fitting place for current window.
60   win->start_x = 0; // if window is first in chain, place it on top-left corner
61   win->start_y = 0;
62   if (0 != win->prev) {
63     win->start_x = win->prev->start_x + win->prev->width; // next best default: open new window column with it
64     if (win->prev->height < win_meta->height) { // ... unless the previous window does not fill a whole column
65       struct Win * last_ceiling;
66       last_ceiling = win->prev;
67       while (last_ceiling->start_y != 0                               // determine last window serving as a
68              && (last_ceiling->prev->start_y == last_ceiling->start_y // ceiling to other windows or filling
69                  || last_ceiling->prev->width > last_ceiling->width)) // the whole last column's width
70          last_ceiling = last_ceiling->prev;
71       if (win->prev == last_ceiling) {
72         if (win->width <= win->prev->width
73             && win->prev->start_y + win->prev->height + win->height <= win_meta->height) {
74           win->start_x = win->prev->start_x;                         // if prev window is last ceiling, try to
75           win->start_y = win->prev->start_y + win->prev->height; } } // fit window below it; else: use default
76       else {
77         int remaining_width = last_ceiling->width; // calculate free width remaining in last row of last
78         struct Win * win_p = last_ceiling->next;   // window column
79         while (win != win_p) {
80           remaining_width = remaining_width - win_p->width;
81           win_p = win_p->next; }
82         if (win->width <= remaining_width && win->height <= win->prev->height) { // if enough space left in
83           win->start_y = win->prev->start_y;                                     // last column, place window
84           win->start_x = win->prev->start_x + win->prev->width; }                // here
85         else if (win->width <= last_ceiling->width
86                  && win->height + win->prev->height + win->prev->start_y <= win_meta->height ) {
87           win->start_y = last_ceiling->next->start_y + last_ceiling->next->height; // else, try to put it
88           win->start_x = last_ceiling->start_x; }                                  // below
89         else                                                                  // else, put it next to max
90           win->start_x = last_ceiling->width + last_ceiling->start_x; } } } } // width of the last last column
91
92 void update_windows (struct WinMeta * win_meta, struct Win * win) {
93 // Update geometry of win and its next of kin. Before, destroy window, if visible. After, (re-)build it.
94   if (0 != win->curses_win)
95     destroy_window (win);
96   place_window(win_meta, win);
97   if (win->start_y + win->height < win_meta->height) // dependent on window position,
98     win->border_down = 1;                            // append space for borders to be drawn
99   else
100     win->border_down = 0;
101   if (win->start_x > 0)
102     win->border_left = 1;
103   else
104     win->border_left = 0;
105   win->curses_win = newwin(win->height + win->border_down, win->width + win->border_left, win->start_y, win->start_x - win->border_left);
106   if (0 != win->next)
107     update_windows (win_meta, win->next); }
108
109 void destroy_window (struct Win * win) {
110 // Undraw and delete window.
111   undraw_window (win->curses_win);
112   delwin(win->curses_win);
113   win->curses_win = 0; }
114
115 void draw_windows (struct WinMeta * win_meta, struct Win * win) {
116 // Draw all windows from the current one on.
117   draw_window(win_meta, win);
118   if (0 != win->next)
119     draw_windows (win_meta, win->next); }
120
121 void draw_all_windows (struct WinMeta * win_meta) {
122 // Draw all windows from the chain start on.
123   if (win_meta->chain_start)
124     draw_windows (win_meta, win_meta->chain_start); }
125
126 void draw_window(struct WinMeta * win_meta, struct Win * win) {
127 // Draw win's content, including border and title (the latter dependent on space available for it).
128   char ls = '|';
129   char rs = '|';
130   char ts = '-';
131   char bs = '-';
132   char tl = '-';
133   char tr = '+';
134   char bl = '|';
135   char br = '|';
136   if (1 == win->border_down) {
137     bl = '+';
138     br = '+'; }
139   if (1 == win->border_left)
140     tl = '+';
141   wborder(win->curses_win, ls, rs, ts, bs, tl, tr, bl, br);
142   char min_title_length_visible = 3; // 1 char minimal, plus 2 chars for decoration left/right of title
143   if (win->width > min_title_length_visible) {
144     int title_length = strlen(win->title);
145     int title_offset = (((win->width) - (title_length + 2)) / 2) + win->border_left; // + 2 is for decoration
146     if (title_offset < win->border_left)
147       title_offset = win->border_left;
148     int length_visible = strnlen(win->title, win->width - min_title_length_visible);
149     char title[length_visible + 3];
150     char decoration = ' ';
151     if (win_meta->active == win)
152       decoration = '$';
153     memcpy(title + 1, win->title, length_visible);
154     title[0] = title[length_visible + 1] = decoration;
155     title[length_visible + 2] = '\0';
156     mvwaddstr(win->curses_win, 0, title_offset, title); }
157   if (win->height > 1 && win->width > 1) ;
158     win->draw(win);
159   wrefresh(win->curses_win); }
160
161 void undraw_window (WINDOW * win) {
162 // Fill entire window with whitespace.
163   int y, x;
164   for (y = 0; y <= win->_maxy; y++)
165     for (x = 0; x <= win->_maxx; x++)
166       mvwaddch(win, y, x, ' ');
167   wrefresh(win); }
168
169 void resize_window (struct WinMeta * win_meta, char change) {
170 // Grow or shrink currently active window. Correct its geometry and that of its followers.
171   if      (change == '-' && win_meta->active->height > 2)
172       win_meta->active->height--;
173   else if (change == '+' && win_meta->active->height < win_meta->height)
174     win_meta->active->height++;
175   else if (change == '_' && win_meta->active->width > 2)
176       win_meta->active->width--;
177   else if (change == '*')
178     win_meta->active->width++;
179   update_windows(win_meta, win_meta->chain_start);
180   draw_all_windows(win_meta); }
181
182 void cycle_active_window (struct WinMeta * win_meta, char dir) {
183 // Cycle active window selection forwards (dir = 'n') or backwards.
184   if ('n' == dir) {
185     if (win_meta->active->next != 0)
186       win_meta->active = win_meta->active->next;
187     else
188       win_meta->active = win_meta->chain_start; }
189   else {
190     if (win_meta->active->prev != 0)
191       win_meta->active = win_meta->active->prev;
192     else
193       win_meta->active = win_meta->chain_end; }
194   draw_all_windows(win_meta); }
195
196 void shift_window (struct WinMeta * win_meta, char dir) {
197 // Move active window forward/backward in window chain. If jumping beyond start/end, move to other chain end.
198   if (win_meta->active != win_meta->chain_start || win_meta->active != win_meta->chain_end) {
199     if ('f' == dir) {
200       if (win_meta->active == win_meta->chain_end) { // move forward beyond chain end
201         win_meta->active->prev->next = 0;
202         win_meta->chain_end = win_meta->active->prev;
203         win_meta->active->prev = 0;
204         win_meta->active->next = win_meta->chain_start;
205         win_meta->chain_start->prev = win_meta->active;
206         win_meta->chain_start = win_meta->active; }
207       else {                                        // move forward before chain end
208         if (win_meta->chain_start != win_meta->active)
209           win_meta->active->prev->next = win_meta->active->next;
210         else
211           win_meta->chain_start = win_meta->active->next;
212         win_meta->active->next->prev = win_meta->active->prev;
213         win_meta->active->prev = win_meta->active->next;
214         win_meta->active->next = win_meta->active->next->next;
215         win_meta->active->prev->next = win_meta->active;
216         if (0 != win_meta->active->next)
217           win_meta->active->next->prev = win_meta->active;
218         else
219           win_meta->chain_end = win_meta->active; } }
220     else { // mirror of above, backwards
221       if (win_meta->active == win_meta->chain_start) {
222         win_meta->active->next->prev = 0;
223         win_meta->chain_start = win_meta->active->next;
224         win_meta->active->next = 0;
225         win_meta->active->prev = win_meta->chain_end;
226         win_meta->chain_end->next = win_meta->active;
227         win_meta->chain_end = win_meta->active; }
228       else {
229         if (win_meta->chain_end != win_meta->active)
230           win_meta->active->next->prev = win_meta->active->prev;
231         else
232           win_meta->chain_end = win_meta->active->prev;
233         win_meta->active->prev->next = win_meta->active->next;
234         win_meta->active->next = win_meta->active->prev;
235         win_meta->active->prev = win_meta->active->prev->prev;
236         win_meta->active->next->prev = win_meta->active;
237         if (0 != win_meta->active->prev)
238           win_meta->active->prev->next = win_meta->active;
239         else
240           win_meta->chain_start = win_meta->active; } }
241   update_windows(win_meta, win_meta->chain_start);
242   draw_all_windows(win_meta); } }