1 #include <cassert>
2 #include <iostream>
3 #include <map>
4 #include <string>
5 #include <vector>
6 #include <cmath>
7 #include <SDL_types.h>
8 
9 #include "context_menu.h"
10 #include "elconfig.h"
11 #include "elwindows.h"
12 #include "font.h"
13 #include "gamewin.h"
14 #include "gl_init.h"
15 #include "interface.h"
16 #include "sound.h"
17 
18 //
19 //  Implements a simple context menu system using the standard el windows.
20 //  Intended to be a familiar GUI context menu activated via a right mouse click.
21 //  At its simplest, the user code just needs to create a new context object
22 //  providing a callback function for when an option is selected or using boolean
23 //  only options.  The Container class provides a creation/destruction/management/access
24 //  wrapper.
25 //  Author bluap/pjbroad March/April 2008
26 //
27 namespace cm
28 {
29 	//
30 	//  The context menu class.  Each manu uses a seperate object stored in
31 	//	the Container class.  One el window is used for all menus.  This class
32 	//	implements the menu display, option hight-lighting, checkbox options and
33 	//	menu option selection.  It also stores size, colour and state information.
34 	//
35 	class Menu
36 	{
37 		public:
38 			Menu(const char *menu_list, int (*handler)(window_info *, int, int, int, int));
39 			int show(int cm_widget_id);
40 			int display(window_info *win, int mx, int my, Uint32 flags);
41 			int click(window_info *win, int mx, int my, Uint32 flags);
42 			int set(const char *menu_list, int (*handler)(window_info *, int, int, int, int));
43 			int add(const char *menu_list, int (*handler)(window_info *, int, int, int, int));
set_pre_show_handler(void (* handler)(window_info *,int,int,int,window_info *))44 			void set_pre_show_handler(void (*handler)(window_info *, int, int, int, window_info *)) { pre_show_handler = handler; }
45 			int set_sizes(int border, int text_border, int line_sep, float zoom);
46 			int set_colour(size_t cm_id, enum CM_COLOUR_NAME colour_name, float r, float g, float b);
47 			int change_line(size_t line_index, const char *new_entry);
48 			int bool_line(size_t line_index, int *control_var, const char *config_name);
49 			int grey_line(size_t line_index, bool is_grey);
50 			void show_lines(size_t my_id);
set_data(void * data)51 			void set_data(void *data) { data_ptr = data; }
get_data(void) const52 			void *get_data(void) const { return data_ptr; }
53 			float scaled_value(float val) const;
54 
55 		private:
56 			int resize(void);
bool_box_size(void) const57 			int bool_box_size(void) const { return 15; }
58 			int border, text_border, line_sep;
59 			float zoom, bool_tick_width;
60 			int opened_mouse_x, opened_mouse_y;
61 			int (*handler)(window_info *, int, int, int, int);
62 			void (*pre_show_handler)(window_info *, int, int, int, window_info *);
63 			void *data_ptr;
64 			int width, height;
65 			int selection;
66 			bool menu_has_bools;
67 			class Menu_Line
68 			{
69 				public:
Menu_Line(const std::string & _text)70 					Menu_Line(const std::string& _text) : text(_text), control_var(0), config_name(0), is_grey(false), is_separator(false) {}
71 					std::string text;
72 					int *control_var;
73 					const char *config_name;
74 					bool is_grey;
75 					bool is_separator;
76 			};
77 			std::vector<Menu_Line> menu_lines;
78 			class myRGB
79 			{
80 				public:
myRGB(void)81 					myRGB(void) : r(0.0), g(0.0), b(0.0) {}
set(float _r,float _g,float _b)82 					void set(float _r, float _g, float _b) { r=_r; g=_g; b=_b; }
83 					float r, g, b;
84 			};
85 			myRGB highlight_top;
86 			myRGB highlight_bottom;
87 			myRGB text_colour;
88 			myRGB grey_colour;
89 	};
90 
91 
92 	//
93 	//  A simple container class for the context menus.
94 	//  Provides creation and destruction of context menus
95 	//  and any other 'across all menu' functions
96 	//  There can only be one instance!
97 	//
98 	class Container
99 	{
100 		public:
101 			~Container();
102 
103 			// Return the singleton instance of this container
get_instance()104 			static Container& get_instance()
105 			{
106 				static Container container;
107 				return container;
108 			}
109 
110 			size_t create(const char *menu_list, int (*handler)(window_info *, int, int, int, int ));
111 			int destroy(size_t cm_id);
112 			int pre_show_check(Uint32 flags);
113 			void post_show_check(bool force);
114 			int show_if_active(int window_id);
115 			int show_direct(size_t cm_id, int window_id, int widget_id);
set(size_t cm_id,const char * menu_list,int (* handler)(window_info *,int,int,int,int))116 			int set(size_t cm_id, const char *menu_list, int (*handler)(window_info *, int, int, int, int)) { if (!valid(cm_id)) return 0; return menus[cm_id]->set(menu_list, handler); }
add(size_t cm_id,const char * menu_list,int (* handler)(window_info *,int,int,int,int))117 			int add(size_t cm_id, const char *menu_list, int (*handler)(window_info *, int, int, int, int)) { if (!valid(cm_id)) return 0; return menus[cm_id]->add(menu_list, handler); }
set_pre_show_handler(size_t cm_id,void (* handler)(window_info *,int,int,int,window_info *))118 			int set_pre_show_handler(size_t cm_id, void (*handler)(window_info *, int, int, int, window_info *)) { if (!valid(cm_id)) return 0; menus[cm_id]->set_pre_show_handler(handler); return 1; }
bool_line(size_t cm_id,size_t line_index,int * control_var,const char * config_name)119 			int bool_line(size_t cm_id, size_t line_index, int *control_var, const char *config_name) { if (!valid(cm_id)) return 0; return menus[cm_id]->bool_line(line_index, control_var, config_name); }
grey_line(size_t cm_id,size_t line_index,bool is_grey)120 			int grey_line(size_t cm_id, size_t line_index, bool is_grey) { if (!valid(cm_id)) return 0; return menus[cm_id]->grey_line(line_index, is_grey); }
set_sizes(size_t cm_id,int border,int text_border,int line_sep,float zoom)121 			int set_sizes(size_t cm_id, int border, int text_border, int line_sep, float zoom) { if (!valid(cm_id)) return 0; return menus[cm_id]->set_sizes(border, text_border, line_sep, zoom); }
set_colour(size_t cm_id,enum CM_COLOUR_NAME colour_name,float r,float g,float b)122 			int set_colour(size_t cm_id, enum CM_COLOUR_NAME colour_name, float r, float g, float b) { if (!valid(cm_id)) return 0; return menus[cm_id]->set_colour(cm_id, colour_name, r, g, b); }
add_window(size_t cm_id,int window_id)123 			int add_window(size_t cm_id, int window_id) { if (!valid(cm_id)) return 0; full_windows.insert(std::pair<int, size_t>(window_id, cm_id)); return 1; }
add_region(size_t cm_id,int window_id,int posx,int posy,int lenx,int leny)124 			int add_region(size_t cm_id, int window_id, int posx, int posy, int lenx, int leny) { if (!valid(cm_id)) return 0; window_regions.insert(std::pair<int, Region>(window_id, Region(cm_id, posx, posy, lenx, leny))); return 1; }
add_widget(size_t cm_id,int window_id,int widget_id)125 			int add_widget(size_t cm_id, int window_id, int widget_id) { if (!valid(cm_id)) return 0; window_widgets.insert(std::pair<int, Widget>(window_id, Widget(cm_id, widget_id))); return 1; }
remove_window(int window_id)126 			int remove_window(int window_id) {  if (full_windows.erase(window_id)) return 1; return 0;}
remove_regions(int window_id)127 			int remove_regions(int window_id) { if (window_regions.erase(window_id)) return 1; return 0; }
128 			int remove_widget(int window_id, int widget_id);
get_active_window_id(void) const129 			int get_active_window_id(void) const { return active_window_id; }
get_active_widget_id(void) const130 			int get_active_widget_id(void) const { return active_widget_id; }
valid(size_t cm_id) const131 			bool valid(size_t cm_id) const { return cm_id<menus.size() && menus[cm_id]; }
132 			size_t window_shown(void) const;
133 			void showinfo(void);
get_data(size_t cm_id) const134 			void *get_data(size_t cm_id) const { if (!valid(cm_id)) return 0; return menus[cm_id]->get_data(); }
set_data(size_t cm_id,void * data)135 			void set_data(size_t cm_id, void *data) { if (valid(cm_id)) menus[cm_id]->set_data(data); }
get_current_scale(void) const136 			float get_current_scale(void) const { return windows_list.window[cm_window_id].current_scale; }
137 
138 		private:
139 			Container();
140 			Container(const Container&) = delete;
141 			Container& operator=(const Container&) = delete;
142 
143 			class Region	//  Wrapper for window region activation area.
144 			{
145 				public:
Region(size_t _cm_id,int _pos_x,int _pos_y,int _len_x,int _len_y)146 					Region(size_t _cm_id, int _pos_x, int _pos_y, int _len_x, int _len_y)
147 						: cm_id(_cm_id), pos_x(_pos_x), pos_y(_pos_y), len_x(_len_x), len_y(_len_y) {}
148 					size_t cm_id;
149 					int pos_x, pos_y, len_x, len_y;
150 			};
151 			typedef std::multimap<int, Region> REG_MM;
152 			REG_MM window_regions;
153 			class Widget	//  Wrapper for window widget activation area.
154 			{
155 				public:
Widget(size_t _cm_id,int _widget_id)156 					Widget(size_t _cm_id, int _widget_id)
157 						: cm_id(_cm_id), widget_id(_widget_id) {}
158 					size_t cm_id;
159 					int widget_id;
160 			};
161 			typedef std::multimap<int, Widget > WID_MM;
162 			WID_MM window_widgets;
163 			int cm_window_id;
164 			int active_window_id;
165 			int active_widget_id;
166 			std::vector<Menu*> menus;
167 			std::map<int, size_t> full_windows; // <window_id, cm_id>
168 			bool menu_opened;
169 	};
170 
171 	// Generic el windows callback for clicks in a context menu.
click_context_handler(window_info * win,int mx,int my,Uint32 flags)172 	extern "C" int click_context_handler(window_info *win, int mx, int my, Uint32 flags)
173 	{
174 		Menu *thismenu = (Menu *)win->data;
175 		int return_value = thismenu->click(win, mx, my, flags);
176 		if (return_value)
177 			do_click_sound();
178 		else
179 			do_alert1_sound();
180 		return return_value;
181 	}
182 
183 
184 	// Generic el windows callback for displaying a context menu.
display_context_handler(window_info * win,int mx,int my,Uint32 flags)185 	extern "C" int display_context_handler(window_info *win, int mx, int my, Uint32 flags)
186 	{
187 		Menu *thismenu = (Menu *)win->data;
188 		return thismenu->display(win, mx, my, flags);
189 	}
190 
191 
192 	// safely get a window_info pointer from a window_id
window_info_from_id(int window_id)193 	window_info * window_info_from_id(int window_id)
194 	{
195 		if ((window_id > -1) && (window_id < windows_list.num_windows))
196 			return &windows_list.window[window_id];
197 		else
198 			return NULL;
199 	}
200 
201 
202 	// constructor - create the context menu window and initialise containers
Container()203 	Container::Container()
204 		: cm_window_id(-1), active_window_id(-1), active_widget_id(-1), menu_opened(false)
205 	{
206 		menus.resize(20,0);
207 		if ((cm_window_id = create_window("Context Menu", -1, 0, 0, 0, 0, 0,
208 				ELW_USE_UISCALE|ELW_SWITCHABLE_OPAQUE|ELW_USE_BACKGROUND|ELW_USE_BORDER|ELW_ALPHA_BORDER)) == -1)
209 			return;
210 		set_window_handler(cm_window_id, ELW_HANDLER_DISPLAY, (int (*)())&display_context_handler );
211 		set_window_handler(cm_window_id, ELW_HANDLER_CLICK, (int (*)())&click_context_handler );
212 	}
213 
214 
215 	// destructor - destory all instances of context menus and the context menu window
~Container(void)216 	Container::~Container(void)
217 	{
218 		for (size_t i=0; i<menus.size(); ++i)
219 		{
220 			if (menus[i])
221 			{
222 				delete menus[i];
223 				menus[i] = 0;
224 			}
225 		}
226 		destroy_window(cm_window_id);
227 	}
228 
229 
230 	// create a new context menu object
create(const char * menu_list,int (* handler)(window_info *,int,int,int,int))231 	size_t Container::create(const char *menu_list, int (*handler)(window_info *, int, int, int, int ))
232 	{
233 		Menu *temp = new Menu(menu_list, handler);
234 		// find an unused slot ...
235 		for (size_t i=0; i<menus.size(); ++i)
236 		{
237 			if (!menus[i])
238 			{
239 				menus[i] = temp;
240 				return i;
241 			}
242 		}
243 		// ... or create some more space
244 		size_t oldsize = menus.size();
245 		menus.resize(oldsize*2,0);
246 		menus[oldsize] = temp;
247 		return oldsize;
248 	}
249 
250 
251 	// destroy the specified context menu object, removing all the activation points too
destroy(size_t cm_id)252 	int Container::destroy(size_t cm_id)
253 	{
254 		if (!valid(cm_id))
255 			return 0;
256 
257 		bool found_entry = false;
258 
259 		// remove all full windows linked to this context menu
260 		do
261 		{
262 			found_entry = false;
263 			for (std::map<int, size_t>::iterator itp = full_windows.begin(); itp != full_windows.end(); ++itp)
264 			{
265 				if (itp->second == cm_id)
266 				{
267 					full_windows.erase(itp);
268 					found_entry = true;
269 					break;
270 				}
271 			}
272 		} while (found_entry);
273 
274 
275 		// remove all regions linked to this context menu
276 		do
277 		{
278 			found_entry = false;
279 			for (REG_MM::iterator itp = window_regions.begin(); itp != window_regions.end(); ++itp)
280 			{
281 				if (itp->second.cm_id == cm_id)
282 				{
283 					window_regions.erase(itp);
284 					found_entry = true;
285 					break;
286 				}
287 			}
288 		} while (found_entry);
289 
290 
291 		// remove all widgets linked to this context menu
292 		do
293 		{
294 			found_entry = false;
295 			for (WID_MM::iterator itp = window_widgets.begin(); itp != window_widgets.end(); ++itp)
296 			{
297 				if (itp->second.cm_id == cm_id)
298 				{
299 					window_widgets.erase(itp);
300 					found_entry = true;
301 					break;
302 				}
303 			}
304 		} while (found_entry);
305 
306 		delete menus[cm_id];
307 		menus[cm_id] = 0;
308 		return 1;
309 
310 	} // end Container::destroy()
311 
312 
313 	// do the pre show checks and return the activation state, e.g. if mouse right-clicked
pre_show_check(Uint32 flags)314 	int Container::pre_show_check(Uint32 flags)
315 	{
316 		int cm_to_activate = flags & ELW_RIGHT_MOUSE;
317 		if (cm_to_activate && ((flags & KMOD_SHIFT) || (flags & KMOD_ALT) || (flags & KMOD_CTRL)))
318 			cm_to_activate = 0;  // exclude right clicks with modifier keys pressed
319 		menu_opened = false;
320 		return cm_to_activate;
321 	}
322 
323 
324 	// hide the window if its's open and reset the active window/widget values
post_show_check(bool force)325 	void Container::post_show_check(bool force)
326 	{
327 		// don't close a just (show_direct()) opened context menu
328 		if (!force && menu_opened)
329 			select_window(cm_window_id);
330 		else
331 		{
332 			hide_window(cm_window_id);
333 			active_window_id = active_widget_id = -1;
334 		}
335 		menu_opened = false;
336 	}
337 
338 
339 	// check if the specified window has any activation points then open the relevant context menu
show_if_active(int window_id)340 	int Container::show_if_active(int window_id)
341 	{
342 		// check if we're right clicking the title of a window that has a context menu
343 		if (window_id >= 0 && window_id < windows_list.num_windows)
344 		{
345 			window_info *win = &windows_list.window[window_id];
346 			int y = mouse_y - win->cur_y;
347 			if (y < 0 && valid(win->cm_id) &&  mouse_in_window(window_id, mouse_x, mouse_y))
348 				return show_direct(win->cm_id, window_id, -1);
349 		}
350 
351 		// check we're in the specified window
352 		if (mouse_in_window(window_id, mouse_x, mouse_y) < 1)
353 			return 0;
354 
355 		// check if this window has any widget activation settings and open if mouse in that widget
356 		{
357 			std::pair< WID_MM::iterator, WID_MM::iterator> itp = window_widgets.equal_range(window_id);
358 			for (WID_MM::iterator it = itp.first; it != itp.second; ++it)
359 			{
360 				window_info *win = window_info_from_id(window_id);
361 				widget_list *wid = widget_find(window_id, it->second.widget_id);
362 				assert(wid!=NULL && win!=NULL);
363 				if ((mouse_x > win->cur_x + wid->pos_x) && (mouse_x <= win->cur_x + wid->pos_x + wid->len_x) &&
364 		    		(mouse_y > win->cur_y + wid->pos_y) && (mouse_y <= win->cur_y + wid->pos_y + wid->len_y))
365 					return show_direct(it->second.cm_id, window_id, it->second.widget_id);
366 			}
367 		}
368 
369 		// check if this window has any active regions and open if mouse in that region
370 		{
371 			std::pair< REG_MM::iterator, REG_MM::iterator> itp = window_regions.equal_range(window_id);
372 			for (REG_MM::iterator it = itp.first; it != itp.second; ++it)
373 			{
374 				window_info *win = window_info_from_id(window_id);
375 				assert(win!=NULL);
376 				Region *cr = &it->second;
377 				if ((mouse_x > win->cur_x + cr->pos_x) && (mouse_x < win->cur_x + cr->pos_x + cr->len_x) &&
378 		    		(mouse_y > win->cur_y + cr->pos_y) && (mouse_y < win->cur_y + cr->pos_y + cr->len_y))
379 					return show_direct(cr->cm_id, window_id, -1);
380 			}
381 		}
382 
383 		// check if this window has a full-window context menu - do this last to allow others within window
384 		std::map<int,size_t>::iterator fwit = full_windows.find(window_id);
385 		if (fwit != full_windows.end())
386 			return show_direct(fwit->second, window_id, -1);
387 
388 		return 0;
389 
390 	} // end Container::show_if_active()
391 
392 
393 	// directly open the specified conetext menu, use specified window/widget id as if activated
show_direct(size_t cm_id,int window_id,int widget_id)394 	int Container::show_direct(size_t cm_id, int window_id, int widget_id)
395 	{
396 		if (!valid(cm_id))
397 			return 0;
398 		menu_opened = true;
399 		active_window_id = window_id;
400 		active_widget_id = widget_id;
401 		return menus[cm_id]->show(cm_window_id);
402 	}
403 
404 
405 	// remove any activation for the specifed window/widget
remove_widget(int window_id,int widget_id)406 	int Container::remove_widget(int window_id, int widget_id)
407 	{
408 		std::pair< WID_MM::iterator, WID_MM::iterator> itp = window_widgets.equal_range(window_id);
409 		for (WID_MM::iterator it = itp.first; it != itp.second; ++it)
410 		{
411 			if (it->second.widget_id == widget_id)
412 			{
413 				window_widgets.erase(it);
414 				return 1;
415 			}
416 		}
417 		return 0;
418 	}
419 
420 
421 	// return the id the currently open context menu or CM_INIT_VALUE is none open
window_shown(void) const422 	size_t Container::window_shown(void) const
423 	{
424 		window_info * win = window_info_from_id(cm_window_id);
425 		if (!get_show_window(cm_window_id) || win == NULL)
426 			return CM_INIT_VALUE;
427 
428 		Menu *current_menu = (Menu *)win->data;
429 		for (size_t i=0; i<menus.size(); i++)
430 			if (current_menu == menus[i])
431 				return i;
432 		return CM_INIT_VALUE;
433 	}
434 
435 
436 	// for debug - display info on status of container object
showinfo(void)437 	void Container::showinfo(void)
438 	{
439 		std::cout << "\nContext menu information:" << std::endl;
440 
441 		std::cout << "\nMenus:-" << std::endl;
442 		for (size_t i=0; i<menus.size(); ++i)
443 			if (menus[i])
444 				menus[i]->show_lines(i);
445 
446 		std::cout << "\nFull Windows:-" << std::endl;
447 		for (std::map<int, size_t>::iterator itp = full_windows.begin(); itp != full_windows.end(); ++itp)
448 		{
449 			window_info *win = window_info_from_id(itp->first);
450 			std::cout << "  window_id=" << itp->first << " name=[" << ((win!=NULL)?win->window_name:"")
451 					  << "] menu_id=" << itp->second << std::endl;
452 		}
453 
454 		std::cout << "\nWindow Regions:-" << std::endl;
455 		for (REG_MM::iterator itp = window_regions.begin(); itp != window_regions.end(); ++itp)
456 		{
457 			window_info *win = window_info_from_id(itp->first);
458 			std::cout << "  window_id=" << itp->first << " name=[" << ((win!=NULL)?win->window_name:"")
459 					  << "] region=(" << itp->second.pos_x << ", " << itp->second.pos_y << ", " << itp->second.len_x
460 					  << ", " << itp->second.len_y << ")" << " menu_id=" << itp->second.cm_id << std::endl;
461 		}
462 
463 		std::cout << "\nWindow Widgets:-" << std::endl;
464 		for (WID_MM::iterator itp = window_widgets.begin(); itp != window_widgets.end(); ++itp)
465 		{
466 			window_info *win = window_info_from_id(itp->first);
467 			std::cout << "  window_id=" << itp->first << " name=[" << ((win!=NULL)?win->window_name:"")
468 					  << "] widget=" << itp->second.widget_id << " menu_id=" << itp->second.cm_id << std::endl;
469 		}
470 
471 	} // end showinfo()
472 
473 
474 
475 
476 
477 	// Constructor - set default values for new menu
Menu(const char * menu_list,int (* handler)(window_info *,int,int,int,int))478 	Menu::Menu(const char *menu_list, int (*handler)(window_info *, int, int, int, int))
479 		: border(5), text_border(5), line_sep(3), zoom(0.8), data_ptr(0), selection(-1), menu_has_bools(false)
480 	{
481 		set(menu_list, handler);
482 		pre_show_handler = 0;
483 		highlight_top.set(gui_invert_color[0], gui_invert_color[1], gui_invert_color[2]);
484 		highlight_bottom.set(gui_color[0], gui_color[1], gui_color[2]);
485 		text_colour.set(1.0f, 1.0f, 1.0f);
486 		grey_colour.set(0.7f, 0.7f, 0.7f);
487 	}
488 
489 
490 	// Set or replace the menu items
set(const char * menu_list,int (* handler)(window_info *,int,int,int,int))491 	int Menu::set(const char *menu_list, int (*handler)(window_info *, int, int, int, int))
492 	{
493 		this->handler = handler;
494 		menu_lines.clear();
495 		return add(menu_list, handler);
496 	}
497 
498 
499 	// Add new the menu items and optionally replace the handler
add(const char * menu_list,int (* handler)(window_info *,int,int,int,int))500 	int Menu::add(const char *menu_list, int (*handler)(window_info *, int, int, int, int))
501 	{
502 		if (handler)
503 			this->handler = handler;
504 		if (menu_list == NULL)
505 			menu_list = "";
506 		std::string menu_string(menu_list);
507 		for (size_t pos = 0; pos != std::string::npos;)
508 		{
509 			size_t endpos = menu_string.find('\n', pos);
510 			std::string new_entry = menu_string.substr(pos, endpos-pos);
511 			if (!new_entry.empty())
512 			{
513 				Menu_Line newline(new_entry);
514 				if (new_entry == std::string("--"))
515 					newline.is_separator = true;
516 				menu_lines.push_back(newline);
517 			}
518 			if (endpos == std::string::npos)
519 				break;
520 			pos = endpos+1;
521 		}
522 		return resize();
523 	}
524 
525 
526 	//  Change a single menu item if it exists
change_line(size_t line_index,const char * new_entry)527 	int Menu::change_line(size_t line_index, const char *new_entry)
528 	{
529 		if (line_index >= menu_lines.size())
530 			return 0;
531 		menu_lines[line_index].text = std::string(new_entry);
532 		menu_lines[line_index].control_var = 0;
533 		menu_lines[line_index].config_name = 0;
534 		menu_lines[line_index].is_grey = menu_lines[line_index].is_separator = false;
535 		if (new_entry == std::string("--"))
536 			menu_lines[line_index].is_separator = true;
537 		return 1;
538 	}
539 
540 
541 	//  Make a line in the menu an off/on option, control_var determines state
bool_line(size_t line_index,int * control_var,const char * config_name)542 	int Menu::bool_line(size_t line_index, int *control_var, const char *config_name)
543 	{
544 		if (line_index >= menu_lines.size())
545 			return 0;
546 		menu_lines[line_index].control_var = control_var;
547 		menu_lines[line_index].config_name = config_name;
548 		if (!menu_has_bools)
549 		{
550 			menu_has_bools = true;
551 			resize();
552 		}
553 		return 1;
554 	}
555 
556 
557 	//  grey/ungrey a menu line
grey_line(size_t line_index,bool is_grey)558 	int Menu::grey_line(size_t line_index, bool is_grey)
559 	{
560 		if (line_index >= menu_lines.size())
561 			return 0;
562 		menu_lines[line_index].is_grey = is_grey;
563 		return 1;
564 	}
565 
566 
567 	//  Calculate the height/width of the context menu and resize the window
resize(void)568 	int Menu::resize(void)
569 	{
570 		const float scale = scaled_value(1.0);
571 		float fwidth = 0, fheight = 0;
572 		for (size_t i=0; i<menu_lines.size(); i++)
573 		{
574 			const unsigned char* thetext = reinterpret_cast<const unsigned char*>(menu_lines[i].text.c_str());
575 			int str_width = get_string_width_zoom(thetext, UI_FONT, scale);
576 			if (str_width > fwidth)
577 				fwidth = str_width;
578 			if (menu_lines[i].is_separator)
579 				fheight += get_line_height(UI_FONT, 0.5 * scale);
580 			else
581 				fheight += get_line_height(UI_FONT, scale) + line_sep;
582 		}
583 		bool_tick_width = (menu_has_bools)? scaled_value(bool_box_size()+text_border) : 0;
584 		fwidth += bool_tick_width + (border + text_border) * 2;
585 		fheight += border * 2;
586 		height = static_cast<int>(fheight+0.5);
587 		width = static_cast<int>(fwidth+0.5);
588 		return 1;
589 	}
590 
591 
592 	//  show the selected context menu
show(int cm_window_id)593 	int Menu::show(int cm_window_id)
594 	{
595 		// if the window is already displayed, hide it then return
596 		if (get_show_window(cm_window_id))
597 		{
598 			hide_window(cm_window_id);
599 			return 1;
600 		}
601 
602 		// save the mouse position for the callback
603 		int wx = opened_mouse_x = mouse_x;
604 		int wy = opened_mouse_y = mouse_y;
605 
606 		// resize in case scaling changed
607 		resize();
608 
609 		// move the window, one corner anchored to the mouse click position, so its on screen
610 		if (wx+width > window_width)
611 			wx -= width;
612 		if (wy+height > window_height)
613 			wy -= height;
614 		move_window(cm_window_id, -1, 0, wx, wy);
615 
616 		// make sure the window is updated with this instances size and data
617 		windows_list.window[cm_window_id].data = this;
618 		resize_window(cm_window_id, width, height);
619 
620 		// parent_win will be NULL if we don't have one
621 		const Container& container = Container::get_instance();
622 		window_info *parent_win = window_info_from_id(container.get_active_window_id());
623 
624 		// copy any parent window opacity to the context menu
625 		if (parent_win != NULL && (parent_win->flags & ELW_SWITCHABLE_OPAQUE))
626 			windows_list.window[cm_window_id].opaque = parent_win->opaque;
627 		else
628 			// otherwise use the default setting
629 			windows_list.window[cm_window_id].opaque = opaque_window_backgrounds;
630 
631 		/* call any registered pre_show handler */
632 		if (pre_show_handler)
633 		{
634 			// if we have a parent window, the mouse position is the original position that opened the menu
635 			if (parent_win != NULL)
636 			{
637 				int parent_win_x = opened_mouse_x - parent_win->cur_x;
638 				int parent_win_y = opened_mouse_y - parent_win->cur_y;
639 				(*pre_show_handler)(parent_win, container.get_active_widget_id(),
640 					parent_win_x, parent_win_y, window_info_from_id(cm_window_id));
641 			}
642 			else
643 				(*pre_show_handler)(NULL, 0, 0, 0, window_info_from_id(cm_window_id));
644 		}
645 
646 		show_window(cm_window_id);
647 		select_window(cm_window_id);
648 
649 		return 1;
650 	}
651 
652 
653 	//  display the menu options with highlight if mouse is over one
display(window_info * win,int mx,int my,Uint32 flags)654 	int Menu::display(window_info *win, int mx, int my, Uint32 flags)
655 	{
656 		CHECK_GL_ERRORS();
657 		float currenty = border + line_sep;
658 		float scale = scaled_value(1.0);
659 		int line_height = get_line_height(win->font_category, scale);
660 		int box_size = std::round(scaled_value(bool_box_size()));
661 		float line_step = line_sep + max2i(box_size, line_height);
662 
663 		selection = -1;
664 		for (size_t i=0; i<menu_lines.size(); ++i)
665 		{
666 			// if the mouse is over a valid line, draw the highlight and select line
667 			if (!menu_lines[i].is_grey && !menu_lines[i].is_separator &&
668 			  (mouse_y > win->cur_y + currenty - line_sep) &&
669 			  (mouse_y < win->cur_y + currenty + line_step - line_sep) &&
670 			  (mouse_x > win->cur_x + border) &&
671 			  (mouse_x < win->cur_x + width - border))
672 			{
673 				glDisable(GL_TEXTURE_2D);
674 				glBegin(GL_QUADS);
675 				glColor3f(highlight_top.r, highlight_top.g, highlight_top.b);
676 				glVertex3i(border, int(currenty + 0.5) - line_sep, 0);
677 				glColor3f(highlight_bottom.r, highlight_bottom.g, highlight_bottom.b);
678 				glVertex3i(border, int(currenty + line_step + 0.5) - line_sep, 0);
679 				glVertex3i(border+width-2*border, int(currenty + line_step + 0.5) - line_sep, 0);
680 				glColor3f(highlight_top.r, highlight_top.g, highlight_top.b);
681 				glVertex3i(border+width-2*border, int(currenty + 0.5) - line_sep, 0);
682 				glEnd();
683 				glEnable(GL_TEXTURE_2D);
684 				selection = i;
685 			}
686 
687 			// draw a separator ...
688 			if (menu_lines[i].is_separator)
689 			{
690 				int posx = border + text_border;
691 				int posy = currenty + get_line_height(win->font_category, 0.25*scale) - line_sep;
692 				glColor3f(grey_colour.r, grey_colour.g, grey_colour.b);
693 				glDisable(GL_TEXTURE_2D);
694 				glBegin(GL_LINES);
695 				glVertex2i(posx, posy);
696 				glVertex2i(win->len_x - posx, posy);
697 				glEnd();
698 				glEnable(GL_TEXTURE_2D);
699 				currenty += get_line_height(win->font_category, 0.5*scale);
700 			}
701 
702 			// ... or the menu line
703 			else
704 			{
705 				if (menu_lines[i].is_grey)
706 					glColor3f(grey_colour.r, grey_colour.g, grey_colour.b);
707 				else
708 					glColor3f(text_colour.r, text_colour.g, text_colour.b);
709 
710 				// draw the tickbox if bool option */
711 				if (menu_has_bools && menu_lines[i].control_var)
712 				{
713 					int box_x = border+text_border;
714 					int box_y = std::round(currenty + (box_size < line_height ? 0.5 * (line_height - box_size) : 0)) - line_sep / 2;
715 					glDisable(GL_TEXTURE_2D);
716 					glBegin( *menu_lines[i].control_var ? GL_QUADS: GL_LINE_LOOP);
717 					glVertex3i(box_x, box_y, 0);
718 					glVertex3i(box_x + box_size, box_y, 0);
719 					glVertex3i(box_x + box_size, box_y + box_size, 0);
720 					glVertex3i(box_x, box_y + box_size, 0);
721 					glEnd();
722 					glEnable(GL_TEXTURE_2D);
723 				}
724 
725 				// draw the text
726 				int text_y = std::round(currenty + (box_size < line_height ? 0 : 0.5*(box_size - line_height)));
727 				draw_string_zoomed(int(border+text_border+bool_tick_width+0.5), text_y - line_sep / 2,
728 					(const unsigned char *)menu_lines[i].text.c_str(), 1, scaled_value(1.0));
729 				currenty += line_step;
730 			}
731 
732 		} // end foir each line
733 
734 		CHECK_GL_ERRORS();
735 		return 1;
736 
737 	} // end Menu::display()
738 
739 
740 	//  if an option is selected, toggle if a bool option and call any callback function
click(window_info * win,int mx,int my,Uint32 flags)741 	int Menu::click(window_info *win, int mx, int my, Uint32 flags)
742 	{
743 		if (selection < 0)
744 			return 0;
745 
746 		// if a bool line, toggle the control variable
747 		if (menu_lines[selection].control_var)
748 		{
749 			// if we have a config control name, use the vars change function, otherwise just toggle
750 			if (menu_lines[selection].config_name)
751 				toggle_OPT_BOOL_by_name(menu_lines[selection].config_name);
752 			else
753 				*menu_lines[selection].control_var = !*menu_lines[selection].control_var;
754 			if (!handler)
755 				return 1;
756 		}
757 
758 		if (!handler)
759 			return 0;
760 
761 		// if we have a parent window, the mouse position is the original position that opened the menu
762 		const Container& container = Container::get_instance();
763 		window_info *parent_win = window_info_from_id(container.get_active_window_id());
764 		if (parent_win != NULL)
765 		{
766 			int parent_win_x = opened_mouse_x - parent_win->cur_x;
767 			int parent_win_y = opened_mouse_y - parent_win->cur_y;
768 			return (*handler)(parent_win, container.get_active_widget_id(),
769 				parent_win_x, parent_win_y, selection);
770 		}
771 		else
772 			return (*handler)(NULL, 0, 0, 0, selection);
773 	}
774 
775 
776 	// set basic size properties
set_sizes(int border,int text_border,int line_sep,float zoom)777 	int Menu::set_sizes(int border, int text_border, int line_sep, float zoom)
778 	{
779 		this->border = border;
780 		this->text_border = text_border;
781 		this->line_sep = line_sep;
782 		this->zoom = zoom;
783 		return resize();
784 	}
785 
786 	// set the named property colour
set_colour(size_t cm_id,enum CM_COLOUR_NAME colour_name,float r,float g,float b)787 	int Menu::set_colour(size_t cm_id, enum CM_COLOUR_NAME colour_name, float r, float g, float b)
788 	{
789 		switch (colour_name)
790 		{
791 			case CM_HIGHLIGHT_TOP: highlight_top.set(r,g,b); break;
792 			case CM_HIGHLIGHT_BOTTOM: highlight_bottom.set(r,g,b); break;
793 			case CM_TEXT: text_colour.set(r,g,b); break;
794 			case CM_GREY: grey_colour.set(r,g,b); break;
795 			default:
796 				return 0;
797 		}
798 		return 1;
799 	}
800 
801 
802 	// show information about menu lines
show_lines(size_t my_id)803 	void Menu::show_lines(size_t my_id)
804 	{
805 		std::cout << "  menu_id=" << my_id << " has_bools=" << menu_has_bools << std::endl;
806 		for (size_t i=0; i<menu_lines.size(); ++i)
807 		{
808 			if (menu_lines[i].control_var)
809 			{
810 				std::cout << "    [" << menu_lines[i].text << "] value=" << *menu_lines[i].control_var;
811 				if (menu_lines[i].config_name)
812 					std::cout << "  config name=[" << *menu_lines[i].config_name << "]";
813 			}
814 			else
815 				std::cout << "    [" << menu_lines[i].text << "]";
816 			std::cout << " " << ((menu_lines[i].is_grey) ?"greyed":"ungreyed")
817 				  << " " << ((menu_lines[i].is_separator) ?"separator":"normal") << std::endl;
818 		}
819 	}
820 
821 	// calculate a scaled value
scaled_value(float val) const822 	float Menu::scaled_value(float val) const
823 	{
824 		return val * Container::get_instance().get_current_scale() * zoom;
825 	}
826 
827 
828 } // end cm namespace
829 
830 
831 
832 // C wrapper functions
cm_create(const char * menu_list,int (* handler)(window_info *,int,int,int,int))833 extern "C" size_t cm_create(const char *menu_list, int (*handler)(window_info *, int, int, int, int )) { return cm::Container::get_instance().create(menu_list, handler); }
cm_destroy(size_t cm_id)834 extern "C" int cm_destroy(size_t cm_id) { return cm::Container::get_instance().destroy(cm_id); }
cm_pre_show_check(Uint32 flags)835 extern "C" int cm_pre_show_check(Uint32 flags) { return cm::Container::get_instance().pre_show_check(flags); }
cm_post_show_check(int force)836 extern "C" void cm_post_show_check(int force) { cm::Container::get_instance().post_show_check(force); }
cm_show_if_active(int window_id)837 extern "C" int cm_show_if_active(int window_id) { return cm::Container::get_instance().show_if_active(window_id); }
cm_show_direct(size_t cm_id,int window_id,int widget_id)838 extern "C" int cm_show_direct(size_t cm_id, int window_id, int widget_id) { return cm::Container::get_instance().show_direct(cm_id, window_id, widget_id); }
cm_bool_line(size_t cm_id,size_t line_index,int * control_var,const char * config_name)839 extern "C" int cm_bool_line(size_t cm_id, size_t line_index, int *control_var, const char *config_name) { return cm::Container::get_instance().bool_line(cm_id, line_index, control_var, config_name); }
cm_grey_line(size_t cm_id,size_t line_index,int is_grey)840 extern "C" int cm_grey_line(size_t cm_id, size_t line_index, int is_grey) { return cm::Container::get_instance().grey_line(cm_id, line_index, is_grey); }
cm_set(size_t cm_id,const char * menu_list,int (* handler)(window_info *,int,int,int,int))841 extern "C" int cm_set(size_t cm_id, const char *menu_list, int (*handler)(window_info *, int, int, int, int)) { return cm::Container::get_instance().set(cm_id, menu_list, handler); }
cm_add(size_t cm_id,const char * menu_list,int (* handler)(window_info *,int,int,int,int))842 extern "C" int cm_add(size_t cm_id, const char *menu_list, int (*handler)(window_info *, int, int, int, int)) { return cm::Container::get_instance().add(cm_id, menu_list, handler); }
cm_set_pre_show_handler(size_t cm_id,void (* handler)(window_info *,int,int,int,window_info *))843 extern "C" int cm_set_pre_show_handler(size_t cm_id, void (*handler)(window_info *, int, int, int, window_info *)) { return cm::Container::get_instance().set_pre_show_handler(cm_id, handler); }
cm_set_sizes(size_t cm_id,int border,int text_border,int line_sep,float zoom)844 extern "C" int cm_set_sizes(size_t cm_id, int border, int text_border, int line_sep, float zoom) { return cm::Container::get_instance().set_sizes(cm_id, border, text_border, line_sep, zoom); }
cm_set_colour(size_t cm_id,enum CM_COLOUR_NAME colour_name,float r,float g,float b)845 extern "C" int cm_set_colour(size_t cm_id, enum CM_COLOUR_NAME colour_name, float r, float g, float b) { return cm::Container::get_instance().set_colour(cm_id, colour_name, r, g, b); }
cm_add_window(size_t cm_id,int window_id)846 extern "C" int cm_add_window(size_t cm_id, int window_id) { return cm::Container::get_instance().add_window(cm_id, window_id); }
cm_add_region(size_t cm_id,int window_id,int posx,int posy,int lenx,int leny)847 extern "C" int cm_add_region(size_t cm_id, int window_id, int posx, int posy, int lenx, int leny) { return cm::Container::get_instance().add_region(cm_id, window_id, posx, posy, lenx, leny); }
cm_add_widget(size_t cm_id,int window_id,int widget_id)848 extern "C" int cm_add_widget(size_t cm_id, int window_id, int widget_id) { return cm::Container::get_instance().add_widget(cm_id, window_id, widget_id); }
cm_remove_window(int window_id)849 extern "C" int cm_remove_window(int window_id) { return cm::Container::get_instance().remove_window(window_id); }
cm_remove_regions(int window_id)850 extern "C" int cm_remove_regions(int window_id) { return cm::Container::get_instance().remove_regions(window_id); }
cm_remove_widget(int window_id,int widget_id)851 extern "C" int cm_remove_widget(int window_id, int widget_id) { return cm::Container::get_instance().remove_widget(window_id, widget_id); }
cm_showinfo(void)852 extern "C" void cm_showinfo(void) { cm::Container::get_instance().showinfo(); }
cm_valid(size_t cm_id)853 extern "C" int cm_valid(size_t cm_id) { if (cm::Container::get_instance().valid(cm_id)) return 1; else return 0; }
cm_window_shown(void)854 extern "C" size_t cm_window_shown(void) { return cm::Container::get_instance().window_shown(); }
cm_get_data(size_t cm_id)855 extern "C" void *cm_get_data(size_t cm_id) { return cm::Container::get_instance().get_data(cm_id); }
cm_set_data(size_t cm_id,void * data)856 extern "C" void cm_set_data(size_t cm_id, void *data) { cm::Container::get_instance().set_data(cm_id, data); }
857 
858 
859 
860 
861 
862 
863 //
864 //	Provides a window to exercise context menu functions.
865 //
866 #ifdef CONTEXT_MENUS_TEST
867 
868 static int cm_test_win = -1;
869 static size_t cm_test_win_menu = -1;
870 static size_t cm_test_reg_menu = -1;
871 static size_t cm_test_wid_menu = -1;
872 static size_t cm_test_dir_menu = -1;
873 static int cm_test_wid = -1;
874 static int cm_bool_var = 0;
875 static int cm_coord[2][4] = {{200, 100, 40, 40}, {250, 150, 40, 40}};
876 static int cm_grey_var = 0;
877 
cm_test_window_display_handler(window_info * win)878 static int cm_test_window_display_handler(window_info *win)
879 {
880 	static int last_bool_var = cm_bool_var;
881 	int i;
882 	CHECK_GL_ERRORS();
883 	// draw the region boxes so we know where they are
884 	for (i=0; i<2; i++)
885 	{
886 		glDisable(GL_TEXTURE_2D);
887 		glColor3fv(gui_color);
888 		glBegin(GL_LINE_LOOP);
889 		glVertex3i(cm_coord[i][0], cm_coord[i][1], 0);
890 		glVertex3i(cm_coord[i][0]+cm_coord[i][2], cm_coord[i][1], 0);
891 		glVertex3i(cm_coord[i][0]+cm_coord[i][2], cm_coord[i][1]+cm_coord[i][3], 0);
892 		glVertex3i(cm_coord[i][0], cm_coord[i][1]+cm_coord[i][3], 0);
893 		glEnd();
894 		glEnable(GL_TEXTURE_2D);
895 	}
896 	if (cm_bool_var != last_bool_var)
897 	{
898 		last_bool_var = cm_bool_var;
899 		printf("Bool var changed, now=%d\n", cm_bool_var);
900 	}
901 	CHECK_GL_ERRORS();
902 	return 1;
903 }
904 
cm_test_menu_pre_show_handler(window_info * win,int widget_id,int mx,int my,window_info * cm_win)905 static void cm_test_menu_pre_show_handler(window_info *win, int widget_id, int mx, int my, window_info *cm_win)
906 {
907 	printf("CM pre show: Set bool menu cm_grey_line()=%d window_id=%d widget_id=%d mx=%d my=%d winid=%d\n",
908 		cm_grey_line(cm_test_win_menu, 3, cm_grey_var), win->window_id, widget_id, mx, my, cm_win->window_id);
909 }
910 
cm_test_menu_handler(window_info * win,int widget_id,int mx,int my,int option)911 static int cm_test_menu_handler(window_info *win, int widget_id, int mx, int my, int option)
912 {
913 	printf("CM selected: window_id=%d widget_id=%d mx=%d my=%d option=%d\n", win->window_id, widget_id, mx, my, option );
914 	return 1;
915 }
916 
cm_info_but_handler(widget_list * widget,int mx,int my,Uint32 flags)917 static int cm_info_but_handler(widget_list *widget, int mx, int my, Uint32 flags)
918 {
919 	cm_showinfo();
920 	return 1;
921 }
922 
cm_dir_click_handler(widget_list * widget,int mx,int my,Uint32 flags)923 static int cm_dir_click_handler(widget_list *widget, int mx, int my, Uint32 flags)
924 {
925 	if (flags & ELW_RIGHT_MOUSE)
926 		printf("Direct clicked cm_show_direct()=%d\n", cm_show_direct(cm_test_dir_menu, cm_test_win, -1));
927 	return 1;
928 }
929 
cm_add_win_handler(widget_list * widget,int mx,int my,Uint32 flags)930 static int cm_add_win_handler(widget_list *widget, int mx, int my, Uint32 flags)
931 {
932 	printf("Adding window cm_add_window()=%d\n", cm_add_window(cm_test_win_menu, widget->window_id));
933 	return 1;
934 }
935 
cm_del_win_handler(widget_list * widget,int mx,int my,Uint32 flags)936 extern "C" int cm_del_win_handler(widget_list *widget, int mx, int my, Uint32 flags)
937 {
938 	printf("Removing window cm_remove_window()=%d\n", cm_remove_window(widget->window_id));
939 	return 1;
940 }
941 
cm_add_reg_handler(widget_list * widget,int mx,int my,Uint32 flags)942 static int cm_add_reg_handler(widget_list *widget, int mx, int my, Uint32 flags)
943 {
944 	int i;
945 	for (i=0; i<2; i++)
946 		printf("Adding region %d cm_add_region()=%d\n", i,
947 			cm_add_region(cm_test_reg_menu, widget->window_id, cm_coord[i][0], cm_coord[i][1], cm_coord[i][2], cm_coord[i][3] ));
948 	return 1;
949 }
950 
cm_del_reg_handler(widget_list * widget,int mx,int my,Uint32 flags)951 extern "C" int cm_del_reg_handler(widget_list *widget, int mx, int my, Uint32 flags)
952 {
953 	printf("Removing regions status=%d\n", cm_remove_regions(widget->window_id));
954 	return 1;
955 }
956 
cm_add_wid_handler(widget_list * widget,int mx,int my,Uint32 flags)957 static int cm_add_wid_handler(widget_list *widget, int mx, int my, Uint32 flags)
958 {
959 	printf("Adding widget cm_add_widget()=%d\n", cm_add_widget(cm_test_wid_menu, widget->window_id, cm_test_wid));
960 	return 1;
961 }
962 
cm_del_wid_handler(widget_list * widget,int mx,int my,Uint32 flags)963 extern "C" int cm_del_wid_handler(widget_list *widget, int mx, int my, Uint32 flags)
964 {
965 	printf("Removing widget cm_remove_widget()=%d\n", cm_remove_widget(widget->window_id, cm_test_wid));
966 	return 1;
967 }
968 
cm_test_window(char * text,int len)969 extern "C" int cm_test_window(char *text, int len)
970 {
971 	int cm_add_win = -1;
972 	int cm_add_reg = -1;
973 	int cm_add_wid = -1;
974 	int cm_del_win = -1;
975 	int cm_del_reg = -1;
976 	int cm_del_wid = -1;
977 	int cm_info_but = -1;
978 	int cm_dir_but = -1;
979 
980 	if (cm_test_win == -1)
981 	{
982 		cm_test_win = create_window("Test Context Menu", -1, -1, 0, 0, 400, 400, ELW_WIN_DEFAULT);
983 		set_window_handler(cm_test_win, ELW_HANDLER_DISPLAY, (int (*)())&cm_test_window_display_handler );
984 
985 		cm_add_win = button_add_extended(cm_test_win, 100, NULL, 10, 10, 0, 0, 0, 1.0f, "Add window");
986 		cm_add_reg = button_add_extended(cm_test_win, 101, NULL, 10, 50, 0, 0, 0, 1.0f, "Add region");
987 		cm_add_wid = button_add_extended(cm_test_win, 102, NULL, 10, 90, 0, 0, 0, 1.0f, "Add widget");
988 		cm_del_win = button_add_extended(cm_test_win, 103, NULL, 10, 130, 0, 0, 0, 1.0f, "Del window");
989 		cm_del_reg = button_add_extended(cm_test_win, 104, NULL, 10, 170, 0, 0, 0, 1.0f, "Del regions");
990 		cm_del_wid = button_add_extended(cm_test_win, 105, NULL, 10, 210, 0, 0, 0, 1.0f, "Del widget");
991 		cm_info_but = button_add_extended(cm_test_win, 106, NULL, 200, 10, 0, 0, 0, 1.0f, "Show Info");
992 		cm_test_wid = button_add_extended(cm_test_win, 107, NULL, 200, 50, 0, 0, 0, 1.0f, "Test Menu");
993 		cm_dir_but = button_add_extended(cm_test_win, 108, NULL, 200, 200, 0, 0, 0, 1.0f, "Direct Menu");
994 
995 		widget_set_OnClick(cm_test_win, cm_add_win, (int (*)())&cm_add_win_handler);
996 		widget_set_OnClick(cm_test_win, cm_del_win, (int (*)())&cm_del_win_handler);
997 		widget_set_OnClick(cm_test_win, cm_add_reg, (int (*)())&cm_add_reg_handler);
998 		widget_set_OnClick(cm_test_win, cm_del_reg, (int (*)())&cm_del_reg_handler);
999 		widget_set_OnClick(cm_test_win, cm_add_wid, (int (*)())&cm_add_wid_handler);
1000 		widget_set_OnClick(cm_test_win, cm_del_wid, (int (*)())&cm_del_wid_handler);
1001 		widget_set_OnClick(cm_test_win, cm_info_but, (int (*)())&cm_info_but_handler);
1002 		widget_set_OnClick(cm_test_win, cm_dir_but, (int (*)())&cm_dir_click_handler);
1003 
1004 		cm_test_win_menu = cm_create("", NULL);
1005 		cm_test_reg_menu = cm_create("Region 1\nRegion 2\n", cm_test_menu_handler);
1006 		cm_test_wid_menu = cm_create("Widget 1\n--\nWidget 3\nWidget 4\nWidget 5\nWidget 6\n", cm_test_menu_handler);
1007 		cm_test_dir_menu = cm_create("Direct 1\nDirect 2\n", cm_test_menu_handler);
1008 		printf("Created menus window=%lu region=%lu widget=%lu direct=%lu\n",
1009 			cm_test_win_menu, cm_test_reg_menu, cm_test_wid_menu, cm_test_dir_menu);
1010 
1011 		printf("Replacing window menu cm_set()=%d\n", cm_set(cm_test_win_menu, "Window 1\nWindow 2\n--\nGrey me...\nGrey above\n", cm_test_menu_handler));
1012 		printf("Set win menu cm_bool_line()=%d\n", cm_bool_line(cm_test_win_menu, 0, &cm_bool_var, NULL));
1013 		printf("Set win menu cm_bool_line()=%d\n", cm_bool_line(cm_test_win_menu, 1, &cm_bool_var, NULL));
1014 		printf("Set win menu cm_grey_line()=%d\n", cm_grey_line(cm_test_win_menu, 3, cm_grey_var));
1015 		printf("Set win menu cm_bool_line()=%d\n", cm_bool_line(cm_test_win_menu, 4, &cm_grey_var, NULL));
1016 		printf("Set widget menu cm_grey_line()=%d\n", cm_grey_line(cm_test_wid_menu, 3, 1));
1017 		printf("Calling cm_set_pre_show_handler()=%d\n", cm_set_pre_show_handler(cm_test_win_menu, cm_test_menu_pre_show_handler) );
1018 		printf("Changing widget menu sizes cm_set_sizes()=%d\n", cm_set_sizes(cm_test_wid_menu, 10, 10, 10, 2));
1019 		printf("Changing widget menu text colour cm_set_colour()=%d\n", cm_set_colour(cm_test_wid_menu, CM_TEXT, 0, 0, 1));
1020 		printf("Changing widget menu grey colour cm_set_colour()=%d\n", cm_set_colour(cm_test_wid_menu, CM_GREY, 0, 0, 0.5));
1021 		printf("Changing widget menu pale highlight colour cm_set_colour()=%d\n", cm_set_colour(cm_test_wid_menu, CM_HIGHLIGHT_TOP, 1, 0, 0));
1022 		printf("Changing widget menu bright highlight colour cm_set_colour()=%d\n", cm_set_colour(cm_test_wid_menu, CM_HIGHLIGHT_BOTTOM, 0, 1, 0));
1023 	}
1024 	else
1025 	{
1026 		printf("\n\nDestroy cm_test_win_menu cm_destroy()=%d\n", cm_destroy(cm_test_win_menu));
1027 		printf("Destroy cm_test_reg_menu cm_destroy()=%d\n", cm_destroy(cm_test_reg_menu));
1028 		printf("Destroy cm_test_wid_menu cm_destroy()=%d\n", cm_destroy(cm_test_wid_menu));
1029 		printf("Destroy cm_test_dir_menu cm_destroy()=%d\n", cm_destroy(cm_test_dir_menu));
1030 		destroy_window(cm_test_win);
1031 		cm_test_win = -1;
1032 		cm_showinfo();
1033 	}
1034 
1035 	return 1;
1036 }
1037 
1038 #endif
1039