ncurses 5.6 - patch 20070612
[ncurses.git] / c++ / cursesw.cc
1 // * this is for making emacs happy: -*-Mode: C++;-*-
2 /****************************************************************************
3  * Copyright (c) 2007 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.48 2007/03/24 19:00:58 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::printw(const char * fmt, ...)
89 {
90     va_list args;
91     va_start(args, fmt);
92     int result = ::vw_printw(w, fmt, args);
93     va_end(args);
94     return result;
95 }
96
97
98 int
99 NCursesWindow::printw(int y, int x, const char * fmt, ...)
100 {
101     va_list args;
102     va_start(args, fmt);
103     int result = ::wmove(w, y, x);
104     if (result == OK) {
105         result = ::vw_printw(w, fmt, args);
106     }
107     va_end(args);
108     return result;
109 }
110
111
112 void
113 NCursesWindow::set_keyboard(void)
114 {
115     keypad(TRUE);
116     meta(TRUE);
117 }
118
119 void
120 NCursesWindow::err_handler(const char *msg) const THROWS(NCursesException)
121 {
122   THROW(new NCursesException(msg));
123 }
124
125 void
126 NCursesWindow::initialize()
127 {
128     if (!b_initialized) {
129         ::initscr();
130         b_initialized = TRUE;
131         if (colorInitialized == COLORS_NEED_INITIALIZATION) {
132             colorInitialized = COLORS_NOT_INITIALIZED;
133             useColors();
134         }
135         ::noecho();
136         ::cbreak();
137     }
138 }
139
140 void
141 NCursesWindow::constructing()
142 {
143     initialize();
144     ++count;
145 }
146
147 NCursesWindow::NCursesWindow()
148   : w(0), alloced(FALSE), par(0), subwins(0), sib(0)
149 {
150     constructing();
151
152     w = static_cast<WINDOW *>(0);
153     set_keyboard();
154 }
155
156 NCursesWindow::NCursesWindow(int nlines, int ncols, int begin_y, int begin_x)
157   : w(0), alloced(TRUE), par(0), subwins(0), sib(0)
158 {
159     constructing();
160
161     w = ::newwin(nlines, ncols, begin_y, begin_x);
162     if (w == 0) {
163         err_handler("Cannot construct window");
164     }
165     set_keyboard();
166 }
167
168 NCursesWindow::NCursesWindow(WINDOW* window)
169   : w(0), alloced(FALSE), par(0), subwins(0), sib(0)
170 {
171     constructing();
172
173     // We used to use a reference on the "window" parameter, but we cannot do
174     // that with an opaque pointer (see NCURSES_OPAQUE).  If the parameter was
175     // "::stdscr", that is first set via the "constructing() call, and is null
176     // up to that point.  So we allow a null pointer here as meaning the "same"
177     // as "::stdscr".
178     w = window ? window : ::stdscr;
179     set_keyboard();
180 }
181
182 NCursesWindow::NCursesWindow(NCursesWindow& win, int ny, int nx,
183                              int begin_y, int begin_x, char absrel)
184   : w(0), alloced(TRUE), par(0), subwins(0), sib(0)
185 {
186     constructing();
187     if (absrel == 'a') {        // absolute origin
188         begin_y -= win.begy();
189         begin_x -= win.begx();
190     }
191
192     // Link this window into its parent's list of subwindows.
193     // We use derwin(), since this also works for pads.
194     w = ::derwin(win.w, ny, nx, begin_y, begin_x);
195     if (w == 0) {
196         err_handler("Cannot construct subwindow");
197     }
198
199     par = &win;
200     sib = win.subwins;
201     win.subwins = this;
202 }
203
204 NCursesWindow::NCursesWindow(NCursesWindow& win,
205                                 bool do_box NCURSES_PARAM_INIT(TRUE))
206   : w(0), alloced(TRUE), par(0), subwins(0), sib(0)
207 {
208     constructing();
209     int myHeight = win.height();
210     int myWidth  = win.width();
211     w = :: derwin(win.w, myHeight - 2, myWidth - 2, 1, 1);
212     if (w == 0) {
213         err_handler("Cannot construct subwindow");
214     }
215
216     par = &win;
217     sib = win.subwins;
218     win.subwins = this;
219     subwins = 0;
220
221     if (do_box) {
222         win.box();
223         win.touchwin();
224     }
225 }
226
227 NCursesWindow NCursesWindow::Clone()
228 {
229     WINDOW *d = ::dupwin(w);
230     NCursesWindow W(d);
231     W.subwins = subwins;
232     W.sib = sib;
233     W.par = par;
234     W.alloced = alloced;
235     return W;
236 }
237
238 typedef int (*RIPOFFINIT)(NCursesWindow&);
239 static RIPOFFINIT R_INIT[5];       // There can't be more
240 static int r_init_idx   = 0;
241 static RIPOFFINIT* prip = R_INIT;
242
243 NCursesWindow::NCursesWindow(WINDOW *win, int ncols)
244   : w(0), alloced(FALSE), par(0), subwins(0), sib(0)
245 {
246     initialize();
247     w = win;
248     assert((w->_maxx +1 ) == ncols);
249 }
250
251 int _nc_xx_ripoff_init(WINDOW *w, int ncols)
252 {
253     int res = ERR;
254
255     RIPOFFINIT init = *prip++;
256     if (init) {
257         NCursesWindow* W = new NCursesWindow(w,ncols);
258         res = init(*W);
259     }
260     return res;
261 }
262
263 int NCursesWindow::ripoffline(int ripoff_lines,
264                               int (*init)(NCursesWindow& win))
265 {
266     int code = ::_nc_ripoffline(ripoff_lines,_nc_xx_ripoff_init);
267     if (code == OK && init && ripoff_lines) {
268         R_INIT[r_init_idx++] = init;
269     }
270     return code;
271 }
272
273 bool
274 NCursesWindow::isDescendant(NCursesWindow& win)
275 {
276     bool result = FALSE;
277
278     for (NCursesWindow* p = subwins; p != NULL; p = p->sib) {
279         if (p == &win || p->isDescendant(win)) {
280             result = TRUE;
281             break;
282         }
283     }
284     return result;
285 }
286
287 void
288 NCursesWindow::kill_subwindows()
289 {
290     NCursesWindow* p = subwins;
291
292     subwins = 0;
293     while (p != 0) {
294         NCursesWindow* q = p->sib;
295         p->kill_subwindows();
296         if (p->alloced) {
297             if (p->w != 0)
298                 ::delwin(p->w);
299         }
300         delete p;
301         p = q;
302     }
303 }
304
305
306 NCursesWindow::~NCursesWindow()
307 {
308     kill_subwindows();
309
310     if (par != 0) {
311         // Remove this window from the parent's list of subwindows.
312         NCursesWindow * next = par->subwins;
313         NCursesWindow * prev = 0;
314         while (next != 0) {
315             if (next == this) {
316                 if (prev != 0) {
317                     prev->sib = next->sib;
318                 } else {
319                     par->subwins = next->sib;
320                 }
321                 break;
322             }
323             prev = next;
324             next = next->sib;
325         }
326     }
327
328     if (alloced && w != 0)
329         ::delwin(w);
330
331     if (alloced) {
332         --count;
333         if (count == 0) {
334             ::endwin();
335         } else if (count < 0) { // cannot happen!
336             err_handler("Too many windows destroyed");
337         }
338     }
339 }
340
341 // ---------------------------------------------------------------------
342 // Color stuff
343 //
344 int NCursesWindow::colorInitialized = COLORS_NOT_INITIALIZED;
345
346 void
347 NCursesWindow::useColors(void)
348 {
349     if (colorInitialized == COLORS_NOT_INITIALIZED) {
350         if (b_initialized) {
351             if (::has_colors()) {
352                 ::start_color();
353                 colorInitialized = COLORS_ARE_REALLY_THERE;
354             } else {
355                 colorInitialized = COLORS_MONOCHROME;
356             }
357         } else {
358             colorInitialized = COLORS_NEED_INITIALIZATION;
359         }
360     }
361 }
362
363 short
364 NCursesWindow::getPair() const
365 {
366     return static_cast<short>(PAIR_NUMBER(getattrs(w)));
367 }
368
369 short
370 NCursesWindow::getcolor(int getback) const
371 {
372     short fore, back;
373
374     if (HaveColors()) {
375         if (::pair_content(getPair(), &fore, &back) == ERR)
376             err_handler("Can't get color pair");
377     } else {
378         // Monochrome means white on black
379         back = COLOR_BLACK;
380         fore = COLOR_WHITE;
381     }
382     return getback ? back : fore;
383 }
384
385 int NCursesWindow::NumberOfColors()
386 {
387     return (HaveColors()) ? COLORS : 1;
388 }
389
390 short
391 NCursesWindow::getcolor() const
392 {
393     return (HaveColors()) ? getPair() : 0;
394 }
395
396 int
397 NCursesWindow::setpalette(short fore, short back, short pair)
398 {
399     return (HaveColors()) ? ::init_pair(pair, fore, back) : OK;
400 }
401
402 int
403 NCursesWindow::setpalette(short fore, short back)
404 {
405     return setpalette(fore, back, getPair());
406 }
407
408
409 int
410 NCursesWindow::setcolor(short pair)
411 {
412     if (HaveColors()) {
413         if ((pair < 1) || (pair > COLOR_PAIRS))
414             err_handler("Can't set color pair");
415
416         attroff(A_COLOR);
417         attrset(COLOR_PAIR(pair));
418     }
419     return OK;
420 }
421
422 #if HAVE_HAS_KEY
423 bool NCursesWindow::has_mouse() const
424 {
425     return ((::has_key(KEY_MOUSE) || ::_nc_has_mouse())
426              ? TRUE : FALSE);
427 }
428 #endif