[release] version bump to 13.0 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     _textField.borderStyle = UITextBorderStyleRoundedRect;
65     _textField.returnKeyType = UIReturnKeyDone;
66     _textField.autocapitalizationType = UITextAutocapitalizationTypeNone;
67     _textField.delegate = self;
68     
69     CGRect labelFrame = textFieldFrame;
70     labelFrame.origin.x = 0;
71     _heading = [[UILabel alloc] initWithFrame:labelFrame];
72     _heading.adjustsFontSizeToFitWidth = YES;
73
74     [self addSubview:_heading];
75     [self addSubview:_textField];
76    
77     self.userInteractionEnabled = YES;
78
79     [self setAlpha:0.9];
80     [[NSNotificationCenter defaultCenter] addObserver:self
81                                              selector:@selector(textChanged:)
82                                                  name:UITextFieldTextDidChangeNotification
83                                                object:_textField];
84     [[NSNotificationCenter defaultCenter] addObserver:self 
85                                              selector:@selector(keyboardDidHide:) 
86                                                  name:UIKeyboardDidHideNotification
87                                                object:nil];
88     [[NSNotificationCenter defaultCenter] addObserver:self
89                                              selector:@selector(keyboardWillShow:)
90                                                  name:UIKeyboardWillShowNotification
91                                                object:nil];
92     [[NSNotificationCenter defaultCenter] addObserver:self
93                                              selector:@selector(keyboardDidShow:)
94                                                  name:UIKeyboardDidShowNotification
95                                                object:nil];
96   }
97   return self;
98 }
99
100 - (void)layoutSubviews
101 {
102   CGFloat headingW = 0;
103   if (_heading.text and _heading.text.length > 0)
104   {
105     CGSize headingSize = [_heading.text sizeWithFont:[UIFont systemFontOfSize:[UIFont systemFontSize]]];
106     headingW = MIN(self.bounds.size.width/2, headingSize.width+30);
107   }
108   CGFloat y = _kbHeight <= 0 ? 
109     _textField.frame.origin.y : 
110     MIN(self.bounds.size.height-_kbHeight, self.bounds.size.height/5*3) - INPUT_BOX_HEIGHT - SPACE_BETWEEN_INPUT_AND_KEYBOARD;
111   _heading.frame = CGRectMake(0, y, headingW, INPUT_BOX_HEIGHT);
112   _textField.frame = CGRectMake(headingW, y, self.bounds.size.width-headingW, INPUT_BOX_HEIGHT);
113 }
114
115 -(void)keyboardWillShow:(NSNotification *) notification{
116   NSDictionary* info = [notification userInfo];
117   CGRect kbRect = [[info objectForKey:UIKeyboardFrameEndUserInfoKey] CGRectValue];
118   LOG(@"keyboardWillShow: keyboard frame: %@", NSStringFromCGRect(kbRect));
119   _kbHeight = kbRect.size.width;
120   [self setNeedsLayout];
121   _keyboardIsShowing = 1;
122 }
123
124 -(void)keyboardDidShow:(NSNotification *) notification{
125   LOG(@"keyboardDidShow: deactivated: %d", _deactivated);
126   _keyboardIsShowing = 2;
127   if (_deactivated)
128     [self doDeactivate:nil];
129 }
130
131 - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
132 {
133   PRINT_SIGNATURE();
134   [_textField resignFirstResponder];
135 }
136
137 - (BOOL)textFieldShouldEndEditing:(UITextField *)textField
138 {
139   LOG(@"%s: keyboard IsShowing %d", __PRETTY_FUNCTION__, _keyboardIsShowing);
140   // Do not break the keyboard show up process, else we will lost
141   // keyboard did hide notifaction.
142   return _keyboardIsShowing != 1;
143 }
144
145 - (void)textFieldDidEndEditing:(UITextField *)textField
146 {
147   PRINT_SIGNATURE();
148   [self deactivate];
149 }
150
151 -(BOOL)textFieldShouldReturn:(UITextField *)textField{
152   PRINT_SIGNATURE();
153   _confirmed = YES;
154   [_textField resignFirstResponder];
155   return YES;
156 }
157
158 - (void)keyboardDidHide:(id)sender
159 {
160   PRINT_SIGNATURE();
161   
162   _keyboardIsShowing = 0;
163
164   if (_textField.editing)
165   {
166     LOG(@"kb hide when editing, it could be a language switch");
167     return;
168   }
169
170   [self deactivate];
171 }
172
173 - (void) doActivate:(NSDictionary *)dict
174 {
175   PRINT_SIGNATURE();
176   [g_xbmcController activateKeyboard:self];
177   [_textField becomeFirstResponder];
178   [self setNeedsLayout];
179   keyboardFinishedEvent.Reset();
180 }
181
182 - (void)activate
183 {
184   PRINT_SIGNATURE();
185   if([NSThread currentThread] != [NSThread mainThread])
186   {
187     [self performSelectorOnMainThread:@selector(doActivate:) withObject:nil  waitUntilDone:YES];  
188   }
189   else
190   {
191     // this would be fatal! We never should be called from the ios mainthread
192     return;
193   }
194
195   // emulate a modale dialog here
196   // we are waiting on the user finishing the keyboard
197   // and have to process our app while doing that
198   // basicall what our GUIDialog does if called modal
199   while(!keyboardFinishedEvent.WaitMSec(500) && !g_application.m_bStop)
200   {
201     if (NULL != _canceled && *_canceled)
202     {
203       [self deactivate];
204       _canceled = NULL;
205     }
206     // if we are not in xbmc main thread, ProcessRenderLoop() is nop.
207     g_windowManager.ProcessRenderLoop();
208   }
209 }
210
211 - (void) doDeactivate:(NSDictionary *)dict
212 {
213   LOG(@"%s: keyboard IsShowing %d", __PRETTY_FUNCTION__, _keyboardIsShowing);
214   _deactivated = YES;
215   
216   // Do not break keyboard show up process, if so there's a bug of ios4 will not
217   // notify us keyboard hide.
218   if (_keyboardIsShowing == 1)
219     return;
220
221   // invalidate our callback object
222   if(_iosKeyboard)
223   {
224     _iosKeyboard->invalidateCallback();
225     _iosKeyboard = nil;
226   }
227   // give back the control to whoever
228   [_textField resignFirstResponder];
229
230   // allways calld in the mainloop context
231   // detach the keyboard view from our main controller
232   [g_xbmcController deactivateKeyboard:self];
233   
234   // until keyboard did hide, we let the calling thread break loop
235   if (0 == _keyboardIsShowing)
236   {
237     // no more notification we want to receive.
238     [[NSNotificationCenter defaultCenter] removeObserver: self];
239     
240     keyboardFinishedEvent.Set();
241   }
242 }
243
244 - (void) deactivate
245 {
246   PRINT_SIGNATURE();
247   if([NSThread currentThread] != [NSThread mainThread])
248   {
249     [self performSelectorOnMainThread:@selector(doDeactivate:) withObject:nil  waitUntilDone:YES];  
250   }
251   else
252   {
253     [self doDeactivate:nil];
254   }
255 }
256
257 - (void) setHeading:(NSString *)heading
258 {
259   if (heading && heading.length > 0) {
260     _heading.text = [NSString stringWithFormat:@" %@:", heading];
261   }
262   else {
263     _heading.text = nil;
264   }
265 }
266
267 - (void) setDefault:(NSString *)defaultText
268 {
269   [_textField setText:defaultText];
270   [self textChanged:nil];
271 }
272
273 - (void) setHidden:(BOOL)hidden
274 {
275   [_textField setSecureTextEntry:hidden];
276 }
277
278 - (void) textChanged:(NSNotification*)aNotification; {
279   if (![self.text isEqualToString:_textField.text])
280   {
281     [self.text setString:_textField.text];
282     if (_iosKeyboard)
283     {
284       _iosKeyboard->fireCallback([self.text UTF8String]);
285     }
286   }
287 }
288
289 - (void) setCancelFlag:(bool *)cancelFlag
290 {
291   _canceled = cancelFlag;
292 }
293
294 - (void) dealloc
295 {
296   PRINT_SIGNATURE();
297   self.text = nil;
298   [super dealloc];
299 }
300 @end