1 // =====================================================================
2 //
3 // flinput2.cxx
4 //
5 // Author: Stelios Buononos, M0GLD
6 // Copyright: 2010
7 //
8 // This file is part of flmsg.
9 //
10 // This is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation; either version 3 of the License, or
13 // (at your option) any later version.
14 //
15 // This software is distributed in the hope that it will be useful,
16 // but WITHOUT ANY WARRANTY; without even the implied warranty of
17 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  It is
18 // copyright under the GNU General Public License.
19 //
20 // You should have received a copy of the GNU General Public License
21 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
22 //
23 // =====================================================================
24 
25 #include "config.h"
26 
27 #include <cctype>
28 
29 #include <FL/Fl.H>
30 #include <FL/Fl_Window.H>
31 #include <FL/Fl_Widget.H>
32 #include <FL/Fl_Input.H>
33 #include <FL/Fl_Menu_Item.H>
34 #include <FL/Fl_Tooltip.H>
35 
36 #include "icons.h"
37 #include "flinput2.h"
38 #include "gettext.h"
39 #include "util.h"
40 
41 enum { OP_UNDO, OP_CUT, OP_COPY, OP_PASTE, OP_DELETE, OP_CLEAR, OP_SELECT_ALL };
42 
43 static Fl_Menu_Item cmenu[] = {
44 	{ icons::make_icon_label(_("Undo"), edit_undo_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
45 	{ icons::make_icon_label(_("Cut"), edit_cut_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
46 	{ icons::make_icon_label(_("Copy"), edit_copy_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
47 	{ icons::make_icon_label(_("Paste"), edit_paste_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
48 	{ icons::make_icon_label(_("Delete"), trash_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
49 	{ icons::make_icon_label(_("Clear"), edit_clear_icon), 0, 0, 0, FL_MENU_DIVIDER, _FL_MULTI_LABEL },
50 	{ icons::make_icon_label(_("Select All"), edit_select_all_icon), 0, 0, 0, 0, _FL_MULTI_LABEL },
51 	{ 0 }
52 };
53 static bool cmenu_init = false;
54 
55 
Fl_Input2(int x,int y,int w,int h,const char * l)56 Fl_Input2::Fl_Input2(int x, int y, int w, int h, const char* l)
57 : Fl_Input(x, y, w, h, l)
58 {
59 	if (!cmenu_init) {
60 		for (size_t i = 0; i < sizeof(cmenu)/sizeof(*cmenu) - 1; i++)
61 			if (cmenu[i].labeltype() == _FL_MULTI_LABEL)
62 				icons::set_icon_label(&cmenu[i]);
63 		cmenu_init = true;
64 	}
65 }
66 
handle(int event)67 int Fl_Input2::handle(int event)
68 {
69 	switch (event) {
70 		case FL_KEYBOARD: {
71 			int b = Fl::event_key();
72 			int p = position();
73 			// stop the move-to-next-field madness, we have Tab for that!
74 			if (unlikely((b == FL_Left && p == 0) || (b == FL_Right && p == size()) ||
75 						 (b == FL_Up && line_start(p) == 0) ||
76 						 (b == FL_Down && line_end(p) == size())))
77 				return 1;
78 			else if (unlikely(Fl::event_state() & (FL_ALT | FL_META))) {
79 				switch (b) {
80 					case 'c': { // capitalise
81 						if (readonly() || p == size())
82 							return 1;
83 
84 						while (p < size() && isspace(*(value() + p)))
85 							p++;
86 						if (p == size())
87 							return 1;
88 						char c = toupper(*(value() + p));
89 						replace(p, p + 1, &c, 1);
90 						position(word_end(p));
91 					}
92 						return 1;
93 					case 'u': case 'l': { // upper/lower case
94 						if (readonly() || p == size())
95 							return 1;
96 						while (p < size() && isspace(*(value() + p)))
97 							p++;
98 						int n = word_end(p) - p;
99 						if (n == 0)
100 							return 1;
101 
102 						char* s = new char[n];
103 						memcpy(s, value() + p, n);
104 						if (b == 'u')
105 							for (int i = 0; i < n; i++)
106 								s[i] = toupper(s[i]);
107 						else
108 							for (int i = 0; i < n; i++)
109 								s[i] = tolower(s[i]);
110 						replace(p, p + n, s, n);
111 						position(p + n);
112 						delete [] s;
113 						return 1;
114 					}
115 					default:
116 						break;
117 				}
118 			}
119 		}
120 			return Fl_Input::handle(event);
121 		case FL_MOUSEWHEEL: {
122 			if (!((type() & (FL_MULTILINE_INPUT | FL_MULTILINE_OUTPUT)) && Fl::event_inside(this)))
123 				return Fl_Input::handle(event);
124 			int d;
125 			if (!((d = Fl::event_dy()) || (d = Fl::event_dx())))
126 				return Fl_Input::handle(event);
127 			if (Fl::focus() != this)
128 				take_focus();
129 			up_down_position(d + (d > 0 ? line_end(position()) : line_start(position())));
130 			return 1;
131 		}
132 		case FL_PUSH:
133 			if (Fl::event_button() == FL_RIGHT_MOUSE)
134 				break;
135 			// fall through
136 		default:
137 			return Fl_Input::handle(event);
138 	}
139 
140 	bool sel = position() != mark(), ro = readonly();
141 	icons::set_active(&cmenu[OP_UNDO], !ro);
142 	icons::set_active(&cmenu[OP_CUT], !ro && sel);
143 	icons::set_active(&cmenu[OP_COPY], sel);
144 	icons::set_active(&cmenu[OP_PASTE], !ro);
145 	icons::set_active(&cmenu[OP_DELETE], !ro && sel);
146 	icons::set_active(&cmenu[OP_CLEAR], !ro && size());
147 	icons::set_active(&cmenu[OP_SELECT_ALL], size());
148 
149 	take_focus();
150 	window()->cursor(FL_CURSOR_DEFAULT);
151 	int t = Fl_Tooltip::enabled();
152 	Fl_Tooltip::disable();
153 	const Fl_Menu_Item* m = cmenu->popup(Fl::event_x(), Fl::event_y());
154 	Fl_Tooltip::enable(t);
155 
156 	if (!m)
157 		return 1;
158 	switch (m - cmenu) {
159 		case OP_UNDO:
160 			undo();
161 			break;
162 		case OP_CUT:
163 			cut();
164 			copy_cuts();
165 			break;
166 		case OP_COPY:
167 			copy(1);
168 			break;
169 		case OP_PASTE:
170 			Fl::paste(*this, 1);
171 			break;
172 		case OP_DELETE:
173 			cut();
174 			break;
175 		case OP_CLEAR:
176 			cut(0, size());
177 			break;
178 		case OP_SELECT_ALL:
179 			position(0, size());
180 			break;
181 	}
182 
183 	return 1;
184 }
185