1 /*
2 *
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
7 *
8 * See LICENSING which should be included
9 * along with this file for more details
10 *
11 */
12
13 #include <cstdlib>
14 #include <iostream>
15 #include <string>
16 #include <fstream>
17 #include <sstream>
18 #include <algorithm>
19
20 #include "bitmap.h"
21 #include "feio.h"
22 #include "error.h"
23 #include "festring.h"
24 #include "graphics.h"
25 #include "rawbit.h"
26 #include "specialkeys.h"
27 #include "whandler.h"
28
29 #include "dbgmsgproj.h"
30
31 /**
32 * They modify current behavior of anything that accepts such modifications,
33 * or
34 * they may override whatever is happening with something else.
35 */
36
37 //#undef REQ
38 //#define REQ(name) \
39 // bool b##name##Request=false; \
40 // void specialkeys::Clear##name##Request(){b##name##Requested=false;}
41 //REQ(Filter);
42 //REQ(FocusedElementHelp);
43 //REQ(CopyToClipboard);
44 //REQ(PasteFromClipboard);
45
FilterListQuestion(cfestring what)46 cfestring specialkeys::FilterListQuestion(cfestring what)
47 {
48 festring What=what;
49
50 v2 pos = v2(16, 6);
51 static festring Topic = "Type this list filter:";
52
53 int R = iosystem::StringQuestion(What, Topic, pos, WHITE, 0, 30, false /*TODO !bGameIsRunning*/, true, NULL);
54
55 /**
56 * TODO
57 * clear the filter text using the background!
58 * needed when pressing ESC or ENTER and not changing the filter!
59 * tip: igraph::BlitBackGround(pos, v2(RES.X, 23));
60 * also, could may be copy the background from doublebuffer and just paste it back after....
61 */
62
63 // cheap workaround to not look too bad (like input was not accepted) at least
64 // DOUBLE_BUFFER->Fill(pos,v2(RES.X, 23),BLACK);
65 FONT->Printf(DOUBLE_BUFFER, pos, BLACK, "%s", Topic.CStr());
66 FONT->Printf(DOUBLE_BUFFER, v2(pos.X, pos.Y + 10), BLACK, "%s_", What.CStr());
67
68 if(R == NORMAL_EXIT){ DBG1(What.CStr());
69 return What;
70 }
71
72 if(R == ABORTED)
73 return what;
74
75 return cfestring();
76 }
77
IsRequestedEvent(SKEvent e)78 bool specialkeys::IsRequestedEvent(SKEvent e)
79 {
80 return (e==Request);
81 }
82
83 std::vector<festring> afsHelpDialog;
DrawHelpDialog(bitmap * Buffer)84 void DrawHelpDialog(bitmap* Buffer) //TODO this kind'o message should be more global to be easier to be used...
85 { //TODO create a buffer to not re-draw every loop... unless want to animate it...
86 if(afsHelpDialog.size()==0)return;
87
88 int iFontLetterWidth=8;
89 int iLH=15;
90
91 v2 v2Border;
92 v2Border.Y=afsHelpDialog.size()*iLH+iLH*2;
93 for(int i=0;i<afsHelpDialog.size();i++){
94 int iW = afsHelpDialog[i].GetSize()*iFontLetterWidth;
95 if(v2Border.X<iW)v2Border.X=iW;
96 }
97
98 v2Border.X+=iLH*2;
99
100 v2 v2TL(RES.X/2-v2Border.X/2,RES.Y/2-v2Border.Y/2);
101
102 Buffer->Fill(v2TL,v2Border,BLACK);
103 graphics::DrawRectangleOutlineAround(Buffer, v2TL, v2Border, DARK_GRAY, true);
104
105 v2TL+=v2(iLH,iLH);
106
107 for(int i=0;i<afsHelpDialog.size();i++)
108 FONT->Printf(Buffer, v2(v2TL.X,v2TL.Y+i*iLH), WHITE, "%s", afsHelpDialog[i].CStr());
109 }
110
111 bool bConsumingEvent=false;
IsConsumingEvent()112 bool specialkeys::IsConsumingEvent()
113 {
114 return bConsumingEvent;
115 }
116
117 int specialkeys::Request=-1;
ConsumeEvent(SKEvent e,festring & fsInOut)118 bool specialkeys::ConsumeEvent(SKEvent e,festring& fsInOut){DBGLN;
119 if(!IsRequestedEvent(e))
120 return false;
121
122 bConsumingEvent=true;
123
124 switch(e){
125 case ClearStringInput:{
126 fsInOut.Empty();
127 Request=-1;
128 };break;
129
130 case Filter:{DBGLN;
131 globalwindowhandler::SuspendKeyTimeout();
132 fsInOut=FilterListQuestion(fsInOut);
133 globalwindowhandler::ResumeKeyTimeout();
134 // bFilterRequest=false;
135 Request=-1;
136 };break;
137
138 case CopyToClipboard:{DBGLN;
139 #ifdef UNIX //TODO for MACOSX can use `pbcopy` see https://github.com/Attnam/ivan/pull/458#discussion_r212823516
140 /* TODO
141 * the executable OS command may allow, thru the input text, any OS commands and that is not good.
142 * ex.: echo just needs to be broken like: echo -n ''`anyOtherOSCommand`'' |xclip -i //where the input string is "'`anyOtherOSCommand`'"
143 * so this needs to be done in some other way...
144 std::ostringstream osStkCmd;
145 // this will execute an OS command
146 osStkCmd<<"echo -n '"<<fsInOut.CStr()<<"' |xclip -i";
147 FILE* pipeFile = popen(osStkCmd.str().c_str(),"r"); //TODO exec pipelessly
148 if(pipeFile!=NULL){
149 pclose(pipeFile);
150 }else{
151 DBG2("unable to execute popen() with cmd: ",osStkCmd.str());
152 }
153 */
154 #endif
155 //TODO windows (can be hardcoded)
156 };break;
157
158 case PasteFromClipboard:{DBGLN; //TODO iosystem::StringQuestion? globalwindowhandler::GetKey at keybuffer?
159 #ifdef UNIX //TODO works for MACOSX too?
160 std::ostringstream osStkCmd;
161 osStkCmd<<"xclip -o";
162 FILE* pipeFile = popen(osStkCmd.str().c_str(),"r");
163 if(pipeFile!=NULL){
164 static const int i=10*1024;
165 char buf[i];
166 if(fread(buf,1,i,pipeFile)>0)
167 fsInOut = buf;
168 pclose(pipeFile);
169 }else{
170 DBG2("unable to execute popen() with cmd: ",osStkCmd.str());
171 }
172 #endif
173 //TODO windows (can be hardcoded)
174 };break;
175
176 case FocusedElementHelp:{DBGLN;
177 globalwindowhandler::SuspendKeyTimeout();
178
179 std::stringstream ss(fsInOut.CStr());
180 std::string line;
181 std::vector<festring> afs;
182 festring fsLine;
183 while(std::getline(ss,line,'\n')){ // if(fsInOut.Find('\n',0)!=festring::NPos){
184 afs.clear();
185 fsLine=line.c_str();
186 festring::SplitString(fsLine,afs,80,0); //TODO limit line length based on fontWidth vs screenWidth
187 for(int i=0;i<afs.size();i++){
188 afsHelpDialog.push_back(festring(afs[i]));
189 }
190 }
191
192 graphics::BlitDBToScreen();DBGLN;
193
194 GET_KEY(true);DBGLN;
195
196 afsHelpDialog.clear();
197
198 globalwindowhandler::ResumeKeyTimeout();
199
200 Request=-1; //it is com
201 };break;
202 }
203
204 bConsumingEvent=false;
205
206 return true;
207 }
208
init()209 void specialkeys::init()
210 {
211 globalwindowhandler::SetFunctionKeyHandler(specialkeys::FunctionKeyHandler);
212 globalwindowhandler::SetControlKeyHandler(specialkeys::ControlKeyHandler);
213
214 graphics::AddDrawAboveAll(&DrawHelpDialog,90000,"HelpDialog");
215 }
216
217 typedef std::map<SDL_Keycode,specialkeyhandler> ckhmap;
218 ckhmap CkhMap;
219
FunctionKeyHandler(SDL_Keycode key)220 bool specialkeys::FunctionKeyHandler(SDL_Keycode key)
221 {DBGLN;
222 switch(key){ //TODO how to not use SDLK_ keys here??? shouldnt anyway????
223 case SDLK_F1:DBGLN;
224 Request=FocusedElementHelp;
225 return true;
226 default:
227 ckhmap::iterator Iterator = CkhMap.find(key);
228 if(Iterator != CkhMap.end()){
229 Iterator->second();
230 return true;
231 }
232 break;
233 }
234 return false;
235 }
236
237 /**
238 * add Function or Ctrl+ key handler
239 */
AddCtrlOrFuncKeyHandler(SDL_Keycode key,specialkeyhandler Handler)240 void specialkeys::AddCtrlOrFuncKeyHandler(SDL_Keycode key, specialkeyhandler Handler)
241 {
242 ckhmap::iterator Iterator = CkhMap.find(key);
243 if(Iterator != CkhMap.end())
244 ABORT("control key handler already set for key %d",key);
245
246 CkhMap.insert(std::make_pair(key,Handler));
247 }
248
249 /**
250 * Ctrl+FunctionKey is here tho
251 */
ControlKeyHandler(SDL_Keycode key)252 bool specialkeys::ControlKeyHandler(SDL_Keycode key)
253 {
254 switch(key){ //TODO use SDLK_ keys?
255 case 'f':
256 Request=Filter;
257 return true;
258 case 'c':
259 Request=CopyToClipboard;
260 return true;
261 case 'v':
262 Request=PasteFromClipboard;
263 return true;
264 case SDLK_DELETE:
265 Request=ClearStringInput;
266 return true;
267 default:
268 ckhmap::iterator Iterator = CkhMap.find(key);
269 if(Iterator != CkhMap.end()){
270 Iterator->second();
271 return true;
272 }
273 break;
274 }
275
276 return false;
277 }
278
279