Merge pull request #4314 from MartijnKaijser/beta1
[vuplus_xbmc] / xbmc / osx / ios / IOSKeyboardView.mm
1 /*
2  *      Copyright (C) 2012-2013 Team XBMC
3  *      http://xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20 #define BOOL XBMC_BOOL 
21 #include "guilib/GUIWindowManager.h"
22 #include "guilib/GUIKeyboardFactory.h"
23 #include "threads/Event.h"
24 #include "Application.h"
25 #undef BOOL
26
27 #import "IOSKeyboardView.h"
28 #import "IOSScreenManager.h"
29 #import "XBMCController.h"
30 #import "XBMCDebugHelpers.h"
31 #include "IOSKeyboard.h"
32
33 static CEvent keyboardFinishedEvent;
34
35 #define INPUT_BOX_HEIGHT 30
36 #define SPACE_BETWEEN_INPUT_AND_KEYBOARD 0
37
38 @implementation KeyboardView
39 @synthesize text;
40 @synthesize _confirmed;
41 @synthesize _iosKeyboard;
42
43 - (id)initWithFrame:(CGRect)frame
44 {
45   self = [super initWithFrame:frame];
46   if (self) 
47   {
48     _iosKeyboard = nil;
49     _keyboardIsShowing = 0;
50     _kbHeight = 0;
51     _confirmed = NO;
52     _canceled = NULL;
53     _deactivated = NO;
54     
55     self.text = [NSMutableString stringWithString:@""];
56
57    // default input box position above the half screen.
58     CGRect textFieldFrame = CGRectMake(frame.size.width/2, 
59                                        frame.size.height/2-INPUT_BOX_HEIGHT-SPACE_BETWEEN_INPUT_AND_KEYBOARD, 
60                                        frame.size.width/2, 
61                                        INPUT_BOX_HEIGHT);
62     _textField = [[UITextField alloc] initWithFrame:textFieldFrame];
63     _textField.clearButtonMode = UITextFieldViewModeAlways;
64     // UITextBorderStyleRoundedRect; - with round rect we can't control backgroundcolor
65     _textField.borderStyle = UITextBorderStyleNone;    
66     _textField.returnKeyType = UIReturnKeyDone;
67     _textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
68     _textField.backgroundColor = [UIColor whiteColor];
69     _textField.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
70     _textField.delegate = self;
71     
72     CGRect labelFrame = textFieldFrame;
73     labelFrame.origin.x = 0;
74     _heading = [[UITextField alloc] initWithFrame:labelFrame];
75     _heading.borderStyle = UITextBorderStyleNone;
76     _heading.backgroundColor = [UIColor whiteColor];
77     _heading.adjustsFontSizeToFitWidth = YES;
78     _heading.contentVerticalAlignment = UIControlContentVerticalAlignmentCenter;
79     _heading.enabled = NO;
80
81     [self addSubview:_heading];
82     [self addSubview:_textField];
83    
84     self.userInteractionEnabled = YES;
85
86     [self setAlpha:0.9];
87     [[NSNotificationCenter defaultCenter] addObserver:self
88                                              selector:@selector(textChanged:)
89                                                  name:UITextFieldTextDidChangeNotification
90                                                object:_textField];
91     [[NSNotificationCenter defaultCenter] addObserver:self 
92                                              selector:@selector(keyboardDidHide:) 
93                                                  name:UIKeyboardDidHideNotification
94                                                object:nil];
95     [[NSNotificationCenter defaultCenter] addObserver:self
96                                              selector:@selector(keyboardWillShow:)
97                                                  name:UIKeyboardWillShowNotification
98                                                object:nil];
99     [[NSNotificationCenter defaultCenter] addObserver:self
100                                              selector:@selector(keyboardDidShow:)
101                                                  name:UIKeyboardDidShowNotification
102                                                object:nil];
103   }
104   return self;
105 }
106
107 - (void)layoutSubviews
108 {
109   CGFloat headingW = 0;
110   if (_heading.text and _heading.text.length > 0)
111   {
112     CGSize headingSize = [_heading.text sizeWithFont:[UIFont systemFontOfSize:[UIFont systemFontSize]]];
113     headingW = MIN(self.bounds.size.width/2, headingSize.width+30);
114   }
115   CGFloat y = _kbHeight <= 0 ? 
116     _textField.frame.origin.y : 
117     MIN(self.bounds.size.height-_kbHeight, self.bounds.size.height/5*3) - INPUT_BOX_HEIGHT - SPACE_BETWEEN_INPUT_AND_KEYBOARD;
118   _heading.frame = CGRectMake(0, y, headingW, INPUT_BOX_HEIGHT);
119   _textField.frame = CGRectMake(headingW, y, self.bounds.size.width-headingW, INPUT_BOX_HEIGHT);
120 }
121
122 -(void)keyboardWillShow:(NSNotification *) notification{
123   NSDictionary* info = [notification userInfo];
124   CGRect kbRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
125   LOG(@"keyboardWillShow: keyboard frame: %@", NSStringFromCGRect(kbRect));
126   _kbHeight = kbRect.size.width;
127   [self setNeedsLayout];
128   _keyboardIsShowing = 1;
129 }
130
131 -(void)keyboardDidShow:(NSNotification *) notification{
132   LOG(@"keyboardDidShow: deactivated: %d", _deactivated);
133   _keyboardIsShowing = 2;
134   if (_deactivated)
135     [self doDeactivate:nil];
136 }
137
138 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
139 {
140   PRINT_SIGNATURE();
141   [_textField resignFirstResponder];
142 }
143
144 - (BOOL)textFieldShouldEndEditing:(UITextField *)textField
145 {
146   LOG(@"%s: keyboard IsShowing %d", __PRETTY_FUNCTION__, _keyboardIsShowing);
147   // Do not break the keyboard show up process, else we will lost
148   // keyboard did hide notifaction.
149   return _keyboardIsShowing != 1;
150 }
151
152 - (void)textFieldDidEndEditing:(UITextField *)textField
153 {
154   PRINT_SIGNATURE();
155   [self deactivate];
156 }
157
158 -(BOOL)textFieldShouldReturn:(UITextField *)textField{
159   PRINT_SIGNATURE();
160   _confirmed = YES;
161   [_textField resignFirstResponder];
162   return YES;
163 }
164
165 - (void)keyboardDidHide:(id)sender
166 {
167   PRINT_SIGNATURE();
168   
169   _keyboardIsShowing = 0;
170
171   if (_textField.editing)
172   {
173     LOG(@"kb hide when editing, it could be a language switch");
174     return;
175   }
176
177   [self deactivate];
178 }
179
180 - (void) doActivate:(NSDictionary *)dict
181 {
182   PRINT_SIGNATURE();
183   [g_xbmcController activateKeyboard:self];
184   [_textField becomeFirstResponder];
185   [self setNeedsLayout];
186   keyboardFinishedEvent.Reset();
187 }
188
189 - (void)activate
190 {
191   PRINT_SIGNATURE();
192   if([NSThread currentThread] != [NSThread mainThread])
193   {
194     [self performSelectorOnMainThread:@selector(doActivate:) withObject:nil  waitUntilDone:YES];  
195   }
196   else
197   {
198     // this would be fatal! We never should be called from the ios mainthread
199     return;
200   }
201
202   // emulate a modale dialog here
203   // we are waiting on the user finishing the keyboard
204   // and have to process our app while doing that
205   // basicall what our GUIDialog does if called modal
206   while(!keyboardFinishedEvent.WaitMSec(500) && !g_application.m_bStop)
207   {
208     if (NULL != _canceled && *_canceled)
209     {
210       [self deactivate];
211       _canceled = NULL;
212     }
213     // if we are not in xbmc main thread, ProcessRenderLoop() is nop.
214     g_windowManager.ProcessRenderLoop();
215   }
216 }
217
218 - (void) doDeactivate:(NSDictionary *)dict
219 {
220   LOG(@"%s: keyboard IsShowing %d", __PRETTY_FUNCTION__, _keyboardIsShowing);
221   _deactivated = YES;
222   
223   // Do not break keyboard show up process, if so there's a bug of ios4 will not
224   // notify us keyboard hide.
225   if (_keyboardIsShowing == 1)
226     return;
227
228   // invalidate our callback object
229   if(_iosKeyboard)
230   {
231     _iosKeyboard->invalidateCallback();
232     _iosKeyboard = nil;
233   }
234   // give back the control to whoever
235   [_textField resignFirstResponder];
236
237   // allways calld in the mainloop context
238   // detach the keyboard view from our main controller
239   [g_xbmcController deactivateKeyboard:self];
240   
241   // until keyboard did hide, we let the calling thread break loop
242   if (0 == _keyboardIsShowing)
243   {
244     // no more notification we want to receive.
245     [[NSNotificationCenter defaultCenter] removeObserver: self];
246     
247     keyboardFinishedEvent.Set();
248   }
249 }
250
251 - (void) deactivate
252 {
253   PRINT_SIGNATURE();
254   if([NSThread currentThread] != [NSThread mainThread])
255   {
256     [self performSelectorOnMainThread:@selector(doDeactivate:) withObject:nil  waitUntilDone:YES];  
257   }
258   else
259   {
260     [self doDeactivate:nil];
261   }
262 }
263
264 - (void) setKeyboardText:(NSString*)aText closeKeyboard:(BOOL)closeKeyboard
265 {
266   LOG(@"%s: %@, %d", __PRETTY_FUNCTION__, aText, closeKeyboard);
267   if([NSThread currentThread] != [NSThread mainThread])
268   {
269     [self performSelectorOnMainThread:@selector(setDefault:) withObject:aText  waitUntilDone:YES];
270   }
271   else
272   {
273     [self setDefault:aText];
274   }
275   if (closeKeyboard)
276   {
277     _confirmed = YES;
278     [self deactivate];
279   }
280 }
281
282 - (void) setHeading:(NSString *)heading
283 {
284   if (heading && heading.length > 0) {
285     _heading.text = [NSString stringWithFormat:@" %@:", heading];
286   }
287   else {
288     _heading.text = nil;
289   }
290 }
291
292 - (void) setDefault:(NSString *)defaultText
293 {
294   [_textField setText:defaultText];
295   [self textChanged:nil];
296 }
297
298 - (void) setHidden:(BOOL)hidden
299 {
300   [_textField setSecureTextEntry:hidden];
301 }
302
303 - (void) textChanged:(NSNotification*)aNotification; {
304   if (![self.text isEqualToString:_textField.text])
305   {
306     [self.text setString:_textField.text];
307     if (_iosKeyboard)
308     {
309       _iosKeyboard->fireCallback([self.text UTF8String]);
310     }
311   }
312 }
313
314 - (void) setCancelFlag:(bool *)cancelFlag
315 {
316   _canceled = cancelFlag;
317 }
318
319 - (void) dealloc
320 {
321   PRINT_SIGNATURE();
322   self.text = nil;
323   [super dealloc];
324 }
325 @end