1 /*
2     This file is part of Kismet
3 
4     Kismet is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     Kismet is distributed in the hope that it will be useful,
10       but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with Kismet; if not, write to the Free Software
16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19 #include "config.h"
20 
21 #include <sys/types.h>
22 #include <dirent.h>
23 #include <sys/stat.h>
24 #include <unistd.h>
25 
26 // Panel has to be here to pass configure, so just test these
27 #if (defined(HAVE_LIBNCURSES) || defined (HAVE_LIBCURSES))
28 
29 #include "kis_panel_widgets.h"
30 #include "kis_panel_frontend.h"
31 #include "timetracker.h"
32 #include "messagebus.h"
33 
34 #define WIN_CENTER(h, w)   (LINES / 2) - ((h) / 2), (COLS / 2) - ((w) / 2), (h), (w)
35 
Strlen(string str)36 unsigned int Kis_Panel_Specialtext::Strlen(string str) {
37 	int npos = 0;
38 	int escape = 0;
39 
40 	for (unsigned int pos = 0; pos < str.size(); pos++) {
41 		if (str[pos] == '\004') {
42 			escape = 1;
43 			continue;
44 		}
45 
46 		if (escape) {
47 			escape = 0;
48 
49 			if (str[pos] == 'C') {
50 				// Color escape code:
51 				// \004Ccolorpref;text
52 
53 				// Catch malforms at the end
54 				if (pos >= str.length()) {
55 					continue;
56 				}
57 
58 				size_t colorend = str.find(";", pos + 1);
59 
60 				if (colorend == string::npos)
61 					continue;
62 
63 				pos = colorend;
64 			}
65 
66 			continue;
67 		}
68 
69 		npos++;
70 	}
71 
72 	return npos;
73 }
74 
Mvwaddnstr(WINDOW * win,int y,int x,string str,int n,Kis_Panel * panel,int colorpair)75 void Kis_Panel_Specialtext::Mvwaddnstr(WINDOW *win, int y, int x, string str, int n,
76 									   Kis_Panel *panel, int colorpair) {
77 	int npos = 0;
78 	int escape = 0;
79 
80 	for (unsigned int pos = 0; pos < str.size(); pos++) {
81 		if (str[pos] == '\004') {
82 			escape = 1;
83 			continue;
84 		}
85 
86 		// Handle the attributes
87 		if (escape) {
88 			if (str[pos] == 'u') {
89 				wattron(win, WA_UNDERLINE);
90 			} else if (str[pos] == 'U') {
91 				wattroff(win, WA_UNDERLINE);
92 			} else if (str[pos] == 's') {
93 				wattron(win, WA_STANDOUT);
94 			} else if (str[pos] == 'S') {
95 				wattroff(win, WA_STANDOUT);
96 			} else if (str[pos] == 'r') {
97 				wattron(win, WA_REVERSE);
98 			} else if (str[pos] == 'R') {
99 				wattroff(win, WA_REVERSE);
100 			} else if (str[pos] == 'b') {
101 				if ((colorpair & A_BOLD) == 0)
102 					wattron(win, WA_BOLD);
103 			} else if (str[pos] == 'B') {
104 				if ((colorpair & A_BOLD) == 0)
105 					wattroff(win, WA_BOLD);
106 			} else if (str[pos] == 'C') {
107 				// Color escape code:
108 				// \004Ccolorpref;text
109 
110 				// Catch malforms at the end
111 				if (pos >= str.length()) {
112 					continue;
113 				}
114 
115 				size_t colorend = str.find(";", pos + 1);
116 
117 				if (colorend != string::npos) {
118 					string cpref = str.substr(pos + 1, colorend - pos - 1);
119 					int c = 0;
120 
121 
122 					panel->ColorFromPref(c, cpref);
123 					pos = colorend;
124 
125 					if (c > 0)
126 						wattrset(win, c);
127 				}
128 			} else {
129 				// fprintf(stderr, "invalid escape '%c'\n", str[pos]);
130 				// Backfill the unescaped data
131 				escape = 0;
132 				if (npos <= n) {
133 					mvwaddch(win, y, x + npos, '\\');
134 					npos++;
135 				}
136 				if (npos <= n) {
137 					mvwaddch(win, y, x + npos, str[npos]);
138 					npos++;
139 				}
140 			}
141 
142 			escape = 0;
143 			continue;
144 		}
145 
146 		// Otherwise write the character, if we can.  We DON'T abort here,
147 		// because we need to process to the end of the string to turn off
148 		// any attributes that were on
149 		if (npos <= n) {
150 			mvwaddch(win, y, x + npos, str[pos]);
151 			npos++;
152 			continue;
153 		}
154 	}
155 }
156 
Kis_Panel_Color()157 Kis_Panel_Color::Kis_Panel_Color() {
158 	// nextindex = COLORS + 1;
159 	nextindex = 1;
160 }
161 
AddColor(string color,string pref)162 int Kis_Panel_Color::AddColor(string color, string pref) {
163 	map<string, Kis_Panel_Color::color_rec>::iterator cimi;
164 	short nums[2] = {0, 0};
165 	int bold = 0;
166 	int pair;
167 
168 	if ((cimi = color_index_map.find(StrLower(color))) != color_index_map.end()) {
169 		return cimi->second.colorindex;
170 	}
171 
172 	if (nextindex == COLOR_PAIRS - 1) {
173 		// fprintf(stderr, "debug - too many color pairs\n");
174 		return COLOR_PAIR(0);
175 	}
176 
177 	vector<string> colorpair = StrTokenize(color, ",");
178 
179 	if (colorpair.size() < 1)
180 		colorpair.push_back("white");
181 	if (colorpair.size() < 2)
182 		colorpair.push_back("black");
183 
184 	colorpair[0] = StrLower(colorpair[0]);
185 	colorpair[1] = StrLower(colorpair[1]);
186 
187 	for (unsigned int x = 0; x < 2; x++) {
188 		string clr = colorpair[x];
189 
190 		if (clr == "grey" || clr == "gray")
191 			clr = "hi-black";
192 
193 		// First, find if theres a hi-
194 		if (clr.substr(0, 3) == "hi-") {
195 			bold = 1;
196 			clr = clr.substr(3, clr.length() - 3);
197 		}
198 
199 		// Then match all the colors
200 		if (clr == "default")
201 			nums[x] = -1;
202 		else if (clr == "black")
203 			nums[x] = COLOR_BLACK;
204 		else if (clr == "red")
205 			nums[x] = COLOR_RED;
206 		else if (clr == "green")
207 			nums[x] = COLOR_GREEN;
208 		else if (clr == "yellow")
209 			nums[x] = COLOR_YELLOW;
210 		else if (clr == "blue")
211 			nums[x] = COLOR_BLUE;
212 		else if (clr == "magenta")
213 			nums[x] = COLOR_MAGENTA;
214 		else if (clr == "cyan")
215 			nums[x] = COLOR_CYAN;
216 		else if (clr == "white")
217 			nums[x] = COLOR_WHITE;
218 	}
219 
220 	// fprintf(stderr, "debug - color init_pair %d vals %d, %d\n", nextindex, nums[0], nums[1]);
221 	init_pair(nextindex, nums[0], nums[1]);
222 
223 	pair = COLOR_PAIR(nextindex);
224 
225 	if (bold) {
226 		pair |= A_BOLD;
227 	}
228 
229 	color_rec cr;
230 	cr.pref = pref;
231 	cr.color[0] = colorpair[0];
232 	cr.color[1] = colorpair[1];
233 	cr.colorindex = pair;
234 
235 	color_index_map[StrLower(color)] = cr;
236 	nextindex++;
237 
238 	return pair;
239 }
240 
RemapAllColors(string oldcolor,string newcolor,ConfigFile * conf)241 void Kis_Panel_Color::RemapAllColors(string oldcolor, string newcolor,
242 									 ConfigFile *conf) {
243 	map<string, Kis_Panel_Color::color_rec>::iterator cri;
244 	string o = StrLower(oldcolor), n = StrLower(newcolor);
245 
246 	for (cri = color_index_map.begin(); cri != color_index_map.end(); ++cri) {
247 		int s = 0;
248 
249 		if (cri->second.pref == "")
250 			continue;
251 
252 		if (cri->second.color[0] == o) {
253 			cri->second.color[0] = n;
254 			s = 1;
255 		}
256 
257 		if (cri->second.color[1] == o) {
258 			cri->second.color[1] = n;
259 			s = 1;
260 		}
261 
262 		if (s)
263 			conf->SetOpt(cri->second.pref, cri->second.color[0] + string(",") +
264 						 cri->second.color[1], time(0));
265 	}
266 }
267 
panelint_draw_timer(TIMEEVENT_PARMS)268 int panelint_draw_timer(TIMEEVENT_PARMS) {
269 	return ((PanelInterface *) parm)->DrawInterface();
270 }
271 
272 // Pollable panel interface driver
PanelInterface()273 PanelInterface::PanelInterface() {
274 	fprintf(stderr, "FATAL OOPS:  PanelInterface() w/ no globalreg\n");
275 	exit(1);
276 }
277 
PanelInterface(GlobalRegistry * in_globalreg)278 PanelInterface::PanelInterface(GlobalRegistry *in_globalreg) {
279 	globalreg = in_globalreg;
280 
281 	// Init curses
282 	initscr();
283 	raw();
284 	cbreak();
285 	noecho();
286 	keypad(stdscr, 1);
287 	meta(stdscr, 1);
288 	mousemask(ALL_MOUSE_EVENTS | REPORT_MOUSE_POSITION, NULL);
289 	start_color();
290 	use_default_colors();
291 
292 	draweventid =
293 		globalreg->timetracker->RegisterTimer(SERVER_TIMESLICES_SEC / 2,
294 											  NULL, 1, &panelint_draw_timer,
295 											  (void *) this);
296 
297 	globalreg->RegisterPollableSubsys(this);
298 
299 	getmaxyx(stdscr, hsize, vsize);
300 };
301 
~PanelInterface()302 PanelInterface::~PanelInterface() {
303 	for (unsigned int x = 0; x < live_panels.size(); x++)
304 		delete live_panels[x];
305 
306 	globalreg->timetracker->RemoveTimer(draweventid);
307 
308 	globalreg->RemovePollableSubsys(this);
309 
310 	erase();
311 
312 	if (isendwin() == 0)
313 		endwin();
314 }
315 
MergeSet(int in_max_fd,fd_set * out_rset,fd_set * out_wset)316 int PanelInterface::MergeSet(int in_max_fd, fd_set *out_rset, fd_set *out_wset) {
317 	if (globalreg->spindown)
318 		return in_max_fd;
319 
320 	if (live_panels.size() == 0)
321 		return in_max_fd;
322 
323 	// add stdin to the listen set
324 	FD_SET(fileno(stdin), out_rset);
325 
326 	if (in_max_fd < fileno(stdin))
327 		return fileno(stdin);
328 
329 	return in_max_fd;
330 }
331 
Poll(fd_set & in_rset,fd_set & in_wset)332 int PanelInterface::Poll(fd_set& in_rset, fd_set& in_wset) {
333 	if (live_panels.size() == 0)
334 		return 0;
335 
336 	if (FD_ISSET(fileno(stdin), &in_rset)) {
337 		// Poll via the top of the stack
338 		int ret;
339 
340 		ret = live_panels[live_panels.size() - 1]->Poll();
341 		DrawInterface();
342 
343 		if (ret < 0)
344 			globalreg->fatal_condition = 1;
345 		return ret;
346 	}
347 
348 	return 0;
349 }
350 
ResizeInterface()351 void PanelInterface::ResizeInterface() {
352 	int nh, nv;
353 
354 	endwin();
355 	refresh();
356 	clear();
357 
358 	getmaxyx(stdscr, nh, nv);
359 
360 	if (hsize == nh && vsize == nv) {
361 		return;
362 	}
363 
364 	for (unsigned int x = 0; x < live_panels.size(); x++) {
365 		// If it's full screen, keep it full screen, otherwise
366 		// re-center it
367 		if (live_panels[x]->FetchSzy() == hsize &&
368 			live_panels[x]->FetchSzx() == vsize) {
369 			live_panels[x]->Position(0, 0, nh, nv);
370 		} else {
371 			int rsy = live_panels[x]->FetchSzy(), rsx = live_panels[x]->FetchSzx();
372 
373 			if (rsy > nh)
374 				rsy = nh;
375 
376 			if (rsx > nv)
377 				rsx = nv;
378 
379 			live_panels[x]->Position(WIN_CENTER(rsy, rsx));
380 		}
381 	}
382 
383 	hsize = nh;
384 	vsize = nv;
385 }
386 
DrawInterface()387 int PanelInterface::DrawInterface() {
388 	// Draw all the panels
389 	for (unsigned int x = 0; x < live_panels.size(); x++) {
390 		live_panels[x]->DrawPanel();
391 	}
392 
393 	// Call the update
394 	update_panels();
395 	doupdate();
396 
397 	// Delete dead panels from before
398 	for (unsigned int x = 0; x < dead_panels.size(); x++) {
399 		delete(dead_panels[x]);
400 	}
401 	dead_panels.clear();
402 
403 	return 1;
404 }
405 
AddPanel(Kis_Panel * in_panel)406 void PanelInterface::AddPanel(Kis_Panel *in_panel) {
407 	live_panels.push_back(in_panel);
408 }
409 
KillPanel(Kis_Panel * in_panel)410 void PanelInterface::KillPanel(Kis_Panel *in_panel) {
411 	for (unsigned int x = 0; x < live_panels.size(); x++) {
412 		if (live_panels[x] == in_panel) {
413 			dead_panels.push_back(in_panel);
414 			live_panels.erase(live_panels.begin() + x);
415 		}
416 	}
417 }
418 
Kis_Panel_Component(GlobalRegistry * in_globalreg,Kis_Panel * in_panel)419 Kis_Panel_Component::Kis_Panel_Component(GlobalRegistry *in_globalreg,
420 										 Kis_Panel *in_panel) {
421 	globalreg = in_globalreg;
422 	parent_panel = in_panel;
423 	window = in_panel->FetchDrawWindow();
424 	visible = 0;
425 	active = 0;
426 
427 	sx = sy = ex = ey = lx = ly = 0;
428 	px = py = 0;
429 	mx = my = 0;
430 	layout_dirty = 0;
431 
432 	cb_switch = cb_activate = NULL;
433 
434 	color_active = color_inactive = 0;
435 
436 	color_active_pref = "panel_text_color";
437 	color_inactive_pref = "panel_textdis_color";
438 
439 	name = "GENERIC_WIDGET";
440 }
441 
SetCallback(int cbtype,int (* cb)(COMPONENT_CALLBACK_PARMS),void * aux)442 void Kis_Panel_Component::SetCallback(int cbtype, int (*cb)(COMPONENT_CALLBACK_PARMS),
443 									  void *aux) {
444 	switch (cbtype) {
445 		case COMPONENT_CBTYPE_SWITCH:
446 			cb_switch = cb;
447 			cb_switch_aux = aux;
448 			break;
449 		case COMPONENT_CBTYPE_ACTIVATED:
450 			cb_activate = cb;
451 			cb_activate_aux = aux;
452 			break;
453 	}
454 }
455 
ClearCallback(int cbtype)456 void Kis_Panel_Component::ClearCallback(int cbtype) {
457 	switch (cbtype) {
458 		case COMPONENT_CBTYPE_SWITCH:
459 			cb_switch = NULL;
460 			break;
461 		case COMPONENT_CBTYPE_ACTIVATED:
462 			cb_activate = NULL;
463 			break;
464 	}
465 }
466 
Kis_Panel_Packbox(GlobalRegistry * in_globalreg,Kis_Panel * in_panel)467 Kis_Panel_Packbox::Kis_Panel_Packbox(GlobalRegistry *in_globalreg,
468 									 Kis_Panel *in_panel) :
469 	Kis_Panel_Component(in_globalreg, in_panel) {
470 	homogenous = 0;
471 	packing = 0;
472 	spacing = 0;
473 	center = 0;
474 
475 	name = "GENERIC_PACKBOX";
476 }
477 
~Kis_Panel_Packbox()478 Kis_Panel_Packbox::~Kis_Panel_Packbox() {
479 	// Nothing to do really
480 }
481 
GetVisible()482 int Kis_Panel_Packbox::GetVisible() {
483 	if (visible == 0)
484 		return 0;
485 
486 	int any_vis = 0;
487 
488 	for (list<Kis_Panel_Packbox::packbox_details>::iterator x = packed_items.begin();
489 		 x != packed_items.end(); ++x) {
490 		if ((*x).widget->GetVisible()) {
491 			any_vis = 1;
492 			break;
493 		}
494 	}
495 
496 	if (any_vis)
497 		return 1;
498 
499 	return 0;
500 }
501 
Pack_Start(Kis_Panel_Component * in_widget,int in_fill,int in_padding)502 void Kis_Panel_Packbox::Pack_Start(Kis_Panel_Component *in_widget, int in_fill,
503 								   int in_padding) {
504 	packbox_details det;
505 
506 	det.widget = in_widget;
507 	det.fill = in_fill;
508 	det.padding = in_padding;
509 
510 	packed_items.push_front(det);
511 
512 	layout_dirty = 1;
513 }
514 
Pack_End(Kis_Panel_Component * in_widget,int in_fill,int in_padding)515 void Kis_Panel_Packbox::Pack_End(Kis_Panel_Component *in_widget, int in_fill,
516 								 int in_padding) {
517 	packbox_details det;
518 
519 	det.widget = in_widget;
520 	det.fill = in_fill;
521 	det.padding = in_padding;
522 
523 	packed_items.push_back(det);
524 
525 	layout_dirty = 1;
526 }
527 
Pack_Before_Named(string in_name,Kis_Panel_Component * in_widget,int in_fill,int in_padding)528 void Kis_Panel_Packbox::Pack_Before_Named(string in_name,
529 										  Kis_Panel_Component *in_widget,
530 										  int in_fill, int in_padding) {
531 	list<Kis_Panel_Packbox::packbox_details>::iterator i;
532 	packbox_details det;
533 
534 	det.widget = in_widget;
535 	det.fill = in_fill;
536 	det.padding = in_padding;
537 
538 	layout_dirty = 1;
539 
540 	for (i = packed_items.begin(); i != packed_items.end(); ++i) {
541 		if ((*i).widget->GetName() == in_name) {
542 			packed_items.insert(i, det);
543 			return;
544 		}
545 	}
546 
547 	packed_items.push_back(det);
548 	return;
549 }
550 
Pack_After_Named(string in_name,Kis_Panel_Component * in_widget,int in_fill,int in_padding)551 void Kis_Panel_Packbox::Pack_After_Named(string in_name,
552 										 Kis_Panel_Component *in_widget,
553 										 int in_fill, int in_padding) {
554 	list<Kis_Panel_Packbox::packbox_details>::iterator i;
555 	packbox_details det;
556 
557 	det.widget = in_widget;
558 	det.fill = in_fill;
559 	det.padding = in_padding;
560 
561 	layout_dirty = 1;
562 
563 	for (i = packed_items.begin(); i != packed_items.end(); ++i) {
564 		if ((*i).widget->GetName() == in_name) {
565 			packed_items.insert(++i, det);
566 			return;
567 		}
568 	}
569 
570 	packed_items.push_back(det);
571 	return;
572 }
573 
Pack_Remove(Kis_Panel_Component * in_widget)574 void Kis_Panel_Packbox::Pack_Remove(Kis_Panel_Component *in_widget) {
575 	list<Kis_Panel_Packbox::packbox_details>::iterator i;
576 
577 	for (i = packed_items.begin(); i != packed_items.end(); ++i) {
578 		if ((*i).widget == in_widget) {
579 			packed_items.erase(i);
580 			layout_dirty = 1;
581 			return;
582 		}
583 	}
584 }
585 
Pack_Widgets()586 void Kis_Panel_Packbox::Pack_Widgets() {
587 	int size, psize, msize, pos;
588 	list<Kis_Panel_Packbox::packbox_details>::iterator i;
589 
590 	if (visible == 0)
591 		return;
592 
593 	// Get the packing direction
594 	if (packing == 0) {
595 		size = lx;
596 	} else {
597 		size = ly;
598 	}
599 
600 	// If we're homogenous, we divide by the # of widgets, find out if we're too
601 	// small for any of them, and decrease until we can fit them
602 	if (homogenous) {
603 		int ndivs = packed_items.size();
604 		int perbox = 0;
605 
606 		for (i = packed_items.begin(); i != packed_items.end(); ++i) {
607 			if ((*i).widget->GetVisible() == 0) {
608 				ndivs--;
609 				continue;
610 			}
611 		}
612 
613 		for (i = packed_items.begin(); i != packed_items.end(); ++i) {
614 			int wmsize;
615 
616 			if ((*i).widget->GetVisible() == 0) {
617 				continue;
618 			}
619 
620 			perbox = (int) ((float) (size - (spacing * (ndivs - 1)))  / ndivs);
621 
622 			if (packing == 0) {
623 				wmsize = (*i).widget->GetMinX();
624 			} else {
625 				wmsize = (*i).widget->GetMinY();
626 			}
627 
628 			// If someone can't fit, decrease the number of divisions until we
629 			// can, and we just don't draw those widgets.  Yeah, it sucks,
630 			// don't over-pack a small frame
631 			if (wmsize > perbox) {
632 				// If we simply can't fix the widget in, period, then bail on
633 				// drawing.
634 				if (ndivs <= 1) {
635 					// fprintf(stderr, "we couldn't find, wah\n");
636 					return;
637 				}
638 
639 				ndivs -= 1;
640 				i = packed_items.begin();
641 				continue;
642 			}
643 		}
644 
645 		i = packed_items.begin();
646 		for (int x = 0; x < ndivs && i != packed_items.end(); x++, ++i) {
647 			if ((*i).widget->GetVisible() == 0) {
648 				x--;
649 				continue;
650 			}
651 
652 			// Set the position of each widget
653 			int ww = perbox - ((*i).padding * 2);
654 			int co = 0;
655 
656 			// Get the preferred size (or best we can do) OR the fill
657 			int psize = 0, op = 0;
658 			if ((*i).fill == 0) {
659 				if (packing == 0) {
660 					psize = (*i).widget->GetPrefX() + ((*i).padding * 2);
661 					op = ly;
662 				} else {
663 					psize = (*i).widget->GetPrefY() + ((*i).padding * 2);
664 					op = lx;
665 				}
666 
667 				if (psize > ww)
668 					psize = ww;
669 			} else {
670 				psize = ww;
671 			}
672 
673 			if (center && psize != ww) {
674 				co = (ww - psize) / 2;
675 			}
676 
677 			if (packing == 0) {
678 				(*i).widget->SetPosition(
679 						sx + (perbox * x) + (*i).padding + co, sy,
680 						sx + (perbox * x) + (*i).padding + co + psize, sy + op);
681 			} else {
682 				(*i).widget->SetPosition(
683 						sx, sy + (perbox * x) + (*i).padding + co, sx + op,
684 						sy + (perbox * x) + (*i).padding + co + psize);
685 			}
686 
687 		}
688 
689 		return;
690 		// Done w/ homogenous spacing
691 	}
692 
693 	// Non-homogenous spacing
694 	// Pass 1: Can we fit everyone who has a preferred size in?  If we can, then
695 	// we can just start expanding them (or just plain draw them as is if we
696 	// don't have any filler).  Calculate preferred and minimum sizes simultaneously
697 	// to save another iteration.
698 	psize = 0;
699 	msize = 0;
700 	for (i = packed_items.begin(); i != packed_items.end(); ++i) {
701 		if ((*i).widget->GetVisible() == 0)
702 			continue;
703 
704 		if (packing == 0) {
705 			psize += (*i).widget->GetPrefX() + ((*i).padding * 2);
706 			msize += (*i).widget->GetMinX() + ((*i).padding * 2);
707 		} else {
708 			psize += (*i).widget->GetPrefY() + ((*i).padding * 2);
709 			msize += (*i).widget->GetMinY() + ((*i).padding * 2);
710 		}
711 	}
712 
713 	// If we can't fit the preferred, can we fit the minimum?
714 	if (psize > size) {
715 		// fprintf(stderr, "debug - %p can't fit preferred\n", this);
716 		if (msize <= size) {
717 			// fprintf(stderr, "debug - %p can fit in size\n", this);
718 			pos = 0;
719 			// Fit them via minsize, giving them space from the free
720 			// bucket so long as we have it
721 			int bucket = size - msize;
722 
723 			// fprintf(stderr, "debug - %p has bucket %d for items, min %d\n", this, bucket, msize);
724 
725 			i = packed_items.begin();
726 			for (int x = 0; i != packed_items.end(); ++i, x++) {
727 				if ((*i).widget->GetVisible() == 0)
728 					continue;
729 
730 				int mp, pp, op;
731 
732 				if (packing == 0) {
733 					mp = (*i).widget->GetMinX();
734 					pp = (*i).widget->GetPrefX();
735 					/*
736 					op = (*i).widget->GetPrefY();
737 					if (op > ly || op == 0)
738 						op = ly;
739 						*/
740 					op = ly;
741 				} else {
742 					mp = (*i).widget->GetMinY();
743 					pp = (*i).widget->GetPrefY();
744 					/*
745 					op = (*i).widget->GetPrefX();
746 					if (op > lx || op == 0)
747 						op = lx;
748 						*/
749 					op = lx;
750 				}
751 
752 				int ww;
753 				ww = mp + ((*i).padding * 2);
754 				// fprintf(stderr, "debug - %p item %d gets %d\n", this, x, ww);
755 				if (bucket > 0 && mp < pp) {
756 					int delta = pp - mp;
757 					if (delta > bucket)
758 						delta = bucket;
759 					// fprintf(stderr, "debug - %p gave %d to item %d min %d wanted %d was %d now %d\n", this, delta, x, mp, pp - mp, ww, ww+delta);
760 					bucket -= delta;
761 					ww += delta;
762 				}
763 
764 				if (packing == 0) {
765 					(*i).widget->SetPosition(
766 						sx + (spacing * x) + pos, sy,
767 						sx + (spacing * x) + pos + ww, sy + op);
768 				} else {
769 					(*i).widget->SetPosition(
770 						sx, sy + (spacing * x) + pos,
771 						sx + op, sy + (spacing * x) + pos + ww);
772 				}
773 
774 				pos += ww;
775 			}
776 		}
777 
778 		return;
779 	}
780 
781 	/* Otherwise, we can more than fit our widgets...
782 	 * So the first order of business, find out how many are set to expand,
783 	 * and how much slush space we have to give them */
784 	// fprintf(stderr, "debug - %p we can fit all preferred\n", this);
785 	int bucket = 0;
786 	int num_fill = 0;
787 	for (i = packed_items.begin(); i != packed_items.end(); ++i) {
788 		int pp;
789 
790 		if ((*i).widget->GetVisible() == 0)
791 			continue;
792 
793 		if (packing == 0) {
794 			pp = (*i).widget->GetPrefX();
795 		} else {
796 			pp = (*i).widget->GetPrefY();
797 		}
798 
799 		/* Add up all the ones which aren't expanding to let us know
800 		 * how much we can give to the ones we can give more to */
801 		if ((*i).fill == 0) {
802 			bucket += pp;
803 		} else {
804 			num_fill++;
805 		}
806 	}
807 
808 	// Reclaim our variable - our free bucket is the remainder of unclaimed
809 	// stuff
810 	bucket = size - bucket - (spacing * (packed_items.size() - 1));
811 	// fprintf(stderr, "debug - %p bucket %d fill %d\n", this, bucket, num_fill);
812 
813 	// Distribute the bucket over the expandable widgets, position, and draw
814 	pos = 0;
815 	i = packed_items.begin();
816 	for (int x = 0; i != packed_items.end(); ++i, x++) {
817 		int pp, op;
818 
819 		if ((*i).widget->GetVisible() == 0)
820 			continue;
821 
822 		if (packing == 0) {
823 			pp = (*i).widget->GetPrefX();
824 			/*
825 			op = (*i).widget->GetPrefY();
826 			if (op > ly || op == 0)
827 				op = ly;
828 				*/
829 			op = ly;
830 		} else {
831 			pp = (*i).widget->GetPrefY();
832 			/*
833 			op = (*i).widget->GetPrefX();
834 			if (op > lx || op == 0)
835 				op = lx;
836 				*/
837 			op = lx;
838 		}
839 
840 		// Disperse the bucket over the items we have left
841 		if ((*i).fill != 0 && num_fill != 0) {
842 			pp = bucket / num_fill;
843 			bucket = bucket - pp;
844 			num_fill--;
845 		}
846 
847 		if (packing == 0) {
848 			(*i).widget->SetPosition(
849 						 sx + pos, sy,
850 						 sx + pos + pp, sy + op);
851 		} else {
852 			(*i).widget->SetPosition(
853 						 sx, sy + pos, sx + op, sy + pos + pp);
854 		}
855 
856 		pos += pp + spacing;
857 	}
858 }
859 
DrawComponent()860 void Kis_Panel_Packbox::DrawComponent() {
861 	list<Kis_Panel_Packbox::packbox_details>::iterator i;
862 
863 	if (visible == 0)
864 		return;
865 
866 	for (i = packed_items.begin(); i != packed_items.end(); ++i) {
867 		if ((*i).widget->GetLayoutDirty()) {
868 			layout_dirty = 1;
869 			break;
870 		}
871 	}
872 
873 	if (layout_dirty) {
874 		Pack_Widgets();
875 		layout_dirty = 0;
876 	}
877 
878 	for (i = packed_items.begin(); i != packed_items.end(); ++i) {
879 		(*i).widget->DrawComponent();
880 		(*i).widget->SetLayoutDirty(0);
881 	}
882 }
883 
Kis_Menu(GlobalRegistry * in_globalreg,Kis_Panel * in_panel)884 Kis_Menu::Kis_Menu(GlobalRegistry *in_globalreg, Kis_Panel *in_panel) :
885 	Kis_Panel_Component(in_globalreg, in_panel) {
886 	globalreg = in_globalreg;
887 	cur_menu = -1;
888 	cur_item = -1;
889 	sub_item = -1;
890 	sub_menu = -1;
891 	mouse_triggered = 0;
892 	menuwin = NULL;
893 	submenuwin = NULL;
894 	text_color = border_color = disable_color = 0;
895 
896 	parent_panel->InitColorPref("menu_text_color", "white,blue");
897 	parent_panel->InitColorPref("menu_border_color", "cyan,blue");
898 	parent_panel->InitColorPref("menu_disable_color", "cyan,blue");
899 }
900 
~Kis_Menu()901 Kis_Menu::~Kis_Menu() {
902 	ClearMenus();
903 
904 	if (menuwin != NULL)
905 		delwin(menuwin);
906 	if (submenuwin != NULL)
907 		delwin(submenuwin);
908 }
909 
AddMenu(string in_text,int targ_char)910 int Kis_Menu::AddMenu(string in_text, int targ_char) {
911 	_menu *menu = new _menu;
912 
913 	menu->text = in_text;
914 	if (targ_char < 0 || targ_char > (int) in_text.length() - 1)
915 		menu->targchar = -1;
916 	else
917 		menu->targchar = targ_char;
918 
919 	menu->width = 0;
920 
921 	menu->id = menubar.size();
922 
923 	menu->submenu = 0;
924 	menu->visible = 1;
925 	menu->checked = -1;
926 
927 	menubar.push_back(menu);
928 
929 	return menu->id;
930 }
931 
SetMenuVis(int in_menu,int in_vis)932 void Kis_Menu::SetMenuVis(int in_menu, int in_vis) {
933 	if (in_menu < 0 || in_menu > (int) menubar.size() - 1)
934 		return;
935 
936 	menubar[in_menu]->visible = in_vis;
937 }
938 
AddMenuItem(string in_text,int menuid,char extra)939 int Kis_Menu::AddMenuItem(string in_text, int menuid, char extra) {
940 	if (menuid < 0 || menuid > (int) menubar.size() - 1)
941 		return -1;
942 
943 	_menuitem *item = new _menuitem;
944 
945 	item->parentmenu = menuid;
946 	item->text = in_text;
947 	item->extrachar = extra;
948 	item->id = menubar[menuid]->items.size();
949 	item->visible = 1;
950 	item->checked = -1;
951 	item->colorpair = -1;
952 	item->callback = NULL;
953 	item->auxptr = NULL;
954 	item->checksymbol = 'X';
955 
956 	if (extra != 0) {
957 		for (vector<Kis_Menu::_menuitem *>::iterator p =
958 			 menubar[menuid]->items.begin(); p != menubar[menuid]->items.end(); p++) {
959 			if ((*p)->extrachar == extra) {
960 				_MSG("New menu item '" + in_text + "' shortcut '" + extra + "' "
961 					 "conflicts with existing item '" + (*p)->text + "'",
962 					 MSGFLAG_ERROR);
963 				item->extrachar = 0;
964 			}
965 		}
966 	}
967 
968 	// Auto-disable spacers
969 	if (item->text[0] != '-')
970 		item->enabled = 1;
971 	else
972 		item->enabled = 0;
973 
974 	item->submenu = -1;
975 
976 	menubar[menuid]->items.push_back(item);
977 
978 	if ((int) in_text.length() > menubar[menuid]->width)
979 		menubar[menuid]->width = in_text.length();
980 
981 	return (menuid * 100) + item->id + 1;
982 }
983 
SetMenuItemChecked(int in_item,int in_checked)984 void Kis_Menu::SetMenuItemChecked(int in_item, int in_checked) {
985 	int mid = in_item / 100;
986 	int iid = (in_item % 100) - 1;
987 
988 	if (mid < 0 || mid >= (int) menubar.size())
989 		return;
990 
991 	if (iid < 0 || iid > (int) menubar[mid]->items.size())
992 		return;
993 
994 	menubar[mid]->items[iid]->checked = in_checked;
995 	menubar[mid]->checked = -1;
996 
997 	// Update the checked menu status
998 	for (unsigned int x = 0; x < menubar[mid]->items.size(); x++) {
999 		if (menubar[mid]->items[x]->checked > menubar[mid]->checked)
1000 			menubar[mid]->checked = menubar[mid]->items[x]->checked;
1001 	}
1002 }
1003 
SetMenuItemColor(int in_item,string in_color)1004 void Kis_Menu::SetMenuItemColor(int in_item, string in_color) {
1005 	int mid = in_item / 100;
1006 	int iid = (in_item % 100) - 1;
1007 
1008 	if (mid < 0 || mid >= (int) menubar.size())
1009 		return;
1010 
1011 	if (iid < 0 || iid > (int) menubar[mid]->items.size())
1012 		return;
1013 
1014 	menubar[mid]->items[iid]->colorpair = parent_panel->AddColor(in_color);
1015 }
1016 
AddSubMenuItem(string in_text,int menuid,char extra)1017 int Kis_Menu::AddSubMenuItem(string in_text, int menuid, char extra) {
1018 	if (menuid < 0 || menuid > (int) menubar.size() - 1)
1019 		return -1;
1020 
1021 	// Add a new menu to the menu handling system, which gives us
1022 	// rational IDs and such.
1023 	int smenuid = AddMenu(in_text, 0);
1024 	// Mark the new menu record as a submenu so it doesn't get drawn
1025 	// in the menubar
1026 	menubar[smenuid]->submenu = 1;
1027 
1028 	// Add a menu item to the requested parent menu, and flag it as a submenu
1029 	// pointing to our menuid so we can find it during drawing
1030 	int sitem = AddMenuItem(in_text, menuid, extra);
1031 
1032 	menubar[menuid]->items[(sitem % 100) - 1]->submenu = smenuid;
1033 
1034 	// Return the id of the menu we made so we can add things to it
1035 	return smenuid;
1036 }
1037 
DisableMenuItem(int in_item)1038 void Kis_Menu::DisableMenuItem(int in_item) {
1039 	int mid = in_item / 100;
1040 	int iid = (in_item % 100) - 1;
1041 
1042 	if (mid < 0 || mid >= (int) menubar.size())
1043 		return;
1044 
1045 	if (iid < 0 || iid > (int) menubar[mid]->items.size())
1046 		return;
1047 
1048 	menubar[mid]->items[iid]->enabled = 0;
1049 }
1050 
SetMenuItemCallback(int in_item,kis_menuitem_cb in_cb,void * in_aux)1051 void Kis_Menu::SetMenuItemCallback(int in_item, kis_menuitem_cb in_cb, void *in_aux) {
1052 	int mid = in_item / 100;
1053 	int iid = (in_item % 100) - 1;
1054 
1055 	if (mid < 0 || mid >= (int) menubar.size())
1056 		return;
1057 
1058 	if (iid < 0 || iid > (int) menubar[mid]->items.size())
1059 		return;
1060 
1061 	menubar[mid]->items[iid]->callback = in_cb;
1062 	menubar[mid]->items[iid]->auxptr = in_aux;
1063 }
1064 
ClearMenuItemCallback(int in_item)1065 void Kis_Menu::ClearMenuItemCallback(int in_item) {
1066 	int mid = in_item / 100;
1067 	int iid = (in_item % 100) - 1;
1068 
1069 	if (mid < 0 || mid >= (int) menubar.size())
1070 		return;
1071 
1072 	if (iid < 0 || iid > (int) menubar[mid]->items.size())
1073 		return;
1074 
1075 	menubar[mid]->items[iid]->callback = NULL;
1076 	menubar[mid]->items[iid]->auxptr = NULL;
1077 }
1078 
SetMenuItemCheckSymbol(int in_item,char in_sym)1079 void Kis_Menu::SetMenuItemCheckSymbol(int in_item, char in_sym) {
1080 	int mid = in_item / 100;
1081 	int iid = (in_item % 100) - 1;
1082 
1083 	if (mid < 0 || mid >= (int) menubar.size())
1084 		return;
1085 
1086 	if (iid < 0 || iid > (int) menubar[mid]->items.size())
1087 		return;
1088 
1089 	menubar[mid]->items[iid]->checksymbol = in_sym;
1090 }
1091 
EnableMenuItem(int in_item)1092 void Kis_Menu::EnableMenuItem(int in_item) {
1093 	int mid = in_item / 100;
1094 	int iid = (in_item % 100) - 1;
1095 
1096 	if (mid < 0 || mid >= (int) menubar.size())
1097 		return;
1098 
1099 	if (iid < 0 || iid > (int) menubar[mid]->items.size())
1100 		return;
1101 
1102 	menubar[mid]->items[iid]->enabled = 1;
1103 }
1104 
EnableAllItems(int in_menu)1105 void Kis_Menu::EnableAllItems(int in_menu) {
1106 	if (in_menu < 0 || in_menu >= (int) menubar.size())
1107 		return;
1108 
1109 	for (unsigned int x = 0; x < menubar[in_menu]->items.size(); x++)
1110 		menubar[in_menu]->items[x]->enabled = 1;
1111 }
1112 
DisableAllItems(int in_menu)1113 void Kis_Menu::DisableAllItems(int in_menu) {
1114 	if (in_menu < 0 || in_menu >= (int) menubar.size())
1115 		return;
1116 
1117 	for (unsigned int x = 0; x < menubar[in_menu]->items.size(); x++)
1118 		menubar[in_menu]->items[x]->enabled = 0;
1119 }
1120 
SetMenuItemVis(int in_item,int in_vis)1121 void Kis_Menu::SetMenuItemVis(int in_item, int in_vis) {
1122 	int mid = in_item / 100;
1123 	int iid = (in_item % 100) - 1;
1124 
1125 	if (mid < 0 || mid >= (int) menubar.size())
1126 		return;
1127 
1128 	if (iid < 0 || iid > (int) menubar[mid]->items.size())
1129 		return;
1130 
1131 	menubar[mid]->items[iid]->visible = in_vis;
1132 }
1133 
ClearMenus()1134 void Kis_Menu::ClearMenus() {
1135 	// Deconstruct the menubar
1136 	for (unsigned int x = 0; x < menubar.size(); x++) {
1137 		for (unsigned int y = 0; y < menubar[x]->items.size(); y++)
1138 			delete menubar[x]->items[y];
1139 		delete menubar[x];
1140 	}
1141 }
1142 
FindMenu(string in_menu)1143 int Kis_Menu::FindMenu(string in_menu) {
1144 	for (unsigned int x = 0; x < menubar.size(); x++) {
1145 		if (menubar[x]->text == in_menu)
1146 			return menubar[x]->id;
1147 	}
1148 
1149 	return -1;
1150 }
1151 
Activate(int subcomponent)1152 void Kis_Menu::Activate(int subcomponent) {
1153 	Kis_Panel_Component::Activate(subcomponent);
1154 
1155 	cur_menu = subcomponent - 1;
1156 	cur_item = -1;
1157 	sub_menu = -1;
1158 	sub_item = -1;
1159 }
1160 
Deactivate()1161 void Kis_Menu::Deactivate() {
1162 	Kis_Panel_Component::Deactivate();
1163 
1164 	cur_menu = -1;
1165 	cur_item = -1;
1166 	sub_menu = -1;
1167 	sub_item = -1;
1168 	mouse_triggered = 0;
1169 
1170 	if (submenuwin) {
1171 		delwin(submenuwin);
1172 		submenuwin = NULL;
1173 	}
1174 
1175 	if (menuwin) {
1176 		delwin(menuwin);
1177 		menuwin = NULL;
1178 	}
1179 }
1180 
DrawMenu(_menu * menu,WINDOW * win,int hpos,int vpos)1181 void Kis_Menu::DrawMenu(_menu *menu, WINDOW *win, int hpos, int vpos) {
1182 	_menu *submenu = NULL;
1183 	int subvpos = -1;
1184 	int subhpos = -1;
1185 	int dsz = 0;
1186 	int width_add_check = 0, width_add_en = 0, mod_width = 0;
1187 
1188 	// Resize the menu window, taking invisible items into account, also
1189 	// figure out the offset for any checked or disabled items
1190 	for (unsigned int y = 0; y < menu->items.size(); y++) {
1191 		if (menu->items[y]->visible) {
1192 			dsz++;
1193 
1194 			if (menu->items[y]->checked > -1)
1195 				width_add_check = 3;
1196 
1197 			if (menu->items[y]->enabled < 1)
1198 				width_add_en = 2;
1199 
1200 		}
1201 	}
1202 
1203 	mod_width = menu->width + 5 + width_add_check + width_add_en;
1204 
1205 	wresize(win, dsz + 2, mod_width);
1206 
1207 	// move it
1208 	mvderwin(win, vpos, hpos);
1209 
1210 	// Draw the box
1211 	wattrset(win, border_color);
1212 	box(win, 0, 0);
1213 
1214 	// Use dsz as the position to draw into
1215 	dsz = 0;
1216 	for (unsigned int y = 0; y < menu->items.size(); y++) {
1217 		string menuline;
1218 
1219 		if (menu->items[y]->visible == 0)
1220 			continue;
1221 
1222 		// Shortcut out a spacer
1223 		if (menu->items[y]->text[0] == '-') {
1224 			wattrset(win, border_color);
1225 			mvwhline(win, 1 + dsz, 1, ACS_HLINE, mod_width - 1);
1226 			mvwaddch(win, 1 + dsz, 0, ACS_LTEE);
1227 			mvwaddch(win, 1 + dsz, mod_width - 1, ACS_RTEE);
1228 			dsz++;
1229 			continue;
1230 		}
1231 
1232 		wattrset(win, text_color);
1233 
1234 		if (menu->items[y]->colorpair != -1)
1235 			wattrset(win, menu->items[y]->colorpair);
1236 
1237 		// Hilight the current item
1238 		if (((int) menu->id == cur_menu && (int) y == cur_item) ||
1239 			((int) menu->id == sub_menu && (int) y == sub_item))
1240 			wattron(win, WA_REVERSE);
1241 
1242 		// Draw the check
1243 		if (menu->items[y]->checked == 1) {
1244 			string cs = "  ";
1245 			cs[0] = menu->items[y]->checksymbol;
1246 			menuline += cs;
1247 		} else if (menu->items[y]->checked == 0 || menu->checked > -1) {
1248 			menuline += "  ";
1249 		}
1250 
1251 		// Dim a disabled item
1252 		if (menu->items[y]->enabled == 0) {
1253 			wattrset(win, disable_color);
1254 		}
1255 
1256 		// Format it with 'Foo     F'
1257 		if (menu->items[y]->enabled == 0)
1258 			menuline += "(";
1259 		menuline += menu->items[y]->text;
1260 		if (menu->items[y]->enabled == 0)
1261 			menuline += ")";
1262 		menuline += " ";
1263 		for (unsigned int z = menuline.length();
1264 			 (int) z <= mod_width - 5; z++) {
1265 			menuline = menuline + string(" ");
1266 		}
1267 
1268 		if (menu->items[y]->submenu != -1) {
1269 			menuline = menuline + ">>";
1270 
1271 			// Draw again, using our submenu, if it's active
1272 			if (menu->items[y]->submenu == cur_menu) {
1273 				submenu = menubar[menu->items[y]->submenu];
1274 				subvpos = vpos + dsz;
1275 				subhpos = hpos + menu->width + 6;
1276 			}
1277 		} else if (menu->items[y]->extrachar != 0) {
1278 			menuline = menuline + " " + menu->items[y]->extrachar;
1279 		} else {
1280 			menuline = menuline + "  ";
1281 		}
1282 
1283 		// Print it
1284 		mvwaddstr(win, 1 + dsz, 1, menuline.c_str());
1285 
1286 		if (((int) menu->id == cur_menu && (int) y == cur_item) ||
1287 			((int) menu->id == sub_menu && (int) y == sub_item))
1288 			wattroff(win, WA_REVERSE);
1289 
1290 		dsz++;
1291 	}
1292 
1293 	// Draw the expanded submenu
1294 	if (subvpos > 0 && subhpos > 0) {
1295 		if (submenuwin == NULL)
1296 			submenuwin = derwin(window, 1, 1, 0, 0);
1297 
1298 		DrawMenu(submenu, submenuwin, subhpos, subvpos);
1299 	}
1300 }
1301 
DrawComponent()1302 void Kis_Menu::DrawComponent() {
1303 	if (visible == 0)
1304 		return;
1305 
1306 	parent_panel->ColorFromPref(text_color, "menu_text_color");
1307 	parent_panel->ColorFromPref(border_color, "menu_border_color");
1308 	parent_panel->ColorFromPref(disable_color, "menu_disable_color");
1309 
1310 	int hpos = 3;
1311 
1312 	if (menuwin == NULL)
1313 		menuwin = derwin(window, 1, 1, 0, 0);
1314 
1315 	wattron(window, border_color);
1316 	mvwaddstr(window, sy, sx + 1, "~ ");
1317 
1318 	// Draw the menu bar itself
1319 	for (unsigned int x = 0; x < menubar.size(); x++) {
1320 		if (menubar[x]->submenu || menubar[x]->visible == 0)
1321 			continue;
1322 
1323 		wattron(window, text_color);
1324 
1325 		// If the current menu is the selected one, hilight it
1326 		if ((int) x == cur_menu || (int) x == sub_menu)
1327 			wattron(window, WA_REVERSE);
1328 
1329 		// Draw the menu
1330 		mvwaddstr(window, sy, sx + hpos, (menubar[x]->text).c_str());
1331 		// Set the hilight
1332 		if (menubar[x]->targchar >= 0) {
1333 			wattron(window, WA_UNDERLINE);
1334 			mvwaddch(window, sy, sx + hpos + menubar[x]->targchar,
1335 					 menubar[x]->text[menubar[x]->targchar]);
1336 			wattroff(window, WA_UNDERLINE);
1337 		}
1338 
1339 		wattroff(window, WA_REVERSE);
1340 
1341 		mvwaddstr(window, sy, sx + hpos + menubar[x]->text.length(), " ");
1342 
1343 		// Draw the menu itself, if we've got an item selected in it
1344 		if (((int) x == cur_menu || (int) x == sub_menu) &&
1345 			(sub_item >= 0 || cur_item >= 0 || mouse_triggered)) {
1346 
1347 			DrawMenu(menubar[x], menuwin, sx + hpos, sy + 1);
1348 		}
1349 
1350 		hpos += menubar[x]->text.length() + 1;
1351 	}
1352 	wattroff(window, text_color);
1353 }
1354 
FindNextEnabledItem()1355 void Kis_Menu::FindNextEnabledItem() {
1356 	int looped = 0;
1357 
1358 	// Handle disabled and spacer items
1359 	if (menubar[cur_menu]->items[cur_item]->enabled == 0) {
1360 		// find the next enabled item
1361 		for (int i = cur_item; i <= (int) menubar[cur_menu]->items.size(); i++) {
1362 			// Loop
1363 			if (i >= (int) menubar[cur_menu]->items.size()) {
1364 				looped = 1;
1365 				i = 0;
1366 			}
1367 
1368 			if (looped && i == cur_item) {
1369 				cur_item = 0;
1370 				break;
1371 			}
1372 
1373 			if (menubar[cur_menu]->items[i]->visible == 0)
1374 				continue;
1375 
1376 			if (menubar[cur_menu]->items[i]->enabled) {
1377 				cur_item = i;
1378 				break;
1379 			}
1380 		}
1381 	}
1382 }
1383 
FindPrevEnabledItem()1384 void Kis_Menu::FindPrevEnabledItem() {
1385 	int looped = 0;
1386 
1387 	// Handle disabled and spacer items
1388 	if (menubar[cur_menu]->items[cur_item]->enabled == 0) {
1389 		// find the next enabled item
1390 		for (int i = cur_item; i >= -1; i--) {
1391 			// Loop
1392 			if (i < 0) {
1393 				i = menubar[cur_menu]->items.size() - 1;
1394 				looped = 1;
1395 			}
1396 
1397 			if (looped && i == cur_item) {
1398 				cur_item = 0;
1399 				break;
1400 			}
1401 
1402 			if (menubar[cur_menu]->items[i]->visible == 0)
1403 				continue;
1404 
1405 			if (menubar[cur_menu]->items[i]->enabled) {
1406 				cur_item = i;
1407 				break;
1408 			}
1409 		}
1410 	}
1411 }
1412 
KeyPress(int in_key)1413 int Kis_Menu::KeyPress(int in_key) {
1414 	if (visible == 0)
1415 		return -1;
1416 
1417 	// Activate menu
1418 	if (in_key == '~' || in_key == '`' || in_key == 0x1B) {
1419 		if (cur_menu < 0) {
1420 			Activate(1);
1421 		} else {
1422 			// Break out of submenus
1423 			if (sub_menu != -1) {
1424 				cur_menu = sub_menu;
1425 				cur_item = sub_item;
1426 				sub_menu = sub_item = -1;
1427 
1428 				if (submenuwin) {
1429 					delwin(submenuwin);
1430 					submenuwin = NULL;
1431 				}
1432 
1433 				return 0;
1434 			}
1435 
1436 			Deactivate();
1437 		}
1438 
1439 		// We consume it but the framework doesn't get a state change
1440 		return -1;
1441 	}
1442 
1443 	// Menu movement
1444 	if (in_key == KEY_RIGHT && cur_menu >= 0) {
1445 
1446 		// Break out of submenus on l/r
1447 		if (sub_menu != -1) {
1448 			cur_menu = sub_menu;
1449 			cur_item = sub_item;
1450 			sub_menu = sub_item = -1;
1451 			return -1;
1452 		}
1453 
1454 		for (unsigned int nm = cur_menu + 1; nm < menubar.size(); nm++) {
1455 			if (menubar[nm]->submenu == 0) {
1456 				cur_menu = nm;
1457 				cur_item = 0;
1458 				FindNextEnabledItem();
1459 				break;
1460 			}
1461 		}
1462 
1463 		return -1;
1464 	}
1465 
1466 	if (in_key == KEY_LEFT && cur_menu > 0) {
1467 		// Break out of submenus on l/r
1468 		if (sub_menu != -1) {
1469 			cur_menu = sub_menu;
1470 			cur_item = sub_item;
1471 			sub_menu = sub_item = -1;
1472 			return -1;
1473 		}
1474 
1475 		for (int nm = cur_menu - 1; nm >= 0; nm--) {
1476 			if (menubar[nm]->submenu == 0) {
1477 				cur_menu = nm;
1478 				cur_item = 0;
1479 				FindNextEnabledItem();
1480 				break;
1481 			}
1482 		}
1483 
1484 		return -1;
1485 	}
1486 
1487 	if (in_key == KEY_DOWN && cur_menu >= 0 &&
1488 		cur_item <= (int) menubar[cur_menu]->items.size() - 1) {
1489 
1490 		if (cur_item == (int) menubar[cur_menu]->items.size() - 1) {
1491 			cur_item = 0;
1492 			FindNextEnabledItem();
1493 			return -1;
1494 		}
1495 
1496 		cur_item++;
1497 
1498 		FindNextEnabledItem();
1499 
1500 		return -1;
1501 	}
1502 
1503 	if (in_key == KEY_UP && cur_item >= 0) {
1504 		if (cur_item == 0) {
1505 			cur_item = menubar[cur_menu]->items.size() - 1;
1506 			FindPrevEnabledItem();
1507 			return -1;
1508 		}
1509 
1510 		cur_item--;
1511 
1512 		FindPrevEnabledItem();
1513 
1514 		return -1;
1515 	}
1516 
1517 	// Space or enter
1518 	if ((in_key == ' ' || in_key == 0x0A || in_key == KEY_ENTER) && cur_menu >= 0) {
1519 		if (cur_item == -1) {
1520 			cur_item = 0;
1521 			FindNextEnabledItem();
1522 			return -1;
1523 		}
1524 
1525 		// Are we entering a submenu?
1526 		if (sub_menu == -1 && menubar[cur_menu]->items[cur_item]->submenu != -1) {
1527 			// Remember where we were
1528 			sub_menu = cur_menu;
1529 			sub_item = cur_item;
1530 			cur_menu = menubar[cur_menu]->items[cur_item]->submenu;
1531 			cur_item = 0;
1532 			return -1;
1533 		}
1534 
1535 		if (menubar[cur_menu]->items[cur_item]->enabled == 0) {
1536 			FindNextEnabledItem();
1537 			return -1;
1538 		}
1539 
1540 		int ret = (cur_menu * 100) + cur_item + 1;
1541 
1542 		// Per-menu callbacks
1543 		if (menubar[cur_menu]->items[cur_item]->callback != NULL)
1544 			(*(menubar[cur_menu]->items[cur_item]->callback))(globalreg, ret,
1545 					menubar[cur_menu]->items[cur_item]->auxptr);
1546 
1547 		// Widget-wide callbacks
1548 		if (cb_activate != NULL)
1549 			(*cb_activate)(this, ret, cb_activate_aux, globalreg);
1550 
1551 		Deactivate();
1552 
1553 		// Generic fallthrough
1554 		return ret;
1555 	}
1556 
1557 	// Key shortcuts
1558 	if (cur_menu >= 0) {
1559 		if (cur_item < 0) {
1560 			// Try w/ the proper case
1561 			for (unsigned int x = 0; x < menubar.size(); x++) {
1562 				if (in_key == menubar[x]->text[menubar[x]->targchar]) {
1563 					cur_menu = x;
1564 					cur_item = 0;
1565 					FindNextEnabledItem();
1566 					return -1;
1567 				}
1568 			}
1569 			// Try with lowercase, if we didn't find one already
1570 			for (unsigned int x = 0; x < menubar.size(); x++) {
1571 				if (tolower(in_key) ==
1572 					tolower(menubar[x]->text[menubar[x]->targchar])) {
1573 					cur_menu = x;
1574 					cur_item = 0;
1575 					FindNextEnabledItem();
1576 					return -1;
1577 				}
1578 			}
1579 			return -1;
1580 		} else {
1581 			for (unsigned int x = 0; x < menubar[cur_menu]->items.size(); x++) {
1582 				if (in_key == menubar[cur_menu]->items[x]->extrachar &&
1583 					menubar[cur_menu]->items[x]->enabled == 1) {
1584 					int ret = (cur_menu * 100) + x + 1;
1585 
1586 					// Per-menu callbacks
1587 					if (menubar[cur_menu]->items[x]->callback != NULL) {
1588 						(*(menubar[cur_menu]->items[x]->callback))
1589 							(globalreg, ret, menubar[cur_menu]->items[x]->auxptr);
1590 					}
1591 
1592 					// Widget-wide callbacks
1593 					if (cb_activate != NULL)
1594 						(*cb_activate)(this, ret, cb_activate_aux, globalreg);
1595 
1596 					Deactivate();
1597 
1598 					// Generic fallthrough
1599 					return ret;
1600 				}
1601 			}
1602 			return -1;
1603 		}
1604 	}
1605 
1606 	return 0;
1607 }
1608 
MouseEvent(MEVENT * mevent)1609 int Kis_Menu::MouseEvent(MEVENT *mevent) {
1610 	// Menu win/subwin coordinates
1611 	int wbx, wby, wlx, wly;
1612 	int match_any_win = 0;
1613 
1614 	if (mevent->bstate == 4 && mevent->y == sy) {
1615 		// Click happened somewhere in the menubar
1616 		int hpos = 3;
1617 		for (unsigned int x = 0; x < menubar.size(); x++) {
1618 			if (menubar[x]->submenu || menubar[x]->visible == 0)
1619 				continue;
1620 
1621 			if (mevent->x < hpos)
1622 				break;
1623 
1624 			if (mevent->x <= hpos + (int) menubar[x]->text.length()) {
1625 				if ((int) x == cur_menu) {
1626 					Deactivate();
1627 					// Consume w/ no state change to caller
1628 					return -1;
1629 				} else {
1630 					Activate(0);
1631 					cur_menu = x;
1632 					cur_item = 0;
1633 
1634 					FindNextEnabledItem();
1635 
1636 					sub_menu = -1;
1637 					sub_item = -1;
1638 
1639 					if (submenuwin) {
1640 						delwin(submenuwin);
1641 						submenuwin = NULL;
1642 					}
1643 
1644 					mouse_triggered = 1;
1645 					// Consume w/ no state change to caller
1646 					return -1;
1647 				}
1648 			}
1649 
1650 			hpos += menubar[x]->text.length() + 1;
1651 		} /* menu list */
1652 	} else if (mevent->bstate == 4 && mevent->y > sy && cur_menu >= 0) {
1653 		// If we have a submenu
1654 		if (submenuwin) {
1655 			// See if we fall w/in it
1656 			getparyx(submenuwin, wby, wbx);
1657 			getmaxyx(submenuwin, wly, wlx);
1658 
1659 			// If we're anywhere in the window we don't close menus
1660 			if (mevent->x >= wbx && mevent->x < wbx + wlx &&
1661 				mevent->y >= wby && mevent->y < wby + wly)
1662 				match_any_win = 1;
1663 
1664 			// Our range shouldn't include the borders
1665 			if (mevent->x > wbx && mevent->x < wbx + wlx &&
1666 				mevent->y > wby && mevent->y < wby + wly - 1) {
1667 
1668 				int mitem = mevent->y - wby - 1;
1669 
1670 				if (mitem >= 0 && mitem < (int) menubar[cur_menu]->items.size()) {
1671 					if (menubar[cur_menu]->items[mitem]->enabled == 1) {
1672 
1673 						int ret = (cur_menu * 100) + mitem + 1;
1674 
1675 						// Per-menu callbacks
1676 						if (menubar[cur_menu]->items[mitem]->callback != NULL)
1677 							(*(menubar[cur_menu]->items[mitem]->callback))
1678 								(globalreg, ret,
1679 								 menubar[cur_menu]->items[mitem]->auxptr);
1680 
1681 						// Widget-wide callbacks
1682 						if (cb_activate != NULL)
1683 							(*cb_activate)(this, ret, cb_activate_aux, globalreg);
1684 
1685 						Deactivate();
1686 
1687 						return ret;
1688 					}
1689 				}
1690 			}
1691 		}
1692 
1693 		if (menuwin) {
1694 			// See if we fall w/in the main menu
1695 			getparyx(menuwin, wby, wbx);
1696 			getmaxyx(menuwin, wly, wlx);
1697 
1698 			// If we're anywhere in the window we don't close menus
1699 			if (mevent->x >= wbx && mevent->x < wbx + wlx &&
1700 				mevent->y >= wby && mevent->y < wby + wly)
1701 				match_any_win = 1;
1702 
1703 			// Our range shouldn't include the borders
1704 			if (mevent->x > wbx && mevent->x < wbx + wlx &&
1705 				mevent->y > wby && mevent->y < wby + wly - 1) {
1706 
1707 				// If we had a sub menu, close it
1708 				if (sub_menu >= 0) {
1709 					cur_menu = sub_menu;
1710 					cur_item = sub_item;
1711 
1712 					sub_menu = sub_item = -1;
1713 
1714 					if (submenuwin) {
1715 						delwin(submenuwin);
1716 						submenuwin = NULL;
1717 					}
1718 
1719 					// And drop out of processing now, we don't want to select
1720 					// the original menu item
1721 					return -1;
1722 				}
1723 
1724 				int mitem = mevent->y - wby - 1;
1725 
1726 				if (mitem >= 0 && mitem < (int) menubar[cur_menu]->items.size()) {
1727 					if (menubar[cur_menu]->items[mitem]->enabled == 1) {
1728 
1729 						// Are we entering a submenu?
1730 						if (menubar[cur_menu]->items[mitem]->submenu != -1) {
1731 							// Remember where we were
1732 							sub_menu = cur_menu;
1733 							sub_item = cur_item;
1734 							cur_menu = menubar[cur_menu]->items[mitem]->submenu;
1735 							cur_item = 0;
1736 							return -1;
1737 						}
1738 
1739 						// Otherwise, trigger the menu item
1740 						int ret = (cur_menu * 100) + mitem + 1;
1741 
1742 						// Per-menu callbacks
1743 						if (menubar[cur_menu]->items[mitem]->callback != NULL)
1744 							(*(menubar[cur_menu]->items[mitem]->callback))
1745 								(globalreg, ret,
1746 								 menubar[cur_menu]->items[mitem]->auxptr);
1747 
1748 						// Widget-wide callbacks
1749 						if (cb_activate != NULL)
1750 							(*cb_activate)(this, ret, cb_activate_aux, globalreg);
1751 
1752 						Deactivate();
1753 
1754 						return ret;
1755 					}
1756 				}
1757 			}
1758 		}
1759 
1760 		// Close menus entirely if we're clicking somewhere else in the screen
1761 		if (match_any_win == 0) {
1762 			Deactivate();
1763 			return -1;
1764 		}
1765 
1766 #if 0
1767 		int hpos = 3;
1768 		int ypos = sy + 2;
1769 		for (unsigned int x = 0; x < (unsigned int) (cur_menu + 1); x++) {
1770 			if (menubar[x]->submenu || menubar[x]->visible == 0)
1771 				continue;
1772 
1773 			if (mevent->x < hpos)
1774 				break;
1775 
1776 			hpos += menubar[x]->text.length() + 1;
1777 
1778 			if (mevent->x <= hpos + (int) menubar[x]->text.length()) {
1779 				for (unsigned int y = 0; y < menubar[x]->items.size(); y++) {
1780 					if (menubar[x]->items[y]->visible == 0)
1781 						continue;
1782 
1783 					if (menubar[x]->items[y]->text[0] == '-') {
1784 						ypos++;
1785 						continue;
1786 					}
1787 
1788 					if (ypos == mevent->y) {
1789 						if (menubar[x]->items[y]->enabled == 0)
1790 							return -1;
1791 
1792 						// Trigger the item
1793 						if (cb_activate != NULL)
1794 							(*cb_activate)(this, (cur_menu * 100) + y + 1,
1795 										   cb_activate_aux, globalreg);
1796 
1797 						Deactivate();
1798 
1799 						return (cur_menu * 100) + y + 1;
1800 					}
1801 
1802 					ypos++;
1803 				}
1804 			}
1805 		}
1806 
1807 #endif
1808 
1809 	}
1810 
1811 	return 0;
1812 }
1813 
Kis_Free_Text(GlobalRegistry * in_globalreg,Kis_Panel * in_panel)1814 Kis_Free_Text::Kis_Free_Text(GlobalRegistry *in_globalreg, Kis_Panel *in_panel) :
1815 	Kis_Panel_Component(in_globalreg, in_panel) {
1816 	globalreg = in_globalreg;
1817 	scroll_pos = 0;
1818 	SetMinSize(1, 1);
1819 	alignment = 0;
1820 	follow_tail = 0;
1821 	max_text = -1;
1822 }
1823 
~Kis_Free_Text()1824 Kis_Free_Text::~Kis_Free_Text() {
1825 	// Nothing
1826 }
1827 
DrawComponent()1828 void Kis_Free_Text::DrawComponent() {
1829 	if (visible == 0)
1830 		return;
1831 
1832 	int c;
1833 
1834 	parent_panel->ColorFromPref(color_active, color_active_pref);
1835 	parent_panel->ColorFromPref(color_inactive, color_inactive_pref);
1836 
1837 	c = SetTransColor(color_active);
1838 
1839 	if (ly < (int) text_vec.size() && follow_tail && scroll_pos < 0)
1840 		scroll_pos = text_vec.size() - ly + 1;
1841 
1842 	if (scroll_pos < 0 || scroll_pos > (int) text_vec.size())
1843 		scroll_pos = 0;
1844 
1845 	int px = 0;
1846 	for (unsigned int x = scroll_pos; x < text_vec.size() && px < ly; x++) {
1847 		// Use the special formatter
1848 		Kis_Panel_Specialtext::Mvwaddnstr(window, sy + px, sx,
1849 										  text_vec[x],
1850 										  lx - 1, parent_panel, c);
1851 		px++;
1852 	}
1853 
1854 	if ((int) text_vec.size() > ly) {
1855 		// Draw the hash scroll bar
1856 		mvwvline(window, sy, sx + lx - 1, ACS_VLINE, ly);
1857 		// Figure out how far down our text we are
1858 		// int perc = ey * (scroll_pos / text_vec.size());
1859 		float perc = (float) ly * (float) ((float) (scroll_pos) /
1860 										   (float) (text_vec.size() - ly));
1861 		wattron(window, WA_REVERSE);
1862 		// Draw the solid position
1863 		mvwaddch(window, sy + (int) perc, sx + lx - 1, ACS_BLOCK);
1864 
1865 		wattroff(window, WA_REVERSE);
1866 	}
1867 }
1868 
KeyPress(int in_key)1869 int Kis_Free_Text::KeyPress(int in_key) {
1870 	if (visible == 0)
1871 		return 0;
1872 
1873 	int scrollable = 1;
1874 
1875 	if ((int) text_vec.size() <= ly)
1876 		scrollable = 0;
1877 
1878 	if (scrollable && in_key == KEY_UP && scroll_pos > 0) {
1879 		scroll_pos--;
1880 		return 0;
1881 	}
1882 
1883 	if (scrollable && in_key == KEY_DOWN &&
1884 		// Don't allow scrolling off the end
1885 		scroll_pos < ((int) text_vec.size() - ly)) {
1886 		scroll_pos++;
1887 		return 0;
1888 	}
1889 
1890 	if (scrollable && in_key == KEY_PPAGE && scroll_pos > 0) {
1891 		scroll_pos -= (ly - 1);
1892 		if (scroll_pos < 0)
1893 			scroll_pos = 0;
1894 		return 0;
1895 	}
1896 
1897 	if (scrollable && in_key == KEY_NPAGE) {
1898 		scroll_pos += (ly - 1);
1899 		if (scroll_pos >= ((int) text_vec.size() - ly))
1900 			scroll_pos = ((int) text_vec.size() - ly);
1901 		return 0;
1902 	}
1903 
1904 	return 1;
1905 }
1906 
SetText(string in_text)1907 void Kis_Free_Text::SetText(string in_text) {
1908 	text_vec = StrTokenize(in_text, "\n");
1909 	SetPreferredSize(Kis_Panel_Specialtext::Strlen(in_text), 1);
1910 }
1911 
SetText(vector<string> in_text)1912 void Kis_Free_Text::SetText(vector<string> in_text) {
1913 	unsigned int ml = 0;
1914 
1915 	for (unsigned x = 0; x < in_text.size(); x++) {
1916 		if (Kis_Panel_Specialtext::Strlen(in_text[x]) > ml)
1917 			ml = Kis_Panel_Specialtext::Strlen(in_text[x]);
1918 	}
1919 
1920 	text_vec = in_text;
1921 
1922 	SetPreferredSize(ml, in_text.size());
1923 
1924 	if (follow_tail)
1925 		scroll_pos = -1;
1926 }
1927 
AppendText(string in_text)1928 void Kis_Free_Text::AppendText(string in_text) {
1929 	text_vec.push_back(in_text);
1930 
1931 	if (max_text > 0 && (int) text_vec.size() > max_text) {
1932 		text_vec.erase(text_vec.begin(), text_vec.begin() + text_vec.size() - max_text);
1933 	}
1934 
1935 	if (lx < (int) Kis_Panel_Specialtext::Strlen(in_text))
1936 		SetPreferredSize(Kis_Panel_Specialtext::Strlen(in_text), text_vec.size());
1937 
1938 	// If we're following the tail then jump to the bottom when we add text
1939 	if (ly < (int) text_vec.size() && follow_tail)
1940 		scroll_pos = text_vec.size() - ly;
1941 }
1942 
ProcessMessage(string in_msg,int in_flags)1943 void KisStatusText_Messageclient::ProcessMessage(string in_msg, int in_flags) {
1944 	if ((in_flags & MSGFLAG_INFO)) {
1945 		((Kis_Status_Text *) auxptr)->AddLine("\004bINFO\004B: " + in_msg, 6);
1946 	} else if ((in_flags & MSGFLAG_ERROR)) {
1947 		((Kis_Status_Text *) auxptr)->AddLine("\004rERROR\004R: " + in_msg, 7);
1948 	} else if ((in_flags & MSGFLAG_FATAL)) {
1949 		((Kis_Status_Text *) auxptr)->AddLine("\004rFATAL\004R: " + in_msg, 7);
1950 	} else {
1951 		((Kis_Status_Text *) auxptr)->AddLine(in_msg);
1952 	}
1953 }
1954 
Kis_Status_Text(GlobalRegistry * in_globalreg,Kis_Panel * in_panel)1955 Kis_Status_Text::Kis_Status_Text(GlobalRegistry *in_globalreg, Kis_Panel *in_panel) :
1956 	Kis_Panel_Component(in_globalreg, in_panel) {
1957 	globalreg = in_globalreg;
1958 	scroll_pos = 0;
1959 	status_color_normal = -1;
1960 	parent_panel->InitColorPref("status_normal_color", "white,black");
1961 }
1962 
~Kis_Status_Text()1963 Kis_Status_Text::~Kis_Status_Text() {
1964 	// Nothing
1965 }
1966 
DrawComponent()1967 void Kis_Status_Text::DrawComponent() {
1968 	parent_panel->ColorFromPref(status_color_normal, "status_normal_color");
1969 
1970 	if (visible == 0)
1971 		return;
1972 
1973 	wattrset(window, status_color_normal);
1974 
1975 	for (unsigned int x = 0; x < text_vec.size() && (int) x < ly; x++) {
1976 		Kis_Panel_Specialtext::Mvwaddnstr(window, ey - x, sx,
1977 										  text_vec[text_vec.size() - x - 1],
1978 										  ex - 1, parent_panel, status_color_normal);
1979 	}
1980 }
1981 
KeyPress(int in_key)1982 int Kis_Status_Text::KeyPress(int in_key) {
1983 	if (visible == 0)
1984 		return 0;
1985 
1986 	return 1;
1987 }
1988 
AddLine(string in_line,int headeroffset)1989 void Kis_Status_Text::AddLine(string in_line, int headeroffset) {
1990 	vector<string> lw = LineWrap(in_line, headeroffset, ex - 1);
1991 
1992 	for (unsigned int x = 0; x < lw.size(); x++) {
1993 		text_vec.push_back(lw[x]);
1994 	}
1995 
1996 	if ((int) text_vec.size() > py) {
1997 		text_vec.erase(text_vec.begin(), text_vec.begin() + text_vec.size() - py);
1998 	}
1999 }
2000 
Kis_Field_List(GlobalRegistry * in_globalreg,Kis_Panel * in_panel)2001 Kis_Field_List::Kis_Field_List(GlobalRegistry *in_globalreg, Kis_Panel *in_panel) :
2002 	Kis_Panel_Component(in_globalreg, in_panel) {
2003 	globalreg = in_globalreg;
2004 	scroll_pos = 0;
2005 	field_w = 0;
2006 }
2007 
~Kis_Field_List()2008 Kis_Field_List::~Kis_Field_List() {
2009 	// Nothing
2010 }
2011 
DrawComponent()2012 void Kis_Field_List::DrawComponent() {
2013 	if (visible == 0)
2014 		return;
2015 
2016 	parent_panel->ColorFromPref(color_active, color_active_pref);
2017 	parent_panel->ColorFromPref(color_inactive, color_inactive_pref);
2018 
2019 	SetTransColor(color_active);
2020 
2021 	for (unsigned int x = 0; x < field_vec.size() && (int) x < ey; x++) {
2022 		// Set the field name to bold
2023 		wattron(window, WA_UNDERLINE);
2024 		mvwaddnstr(window, sy + x, sx, field_vec[x + scroll_pos].c_str(), field_w);
2025 		mvwaddch(window, sy + x, sx + field_w, ':');
2026 		wattroff(window, WA_UNDERLINE);
2027 
2028 		// Draw the data, leave room on the end for the scrollbar
2029 		mvwaddnstr(window, sy + x, sx + field_w + 2, data_vec[x + scroll_pos].c_str(),
2030 				   sx - field_w - 3);
2031 	}
2032 
2033 	if ((int) field_vec.size() > ey) {
2034 		// Draw the hash scroll bar
2035 		mvwvline(window, sy, sx + ex - 1, ACS_VLINE, ey);
2036 		// Figure out how far down our text we are
2037 		// int perc = ey * (scroll_pos / text_vec.size());
2038 		float perc = (float) ey * (float) ((float) (scroll_pos) /
2039 										   (float) (field_vec.size() - ey));
2040 		wattron(window, WA_REVERSE);
2041 		// Draw the solid position
2042 		mvwaddch(window, sy + (int) perc, sx + ex - 1, ACS_BLOCK);
2043 
2044 		wattroff(window, WA_REVERSE);
2045 	}
2046 }
2047 
KeyPress(int in_key)2048 int Kis_Field_List::KeyPress(int in_key) {
2049 	if (visible == 0)
2050 		return 0;
2051 
2052 	int scrollable = 1;
2053 
2054 	if ((int) field_vec.size() <= ey)
2055 		scrollable = 0;
2056 
2057 	if (scrollable && in_key == KEY_UP && scroll_pos > 0) {
2058 		scroll_pos--;
2059 		return 0;
2060 	}
2061 
2062 	if (scrollable && in_key == KEY_DOWN &&
2063 		scroll_pos < ((int) field_vec.size() - ey)) {
2064 		scroll_pos++;
2065 		return 0;
2066 	}
2067 
2068 	if (scrollable && in_key == KEY_PPAGE && scroll_pos > 0) {
2069 		scroll_pos -= (ey - 1);
2070 		if (scroll_pos < 0)
2071 			scroll_pos = 0;
2072 		return 0;
2073 	}
2074 
2075 	if (scrollable && in_key == KEY_NPAGE) {
2076 		scroll_pos += (ey - 1);
2077 		if (scroll_pos >= ((int) field_vec.size() - ey))
2078 			scroll_pos = ((int) field_vec.size() - ey);
2079 		return 0;
2080 	}
2081 
2082 	return 1;
2083 }
2084 
AddData(string in_field,string in_data)2085 int Kis_Field_List::AddData(string in_field, string in_data) {
2086 	int pos = field_vec.size();
2087 	field_vec.push_back(in_field);
2088 	data_vec.push_back(in_data);
2089 
2090 	if (in_field.length() > field_w)
2091 		field_w = in_field.length();
2092 
2093 	return (int) pos;
2094 }
2095 
ModData(unsigned int in_row,string in_field,string in_data)2096 int Kis_Field_List::ModData(unsigned int in_row, string in_field, string in_data) {
2097 	if (in_row >= field_vec.size())
2098 		return -1;
2099 
2100 	field_vec[in_row] = in_field;
2101 	data_vec[in_row] = in_data;
2102 
2103 	return (int) in_row;
2104 }
2105 
Kis_Scrollable_Table(GlobalRegistry * in_globalreg,Kis_Panel * in_panel)2106 Kis_Scrollable_Table::Kis_Scrollable_Table(GlobalRegistry *in_globalreg,
2107 										   Kis_Panel *in_panel) :
2108 	Kis_Panel_Component(in_globalreg, in_panel) {
2109 
2110 	globalreg = in_globalreg;
2111 
2112 	scroll_pos = 0;
2113 	hscroll_pos = 0;
2114 	selected = -1;
2115 
2116 	SetMinSize(0, 3);
2117 
2118 	draw_lock_scroll_top = 0;
2119 	draw_highlight_selected = 1;
2120 	draw_titles = 1;
2121 }
2122 
~Kis_Scrollable_Table()2123 Kis_Scrollable_Table::~Kis_Scrollable_Table() {
2124 	for (unsigned int x = 0; x < data_vec.size(); x++) {
2125 		delete data_vec[x];
2126 	}
2127 }
2128 
DrawComponent()2129 void Kis_Scrollable_Table::DrawComponent() {
2130 	if (visible == 0)
2131 		return;
2132 
2133 	parent_panel->ColorFromPref(color_active, color_active_pref);
2134 	parent_panel->ColorFromPref(color_inactive, color_inactive_pref);
2135 
2136 	SetTransColor(color_active);
2137 
2138 	// Current character position x
2139 	int xcur = 0;
2140 	int ycur = 0;
2141 	string ftxt;
2142 
2143 	// Assign widths to '0' sized things by dividing what's left
2144 	// into them.  We'll assume the caller doesn't generate a horizontally
2145 	// scrollable table with variable width fields.
2146 	int ndynf = 0, spare = lx;
2147 
2148 	if ((int) data_vec.size() > ly)
2149 		spare -= 1;
2150 
2151 	for (unsigned int x = 0; x < title_vec.size(); x++) {
2152 		title_vec[x].draw_width = title_vec[x].width;
2153 
2154 		if (title_vec[x].width < 0)
2155 			continue;
2156 
2157 		if (title_vec[x].width == 0) {
2158 			ndynf++;
2159 			continue;
2160 		}
2161 
2162 		spare -= (title_vec[x].draw_width + 1);
2163 	}
2164 	// Distribute the spare over the rest
2165 	if (spare > 0) {
2166 		for (unsigned int x = 0; x < title_vec.size() && ndynf > 0; x++) {
2167 			if (title_vec[x].width == 0) {
2168 				title_vec[x].draw_width = spare / ndynf;
2169 				spare -= spare / ndynf--;
2170 			}
2171 		}
2172 	}
2173 
2174 	// Print across the titles
2175 	if (draw_titles) {
2176 		wattron(window, WA_UNDERLINE);
2177 		for (unsigned int x = hscroll_pos; x < title_vec.size() && xcur < lx; x++) {
2178 
2179 			int w = title_vec[x].draw_width;
2180 
2181 			if (xcur + w >= ex)
2182 				w = lx - xcur;
2183 
2184 			// Align the field w/in the width
2185 			ftxt = AlignString(title_vec[x].title, ' ', title_vec[x].alignment, w);
2186 
2187 			// Write it out
2188 			mvwaddstr(window, sy, sx + xcur, ftxt.c_str());
2189 
2190 			// Advance by the width + 1
2191 			xcur += w + 1;
2192 		}
2193 		wattroff(window, WA_UNDERLINE);
2194 		ycur += 1;
2195 	}
2196 
2197 	if ((int) data_vec.size() > ly) {
2198 		// Draw the scroll bar
2199 		mvwvline(window, sy, sx + lx - 1, ACS_VLINE, ly);
2200 		float perc = (float) ly * (float) ((float) (scroll_pos) /
2201 										   (float) (data_vec.size() - ly));
2202 		if (perc > ly - 1)
2203 			perc = ly - 1;
2204 		wattron(window, WA_REVERSE);
2205 		mvwaddch(window, sy + (int) perc, sx + lx - 1, ACS_BLOCK);
2206 		wattroff(window, WA_REVERSE);
2207 	}
2208 
2209 	// Jump to the scroll location to start drawing rows
2210 	for (unsigned int r = scroll_pos ? scroll_pos : 0;
2211 		 r < data_vec.size() && ycur < ly; r++) {
2212 		// Print across
2213 		xcur = 0;
2214 
2215 		if ((int) r == selected && draw_highlight_selected) {
2216 			wattron(window, WA_REVERSE);
2217 			mvwhline(window, sy + ycur, sx, ' ', lx);
2218 		}
2219 
2220 		for (unsigned int x = hscroll_pos; x < data_vec[r]->data.size() &&
2221 			 xcur < lx && x < title_vec.size(); x++) {
2222 			int w = title_vec[x].draw_width;
2223 
2224 			if (xcur + w >= lx)
2225 				w = lx - xcur;
2226 
2227 			ftxt = AlignString(data_vec[r]->data[x], ' ', title_vec[x].alignment, w);
2228 
2229 			mvwaddstr(window, sy + ycur, sx + xcur, ftxt.c_str());
2230 
2231 			xcur += w + 1;
2232 		}
2233 
2234 		if ((int) r == selected && draw_highlight_selected)
2235 			wattroff(window, WA_REVERSE);
2236 
2237 		ycur += 1;
2238 
2239 	}
2240 }
2241 
KeyPress(int in_key)2242 int Kis_Scrollable_Table::KeyPress(int in_key) {
2243 	if (visible == 0)
2244 		return 0;
2245 
2246 	int scrollable = 1;
2247 	if ((int) data_vec.size() < ly)
2248 		scrollable = 0;
2249 
2250 	// Selected up one, scroll up one if we need to
2251 	if (in_key == KEY_UP) {
2252 		if (draw_highlight_selected == 0 && scrollable) {
2253 			// If we're not drawing the highlights then we don't mess
2254 			// with the selected item at all, we just slide the scroll
2255 			// pos up and down, and make sure we don't let them scroll
2256 			// off the end of the world, keep as much of the tail in view
2257 			// as possible
2258 			if (scroll_pos > 0)
2259 				scroll_pos--;
2260 		} else if (selected > 0) {
2261 			selected--;
2262 			if (scrollable && scroll_pos > 0 && scroll_pos > selected) {
2263 				scroll_pos--;
2264 			}
2265 		}
2266 	}
2267 
2268 	if (in_key == KEY_DOWN && selected < (int) data_vec.size() - 1) {
2269 		if (draw_highlight_selected == 0 && scrollable) {
2270 			// If we're not drawing the highlights then we don't mess
2271 			// with the selected item at all, we just slide the scroll
2272 			// pos up and down, and make sure we don't let them scroll
2273 			// off the end of the world, keep as much of the tail in view
2274 			// as possible
2275 			if (scroll_pos + ly <= (int) data_vec.size() - 1)
2276 				scroll_pos++;
2277 		} else if (draw_lock_scroll_top && scrollable &&
2278 			scroll_pos + ly - 1 <= selected) {
2279 			// If we're locked to always keep the list filled, we can only
2280 			// scroll until the bottom is visible.  This implies we don't
2281 			// show the selected row, too
2282 			selected++;
2283 			scroll_pos++;
2284 		} else {
2285 			selected++;
2286 			if (scrollable && scroll_pos + ly - 1 <= selected) {
2287 				scroll_pos++;
2288 			}
2289 		}
2290 	}
2291 
2292 	if (in_key == KEY_RIGHT && hscroll_pos < (int) title_vec.size() - 1) {
2293 		hscroll_pos++;
2294 	}
2295 
2296 	if (in_key == KEY_LEFT && hscroll_pos > 0) {
2297 		hscroll_pos--;
2298 	}
2299 
2300 	if (in_key == '\n' || in_key == '\r' || in_key == ' ') {
2301 		if (cb_activate != NULL)
2302 			(*cb_activate)(this, GetSelected(), cb_activate_aux, globalreg);
2303 
2304 		return GetSelected();
2305 	}
2306 
2307 	return 0;
2308 }
2309 
GetSelected()2310 int Kis_Scrollable_Table::GetSelected() {
2311 	if (selected >= 0 && selected < (int) data_vec.size()) {
2312 		return data_vec[selected]->key;
2313 	}
2314 
2315 	return -1;
2316 }
2317 
GetRow(int in_key)2318 vector<string> Kis_Scrollable_Table::GetRow(int in_key) {
2319 	vector<string> ret;
2320 
2321 	if (in_key >= 0 && in_key < (int) data_vec.size()) {
2322 		return data_vec[in_key]->data;
2323 	}
2324 
2325 	return ret;
2326 }
2327 
GetSelectedData()2328 vector<string> Kis_Scrollable_Table::GetSelectedData() {
2329 	vector<string> ret;
2330 
2331 	if (selected >= 0 && selected < (int) data_vec.size()) {
2332 		return data_vec[selected]->data;
2333 	}
2334 
2335 	return ret;
2336 }
2337 
SetSelected(int in_key)2338 int Kis_Scrollable_Table::SetSelected(int in_key) {
2339 	for (unsigned int x = 0; x < data_vec.size(); x++) {
2340 		if (data_vec[x]->key == in_key) {
2341 			selected = x;
2342 			return 1;
2343 		}
2344 	}
2345 
2346 	return 0;
2347 }
2348 
AddTitles(vector<Kis_Scrollable_Table::title_data> in_titles)2349 int Kis_Scrollable_Table::AddTitles(vector<Kis_Scrollable_Table::title_data>
2350 									in_titles) {
2351 	title_vec = in_titles;
2352 	return 1;
2353 }
2354 
AddRow(int in_key,vector<string> in_fields)2355 int Kis_Scrollable_Table::AddRow(int in_key, vector<string> in_fields) {
2356 	if (key_map.find(in_key) != key_map.end()) {
2357 		_MSG("Scrollable_Table tried to add row already keyed", MSGFLAG_ERROR);
2358 		return -1;
2359 	}
2360 
2361 	if (in_fields.size() != title_vec.size()) {
2362 		_MSG("Scrollable_Table added row with a different number of fields than "
2363 			 "the title", MSGFLAG_ERROR);
2364 	}
2365 
2366 	row_data *r = new row_data;
2367 	r->key = in_key;
2368 	r->data = in_fields;
2369 
2370 	key_map[in_key] = 1;
2371 
2372 	data_vec.push_back(r);
2373 
2374 	SetPreferredSize(0, data_vec.size() + 2);
2375 
2376 	return 1;
2377 }
2378 
DelRow(int in_key)2379 int Kis_Scrollable_Table::DelRow(int in_key) {
2380 	if (key_map.find(in_key) == key_map.end()) {
2381 		// _MSG("Scrollable_Table tried to del row that doesn't exist", MSGFLAG_ERROR);
2382 		return -1;
2383 	}
2384 
2385 	key_map.erase(key_map.find(in_key));
2386 
2387 	for (unsigned int x = 0; x < data_vec.size(); x++) {
2388 		if (data_vec[x]->key == in_key) {
2389 			delete data_vec[x];
2390 			data_vec.erase(data_vec.begin() + x);
2391 			break;
2392 		}
2393 	}
2394 
2395 	if (scroll_pos >= (int) data_vec.size()) {
2396 		scroll_pos = data_vec.size() - 1;
2397 		if (scroll_pos < 0)
2398 			scroll_pos = 0;
2399 	}
2400 
2401 	if (selected >= (int) data_vec.size()) {
2402 		selected = data_vec.size() - 1;
2403 	}
2404 
2405 	return 1;
2406 }
2407 
ReplaceRow(int in_key,vector<string> in_fields)2408 int Kis_Scrollable_Table::ReplaceRow(int in_key, vector<string> in_fields) {
2409 	if (key_map.find(in_key) == key_map.end()) {
2410 		// Add a row instead
2411 		return AddRow(in_key, in_fields);
2412 
2413 #if 0
2414 		_MSG("Scrollable_Table tried to replace row that doesn't exist",
2415 			 MSGFLAG_ERROR);
2416 		return -1;
2417 #endif
2418 	}
2419 
2420 	for (unsigned int x = 0; x < data_vec.size(); x++) {
2421 		if (data_vec[x]->key == in_key) {
2422 			data_vec[x]->data = in_fields;
2423 			break;
2424 		}
2425 	}
2426 
2427 	return 1;
2428 }
2429 
Clear()2430 void Kis_Scrollable_Table::Clear() {
2431 	for (unsigned int x = 0; x < data_vec.size(); x++)
2432 		delete data_vec[x];
2433 
2434 	data_vec.clear();
2435 	key_map.clear();
2436 
2437 	return;
2438 }
2439 
Kis_Single_Input(GlobalRegistry * in_globalreg,Kis_Panel * in_panel)2440 Kis_Single_Input::Kis_Single_Input(GlobalRegistry *in_globalreg,
2441 								   Kis_Panel *in_panel) :
2442 	Kis_Panel_Component(in_globalreg, in_panel) {
2443 	globalreg = in_globalreg;
2444 	curs_pos = 0;
2445 	inp_pos = 0;
2446 	label_pos = LABEL_POS_NONE;
2447 	max_len = 0;
2448 	draw_len = 0;
2449 }
2450 
~Kis_Single_Input()2451 Kis_Single_Input::~Kis_Single_Input() {
2452 	// Nothing
2453 }
2454 
DrawComponent()2455 void Kis_Single_Input::DrawComponent() {
2456 	if (visible == 0)
2457 		return;
2458 
2459 	parent_panel->ColorFromPref(color_active, color_active_pref);
2460 	parent_panel->ColorFromPref(color_inactive, color_inactive_pref);
2461 
2462 	SetTransColor(color_active);
2463 
2464 	int xoff = 0;
2465 	int yoff = 0;
2466 
2467 	// Draw the label if we can, in bold
2468 	if (ly >= 2 && label_pos == LABEL_POS_TOP) {
2469 		wattron(window, WA_BOLD);
2470 		mvwaddnstr(window, sy, sx, label.c_str(), lx);
2471 		wattroff(window, WA_BOLD);
2472 		yoff = 1;
2473 	} else if (label_pos == LABEL_POS_LEFT) {
2474 		wattron(window, WA_BOLD);
2475 		mvwaddnstr(window, sy, sx, label.c_str(), lx);
2476 		wattroff(window, WA_BOLD);
2477 		xoff += label.length() + 1;
2478 	}
2479 
2480 	// set the drawing length
2481 	draw_len = lx - xoff;
2482 
2483 	if (draw_len < 0)
2484 		draw_len = 0;
2485 
2486 	// Don't let us fall behind start
2487 	if (curs_pos < 0)
2488 		curs_pos = 0;
2489 
2490 	// Clean up any silliness that might be present from initialization
2491 	if (inp_pos - curs_pos >= draw_len)
2492 		curs_pos = inp_pos - draw_len + 1;
2493 
2494 	// Reset the default color again since we messed with bold attributes
2495 	SetTransColor(color_active);
2496 
2497 	// Invert for the text
2498 	wattron(window, WA_REVERSE);
2499 
2500 	/* draw the inverted line */
2501 	mvwhline(window, sy + yoff, sx + xoff, ' ', draw_len);
2502 
2503 	if (curs_pos >= (int) text.length())
2504 		curs_pos = 0;
2505 
2506 	// fprintf(stderr, "debug - about to try to substr %d %d from len %d\n", curs_pos, draw_len, text.length());
2507 
2508 	/* draw the text from cur to what fits */
2509 	mvwaddnstr(window, sy + yoff, sx + xoff,
2510 			   text.substr(curs_pos, draw_len).c_str(), draw_len);
2511 
2512 	/* Underline & unreverse the last character of the text (or space) */
2513 	wattroff(window, WA_REVERSE);
2514 
2515 	if (active) {
2516 		wattron(window, WA_UNDERLINE);
2517 		char ch;
2518 		if (inp_pos < (int) text.length())
2519 			ch = text[inp_pos];
2520 		else
2521 			ch = ' ';
2522 
2523 		mvwaddch(window, sy + yoff, sx + xoff + (inp_pos - curs_pos), ch);
2524 		wattroff(window, WA_UNDERLINE);
2525 	}
2526 }
2527 
KeyPress(int in_key)2528 int Kis_Single_Input::KeyPress(int in_key) {
2529 	if (visible == 0 || draw_len == 0)
2530 		return 0;
2531 
2532 	// scroll left, and move the viewing window if we have to
2533 	if (in_key == KEY_LEFT && inp_pos > 0) {
2534 		inp_pos--;
2535 		if (inp_pos < curs_pos)
2536 			curs_pos = inp_pos;
2537 		return 0;
2538 	}
2539 
2540 	// scroll right, and move the viewing window if we have to
2541 	if (in_key == KEY_RIGHT && inp_pos < (int) text.length()) {
2542 		inp_pos++;
2543 
2544 		if (inp_pos - curs_pos >= draw_len)
2545 			curs_pos = inp_pos - draw_len + 1;
2546 
2547 		return 0;
2548 	}
2549 
2550 	// Catch home/end (if we can)
2551 	if (in_key == KEY_HOME) {
2552 		inp_pos = 0;
2553 		curs_pos = 0;
2554 
2555 		return 0;
2556 	}
2557 
2558 	if (in_key == KEY_END) {
2559 		inp_pos = text.length();
2560 		curs_pos = inp_pos - draw_len + 1;
2561 
2562 		return 0;
2563 	}
2564 
2565 	// Catch deletes
2566 	if ((in_key == KEY_BACKSPACE || in_key == 0x7F) && text.length() > 0) {
2567 		if (inp_pos == 0)
2568 			inp_pos = 1;
2569 
2570 		text.erase(text.begin() + (inp_pos - 1));
2571 
2572 		if (inp_pos > 0)
2573 			inp_pos--;
2574 
2575 		if (inp_pos < curs_pos)
2576 			curs_pos = inp_pos;
2577 
2578 		return 0;
2579 	}
2580 
2581 	// Lastly, if the character is in our filter of allowed characters for typing,
2582 	// and if we have room, insert it and scroll to the right
2583 	if ((int) text.length() < max_len &&
2584 		filter_map.find(in_key) != filter_map.end()) {
2585 		char ins[2] = { (char) in_key, (char) 0 };
2586 		text.insert(inp_pos, ins);
2587 		inp_pos++;
2588 
2589 		if (inp_pos - curs_pos >= draw_len)
2590 			curs_pos = inp_pos - draw_len + 1;
2591 
2592 		return 0;
2593 	}
2594 
2595 	return 0;
2596 }
2597 
MouseEvent(MEVENT * mevent)2598 int Kis_Single_Input::MouseEvent(MEVENT *mevent) {
2599 	int mwx, mwy;
2600 	getbegyx(window, mwy, mwx);
2601 
2602 	mwx = mevent->x - mwx;
2603 	mwy = mevent->y - mwy;
2604 
2605 	if (mevent->bstate == 4 && mwy == sy && mwx >= sx && mwx <= ex) {
2606 		// Single input only does a focus switch on mouse events
2607 		if (cb_switch != NULL)
2608 			(*cb_switch)(this, 1, cb_switch_aux, globalreg);
2609 
2610 		return 1;
2611 	}
2612 
2613 	return 0;
2614 }
2615 
SetCharFilter(string in_charfilter)2616 void Kis_Single_Input::SetCharFilter(string in_charfilter) {
2617 	filter_map.clear();
2618 	for (unsigned int x = 0; x < in_charfilter.length(); x++) {
2619 		filter_map[in_charfilter[x]] = 1;
2620 	}
2621 }
2622 
SetLabel(string in_label,KisWidget_LabelPos in_pos)2623 void Kis_Single_Input::SetLabel(string in_label, KisWidget_LabelPos in_pos) {
2624 	label = in_label;
2625 	label_pos = in_pos;
2626 	SetPreferredSize(label.length() + max_len + 1, 1);
2627 	SetMinSize(label.length() + 3, 1);
2628 }
2629 
SetTextLen(int in_len)2630 void Kis_Single_Input::SetTextLen(int in_len) {
2631 	max_len = in_len;
2632 	SetPreferredSize(in_len + label.length() + 1, 1);
2633 	SetMinSize(label.length() + 3, 1);
2634 }
2635 
SetText(string in_text,int dpos,int ipos)2636 void Kis_Single_Input::SetText(string in_text, int dpos, int ipos) {
2637 	text = in_text;
2638 
2639 	if (ipos < 0)
2640 		inp_pos = in_text.length();
2641 	else
2642 		inp_pos = ipos;
2643 
2644 	if (dpos < 0)
2645 		curs_pos = 0;
2646 	else
2647 		curs_pos = dpos;
2648 }
2649 
GetText()2650 string Kis_Single_Input::GetText() {
2651 	return text;
2652 }
2653 
Kis_Button(GlobalRegistry * in_globalreg,Kis_Panel * in_panel)2654 Kis_Button::Kis_Button(GlobalRegistry *in_globalreg, Kis_Panel *in_panel) :
2655 	Kis_Panel_Component(in_globalreg, in_panel) {
2656 	globalreg = in_globalreg;
2657 
2658 	active = 0;
2659 	SetMinSize(3, 1);
2660 }
2661 
~Kis_Button()2662 Kis_Button::~Kis_Button() {
2663 	// nada
2664 }
2665 
DrawComponent()2666 void Kis_Button::DrawComponent() {
2667 	if (visible == 0)
2668 		return;
2669 
2670 	parent_panel->ColorFromPref(color_active, color_active_pref);
2671 	parent_panel->ColorFromPref(color_inactive, color_inactive_pref);
2672 
2673 	SetTransColor(color_active);
2674 
2675 	// Draw the highlighted button area if we're active
2676 	if (active)
2677 		wattron(window, WA_REVERSE);
2678 
2679 	mvwhline(window, sy, sx, ' ', lx);
2680 
2681 	// Center the text
2682 	int tx = (lx / 2) - (text.length() / 2);
2683 	mvwaddnstr(window, sy, sx + tx, text.c_str(), lx - tx);
2684 
2685 	// Add the ticks
2686 	mvwaddch(window, sy, sx, '[');
2687 	mvwaddch(window, sy, sx + lx - 1, ']');
2688 
2689 	if (active)
2690 		wattroff(window, WA_REVERSE);
2691 }
2692 
KeyPress(int in_key)2693 int Kis_Button::KeyPress(int in_key) {
2694 	if (visible == 0)
2695 		return 0;
2696 
2697 	if (in_key == KEY_ENTER || in_key == '\n' || in_key == ' ') {
2698 		if (cb_activate != NULL)
2699 			(*cb_activate)(this, 1, cb_activate_aux, globalreg);
2700 
2701 		return 1;
2702 	}
2703 
2704 	return 0;
2705 }
2706 
MouseEvent(MEVENT * mevent)2707 int Kis_Button::MouseEvent(MEVENT *mevent) {
2708 	int mwx, mwy;
2709 	getbegyx(window, mwy, mwx);
2710 
2711 	mwx = mevent->x - mwx;
2712 	mwy = mevent->y - mwy;
2713 
2714 	if (mevent->bstate == 4 && mwy == sy && mwx >= sx && mwx <= ex) {
2715 		if (cb_activate != NULL)
2716 			(*cb_activate)(this, 1, cb_activate_aux, globalreg);
2717 
2718 		return 1;
2719 	}
2720 
2721 	return 0;
2722 }
2723 
SetText(string in_text)2724 void Kis_Button::SetText(string in_text) {
2725 	text = in_text;
2726 	SetPreferredSize(text.length() + 4, 1);
2727 }
2728 
Kis_Checkbox(GlobalRegistry * in_globalreg,Kis_Panel * in_panel)2729 Kis_Checkbox::Kis_Checkbox(GlobalRegistry *in_globalreg, Kis_Panel *in_panel) :
2730 	Kis_Panel_Component(in_globalreg, in_panel) {
2731 	globalreg = in_globalreg;
2732 
2733 	active = 0;
2734 	checked = 0;
2735 }
2736 
~Kis_Checkbox()2737 Kis_Checkbox::~Kis_Checkbox() {
2738 	// nada
2739 }
2740 
DrawComponent()2741 void Kis_Checkbox::DrawComponent() {
2742 	if (visible == 0)
2743 		return;
2744 
2745 	parent_panel->ColorFromPref(color_active, color_active_pref);
2746 	parent_panel->ColorFromPref(color_inactive, color_inactive_pref);
2747 
2748 	SetTransColor(color_active);
2749 
2750 	// Draw the highlighted button area if we're active
2751 	if (active)
2752 		wattron(window, WA_REVERSE);
2753 
2754 	mvwhline(window, sy, sx, ' ', lx);
2755 
2756 	if (checked) {
2757 		mvwaddnstr(window, sy, sx, "[X]", 3);
2758 	} else {
2759 		mvwaddnstr(window, sy, sx, "[ ]", 3);
2760 	}
2761 
2762 	mvwaddnstr(window, sy, sx + 4, text.c_str(), lx - 4);
2763 
2764 	if (active)
2765 		wattroff(window, WA_REVERSE);
2766 }
2767 
Activate(int subcomponent)2768 void Kis_Checkbox::Activate(int subcomponent) {
2769 	active = 1;
2770 }
2771 
Deactivate()2772 void Kis_Checkbox::Deactivate() {
2773 	active = 0;
2774 }
2775 
KeyPress(int in_key)2776 int Kis_Checkbox::KeyPress(int in_key) {
2777 	if (visible == 0)
2778 		return 0;
2779 
2780 	if (in_key == KEY_ENTER || in_key == '\n' || in_key == ' ') {
2781 		checked = !checked;
2782 
2783 		if (cb_activate != NULL)
2784 			(*cb_activate)(this, 1, cb_activate_aux, globalreg);
2785 
2786 		return 0;
2787 	}
2788 
2789 	return 0;
2790 }
2791 
MouseEvent(MEVENT * mevent)2792 int Kis_Checkbox::MouseEvent(MEVENT *mevent) {
2793 	int mwx, mwy;
2794 	getbegyx(window, mwy, mwx);
2795 
2796 	mwx = mevent->x - mwx;
2797 	mwy = mevent->y - mwy;
2798 
2799 	if (mevent->bstate == 4 && mwy == sy && mwx >= sx && mwx <= ex) {
2800 		checked = !checked;
2801 
2802 		if (cb_activate != NULL)
2803 			(*cb_activate)(this, 1, cb_activate_aux, globalreg);
2804 
2805 		return 1;
2806 	}
2807 
2808 	return 0;
2809 }
2810 
SetText(string in_text)2811 void Kis_Checkbox::SetText(string in_text) {
2812 	text = in_text;
2813 	SetPreferredSize(text.length() + 4, 1);
2814 }
2815 
GetChecked()2816 int Kis_Checkbox::GetChecked() {
2817 	return checked;
2818 }
2819 
SetChecked(int in_check)2820 void Kis_Checkbox::SetChecked(int in_check) {
2821 	checked = in_check;
2822 }
2823 
Kis_Radiobutton(GlobalRegistry * in_globalreg,Kis_Panel * in_panel)2824 Kis_Radiobutton::Kis_Radiobutton(GlobalRegistry *in_globalreg, Kis_Panel *in_panel) :
2825 	Kis_Panel_Component(in_globalreg, in_panel) {
2826 	globalreg = in_globalreg;
2827 
2828 	active = 0;
2829 	checked = 0;
2830 }
2831 
~Kis_Radiobutton()2832 Kis_Radiobutton::~Kis_Radiobutton() {
2833 	// nada
2834 }
2835 
DrawComponent()2836 void Kis_Radiobutton::DrawComponent() {
2837 	if (visible == 0)
2838 		return;
2839 
2840 	parent_panel->ColorFromPref(color_active, color_active_pref);
2841 	parent_panel->ColorFromPref(color_inactive, color_inactive_pref);
2842 
2843 	SetTransColor(color_active);
2844 
2845 	// Draw the highlighted button area if we're active
2846 	if (active)
2847 		wattron(window, WA_REVERSE);
2848 
2849 	mvwhline(window, sy, sx, ' ', lx);
2850 
2851 	if (checked) {
2852 		mvwaddnstr(window, sy, sx, "(*)", 3);
2853 	} else {
2854 		mvwaddnstr(window, sy, sx, "( )", 3);
2855 	}
2856 
2857 	mvwaddnstr(window, sy, sx + 4, text.c_str(), lx - 4);
2858 
2859 	if (active)
2860 		wattroff(window, WA_REVERSE);
2861 }
2862 
Activate(int subcomponent)2863 void Kis_Radiobutton::Activate(int subcomponent) {
2864 	active = 1;
2865 }
2866 
Deactivate()2867 void Kis_Radiobutton::Deactivate() {
2868 	active = 0;
2869 }
2870 
KeyPress(int in_key)2871 int Kis_Radiobutton::KeyPress(int in_key) {
2872 	if (visible == 0)
2873 		return 0;
2874 
2875 	if (in_key == KEY_ENTER || in_key == '\n' || in_key == ' ') {
2876 		if (!checked)
2877 			SetChecked(1);
2878 
2879 		if (cb_activate != NULL)
2880 			(*cb_activate)(this, 1, cb_activate_aux, globalreg);
2881 
2882 		return 0;
2883 	}
2884 
2885 	return 0;
2886 }
2887 
MouseEvent(MEVENT * mevent)2888 int Kis_Radiobutton::MouseEvent(MEVENT *mevent) {
2889 	int mwx, mwy;
2890 	getbegyx(window, mwy, mwx);
2891 
2892 	mwx = mevent->x - mwx;
2893 	mwy = mevent->y - mwy;
2894 
2895 	if (mevent->bstate == 4 && mwy == sy && mwx >= sx && mwx <= ex) {
2896 		if (!checked)
2897 			SetChecked(1);
2898 
2899 		if (cb_activate != NULL)
2900 			(*cb_activate)(this, 1, cb_activate_aux, globalreg);
2901 
2902 		return 1;
2903 	}
2904 
2905 	return 0;
2906 }
2907 
SetText(string in_text)2908 void Kis_Radiobutton::SetText(string in_text) {
2909 	text = in_text;
2910 	SetPreferredSize(text.length() + 4, 1);
2911 }
2912 
GetChecked()2913 int Kis_Radiobutton::GetChecked() {
2914 	return checked;
2915 }
2916 
SetChecked(int in_check)2917 void Kis_Radiobutton::SetChecked(int in_check) {
2918 	checked = in_check;
2919 
2920 	for (unsigned int x = 0; x < linked_vec.size() && in_check; x++)
2921 		linked_vec[x]->SetChecked(0);
2922 
2923 }
2924 
LinkRadiobutton(Kis_Radiobutton * in_button)2925 void Kis_Radiobutton::LinkRadiobutton(Kis_Radiobutton *in_button) {
2926 	linked_vec.push_back(in_button);
2927 }
2928 
AddExtDataVec(string name,int layer,string colorpref,string colordefault,char line,char fill,int overunder,vector<int> * in_dv)2929 void Kis_IntGraph::AddExtDataVec(string name, int layer, string colorpref,
2930 								 string colordefault, char line, char fill,
2931 								 int overunder, vector<int> *in_dv) {
2932 	graph_source gs;
2933 
2934 	gs.layer = layer;
2935 	gs.colorpref = colorpref;
2936 	gs.colordefault = colordefault;
2937 	gs.colorval = 0;
2938 	snprintf(gs.line, 2, "%c", line);
2939 	snprintf(gs.fill, 2, "%c", fill);
2940 	gs.data = in_dv;
2941 	gs.name = name;
2942 	gs.overunder = overunder;
2943 
2944 	// Can't figure out how to do a sort template of a template class, so hell
2945 	// with it, we'll do it sloppily here.  Assembled least to greatest priority
2946 	for (unsigned int x = 0; x < data_vec.size(); x++) {
2947 		if (layer < data_vec[x].layer) {
2948 			data_vec.insert(data_vec.begin() + x, gs);
2949 			return;
2950 		}
2951 	}
2952 
2953 	if (name.length() > maxlabel)
2954 		maxlabel = name.length();
2955 
2956 	// Add it to the end otherwise
2957 	data_vec.push_back(gs);
2958 }
2959 
KeyPress(int in_key)2960 int Kis_IntGraph::KeyPress(int in_key) {
2961 	// Nothing to do for now
2962 	return 1;
2963 }
2964 
DrawComponent()2965 void Kis_IntGraph::DrawComponent() {
2966 	if (visible == 0)
2967 		return;
2968 
2969 	char backing[32];
2970 
2971 	parent_panel->ColorFromPref(color_fw, color_active_pref);
2972 
2973 	for (unsigned int x = 0; x < data_vec.size(); x++) {
2974 		parent_panel->InitColorPref(data_vec[x].colorpref, data_vec[x].colordefault);
2975 		parent_panel->ColorFromPref(data_vec[x].colorval, data_vec[x].colorpref);
2976 	}
2977 
2978 	// We want the same scale for over/under, so we'll calculate the
2979 	// height as h - 1 (label) (div 2 if o/u)
2980 	int gh = (ly - 1) / (graph_mode == 1 ? 2 : 1) - xgraph_size;
2981 	// Zero position on the graph is the bottom, or center, depending
2982 	// on normal or over/under
2983 	int gzero = ey - (graph_mode == 1 ? gh : 1) - xgraph_size;
2984 	// Width - label
2985 	int gw = lx;
2986 	unsigned int gxofft;
2987 
2988 	// Set the drawing max and min
2989 	int dmax_y = max_y;
2990 	int dmin_y = min_y;
2991 
2992 	// Go through the list and get the max if we're auto-scaling
2993 	if (max_y == 0 || min_y == 0) {
2994 		for (unsigned int x = 0; x < data_vec.size(); x++) {
2995 			for (unsigned int z = 0; z < data_vec[x].data->size(); z++) {
2996 				if (max_y == 0 &&
2997 					(((*(data_vec[x].data))[z] > 0 &&
2998 					 dmax_y < (*(data_vec[x].data))[z]) ||
2999 					((*(data_vec[x].data))[z] < 0 &&
3000 					 dmax_y > (*(data_vec[x].data))[z])))
3001 					dmax_y = (*(data_vec[x].data))[z];
3002 			}
3003 		}
3004 	}
3005 
3006 	// adjust the drawing size
3007 	snprintf(backing, 32, " %d ", dmax_y);
3008 	gxofft = strlen(backing);
3009 	snprintf(backing, 32, " %d ", dmin_y);
3010 	if (strlen(backing) > gxofft)
3011 		gxofft = strlen(backing);
3012 	gw -= gxofft;
3013 
3014 	// Go through from least to greatest priority so that the "high" priority
3015 	// draws over the old
3016 	for (unsigned int x = 0; x < data_vec.size(); x++) {
3017 		int xmod = 0;
3018 		int xgroup = 1;
3019 		int dvsize = data_vec[x].data->size();
3020 
3021 		if (inter_x) {
3022 			xmod = (int) ceilf((float) dvsize / (float) gw);
3023 			xgroup = xmod * 2;
3024 		}
3025 
3026 		for (int gx = 0; (gx < gw) && inter_x; gx++) {
3027 			int r = 0, py, nuse = 0;
3028 			// We make the assumption here that T is a numerical
3029 			// type in some fashion, if this is ever not true we'll have
3030 			// to do something else
3031 			// int avg = 0;
3032 			int max = 0;
3033 
3034 			// Interpolate down if we have too much data
3035 			if (gw < dvsize) {
3036 				// Center of the samples we look at
3037 				r = (int) (((float) gx / (float) gw) * (float) dvsize);
3038 
3039 				// Determine the local max across our range
3040 				for (int pos = -1 * (xgroup / 2); pos < (xgroup / 2); pos++) {
3041 					if (r + pos >= dvsize || r + pos < 0) {
3042 						continue;
3043 					}
3044 
3045 					// Max depending on if we're neg or pos data
3046 					if ((*(data_vec[x].data))[r + pos] >= 0 &&
3047 						 (*(data_vec[x].data))[r + pos] > max) {
3048 						if ((*(data_vec[x].data))[r+pos] > dmax_y) {
3049 							max = dmax_y;
3050 						} else {
3051 							max = (*(data_vec[x].data))[r + pos];
3052 						}
3053 					} else if ((*(data_vec[x].data))[r + pos] < 0 &&
3054 						 (*(data_vec[x].data))[r + pos] < max) {
3055 						if ((*(data_vec[x].data))[r+pos] < dmin_y) {
3056 							max = dmin_y;
3057 						} else {
3058 							max = (*(data_vec[x].data))[r + pos];
3059 						}
3060 					}
3061 
3062 					nuse++;
3063 				}
3064 			} else {
3065 				nuse = 1;
3066 				unsigned int pos = (unsigned int) (((float) gx/gw) * dvsize);
3067 				if (pos >= (*(data_vec)[x].data).size() || pos < 0) {
3068 					max = min_y;
3069 				} else {
3070 					max = (*(data_vec)[x].data)[pos];
3071 				}
3072 			}
3073 
3074 			if (nuse == 0) {
3075 				continue;
3076 			}
3077 
3078 			// If we're negative, do the math differently
3079 			// Adapt the group max to our scale
3080 			float adapted = 0;
3081 
3082 			if (max < 0) {
3083 				adapted =
3084 					(float) (abs(max) + dmin_y) /
3085 					(float) (abs(dmax_y) + dmin_y);
3086 			} else {
3087 				adapted = (float) (max - min_y) / (float) (dmax_y - min_y);
3088 			}
3089 
3090 			// Scale it to the height of the graph
3091 			py = (int) ((float) gh * adapted);
3092 
3093 			// Set the color once
3094 			wattrset(window, data_vec[x].colorval);
3095 
3096 			// If we're plotting over/normal, we do nothing
3097 			// If we're plotting under, we invert and draw below
3098 			int oumod = 1;
3099 			if (data_vec[x].overunder < 0 && graph_mode == 1)
3100 				oumod = -1;
3101 
3102 			for (int gy = gh; gy >= 0; gy--) {
3103 				if (gy == py)
3104 					mvwaddstr(window, gzero - (gy * oumod), sx + gx + gxofft,
3105 							  data_vec[x].line);
3106 				else if (gy < py)
3107 					mvwaddstr(window, gzero - (gy * oumod), sx + gx + gxofft,
3108 							  data_vec[x].fill);
3109 			}
3110 		}
3111 
3112 		int rwidth = (int) kismin(2, (1.0f / dvsize) * gw);
3113 		for (int dvx = 0; dvx < dvsize && inter_x == 0; dvx++) {
3114 			int py = 0;
3115 			int max = (*(data_vec)[x].data)[dvx];
3116 			int drawx = (int) (((float) dvx / dvsize) * gw);
3117 
3118 			// If we're negative, do the math differently
3119 			// Adapt the group max to our scale
3120 			float adapted = 0;
3121 
3122 			if (max < 0) {
3123 				adapted =
3124 					(float) (abs(max) + dmin_y) /
3125 					(float) (abs(dmax_y) + dmin_y);
3126 			} else {
3127 				adapted = (float) (max - min_y) / (float) (dmax_y - min_y);
3128 			}
3129 
3130 			// Scale it to the height of the graph
3131 			py = (int) ((float) gh * adapted);
3132 
3133 			// Set the color once
3134 			wattrset(window, data_vec[x].colorval);
3135 
3136 			for (int rdx = rwidth * -1; rdx < rwidth; rdx++) {
3137 				// If we're plotting over/normal, we do nothing
3138 				// If we're plotting under, we invert and draw below
3139 				int oumod = 1;
3140 				if (data_vec[x].overunder < 0 && graph_mode == 1)
3141 					oumod = -1;
3142 
3143 				for (int gy = gh; gy >= 0; gy--) {
3144 					if (gy == py)
3145 						mvwaddstr(window, gzero - (gy * oumod),
3146 								  sx + drawx + rdx + gxofft,
3147 								  data_vec[x].line);
3148 					else if (gy < py && data_vec[x].fill)
3149 						mvwaddstr(window, gzero - (gy * oumod),
3150 								  sx + drawx + rdx + gxofft,
3151 								  data_vec[x].fill);
3152 				}
3153 
3154 			}
3155 		}
3156 	}
3157 
3158 	if (draw_layers) {
3159 		// Draw the labels (right-hand)
3160 		int posmod = 0, negmod = 0;
3161 		// Set the backing blank
3162 		memset(backing, ' ', 32);
3163 		if ((maxlabel + 4) >=  32)
3164 			maxlabel = (32 - 4);
3165 		backing[maxlabel + 4] = '\0';
3166 		// Draw the component name labels
3167 		for (unsigned int x = 0; x < data_vec.size(); x++) {
3168 			// Position
3169 			int lpos = 0;
3170 			// Text color
3171 			if (data_vec[x].overunder < 0 && graph_mode == 1) {
3172 				lpos = ey - negmod++;
3173 			} else {
3174 				lpos = sy + posmod++;
3175 			}
3176 			// Fill in the blocking
3177 			wattrset(window, color_fw);
3178 			mvwaddstr(window, lpos, ex - (maxlabel + 4), backing);
3179 			// Fill in the label
3180 			mvwaddstr(window, lpos, ex - (maxlabel), data_vec[x].name.c_str());
3181 
3182 			// Fill in the colors
3183 			wattrset(window, data_vec[x].colorval);
3184 			mvwaddstr(window, lpos, ex - (maxlabel + 3), data_vec[x].line);
3185 			mvwaddstr(window, lpos, ex - (maxlabel + 2), data_vec[x].fill);
3186 		}
3187 	}
3188 
3189 	// Draw the X marker labels
3190 	wattrset(window, color_fw);
3191 	for (unsigned int x = 0; x < label_x.size() && label_x_graphref >= 0; x++) {
3192 		// GX within the # of samples on the graph
3193 		int lgx = (int) (((float) gw / data_vec[label_x_graphref].data->size()) *
3194 			label_x[x].position);
3195 		for (unsigned int y = 0; y < label_x[x].label.size(); y++) {
3196 			mvwaddch(window, gzero + y + 1, sx + lgx + gxofft, label_x[x].label[y]);
3197 		}
3198 	}
3199 
3200 	// Reuse the backing for the scale
3201 	if (draw_scale) {
3202 		snprintf(backing, 32, " %d ", dmax_y);
3203 		wattrset(window, color_fw);
3204 		mvwaddstr(window, sy, sx, backing);
3205 
3206 		wattrset(window, color_fw);
3207 		mvwhline(window, gzero, sx, ACS_HLINE, lx);
3208 
3209 		snprintf(backing, 32, " %d ", min_y);
3210 		mvwaddstr(window, gzero, sx, backing);
3211 	}
3212 }
3213 
3214 #if 0
3215 int Kis_PolarGraph::KeyPress(int in_key) {
3216 	return 1;
3217 }
3218 
3219 void Kis_PolarGraph::DrawComponent() {
3220 	if (visible == 0)
3221 		return;
3222 
3223 	// Square ourselves to the shortest dimension
3224 	if (lx < ly)
3225 		ly = lx;
3226 	else
3227 		lx = ly;
3228 
3229 	parent_panel->InitColorPref(color_active_pref, "white,black");
3230 	parent_panel->ColorFromPref(color_fw, color_active_pref);
3231 
3232 	for (unsigned int x = 0; x < point_vec.size(); x++) {
3233 		// Real position
3234 		int px = (lx / 2) + point_vec[x].r * sin(point_vec[x].theta) * (lx / 2);
3235 		int py = (ly / 2) - point_vec[x].r * cos(point_vec[x].theta) * (lx / 2);
3236 
3237 		// fprintf(stderr, "debug - graph %s pos %d %d\n", point_vec[x].name.c_str(), px, py);
3238 
3239 		// Plot the text at the position
3240 		//wattrset(window, point_vec[x].colorval);
3241 
3242 		// Plot w/in boundaries
3243 		mvwaddnstr(window, sy + py, sx + px, point_vec[x].name.c_str(),
3244 				  point_vec[x].name.length());
3245 	}
3246 
3247 }
3248 
3249 void Kis_PolarGraph::AddPoint(int id, graph_point gp) {
3250 	parent_panel->InitColorPref(gp.colorpref, gp.colordefault);
3251 	parent_panel->ColorFromPref(gp.colorval, gp.colorpref);
3252 
3253 	gp.id = id;
3254 
3255 	if (fabs(gp.r) > maxr)
3256 		maxr = fabs(gp.r);
3257 
3258 	for (unsigned int x = 0; x < point_vec.size(); x++) {
3259 		if (point_vec[x].id == id) {
3260 			point_vec[x] = gp;
3261 			return;
3262 		}
3263 	}
3264 
3265 	point_vec.push_back(gp);
3266 }
3267 
3268 void Kis_PolarGraph::DelPoint(int id) {
3269 	maxr = 0;
3270 
3271 	for (unsigned int x = 0; x < point_vec.size(); x++) {
3272 		if (point_vec[x].id == id) {
3273 			point_vec.erase(point_vec.begin() + x);
3274 			x--;
3275 			continue;
3276 		}
3277 
3278 		if (fabs(point_vec[x].r) > maxr)
3279 			maxr = fabs(point_vec[x].r);
3280 	}
3281 }
3282 
3283 void Kis_PolarGraph::ClearPoints() {
3284 	maxr = 0;
3285 	point_vec.clear();
3286 }
3287 
3288 Kis_Filepicker::Kis_Filepicker(GlobalRegistry *in_globalreg, Kis_Panel *in_panel) :
3289 	Kis_Scrollable_Table(in_globalreg, in_panel) {
3290 
3291 	globalreg = in_globalreg;
3292 	active = 0;
3293 
3294 	vector<Kis_Scrollable_Table::title_data> titles;
3295 	Kis_Scrollable_Table::title_data t;
3296 	t.width = 0;
3297 	t.title = "File";
3298 	t.alignment = 0;
3299 	titles.push_back(t);
3300 
3301 	AddTitles(titles);
3302 
3303 	SetDrawTitles(0);
3304 	SetLockScrollTop(1);
3305 }
3306 
3307 Kis_Filepicker::~Kis_Filepicker() {
3308 
3309 }
3310 
3311 void Kis_Filepicker::SetDirectory(string in_dir) {
3312 	DIR *dir;
3313 	struct dirent *file;
3314 	struct stat sbuf;
3315 	vector<string> content;
3316 
3317 	if (in_dir == cur_directory)
3318 		return;
3319 
3320 	if (in_dir[in_dir.length() - 1] != '/')
3321 		in_dir += "/";
3322 
3323 	cur_directory = in_dir;
3324 
3325 	if ((dir = opendir(in_dir.c_str())) == NULL) {
3326 		content.push_back("[ Invalid Directory:");
3327 		content.push_back(string(" ") + in_dir + string(" ]"));
3328 		return;
3329 	}
3330 
3331 	while ((file = readdir(dir)) != NULL) {
3332 		if (string(file->d_name) == ".")
3333 			continue;
3334 
3335 		if (stat(file->d_name, &sbuf) < 0)
3336 			continue;
3337 
3338 		if (S_ISDIR(sbuf.st_mode)) {
3339 			string n = string(file->d_name);
3340 			if (n != "..")
3341 				n += "/";
3342 
3343 			content.push_back(n);
3344 		}
3345 	}
3346 
3347 	rewinddir(dir);
3348 
3349 	while ((file = readdir(dir)) != NULL) {
3350 		if (stat(file->d_name, &sbuf) < 0)
3351 			continue;
3352 
3353 		if (S_ISREG(sbuf.st_mode)) {
3354 			content.push_back(string(file->d_name));
3355 		}
3356 	}
3357 
3358 	closedir(dir);
3359 
3360 	vector<string> td;
3361 	td.push_back("");
3362 
3363 	for (unsigned int x = 0; x < content.size(); x++) {
3364 		td[0] = content[x];
3365 		ReplaceRow(x, td);
3366 	}
3367 
3368 	SetFile(set_file);
3369 }
3370 
3371 void Kis_Filepicker::SetFile(string in_file) {
3372 	set_file = in_file;
3373 
3374 	if (set_file == "")
3375 		return;
3376 
3377 	for (unsigned int x = 0; x < data_vec.size(); x++) {
3378 		if (data_vec[x]->data[0] == set_file) {
3379 			SetSelected(x);
3380 			return;
3381 		}
3382 	}
3383 }
3384 
3385 int Kis_Filepicker::KeyPress(int in_key) {
3386 	struct stat sbuf;
3387 
3388 	if (visible == 0)
3389 		return 0;
3390 
3391 	if (data_vec.size() > 0 && (in_key == '\n' || in_key == '\r' || in_key == ' ')) {
3392 		vector<string> sel = GetSelectedData();
3393 		if (sel.size() == 1) {
3394 			if (stat(string(cur_directory + sel[0]).c_str(), &sbuf) == 0) {
3395 				if (sel[0] == "..") {
3396 					if (sel[0].rfind("/") != string::npos) {
3397 						SetDirectory(sel[0].substr(0, sel[0].rfind("/")));
3398 					}
3399 				} else if (S_ISDIR(sbuf.st_mode)) {
3400 					SetDirectory(cur_directory + sel[0]);
3401 					return 0;
3402 				}
3403 			}
3404 		}
3405 	}
3406 
3407 	return Kis_Scrollable_Table::KeyPress(in_key);
3408 }
3409 
3410 #endif
3411 
Kis_Panel(GlobalRegistry * in_globalreg,KisPanelInterface * in_intf)3412 Kis_Panel::Kis_Panel(GlobalRegistry *in_globalreg, KisPanelInterface *in_intf) {
3413 	globalreg = in_globalreg;
3414 	kpinterface = in_intf;
3415 	win = newwin(1, 1, 0, 0);
3416 	pan = new_panel(win);
3417 	hide_panel(pan);
3418 	menu = NULL;
3419 
3420 	text_color = border_color = 0;
3421 	InitColorPref("panel_text_color", "white,black");
3422 	InitColorPref("panel_border_color", "blue,black");
3423 	ColorFromPref(text_color, "panel_text_color");
3424 	ColorFromPref(border_color, "panel_border_color");
3425 
3426 	sx = sy = sizex = sizey = 0;
3427 
3428 	active_component = NULL;
3429 	main_component = NULL;
3430 	tab_pos = -1;
3431 
3432 	last_key = 0;
3433 	last_key_time.tv_sec = 0;
3434 
3435 	escape_timer = -1;
3436 }
3437 
~Kis_Panel()3438 Kis_Panel::~Kis_Panel() {
3439 	for (unsigned int x = 0; x < pan_comp_vec.size(); x++) {
3440 		if (pan_comp_vec[x].comp_flags & KIS_PANEL_COMP_STATIC)
3441 			continue;
3442 
3443 		delete pan_comp_vec[x].comp;
3444 	}
3445 
3446 	if (pan != NULL)
3447 		del_panel(pan);
3448 	if (win != NULL)
3449 		delwin(win);
3450 }
3451 
AddComponentVec(Kis_Panel_Component * in_comp,int in_flags)3452 void Kis_Panel::AddComponentVec(Kis_Panel_Component *in_comp, int in_flags) {
3453 	component_entry etr;
3454 
3455 	etr.comp_flags = in_flags;
3456 	etr.comp = in_comp;
3457 
3458 	pan_comp_vec.push_back(etr);
3459 }
3460 
DelComponentVec(Kis_Panel_Component * in_comp)3461 void Kis_Panel::DelComponentVec(Kis_Panel_Component *in_comp) {
3462 	for (unsigned int x = 0; x < pan_comp_vec.size(); x++) {
3463 		if (pan_comp_vec[x].comp == in_comp) {
3464 			pan_comp_vec.erase(pan_comp_vec.begin() + x);
3465 			return;
3466 		}
3467 	}
3468 }
3469 
SetActiveComponent(Kis_Panel_Component * in_comp)3470 void Kis_Panel::SetActiveComponent(Kis_Panel_Component *in_comp) {
3471 	for (unsigned int x = 0; x < pan_comp_vec.size(); x++) {
3472 		if (pan_comp_vec[x].comp == in_comp) {
3473 			active_component = in_comp;
3474 			tab_pos = x;
3475 			in_comp->Activate(0);
3476 		} else {
3477 			pan_comp_vec[x].comp->Deactivate();
3478 		}
3479 	}
3480 }
3481 
KeyPress(int in_key)3482 int Kis_Panel::KeyPress(int in_key) {
3483 	int ret;
3484 
3485 	if (menu) {
3486 		ret = menu->KeyPress(in_key);
3487 
3488 		if (ret != 0)
3489 			return 0;
3490 	}
3491 
3492 	// figure out if we need to get to a visible item first and jump to it via the
3493 	// tab function
3494 	if (active_component != NULL && active_component->GetVisible() == 0 &&
3495 		in_key != '\t')
3496 		KeyPress('\t');
3497 
3498 	if (in_key == '\t' && tab_pos >= 0) {
3499 		int set = -1;
3500 
3501 		// Find from current to end
3502 		for (unsigned int x = tab_pos + 1; x < pan_comp_vec.size(); x++) {
3503 			if ((pan_comp_vec[x].comp_flags & KIS_PANEL_COMP_TAB) == 0 ||
3504 				(pan_comp_vec[x].comp->GetVisible() == 0))
3505 				continue;
3506 
3507 			set = x;
3508 			break;
3509 		}
3510 
3511 		// No?  Find from start
3512 		if (set == -1) {
3513 			for (unsigned int x = 0; x < pan_comp_vec.size(); x++) {
3514 				if ((pan_comp_vec[x].comp_flags & KIS_PANEL_COMP_TAB) == 0 ||
3515 					(pan_comp_vec[x].comp->GetVisible() == 0))
3516 					continue;
3517 
3518 				set = x;
3519 				break;
3520 			}
3521 		}
3522 
3523 		// No?  Someone deleted the tabable components then, just stop
3524 		if (set == -1) {
3525 			tab_pos = -1;
3526 			return 0;
3527 		}
3528 
3529 		pan_comp_vec[tab_pos].comp->Deactivate();
3530 		tab_pos = set;
3531 
3532 		pan_comp_vec[tab_pos].comp->Activate(1);
3533 		active_component = pan_comp_vec[tab_pos].comp;
3534 	}
3535 
3536 	if (active_component != NULL) {
3537 		ret = active_component->KeyPress(in_key);
3538 		return 0;
3539 	}
3540 
3541 	return 0;
3542 }
3543 
MouseEvent(MEVENT * mevent)3544 int Kis_Panel::MouseEvent(MEVENT *mevent) {
3545 	int ret;
3546 
3547 	// We just process every component until we get a non-0
3548 	for (unsigned int x = 0; x < pan_comp_vec.size(); x++) {
3549 		if ((pan_comp_vec[x].comp_flags & KIS_PANEL_COMP_EVT) == 0)
3550 			continue;
3551 
3552 		ret = pan_comp_vec[x].comp->MouseEvent(mevent);
3553 
3554 		if (ret != 0) {
3555 			// Positive response means switch the focus
3556 			if (ret >= 0) {
3557 				if (active_component != NULL)
3558 					active_component->Deactivate();
3559 
3560 				if ((pan_comp_vec[x].comp_flags & KIS_PANEL_COMP_TAB) &&
3561 					tab_pos >= 0) {
3562 					tab_pos = x;
3563 				}
3564 
3565 				active_component = pan_comp_vec[x].comp;
3566 				active_component->Activate(0);
3567 			}
3568 
3569 			return 0;
3570 		}
3571 	}
3572 
3573 	return 0;
3574 }
3575 
InitColorPref(string in_pref,string in_def)3576 void Kis_Panel::InitColorPref(string in_pref, string in_def) {
3577 	if (kpinterface->prefs->FetchOpt(in_pref) == "")
3578 		kpinterface->prefs->SetOpt(in_pref, in_def, 1);
3579 }
3580 
ColorFromPref(int & clr,string in_pref)3581 void Kis_Panel::ColorFromPref(int &clr, string in_pref) {
3582 	/*
3583 	if (kpinterface->prefs->FetchOptDirty(in_pref) || clr == 0) {
3584 		kpinterface->prefs->SetOptDirty(in_pref, 0);
3585 		*/
3586 		clr = kpinterface->colors.AddColor(kpinterface->prefs->FetchOpt(in_pref),
3587 										   in_pref);
3588 		/*
3589 	}
3590 	*/
3591 
3592 	return;
3593 }
3594 
RemapAllColors(string oldcolor,string newcolor)3595 void Kis_Panel::RemapAllColors(string oldcolor, string newcolor) {
3596 	kpinterface->colors.RemapAllColors(oldcolor, newcolor, kpinterface->prefs);
3597 }
3598 
AddColor(string in_color)3599 int Kis_Panel::AddColor(string in_color) {
3600 	return kpinterface->colors.AddColor(in_color, "");
3601 }
3602 
Position(int in_sy,int in_sx,int in_y,int in_x)3603 void Kis_Panel::Position(int in_sy, int in_sx, int in_y, int in_x) {
3604 	sx = in_sx;
3605 	sy = in_sy;
3606 	sizex = in_x;
3607 	sizey = in_y;
3608 
3609 	if (win == NULL) {
3610 		win = newwin(sizey, sizex, sy, sx);
3611 	}
3612 
3613 	if (pan == NULL) {
3614 		pan = new_panel(win);
3615 	} else {
3616 		wresize(win, sizey, sizex);
3617 		replace_panel(pan, win);
3618 		move_panel(pan, sy, sx);
3619 		ClearPanel();
3620 	}
3621 
3622 	keypad(win, true);
3623 	meta(win, true);
3624 
3625 	if (menu != NULL)
3626 		menu->SetPosition(1, 0, 0, 0);
3627 
3628 	if (main_component != NULL)
3629 		main_component->SetPosition(1, 1, in_x - 1, in_y - 2);
3630 }
3631 
kp_escape_timer(TIMEEVENT_PARMS)3632 int kp_escape_timer(TIMEEVENT_PARMS) {
3633 	// fprintf(stderr, "trigger escape timer %u %u\n", globalreg->timestamp.tv_sec, globalreg->timestamp.tv_usec);
3634 	ungetch(0x00);
3635 	((Kis_Panel *) parm)->Poll();
3636 
3637 	return 0;
3638 }
3639 
Poll()3640 int Kis_Panel::Poll() {
3641 	if (globalreg->spindown)
3642 		return 0;
3643 
3644 	int get = wgetch(win);
3645 	MEVENT mevent;
3646 	int ret;
3647 	int escape_timer_possible = 1;
3648 
3649 	/*
3650 	// Timeout on our internal escape handler
3651 	struct timeval key_diff;
3652 
3653 	SubtractTimeval(&(globalreg->timestamp),
3654 					&last_key_time,
3655 					&key_diff);
3656 
3657 	if (key_diff.tv_sec > 1 ||
3658 		key_diff.tv_usec > 500000) {
3659 		last_key = 0;
3660 	}
3661 
3662 	last_key_time.tv_sec = globalreg->timestamp.tv_sec;
3663 	last_key_time.tv_usec = globalreg->timestamp.tv_usec;
3664 	*/
3665 
3666 	if (get == KEY_RESIZE) {
3667 		globalreg->winch = 1;
3668 	}
3669 
3670 	if (escape_timer > 0) {
3671 		globalreg->timetracker->RemoveTimer(escape_timer);
3672 		escape_timer = -1;
3673 
3674 		// Don't allow requeuing a second timer on a timered escape
3675 		escape_timer_possible = 0;
3676 	}
3677 
3678 	// If we're getting triggered from the timer callback
3679 	if (get == 0x00) {
3680 		get = 0x1b;
3681 	}
3682 
3683 	if (get == 0x1b && last_key != 0x1b) {
3684 		last_key = 0x1b;
3685 		// fprintf(stderr, "schedule escape timer %u %u\n", globalreg->timestamp.tv_sec, globalreg->timestamp.tv_usec);
3686 		if (escape_timer_possible) {
3687 			escape_timer =
3688 				globalreg->timetracker->RegisterTimer(2, NULL, 0,
3689 													  &kp_escape_timer, this);
3690 			return 1;
3691 		}
3692 	} else if (last_key == 0x1b && get == 0x5b) {
3693 		last_key = 0x5b;
3694 		return 1;
3695 	} else if (last_key == 0x5b) {
3696 		switch (get) {
3697 			case 0x41:
3698 				get = KEY_UP;
3699 				break;
3700 			case 0x42:
3701 				get = KEY_DOWN;
3702 				break;
3703 			case 0x43:
3704 				get = KEY_RIGHT;
3705 				break;
3706 			case 0x44:
3707 				get = KEY_LEFT;
3708 				break;
3709 			default:
3710 				break;
3711 		}
3712 	} else {
3713 		last_key = 0;
3714 	}
3715 
3716 	if (get == KEY_MOUSE) {
3717 		getmouse(&mevent);
3718 		ret = MouseEvent(&mevent);
3719 	} else {
3720 		// fprintf(stderr, "debug - passing key %02x\n", get);
3721 		ret = KeyPress(get);
3722 	}
3723 
3724 	// If we can't trigger a timer, this means we came in from a timer,
3725 	// which means we should force an extra interface redraw to handle what
3726 	// it may have changed, we don't get our normal redraw since we didn't get
3727 	// a real keypress
3728 	if (escape_timer_possible == 0)
3729 		kpinterface->DrawInterface();
3730 
3731 	if (ret < 0)
3732 		return ret;
3733 
3734 	return 1;
3735 }
3736 
SetTitle(string in_title)3737 void Kis_Panel::SetTitle(string in_title) {
3738 	title = in_title;
3739 }
3740 
DrawTitleBorder()3741 void Kis_Panel::DrawTitleBorder() {
3742 	ColorFromPref(text_color, "panel_text_color");
3743 	ColorFromPref(border_color, "panel_border_color");
3744 
3745 	wattrset(win, border_color);
3746 	box(win, 0, 0);
3747 	wattron(win, WA_UNDERLINE);
3748 	mvwaddstr(win, 0, 3, title.c_str());
3749 	wattroff(win, WA_UNDERLINE);
3750 
3751 	wattrset(win, text_color);
3752 }
3753 
DrawComponentVec()3754 void Kis_Panel::DrawComponentVec() {
3755 	wattrset(win, text_color);
3756 	for (unsigned int x = 0; x < pan_comp_vec.size(); x++) {
3757 		if ((pan_comp_vec[x].comp_flags & KIS_PANEL_COMP_DRAW) == 0)
3758 			continue;
3759 
3760 		pan_comp_vec[x].comp->DrawComponent();
3761 	}
3762 
3763 	if (menu != NULL)
3764 		menu->DrawComponent();
3765 }
3766 
3767 #endif
3768 
3769