webengine/osswebengine/WebCore/rendering/RenderFileUploadControl.cpp
changeset 0 dd21522fd290
equal deleted inserted replaced
-1:000000000000 0:dd21522fd290
       
     1 /*
       
     2  * Copyright (C) 2006, 2007 Apple Inc. All rights reserved.
       
     3  *
       
     4  * This library is free software; you can redistribute it and/or
       
     5  * modify it under the terms of the GNU Library General Public
       
     6  * License as published by the Free Software Foundation; either
       
     7  * version 2 of the License, or (at your option) any later version.
       
     8  *
       
     9  * This library is distributed in the hope that it will be useful,
       
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    12  * Library General Public License for more details.
       
    13  *
       
    14  * You should have received a copy of the GNU Library General Public License
       
    15  * along with this library; see the file COPYING.LIB.  If not, write to
       
    16  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
       
    17  * Boston, MA 02110-1301, USA.
       
    18  *
       
    19  */
       
    20 
       
    21 #include "config.h"
       
    22 #include "RenderFileUploadControl.h"
       
    23 
       
    24 #include "FrameView.h"
       
    25 #include "GraphicsContext.h"
       
    26 #include "HTMLInputElement.h"
       
    27 #include "HTMLNames.h"
       
    28 #include "Icon.h"
       
    29 #include "LocalizedStrings.h"
       
    30 #include "RenderButton.h"
       
    31 #include "RenderText.h"
       
    32 #include "RenderTheme.h"
       
    33 #include "RenderView.h"
       
    34 #include "TextStyle.h"
       
    35 #include <math.h>
       
    36 
       
    37 using namespace std;
       
    38 
       
    39 namespace WebCore {
       
    40 
       
    41 using namespace HTMLNames;
       
    42 
       
    43 const int afterButtonSpacing = 4;
       
    44 const int iconHeight = 16;
       
    45 const int iconWidth = 16;
       
    46 const int iconFilenameSpacing = 2;
       
    47 const int defaultWidthNumChars = 34;
       
    48 const int buttonShadowHeight = 2;
       
    49 
       
    50 class HTMLFileUploadInnerButtonElement : public HTMLInputElement {
       
    51 public:
       
    52     HTMLFileUploadInnerButtonElement(Document*, Node* shadowParent);
       
    53 
       
    54     virtual bool isShadowNode() const { return true; }
       
    55     virtual Node* shadowParentNode() { return m_shadowParent; }
       
    56 
       
    57 private:
       
    58     Node* m_shadowParent;    
       
    59 };
       
    60 
       
    61 RenderFileUploadControl::RenderFileUploadControl(HTMLInputElement* input)
       
    62     : RenderBlock(input)
       
    63     , m_button(0)
       
    64     , m_fileChooser(FileChooser::create(this, input->value()))
       
    65 {
       
    66 }
       
    67 
       
    68 RenderFileUploadControl::~RenderFileUploadControl()
       
    69 {
       
    70     if (m_button)
       
    71         m_button->detach();
       
    72     m_fileChooser->disconnectClient();
       
    73 }
       
    74 
       
    75 void RenderFileUploadControl::setStyle(RenderStyle* newStyle)
       
    76 {
       
    77     // Force text-align to match the direction
       
    78     if (newStyle->direction() == LTR)
       
    79         newStyle->setTextAlign(LEFT);
       
    80     else
       
    81         newStyle->setTextAlign(RIGHT);
       
    82 
       
    83     RenderBlock::setStyle(newStyle);
       
    84     if (m_button)
       
    85         m_button->renderer()->setStyle(createButtonStyle(newStyle));
       
    86 
       
    87     setReplaced(isInline());
       
    88 }
       
    89 
       
    90 void RenderFileUploadControl::valueChanged()
       
    91 {
       
    92     // onChange may destroy this renderer
       
    93     RefPtr<FileChooser> fileChooser = m_fileChooser;
       
    94 
       
    95     HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(node());
       
    96     inputElement->setValueFromRenderer(fileChooser->filename());
       
    97     inputElement->onChange();
       
    98  
       
    99     // only repaint if it doesn't seem we have been destroyed
       
   100     if (!fileChooser->disconnected())
       
   101         repaint();
       
   102 }
       
   103 
       
   104 void RenderFileUploadControl::click()
       
   105 {
       
   106      m_fileChooser->openFileChooser(node()->document());
       
   107 }
       
   108 
       
   109 void RenderFileUploadControl::updateFromElement()
       
   110 {
       
   111     HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(node());
       
   112 
       
   113     if (!m_button) {
       
   114         m_button = new HTMLFileUploadInnerButtonElement(document(), inputElement);
       
   115         m_button->setInputType("button");
       
   116         m_button->setValue(fileButtonChooseFileLabel());
       
   117         RenderStyle* buttonStyle = createButtonStyle(style());
       
   118         RenderObject* renderer = m_button->createRenderer(renderArena(), buttonStyle);
       
   119         m_button->setRenderer(renderer);
       
   120         renderer->setStyle(buttonStyle);
       
   121         renderer->updateFromElement();
       
   122         m_button->setAttached();
       
   123         m_button->setInDocument(true);
       
   124 
       
   125         addChild(renderer);
       
   126     }
       
   127 
       
   128     m_button->setDisabled(!theme()->isEnabled(this));
       
   129 
       
   130     // This only supports clearing out the filename, but that's OK because for
       
   131     // security reasons that's the only change the DOM is allowed to make.
       
   132     if (inputElement->value().isEmpty() && !m_fileChooser->filename().isEmpty()) {
       
   133         m_fileChooser->clear();
       
   134         repaint();
       
   135     }
       
   136 }
       
   137 
       
   138 int RenderFileUploadControl::maxFilenameWidth() const
       
   139 {
       
   140     return max(0, contentWidth() - m_button->renderer()->width() - afterButtonSpacing
       
   141         - (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0));
       
   142 }
       
   143 
       
   144 RenderStyle* RenderFileUploadControl::createButtonStyle(RenderStyle* parentStyle) const
       
   145 {
       
   146     RenderStyle* style = getPseudoStyle(RenderStyle::FILE_UPLOAD_BUTTON);
       
   147     if (!style) {
       
   148         style = new (renderArena()) RenderStyle;
       
   149         if (parentStyle)
       
   150             style->inheritFrom(parentStyle);
       
   151     }
       
   152 
       
   153     // Button text will wrap on file upload controls with widths smaller than the intrinsic button width
       
   154     // without this setWhiteSpace.
       
   155     style->setWhiteSpace(NOWRAP);
       
   156 
       
   157     return style;
       
   158 }
       
   159 
       
   160 void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, int tx, int ty)
       
   161 {
       
   162     if (style()->visibility() != VISIBLE)
       
   163         return;
       
   164     
       
   165     // Push a clip.
       
   166     if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) {
       
   167         IntRect clipRect(tx + borderLeft(), ty + borderTop(),
       
   168                          width() - borderLeft() - borderRight(), height() - borderBottom() - borderTop() + buttonShadowHeight);
       
   169 #if PLATFORM(SYMBIAN)
       
   170         clipRect = intersection(clipRect, paintInfo.rect);
       
   171 #endif
       
   172         if (clipRect.isEmpty())
       
   173             return;
       
   174         paintInfo.context->save();
       
   175         paintInfo.context->clip(clipRect);
       
   176     }
       
   177 
       
   178     if (paintInfo.phase == PaintPhaseForeground) {
       
   179         const String& displayedFilename = m_fileChooser->basenameForWidth(style()->font(), maxFilenameWidth());        
       
   180         unsigned length = displayedFilename.length();
       
   181         const UChar* string = displayedFilename.characters();
       
   182         TextStyle textStyle(0, 0, 0, style()->direction() == RTL, style()->unicodeBidi() == Override);
       
   183         TextRun textRun(string, length);
       
   184         
       
   185         // Determine where the filename should be placed
       
   186         int contentLeft = tx + borderLeft() + paddingLeft();
       
   187         int buttonAndIconWidth = m_button->renderer()->width() + afterButtonSpacing
       
   188             + (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0);
       
   189         int textX;
       
   190         if (style()->direction() == LTR)
       
   191             textX = contentLeft + buttonAndIconWidth;
       
   192         else
       
   193             textX = contentLeft + contentWidth() - buttonAndIconWidth - style()->font().width(textRun);
       
   194         // We want to match the button's baseline
       
   195         RenderButton* buttonRenderer = static_cast<RenderButton*>(m_button->renderer());
       
   196         int textY = buttonRenderer->absoluteBoundingBoxRect().y()
       
   197             + buttonRenderer->marginTop() + buttonRenderer->borderTop() + buttonRenderer->paddingTop()
       
   198             + buttonRenderer->baselinePosition(true, false);
       
   199 
       
   200         paintInfo.context->setFont(style()->font());
       
   201         paintInfo.context->setFillColor(style()->color());
       
   202         
       
   203         // Draw the filename
       
   204         paintInfo.context->drawBidiText(textRun, IntPoint(textX, textY), textStyle);
       
   205         
       
   206         if (m_fileChooser->icon()) {
       
   207             // Determine where the icon should be placed
       
   208             int iconY = ty + borderTop() + paddingTop() + (contentHeight() - iconHeight) / 2;
       
   209             int iconX;
       
   210             if (style()->direction() == LTR)
       
   211                 iconX = contentLeft + m_button->renderer()->width() + afterButtonSpacing;
       
   212             else
       
   213                 iconX = contentLeft + contentWidth() - m_button->renderer()->width() - afterButtonSpacing - iconWidth;
       
   214 
       
   215             // Draw the file icon
       
   216             m_fileChooser->icon()->paint(paintInfo.context, IntRect(iconX, iconY, iconWidth, iconHeight));
       
   217         }
       
   218     }
       
   219 
       
   220     // Paint the children.
       
   221     RenderBlock::paintObject(paintInfo, tx, ty);
       
   222 
       
   223     // Pop the clip.
       
   224     if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds)
       
   225         paintInfo.context->restore();
       
   226 }
       
   227 
       
   228 void RenderFileUploadControl::calcPrefWidths()
       
   229 {
       
   230     ASSERT(prefWidthsDirty());
       
   231 
       
   232     m_minPrefWidth = 0;
       
   233     m_maxPrefWidth = 0;
       
   234 
       
   235     if (style()->width().isFixed() && style()->width().value() > 0)
       
   236         m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value());
       
   237     else {
       
   238         // Figure out how big the filename space needs to be for a given number of characters
       
   239         // (using "0" as the nominal character).
       
   240         const UChar ch = '0';
       
   241         float charWidth = style()->font().floatWidth(TextRun(&ch, 1), TextStyle(0, 0, 0, false, false, false));
       
   242         m_maxPrefWidth = (int)ceilf(charWidth * defaultWidthNumChars);
       
   243     }
       
   244 
       
   245     if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
       
   246         m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
       
   247         m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
       
   248     } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
       
   249         m_minPrefWidth = 0;
       
   250     else
       
   251         m_minPrefWidth = m_maxPrefWidth;
       
   252 
       
   253     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
       
   254         m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
       
   255         m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
       
   256     }
       
   257 
       
   258     int toAdd = paddingLeft() + paddingRight() + borderLeft() + borderRight();
       
   259     m_minPrefWidth += toAdd;
       
   260     m_maxPrefWidth += toAdd;
       
   261 
       
   262     setPrefWidthsDirty(false);
       
   263 }
       
   264 
       
   265 void RenderFileUploadControl::receiveDroppedFile(const String& filename)
       
   266 {
       
   267     m_fileChooser->chooseFile(filename);
       
   268 }
       
   269 
       
   270 HTMLFileUploadInnerButtonElement::HTMLFileUploadInnerButtonElement(Document* doc, Node* shadowParent)
       
   271     : HTMLInputElement(doc)
       
   272     , m_shadowParent(shadowParent)
       
   273 {
       
   274 }
       
   275 
       
   276 } // namespace WebCore