blob: 7d300ff38b4e43015ce6dfef48b7d3ad7b20092e [file] [log] [blame]
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05301// * This makes emacs happy -*-Mode: C++;-*-
2/****************************************************************************
micky3879b9f5e72025-07-08 18:04:53 -04003 * Copyright 2018-2021,2023 Thomas E. Dickey *
4 * Copyright 1998-2012,2017 Free Software Foundation, Inc. *
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +05305 * *
6 * Permission is hereby granted, free of charge, to any person obtaining a *
7 * copy of this software and associated documentation files (the *
8 * "Software"), to deal in the Software without restriction, including *
9 * without limitation the rights to use, copy, modify, merge, publish, *
10 * distribute, distribute with modifications, sublicense, and/or sell *
11 * copies of the Software, and to permit persons to whom the Software is *
12 * furnished to do so, subject to the following conditions: *
13 * *
14 * The above copyright notice and this permission notice shall be included *
15 * in all copies or substantial portions of the Software. *
16 * *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS *
18 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF *
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. *
20 * IN NO EVENT SHALL THE ABOVE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, *
21 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR *
22 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR *
23 * THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
24 * *
25 * Except as contained in this notice, the name(s) of the above copyright *
26 * holders shall not be used in advertising or otherwise to promote the *
27 * sale, use or other dealings in this Software without prior written *
28 * authorization. *
29 ****************************************************************************/
30
31/*
32 * Silly demo program for the NCursesPanel class.
33 *
34 * written by Anatoly Ivasyuk (anatoly@nick.csh.rit.edu)
35 *
36 * Demo code for NCursesMenu and NCursesForm written by
37 * Juergen Pfeifer
38 *
micky3879b9f5e72025-07-08 18:04:53 -040039 * $Id: demo.cc,v 1.52 2023/08/26 19:14:14 tom Exp $
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +053040 */
41
42#include "internal.h"
43#include "cursesapp.h"
44#include "cursesm.h"
45#include "cursesf.h"
46
micky3879b9f5e72025-07-08 18:04:53 -040047#if (defined(_WIN32) || defined(_WIN64))
Steve Kondikae271bc2015-11-15 02:50:53 +010048#undef KEY_EVENT
micky3879b9f5e72025-07-08 18:04:53 -040049#undef sleep
50#define sleep(n) Sleep(n)
51#else
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +053052extern "C" unsigned int sleep(unsigned int);
Steve Kondikae271bc2015-11-15 02:50:53 +010053#endif
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +053054
55#undef index // needed for NeXT
56
57//
58// -------------------------------------------------------------------------
59//
60class SillyDemo
61{
62 public:
63 void run(int sleeptime) {
64
65 NCursesPanel *mystd = new NCursesPanel();
66
67 // Make a few small demo panels
68
69 NCursesPanel *u = new NCursesPanel(8, 20, 12, 4);
70 NCursesPanel *v = new NCursesPanel(8, 20, 10, 6);
71 NCursesPanel *w = new NCursesPanel(8, 20, 8, 8);
72 NCursesPanel *x = new NCursesPanel(8, 20, 6, 10);
73 NCursesPanel *y = new NCursesPanel(8, 20, 4, 12);
74 NCursesPanel *z = new NCursesPanel(8, 30, 2, 14);
75
76 // Draw something on the main screen, so we can see what happens
77 // when panels get moved or deleted.
78
79 mystd->box();
80 mystd->move(mystd->height()/2, 1);
81 mystd->hline(mystd->width()-2);
82 mystd->move(1, mystd->width()/2);
83 mystd->vline(mystd->height()-2);
84 mystd->addch(0, mystd->width()/2, ACS_TTEE);
85 mystd->addch(mystd->height()-1, mystd->width()/2, ACS_BTEE);
86 mystd->addch(mystd->height()/2, 0, ACS_LTEE);
87 mystd->addch(mystd->height()/2, mystd->width()-1, ACS_RTEE);
88 mystd->addch(mystd->height()/2, mystd->width()/2, ACS_PLUS);
89
90 // Draw frames with titles around panels so that we can see where
91 // the panels are located.
92 u->boldframe("Win U");
93 v->frame("Win V");
94 w->boldframe("Win W");
95 x->frame("Win X");
96 y->boldframe("Win Y");
97 z->frame("Win Z");
98 if (NCursesApplication::getApplication()->useColors()) {
99 u->bkgd(' '|COLOR_PAIR(1));
100 w->bkgd(' '|COLOR_PAIR(1));
101 y->bkgd(' '|COLOR_PAIR(1));
102 v->bkgd(' '|COLOR_PAIR(2));
103 x->bkgd(' '|COLOR_PAIR(2));
104 z->bkgd(' '|COLOR_PAIR(2));
105 }
106
107 // A refresh to any valid panel updates all panels and refreshes
micky3879b9f5e72025-07-08 18:04:53 -0400108 // the screen. Using mystd is just convenient - We know it is always
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530109 // valid until the end of the program.
110
111 mystd->refresh();
112 sleep(sleeptime);
113
114 // Show what happens when panels are deleted and moved.
115
116 sleep(sleeptime);
117 delete u;
118 mystd->refresh();
119
120 sleep(sleeptime);
121 delete z;
122 mystd->refresh();
123
124 sleep(sleeptime);
125 delete v;
126 mystd->refresh();
127
128 // show how it looks when a panel moves
129 sleep(sleeptime);
130 y->mvwin(5, 30);
131 mystd->refresh();
132
133 sleep(sleeptime);
134 delete y;
135 mystd->refresh();
136
137 // show how it looks when you raise a panel
138 sleep(sleeptime);
139 w->top();
140 mystd->refresh();
141
142 sleep(sleeptime);
143 delete w;
144 mystd->refresh();
145
146 sleep(sleeptime);
147 delete x;
148
149 mystd->clear();
150 mystd->refresh();
151
152 // Don't forget to clean up the main screen. Since this is the
153 // last thing using NCursesWindow, this has the effect of
154 // shutting down ncurses and restoring the terminal state.
155
156 sleep(sleeptime);
157 delete mystd;
158 }
159};
160
161class UserData
162{
163private:
164 int u;
165public:
166 UserData(int x) : u(x) {}
167 int sleeptime() const { return u; }
168};
169//
170// -------------------------------------------------------------------------
171//
micky3879b9f5e72025-07-08 18:04:53 -0400172template<class T> class NCURSES_CXX_IMPEXP MyAction : public NCursesUserItem<UserData>
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530173{
174public:
175 MyAction (const char* p_name,
176 const T* p_UserData)
177 : NCursesUserItem<T>(p_name, static_cast<const char*>(0), p_UserData)
178 {}
179
micky3879b9f5e72025-07-08 18:04:53 -0400180 virtual ~MyAction() THROWS(NCursesException) {}
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530181
182 bool action() {
183 SillyDemo a;
184 a.run(NCursesUserItem<T>::UserData()->sleeptime());
185 return FALSE;
186 }
187};
188
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530189class QuitItem : public NCursesMenuItem
190{
191public:
192 QuitItem() : NCursesMenuItem("Quit") {
193 }
194
195 bool action() {
196 return TRUE;
197 }
198};
199//
200// -------------------------------------------------------------------------
201//
202class Label : public NCursesFormField
203{
204public:
205 Label(const char* title,
206 int row, int col)
207 : NCursesFormField(1, static_cast<int>(::strlen(title)), row, col) {
208 set_value(title);
209 options_off(O_EDIT|O_ACTIVE);
210 }
211};
212//
213// -------------------------------------------------------------------------
214//
215class MyFieldType : public UserDefinedFieldType
216{
217private:
218 int chk;
219protected:
220 bool field_check(NCursesFormField& f) {
Steve Kondikae271bc2015-11-15 02:50:53 +0100221 (void) f;
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530222 return TRUE;
223 }
224 bool char_check(int c) {
225 return (c==chk?TRUE:FALSE);
226 }
227public:
228 MyFieldType(int x) : chk(x) {
229 }
230};
231//
232// -------------------------------------------------------------------------
233//
234class TestForm : public NCursesForm
235{
236private:
237 NCursesFormField** F;
238 MyFieldType* mft;
239 Integer_Field *ift;
240 Enumeration_Field *eft;
241
242 static const char *weekdays[];
243
244public:
245 TestForm()
246 : NCursesForm(13, 51, (lines() - 15)/2, (cols() - 53)/2),
247 F(0),
248 mft(0),
249 ift(0),
250 eft(0)
251 {
252
253 F = new NCursesFormField*[10];
254 mft = new MyFieldType('X');
255 ift = new Integer_Field(0, 1, 10);
256 eft = new Enumeration_Field(weekdays);
257
258 F[0] = new Label("Demo Entry Form", 0, 16);
259 F[1] = new Label("Weekday Enum", 2, 1);
260 F[2] = new Label("Number(1-10)", 2, 21);
261 F[3] = new Label("Only 'X'", 2, 35);
262 F[4] = new Label("Multiline Field (Dynamic and Scrollable)", 5, 1);
263 F[5] = new NCursesFormField(1, 18, 3, 1);
264 F[6] = new NCursesFormField(1, 12, 3, 21);
265 F[7] = new NCursesFormField(1, 12, 3, 35);
266 F[8] = new NCursesFormField(4, 46, 6, 1, 2);
267 F[9] = new NCursesFormField();
268
269 InitForm(F, TRUE, TRUE);
270 boldframe();
271
272 F[5]->set_fieldtype(*eft);
273 F[6]->set_fieldtype(*ift);
274
275 F[7]->set_fieldtype(*mft);
276 F[7]->set_maximum_growth(20); // max. 20 characters
277 F[7]->options_off(O_STATIC); // make field dynamic
278
279 F[8]->set_maximum_growth(10); // max. 10 lines
280 F[8]->options_off(O_STATIC); // make field dynamic
281 }
282
283 TestForm& operator=(const TestForm& rhs)
284 {
285 if (this != &rhs) {
286 *this = rhs;
287 }
288 return *this;
289 }
290
291 TestForm(const TestForm& rhs)
292 : NCursesForm(rhs), F(0), mft(0), ift(0), eft(0)
293 {
294 }
295
micky3879b9f5e72025-07-08 18:04:53 -0400296 ~TestForm() THROWS(NCursesException) {
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530297 delete mft;
298 delete ift;
299 delete eft;
300 }
301};
302
303const char* TestForm::weekdays[] = {
304 "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
305 "Friday", "Saturday", NULL };
306//
307// -------------------------------------------------------------------------
308//
309class FormAction : public NCursesMenuItem
310{
311public:
312 FormAction(const char *s) : NCursesMenuItem(s) {
313 }
314
315 bool action() {
316 TestForm F;
317 Soft_Label_Key_Set* S = new Soft_Label_Key_Set;
318 for(int i=1; i <= S->labels(); i++) {
319 char buf[8];
320 assert(i < 100);
micky3879b9f5e72025-07-08 18:04:53 -0400321 ::_nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) "Frm%02d", i % 100);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530322 (*S)[i] = buf; // Text
323 (*S)[i] = Soft_Label_Key_Set::Soft_Label_Key::Left; // Justification
324 }
325 NCursesApplication::getApplication()->push(*S);
326 F();
327 NCursesApplication::getApplication()->pop();
328 delete S;
329 return FALSE;
330 }
331};
332//
333// -------------------------------------------------------------------------
334//
335class PadAction : public NCursesMenuItem
336{
337public:
338 PadAction(const char* s) : NCursesMenuItem(s) {
339 }
340
341 bool action() {
342 const int GRIDSIZE = 3;
343 const int PADSIZE = 200;
344 unsigned gridcount = 0;
345
346 NCursesPanel mystd;
347 NCursesPanel P(mystd.lines()-2, mystd.cols()-2, 1, 1);
348 NCursesFramedPad FP(P, PADSIZE, PADSIZE);
349
350 for (int i=0; i < PADSIZE; i++) {
351 for (int j=0; j < PADSIZE; j++) {
352 if (i % GRIDSIZE == 0 && j % GRIDSIZE == 0) {
353 if (i==0 || j==0)
354 FP.addch('+');
355 else
356 FP.addch(static_cast<chtype>('A' + (gridcount++ % 26)));
357 }
358 else if (i % GRIDSIZE == 0)
359 FP.addch('-');
360 else if (j % GRIDSIZE == 0)
361 FP.addch('|');
362 else
363 FP.addch(' ');
364 }
365 }
366
367 P.label("Pad Demo", NULL);
368 FP();
369 P.clear();
370 return FALSE;
371 }
372};
373
374//
375// -------------------------------------------------------------------------
376//
377class PassiveItem : public NCursesMenuItem
378{
379public:
380 PassiveItem(const char* text) : NCursesMenuItem(text) {
381 options_off(O_SELECTABLE);
382 }
383};
384
385//
386// -------------------------------------------------------------------------
387//
388class ScanAction : public NCursesMenuItem
389{
390public:
391 ScanAction(const char* s) : NCursesMenuItem(s) {
392 }
393
394 bool action() {
395 NCursesPanel *mystd = new NCursesPanel();
396
397 NCursesPanel *w = new NCursesPanel(mystd->lines() - 2, mystd->cols() - 2, 1, 1);
398 w->box();
399 w->refresh();
400
401 NCursesPanel *s = new NCursesPanel(w->lines() - 6, w->cols() - 6, 3, 3);
402 s->scrollok(TRUE);
403 ::echo();
404
405 s->printw("Enter decimal integers. The running total will be shown\n");
406 int nvalue = -1;
407 int result = 0;
408 while (nvalue != 0) {
409 nvalue = 0;
410 s->scanw("%d", &nvalue);
411 if (nvalue != 0) {
412 s->printw("%d: ", result += nvalue);
413 }
414 s->refresh();
415 }
416 s->printw("\nPress any key to continue...");
417 s->getch();
418
419 delete s;
420 delete w;
421 delete mystd;
422 ::noecho();
423 return FALSE;
424 }
425};
426
427//
428// -------------------------------------------------------------------------
429//
430class MyMenu : public NCursesMenu
431{
432private:
433 NCursesPanel* P;
434 NCursesMenuItem** I;
435 UserData *u;
436 #define n_items 7
437
438public:
439 MyMenu ()
440 : NCursesMenu (n_items+2, 8, (lines()-10)/2, (cols()-10)/2),
441 P(0), I(0), u(0)
442 {
443 u = new UserData(1);
444 I = new NCursesMenuItem*[1+n_items];
445 I[0] = new PassiveItem("One");
446 I[1] = new PassiveItem("Two");
447 I[2] = new MyAction<UserData> ("Silly", u);
448 I[3] = new FormAction("Form");
449 I[4] = new PadAction("Pad");
450 I[5] = new ScanAction("Scan");
451 I[6] = new QuitItem();
452 I[7] = new NCursesMenuItem(); // Terminating empty item
453
454 InitMenu(I, TRUE, TRUE);
455
456 P = new NCursesPanel(1, n_items, LINES-1, 1);
457 boldframe("Demo", "Silly");
458 P->show();
459 }
460
461 MyMenu& operator=(const MyMenu& rhs)
462 {
463 if (this != &rhs) {
464 *this = rhs;
465 }
466 return *this;
467 }
468
469 MyMenu(const MyMenu& rhs)
470 : NCursesMenu(rhs), P(0), I(0), u(0)
471 {
472 }
473
micky3879b9f5e72025-07-08 18:04:53 -0400474 ~MyMenu() THROWS(NCursesException)
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530475 {
476 P->hide();
477 delete P;
478 delete u;
479 }
480
481 virtual void On_Menu_Init()
482 {
483 NCursesWindow W(::stdscr);
484 P->move(0, 0);
485 P->clrtoeol();
486 for(int i=1; i<=count(); i++)
487 P->addch('0' + i);
488 P->bkgd(W.getbkgd());
489 refresh();
490 }
491
492 virtual void On_Menu_Termination()
493 {
494 P->move(0, 0);
495 P->clrtoeol();
496 refresh();
497 }
498
499 virtual void On_Item_Init(NCursesMenuItem& item)
500 {
501 P->move(0, item.index());
502 P->attron(A_REVERSE);
503 P->printw("%1d", 1+item.index());
504 P->attroff(A_REVERSE);
505 refresh();
506 }
507
508 virtual void On_Item_Termination(NCursesMenuItem& item)
509 {
510 P->move(0, item.index());
511 P->attroff(A_REVERSE);
512 P->printw("%1d", 1+item.index());
513 refresh();
514 }
515};
516//
517// -------------------------------------------------------------------------
518//
519class TestApplication : public NCursesApplication
520{
521protected:
522 int titlesize() const { return 1; }
523 void title();
524 Soft_Label_Key_Set::Label_Layout useSLKs() const {
525 return Soft_Label_Key_Set::PC_Style_With_Index;
526 }
527 void init_labels(Soft_Label_Key_Set& S) const;
528
529public:
530 TestApplication() : NCursesApplication(TRUE) {
531 }
532
533 int run();
534};
535
536void TestApplication::init_labels(Soft_Label_Key_Set& S) const
537{
538 for(int i=1; i <= S.labels(); i++) {
539 char buf[8];
540 assert(i < 100);
micky3879b9f5e72025-07-08 18:04:53 -0400541 ::_nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) "Key%02d", i % 100);
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530542 S[i] = buf; // Text
543 S[i] = Soft_Label_Key_Set::Soft_Label_Key::Left; // Justification
544 }
545}
546
547void TestApplication::title()
548{
549 const char * const titleText = "Simple C++ Binding Demo";
550 const int len = ::strlen(titleText);
551
micky3879b9f5e72025-07-08 18:04:53 -0400552 getTitleWindow()->bkgd(screen_titles());
553 getTitleWindow()->addstr(0, (getTitleWindow()->cols() - len)/2, titleText);
554 getTitleWindow()->noutrefresh();
Amit Daniel Kachhape6a01f52011-07-20 11:45:59 +0530555}
556
557
558int TestApplication::run()
559{
560 MyMenu M;
561 M();
562 return 0;
563}
564
565//
566// -------------------------------------------------------------------------
567//
568static TestApplication *Demo = new TestApplication();
micky3879b9f5e72025-07-08 18:04:53 -0400569
570#if (defined(_WIN32) || defined(_WIN64))
571// This is actually only needed when ncurses is a dll
572NCURSES_CXX_MAIN
573#endif