ncurses 6.0 - patch 20151017
[ncurses.git] / c++ / cursesw.cc
1 // * this is for making emacs happy: -*-Mode: C++;-*-
2 /****************************************************************************
3  * Copyright (c) 2007-2012,2014 Free Software Foundation, Inc.              *
4  *                                                                          *
5  * Permission is hereby granted, free of charge, to any person obtaining a  *
6  * copy of this software and associated documentation files (the            *
7  * "Software"), to deal in the Software without restriction, including      *
8  * without limitation the rights to use, copy, modify, merge, publish,      *
9  * distribute, distribute with modifications, sublicense, and/or sell       *
10  * copies of the Software, and to permit persons to whom the Software is    *
11  * furnished to do so, subject to the following conditions:                 *
12  *                                                                          *
13  * The above copyright notice and this permission notice shall be included  *
14  * in all copies or substantial portions of the Software.                   *
15  *                                                                          *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS  *
17  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF               *
18  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.   *
19  * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,   *
20  * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR    *
21  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR    *
22  * THE USE OR OTHER DEALINGS IN THE SOFTWARE.                               *
23  *                                                                          *
24  * Except as contained in this notice, the name(s) of the above copyright   *
25  * holders shall not be used in advertising or otherwise to promote the     *
26  * sale, use or other dealings in this Software without prior written       *
27  * authorization.                                                           *
28  ****************************************************************************/
29
30 /*
31  * Authors:
32  *      Thomas E. Dickey
33  *      Juergen Pfeifer
34  *
35  * The NCursesWindow class was originally based on a file written by
36  * Eric Newton, later modified by Ulrich Drepper and Anatoly Ivasyuk.
37  * However, aside from the compatible interface definition, no trace
38  * of the original code remains in this version: it consists only of
39  * changes introduced since 1995.
40  */
41
42 #include "internal.h"
43 #include "cursesw.h"
44
45 MODULE_ID("$Id: cursesw.cc,v 1.54 2014/02/01 22:10:42 tom Exp $")
46
47 #define COLORS_NEED_INITIALIZATION  -1
48 #define COLORS_NOT_INITIALIZED       0
49 #define COLORS_MONOCHROME            1
50 #define COLORS_ARE_REALLY_THERE      2
51
52 #define HaveColors() (colorInitialized == COLORS_ARE_REALLY_THERE)
53
54 // declare static variables for the class
55 long NCursesWindow::count = 0L;
56 bool NCursesWindow::b_initialized = FALSE;
57
58 int
59 NCursesWindow::scanw(const char* fmt, ...)
60 {
61     int result = ERR;
62
63     va_list args;
64     va_start(args, fmt);
65     result = ::vw_scanw (w, const_cast<NCURSES_CONST char *>(fmt), args);
66     va_end(args);
67
68     return result;
69 }
70
71
72 int
73 NCursesWindow::scanw(int y, int x, const char* fmt, ...)
74 {
75     int result = ERR;
76
77     if (::wmove(w, y, x) != ERR) {
78         va_list args;
79         va_start(args, fmt);
80         result = ::vw_scanw (w, const_cast<NCURSES_CONST char *>(fmt), args);
81         va_end(args);
82     }
83     return result;
84 }
85
86
87 int
88 NCursesWindow::scanw(const char* fmt, va_list args)
89 {
90     int result = ERR;
91
92     result = ::vw_scanw (w, const_cast<NCURSES_CONST char *>(fmt), args);
93
94     return result;
95 }
96
97
98 int
99 NCursesWindow::scanw(int y, int x, const char* fmt, va_list args)
100 {
101     int result = ERR;
102
103     if (::wmove(w, y, x) != ERR) {
104         result = ::vw_scanw (w, const_cast<NCURSES_CONST char *>(fmt), args);
105     }
106     return result;
107 }
108
109
110 int
111 NCursesWindow::printw(const char * fmt, ...)
112 {
113     va_list args;
114     va_start(args, fmt);
115     int result = ::vw_printw(w, fmt, args);
116     va_end(args);
117     return result;
118 }
119
120
121 int
122 NCursesWindow::printw(int y, int x, const char * fmt, ...)
123 {
124     va_list args;
125     va_start(args, fmt);
126     int result = ::wmove(w, y, x);
127     if (result == OK) {
128         result = ::vw_printw(w, fmt, args);
129     }
130     va_end(args);
131     return result;
132 }
133
134
135 int
136 NCursesWindow::printw(const char * fmt, va_list args)
137 {
138     int result = ::vw_printw(w, fmt, args);
139     return result;
140 }
141
142
143 int
144 NCursesWindow::printw(int y, int x, const char * fmt, va_list args)
145 {
146     int result = ::wmove(w, y, x);
147     if (result == OK) {
148         result = ::vw_printw(w, fmt, args);
149     }
150     return result;
151 }
152
153
154 void
155 NCursesWindow::set_keyboard(void)
156 {
157     keypad(TRUE);
158     meta(TRUE);
159 }
160
161 void
162 NCursesWindow::err_handler(const char *msg) const THROWS(NCursesException)
163 {
164   THROW(new NCursesException(msg));
165 }
166
167 void
168 NCursesWindow::initialize()
169 {
170     if (!b_initialized) {
171         ::initscr();
172         b_initialized = TRUE;
173         if (colorInitialized == COLORS_NEED_INITIALIZATION) {
174             colorInitialized = COLORS_NOT_INITIALIZED;
175             useColors();
176         }
177         ::noecho();
178         ::cbreak();
179     }
180 }
181
182 void
183 NCursesWindow::constructing()
184 {
185     initialize();
186     ++count;
187 }
188
189 NCursesWindow::NCursesWindow()
190   : w(0), alloced(FALSE), par(0), subwins(0), sib(0)
191 {
192     constructing();
193
194     w = static_cast<WINDOW *>(0);
195 }
196
197 NCursesWindow::NCursesWindow(int nlines, int ncols, int begin_y, int begin_x)
198   : w(0), alloced(TRUE), par(0), subwins(0), sib(0)
199 {
200     constructing();
201
202     w = ::newwin(nlines, ncols, begin_y, begin_x);
203     if (w == 0) {
204         err_handler("Cannot construct window");
205     }
206     set_keyboard();
207 }
208
209 NCursesWindow::NCursesWindow(WINDOW* window)
210   : w(0), alloced(FALSE), par(0), subwins(0), sib(0)
211 {
212     constructing();
213
214     // We used to use a reference on the "window" parameter, but we cannot do
215     // that with an opaque pointer (see NCURSES_OPAQUE).  If the parameter was
216     // "::stdscr", that is first set via the "constructing() call, and is null
217     // up to that point.  So we allow a null pointer here as meaning the "same"
218     // as "::stdscr".
219     w = window ? window : ::stdscr;
220     set_keyboard();
221 }
222
223 NCursesWindow::NCursesWindow(NCursesWindow& win, int ny, int nx,
224                              int begin_y, int begin_x, char absrel)
225   : w(0), alloced(TRUE), par(0), subwins(0), sib(0)
226 {
227     constructing();
228     if (absrel == 'a') {        // absolute origin
229         begin_y -= win.begy();
230         begin_x -= win.begx();
231     }
232
233     // Link this window into its parent's list of subwindows.
234     // We use derwin(), since this also works for pads.
235     w = ::derwin(win.w, ny, nx, begin_y, begin_x);
236     if (w == 0) {
237         err_handler("Cannot construct subwindow");
238     }
239
240     par = &win;
241     sib = win.subwins;
242     win.subwins = this;
243 }
244
245 NCursesWindow::NCursesWindow(NCursesWindow& win,
246                                 bool do_box NCURSES_PARAM_INIT(TRUE))
247   : w(0), alloced(TRUE), par(0), subwins(0), sib(0)
248 {
249     constructing();
250     int myHeight = win.height();
251     int myWidth  = win.width();
252     w = :: derwin(win.w, myHeight - 2, myWidth - 2, 1, 1);
253     if (w == 0) {
254         err_handler("Cannot construct subwindow");
255     }
256
257     par = &win;
258     sib = win.subwins;
259     win.subwins = this;
260     subwins = 0;
261
262     if (do_box) {
263         win.box();
264         win.touchwin();
265     }
266 }
267
268 NCursesWindow NCursesWindow::Clone()
269 {
270     WINDOW *d = ::dupwin(w);
271     NCursesWindow W(d);
272     W.subwins = subwins;
273     W.sib = sib;
274     W.par = par;
275     W.alloced = alloced;
276     return W;
277 }
278
279 typedef int (*RIPOFFINIT)(NCursesWindow&);
280 static RIPOFFINIT R_INIT[5];       // There can't be more
281 static int r_init_idx   = 0;
282 static RIPOFFINIT* prip = R_INIT;
283
284 NCursesWindow::NCursesWindow(WINDOW *win, int ncols)
285   : w(0), alloced(FALSE), par(0), subwins(0), sib(0)
286 {
287     (void) ncols;
288     initialize();
289     w = win;
290 }
291
292 int _nc_xx_ripoff_init(WINDOW *w, int ncols)
293 {
294     (void) ncols;
295     int res = ERR;
296
297     RIPOFFINIT init = *prip++;
298     if (init) {
299         res = init(*(new NCursesWindow(w,ncols)));
300     }
301     return res;
302 }
303
304 int NCursesWindow::ripoffline(int ripoff_lines,
305                               int (*init)(NCursesWindow& win))
306 {
307     int code = ::_nc_ripoffline(ripoff_lines,_nc_xx_ripoff_init);
308     if (code == OK && init && ripoff_lines) {
309         R_INIT[r_init_idx++] = init;
310     }
311     return code;
312 }
313
314 bool
315 NCursesWindow::isDescendant(NCursesWindow& win)
316 {
317     bool result = FALSE;
318
319     for (NCursesWindow* p = subwins; p != NULL; p = p->sib) {
320         if (p == &win || p->isDescendant(win)) {
321             result = TRUE;
322             break;
323         }
324     }
325     return result;
326 }
327
328 void
329 NCursesWindow::kill_subwindows()
330 {
331     NCursesWindow* p = subwins;
332
333     subwins = 0;
334     while (p != 0) {
335         NCursesWindow* q = p->sib;
336         p->kill_subwindows();
337         if (p->alloced) {
338             if (p->w != 0)
339                 ::delwin(p->w);
340         }
341         delete p;
342         p = q;
343     }
344 }
345
346
347 NCursesWindow::~NCursesWindow()
348 {
349     kill_subwindows();
350
351     if (par != 0) {
352         // Remove this window from the parent's list of subwindows.
353         NCursesWindow * next = par->subwins;
354         NCursesWindow * prev = 0;
355         while (next != 0) {
356             if (next == this) {
357                 if (prev != 0) {
358                     prev->sib = next->sib;
359                 } else {
360                     par->subwins = next->sib;
361                 }
362                 break;
363             }
364             prev = next;
365             next = next->sib;
366         }
367     }
368
369     if (alloced && w != 0)
370         ::delwin(w);
371
372     if (alloced) {
373         --count;
374         if (count == 0) {
375             ::endwin();
376         } else if (count < 0) { // cannot happen!
377             err_handler("Too many windows destroyed");
378         }
379     }
380 }
381
382 // ---------------------------------------------------------------------
383 // Color stuff
384 //
385 int NCursesWindow::colorInitialized = COLORS_NOT_INITIALIZED;
386
387 void
388 NCursesWindow::useColors(void)
389 {
390     if (colorInitialized == COLORS_NOT_INITIALIZED) {
391         if (b_initialized) {
392             if (::has_colors()) {
393                 ::start_color();
394                 colorInitialized = COLORS_ARE_REALLY_THERE;
395             } else {
396                 colorInitialized = COLORS_MONOCHROME;
397             }
398         } else {
399             colorInitialized = COLORS_NEED_INITIALIZATION;
400         }
401     }
402 }
403
404 NCURSES_PAIRS_T
405 NCursesWindow::getPair() const
406 {
407     return static_cast<NCURSES_PAIRS_T>(PAIR_NUMBER(getattrs(w)));
408 }
409
410 NCURSES_COLOR_T
411 NCursesWindow::getcolor(int getback) const
412 {
413     NCURSES_COLOR_T fore, back;
414
415     if (HaveColors()) {
416         if (::pair_content(getPair(), &fore, &back) == ERR)
417             err_handler("Can't get color pair");
418     } else {
419         // Monochrome means white on black
420         back = COLOR_BLACK;
421         fore = COLOR_WHITE;
422     }
423     return getback ? back : fore;
424 }
425
426 int NCursesWindow::NumberOfColors()
427 {
428     return (HaveColors()) ? COLORS : 1;
429 }
430
431 NCURSES_PAIRS_T
432 NCursesWindow::getcolor() const
433 {
434     return (HaveColors()) ? getPair() : 0;
435 }
436
437 int
438 NCursesWindow::setpalette(NCURSES_COLOR_T fore, NCURSES_COLOR_T back, NCURSES_PAIRS_T pair)
439 {
440     return (HaveColors()) ? ::init_pair(pair, fore, back) : OK;
441 }
442
443 int
444 NCursesWindow::setpalette(NCURSES_COLOR_T fore, NCURSES_COLOR_T back)
445 {
446     return setpalette(fore, back, getPair());
447 }
448
449
450 int
451 NCursesWindow::setcolor(NCURSES_PAIRS_T pair)
452 {
453     if (HaveColors()) {
454         if ((pair < 1) || (pair > COLOR_PAIRS))
455             err_handler("Can't set color pair");
456
457         attroff(A_COLOR);
458         attrset(COLOR_PAIR(pair));
459     }
460     return OK;
461 }
462
463 #if HAVE_HAS_KEY
464 bool NCursesWindow::has_mouse() const
465 {
466     return ((::has_key(KEY_MOUSE) || ::has_mouse())
467              ? TRUE : FALSE);
468 }
469 #endif