WebCore/rendering/RenderFileUploadControl.cpp
changeset 0 4f2f89ce4247
equal deleted inserted replaced
-1:000000000000 0:4f2f89ce4247
       
     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 "Chrome.h"
       
    25 #include "FileList.h"
       
    26 #include "Frame.h"
       
    27 #include "FrameView.h"
       
    28 #include "GraphicsContext.h"
       
    29 #include "HTMLInputElement.h"
       
    30 #include "HTMLNames.h"
       
    31 #include "ShadowElement.h"
       
    32 #include "Icon.h"
       
    33 #include "LocalizedStrings.h"
       
    34 #include "Page.h"
       
    35 #include "RenderButton.h"
       
    36 #include "RenderText.h"
       
    37 #include "RenderTheme.h"
       
    38 #include "RenderView.h"
       
    39 #include <math.h>
       
    40 
       
    41 using namespace std;
       
    42 
       
    43 namespace WebCore {
       
    44 
       
    45 using namespace HTMLNames;
       
    46 
       
    47 const int afterButtonSpacing = 4;
       
    48 const int iconHeight = 16;
       
    49 const int iconWidth = 16;
       
    50 const int iconFilenameSpacing = 2;
       
    51 const int defaultWidthNumChars = 34;
       
    52 const int buttonShadowHeight = 2;
       
    53 
       
    54 RenderFileUploadControl::RenderFileUploadControl(HTMLInputElement* input)
       
    55     : RenderBlock(input)
       
    56 {
       
    57     FileList* list = input->files();
       
    58     Vector<String> filenames;
       
    59     unsigned length = list ? list->length() : 0;
       
    60     for (unsigned i = 0; i < length; ++i)
       
    61         filenames.append(list->item(i)->path());
       
    62     m_fileChooser = FileChooser::create(this, filenames);
       
    63 }
       
    64 
       
    65 RenderFileUploadControl::~RenderFileUploadControl()
       
    66 {
       
    67     if (m_button)
       
    68         m_button->detach();
       
    69     m_fileChooser->disconnectClient();
       
    70 }
       
    71 
       
    72 void RenderFileUploadControl::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
       
    73 {
       
    74     RenderBlock::styleDidChange(diff, oldStyle);
       
    75     if (m_button)
       
    76         m_button->renderer()->setStyle(createButtonStyle(style()));
       
    77 
       
    78     setReplaced(isInline());
       
    79 }
       
    80 
       
    81 void RenderFileUploadControl::valueChanged()
       
    82 {
       
    83     // dispatchFormControlChangeEvent may destroy this renderer
       
    84     RefPtr<FileChooser> fileChooser = m_fileChooser;
       
    85 
       
    86     HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(node());
       
    87     inputElement->setFileListFromRenderer(fileChooser->filenames());
       
    88     inputElement->dispatchFormControlChangeEvent();
       
    89  
       
    90     // only repaint if it doesn't seem we have been destroyed
       
    91     if (!fileChooser->disconnected())
       
    92         repaint();
       
    93 }
       
    94 
       
    95 bool RenderFileUploadControl::allowsMultipleFiles()
       
    96 {
       
    97 #if ENABLE(DIRECTORY_UPLOAD)
       
    98     if (allowsDirectoryUpload())
       
    99       return true;
       
   100 #endif
       
   101 
       
   102     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
       
   103     return !input->getAttribute(multipleAttr).isNull();
       
   104 }
       
   105 
       
   106 #if ENABLE(DIRECTORY_UPLOAD)
       
   107 bool RenderFileUploadControl::allowsDirectoryUpload()
       
   108 {
       
   109     HTMLInputElement* input = static_cast<HTMLInputElement*>(node());
       
   110     return !input->getAttribute(webkitdirectoryAttr).isNull();
       
   111 }
       
   112 #endif
       
   113 
       
   114 String RenderFileUploadControl::acceptTypes()
       
   115 {
       
   116     return static_cast<HTMLInputElement*>(node())->accept();
       
   117 }
       
   118 
       
   119 void RenderFileUploadControl::chooseIconForFiles(FileChooser* chooser, const Vector<String>& filenames)
       
   120 {
       
   121     if (Chrome* chromePointer = chrome())
       
   122         chromePointer->chooseIconForFiles(filenames, chooser);
       
   123 }
       
   124 
       
   125 void RenderFileUploadControl::click()
       
   126 {
       
   127     if (Chrome* chromePointer = chrome())
       
   128         chromePointer->runOpenPanel(node()->document()->frame(), m_fileChooser);
       
   129 }
       
   130 
       
   131 Chrome* RenderFileUploadControl::chrome() const
       
   132 {
       
   133     Frame* frame = node()->document()->frame();
       
   134     if (!frame)
       
   135         return 0;
       
   136     Page* page = frame->page();
       
   137     if (!page)
       
   138         return 0;
       
   139     return page->chrome();
       
   140 }
       
   141 
       
   142 void RenderFileUploadControl::updateFromElement()
       
   143 {
       
   144     HTMLInputElement* inputElement = static_cast<HTMLInputElement*>(node());
       
   145     ASSERT(inputElement->inputType() == HTMLInputElement::FILE);
       
   146     
       
   147     if (!m_button) {
       
   148         m_button = ShadowInputElement::create(inputElement);
       
   149         m_button->setInputType("button");
       
   150         m_button->setValue(fileButtonChooseFileLabel());
       
   151         RefPtr<RenderStyle> buttonStyle = createButtonStyle(style());
       
   152         RenderObject* renderer = m_button->createRenderer(renderArena(), buttonStyle.get());
       
   153         m_button->setRenderer(renderer);
       
   154         renderer->setStyle(buttonStyle.release());
       
   155         renderer->updateFromElement();
       
   156         m_button->setAttached();
       
   157         m_button->setInDocument();
       
   158 
       
   159         addChild(renderer);
       
   160     }
       
   161 
       
   162     m_button->setDisabled(!theme()->isEnabled(this));
       
   163 
       
   164     // This only supports clearing out the files, but that's OK because for
       
   165     // security reasons that's the only change the DOM is allowed to make.
       
   166     FileList* files = inputElement->files();
       
   167     ASSERT(files);
       
   168     if (files && files->isEmpty() && !m_fileChooser->filenames().isEmpty()) {
       
   169         m_fileChooser->clear();
       
   170         repaint();
       
   171     }
       
   172 }
       
   173 
       
   174 int RenderFileUploadControl::maxFilenameWidth() const
       
   175 {
       
   176     return max(0, contentWidth() - m_button->renderBox()->width() - afterButtonSpacing
       
   177         - (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0));
       
   178 }
       
   179 
       
   180 PassRefPtr<RenderStyle> RenderFileUploadControl::createButtonStyle(const RenderStyle* parentStyle) const
       
   181 {
       
   182     RefPtr<RenderStyle> style = getCachedPseudoStyle(FILE_UPLOAD_BUTTON);
       
   183     if (!style) {
       
   184         style = RenderStyle::create();
       
   185         if (parentStyle)
       
   186             style->inheritFrom(parentStyle);
       
   187     }
       
   188 
       
   189     // Button text will wrap on file upload controls with widths smaller than the intrinsic button width
       
   190     // without this setWhiteSpace.
       
   191     style->setWhiteSpace(NOWRAP);
       
   192 
       
   193     return style.release();
       
   194 }
       
   195 
       
   196 void RenderFileUploadControl::paintObject(PaintInfo& paintInfo, int tx, int ty)
       
   197 {
       
   198     if (style()->visibility() != VISIBLE)
       
   199         return;
       
   200     ASSERT(m_fileChooser);
       
   201     
       
   202     // Push a clip.
       
   203     if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds) {
       
   204         IntRect clipRect(tx + borderLeft(), ty + borderTop(),
       
   205                          width() - borderLeft() - borderRight(), height() - borderBottom() - borderTop() + buttonShadowHeight);
       
   206         if (clipRect.isEmpty())
       
   207             return;
       
   208         paintInfo.context->save();
       
   209         paintInfo.context->clip(clipRect);
       
   210     }
       
   211 
       
   212     if (paintInfo.phase == PaintPhaseForeground) {
       
   213         const String& displayedFilename = fileTextValue();
       
   214         unsigned length = displayedFilename.length();
       
   215         const UChar* string = displayedFilename.characters();
       
   216         TextRun textRun(string, length, false, 0, 0, style()->direction() == RTL, style()->unicodeBidi() == Override);
       
   217         
       
   218         // Determine where the filename should be placed
       
   219         int contentLeft = tx + borderLeft() + paddingLeft();
       
   220         int buttonAndIconWidth = m_button->renderBox()->width() + afterButtonSpacing
       
   221             + (m_fileChooser->icon() ? iconWidth + iconFilenameSpacing : 0);
       
   222         int textX;
       
   223         if (style()->direction() == LTR)
       
   224             textX = contentLeft + buttonAndIconWidth;
       
   225         else
       
   226             textX = contentLeft + contentWidth() - buttonAndIconWidth - style()->font().width(textRun);
       
   227         // We want to match the button's baseline
       
   228         RenderButton* buttonRenderer = toRenderButton(m_button->renderer());
       
   229         int textY = buttonRenderer->absoluteBoundingBoxRect().y()
       
   230             + buttonRenderer->marginTop() + buttonRenderer->borderTop() + buttonRenderer->paddingTop()
       
   231             + buttonRenderer->baselinePosition(true, false);
       
   232 
       
   233         paintInfo.context->setFillColor(style()->visitedDependentColor(CSSPropertyColor), style()->colorSpace());
       
   234         
       
   235         // Draw the filename
       
   236         paintInfo.context->drawBidiText(style()->font(), textRun, IntPoint(textX, textY));
       
   237         
       
   238         if (m_fileChooser->icon()) {
       
   239             // Determine where the icon should be placed
       
   240             int iconY = ty + borderTop() + paddingTop() + (contentHeight() - iconHeight) / 2;
       
   241             int iconX;
       
   242             if (style()->direction() == LTR)
       
   243                 iconX = contentLeft + m_button->renderBox()->width() + afterButtonSpacing;
       
   244             else
       
   245                 iconX = contentLeft + contentWidth() - m_button->renderBox()->width() - afterButtonSpacing - iconWidth;
       
   246 
       
   247             // Draw the file icon
       
   248             m_fileChooser->icon()->paint(paintInfo.context, IntRect(iconX, iconY, iconWidth, iconHeight));
       
   249         }
       
   250     }
       
   251 
       
   252     // Paint the children.
       
   253     RenderBlock::paintObject(paintInfo, tx, ty);
       
   254 
       
   255     // Pop the clip.
       
   256     if (paintInfo.phase == PaintPhaseForeground || paintInfo.phase == PaintPhaseChildBlockBackgrounds)
       
   257         paintInfo.context->restore();
       
   258 }
       
   259 
       
   260 void RenderFileUploadControl::calcPrefWidths()
       
   261 {
       
   262     ASSERT(prefWidthsDirty());
       
   263 
       
   264     m_minPrefWidth = 0;
       
   265     m_maxPrefWidth = 0;
       
   266 
       
   267     if (style()->width().isFixed() && style()->width().value() > 0)
       
   268         m_minPrefWidth = m_maxPrefWidth = calcContentBoxWidth(style()->width().value());
       
   269     else {
       
   270         // Figure out how big the filename space needs to be for a given number of characters
       
   271         // (using "0" as the nominal character).
       
   272         const UChar ch = '0';
       
   273         float charWidth = style()->font().floatWidth(TextRun(&ch, 1, false, 0, 0, false, false, false));
       
   274         m_maxPrefWidth = (int)ceilf(charWidth * defaultWidthNumChars);
       
   275     }
       
   276 
       
   277     if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
       
   278         m_maxPrefWidth = max(m_maxPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
       
   279         m_minPrefWidth = max(m_minPrefWidth, calcContentBoxWidth(style()->minWidth().value()));
       
   280     } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
       
   281         m_minPrefWidth = 0;
       
   282     else
       
   283         m_minPrefWidth = m_maxPrefWidth;
       
   284 
       
   285     if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
       
   286         m_maxPrefWidth = min(m_maxPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
       
   287         m_minPrefWidth = min(m_minPrefWidth, calcContentBoxWidth(style()->maxWidth().value()));
       
   288     }
       
   289 
       
   290     int toAdd = borderAndPaddingWidth();
       
   291     m_minPrefWidth += toAdd;
       
   292     m_maxPrefWidth += toAdd;
       
   293 
       
   294     setPrefWidthsDirty(false);
       
   295 }
       
   296 
       
   297 void RenderFileUploadControl::receiveDroppedFiles(const Vector<String>& paths)
       
   298 {
       
   299     if (allowsMultipleFiles())
       
   300         m_fileChooser->chooseFiles(paths);
       
   301     else
       
   302         m_fileChooser->chooseFile(paths[0]);
       
   303 }
       
   304 
       
   305 String RenderFileUploadControl::buttonValue()
       
   306 {
       
   307     if (!m_button)
       
   308         return String();
       
   309     
       
   310     return m_button->value();
       
   311 }
       
   312 
       
   313 String RenderFileUploadControl::fileTextValue() const
       
   314 {
       
   315     return m_fileChooser->basenameForWidth(style()->font(), maxFilenameWidth());
       
   316 }
       
   317     
       
   318 } // namespace WebCore