/* * This file illustrates more advanced use of a string browser, as well as use * of string editors. The display in this example consists of a simple control * panel at the top, and a string browser below. The control panel contains a * string editor to enter a numeric index, a button to select the string at * that index, and a string editor to display the string at the selected index * when the button is pressed. The elements in the control panel illustrate * how values are read from a string browser, and read from and written to a * string editor. * * The string browser in the lower part of the display contains a fixed list of * strings. When the user selects one of the strings by clicking on it, the * control panel values are updated accordingly. * * Another feature of the top string editor in the control panel is that * hitting the return key while in the edit box causes the push button to be * pressed automatically. This illustrates how keyboard events can be handled * specially for particular interfaces. * * This example illustrates five different usage levels of the InterViews * library: * * (1) Declaration of InterViews library objects, with no class * specialization. * (2) Specialization of an InterViews library class. * (3) Specialization of an InterViews library class, with specialization of * an event handler function. * (4) Specialization of an InterViews library class, where selective * copying of library source code is necessary. * (5) Specialization of an InterViews library class, where modification of * a library class is necessary. * * Ideally, a C++ library, such as InterViews, should be designed so that usage * levels 4 and 5 are not necessary. However, library designers cannot always * anticipate all potential forms of use, so levels 4 and 5 are sometimes * required. In the case of InterViews, the source code is fully available, * which makes levels 4 and 5 possible. * * Usage level 5 should hopefully be rare with a well designed library. With * InterViews, it is most often necessary when a library class function is * declared private to which a specialized class would like access. It may * also be necessary when an advanced form of interface processing is necessary * that was not at all anticipated by the InterViews library developers. In * such cases, the InterViews .h file is copied and modified as necessary. */ #include #include #include #include #include #include #include #include #include #include #include "strbrowser.h" /* Modified lib class -- see comments above and below*/ #include #include #include #include "quit-exception.h" #include "std-macros.h" class LStringEditor; class LStringBrowser; /* * Class SelectButton is a standard specialization of an InterViews PushButton * (usage level 2). The SelectButton constructor is passed pointers to the * other elements of the control panel so that SelectButton::Press can set the * values appropriately. */ class SelectButton : public PushButton { public: SelectButton(char* label, LStringBrowser* sb, LStringEditor* ed1, StringEditor* ed2); void Press(); private: LStringBrowser* sb; LStringEditor* ed1; StringEditor* ed2; }; /* * Class LStringEditor is a "local" string editor that detects when the return * key is hit, and calls SelectButton::Press thereupon. This specialization of * the InterViews StringEditor illustrates how to specialize a virtual handler * function without having to copy the source code from the InterViews library * (usage level 3). In this case, the virtual handler StringEditor::HandleChar * is specialized to detect when the return key is hit, whereupon local * processing is done. * * After performing local processing, the specialization of HandleChar calls * the library StringEditor::HandleChar explicitly. In this way, the * specialized HandleChar only needs to handle local processing, letting the * library HandleChar handle all other character processing as normal for a * StringEditor. This is a standard technique to "forward" event handling from * a local handler to a more global handler. I.e., LStringEditor::HandleChar * does its local processing, then forwards the received event to the library * StringEditor::HandleChar for any additional processing. */ class LStringEditor : public StringEditor { public: LStringEditor(ButtonState* bs, char* s): StringEditor(bs, s) {} void SetButton(SelectButton* sb); protected: SelectButton* sb; bool HandleChar(char c); }; /* * Class LStringBrowser is a "local" string browser that detects when a down * event happens and updates the control panel items thereupon. This * specialization of the InterViews StringBrowser illustrates how to specialize * a virtual handler function where copying of library code is necessary. * Here, the virtual handler StringBrowser::Handle is specialized to detect * when a down event occurs, whereupon local processing is done. * * Due to the way in which the StringBrowser::Handle processes events, it is * necessary to copy its body (usage level 4), rather than "forwarding" to it * as described above for LStringEditor::HandleChar. Further, it is necessary * to copy and modify InterViews' strbrowser.{h,c}, in order to make a two * previously private functions protected (usage level 5). */ class LStringBrowser : public StringBrowser { public: LStringBrowser(ButtonState* bs, int rows, int cols) : StringBrowser(bs, rows, cols) {} void SetEditors(LStringEditor* ed1, StringEditor* ed2); void LStringBrowser::Handle(Event& e); private: LStringEditor* ed1; StringEditor* ed2; }; /* * FUNCTION: SelectButton::SelectButton(char* label * LStringBrowser* sb, StringEditor* ed1, StringEditor* ed2) * DESCRIPTION: * The SelectButton constructor. It takes pointers to the other * interactors in the display so that Press can do its thing. */ SelectButton::SelectButton(char* label, LStringBrowser* sb, LStringEditor* ed1, StringEditor* ed2) : PushButton(label, new ButtonState(0), 1) { this->sb = sb; this->ed1 = ed1; this->ed2 = ed2; } /* * FUNCTION: SelectButton::Press() * DESCRIPTION: * Extract the string value from the top string editor in the control * panel. Check numeric value for the proper range, and use it to extract * a value from the string browser in the botton of the display. */ void SelectButton::Press() { int i = atoi(ed1->Text()); if (i<0) ed2->Message("Index too small"); else if (i>=sb->Count()) ed2->Message("Index too large"); else { ed2->Message(sb->String(atoi(ed1->Text()))); sb->UnselectAll(); sb->Select(i); } } /* * FUNCTION: bool LStringEditor::HandleChar(char c) * DESCRIPTION: * Handle a character that has been typed within the LStringEditor * boundaries. HandleChar is called from StringEditor::Handle when a * keyboard event is detected. */ bool LStringEditor::HandleChar(char c) { if (c == '\r') { sb->Press(); return false; } else return StringEditor::HandleChar(c); } /* * FUNCTION: void LStringEditor::SetButton(SelectButton* sb); * DESCRIPTION: * Set the button pointer in the LStringEditor so HandleChar can call * Press. It's necessary to do the setting in this function instead of * the constructor due to the mutual referencing between the various * interactors in the display. I.e., the button points to the editor and * the editor points to the button, so one of them has to set pointers * after construction. */ void LStringEditor::SetButton(SelectButton* sb) { this->sb = sb; } /* * FUNCTION: void LStringBrowser::Handle(Event& e) * DESCRIPTION: * Handle events for a local string browser. As noted above, this is a * copy of StringBrowser::Handle. Additions are indicated by NEW * comments. */ void LStringBrowser::Handle(Event& e) { int i; char s[10]; if (e.eventType == KeyEvent) { HandleKeyEvent(e); } else { bool done = false; do { switch (e.eventType) { case DownEvent: done = HandleDownEvent(e); /* NEW for LStringBrowser */ i = Selection(0); if (i >= 0) { /* i = -1 if no selection was made */ sprintf(s, "%d", i); ed1->Message(s); ed2->Message(String(i)); } /* END NEW */ break; case KeyEvent: done = HandleKeyEvent(e); break; } if (!done) { Read(e); } } while (!done); } } /* * FUNCTION: void LStringBrowser::SetEditors( * LStringEditor* ed1, StringEditor* ed2) * DESCRIPTION: * Set the string editor pointers so that LStringBrowser::Handle can do * its thing. It's necessary to do the setting in this function instead * of the constructor due to the mutual referencing between the various * interactors in the display. I.e., the editors point to the browser and * the browser points to the editors, so one of them has to set pointers * after construction. */ void LStringBrowser::SetEditors(LStringEditor* ed1, StringEditor* ed2) { this->ed1 = ed1; this->ed2 = ed2; } /* * Class QuitButton is a standard specialization of InterViews::PushButton. * QuitButton::Press throws a quit exception that's caught at the top-level of * main(). */ class QuitButton : public PushButton { public: QuitButton(const char* label, ButtonState* bs, int val) : PushButton(label, bs, val) {} private: void Press() {throw new QuitException();} }; /* * Class WeirdoGlue is used to track down wierd glue behavior, by setting a * breakpoint in its Redraw method. */ class WeirdoGlue : public HGlue { public: WeirdoGlue(int natural = 0, int stretch = hfil) : HGlue(natural, stretch) {}; WeirdoGlue(int natural, int shrink, int stretch) : HGlue(natural, shrink, stretch) {}; WeirdoGlue* SetInstance(const char* name) { Interactor::SetInstance(name); return this; } protected: void Redraw(Coord, Coord, Coord, Coord); }; void WeirdoGlue::Redraw(Coord l, Coord b, Coord r, Coord t) { HGlue::Redraw(l, b, r, t); } /* * FUNCTION: main() * DESCRIPTION: * Compose the interface and fire it up. */ main() { World* w = new World(); ButtonState* dummyState = new ButtonState(1); LStringBrowser* browser = new LStringBrowser(dummyState, 10, 35); WeirdoGlue* wg; /* * A simple control panel to select a string from the browser is in the top * of the display. The scrolled string browser is below the panel. */ LStringEditor* selectEditor = new LStringEditor(dummyState, "0"); LStringEditor* displayEditor = new LStringEditor( dummyState, " "); displayEditor->Listen(noEvents); SelectButton* selectButton = new SelectButton("Select", browser, selectEditor, displayEditor); selectEditor->SetButton(selectButton); displayEditor->SetButton(selectButton); /* * Build the string browser. */ browser->SetEditors(selectEditor, displayEditor); browser->Append("aaaaaa"); browser->Append("bbbbbb"); browser->Append("ccccc"); browser->Append("dddd"); browser->Append("eeeeeeee"); browser->Append("ffffffff"); browser->Append("ggg"); browser->Append("hhhhhhhhhhhhhh"); browser->Append("iiiiii"); browser->Append("jjjjjj"); browser->Append("kkkkkk"); browser->Append("lllllllllllll"); browser->Append("mmmmmm"); browser->Append("nnnnnnnnnn"); /* * Attach the scroll bar to the string browser. */ VScroller* scroller = new VScroller(browser); HBox* scrolledBrowser = new HBox(browser, scroller); /* * Put the control panel and scrolled browser together. */ VBox* panel = new VBox( new VGlue(round(0.10*inch)), new HBox( (new WeirdoGlue(round(0.10*inch), 0))->SetInstance("1"), new Message("Enter index of string to read: "), new Frame(selectEditor), new HGlue(round(0.10*inch),0)), new VGlue(round(0.10*inch)), new HBox( (new WeirdoGlue(round(0.10*inch), 0))->SetInstance("2"), new Message("Press to read selected string: "), new HGlue(round(0.10*inch), 0), selectButton, new HGlue(round(0.10*inch), 0), new QuitButton("Quit", dummyState, 0), new HGlue(round(0.10*inch), 0, hfil)), new VGlue(round(0.10*inch)), new HBox( new HGlue(round(0.10*inch), 0), new Message("Value of selected string is: "), new Frame(displayEditor), new HGlue(round(0.10*inch), 0)) ); panel->Align(Left); panel->Update(); /* * Initialize the control panel selection. */ selectButton->Press(); /* * Fire it up. */ w->InsertApplication( new Tray( new VBox( panel, new VGlue(round(0.20*inch)), new Frame(scrolledBrowser)) ) ); try { w->Run(); } catch (QuitException*) { } delete w; }