home · contact · privacy
Uploading current state of work.
[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   draw_windows (win_meta, win_meta->chain_start); }
124
125 void draw_window(struct WinMeta * win_meta, struct Win * win) {
126 // Draw win's content, including border and title (the latter dependent on space available for it).
127   char ls = '|';
128   char rs = '|';
129   char ts = '-';
130   char bs = '-';
131   char tl = '-';
132   char tr = '+';
133   char bl = '|';
134   char br = '|';
135   if (1 == win->border_down) {
136     bl = '+';
137     br = '+'; }
138   if (1 == win->border_left)
139     tl = '+';
140   wborder(win->curses_win, ls, rs, ts, bs, tl, tr, bl, br);
141   char min_title_length_visible = 3; // 1 char minimal, plus 2 chars for decoration left/right of title
142   if (win->width > min_title_length_visible) {
143     int title_length = strlen(win->title);
144     int title_offset = (((win->width) - (title_length + 2)) / 2) + win->border_left; // + 2 is for decoration
145     if (title_offset < win->border_left)
146       title_offset = win->border_left;
147     int length_visible = strnlen(win->title, win->width - min_title_length_visible);
148     char title[length_visible + 3];
149     char decoration = ' ';
150     if (win_meta->active == win)
151       decoration = '$';
152     memcpy(title + 1, win->title, length_visible);
153     title[0] = title[length_visible + 1] = decoration;
154     title[length_visible + 2] = '\0';
155     mvwaddstr(win->curses_win, 0, title_offset, title); }
156   if (win->height > 1 && win->width > 1) ;
157     win->draw(win);
158   wrefresh(win->curses_win); }
159
160 void undraw_window (WINDOW * win) {
161 // Fill entire window with whitespace.
162   int y, x;
163   for (y = 0; y <= win->_maxy; y++)
164     for (x = 0; x <= win->_maxx; x++)
165       mvwaddch(win, y, x, ' ');
166   wrefresh(win); }
167
168 void resize_window (struct WinMeta * win_meta, char change) {
169 // Grow or shrink currently active window. Correct its geometry and that of its followers.
170   if      (change == '-' && win_meta->active->height > 2)
171       win_meta->active->height--;
172   else if (change == '+' && win_meta->active->height < win_meta->height)
173     win_meta->active->height++;
174   else if (change == '_' && win_meta->active->width > 2)
175       win_meta->active->width--;
176   else if (change == '*')
177     win_meta->active->width++;
178   update_windows(win_meta, win_meta->chain_start);
179   draw_all_windows(win_meta); }
180
181 void cycle_active_window (struct WinMeta * win_meta, char dir) {
182 // Cycle active window selection forwards (dir = 'n') or backwards.
183   if ('n' == dir) {
184     if (win_meta->active->next != 0)
185       win_meta->active = win_meta->active->next;
186     else
187       win_meta->active = win_meta->chain_start; }
188   else {
189     if (win_meta->active->prev != 0)
190       win_meta->active = win_meta->active->prev;
191     else
192       win_meta->active = win_meta->chain_end; }
193   draw_all_windows(win_meta); }
194
195 void shift_window (struct WinMeta * win_meta, char dir) {
196 // Move active window forward/backward in window chain. If jumping beyond start/end, move to other chain end.
197   if (win_meta->active != win_meta->chain_start || win_meta->active != win_meta->chain_end) {
198     if ('f' == dir) {
199       if (win_meta->active == win_meta->chain_end) { // move forward beyond chain end
200         win_meta->active->prev->next = 0;
201         win_meta->chain_end = win_meta->active->prev;
202         win_meta->active->prev = 0;
203         win_meta->active->next = win_meta->chain_start;
204         win_meta->chain_start->prev = win_meta->active;
205         win_meta->chain_start = win_meta->active; }
206       else {                                        // move forward before chain end
207         if (win_meta->chain_start != win_meta->active)
208           win_meta->active->prev->next = win_meta->active->next;
209         else
210           win_meta->chain_start = win_meta->active->next;
211         win_meta->active->next->prev = win_meta->active->prev;
212         win_meta->active->prev = win_meta->active->next;
213         win_meta->active->next = win_meta->active->next->next;
214         win_meta->active->prev->next = win_meta->active;
215         if (0 != win_meta->active->next)
216           win_meta->active->next->prev = win_meta->active;
217         else
218           win_meta->chain_end = win_meta->active; } }
219     else { // mirror of above, backwards
220       if (win_meta->active == win_meta->chain_start) {
221         win_meta->active->next->prev = 0;
222         win_meta->chain_start = win_meta->active->next;
223         win_meta->active->next = 0;
224         win_meta->active->prev = win_meta->chain_end;
225         win_meta->chain_end->next = win_meta->active;
226         win_meta->chain_end = win_meta->active; }
227       else {
228         if (win_meta->chain_end != win_meta->active)
229           win_meta->active->next->prev = win_meta->active->prev;
230         else
231           win_meta->chain_end = win_meta->active->prev;
232         win_meta->active->prev->next = win_meta->active->next;
233         win_meta->active->next = win_meta->active->prev;
234         win_meta->active->prev = win_meta->active->prev->prev;
235         win_meta->active->next->prev = win_meta->active;
236         if (0 != win_meta->active->prev)
237           win_meta->active->prev->next = win_meta->active;
238         else
239           win_meta->chain_start = win_meta->active; } }
240   update_windows(win_meta, win_meta->chain_start);
241   draw_all_windows(win_meta); } }