f651e18eddc6172bbc0f6415a23dea29156e311c
[ncurses.git] / c++ / demo.cc
1 // * This makes emacs happy -*-Mode: C++;-*-
2 /****************************************************************************
3  * Copyright 2018-2019,2020 Thomas E. Dickey                                *
4  * Copyright 1998-2012,2017 Free Software Foundation, Inc.                  *
5  *                                                                          *
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  *
39  * $Id: demo.cc,v 1.46 2020/05/24 01:40:20 anonymous.maarten Exp $
40  */
41
42 #include "internal.h"
43 #include "cursesapp.h"
44 #include "cursesm.h"
45 #include "cursesf.h"
46
47 #ifdef _WIN32
48 #undef KEY_EVENT
49 #endif
50
51 #ifndef _WIN32
52 extern "C" unsigned int sleep(unsigned int);
53 #endif
54
55 #undef index // needed for NeXT
56
57 //
58 // -------------------------------------------------------------------------
59 //
60 class 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
108     //  the screen.  Using mystd is just convenient - We know it's always
109     //  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
161 class UserData
162 {
163 private:
164   int u;
165 public:
166   UserData(int x) : u(x) {}
167   int sleeptime() const { return u; }
168 };
169 //
170 // -------------------------------------------------------------------------
171 //
172 template<class T> class MyAction : public NCursesUserItem<T>
173 {
174 public:
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
180   virtual ~MyAction() THROWS(NCursesException) {}
181
182   bool action() {
183     SillyDemo a;
184     a.run(NCursesUserItem<T>::UserData()->sleeptime());
185     return FALSE;
186   }
187 };
188
189 template class MyAction<UserData>;
190 template class NCURSES_CXX_IMPEXP NCursesUserItem<UserData>;
191
192 class QuitItem : public NCursesMenuItem
193 {
194 public:
195   QuitItem() : NCursesMenuItem("Quit") {
196   }
197
198   bool action() {
199     return TRUE;
200   }
201 };
202 //
203 // -------------------------------------------------------------------------
204 //
205 class Label : public NCursesFormField
206 {
207 public:
208   Label(const char* title,
209         int row, int col)
210     : NCursesFormField(1, static_cast<int>(::strlen(title)), row, col) {
211       set_value(title);
212       options_off(O_EDIT|O_ACTIVE);
213   }
214 };
215 //
216 // -------------------------------------------------------------------------
217 //
218 class MyFieldType : public UserDefinedFieldType
219 {
220 private:
221   int chk;
222 protected:
223   bool field_check(NCursesFormField& f) {
224     (void) f;
225     return TRUE;
226   }
227   bool char_check(int c) {
228     return (c==chk?TRUE:FALSE);
229   }
230 public:
231   MyFieldType(int x) : chk(x) {
232   }
233 };
234 //
235 // -------------------------------------------------------------------------
236 //
237 class TestForm : public NCursesForm
238 {
239 private:
240   NCursesFormField** F;
241   MyFieldType* mft;
242   Integer_Field *ift;
243   Enumeration_Field *eft;
244
245   static const char *weekdays[];
246
247 public:
248   TestForm()
249     : NCursesForm(13, 51, (lines() - 15)/2, (cols() - 53)/2),
250       F(0),
251       mft(0),
252       ift(0),
253       eft(0)
254   {
255
256     F     = new NCursesFormField*[10];
257     mft   = new MyFieldType('X');
258     ift   = new Integer_Field(0, 1, 10);
259     eft   = new Enumeration_Field(weekdays);
260
261     F[0]  = new Label("Demo Entry Form", 0, 16);
262     F[1]  = new Label("Weekday Enum", 2, 1);
263     F[2]  = new Label("Number(1-10)", 2, 21);
264     F[3]  = new Label("Only 'X'", 2, 35);
265     F[4]  = new Label("Multiline Field (Dynamic and Scrollable)", 5, 1);
266     F[5]  = new NCursesFormField(1, 18, 3, 1);
267     F[6]  = new NCursesFormField(1, 12, 3, 21);
268     F[7]  = new NCursesFormField(1, 12, 3, 35);
269     F[8]  = new NCursesFormField(4, 46, 6, 1, 2);
270     F[9]  = new NCursesFormField();
271
272     InitForm(F, TRUE, TRUE);
273     boldframe();
274
275     F[5]->set_fieldtype(*eft);
276     F[6]->set_fieldtype(*ift);
277
278     F[7]->set_fieldtype(*mft);
279     F[7]->set_maximum_growth(20); // max. 20 characters
280     F[7]->options_off(O_STATIC);  // make field dynamic
281
282     F[8]->set_maximum_growth(10); // max. 10 lines
283     F[8]->options_off(O_STATIC);  // make field dynamic
284   }
285
286   TestForm& operator=(const TestForm& rhs)
287   {
288     if (this != &rhs) {
289       *this = rhs;
290     }
291     return *this;
292   }
293
294   TestForm(const TestForm& rhs)
295     : NCursesForm(rhs), F(0), mft(0), ift(0), eft(0)
296   {
297   }
298
299   ~TestForm() THROWS(NCursesException) {
300     delete mft;
301     delete ift;
302     delete eft;
303   }
304 };
305
306 const char* TestForm::weekdays[] = {
307     "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
308     "Friday", "Saturday", NULL };
309 //
310 // -------------------------------------------------------------------------
311 //
312 class FormAction : public NCursesMenuItem
313 {
314 public:
315   FormAction(const char *s) : NCursesMenuItem(s) {
316   }
317
318   bool action() {
319     TestForm F;
320     Soft_Label_Key_Set* S = new Soft_Label_Key_Set;
321     for(int i=1; i <= S->labels(); i++) {
322       char buf[8];
323       assert(i < 100);
324       ::_nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) "Frm%02d", i % 100);
325       (*S)[i] = buf;                                      // Text
326       (*S)[i] = Soft_Label_Key_Set::Soft_Label_Key::Left; // Justification
327     }
328     NCursesApplication::getApplication()->push(*S);
329     F();
330     NCursesApplication::getApplication()->pop();
331     delete S;
332     return FALSE;
333   }
334 };
335 //
336 // -------------------------------------------------------------------------
337 //
338 class PadAction : public NCursesMenuItem
339 {
340 public:
341   PadAction(const char* s) : NCursesMenuItem(s) {
342   }
343
344   bool action() {
345     const int GRIDSIZE = 3;
346     const int PADSIZE  = 200;
347     unsigned gridcount = 0;
348
349     NCursesPanel mystd;
350     NCursesPanel P(mystd.lines()-2, mystd.cols()-2, 1, 1);
351     NCursesFramedPad FP(P, PADSIZE, PADSIZE);
352
353     for (int i=0; i < PADSIZE; i++) {
354       for (int j=0; j < PADSIZE; j++) {
355         if (i % GRIDSIZE == 0 && j % GRIDSIZE == 0) {
356           if (i==0 || j==0)
357             FP.addch('+');
358           else
359             FP.addch(static_cast<chtype>('A' + (gridcount++ % 26)));
360         }
361         else if (i % GRIDSIZE == 0)
362           FP.addch('-');
363         else if (j % GRIDSIZE == 0)
364           FP.addch('|');
365         else
366           FP.addch(' ');
367       }
368     }
369
370     P.label("Pad Demo", NULL);
371     FP();
372     P.clear();
373     return FALSE;
374   }
375 };
376
377 //
378 // -------------------------------------------------------------------------
379 //
380 class PassiveItem : public NCursesMenuItem
381 {
382 public:
383   PassiveItem(const char* text) : NCursesMenuItem(text) {
384     options_off(O_SELECTABLE);
385   }
386 };
387
388 //
389 // -------------------------------------------------------------------------
390 //
391 class ScanAction : public NCursesMenuItem
392 {
393 public:
394   ScanAction(const char* s) : NCursesMenuItem(s) {
395   }
396
397   bool action() {
398     NCursesPanel *mystd = new NCursesPanel();
399
400     NCursesPanel *w = new NCursesPanel(mystd->lines() - 2, mystd->cols() - 2, 1, 1);
401     w->box();
402     w->refresh();
403
404     NCursesPanel *s = new NCursesPanel(w->lines() - 6, w->cols() - 6, 3, 3);
405     s->scrollok(TRUE);
406     ::echo();
407
408     s->printw("Enter decimal integers.  The running total will be shown\n");
409     int nvalue = -1;
410     int result = 0;
411     while (nvalue != 0) {
412       nvalue = 0;
413       s->scanw("%d", &nvalue);
414       if (nvalue != 0) {
415         s->printw("%d: ", result += nvalue);
416       }
417       s->refresh();
418     }
419     s->printw("\nPress any key to continue...");
420     s->getch();
421
422     delete s;
423     delete w;
424     delete mystd;
425     ::noecho();
426     return FALSE;
427   }
428 };
429
430 //
431 // -------------------------------------------------------------------------
432 //
433 class MyMenu : public NCursesMenu
434 {
435 private:
436   NCursesPanel* P;
437   NCursesMenuItem** I;
438   UserData *u;
439   #define n_items 7
440
441 public:
442   MyMenu ()
443     : NCursesMenu (n_items+2, 8, (lines()-10)/2, (cols()-10)/2),
444       P(0), I(0), u(0)
445   {
446     u = new UserData(1);
447     I = new NCursesMenuItem*[1+n_items];
448     I[0] = new PassiveItem("One");
449     I[1] = new PassiveItem("Two");
450     I[2] = new MyAction<UserData> ("Silly", u);
451     I[3] = new FormAction("Form");
452     I[4] = new PadAction("Pad");
453     I[5] = new ScanAction("Scan");
454     I[6] = new QuitItem();
455     I[7] = new NCursesMenuItem(); // Terminating empty item
456
457     InitMenu(I, TRUE, TRUE);
458
459     P = new NCursesPanel(1, n_items, LINES-1, 1);
460     boldframe("Demo", "Silly");
461     P->show();
462   }
463
464   MyMenu& operator=(const MyMenu& rhs)
465   {
466     if (this != &rhs) {
467       *this = rhs;
468     }
469     return *this;
470   }
471
472   MyMenu(const MyMenu& rhs)
473     : NCursesMenu(rhs), P(0), I(0), u(0)
474   {
475   }
476
477   ~MyMenu() THROWS(NCursesException)
478   {
479     P->hide();
480     delete P;
481     delete u;
482   }
483
484   virtual void On_Menu_Init()
485   {
486     NCursesWindow W(::stdscr);
487     P->move(0, 0);
488     P->clrtoeol();
489     for(int i=1; i<=count(); i++)
490       P->addch('0' + i);
491     P->bkgd(W.getbkgd());
492     refresh();
493   }
494
495   virtual void On_Menu_Termination()
496   {
497     P->move(0, 0);
498     P->clrtoeol();
499     refresh();
500   }
501
502   virtual void On_Item_Init(NCursesMenuItem& item)
503   {
504     P->move(0, item.index());
505     P->attron(A_REVERSE);
506     P->printw("%1d", 1+item.index());
507     P->attroff(A_REVERSE);
508     refresh();
509   }
510
511   virtual void On_Item_Termination(NCursesMenuItem& item)
512   {
513     P->move(0, item.index());
514     P->attroff(A_REVERSE);
515     P->printw("%1d", 1+item.index());
516     refresh();
517   }
518 };
519 //
520 // -------------------------------------------------------------------------
521 //
522 class TestApplication : public NCursesApplication
523 {
524 protected:
525   int titlesize() const { return 1; }
526   void title();
527   Soft_Label_Key_Set::Label_Layout useSLKs() const {
528     return Soft_Label_Key_Set::PC_Style_With_Index;
529   }
530   void init_labels(Soft_Label_Key_Set& S) const;
531
532 public:
533   TestApplication() : NCursesApplication(TRUE) {
534   }
535
536   int run();
537 };
538
539 void TestApplication::init_labels(Soft_Label_Key_Set& S) const
540 {
541   for(int i=1; i <= S.labels(); i++) {
542     char buf[8];
543     assert(i < 100);
544     ::_nc_SPRINTF(buf, _nc_SLIMIT(sizeof(buf)) "Key%02d", i % 100);
545     S[i] = buf;                                      // Text
546     S[i] = Soft_Label_Key_Set::Soft_Label_Key::Left; // Justification
547   }
548 }
549
550 void TestApplication::title()
551 {
552   const char * const titleText = "Simple C++ Binding Demo";
553   const int len = ::strlen(titleText);
554
555   titleWindow->bkgd(screen_titles());
556   titleWindow->addstr(0, (titleWindow->cols() - len)/2, titleText);
557   titleWindow->noutrefresh();
558 }
559
560
561 int TestApplication::run()
562 {
563   MyMenu M;
564   M();
565   return 0;
566 }
567
568 //
569 // -------------------------------------------------------------------------
570 //
571 static TestApplication *Demo = new TestApplication();