Home | History | Annotate | Download | only in form
      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 
     42 static void inval(NPP instance) {
     43     browser->invalidaterect(instance, NULL);
     44 }
     45 
     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 
     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 
     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 
    107 FormPlugin::~FormPlugin() {
    108     gPaintI.deletePaint(m_paintInput);
    109     gPaintI.deletePaint(m_paintActive);
    110     gPaintI.deletePaint(m_paintText);
    111 }
    112 
    113 bool FormPlugin::supportsDrawingModel(ANPDrawingModel model) {
    114     return (model == kBitmap_ANPDrawingModel);
    115 }
    116 
    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 
    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 
    163 ANPPaint* FormPlugin::getPaint(TextInput* input) {
    164     return (input == m_activeInput) ? m_paintActive : m_paintInput;
    165 }
    166 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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