1 /*
2 * Copyright 2008, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26 #include "FormPlugin.h"
27
28 #include <stdio.h>
29 #include <sys/time.h>
30 #include <time.h>
31 #include <math.h>
32 #include <string.h>
33
34 extern NPNetscapeFuncs* browser;
35 extern ANPLogInterfaceV0 gLogI;
36 extern ANPCanvasInterfaceV0 gCanvasI;
37 extern ANPPaintInterfaceV0 gPaintI;
38 extern ANPTypefaceInterfaceV0 gTypefaceI;
39 extern ANPWindowInterfaceV0 gWindowI;
40
41
inval(NPP instance)42 static void inval(NPP instance) {
43 browser->invalidaterect(instance, NULL);
44 }
45
rnd16(float x,int inset)46 static uint16_t rnd16(float x, int inset) {
47 int ix = (int)roundf(x) + inset;
48 if (ix < 0) {
49 ix = 0;
50 }
51 return static_cast<uint16_t>(ix);
52 }
53
inval(NPP instance,const ANPRectF & r,bool doAA)54 static void inval(NPP instance, const ANPRectF& r, bool doAA) {
55 const int inset = doAA ? -1 : 0;
56
57 PluginObject *obj = reinterpret_cast<PluginObject*>(instance->pdata);
58 NPRect inval;
59 inval.left = rnd16(r.left, inset);
60 inval.top = rnd16(r.top, inset);
61 inval.right = rnd16(r.right, -inset);
62 inval.bottom = rnd16(r.bottom, -inset);
63 browser->invalidaterect(instance, &inval);
64 }
65
66 ///////////////////////////////////////////////////////////////////////////////
67
FormPlugin(NPP inst)68 FormPlugin::FormPlugin(NPP inst) : SubPlugin(inst) {
69
70 m_hasFocus = false;
71 m_activeInput = NULL;
72
73 memset(&m_usernameInput, 0, sizeof(m_usernameInput));
74 memset(&m_passwordInput, 0, sizeof(m_passwordInput));
75
76 m_usernameInput.text[0] = '\0';
77 m_usernameInput.charPtr = 0;
78
79 m_passwordInput.text[0] = '\0';
80 m_passwordInput.charPtr = 0;
81
82 m_paintInput = gPaintI.newPaint();
83 gPaintI.setFlags(m_paintInput, gPaintI.getFlags(m_paintInput) | kAntiAlias_ANPPaintFlag);
84 gPaintI.setColor(m_paintInput, 0xFFFFFFFF);
85
86 m_paintActive = gPaintI.newPaint();
87 gPaintI.setFlags(m_paintActive, gPaintI.getFlags(m_paintActive) | kAntiAlias_ANPPaintFlag);
88 gPaintI.setColor(m_paintActive, 0xFFFFFF00);
89
90 m_paintText = gPaintI.newPaint();
91 gPaintI.setFlags(m_paintText, gPaintI.getFlags(m_paintText) | kAntiAlias_ANPPaintFlag);
92 gPaintI.setColor(m_paintText, 0xFF000000);
93 gPaintI.setTextSize(m_paintText, 18);
94
95 ANPTypeface* tf = gTypefaceI.createFromName("serif", kItalic_ANPTypefaceStyle);
96 gPaintI.setTypeface(m_paintText, tf);
97 gTypefaceI.unref(tf);
98
99 //register for key and visibleRect events
100 ANPEventFlags flags = kKey_ANPEventFlag;
101 NPError err = browser->setvalue(inst, kAcceptEvents_ANPSetValue, &flags);
102 if (err != NPERR_NO_ERROR) {
103 gLogI.log(kError_ANPLogType, "Error selecting input events.");
104 }
105 }
106
~FormPlugin()107 FormPlugin::~FormPlugin() {
108 gPaintI.deletePaint(m_paintInput);
109 gPaintI.deletePaint(m_paintActive);
110 gPaintI.deletePaint(m_paintText);
111 }
112
supportsDrawingModel(ANPDrawingModel model)113 bool FormPlugin::supportsDrawingModel(ANPDrawingModel model) {
114 return (model == kBitmap_ANPDrawingModel);
115 }
116
drawPlugin(const ANPBitmap & bitmap,const ANPRectI & clip)117 void FormPlugin::drawPlugin(const ANPBitmap& bitmap, const ANPRectI& clip) {
118 ANPCanvas* canvas = gCanvasI.newCanvas(&bitmap);
119
120 ANPRectF clipR;
121 clipR.left = clip.left;
122 clipR.top = clip.top;
123 clipR.right = clip.right;
124 clipR.bottom = clip.bottom;
125 gCanvasI.clipRect(canvas, &clipR);
126
127 draw(canvas);
128 gCanvasI.deleteCanvas(canvas);
129 }
130
draw(ANPCanvas * canvas)131 void FormPlugin::draw(ANPCanvas* canvas) {
132 NPP instance = this->inst();
133 PluginObject *obj = (PluginObject*) instance->pdata;
134
135 const float inputWidth = 60;
136 const float inputHeight = 30;
137 const int W = obj->window->width;
138 const int H = obj->window->height;
139
140 // color the plugin canvas
141 gCanvasI.drawColor(canvas, (m_hasFocus) ? 0xFFCDCDCD : 0xFF545454);
142
143 // draw the username box (5 px from the top edge)
144 m_usernameInput.rect.left = 5;
145 m_usernameInput.rect.top = 5;
146 m_usernameInput.rect.right = W - 5;
147 m_usernameInput.rect.bottom = m_usernameInput.rect.top + inputHeight;
148 gCanvasI.drawRect(canvas, &m_usernameInput.rect, getPaint(&m_usernameInput));
149 drawText(canvas, m_usernameInput);
150
151 // draw the password box (5 px from the bottom edge)
152 m_passwordInput.rect.left = 5;
153 m_passwordInput.rect.top = H - (inputHeight + 5);
154 m_passwordInput.rect.right = W - 5;
155 m_passwordInput.rect.bottom = m_passwordInput.rect.top + inputHeight;
156 gCanvasI.drawRect(canvas, &m_passwordInput.rect, getPaint(&m_passwordInput));
157 drawPassword(canvas, m_passwordInput);
158
159 //invalidate the canvas
160 //inval(instance);
161 }
162
getPaint(TextInput * input)163 ANPPaint* FormPlugin::getPaint(TextInput* input) {
164 return (input == m_activeInput) ? m_paintActive : m_paintInput;
165 }
166
drawText(ANPCanvas * canvas,TextInput textInput)167 void FormPlugin::drawText(ANPCanvas* canvas, TextInput textInput) {
168
169 // get font metrics
170 ANPFontMetrics fontMetrics;
171 gPaintI.getFontMetrics(m_paintText, &fontMetrics);
172
173 gCanvasI.drawText(canvas, textInput.text, textInput.charPtr,
174 textInput.rect.left + 5,
175 textInput.rect.bottom - fontMetrics.fBottom, m_paintText);
176 }
177
drawPassword(ANPCanvas * canvas,TextInput passwordInput)178 void FormPlugin::drawPassword(ANPCanvas* canvas, TextInput passwordInput) {
179
180 // get font metrics
181 ANPFontMetrics fontMetrics;
182 gPaintI.getFontMetrics(m_paintText, &fontMetrics);
183
184 // comput the circle dimensions and initial location
185 float initialX = passwordInput.rect.left + 5;
186 float ovalBottom = passwordInput.rect.bottom - 2;
187 float ovalTop = ovalBottom - (fontMetrics.fBottom - fontMetrics.fTop);
188 float ovalWidth = ovalBottom - ovalTop;
189 float ovalSpacing = 3;
190
191 // draw circles instead of the actual text
192 for (uint32_t x = 0; x < passwordInput.charPtr; x++) {
193 ANPRectF oval;
194 oval.left = initialX + ((ovalWidth + ovalSpacing) * (float) x);
195 oval.right = oval.left + ovalWidth;
196 oval.top = ovalTop;
197 oval.bottom = ovalBottom;
198 gCanvasI.drawOval(canvas, &oval, m_paintText);
199 }
200 }
201
handleEvent(const ANPEvent * evt)202 int16_t FormPlugin::handleEvent(const ANPEvent* evt) {
203 NPP instance = this->inst();
204
205 switch (evt->eventType) {
206 case kDraw_ANPEventType:
207 switch (evt->data.draw.model) {
208 case kBitmap_ANPDrawingModel:
209 drawPlugin(evt->data.draw.data.bitmap, evt->data.draw.clip);
210 return 1;
211 default:
212 break; // unknown drawing model
213 }
214 break;
215
216 case kLifecycle_ANPEventType:
217 if (evt->data.lifecycle.action == kLoseFocus_ANPLifecycleAction) {
218 gLogI.log(kDebug_ANPLogType, "----%p Loosing Focus", instance);
219
220 if (m_activeInput) {
221 // hide the keyboard
222 gWindowI.showKeyboard(instance, false);
223
224 //reset the activeInput
225 m_activeInput = NULL;
226 }
227
228 m_hasFocus = false;
229 inval(instance);
230 return 1;
231 }
232 else if (evt->data.lifecycle.action == kGainFocus_ANPLifecycleAction) {
233 gLogI.log(kDebug_ANPLogType, "----%p Gaining Focus", instance);
234 m_hasFocus = true;
235 inval(instance);
236 return 1;
237 }
238 break;
239
240 case kMouse_ANPEventType: {
241
242 int x = evt->data.mouse.x;
243 int y = evt->data.mouse.y;
244 if (kDown_ANPMouseAction == evt->data.mouse.action) {
245
246 TextInput* currentInput = validTap(x,y);
247
248 if (currentInput)
249 gWindowI.showKeyboard(instance, true);
250 else if (m_activeInput)
251 gWindowI.showKeyboard(instance, false);
252
253 if (currentInput != m_activeInput)
254 switchActiveInput(currentInput);
255
256 return 1;
257 }
258 break;
259 }
260
261 case kKey_ANPEventType:
262 if (evt->data.key.action == kDown_ANPKeyAction) {
263
264 //handle navigation keys
265 if (evt->data.key.nativeCode >= kDpadUp_ANPKeyCode
266 && evt->data.key.nativeCode <= kDpadCenter_ANPKeyCode) {
267 return handleNavigation(evt->data.key.nativeCode) ? 1 : 0;
268 }
269
270 if (m_activeInput) {
271 handleTextInput(m_activeInput, evt->data.key.nativeCode,
272 evt->data.key.unichar);
273 inval(instance, m_activeInput->rect, true);
274 }
275 }
276 return 1;
277
278 default:
279 break;
280 }
281 return 0; // unknown or unhandled event
282 }
283
switchActiveInput(TextInput * newInput)284 void FormPlugin::switchActiveInput(TextInput* newInput) {
285 NPP instance = this->inst();
286
287 if (m_activeInput) {
288 inval(instance, m_activeInput->rect, true); // inval the old
289 gWindowI.clearVisibleRects(instance);
290 }
291
292 m_activeInput = newInput; // set the new active input
293
294 if (m_activeInput) {
295 inval(instance, m_activeInput->rect, true); // inval the new
296 scrollIntoView(m_activeInput);
297 }
298 }
299
handleNavigation(ANPKeyCode keyCode)300 bool FormPlugin::handleNavigation(ANPKeyCode keyCode) {
301 NPP instance = this->inst();
302
303 gLogI.log(kDebug_ANPLogType, "----%p Recvd Nav Key %d", instance, keyCode);
304
305 if (!m_activeInput) {
306 gWindowI.showKeyboard(instance, true);
307 switchActiveInput(&m_usernameInput);
308 }
309 else if (m_activeInput == &m_usernameInput) {
310 if (keyCode == kDpadDown_ANPKeyCode) {
311 switchActiveInput(&m_passwordInput);
312 }
313 else if (keyCode == kDpadCenter_ANPKeyCode)
314 gWindowI.showKeyboard(instance, false);
315 else if (keyCode == kDpadUp_ANPKeyCode)
316 return false;
317 }
318 else if (m_activeInput == &m_passwordInput) {
319 if (keyCode == kDpadUp_ANPKeyCode) {
320 switchActiveInput(&m_usernameInput);
321 }
322 else if (keyCode == kDpadCenter_ANPKeyCode)
323 gWindowI.showKeyboard(instance, false);
324 else if (keyCode == kDpadDown_ANPKeyCode)
325 return false;
326 }
327
328 return true;
329 }
330
handleTextInput(TextInput * input,ANPKeyCode keyCode,int32_t unichar)331 void FormPlugin::handleTextInput(TextInput* input, ANPKeyCode keyCode, int32_t unichar) {
332 NPP instance = this->inst();
333
334 //make sure the input field is in view
335 scrollIntoView(input);
336
337 //handle the delete operation
338 if (keyCode == kDel_ANPKeyCode) {
339 if (input->charPtr > 0) {
340 input->charPtr--;
341 }
342 return;
343 }
344
345 //check to see that the input is not full
346 if (input->charPtr >= (sizeof(input->text) - 1))
347 return;
348
349 //add the character
350 input->text[input->charPtr] = static_cast<char>(unichar);
351 input->charPtr++;
352
353 gLogI.log(kDebug_ANPLogType, "----%p Text: %c", instance, unichar);
354 }
355
scrollIntoView(TextInput * input)356 void FormPlugin::scrollIntoView(TextInput* input) {
357 NPP instance = this->inst();
358 PluginObject *obj = (PluginObject*) instance->pdata;
359 NPWindow *window = obj->window;
360
361 // find the textInput's global rect coordinates
362 ANPRectI visibleRects[1];
363 visibleRects[0].left = input->rect.left;
364 visibleRects[0].top = input->rect.top;
365 visibleRects[0].right = input->rect.right;
366 visibleRects[0].bottom = input->rect.bottom;
367
368 gWindowI.setVisibleRects(instance, visibleRects, 1);
369 }
370
validTap(int x,int y)371 TextInput* FormPlugin::validTap(int x, int y) {
372
373 if (x > m_usernameInput.rect.left && x < m_usernameInput.rect.right &&
374 y > m_usernameInput.rect.top && y < m_usernameInput.rect.bottom)
375 return &m_usernameInput;
376 else if (x >m_passwordInput.rect.left && x < m_passwordInput.rect.right &&
377 y > m_passwordInput.rect.top && y < m_passwordInput.rect.bottom)
378 return &m_passwordInput;
379 else
380 return NULL;
381 }
382