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 <cassert>
27 #include <cerrno>
28 #include <cstdlib>
29 
30 #include "curs.h"
31 #include "eventqueue.h"
32 #include "windowbase.h"
33 #include "yacursconst.h"
34 #include "yacursex.h"
35 
36 using namespace YACURS;
37 
38 //
39 // Private
40 //
41 
42 //
43 // Protected
44 //
curses_window() const45 YACURS::INTERNAL::CursWin* WindowBase::curses_window() const {
46     return _curses_window;
47 }
48 
area() const49 const Area& WindowBase::area() const { return _area; }
50 
widget_area() const51 Area WindowBase::widget_area() const {
52     Area widget_area;
53 
54     if (_frame) {
55         // we have to take the frame into account, thus
56         // minus Margin(1,1,1,1)
57         widget_area = (_area - _margin) - Margin(1, 1, 1, 1);
58     } else {
59         widget_area = _area - _margin;
60     }
61 
62     return widget_area;
63 }
64 
unrealize()65 void WindowBase::unrealize() {
66     UNREALIZE_ENTER;
67 
68     assert(_curses_window != 0);
69 
70     delete _curses_window;
71     _curses_window = 0;
72 
73     UNREALIZE_LEAVE;
74 }
75 
on_close()76 bool WindowBase::on_close() { return true; }
77 
78 //
79 // Public
80 //
81 
WindowBase(const Margin & m)82 WindowBase::WindowBase(const Margin& m)
83     : _area(Coordinates(), Curses::inquiry_screensize()),
84       _margin(m),
85       _curses_window(0),
86       _frame(false),
87       _shown(false),
88       _color(DEFAULT) {
89     // We always want to receive this event. Therefore it was moved
90     // from show() to ctor.
91     EventQueue::connect_event(EventConnectorMethod1<WindowBase>(
92         EVT_SIGWINCH, this, &WindowBase::resize_handler));
93 }
94 
~WindowBase()95 WindowBase::~WindowBase() {
96     EventQueue::disconnect_event(EventConnectorMethod1<WindowBase>(
97         EVT_REDRAW, this, &WindowBase::redraw_handler));
98 
99     EventQueue::disconnect_event(EventConnectorMethod1<WindowBase>(
100         EVT_FORCEREFRESH, this, &WindowBase::force_refresh_handler));
101     EventQueue::disconnect_event(EventConnectorMethod1<WindowBase>(
102         EVT_REFRESH, this, &WindowBase ::refresh_handler));
103     EventQueue::disconnect_event(EventConnectorMethod1<WindowBase>(
104         EVT_SIGWINCH, this, &WindowBase::resize_handler));
105 
106     if (realization() == REALIZED) {
107         assert(_curses_window != 0);
108         delete _curses_window;
109     }
110 }
111 
margin(const Margin & m)112 void WindowBase::margin(const Margin& m) {
113     if (realization() == REALIZED) throw EXCEPTIONS::AlreadyRealized();
114     _margin = m;
115 }
116 
margin() const117 const Margin& WindowBase::margin() const { return _margin; }
118 
frame() const119 bool WindowBase::frame() const { return _frame; }
120 
frame(bool b)121 void WindowBase::frame(bool b) { _frame = b; }
122 
color(COLOROBJ c)123 void WindowBase::color(COLOROBJ c) { _color = c; }
124 
125 COLOROBJ
color() const126 WindowBase::color() const { return _color; }
127 
show()128 void WindowBase::show() {
129     if (realization() != UNREALIZED) return;
130 
131     EventQueue::connect_event(EventConnectorMethod1<WindowBase>(
132         EVT_REDRAW, this, &WindowBase::redraw_handler));
133 
134     EventQueue::connect_event(EventConnectorMethod1<WindowBase>(
135         EVT_FORCEREFRESH, this, &WindowBase::force_refresh_handler));
136     EventQueue::connect_event(EventConnectorMethod1<WindowBase>(
137         EVT_REFRESH, this, &WindowBase::refresh_handler));
138 
139     realize();
140     refresh(true);
141     EventQueue::submit(EventEx<WindowBase*>(EVT_WINDOW_SHOW, this));
142 
143     _shown = true;
144 }
145 
close()146 void WindowBase::close() {
147     if (realization() != REALIZED) return;
148 
149     if (!on_close()) return;
150 
151     unrealize();
152 
153     EventQueue::disconnect_event(EventConnectorMethod1<WindowBase>(
154         EVT_REDRAW, this, &WindowBase::redraw_handler));
155 
156     EventQueue::disconnect_event(EventConnectorMethod1<WindowBase>(
157         EVT_FORCEREFRESH, this, &WindowBase::force_refresh_handler));
158     EventQueue::disconnect_event(EventConnectorMethod1<WindowBase>(
159         EVT_REFRESH, this, &WindowBase ::refresh_handler));
160 
161     // We might have obstructed another window, so make sure it
162     // receives a refresh.
163     //
164     // See also Window::refresh().
165     EventQueue::submit(EVT_FORCEREFRESH);
166     EventQueue::submit(EVT_REFRESH);
167     EventQueue::submit(EVT_DOUPDATE);
168 
169     // Change: earlier, it was submitted before
170     // EVT_REFRESH/EVT_DOUPDATE
171     //
172     // This caused problems with the focus manager, if a Label was
173     // updated in the EVT_WINDOW_CLOSE handler.
174     EventQueue::submit(EventEx<WindowBase*>(EVT_WINDOW_CLOSE, this));
175 
176     _shown = false;
177 }
178 
shown() const179 bool WindowBase::shown() const { return _shown; }
180 
redraw_handler(Event & e)181 void WindowBase::redraw_handler(Event& e) {
182     if (realization() != REALIZED) return;
183 
184     assert(e == EVT_REDRAW);
185     assert(_curses_window != 0);
186 
187     _curses_window->clear();
188 }
189 
force_refresh_handler(Event & e)190 void WindowBase::force_refresh_handler(Event& e) {
191     if (realization() != REALIZED) return;
192 
193     assert(e == EVT_FORCEREFRESH);
194     assert(_curses_window != 0);
195 
196     _curses_window->touch();
197 }
198 
refresh_handler(Event & e)199 void WindowBase::refresh_handler(Event& e) {
200     assert(e == EVT_REFRESH);
201     refresh(false);
202 }
203 
resize_handler(Event & e)204 void WindowBase::resize_handler(Event& e) {
205     assert(e == EVT_SIGWINCH);
206     EventEx<Size>& winch = dynamic_cast<EventEx<Size>&>(e);
207     resize(Area(Coordinates(0, 0), winch.data()));
208 }
209 
refresh(bool immediate)210 void WindowBase::refresh(bool immediate) {
211     if (realization() != REALIZED && realization() != REALIZING) return;
212 
213     assert(_curses_window != 0);
214 
215     _curses_window->refresh(immediate);
216 }
217 
resize(const Area & a)218 void WindowBase::resize(const Area& a) {
219     //
220     // Keep in mind: a resize does not refresh!
221     //
222 
223     if (realization() != REALIZED) {
224         // Even if we're not realized, we keep track of the area at
225         // our disposition.
226         //
227         // This was mainly introduced for the screen unlock dialog.
228         _area = a;
229         return;
230     }
231 
232     assert(a.x() > -1);
233     assert(a.y() > -1);
234     assert(a.rows() > 0);
235     assert(a.cols() > 0);
236 
237     unrealize();
238 
239     _area = a;
240 
241     realize();
242 }
243 
realize()244 void WindowBase::realize() {
245     REALIZE_ENTER;
246 
247     assert(_area.x() >= 0);
248     assert(_area.y() >= 0);
249     assert(_area.rows() > 0);
250     assert(_area.cols() > 0);
251 
252     Area _tmp = _area - _margin;
253 
254     if (_tmp.x() < 0 || _tmp.y() < 0 || _tmp.rows() < MIN_WINDOW_ROWS ||
255         _tmp.cols() < MIN_WINDOW_COLS) {
256         realization(UNREALIZED);
257         return;
258     }
259 
260     assert(_curses_window == 0);
261     try {
262         _curses_window = new YACURS::INTERNAL::CursWin(_tmp, _color);
263         _curses_window->scrollok(false);
264         _curses_window->leaveok(true);
265 
266         if (_frame) {
267             _curses_window->box();
268         }
269     } catch (EXCEPTIONS::CursesException&) {
270         if (_curses_window != 0) {
271             delete _curses_window;
272             _curses_window = 0;
273         }
274         realization(UNREALIZED);
275         throw;
276     }
277 
278     REALIZE_LEAVE;
279 }
280