1 /*
2 	scrnman.cc	Screen Manager
3 	Copyright (c) 1997-8,2000,2004 Kriang Lerdsuwanakij
4 	email:		lerdsuwa@users.sourceforge.net
5 
6 	This program is free software; you can redistribute it and/or modify
7 	it under the terms of the GNU General Public License as published by
8 	the Free Software Foundation; either version 2 of the License, or
9 	(at your option) any later version.
10 
11 	This program is distributed in the hope that it will be useful,
12 	but WITHOUT ANY WARRANTY; without even the implied warranty of
13 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 	GNU General Public License for more details.
15 
16 	You should have received a copy of the GNU General Public License
17 	along with this program; if not, write to the Free Software
18 	Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21 #include "scrnman.h"
22 
23 /*************************************************************************
24 	Screen manager base class
25 *************************************************************************/
26 
RequestAddWindow(NCWindowBase * win)27 void	NCScreenManager::RequestAddWindow(NCWindowBase *win)
28 {
29 	winList.push_back(win);
30 }
31 
RequestDeleteWindow(NCWindowBase * win)32 void	NCScreenManager::RequestDeleteWindow(NCWindowBase *win)
33 {
34 	for (sptr_list<NCWindowBase>::iterator iter = winList.begin();
35 	     iter != winList.end(); ++iter) {
36 		if ((*iter)() == win) {
37 			(*iter).release();	// Since this is called from
38 						// the destructor, release
39 						// the ref count to avoid
40 						// calling the same destructor
41 						// again.
42 			winList.erase(iter);
43 			break;
44 		}
45 	}
46 }
47 
RequestResize()48 void	NCScreenManager::RequestResize()
49 {
50 	for (sptr_list<NCWindowBase>::iterator iter = winList.begin();
51 	     iter != winList.end(); ++iter) {
52 		(*iter)->DoResize();
53 	}
54 	RequestRefresh();
55 }
56 
RequestRefresh()57 void	NCScreenManager::RequestRefresh()
58 {
59 	for (sptr_list<NCWindowBase>::iterator iter = winList.begin();
60 	     iter != winList.end(); ++iter) {
61 		(*iter)->DoTouch();
62 	}
63 	clearok(curscr, TRUE);
64 	wrefresh(stdscr);
65 	RequestUpdateAndRestCursor();	// Redraw all windows
66 }
67 
RequestUpdate()68 void	NCScreenManager::RequestUpdate()
69 {
70 	for (sptr_list<NCWindowBase>::iterator iter = winList.begin();
71 	     iter != winList.end(); ++iter) {
72 		(*iter)->DoUpdate();
73 	}
74 	doupdate();			// Transfer to terminal
75 }
76 
RequestRestCursor()77 void	NCScreenManager::RequestRestCursor()
78 {
79 	if (cursorWin)
80 		cursorWin->DoRestCursor();
81 	else
82 		DoRestCursor();
83 	doupdate();			// Change cursor position
84 }
85 
RequestUpdateAndRestCursor()86 void	NCScreenManager::RequestUpdateAndRestCursor()
87 {
88 	for (sptr_list<NCWindowBase>::iterator iter = winList.begin();
89 	     iter != winList.end(); ++iter) {
90 		(*iter)->DoUpdate();
91 	}
92 	RequestRestCursor();
93 }
94 
RequestChangeMode(int mode)95 void	NCScreenManager::RequestChangeMode(int mode)
96 {
97 				// Changing sizes for all window is very
98 				// costly, make sure we really need it
99 
100 	if (mode != geoMan.GetMode()) {		// Change required
101 		geoMan.SetMode(mode);
102 		RequestResize();		// Notify all window
103 	}
104 }
105 
SetFocus(NCWindowBase * win)106 void	NCScreenManager::SetFocus(NCWindowBase *win)
107 {
108 	if (win) {
109 		focusStack.push_back(focusWin);
110 		focusWin = win;
111 	}
112 	else {
113 		if (focusStack.size()) {
114 						// Release ref count
115 			focusWin = focusStack.back().release();
116 			focusStack.pop_back();
117 		}
118 		else
119 			focusWin = 0;
120 	}
121 }
122 
SetCursor(NCWindowBase * win)123 void	NCScreenManager::SetCursor(NCWindowBase *win)
124 {
125 	if (win) {
126 		cursorStack.push_back(cursorWin);
127 		cursorWin = win;
128 	}
129 	else {
130 		if (cursorStack.size()) {
131 						// Release ref count
132 			cursorWin = cursorStack.back().release();
133 			cursorStack.pop_back();
134 		}
135 		else
136 			cursorWin = 0;
137 	}
138 }
139 
NCScreenManager(NCGeometryManagerBase & geoMan_)140 NCScreenManager::NCScreenManager(NCGeometryManagerBase &geoMan_) :
141 				geoMan(geoMan_),
142 				focusWin(NULL), cursorWin(NULL),
143 				permWin(NULL), ctrlWin(NULL),
144 				winList(), focusStack(),
145 				cursorStack()
146 {
147 	SizeChangeNotify::InstallObject(this);
148 	NCurses::SetNotify(SizeChangeNotify::Callback);
149 }
150 
151 unsigned NCScreenManager::mouseScrollRate = 2;
152 
~NCScreenManager()153 NCScreenManager::~NCScreenManager()
154 {
155 }
156 
ReadKeyboard()157 int	NCScreenManager::ReadKeyboard()
158 {
159 	int	c;
160 	if (permWin)
161 		c = wgetch(permWin->win);
162 	else
163 		c = wgetch(focusWin->win);
164 
165 #ifdef NCURSES_MOUSE_VERSION
166 	if (c == KEY_MOUSE)
167 		getmouse(&mouseEvent);
168 #endif
169 	return c;
170 }
171 
KeyboardLoop()172 void	NCScreenManager::KeyboardLoop()
173 {
174 	NCWindowBase *prevMousePressedWin = 0;
175 	MEVENT prevMouseEvent;
176 #ifdef NCURSES_MOUSE_VERSION
177 	if (mouseScrollRate > 0)
178 		halfdelay(mouseScrollRate);
179 #endif
180 	for ( ; ; ) {
181 		if (focusWin) {
182 			int	c = ReadKeyboard();
183 #ifdef NCURSES_MOUSE_VERSION
184 			if (c == KEY_MOUSE) {
185 				// FIXME: Locate top window in case of overlapping windows
186 				for (sptr_list<NCWindowBase>::iterator iter = winList.begin();
187 				     iter != winList.end(); ++iter) {
188 					(*iter)->DoTouch();
189 					if ((*iter)->IsInWin(mouseEvent.x, mouseEvent.y)) {
190 						if (mouseEvent.bstate
191 						    & (BUTTON1_PRESSED | BUTTON2_PRESSED
192 						       | BUTTON3_PRESSED | BUTTON4_PRESSED)) {
193 							if (prevMousePressedWin) {
194 								// Replace BUTTON?_PRESSED with
195 								// BUTTON?_RELEASED
196 								if (prevMouseEvent.bstate & BUTTON1_PRESSED) {
197 									prevMouseEvent.bstate &= ~BUTTON1_PRESSED;
198 									prevMouseEvent.bstate |= BUTTON1_RELEASED;
199 								}
200 								if (prevMouseEvent.bstate & BUTTON2_PRESSED) {
201 									prevMouseEvent.bstate &= ~BUTTON2_PRESSED;
202 									prevMouseEvent.bstate |= BUTTON2_RELEASED;
203 								}
204 								if (prevMouseEvent.bstate & BUTTON3_PRESSED) {
205 									prevMouseEvent.bstate &= ~BUTTON3_PRESSED;
206 									prevMouseEvent.bstate |= BUTTON3_RELEASED;
207 								}
208 								if (prevMouseEvent.bstate & BUTTON4_PRESSED) {
209 									prevMouseEvent.bstate &= ~BUTTON4_PRESSED;
210 									prevMouseEvent.bstate |= BUTTON4_RELEASED;
211 								}
212 								prevMousePressedWin->ProcessMouse(prevMouseEvent);
213 							}
214 							prevMousePressedWin = (*iter)();
215 							prevMouseEvent = mouseEvent;
216 						}
217 
218 						(*iter)->ProcessMouse(mouseEvent);
219 
220 						if (mouseEvent.bstate
221 						    & (BUTTON1_RELEASED | BUTTON2_RELEASED
222 						       | BUTTON3_RELEASED | BUTTON4_RELEASED))
223 							prevMousePressedWin = 0;
224 						break;
225 					}
226 				}
227 			}
228 			else if (c == ERR) {
229 				if (prevMousePressedWin)
230 					prevMousePressedWin->ProcessMouse(prevMouseEvent);
231 			}
232 			else {
233 #endif
234 				prevMousePressedWin = 0;
235 				focusWin->ProcessKey(c);
236 #ifdef NCURSES_MOUSE_VERSION
237 			}
238 #endif
239 		}
240 		else
241 			break;		// Exit loop if focusWin is NULL
242 	}
243 }
244 
DoRestCursor()245 void	NCScreenManager::DoRestCursor()
246 {
247 }
248 
249 NCScreenManager	*NCScreenManager::SizeChangeNotify::notifyObject = NULL;
250 
Callback()251 void	NCScreenManager::SizeChangeNotify::Callback()
252 {
253 	if (notifyObject)
254 		notifyObject->RequestResize();
255 }
256 
InstallObject(NCScreenManager * obj)257 void	NCScreenManager::SizeChangeNotify::InstallObject(NCScreenManager *obj)
258 {
259 	notifyObject = obj;
260 }
261 
262 /*************************************************************************
263 	Window base class
264 *************************************************************************/
265 
DoTouch()266 void	NCWindowBase::DoTouch()
267 {
268 	if (win)
269 		touchwin(win);
270 }
271 
DoRestCursor()272 void	NCWindowBase::DoRestCursor()
273 {
274 }
275 
ProcessCtrl(char,bool)276 void	NCWindowBase::ProcessCtrl(char /*c*/, bool /*set*/)
277 {
278 }
279 
ProcessKey(int)280 void	NCWindowBase::ProcessKey(int /*ch*/)
281 {
282 }
283 
284 #ifdef NCURSES_MOUSE_VERSION
ProcessMouse(MEVENT &)285 void	NCWindowBase::ProcessMouse(MEVENT & /*event*/)
286 {
287 }
288 #endif
289 
IsInWin(int x,int y)290 bool	NCWindowBase::IsInWin(int x, int y)
291 {
292 	return (x >= GetX1() && x <= GetX2() && y >= GetY1() && y <= GetY2());
293 }
294 
InitGeometry()295 void	NCWindowBase::InitGeometry()
296 {
297 	scrnMan.RequestWinRect(id, rect);
298 }
299 
Init()300 void	NCWindowBase::Init()
301 {
302 	InitGeometry();
303 	if (!registered) {
304 		scrnMan.RequestAddWindow(this);
305 		registered = true;
306 	}
307 }
308 
NCWindowBase(NCScreenManager & scrnMan_,int id_)309 NCWindowBase::NCWindowBase(NCScreenManager &scrnMan_, int id_) : id(id_),
310 							win(NULL), rect(),
311 							scrnMan(scrnMan_),
312 							registered(false)
313 {
314 }
315 
~NCWindowBase()316 NCWindowBase::~NCWindowBase()
317 {
318 	if (registered)
319 		scrnMan.RequestDeleteWindow(this);
320 	if (win)
321 		delwin(win);
322 }
323 
KeyboardLoop()324 void	NCWindowBase::KeyboardLoop()
325 {
326 	scrnMan.KeyboardLoop();
327 }
328