1 //
2 // This file is part of libyacurs.
3 // Copyright (C) 2013  Rafael Ostertag
4 //
5 // This program is free software: you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License as
7 // published by the Free Software Foundation, either version 3 of the
8 // License, or (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful, but
11 // WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program.  If not, see
17 // <http://www.gnu.org/licenses/>.
18 //
19 //
20 // $Id$
21 
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25 
26 #include <gettext.h>
27 
28 #include <cassert>
29 #include <cstdlib>
30 
31 #include "dialog.h"
32 #include "eventqueue.h"
33 #include "hotkey.h"
34 #include "yacursex.h"
35 
36 namespace YACURS {
37 namespace INTERNAL {
38 /**
39  * HotKey for cancelling Dialog.
40  */
41 class HotKeyEsc : public HotKey {
42    private:
43     Dialog& _dialog;
44 
45    public:
HotKeyEsc(Dialog & d)46     HotKeyEsc(Dialog& d) : HotKey(27), _dialog(d) {}
47 
HotKeyEsc(const HotKeyEsc & hk)48     HotKeyEsc(const HotKeyEsc& hk) : HotKey(hk), _dialog(hk._dialog) {}
49 
action()50     void action() {
51         _dialog._dstate = DIALOG_CANCEL;
52         _dialog.close();
53     }
54 
clone() const55     HotKey* clone() const { return new HotKeyEsc(*this); }
56 };
57 }  // namespace INTERNAL
58 }  // namespace YACURS
59 
60 using namespace YACURS;
61 
62 //
63 // Private
64 //
65 
66 //
67 // Protected
68 //
ok_button() const69 const Button* const Dialog::ok_button() const { return _bok; }
70 
cancel_button() const71 const Button* const Dialog::cancel_button() const { return _bcancel; }
72 
yes_button() const73 const Button* const Dialog::yes_button() const { return _byes; }
74 
no_button() const75 const Button* const Dialog::no_button() const { return _bno; }
76 
dialog_state(DIALOG_STATE st)77 void Dialog::dialog_state(DIALOG_STATE st) { _dstate = st; }
78 
add_button(Button * b)79 void Dialog::add_button(Button* b) { _hpack->add_back(b); }
80 
add_button(Spacer * s)81 void Dialog::add_button(Spacer* s) { _hpack->add_back(s); }
82 
button_press_handler(Event & e)83 void Dialog::button_press_handler(Event& e) {
84     if (realization() != REALIZED) return;
85 
86     assert(e == EVT_BUTTON_PRESS);
87     EventEx<Button*>& evt = dynamic_cast<EventEx<Button*>&>(e);
88 
89     if (evt.data() == _bok) {
90         _dstate = DIALOG_OK;
91         on_ok_button();
92         close();
93     }
94 
95     if (evt.data() == _bcancel) {
96         _dstate = DIALOG_CANCEL;
97         on_cancel_button();
98         close();
99     }
100 
101     if (evt.data() == _byes) {
102         _dstate = DIALOG_YES;
103         on_yes_button();
104         close();
105     }
106 
107     if (evt.data() == _bno) {
108         _dstate = DIALOG_NO;
109         on_no_button();
110         close();
111     }
112 }
113 
on_ok_button()114 void Dialog::on_ok_button() { /* Intentionally empty */
115 }
116 
on_cancel_button()117 void Dialog::on_cancel_button() { /* Intentionally empty */
118 }
119 
on_yes_button()120 void Dialog::on_yes_button() { /* Intentionally empty */
121 }
122 
on_no_button()123 void Dialog::on_no_button() { /* Intentionally empty */
124 }
125 
126 //
127 // Public
128 //
129 
Dialog(const std::string & title,DIALOG_TYPE dialogType,DIALOG_SIZE dialogSize)130 Dialog::Dialog(const std::string& title, DIALOG_TYPE dialogType,
131                DIALOG_SIZE dialogSize)
132     : _vpack(new VPack),
133       _hpack(new HPack),
134       _byes(0),
135       _bok(0),
136       _ok_spacer(new Spacer),
137       _bcancel(0),
138       _bno(0),
139       _hrule(new HRule),
140       _dstate(DIALOG_CANCEL),
141       _dialog_type(dialogType),
142       _dialog_size(dialogSize),
143       _title(title) {
144     _hrule->color(DIALOG);
145 
146     _vpack->add_back(_hrule);
147     _vpack->add_back(_hpack);
148 
149     switch (_dialog_type) {
150         case OKCANCEL:
151             _bcancel = new Button(_("Cancel"));
152             _hpack->add_back(_bcancel);
153             _hpack->add_front(_ok_spacer);
154             // Fall thru
155 
156         case OK_ONLY:
157             _bok = new Button(_("OK"));
158             _hpack->add_front(_bok);
159             break;
160 
161         case YESNO:
162             _bno = new Button(_("No"));
163             _hpack->add_front(_bno);
164             _hpack->add_front(_ok_spacer);
165             // Fall thru
166 
167         case YES_ONLY:
168             _byes = new Button(_("Yes"));
169             _hpack->add_front(_byes);
170             break;
171 
172         // handled separately
173         case YESNOCANCEL:
174             _bcancel = new Button(_("Cancel"));
175             _hpack->add_back(_bcancel);
176             _hpack->add_front(_ok_spacer);
177             _bno = new Button(_("No"));
178             _hpack->add_front(_bno);
179             _hpack->add_front(_ok_spacer);
180             _byes = new Button(_("Yes"));
181             _hpack->add_front(_byes);
182             break;
183 
184         default:
185             throw EXCEPTIONS::InvalidDialogType();
186             break;
187     }
188 
189     // from Window
190     Window::widget(_vpack);
191     frame(true);
192     color(DIALOG);
193 }
194 
~Dialog()195 Dialog::~Dialog() {
196     assert(_vpack != 0);
197     assert(_hpack != 0);
198     assert(_hrule != 0);
199     assert(_ok_spacer != 0);
200 
201     delete _vpack;
202     delete _hpack;
203     delete _hrule;
204     delete _ok_spacer;
205 
206     if (_bok) delete _bok;
207     if (_byes) delete _byes;
208     if (_bcancel) delete _bcancel;
209     if (_bno) delete _bno;
210 
211     EventQueue::disconnect_event(EventConnectorMethod1<Dialog>(
212         EVT_BUTTON_PRESS, this, &Dialog::button_press_handler));
213 }
214 
widget(WidgetBase * _w)215 void Dialog::widget(WidgetBase* _w) {
216     assert(_vpack != 0);
217     _vpack->add_front(_w);
218 }
219 
220 DIALOG_STATE
dialog_state() const221 Dialog::dialog_state() const { return _dstate; }
222 
223 DIALOG_TYPE
dialog_type() const224 Dialog::dialog_type() const { return _dialog_type; }
225 
title(const std::string & title)226 void Dialog::title(const std::string& title) {
227     _title = title;
228 
229     if (realization() == REALIZED) {
230         // Ugly hack, reset box, so that it will be adjusted to new
231         // title...
232         if (frame()) curses_window()->box();
233 
234         refresh(true);
235     }
236 }
237 
title() const238 std::string Dialog::title() const { return _title; }
239 
refresh(bool immediate)240 void Dialog::refresh(bool immediate) {
241     if (realization() != REALIZED && realization() != REALIZING) return;
242 
243     CurStr tmp(_title, Coordinates(1, 0), DIALOG_TITLE);
244     curses_window()->addstrx(tmp);
245 
246     Window::refresh(immediate);
247 }
248 
realize()249 void Dialog::realize() {
250     REALIZE_ENTER;
251 
252     // Reset the dialog state
253     _dstate = DIALOG_CANCEL;
254 
255     EventQueue::connect_event(EventConnectorMethod1<Dialog>(
256         EVT_BUTTON_PRESS, this, &Dialog::button_press_handler));
257 
258     Margin _margin(2, 2, 2, 2);
259     _vpack->hinting(false);
260 
261     if (_dialog_size == AUTOMATIC) {
262         _vpack->hinting(true);
263         // Compute the margin. We try to vertically center the dialog.
264         int hinted_rows = Window::widget()->size_hint().rows();
265         if (hinted_rows > 0 && hinted_rows < area().rows() - 2) {
266             int vert_margin = (area().rows() - hinted_rows) / 2 - 1;
267             _margin = Margin(vert_margin, 2, vert_margin, 2);
268         }
269     }
270 
271     margin(_margin);
272     Window::realize();
273 
274     add_hotkey(INTERNAL::HotKeyEsc(*this));
275 
276     REALIZE_LEAVE;
277 }
278 
unrealize()279 void Dialog::unrealize() {
280     UNREALIZE_ENTER;
281 
282     EventQueue::disconnect_event(EventConnectorMethod1<Dialog>(
283         EVT_BUTTON_PRESS, this, &Dialog::button_press_handler));
284 
285     Window::unrealize();
286 
287     UNREALIZE_LEAVE;
288 }
289