1/*
2 * Copyright (C) 2009 Apple Inc. All rights reserved.
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 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. 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 APPLE INC. ``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 "config.h"
27
28#if ENABLE(DATAGRID)
29
30#include "RenderDataGrid.h"
31
32#include "CSSStyleSelector.h"
33#include "FocusController.h"
34#include "Frame.h"
35#include "GraphicsContext.h"
36#include "Page.h"
37#include "RenderView.h"
38#include "Scrollbar.h"
39
40using std::min;
41
42namespace WebCore {
43
44static const int cDefaultWidth = 300;
45
46RenderDataGrid::RenderDataGrid(Element* elt)
47    : RenderBlock(elt)
48{
49    if (Page* page = frame()->page()) {
50        m_page = page;
51        m_page->addScrollableArea(this);
52    }
53}
54
55RenderDataGrid::~RenderDataGrid()
56{
57    if (m_page)
58        m_page->removeScrollableArea(this);
59}
60
61void RenderDataGrid::styleDidChange(StyleDifference diff, const RenderStyle* oldStyle)
62{
63    RenderBlock::styleDidChange(diff, oldStyle);
64    recalcStyleForColumns();
65}
66
67void RenderDataGrid::recalcStyleForColumns()
68{
69    DataGridColumnList* columns = gridElement()->columns();
70    unsigned length = columns->length();
71    for (unsigned i = 0; i < length; ++i)
72        recalcStyleForColumn(columns->item(i));
73}
74
75void RenderDataGrid::recalcStyleForColumn(DataGridColumn* column)
76{
77    if (!column->columnStyle())
78        column->setColumnStyle(document()->styleSelector()->pseudoStyleForDataGridColumn(column, style()));
79    if (!column->headerStyle())
80        column->setHeaderStyle(document()->styleSelector()->pseudoStyleForDataGridColumnHeader(column, style()));
81}
82
83RenderStyle* RenderDataGrid::columnStyle(DataGridColumn* column)
84{
85    if (!column->columnStyle())
86        recalcStyleForColumn(column);
87    return column->columnStyle();
88}
89
90RenderStyle* RenderDataGrid::headerStyle(DataGridColumn* column)
91{
92    if (!column->headerStyle())
93        recalcStyleForColumn(column);
94    return column->headerStyle();
95}
96
97void RenderDataGrid::computePreferredLogicalWidths()
98{
99    m_minPreferredLogicalWidth = 0;
100    m_maxPreferredLogicalWidth = 0;
101
102    if (style()->width().isFixed() && style()->width().value() > 0)
103        m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(style()->width().value());
104    else
105        m_maxPreferredLogicalWidth = computeContentBoxLogicalWidth(cDefaultWidth);
106
107    if (style()->minWidth().isFixed() && style()->minWidth().value() > 0) {
108        m_maxPreferredLogicalWidth = max(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
109        m_minPreferredLogicalWidth = max(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->minWidth().value()));
110    } else if (style()->width().isPercent() || (style()->width().isAuto() && style()->height().isPercent()))
111        m_minPreferredLogicalWidth = 0;
112    else
113        m_minPreferredLogicalWidth = m_maxPreferredLogicalWidth;
114
115    if (style()->maxWidth().isFixed() && style()->maxWidth().value() != undefinedLength) {
116        m_maxPreferredLogicalWidth = min(m_maxPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
117        m_minPreferredLogicalWidth = min(m_minPreferredLogicalWidth, computeContentBoxLogicalWidth(style()->maxWidth().value()));
118    }
119
120    int toAdd = borderAndPaddingWidth();
121    m_minPreferredLogicalWidth += toAdd;
122    m_maxPreferredLogicalWidth += toAdd;
123
124    setPreferredLogicalWidthsDirty(false);
125}
126
127void RenderDataGrid::layout()
128{
129    RenderBlock::layout();
130    layoutColumns();
131}
132
133void RenderDataGrid::layoutColumns()
134{
135    // FIXME: Implement.
136}
137
138void RenderDataGrid::paintObject(PaintInfo& paintInfo, int tx, int ty)
139{
140    if (style()->visibility() != VISIBLE)
141        return;
142
143    // Paint our background and border.
144    RenderBlock::paintObject(paintInfo, tx, ty);
145
146    if (paintInfo.phase != PaintPhaseForeground)
147        return;
148
149    // Paint our column headers first.
150    paintColumnHeaders(paintInfo, tx, ty);
151}
152
153void RenderDataGrid::paintColumnHeaders(PaintInfo& paintInfo, int tx, int ty)
154{
155    DataGridColumnList* columns = gridElement()->columns();
156    unsigned length = columns->length();
157    for (unsigned i = 0; i < length; ++i) {
158        DataGridColumn* column = columns->item(i);
159        RenderStyle* columnStyle = headerStyle(column);
160
161        // Don't render invisible columns.
162        if (!columnStyle || columnStyle->display() == NONE || columnStyle->visibility() != VISIBLE)
163            continue;
164
165        // Paint the column header if it intersects the dirty rect.
166        IntRect columnRect(column->rect());
167        columnRect.move(tx, ty);
168        if (columnRect.intersects(paintInfo.rect))
169            paintColumnHeader(column, paintInfo, tx, ty);
170    }
171}
172
173void RenderDataGrid::paintColumnHeader(DataGridColumn*, PaintInfo&, int, int)
174{
175    // FIXME: Implement.
176}
177
178// Scrolling implementation functions
179int RenderDataGrid::scrollSize(ScrollbarOrientation orientation) const
180{
181    return ((orientation == VerticallScrollbar) && m_vBar) ? (m_vBar->totalSize() - m_vBar->visibleSize()) : 0;
182}
183
184void RenderDataGrid::setScrollOffsetFromAnimation(const IntPoint& offset)
185{
186    if (m_vBar)
187        m_vBar->setValue(offset.y(), Scrollbar::FromScrollAnimator);
188}
189
190void RenderDataGrid::valueChanged(Scrollbar*)
191{
192    // FIXME: Implement.
193}
194
195void RenderDataGrid::invalidateScrollbarRect(Scrollbar*, const IntRect&)
196{
197    // FIXME: Implement.
198}
199
200bool RenderDataGrid::isActive() const
201{
202    Page* page = frame()->page();
203    return page && page->focusController()->isActive();
204}
205
206
207IntRect RenderDataGrid::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntRect& scrollbarRect) const
208{
209    RenderView* view = this->view();
210    if (!view)
211        return scrollbarRect;
212
213    IntRect rect = scrollbarRect;
214
215    int scrollbarLeft = width() - borderRight() - scrollbar->width();
216    int scrollbarTop = borderTop();
217    rect.move(scrollbarLeft, scrollbarTop);
218
219    return view->frameView()->convertFromRenderer(this, rect);
220}
221
222IntRect RenderDataGrid::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntRect& parentRect) const
223{
224    RenderView* view = this->view();
225    if (!view)
226        return parentRect;
227
228    IntRect rect = view->frameView()->convertToRenderer(this, parentRect);
229
230    int scrollbarLeft = width() - borderRight() - scrollbar->width();
231    int scrollbarTop = borderTop();
232    rect.move(-scrollbarLeft, -scrollbarTop);
233    return rect;
234}
235
236IntPoint RenderDataGrid::convertFromScrollbarToContainingView(const Scrollbar* scrollbar, const IntPoint& scrollbarPoint) const
237{
238    RenderView* view = this->view();
239    if (!view)
240        return scrollbarPoint;
241
242    IntPoint point = scrollbarPoint;
243
244    int scrollbarLeft = width() - borderRight() - scrollbar->width();
245    int scrollbarTop = borderTop();
246    point.move(scrollbarLeft, scrollbarTop);
247
248    return view->frameView()->convertFromRenderer(this, point);
249}
250
251IntPoint RenderDataGrid::convertFromContainingViewToScrollbar(const Scrollbar* scrollbar, const IntPoint& parentPoint) const
252{
253    RenderView* view = this->view();
254    if (!view)
255        return parentPoint;
256
257    IntPoint point = view->frameView()->convertToRenderer(this, parentPoint);
258
259    int scrollbarLeft = width() - borderRight() - scrollbar->width();
260    int scrollbarTop = borderTop();
261    point.move(-scrollbarLeft, -scrollbarTop);
262    return point;
263}
264
265bool RenderDataGrid::shouldSuspendScrollAnimations() const
266{
267    RenderView* view = this->view();
268    if (!view)
269        return true;
270    return view->frameView()->shouldSuspendScrollAnimations();
271}
272
273}
274
275#endif
276