|
@@ -15,41 +15,28 @@ |
15 |
15 |
|
*/ |
16 |
16 |
|
package com.google.gwt.gen2.table.client; |
17 |
17 |
|
|
18 |
|
- |
import com.google.gwt.core.client.GWT; |
19 |
|
- |
import com.google.gwt.gen2.event.dom.client.HasScrollHandlers; |
20 |
|
- |
import com.google.gwt.gen2.event.dom.client.ScrollEvent; |
21 |
|
- |
import com.google.gwt.gen2.event.dom.client.ScrollHandler; |
22 |
|
- |
import com.google.gwt.gen2.event.shared.HandlerRegistration; |
23 |
|
- |
import com.google.gwt.gen2.table.client.ColumnResizer.ColumnWidthInfo; |
24 |
|
- |
import com.google.gwt.gen2.table.client.TableModelHelper.ColumnSortList; |
25 |
|
- |
import com.google.gwt.gen2.table.client.property.MaximumWidthProperty; |
26 |
|
- |
import com.google.gwt.gen2.table.event.client.ColumnSortEvent; |
27 |
|
- |
import com.google.gwt.gen2.table.event.client.ColumnSortHandler; |
28 |
|
- |
import com.google.gwt.gen2.table.client.HTMLTable.CellFormatter; |
29 |
|
- |
import com.google.gwt.i18n.client.LocaleInfo; |
30 |
|
- |
import com.google.gwt.user.client.Command; |
31 |
|
- |
import com.google.gwt.user.client.DOM; |
32 |
|
- |
import com.google.gwt.user.client.Element; |
33 |
|
- |
import com.google.gwt.user.client.Event; |
34 |
|
- |
import com.google.gwt.user.client.Timer; |
35 |
|
- |
import com.google.gwt.user.client.Window; |
36 |
|
- |
import com.google.gwt.user.client.ui.AbstractImagePrototype; |
37 |
|
- |
import com.google.gwt.user.client.ui.Image; |
38 |
|
- |
import com.google.gwt.user.client.ui.ImageBundle; |
39 |
|
- |
import com.google.gwt.user.client.ui.RootPanel; |
40 |
|
- |
import com.google.gwt.user.client.ui.Widget; |
41 |
|
- |
import com.google.gwt.widgetideas.client.ResizableWidget; |
42 |
|
- |
import com.google.gwt.widgetideas.client.ResizableWidgetCollection; |
|
18 |
+ |
import java.util.*; |
43 |
19 |
|
|
44 |
|
- |
import java.util.ArrayList; |
45 |
|
- |
import java.util.List; |
|
20 |
+ |
import com.google.gwt.core.client.*; |
|
21 |
+ |
import com.google.gwt.gen2.event.dom.client.*; |
|
22 |
+ |
import com.google.gwt.gen2.event.shared.*; |
|
23 |
+ |
import com.google.gwt.gen2.table.client.ColumnResizer.*; |
|
24 |
+ |
import com.google.gwt.gen2.table.client.HTMLTable.*; |
|
25 |
+ |
import com.google.gwt.gen2.table.client.TableModelHelper.*; |
|
26 |
+ |
import com.google.gwt.gen2.table.client.property.*; |
|
27 |
+ |
import com.google.gwt.gen2.table.event.client.*; |
|
28 |
+ |
import com.google.gwt.i18n.client.*; |
|
29 |
+ |
import com.google.gwt.user.client.*; |
|
30 |
+ |
import com.google.gwt.user.client.Timer; |
|
31 |
+ |
import com.google.gwt.user.client.ui.*; |
|
32 |
+ |
import com.google.gwt.widgetideas.client.*; |
46 |
33 |
|
|
47 |
34 |
|
/** |
48 |
35 |
|
* <p> |
49 |
36 |
|
* A ScrollTable consists of a fixed header and footer (optional) that remain |
50 |
37 |
|
* visible and a scrollable body that contains the data. |
51 |
38 |
|
* </p> |
52 |
|
- |
* |
|
39 |
+ |
* <p/> |
53 |
40 |
|
* <p> |
54 |
41 |
|
* In order for the columns in the header table and data table to line up, the |
55 |
42 |
|
* two table must have the same margin, padding, and border widths. You can use |
|
@@ -57,7 +44,7 @@ |
57 |
44 |
|
* must keep the actual sizes consistent (especially with respect to the left |
58 |
45 |
|
* and right side of the cells). |
59 |
46 |
|
* </p> |
60 |
|
- |
* |
|
47 |
+ |
* <p/> |
61 |
48 |
|
* <p> |
62 |
49 |
|
* NOTE: AbstractScrollTable does not resize correctly in older versions of |
63 |
50 |
|
* Mozilla (specifically, Linux hosted mode). In use, the PagingScrollTable will |
|
@@ -66,9 +53,9 @@ |
66 |
53 |
|
* in percentages) on all modern browsers including IE6+, FF2+, Safari2+, |
67 |
54 |
|
* Chrome, Opera 9.6. |
68 |
55 |
|
* </p> |
69 |
|
- |
* |
|
56 |
+ |
* <p/> |
70 |
57 |
|
* <h3>CSS Style Rules</h3> |
71 |
|
- |
* |
|
58 |
+ |
* <p/> |
72 |
59 |
|
* <dl> |
73 |
60 |
|
* <dt>.gwt-ScrollTable</dt> |
74 |
61 |
|
* <dd>applied to the entire widget</dd> |
|
@@ -86,2039 +73,2290 @@ |
86 |
73 |
|
* <dd>wrapper around the footer table</dd> |
87 |
74 |
|
* </dl> |
88 |
75 |
|
*/ |
89 |
|
- |
public abstract class AbstractScrollTable extends Gen2TableComplexPanel implements |
90 |
|
- |
ResizableWidget, HasScrollHandlers { |
91 |
|
- |
/** |
92 |
|
- |
* Browser specific implementation class for {@link AbstractScrollTable}. |
93 |
|
- |
*/ |
94 |
|
- |
private static class Impl { |
95 |
|
- |
/** |
96 |
|
- |
* Create a spacer element that allows the header table to scroll over the |
97 |
|
- |
* vertical scroll bar of the data table. |
98 |
|
- |
* |
99 |
|
- |
* @param wrapper the wrapper that contains the header table |
100 |
|
- |
* @return the spacer element |
101 |
|
- |
*/ |
102 |
|
- |
public Element createSpacer(FixedWidthFlexTable table, Element wrapper) { |
103 |
|
- |
resizeSpacer(table, null, 15); |
104 |
|
- |
return null; |
|
76 |
+ |
public abstract class AbstractScrollTable extends Gen2TableComplexPanel implements ResizableWidget, |
|
77 |
+ |
HasScrollHandlers |
|
78 |
+ |
{ |
|
79 |
+ |
/** |
|
80 |
+ |
* Browser specific implementation class for {@link AbstractScrollTable}. |
|
81 |
+ |
*/ |
|
82 |
+ |
private static class Impl |
|
83 |
+ |
{ |
|
84 |
+ |
/** |
|
85 |
+ |
* Create a spacer element that allows the header table to scroll over the |
|
86 |
+ |
* vertical scroll bar of the data table. |
|
87 |
+ |
* |
|
88 |
+ |
* @param wrapper the wrapper that contains the header table |
|
89 |
+ |
* |
|
90 |
+ |
* @return the spacer element |
|
91 |
+ |
*/ |
|
92 |
+ |
public Element createSpacer( FixedWidthFlexTable table, Element wrapper ) |
|
93 |
+ |
{ |
|
94 |
+ |
resizeSpacer( table, null, 15 ); |
|
95 |
+ |
return null; |
|
96 |
+ |
} |
|
97 |
+ |
|
|
98 |
+ |
/** |
|
99 |
+ |
* Returns the width of a table, minus any padding, in pixels. |
|
100 |
+ |
* |
|
101 |
+ |
* @param table the table |
|
102 |
+ |
* @param includeSpacer true to include the spacer width |
|
103 |
+ |
* |
|
104 |
+ |
* @return the width |
|
105 |
+ |
*/ |
|
106 |
+ |
public int getTableWidth( FixedWidthFlexTable table, boolean includeSpacer ) |
|
107 |
+ |
{ |
|
108 |
+ |
int scrollWidth = table.getElement().getScrollWidth(); |
|
109 |
+ |
if ( !includeSpacer ) |
|
110 |
+ |
{ |
|
111 |
+ |
int spacerWidth = getSpacerWidth( table ); |
|
112 |
+ |
if ( spacerWidth > 0 ) |
|
113 |
+ |
{ |
|
114 |
+ |
scrollWidth -= spacerWidth; |
|
115 |
+ |
} |
|
116 |
+ |
} |
|
117 |
+ |
return scrollWidth; |
|
118 |
+ |
} |
|
119 |
+ |
|
|
120 |
+ |
/** |
|
121 |
+ |
* Recalculate the ideal widths of columns. |
|
122 |
+ |
* |
|
123 |
+ |
* @param scrollTable the scroll table |
|
124 |
+ |
* @param command an optional command to execute while recalculating |
|
125 |
+ |
*/ |
|
126 |
+ |
public void recalculateIdealColumnWidths( AbstractScrollTable scrollTable, Command command ) |
|
127 |
+ |
{ |
|
128 |
+ |
FixedWidthFlexTable headerTable = scrollTable.getHeaderTable(); |
|
129 |
+ |
FixedWidthFlexTable footerTable = scrollTable.getFooterTable(); |
|
130 |
+ |
FixedWidthGrid dataTable = scrollTable.getDataTable(); |
|
131 |
+ |
|
|
132 |
+ |
// Setup all inner tables |
|
133 |
+ |
dataTable.recalculateIdealColumnWidthsSetup(); |
|
134 |
+ |
headerTable.recalculateIdealColumnWidthsSetup(); |
|
135 |
+ |
if ( footerTable != null ) |
|
136 |
+ |
{ |
|
137 |
+ |
footerTable.recalculateIdealColumnWidthsSetup(); |
|
138 |
+ |
} |
|
139 |
+ |
|
|
140 |
+ |
// Perform operations |
|
141 |
+ |
dataTable.recalculateIdealColumnWidthsImpl(); |
|
142 |
+ |
headerTable.recalculateIdealColumnWidthsImpl(); |
|
143 |
+ |
if ( footerTable != null ) |
|
144 |
+ |
{ |
|
145 |
+ |
footerTable.recalculateIdealColumnWidthsImpl(); |
|
146 |
+ |
} |
|
147 |
+ |
|
|
148 |
+ |
// Execute the optional command |
|
149 |
+ |
if ( command != null ) |
|
150 |
+ |
{ |
|
151 |
+ |
command.execute(); |
|
152 |
+ |
} |
|
153 |
+ |
|
|
154 |
+ |
// Teardown all inner tables |
|
155 |
+ |
dataTable.recalculateIdealColumnWidthsTeardown(); |
|
156 |
+ |
headerTable.recalculateIdealColumnWidthsTeardown(); |
|
157 |
+ |
if ( footerTable != null ) |
|
158 |
+ |
{ |
|
159 |
+ |
footerTable.recalculateIdealColumnWidthsTeardown(); |
|
160 |
+ |
} |
|
161 |
+ |
} |
|
162 |
+ |
|
|
163 |
+ |
/** |
|
164 |
+ |
* Reposition the header spacer as needed. |
|
165 |
+ |
* |
|
166 |
+ |
* @param scrollTable the scroll table |
|
167 |
+ |
* @param force if true, ignore the scroll policy |
|
168 |
+ |
*/ |
|
169 |
+ |
public void repositionSpacer( AbstractScrollTable scrollTable, boolean force ) |
|
170 |
+ |
{ |
|
171 |
+ |
// Only ScrollPolicy.BOTH has a vertical scroll bar |
|
172 |
+ |
if ( !force && scrollTable.scrollPolicy != ScrollPolicy.BOTH ) |
|
173 |
+ |
{ |
|
174 |
+ |
return; |
|
175 |
+ |
} |
|
176 |
+ |
|
|
177 |
+ |
Element dataWrapper = scrollTable.dataWrapper; |
|
178 |
+ |
int spacerWidth = dataWrapper.getOffsetWidth() - dataWrapper.getPropertyInt( "clientWidth" ); |
|
179 |
+ |
resizeSpacer( scrollTable.headerTable, scrollTable.headerSpacer, spacerWidth ); |
|
180 |
+ |
if ( scrollTable.footerTable != null ) |
|
181 |
+ |
{ |
|
182 |
+ |
resizeSpacer( scrollTable.footerTable, scrollTable.footerSpacer, spacerWidth ); |
|
183 |
+ |
} |
|
184 |
+ |
} |
|
185 |
+ |
|
|
186 |
+ |
/** |
|
187 |
+ |
* @return true if the scroll bar is on the right |
|
188 |
+ |
*/ |
|
189 |
+ |
boolean isScrollBarOnRight() |
|
190 |
+ |
{ |
|
191 |
+ |
return true; |
|
192 |
+ |
} |
|
193 |
+ |
|
|
194 |
+ |
void resizeSpacer( FixedWidthFlexTable table, Element spacer, int spacerWidth ) |
|
195 |
+ |
{ |
|
196 |
+ |
// Exit early if the spacer is already the correct size |
|
197 |
+ |
if ( spacerWidth == getSpacerWidth( table ) ) |
|
198 |
+ |
{ |
|
199 |
+ |
return; |
|
200 |
+ |
} |
|
201 |
+ |
|
|
202 |
+ |
if ( isScrollBarOnRight() ) |
|
203 |
+ |
{ |
|
204 |
+ |
table.getElement().getStyle().setPropertyPx( "paddingRight", spacerWidth ); |
|
205 |
+ |
} |
|
206 |
+ |
else |
|
207 |
+ |
{ |
|
208 |
+ |
table.getElement().getStyle().setPropertyPx( "paddingLeft", spacerWidth ); |
|
209 |
+ |
} |
|
210 |
+ |
} |
|
211 |
+ |
|
|
212 |
+ |
/** |
|
213 |
+ |
* Get the current width of the spacer element. |
|
214 |
+ |
* |
|
215 |
+ |
* @param table the table to check |
|
216 |
+ |
* |
|
217 |
+ |
* @return the current width |
|
218 |
+ |
*/ |
|
219 |
+ |
private int getSpacerWidth( FixedWidthFlexTable table ) |
|
220 |
+ |
{ |
|
221 |
+ |
// Get the padding string |
|
222 |
+ |
String paddingStr; |
|
223 |
+ |
if ( isScrollBarOnRight() ) |
|
224 |
+ |
{ |
|
225 |
+ |
paddingStr = table.getElement().getStyle().getProperty( "paddingRight" ); |
|
226 |
+ |
} |
|
227 |
+ |
else |
|
228 |
+ |
{ |
|
229 |
+ |
paddingStr = table.getElement().getStyle().getProperty( "paddingLeft" ); |
|
230 |
+ |
} |
|
231 |
+ |
|
|
232 |
+ |
// Check the padding string |
|
233 |
+ |
if ( paddingStr == null || paddingStr.length() < 3 ) |
|
234 |
+ |
{ |
|
235 |
+ |
return -1; |
|
236 |
+ |
} |
|
237 |
+ |
|
|
238 |
+ |
// Parse the int from the padding |
|
239 |
+ |
try |
|
240 |
+ |
{ |
|
241 |
+ |
return Integer.parseInt( paddingStr.substring( 0, paddingStr.length() - 2 ) ); |
|
242 |
+ |
} |
|
243 |
+ |
catch ( NumberFormatException e ) |
|
244 |
+ |
{ |
|
245 |
+ |
return -1; |
|
246 |
+ |
} |
|
247 |
+ |
} |
105 |
248 |
|
} |
106 |
249 |
|
|
107 |
250 |
|
/** |
108 |
|
- |
* Returns the width of a table, minus any padding, in pixels. |
109 |
|
- |
* |
110 |
|
- |
* @param table the table |
111 |
|
- |
* @param includeSpacer true to include the spacer width |
112 |
|
- |
* @return the width |
113 |
|
- |
*/ |
114 |
|
- |
public int getTableWidth(FixedWidthFlexTable table, boolean includeSpacer) { |
115 |
|
- |
int scrollWidth = table.getElement().getScrollWidth(); |
116 |
|
- |
if (!includeSpacer) { |
117 |
|
- |
int spacerWidth = getSpacerWidth(table); |
118 |
|
- |
if (spacerWidth > 0) { |
119 |
|
- |
scrollWidth -= spacerWidth; |
120 |
|
- |
} |
121 |
|
- |
} |
122 |
|
- |
return scrollWidth; |
|
251 |
+ |
* Opera and Old Mozilla put the scroll bar on the left side in RTL mode. |
|
252 |
+ |
*/ |
|
253 |
+ |
private static class ImplLeftScrollBar extends Impl |
|
254 |
+ |
{ |
|
255 |
+ |
@Override boolean isScrollBarOnRight() |
|
256 |
+ |
{ |
|
257 |
+ |
return !LocaleInfo.getCurrentLocale().isRTL(); |
|
258 |
+ |
} |
123 |
259 |
|
} |
124 |
260 |
|
|
125 |
261 |
|
/** |
126 |
|
- |
* Recalculate the ideal widths of columns. |
127 |
|
- |
* |
128 |
|
- |
* @param scrollTable the scroll table |
129 |
|
- |
* @param command an optional command to execute while recalculating |
|
262 |
+ |
* IE puts the scroll bar on the left side in RTL mode. The padding trick |
|
263 |
+ |
* doesn't work, so we use a separate element. |
130 |
264 |
|
*/ |
131 |
|
- |
public void recalculateIdealColumnWidths(AbstractScrollTable scrollTable, |
132 |
|
- |
Command command) { |
133 |
|
- |
FixedWidthFlexTable headerTable = scrollTable.getHeaderTable(); |
134 |
|
- |
FixedWidthFlexTable footerTable = scrollTable.getFooterTable(); |
135 |
|
- |
FixedWidthGrid dataTable = scrollTable.getDataTable(); |
136 |
|
- |
|
137 |
|
- |
// Setup all inner tables |
138 |
|
- |
dataTable.recalculateIdealColumnWidthsSetup(); |
139 |
|
- |
headerTable.recalculateIdealColumnWidthsSetup(); |
140 |
|
- |
if (footerTable != null) { |
141 |
|
- |
footerTable.recalculateIdealColumnWidthsSetup(); |
142 |
|
- |
} |
143 |
|
- |
|
144 |
|
- |
// Perform operations |
145 |
|
- |
dataTable.recalculateIdealColumnWidthsImpl(); |
146 |
|
- |
headerTable.recalculateIdealColumnWidthsImpl(); |
147 |
|
- |
if (footerTable != null) { |
148 |
|
- |
footerTable.recalculateIdealColumnWidthsImpl(); |
149 |
|
- |
} |
150 |
|
- |
|
151 |
|
- |
// Execute the optional command |
152 |
|
- |
if (command != null) { |
153 |
|
- |
command.execute(); |
154 |
|
- |
} |
155 |
|
- |
|
156 |
|
- |
// Teardown all inner tables |
157 |
|
- |
dataTable.recalculateIdealColumnWidthsTeardown(); |
158 |
|
- |
headerTable.recalculateIdealColumnWidthsTeardown(); |
159 |
|
- |
if (footerTable != null) { |
160 |
|
- |
footerTable.recalculateIdealColumnWidthsTeardown(); |
161 |
|
- |
} |
162 |
|
- |
} |
163 |
|
- |
|
164 |
|
- |
/** |
165 |
|
- |
* Reposition the header spacer as needed. |
166 |
|
- |
* |
167 |
|
- |
* @param scrollTable the scroll table |
168 |
|
- |
* @param force if true, ignore the scroll policy |
169 |
|
- |
*/ |
170 |
|
- |
public void repositionSpacer(AbstractScrollTable scrollTable, boolean force) { |
171 |
|
- |
// Only ScrollPolicy.BOTH has a vertical scroll bar |
172 |
|
- |
if (!force && scrollTable.scrollPolicy != ScrollPolicy.BOTH) { |
173 |
|
- |
return; |
174 |
|
- |
} |
175 |
|
- |
|
176 |
|
- |
Element dataWrapper = scrollTable.dataWrapper; |
177 |
|
- |
int spacerWidth = dataWrapper.getOffsetWidth() |
178 |
|
- |
- dataWrapper.getPropertyInt("clientWidth"); |
179 |
|
- |
resizeSpacer(scrollTable.headerTable, scrollTable.headerSpacer, |
180 |
|
- |
spacerWidth); |
181 |
|
- |
if (scrollTable.footerTable != null) { |
182 |
|
- |
resizeSpacer(scrollTable.footerTable, scrollTable.footerSpacer, |
183 |
|
- |
spacerWidth); |
184 |
|
- |
} |
185 |
|
- |
} |
186 |
|
- |
|
187 |
|
- |
/** |
188 |
|
- |
* @return true if the scroll bar is on the right |
189 |
|
- |
*/ |
190 |
|
- |
boolean isScrollBarOnRight() { |
191 |
|
- |
return true; |
192 |
|
- |
} |
193 |
|
- |
|
194 |
|
- |
void resizeSpacer(FixedWidthFlexTable table, Element spacer, int spacerWidth) { |
195 |
|
- |
// Exit early if the spacer is already the correct size |
196 |
|
- |
if (spacerWidth == getSpacerWidth(table)) { |
197 |
|
- |
return; |
198 |
|
- |
} |
199 |
|
- |
|
200 |
|
- |
if (isScrollBarOnRight()) { |
201 |
|
- |
table.getElement().getStyle().setPropertyPx("paddingRight", spacerWidth); |
202 |
|
- |
} else { |
203 |
|
- |
table.getElement().getStyle().setPropertyPx("paddingLeft", spacerWidth); |
204 |
|
- |
} |
205 |
|
- |
} |
206 |
|
- |
|
207 |
|
- |
/** |
208 |
|
- |
* Get the current width of the spacer element. |
209 |
|
- |
* |
210 |
|
- |
* @param table the table to check |
211 |
|
- |
* @return the current width |
212 |
|
- |
*/ |
213 |
|
- |
private int getSpacerWidth(FixedWidthFlexTable table) { |
214 |
|
- |
// Get the padding string |
215 |
|
- |
String paddingStr; |
216 |
|
- |
if (isScrollBarOnRight()) { |
217 |
|
- |
paddingStr = table.getElement().getStyle().getProperty("paddingRight"); |
218 |
|
- |
} else { |
219 |
|
- |
paddingStr = table.getElement().getStyle().getProperty("paddingLeft"); |
220 |
|
- |
} |
221 |
|
- |
|
222 |
|
- |
// Check the padding string |
223 |
|
- |
if (paddingStr == null || paddingStr.length() < 3) { |
224 |
|
- |
return -1; |
225 |
|
- |
} |
226 |
|
- |
|
227 |
|
- |
// Parse the int from the padding |
228 |
|
- |
try { |
229 |
|
- |
return Integer.parseInt(paddingStr.substring(0, paddingStr.length() - 2)); |
230 |
|
- |
} catch (NumberFormatException e) { |
231 |
|
- |
return -1; |
232 |
|
- |
} |
233 |
|
- |
} |
234 |
|
- |
} |
235 |
|
- |
|
236 |
|
- |
/** |
237 |
|
- |
* Opera and Old Mozilla put the scroll bar on the left side in RTL mode. |
238 |
|
- |
*/ |
239 |
|
- |
private static class ImplLeftScrollBar extends Impl { |
240 |
|
- |
@Override |
241 |
|
- |
boolean isScrollBarOnRight() { |
242 |
|
- |
return !LocaleInfo.getCurrentLocale().isRTL(); |
|
265 |
+ |
@SuppressWarnings("unused") |
|
266 |
+ |
private static class ImplIE6 extends ImplLeftScrollBar |
|
267 |
+ |
{ |
|
268 |
+ |
/** |
|
269 |
+ |
* Adding padding to a table in IE will mess up the layout, so we use an |
|
270 |
+ |
* absolutely positioned div to add padding. In RTL mode, the div needs to |
|
271 |
+ |
* be exactly the right width and position or scrollLeft will be affected. |
|
272 |
+ |
* In LTR mode, we can position it anywhere and set the width to a high |
|
273 |
+ |
* number, improving performance. |
|
274 |
+ |
*/ |
|
275 |
+ |
@Override |
|
276 |
+ |
public Element createSpacer( FixedWidthFlexTable table, Element wrapper ) |
|
277 |
+ |
{ |
|
278 |
+ |
Element spacer = DOM.createDiv(); |
|
279 |
+ |
spacer.getStyle().setPropertyPx( "height", 1 ); |
|
280 |
+ |
spacer.getStyle().setPropertyPx( "top", 1 ); |
|
281 |
+ |
spacer.getStyle().setProperty( "position", "absolute" ); |
|
282 |
+ |
if ( !LocaleInfo.getCurrentLocale().isRTL() ) |
|
283 |
+ |
{ |
|
284 |
+ |
spacer.getStyle().setPropertyPx( "left", 1 ); |
|
285 |
+ |
spacer.getStyle().setPropertyPx( "width", 10000 ); |
|
286 |
+ |
} |
|
287 |
+ |
wrapper.appendChild( spacer ); |
|
288 |
+ |
return spacer; |
|
289 |
+ |
} |
|
290 |
+ |
|
|
291 |
+ |
@Override |
|
292 |
+ |
public int getTableWidth( FixedWidthFlexTable table, boolean includeSpacer ) |
|
293 |
+ |
{ |
|
294 |
+ |
return table.getElement().getScrollWidth(); |
|
295 |
+ |
} |
|
296 |
+ |
|
|
297 |
+ |
/** |
|
298 |
+ |
* IE allows the table to resize as widely as needed unless we restrict the |
|
299 |
+ |
* width of a parent element. |
|
300 |
+ |
*/ |
|
301 |
+ |
@Override |
|
302 |
+ |
public void recalculateIdealColumnWidths( AbstractScrollTable scrollTable, Command command ) |
|
303 |
+ |
{ |
|
304 |
+ |
scrollTable.getAbsoluteElement().getStyle().setPropertyPx( "width", 1 ); |
|
305 |
+ |
super.recalculateIdealColumnWidths( scrollTable, command ); |
|
306 |
+ |
scrollTable.getAbsoluteElement().getStyle().setProperty( "width", "100%" ); |
|
307 |
+ |
} |
|
308 |
+ |
|
|
309 |
+ |
@Override void resizeSpacer( FixedWidthFlexTable table, Element spacer, int width ) |
|
310 |
+ |
{ |
|
311 |
+ |
if ( LocaleInfo.getCurrentLocale().isRTL() ) |
|
312 |
+ |
{ |
|
313 |
+ |
int headerWidth = table.getOffsetWidth(); |
|
314 |
+ |
spacer.getStyle().setPropertyPx( "width", width ); |
|
315 |
+ |
spacer.getStyle().setPropertyPx( "right", headerWidth ); |
|
316 |
+ |
} |
|
317 |
+ |
} |
243 |
318 |
|
} |
244 |
|
- |
} |
245 |
319 |
|
|
246 |
|
- |
/** |
247 |
|
- |
* IE puts the scroll bar on the left side in RTL mode. The padding trick |
248 |
|
- |
* doesn't work, so we use a separate element. |
249 |
|
- |
*/ |
250 |
|
- |
@SuppressWarnings("unused") |
251 |
|
- |
private static class ImplIE6 extends ImplLeftScrollBar { |
252 |
|
- |
/** |
253 |
|
- |
* Adding padding to a table in IE will mess up the layout, so we use an |
254 |
|
- |
* absolutely positioned div to add padding. In RTL mode, the div needs to |
255 |
|
- |
* be exactly the right width and position or scrollLeft will be affected. |
256 |
|
- |
* In LTR mode, we can position it anywhere and set the width to a high |
257 |
|
- |
* number, improving performance. |
|
320 |
+ |
/** |
|
321 |
+ |
* A helper class that handles some of the mouse events associated with |
|
322 |
+ |
* resizing columns. |
258 |
323 |
|
*/ |
259 |
|
- |
@Override |
260 |
|
- |
public Element createSpacer(FixedWidthFlexTable table, Element wrapper) { |
261 |
|
- |
Element spacer = DOM.createDiv(); |
262 |
|
- |
spacer.getStyle().setPropertyPx("height", 1); |
263 |
|
- |
spacer.getStyle().setPropertyPx("top", 1); |
264 |
|
- |
spacer.getStyle().setProperty("position", "absolute"); |
265 |
|
- |
if (!LocaleInfo.getCurrentLocale().isRTL()) { |
266 |
|
- |
spacer.getStyle().setPropertyPx("left", 1); |
267 |
|
- |
spacer.getStyle().setPropertyPx("width", 10000); |
268 |
|
- |
} |
269 |
|
- |
wrapper.appendChild(spacer); |
270 |
|
- |
return spacer; |
|
324 |
+ |
private static class MouseResizeWorker |
|
325 |
+ |
{ |
|
326 |
+ |
/** |
|
327 |
+ |
* The width of the area that is available for resize. |
|
328 |
+ |
*/ |
|
329 |
+ |
private static final int RESIZE_CURSOR_WIDTH = 15; |
|
330 |
+ |
|
|
331 |
+ |
/** |
|
332 |
+ |
* The current header cell that the mouse is affecting. |
|
333 |
+ |
*/ |
|
334 |
+ |
private Element curCell = null; |
|
335 |
+ |
|
|
336 |
+ |
/** |
|
337 |
+ |
* The columns under the colSpan of the current cell. |
|
338 |
+ |
*/ |
|
339 |
+ |
private List<ColumnWidthInfo> curCells = new ArrayList<ColumnWidthInfo>(); |
|
340 |
+ |
|
|
341 |
+ |
/** |
|
342 |
+ |
* The index of the current header cell. |
|
343 |
+ |
*/ |
|
344 |
+ |
private int curCellIndex = 0; |
|
345 |
+ |
|
|
346 |
+ |
/** |
|
347 |
+ |
* The current x position of the mouse. |
|
348 |
+ |
*/ |
|
349 |
+ |
private int mouseXCurrent = 0; |
|
350 |
+ |
|
|
351 |
+ |
/** |
|
352 |
+ |
* The last x position of the mouse when we resized. |
|
353 |
+ |
*/ |
|
354 |
+ |
private int mouseXLast = 0; |
|
355 |
+ |
|
|
356 |
+ |
/** |
|
357 |
+ |
* The starting x position of the mouse when resizing a column. |
|
358 |
+ |
*/ |
|
359 |
+ |
private int mouseXStart = 0; |
|
360 |
+ |
|
|
361 |
+ |
/** |
|
362 |
+ |
* A timer used to resize the columns. As long as the timer is active, it |
|
363 |
+ |
* will poll for the new row size and resize the columns. |
|
364 |
+ |
*/ |
|
365 |
+ |
private Timer resizeTimer = new Timer() |
|
366 |
+ |
{ |
|
367 |
+ |
@Override |
|
368 |
+ |
public void run() |
|
369 |
+ |
{ |
|
370 |
+ |
resizeColumn(); |
|
371 |
+ |
schedule( 100 ); |
|
372 |
+ |
} |
|
373 |
+ |
}; |
|
374 |
+ |
|
|
375 |
+ |
/** |
|
376 |
+ |
* A boolean indicating that we are resizing the current header cell. |
|
377 |
+ |
*/ |
|
378 |
+ |
private boolean resizing = false; |
|
379 |
+ |
|
|
380 |
+ |
/** |
|
381 |
+ |
* The index of the first column that will be sacrificed. |
|
382 |
+ |
*/ |
|
383 |
+ |
private int sacrificeCellIndex = -1; |
|
384 |
+ |
|
|
385 |
+ |
/** |
|
386 |
+ |
* The cells that will be sacrificed so the current cells can be resized. |
|
387 |
+ |
*/ |
|
388 |
+ |
private List<ColumnWidthInfo> sacrificeCells = new ArrayList<ColumnWidthInfo>(); |
|
389 |
+ |
|
|
390 |
+ |
/** |
|
391 |
+ |
* The table that this worker affects. |
|
392 |
+ |
*/ |
|
393 |
+ |
private AbstractScrollTable table = null; |
|
394 |
+ |
|
|
395 |
+ |
/** |
|
396 |
+ |
* @return the current cell |
|
397 |
+ |
*/ |
|
398 |
+ |
public Element getCurrentCell() |
|
399 |
+ |
{ |
|
400 |
+ |
return curCell; |
|
401 |
+ |
} |
|
402 |
+ |
|
|
403 |
+ |
/** |
|
404 |
+ |
* @return true if a header is currently being resized |
|
405 |
+ |
*/ |
|
406 |
+ |
public boolean isResizing() |
|
407 |
+ |
{ |
|
408 |
+ |
return resizing; |
|
409 |
+ |
} |
|
410 |
+ |
|
|
411 |
+ |
/** |
|
412 |
+ |
* Resize the column on a mouse event. This method also marks the client as |
|
413 |
+ |
* busy so we do not try to change the size repeatedly. |
|
414 |
+ |
* |
|
415 |
+ |
* @param event the mouse event |
|
416 |
+ |
*/ |
|
417 |
+ |
public void resizeColumn( Event event ) |
|
418 |
+ |
{ |
|
419 |
+ |
mouseXCurrent = DOM.eventGetClientX( event ); |
|
420 |
+ |
} |
|
421 |
+ |
|
|
422 |
+ |
/** |
|
423 |
+ |
* Set the current cell that will be resized based on the mouse event. |
|
424 |
+ |
* |
|
425 |
+ |
* @param event the event that triggered the new cell |
|
426 |
+ |
* |
|
427 |
+ |
* @return true if the cell was actually changed |
|
428 |
+ |
*/ |
|
429 |
+ |
public boolean setCurrentCell( Event event ) |
|
430 |
+ |
{ |
|
431 |
+ |
// Check the resize policy of the table |
|
432 |
+ |
Element cell = null; |
|
433 |
+ |
if ( table.columnResizePolicy == ColumnResizePolicy.MULTI_CELL ) |
|
434 |
+ |
{ |
|
435 |
+ |
cell = table.headerTable.getEventTargetCell( event ); |
|
436 |
+ |
} |
|
437 |
+ |
else if ( table.columnResizePolicy == ColumnResizePolicy.SINGLE_CELL ) |
|
438 |
+ |
{ |
|
439 |
+ |
cell = table.headerTable.getEventTargetCell( event ); |
|
440 |
+ |
if ( cell != null && cell.getPropertyInt( "colSpan" ) > 1 ) |
|
441 |
+ |
{ |
|
442 |
+ |
cell = null; |
|
443 |
+ |
} |
|
444 |
+ |
} |
|
445 |
+ |
|
|
446 |
+ |
// See if we are near the edge of the cell |
|
447 |
+ |
int clientX = event.getClientX(); |
|
448 |
+ |
if ( cell != null ) |
|
449 |
+ |
{ |
|
450 |
+ |
int absLeft = cell.getAbsoluteLeft() - Window.getScrollLeft(); |
|
451 |
+ |
if ( LocaleInfo.getCurrentLocale().isRTL() ) |
|
452 |
+ |
{ |
|
453 |
+ |
if ( clientX < absLeft || clientX > absLeft + RESIZE_CURSOR_WIDTH ) |
|
454 |
+ |
{ |
|
455 |
+ |
cell = null; |
|
456 |
+ |
} |
|
457 |
+ |
} |
|
458 |
+ |
else |
|
459 |
+ |
{ |
|
460 |
+ |
int absRight = absLeft + cell.getOffsetWidth(); |
|
461 |
+ |
if ( clientX < absRight - RESIZE_CURSOR_WIDTH || clientX > absRight ) |
|
462 |
+ |
{ |
|
463 |
+ |
cell = null; |
|
464 |
+ |
} |
|
465 |
+ |
} |
|
466 |
+ |
} |
|
467 |
+ |
|
|
468 |
+ |
// Change out the current cell |
|
469 |
+ |
if ( cell != curCell ) |
|
470 |
+ |
{ |
|
471 |
+ |
// Clear the old cell |
|
472 |
+ |
if ( curCell != null ) |
|
473 |
+ |
{ |
|
474 |
+ |
curCell.getStyle().setProperty( "cursor", "" ); |
|
475 |
+ |
} |
|
476 |
+ |
|
|
477 |
+ |
// Set the new cell |
|
478 |
+ |
curCell = cell; |
|
479 |
+ |
if ( curCell != null ) |
|
480 |
+ |
{ |
|
481 |
+ |
// Check the cell index |
|
482 |
+ |
curCellIndex = getCellIndex( curCell ); |
|
483 |
+ |
if ( curCellIndex < 0 ) |
|
484 |
+ |
{ |
|
485 |
+ |
curCell = null; |
|
486 |
+ |
return false; |
|
487 |
+ |
} |
|
488 |
+ |
|
|
489 |
+ |
// Check for resizable columns within one of the cells in the colspan |
|
490 |
+ |
boolean resizable = false; |
|
491 |
+ |
int colSpan = cell.getPropertyInt( "colSpan" ); |
|
492 |
+ |
curCells = table.getColumnWidthInfo( curCellIndex, colSpan ); |
|
493 |
+ |
for ( ColumnWidthInfo info : curCells ) |
|
494 |
+ |
{ |
|
495 |
+ |
if ( !info.hasMaximumWidth() || !info.hasMinimumWidth() || info.getMaximumWidth() != info.getMinimumWidth() ) |
|
496 |
+ |
{ |
|
497 |
+ |
resizable = true; |
|
498 |
+ |
} |
|
499 |
+ |
} |
|
500 |
+ |
if ( !resizable ) |
|
501 |
+ |
{ |
|
502 |
+ |
curCell = null; |
|
503 |
+ |
curCells = null; |
|
504 |
+ |
return false; |
|
505 |
+ |
} |
|
506 |
+ |
|
|
507 |
+ |
// Update the cursor on the cell |
|
508 |
+ |
curCell.getStyle().setProperty( "cursor", "e-resize" ); |
|
509 |
+ |
} |
|
510 |
+ |
return true; |
|
511 |
+ |
} |
|
512 |
+ |
|
|
513 |
+ |
// The cell did not change |
|
514 |
+ |
return false; |
|
515 |
+ |
} |
|
516 |
+ |
|
|
517 |
+ |
/** |
|
518 |
+ |
* Set the ScrollTable table that this worker affects. |
|
519 |
+ |
* |
|
520 |
+ |
* @param table the scroll table |
|
521 |
+ |
*/ |
|
522 |
+ |
public void setScrollTable( AbstractScrollTable table ) |
|
523 |
+ |
{ |
|
524 |
+ |
this.table = table; |
|
525 |
+ |
} |
|
526 |
+ |
|
|
527 |
+ |
/** |
|
528 |
+ |
* Start resizing the current cell when the user clicks on the right edge of |
|
529 |
+ |
* the cell. |
|
530 |
+ |
* |
|
531 |
+ |
* @param event the mouse event |
|
532 |
+ |
*/ |
|
533 |
+ |
public void startResizing( Event event ) |
|
534 |
+ |
{ |
|
535 |
+ |
if ( curCell != null ) |
|
536 |
+ |
{ |
|
537 |
+ |
resizing = true; |
|
538 |
+ |
mouseXStart = event.getClientX(); |
|
539 |
+ |
mouseXLast = mouseXStart; |
|
540 |
+ |
mouseXCurrent = mouseXStart; |
|
541 |
+ |
|
|
542 |
+ |
// Add the sacrifice cells |
|
543 |
+ |
int numColumns = table.getDataTable().getColumnCount(); |
|
544 |
+ |
int colSpan = curCell.getPropertyInt( "colSpan" ); |
|
545 |
+ |
sacrificeCellIndex = curCellIndex + colSpan; |
|
546 |
+ |
sacrificeCells = table.getColumnWidthInfo( sacrificeCellIndex, numColumns - sacrificeCellIndex ); |
|
547 |
+ |
|
|
548 |
+ |
// Start the timer and listen for changes |
|
549 |
+ |
DOM.setCapture( table.headerWrapper ); |
|
550 |
+ |
resizeTimer.schedule( 20 ); |
|
551 |
+ |
} |
|
552 |
+ |
} |
|
553 |
+ |
|
|
554 |
+ |
/** |
|
555 |
+ |
* Stop resizing the current cell. |
|
556 |
+ |
* |
|
557 |
+ |
* @param event the mouse event |
|
558 |
+ |
*/ |
|
559 |
+ |
public void stopResizing( Event event ) |
|
560 |
+ |
{ |
|
561 |
+ |
if ( curCell != null && resizing ) |
|
562 |
+ |
{ |
|
563 |
+ |
curCell.getStyle().setProperty( "cursor", "" ); |
|
564 |
+ |
curCell = null; |
|
565 |
+ |
resizing = false; |
|
566 |
+ |
DOM.releaseCapture( table.headerWrapper ); |
|
567 |
+ |
resizeTimer.cancel(); |
|
568 |
+ |
resizeColumn(); |
|
569 |
+ |
curCells = null; |
|
570 |
+ |
sacrificeCells = null; |
|
571 |
+ |
table.resizeTablesVertically(); |
|
572 |
+ |
} |
|
573 |
+ |
} |
|
574 |
+ |
|
|
575 |
+ |
/** |
|
576 |
+ |
* Get the scroll table. |
|
577 |
+ |
* |
|
578 |
+ |
* @return the scroll table |
|
579 |
+ |
*/ |
|
580 |
+ |
protected AbstractScrollTable getScrollTable() |
|
581 |
+ |
{ |
|
582 |
+ |
return table; |
|
583 |
+ |
} |
|
584 |
+ |
|
|
585 |
+ |
/** |
|
586 |
+ |
* Get the actual cell index of a cell in the header table. |
|
587 |
+ |
* |
|
588 |
+ |
* @param cell the cell element |
|
589 |
+ |
* |
|
590 |
+ |
* @return the cell index |
|
591 |
+ |
*/ |
|
592 |
+ |
private int getCellIndex( Element cell ) |
|
593 |
+ |
{ |
|
594 |
+ |
int row = OverrideDOM.getRowIndex( DOM.getParent( cell ) ) - 1; |
|
595 |
+ |
int column = OverrideDOM.getCellIndex( cell ); |
|
596 |
+ |
return table.headerTable.getColumnIndex( row, column ) - table.getHeaderOffset(); |
|
597 |
+ |
} |
|
598 |
+ |
|
|
599 |
+ |
/** |
|
600 |
+ |
* Helper method that actually sets the column size. This method is called |
|
601 |
+ |
* periodically by a timer. |
|
602 |
+ |
*/ |
|
603 |
+ |
private void resizeColumn() |
|
604 |
+ |
{ |
|
605 |
+ |
if ( mouseXLast != mouseXCurrent ) |
|
606 |
+ |
{ |
|
607 |
+ |
mouseXLast = mouseXCurrent; |
|
608 |
+ |
|
|
609 |
+ |
// Distribute to the cells being resized |
|
610 |
+ |
int totalDelta = mouseXCurrent - mouseXStart; |
|
611 |
+ |
if ( LocaleInfo.getCurrentLocale().isRTL() ) |
|
612 |
+ |
{ |
|
613 |
+ |
totalDelta *= -1; |
|
614 |
+ |
} |
|
615 |
+ |
totalDelta -= table.columnResizer.distributeWidth( curCells, totalDelta ); |
|
616 |
+ |
|
|
617 |
+ |
// Distribute to the sacrifice cells |
|
618 |
+ |
if ( table.resizePolicy.isSacrificial() ) |
|
619 |
+ |
{ |
|
620 |
+ |
int remaining = table.columnResizer.distributeWidth( sacrificeCells, -totalDelta ); |
|
621 |
+ |
|
|
622 |
+ |
// We don't have enough to sacrifice, redistribute the width |
|
623 |
+ |
if ( remaining != 0 && table.resizePolicy.isFixedWidth() ) |
|
624 |
+ |
{ |
|
625 |
+ |
totalDelta += remaining; |
|
626 |
+ |
table.columnResizer.distributeWidth( curCells, totalDelta ); |
|
627 |
+ |
} |
|
628 |
+ |
|
|
629 |
+ |
// Apply the widths to the sacrifice column |
|
630 |
+ |
table.applyNewColumnWidths( sacrificeCellIndex, sacrificeCells, true ); |
|
631 |
+ |
} |
|
632 |
+ |
|
|
633 |
+ |
// Set the new widths |
|
634 |
+ |
table.applyNewColumnWidths( curCellIndex, curCells, true ); |
|
635 |
+ |
|
|
636 |
+ |
// Scroll to table back into alignment |
|
637 |
+ |
table.scrollTables( false ); |
|
638 |
+ |
} |
|
639 |
+ |
} |
271 |
640 |
|
} |
272 |
641 |
|
|
273 |
|
- |
@Override |
274 |
|
- |
public int getTableWidth(FixedWidthFlexTable table, boolean includeSpacer) { |
275 |
|
- |
return table.getElement().getScrollWidth(); |
|
642 |
+ |
/** |
|
643 |
+ |
* The Opera version of the mouse worker fixes an Opera bug where the cursor |
|
644 |
+ |
* isn't updated if the mouse is hovering over an element DOM object when its |
|
645 |
+ |
* cursor style is changed. |
|
646 |
+ |
*/ |
|
647 |
+ |
@SuppressWarnings("unused") |
|
648 |
+ |
private static class MouseResizeWorkerOpera extends MouseResizeWorker |
|
649 |
+ |
{ |
|
650 |
+ |
/** |
|
651 |
+ |
* A div used to force the cursor to update. |
|
652 |
+ |
*/ |
|
653 |
+ |
private Element cursorUpdateDiv; |
|
654 |
+ |
|
|
655 |
+ |
/** |
|
656 |
+ |
* Constructor. |
|
657 |
+ |
*/ |
|
658 |
+ |
public MouseResizeWorkerOpera() |
|
659 |
+ |
{ |
|
660 |
+ |
cursorUpdateDiv = DOM.createDiv(); |
|
661 |
+ |
DOM.setStyleAttribute( cursorUpdateDiv, "position", "absolute" ); |
|
662 |
+ |
} |
|
663 |
+ |
|
|
664 |
+ |
/** |
|
665 |
+ |
* Set the current cell that will be resized based on the mouse event. |
|
666 |
+ |
* |
|
667 |
+ |
* @param event the event that triggered the new cell |
|
668 |
+ |
* |
|
669 |
+ |
* @return true if the cell was actually changed |
|
670 |
+ |
*/ |
|
671 |
+ |
@Override |
|
672 |
+ |
public boolean setCurrentCell( Event event ) |
|
673 |
+ |
{ |
|
674 |
+ |
// Check if cursor update div is active |
|
675 |
+ |
if ( DOM.eventGetTarget( event ) == cursorUpdateDiv ) |
|
676 |
+ |
{ |
|
677 |
+ |
removeCursorUpdateDiv(); |
|
678 |
+ |
return false; |
|
679 |
+ |
} |
|
680 |
+ |
|
|
681 |
+ |
// Use the parent method |
|
682 |
+ |
boolean cellChanged = super.setCurrentCell( event ); |
|
683 |
+ |
|
|
684 |
+ |
// Position a div that forces a cursor redraw in Opera |
|
685 |
+ |
if ( cellChanged ) |
|
686 |
+ |
{ |
|
687 |
+ |
DOM.setCapture( getScrollTable().headerWrapper ); |
|
688 |
+ |
DOM.setStyleAttribute( cursorUpdateDiv, "height", (Window.getClientHeight() - 1) + "px" ); |
|
689 |
+ |
DOM.setStyleAttribute( cursorUpdateDiv, "width", (Window.getClientWidth() - 1) + "px" ); |
|
690 |
+ |
DOM.setStyleAttribute( cursorUpdateDiv, "left", "0px" ); |
|
691 |
+ |
DOM.setStyleAttribute( cursorUpdateDiv, "top", "0px" ); |
|
692 |
+ |
DOM.appendChild( RootPanel.getBodyElement(), cursorUpdateDiv ); |
|
693 |
+ |
} |
|
694 |
+ |
return cellChanged; |
|
695 |
+ |
} |
|
696 |
+ |
|
|
697 |
+ |
/** |
|
698 |
+ |
* Start resizing the current cell. |
|
699 |
+ |
* |
|
700 |
+ |
* @param event the mouse event |
|
701 |
+ |
*/ |
|
702 |
+ |
@Override |
|
703 |
+ |
public void startResizing( Event event ) |
|
704 |
+ |
{ |
|
705 |
+ |
removeCursorUpdateDiv(); |
|
706 |
+ |
super.startResizing( event ); |
|
707 |
+ |
} |
|
708 |
+ |
|
|
709 |
+ |
/** |
|
710 |
+ |
* Remove the cursor update div from the page. |
|
711 |
+ |
*/ |
|
712 |
+ |
private void removeCursorUpdateDiv() |
|
713 |
+ |
{ |
|
714 |
+ |
if ( DOM.getCaptureElement() != null ) |
|
715 |
+ |
{ |
|
716 |
+ |
DOM.removeChild( RootPanel.getBodyElement(), cursorUpdateDiv ); |
|
717 |
+ |
DOM.releaseCapture( getScrollTable().headerWrapper ); |
|
718 |
+ |
} |
|
719 |
+ |
} |
276 |
720 |
|
} |
277 |
721 |
|
|
278 |
722 |
|
/** |
279 |
|
- |
* IE allows the table to resize as widely as needed unless we restrict the |
280 |
|
- |
* width of a parent element. |
|
723 |
+ |
* Information about the height of the inner tables. |
281 |
724 |
|
*/ |
282 |
|
- |
@Override |
283 |
|
- |
public void recalculateIdealColumnWidths(AbstractScrollTable scrollTable, |
284 |
|
- |
Command command) { |
285 |
|
- |
scrollTable.getAbsoluteElement().getStyle().setPropertyPx("width", 1); |
286 |
|
- |
super.recalculateIdealColumnWidths(scrollTable, command); |
287 |
|
- |
scrollTable.getAbsoluteElement().getStyle().setProperty("width", "100%"); |
|
725 |
+ |
private class TableHeightInfo |
|
726 |
+ |
{ |
|
727 |
+ |
private int headerTableHeight; |
|
728 |
+ |
private int dataTableHeight; |
|
729 |
+ |
private int footerTableHeight; |
|
730 |
+ |
|
|
731 |
+ |
/** |
|
732 |
+ |
* Construct a new {@link TableHeightInfo}. |
|
733 |
+ |
*/ |
|
734 |
+ |
public TableHeightInfo() |
|
735 |
+ |
{ |
|
736 |
+ |
int totalHeight = DOM.getElementPropertyInt( getElement(), "clientHeight" ); |
|
737 |
+ |
headerTableHeight = headerTable.getOffsetHeight(); |
|
738 |
+ |
if ( footerTable != null ) |
|
739 |
+ |
{ |
|
740 |
+ |
footerTableHeight = footerTable.getOffsetHeight(); |
|
741 |
+ |
} |
|
742 |
+ |
dataTableHeight = totalHeight - headerTableHeight - footerTableHeight; |
|
743 |
+ |
} |
288 |
744 |
|
} |
289 |
745 |
|
|
290 |
|
- |
@Override |
291 |
|
- |
void resizeSpacer(FixedWidthFlexTable table, Element spacer, int width) { |
292 |
|
- |
if (LocaleInfo.getCurrentLocale().isRTL()) { |
293 |
|
- |
int headerWidth = table.getOffsetWidth(); |
294 |
|
- |
spacer.getStyle().setPropertyPx("width", width); |
295 |
|
- |
spacer.getStyle().setPropertyPx("right", headerWidth); |
296 |
|
- |
} |
|
746 |
+ |
/** |
|
747 |
+ |
* Information about the width of the inner tables. |
|
748 |
+ |
*/ |
|
749 |
+ |
private class TableWidthInfo |
|
750 |
+ |
{ |
|
751 |
+ |
private int headerTableWidth; |
|
752 |
+ |
private int dataTableWidth; |
|
753 |
+ |
private int footerTableWidth; |
|
754 |
+ |
private int availableWidth; |
|
755 |
+ |
|
|
756 |
+ |
/** |
|
757 |
+ |
* Construct a new {@link TableWidthInfo}. |
|
758 |
+ |
* |
|
759 |
+ |
* @param includeSpacer true to include spacer in calculations |
|
760 |
+ |
*/ |
|
761 |
+ |
public TableWidthInfo( boolean includeSpacer ) |
|
762 |
+ |
{ |
|
763 |
+ |
availableWidth = getAvailableWidth(); |
|
764 |
+ |
headerTableWidth = impl.getTableWidth( headerTable, includeSpacer ); |
|
765 |
+ |
dataTableWidth = dataTable.getElement().getScrollWidth(); |
|
766 |
+ |
if ( footerTable != null ) |
|
767 |
+ |
{ |
|
768 |
+ |
footerTableWidth = impl.getTableWidth( footerTable, includeSpacer ); |
|
769 |
+ |
} |
|
770 |
+ |
} |
|
771 |
+ |
} |
|
772 |
+ |
|
|
773 |
+ |
/** |
|
774 |
+ |
* An {@link ImageBundle} that provides images for {@link AbstractScrollTable} |
|
775 |
+ |
* . |
|
776 |
+ |
*/ |
|
777 |
+ |
public static interface ScrollTableImages extends ImageBundle |
|
778 |
+ |
{ |
|
779 |
+ |
/** |
|
780 |
+ |
* An image used to fill the available width. |
|
781 |
+ |
* |
|
782 |
+ |
* @return a prototype of this image |
|
783 |
+ |
*/ |
|
784 |
+ |
AbstractImagePrototype scrollTableFillWidth(); |
|
785 |
+ |
|
|
786 |
+ |
/** |
|
787 |
+ |
* An image indicating that a column is sorted in ascending order. |
|
788 |
+ |
* |
|
789 |
+ |
* @return a prototype of this image |
|
790 |
+ |
*/ |
|
791 |
+ |
AbstractImagePrototype scrollTableAscending(); |
|
792 |
+ |
|
|
793 |
+ |
/** |
|
794 |
+ |
* An image indicating a column is sorted in descending order. |
|
795 |
+ |
* |
|
796 |
+ |
* @return a prototype of this image |
|
797 |
+ |
*/ |
|
798 |
+ |
AbstractImagePrototype scrollTableDescending(); |
|
799 |
+ |
} |
|
800 |
+ |
|
|
801 |
+ |
/** |
|
802 |
+ |
* The default style name. |
|
803 |
+ |
*/ |
|
804 |
+ |
public static final String DEFAULT_STYLE_NAME = "gwt-ScrollTable"; |
|
805 |
+ |
|
|
806 |
+ |
/** |
|
807 |
+ |
* The resize policies related to user resizing. |
|
808 |
+ |
* <p/> |
|
809 |
+ |
* <ul> |
|
810 |
+ |
* <li>DISABLED - Columns cannot be resized by the user</li> |
|
811 |
+ |
* <li>SINGLE_CELL - Only cells with a colspan of 1 can be resized</li> |
|
812 |
+ |
* <li>MULTI_CELL - All cells can be resized by the user</li> |
|
813 |
+ |
* </ul> |
|
814 |
+ |
*/ |
|
815 |
+ |
public static enum ColumnResizePolicy |
|
816 |
+ |
{ |
|
817 |
+ |
DISABLED, SINGLE_CELL, MULTI_CELL |
297 |
818 |
|
} |
298 |
|
- |
} |
299 |
819 |
|
|
300 |
|
- |
/** |
301 |
|
- |
* A helper class that handles some of the mouse events associated with |
302 |
|
- |
* resizing columns. |
303 |
|
- |
*/ |
304 |
|
- |
private static class MouseResizeWorker { |
305 |
820 |
|
/** |
306 |
|
- |
* The width of the area that is available for resize. |
|
821 |
+ |
* The resize policies of table cells. |
|
822 |
+ |
* <p/> |
|
823 |
+ |
* <ul> |
|
824 |
+ |
* <li>UNCONSTRAINED - Columns shrink and expand independently of each other</li> |
|
825 |
+ |
* <li>FLOW - As one column expands or shrinks, the columns to the right will |
|
826 |
+ |
* do the opposite, trying to maintain the same size. The user can still |
|
827 |
+ |
* expand the grid if there is no more space to take from the columns on the |
|
828 |
+ |
* right.</li> |
|
829 |
+ |
* <li>FIXED_WIDTH - As one column expands or shrinks, the columns to the |
|
830 |
+ |
* right will do the opposite, trying to maintain the same size. The width of |
|
831 |
+ |
* the grid will remain constant, ignoring column resizing that would result |
|
832 |
+ |
* in the grid growing in size.</li> |
|
833 |
+ |
* <li>FILL_WIDTH - Same as FIXED_WIDTH, but the grid will always fill the |
|
834 |
+ |
* available width, even if the widget is resized.</li> |
|
835 |
+ |
* </ul> |
307 |
836 |
|
*/ |
308 |
|
- |
private static final int RESIZE_CURSOR_WIDTH = 15; |
|
837 |
+ |
public static enum ResizePolicy |
|
838 |
+ |
{ |
|
839 |
+ |
UNCONSTRAINED( false, false ), FLOW( false, true ), FIXED_WIDTH( true, true ), FILL_WIDTH( true, true ); |
|
840 |
+ |
|
|
841 |
+ |
private boolean isSacrificial; |
|
842 |
+ |
private boolean isFixedWidth; |
|
843 |
+ |
|
|
844 |
+ |
private ResizePolicy( boolean isFixedWidth, boolean isSacrificial ) |
|
845 |
+ |
{ |
|
846 |
+ |
this.isFixedWidth = isFixedWidth; |
|
847 |
+ |
this.isSacrificial = isSacrificial; |
|
848 |
+ |
} |
|
849 |
+ |
|
|
850 |
+ |
private boolean isFixedWidth() |
|
851 |
+ |
{ |
|
852 |
+ |
return isFixedWidth; |
|
853 |
+ |
} |
|
854 |
+ |
|
|
855 |
+ |
private boolean isSacrificial() |
|
856 |
+ |
{ |
|
857 |
+ |
return isSacrificial; |
|
858 |
+ |
} |
|
859 |
+ |
} |
309 |
860 |
|
|
310 |
861 |
|
/** |
311 |
|
- |
* The current header cell that the mouse is affecting. |
|
862 |
+ |
* The scroll policy of the table. |
|
863 |
+ |
* <p/> |
|
864 |
+ |
* <ul> |
|
865 |
+ |
* <li>HORIZONTAL - Only a horizontal scrollbar will be present.</li> |
|
866 |
+ |
* <li>BOTH - Both a vertical and horizontal scrollbar will be present.</li> |
|
867 |
+ |
* <li>DISABLED - Scrollbars will not appear, even if content doesn't fit</li> |
|
868 |
+ |
* </ul> |
312 |
869 |
|
*/ |
313 |
|
- |
private Element curCell = null; |
|
870 |
+ |
public static enum ScrollPolicy |
|
871 |
+ |
{ |
|
872 |
+ |
HORIZONTAL, BOTH, DISABLED |
|
873 |
+ |
} |
314 |
874 |
|
|
315 |
875 |
|
/** |
316 |
|
- |
* The columns under the colSpan of the current cell. |
|
876 |
+ |
* The sorting policies related to user column sorting. |
|
877 |
+ |
* <p/> |
|
878 |
+ |
* <ul> |
|
879 |
+ |
* <li>DISABLED - Columns cannot be sorted by the user</li> |
|
880 |
+ |
* <li>SINGLE_CELL - Only cells with a colspan of 1 can be sorted</li> |
|
881 |
+ |
* <li>MULTI_CELL - All cells can be sorted by the user</li> |
|
882 |
+ |
* </ul> |
317 |
883 |
|
*/ |
318 |
|
- |
private List<ColumnWidthInfo> curCells = new ArrayList<ColumnWidthInfo>(); |
|
884 |
+ |
public static enum SortPolicy |
|
885 |
+ |
{ |
|
886 |
+ |
DISABLED, SINGLE_CELL, MULTI_CELL |
|
887 |
+ |
} |
319 |
888 |
|
|
320 |
889 |
|
/** |
321 |
|
- |
* The index of the current header cell. |
|
890 |
+ |
* The div that wraps the table wrappers. |
322 |
891 |
|
*/ |
323 |
|
- |
private int curCellIndex = 0; |
|
892 |
+ |
private Element absoluteElem; |
324 |
893 |
|
|
325 |
894 |
|
/** |
326 |
|
- |
* The current x position of the mouse. |
|
895 |
+ |
* The helper class used to resize columns. |
327 |
896 |
|
*/ |
328 |
|
- |
private int mouseXCurrent = 0; |
|
897 |
+ |
private ColumnResizer columnResizer = new ColumnResizer(); |
329 |
898 |
|
|
330 |
899 |
|
/** |
331 |
|
- |
* The last x position of the mouse when we resized. |
|
900 |
+ |
* The policy applied to user actions that resize columns. |
332 |
901 |
|
*/ |
333 |
|
- |
private int mouseXLast = 0; |
|
902 |
+ |
private ColumnResizePolicy columnResizePolicy = ColumnResizePolicy.MULTI_CELL; |
334 |
903 |
|
|
335 |
904 |
|
/** |
336 |
|
- |
* The starting x position of the mouse when resizing a column. |
|
905 |
+ |
* The data table. |
337 |
906 |
|
*/ |
338 |
|
- |
private int mouseXStart = 0; |
|
907 |
+ |
private FixedWidthGrid dataTable; |
339 |
908 |
|
|
340 |
909 |
|
/** |
341 |
|
- |
* A timer used to resize the columns. As long as the timer is active, it |
342 |
|
- |
* will poll for the new row size and resize the columns. |
|
910 |
+ |
* The scrollable wrapper div around the data table. |
343 |
911 |
|
*/ |
344 |
|
- |
private Timer resizeTimer = new Timer() { |
345 |
|
- |
@Override |
346 |
|
- |
public void run() { |
347 |
|
- |
resizeColumn(); |
348 |
|
- |
schedule(100); |
349 |
|
- |
} |
350 |
|
- |
}; |
|
912 |
+ |
private Element dataWrapper; |
351 |
913 |
|
|
352 |
914 |
|
/** |
353 |
|
- |
* A boolean indicating that we are resizing the current header cell. |
|
915 |
+ |
* An image used to show a fill width button. |
354 |
916 |
|
*/ |
355 |
|
- |
private boolean resizing = false; |
|
917 |
+ |
private Image fillWidthImage; |
356 |
918 |
|
|
357 |
919 |
|
/** |
358 |
|
- |
* The index of the first column that will be sacrificed. |
|
920 |
+ |
* A spacer used to stretch the footerTable area so we can scroll past the |
|
921 |
+ |
* edge of the footer table. |
359 |
922 |
|
*/ |
360 |
|
- |
private int sacrificeCellIndex = -1; |
|
923 |
+ |
private Element footerSpacer = null; |
361 |
924 |
|
|
362 |
925 |
|
/** |
363 |
|
- |
* The cells that will be sacrificed so the current cells can be resized. |
|
926 |
+ |
* The footer table. |
364 |
927 |
|
*/ |
365 |
|
- |
private List<ColumnWidthInfo> sacrificeCells = new ArrayList<ColumnWidthInfo>(); |
|
928 |
+ |
private FixedWidthFlexTable footerTable = null; |
366 |
929 |
|
|
367 |
930 |
|
/** |
368 |
|
- |
* The table that this worker affects. |
|
931 |
+ |
* The non-scrollable wrapper div around the footer table. |
369 |
932 |
|
*/ |
370 |
|
- |
private AbstractScrollTable table = null; |
|
933 |
+ |
private Element footerWrapper = null; |
371 |
934 |
|
|
372 |
935 |
|
/** |
373 |
|
- |
* @return the current cell |
|
936 |
+ |
* A spacer used to stretch the headerTable area so we can scroll past the |
|
937 |
+ |
* edge of the header table. |
374 |
938 |
|
*/ |
375 |
|
- |
public Element getCurrentCell() { |
376 |
|
- |
return curCell; |
377 |
|
- |
} |
|
939 |
+ |
private Element headerSpacer; |
378 |
940 |
|
|
379 |
941 |
|
/** |
380 |
|
- |
* @return true if a header is currently being resized |
|
942 |
+ |
* The header table. |
381 |
943 |
|
*/ |
382 |
|
- |
public boolean isResizing() { |
383 |
|
- |
return resizing; |
384 |
|
- |
} |
385 |
944 |
|
|
|
945 |
+ |
private FixedWidthFlexTable headerTable = null; |
386 |
946 |
|
/** |
387 |
|
- |
* Resize the column on a mouse event. This method also marks the client as |
388 |
|
- |
* busy so we do not try to change the size repeatedly. |
389 |
|
- |
* |
390 |
|
- |
* @param event the mouse event |
|
947 |
+ |
* The non-scrollable wrapper div around the header table. |
391 |
948 |
|
*/ |
392 |
|
- |
public void resizeColumn(Event event) { |
393 |
|
- |
mouseXCurrent = DOM.eventGetClientX(event); |
394 |
|
- |
} |
|
949 |
+ |
private Element headerWrapper; |
395 |
950 |
|
|
396 |
951 |
|
/** |
397 |
|
- |
* Set the current cell that will be resized based on the mouse event. |
398 |
|
- |
* |
399 |
|
- |
* @param event the event that triggered the new cell |
400 |
|
- |
* @return true if the cell was actually changed |
|
952 |
+ |
* The images applied to the table. |
401 |
953 |
|
*/ |
402 |
|
- |
public boolean setCurrentCell(Event event) { |
403 |
|
- |
// Check the resize policy of the table |
404 |
|
- |
Element cell = null; |
405 |
|
- |
if (table.columnResizePolicy == ColumnResizePolicy.MULTI_CELL) { |
406 |
|
- |
cell = table.headerTable.getEventTargetCell(event); |
407 |
|
- |
} else if (table.columnResizePolicy == ColumnResizePolicy.SINGLE_CELL) { |
408 |
|
- |
cell = table.headerTable.getEventTargetCell(event); |
409 |
|
- |
if (cell != null && cell.getPropertyInt("colSpan") > 1) { |
410 |
|
- |
cell = null; |
411 |
|
- |
} |
412 |
|
- |
} |
|
954 |
+ |
private ScrollTableImages images; |
413 |
955 |
|
|
414 |
|
- |
// See if we are near the edge of the cell |
415 |
|
- |
int clientX = event.getClientX(); |
416 |
|
- |
if (cell != null) { |
417 |
|
- |
int absLeft = cell.getAbsoluteLeft() - Window.getScrollLeft(); |
418 |
|
- |
if (LocaleInfo.getCurrentLocale().isRTL()) { |
419 |
|
- |
if (clientX < absLeft || clientX > absLeft + RESIZE_CURSOR_WIDTH) { |
420 |
|
- |
cell = null; |
421 |
|
- |
} |
422 |
|
- |
} else { |
423 |
|
- |
int absRight = absLeft + cell.getOffsetWidth(); |
424 |
|
- |
if (clientX < absRight - RESIZE_CURSOR_WIDTH || clientX > absRight) { |
425 |
|
- |
cell = null; |
426 |
|
- |
} |
427 |
|
- |
} |
428 |
|
- |
} |
|
956 |
+ |
/** |
|
957 |
+ |
* The implementation class for this widget. |
|
958 |
+ |
*/ |
|
959 |
+ |
private Impl impl = GWT.create( Impl.class ); |
429 |
960 |
|
|
430 |
|
- |
// Change out the current cell |
431 |
|
- |
if (cell != curCell) { |
432 |
|
- |
// Clear the old cell |
433 |
|
- |
if (curCell != null) { |
434 |
|
- |
curCell.getStyle().setProperty("cursor", ""); |
435 |
|
- |
} |
|
961 |
+ |
/** |
|
962 |
+ |
* The last known height of this widget that the user set. |
|
963 |
+ |
*/ |
|
964 |
+ |
private String lastHeight = null; |
436 |
965 |
|
|
437 |
|
- |
// Set the new cell |
438 |
|
- |
curCell = cell; |
439 |
|
- |
if (curCell != null) { |
440 |
|
- |
// Check the cell index |
441 |
|
- |
curCellIndex = getCellIndex(curCell); |
442 |
|
- |
if (curCellIndex < 0) { |
443 |
|
- |
curCell = null; |
444 |
|
- |
return false; |
445 |
|
- |
} |
|
966 |
+ |
/** |
|
967 |
+ |
* The last scrollLeft position. |
|
968 |
+ |
*/ |
|
969 |
+ |
private int lastScrollLeft; |
446 |
970 |
|
|
447 |
|
- |
// Check for resizable columns within one of the cells in the colspan |
448 |
|
- |
boolean resizable = false; |
449 |
|
- |
int colSpan = cell.getPropertyInt("colSpan"); |
450 |
|
- |
curCells = table.getColumnWidthInfo(curCellIndex, colSpan); |
451 |
|
- |
for (ColumnWidthInfo info : curCells) { |
452 |
|
- |
if (!info.hasMaximumWidth() || !info.hasMinimumWidth() |
453 |
|
- |
|| info.getMaximumWidth() != info.getMinimumWidth()) { |
454 |
|
- |
resizable = true; |
455 |
|
- |
} |
456 |
|
- |
} |
457 |
|
- |
if (!resizable) { |
458 |
|
- |
curCell = null; |
459 |
|
- |
curCells = null; |
460 |
|
- |
return false; |
461 |
|
- |
} |
|
971 |
+ |
/** |
|
972 |
+ |
* An element used to determine the width of the scroll bar. |
|
973 |
+ |
*/ |
|
974 |
+ |
private com.google.gwt.dom.client.Element mockScrollable; |
|
975 |
+ |
|
|
976 |
+ |
/** |
|
977 |
+ |
* A boolean indicating whether or not the grid should try to maintain its |
|
978 |
+ |
* width as much as possible. |
|
979 |
+ |
*/ |
|
980 |
+ |
private ResizePolicy resizePolicy = ResizePolicy.FLOW; |
462 |
981 |
|
|
463 |
|
- |
// Update the cursor on the cell |
464 |
|
- |
curCell.getStyle().setProperty("cursor", "e-resize"); |
|
982 |
+ |
/** |
|
983 |
+ |
* The worker that helps with mouse resize events. |
|
984 |
+ |
*/ |
|
985 |
+ |
private MouseResizeWorker resizeWorker = GWT.create( MouseResizeWorker.class ); |
|
986 |
+ |
|
|
987 |
+ |
/** |
|
988 |
+ |
* The scrolling policy. |
|
989 |
+ |
*/ |
|
990 |
+ |
private ScrollPolicy scrollPolicy = ScrollPolicy.BOTH; |
|
991 |
+ |
|
|
992 |
+ |
/** |
|
993 |
+ |
* The current {@link SortPolicy}. |
|
994 |
+ |
*/ |
|
995 |
+ |
private SortPolicy sortPolicy = SortPolicy.SINGLE_CELL; |
|
996 |
+ |
|
|
997 |
+ |
/** |
|
998 |
+ |
* The cell index of the TD cell that initiated a column sort operation. |
|
999 |
+ |
*/ |
|
1000 |
+ |
private int sortedCellIndex = -1; |
|
1001 |
+ |
|
|
1002 |
+ |
/** |
|
1003 |
+ |
* The row index of the TD cell that initiated a column sort operation. |
|
1004 |
+ |
*/ |
|
1005 |
+ |
private int sortedRowIndex = -1; |
|
1006 |
+ |
|
|
1007 |
+ |
/** |
|
1008 |
+ |
* The wrapper around the image indicator. |
|
1009 |
+ |
*/ |
|
1010 |
+ |
private Element sortedColumnWrapper = null; |
|
1011 |
+ |
|
|
1012 |
+ |
/** |
|
1013 |
+ |
* Constructor. |
|
1014 |
+ |
* |
|
1015 |
+ |
* @param dataTable the data table |
|
1016 |
+ |
* @param headerTable the header table |
|
1017 |
+ |
*/ |
|
1018 |
+ |
public AbstractScrollTable( FixedWidthGrid dataTable, FixedWidthFlexTable headerTable ) |
|
1019 |
+ |
{ |
|
1020 |
+ |
this( dataTable, headerTable, (ScrollTableImages) GWT.create( ScrollTableImages.class ) ); |
|
1021 |
+ |
} |
|
1022 |
+ |
|
|
1023 |
+ |
/** |
|
1024 |
+ |
* Constructor. |
|
1025 |
+ |
* |
|
1026 |
+ |
* @param dataTable the data table |
|
1027 |
+ |
* @param headerTable the header table |
|
1028 |
+ |
* @param images the images to use in the table |
|
1029 |
+ |
*/ |
|
1030 |
+ |
public AbstractScrollTable( FixedWidthGrid dataTable, final FixedWidthFlexTable headerTable, ScrollTableImages images ) |
|
1031 |
+ |
{ |
|
1032 |
+ |
super(); |
|
1033 |
+ |
this.dataTable = dataTable; |
|
1034 |
+ |
this.headerTable = headerTable; |
|
1035 |
+ |
this.images = images; |
|
1036 |
+ |
resizeWorker.setScrollTable( this ); |
|
1037 |
+ |
|
|
1038 |
+ |
// Prepare the header and data tables |
|
1039 |
+ |
prepareTable( dataTable, "dataTable" ); |
|
1040 |
+ |
prepareTable( headerTable, "headerTable" ); |
|
1041 |
+ |
if ( dataTable.getSelectionPolicy().hasInputColumn() ) |
|
1042 |
+ |
{ |
|
1043 |
+ |
headerTable.setColumnWidth( 0, dataTable.getInputColumnWidth() ); |
465 |
1044 |
|
} |
466 |
|
- |
return true; |
467 |
|
- |
} |
468 |
1045 |
|
|
469 |
|
- |
// The cell did not change |
470 |
|
- |
return false; |
|
1046 |
+ |
// Create the main div container |
|
1047 |
+ |
Element mainElem = DOM.createDiv(); |
|
1048 |
+ |
setElement( mainElem ); |
|
1049 |
+ |
setStylePrimaryName( DEFAULT_STYLE_NAME ); |
|
1050 |
+ |
DOM.setStyleAttribute( mainElem, "padding", "0px" ); |
|
1051 |
+ |
DOM.setStyleAttribute( mainElem, "overflow", "hidden" ); |
|
1052 |
+ |
DOM.setStyleAttribute( mainElem, "position", "relative" ); |
|
1053 |
+ |
|
|
1054 |
+ |
// Wrap the table wrappers in another div |
|
1055 |
+ |
absoluteElem = DOM.createDiv(); |
|
1056 |
+ |
absoluteElem.getStyle().setProperty( "position", "absolute" ); |
|
1057 |
+ |
absoluteElem.getStyle().setProperty( "top", "0px" ); |
|
1058 |
+ |
absoluteElem.getStyle().setProperty( "left", "0px" ); |
|
1059 |
+ |
absoluteElem.getStyle().setProperty( "width", "100%" ); |
|
1060 |
+ |
absoluteElem.getStyle().setProperty( "padding", "0px" ); |
|
1061 |
+ |
absoluteElem.getStyle().setProperty( "margin", "0px" ); |
|
1062 |
+ |
absoluteElem.getStyle().setProperty( "border", "0px" ); |
|
1063 |
+ |
absoluteElem.getStyle().setProperty( "overflow", "hidden" ); |
|
1064 |
+ |
mainElem.appendChild( absoluteElem ); |
|
1065 |
+ |
|
|
1066 |
+ |
// Create the table wrapper and spacer |
|
1067 |
+ |
headerWrapper = createWrapper( "headerWrapper" ); |
|
1068 |
+ |
headerSpacer = impl.createSpacer( headerTable, headerWrapper ); |
|
1069 |
+ |
dataWrapper = createWrapper( "dataWrapper" ); |
|
1070 |
+ |
|
|
1071 |
+ |
// Create an element to determine the scroll bar width |
|
1072 |
+ |
mockScrollable = com.google.gwt.dom.client.Element.as( dataWrapper.cloneNode( false ) ); |
|
1073 |
+ |
mockScrollable.getStyle().setProperty( "position", "absolute" ); |
|
1074 |
+ |
mockScrollable.getStyle().setProperty( "top", "0px" ); |
|
1075 |
+ |
mockScrollable.getStyle().setProperty( "left", "0px" ); |
|
1076 |
+ |
mockScrollable.getStyle().setProperty( "width", "100px" ); |
|
1077 |
+ |
mockScrollable.getStyle().setProperty( "height", "100px" ); |
|
1078 |
+ |
mockScrollable.getStyle().setProperty( "visibility", "hidden" ); |
|
1079 |
+ |
mockScrollable.getStyle().setProperty( "overflow", "scroll" ); |
|
1080 |
+ |
mockScrollable.getStyle().setProperty( "zIndex", "-1" ); |
|
1081 |
+ |
absoluteElem.appendChild( mockScrollable ); |
|
1082 |
+ |
|
|
1083 |
+ |
// Create image to fill width |
|
1084 |
+ |
fillWidthImage = new Image() |
|
1085 |
+ |
{ |
|
1086 |
+ |
@Override |
|
1087 |
+ |
public void onBrowserEvent( Event event ) |
|
1088 |
+ |
{ |
|
1089 |
+ |
super.onBrowserEvent( event ); |
|
1090 |
+ |
if ( DOM.eventGetType( event ) == Event.ONCLICK ) |
|
1091 |
+ |
{ |
|
1092 |
+ |
fillWidth(); |
|
1093 |
+ |
} |
|
1094 |
+ |
} |
|
1095 |
+ |
}; |
|
1096 |
+ |
fillWidthImage.setTitle( "Shrink/Expand to fill visible area" ); |
|
1097 |
+ |
images.scrollTableFillWidth().applyTo( fillWidthImage ); |
|
1098 |
+ |
Element fillWidthImageElem = fillWidthImage.getElement(); |
|
1099 |
+ |
DOM.setStyleAttribute( fillWidthImageElem, "cursor", "pointer" ); |
|
1100 |
+ |
DOM.setStyleAttribute( fillWidthImageElem, "position", "absolute" ); |
|
1101 |
+ |
DOM.setStyleAttribute( fillWidthImageElem, "top", "0px" ); |
|
1102 |
+ |
DOM.setStyleAttribute( fillWidthImageElem, "right", "0px" ); |
|
1103 |
+ |
DOM.setStyleAttribute( fillWidthImageElem, "zIndex", "1" ); |
|
1104 |
+ |
add( fillWidthImage, getElement() ); |
|
1105 |
+ |
|
|
1106 |
+ |
// Adopt the header and data tables into the panel |
|
1107 |
+ |
adoptTable( headerTable, headerWrapper, 0 ); |
|
1108 |
+ |
adoptTable( dataTable, dataWrapper, 1 ); |
|
1109 |
+ |
|
|
1110 |
+ |
// Create the sort indicator Image |
|
1111 |
+ |
sortedColumnWrapper = DOM.createSpan(); |
|
1112 |
+ |
|
|
1113 |
+ |
// Add some event handling |
|
1114 |
+ |
sinkEvents( Event.ONMOUSEOUT ); |
|
1115 |
+ |
DOM.setEventListener( dataWrapper, this ); |
|
1116 |
+ |
DOM.sinkEvents( dataWrapper, Event.ONSCROLL ); |
|
1117 |
+ |
DOM.setEventListener( headerWrapper, this ); |
|
1118 |
+ |
DOM.sinkEvents( headerWrapper, Event.ONMOUSEMOVE | Event.ONMOUSEDOWN | Event.ONMOUSEUP | Event.ONCLICK ); |
|
1119 |
+ |
|
|
1120 |
+ |
// Listen for sorting events in the data table |
|
1121 |
+ |
dataTable.addColumnSortHandler( new ColumnSortHandler() |
|
1122 |
+ |
{ |
|
1123 |
+ |
public void onColumnSorted( ColumnSortEvent event ) |
|
1124 |
+ |
{ |
|
1125 |
+ |
// Get the primary column and sort order |
|
1126 |
+ |
int column = -1; |
|
1127 |
+ |
boolean ascending = true; |
|
1128 |
+ |
ColumnSortList sortList = event.getColumnSortList(); |
|
1129 |
+ |
if ( sortList != null ) |
|
1130 |
+ |
{ |
|
1131 |
+ |
column = sortList.getPrimaryColumn(); |
|
1132 |
+ |
ascending = sortList.isPrimaryAscending(); |
|
1133 |
+ |
} |
|
1134 |
+ |
|
|
1135 |
+ |
// Remove the sorted column indicator |
|
1136 |
+ |
if ( isColumnSortable( column ) ) |
|
1137 |
+ |
{ |
|
1138 |
+ |
Element parent = DOM.getParent( sortedColumnWrapper ); |
|
1139 |
+ |
if ( parent != null ) |
|
1140 |
+ |
{ |
|
1141 |
+ |
parent.removeChild( sortedColumnWrapper ); |
|
1142 |
+ |
} |
|
1143 |
+ |
|
|
1144 |
+ |
// Re-add the sorted column indicator |
|
1145 |
+ |
if ( column < 0 ) |
|
1146 |
+ |
{ |
|
1147 |
+ |
sortedCellIndex = -1; |
|
1148 |
+ |
sortedRowIndex = -1; |
|
1149 |
+ |
} |
|
1150 |
+ |
else if ( sortedCellIndex >= 0 && sortedRowIndex >= 0 && headerTable.getRowCount() > sortedRowIndex && headerTable.getCellCount( sortedRowIndex ) > sortedCellIndex ) |
|
1151 |
+ |
{ |
|
1152 |
+ |
CellFormatter formatter = headerTable.getCellFormatter(); |
|
1153 |
+ |
Element td = formatter.getElement( sortedRowIndex, sortedCellIndex ); |
|
1154 |
+ |
applySortedColumnIndicator( td, ascending ); |
|
1155 |
+ |
} |
|
1156 |
+ |
} |
|
1157 |
+ |
} |
|
1158 |
+ |
} ); |
|
1159 |
+ |
} |
|
1160 |
+ |
|
|
1161 |
+ |
public HandlerRegistration addScrollHandler( ScrollHandler handler ) |
|
1162 |
+ |
{ |
|
1163 |
+ |
return addHandler( ScrollEvent.TYPE, handler ); |
471 |
1164 |
|
} |
472 |
1165 |
|
|
473 |
1166 |
|
/** |
474 |
|
- |
* Set the ScrollTable table that this worker affects. |
475 |
|
- |
* |
476 |
|
- |
* @param table the scroll table |
|
1167 |
+ |
* Adjust all column widths so they take up the maximum amount of space |
|
1168 |
+ |
* without needing a horizontal scroll bar. The distribution will be |
|
1169 |
+ |
* proportional to the current width of each column. |
|
1170 |
+ |
* <p/> |
|
1171 |
+ |
* The {@link AbstractScrollTable} must be visible on the page for this method |
|
1172 |
+ |
* to work. |
477 |
1173 |
|
*/ |
478 |
|
- |
public void setScrollTable(AbstractScrollTable table) { |
479 |
|
- |
this.table = table; |
|
1174 |
+ |
public void fillWidth() |
|
1175 |
+ |
{ |
|
1176 |
+ |
List<ColumnWidthInfo> colWidths = getFillColumnWidths( null ); |
|
1177 |
+ |
applyNewColumnWidths( 0, colWidths, false ); |
|
1178 |
+ |
scrollTables( false ); |
480 |
1179 |
|
} |
481 |
1180 |
|
|
482 |
1181 |
|
/** |
483 |
|
- |
* Start resizing the current cell when the user clicks on the right edge of |
484 |
|
- |
* the cell. |
485 |
|
- |
* |
486 |
|
- |
* @param event the mouse event |
|
1182 |
+ |
* @return the cell padding of the tables, in pixels |
487 |
1183 |
|
*/ |
488 |
|
- |
public void startResizing(Event event) { |
489 |
|
- |
if (curCell != null) { |
490 |
|
- |
resizing = true; |
491 |
|
- |
mouseXStart = event.getClientX(); |
492 |
|
- |
mouseXLast = mouseXStart; |
493 |
|
- |
mouseXCurrent = mouseXStart; |
|
1184 |
+ |
public int getCellPadding() |
|
1185 |
+ |
{ |
|
1186 |
+ |
return dataTable.getCellPadding(); |
|
1187 |
+ |
} |
494 |
1188 |
|
|
495 |
|
- |
// Add the sacrifice cells |
496 |
|
- |
int numColumns = table.getDataTable().getColumnCount(); |
497 |
|
- |
int colSpan = curCell.getPropertyInt("colSpan"); |
498 |
|
- |
sacrificeCellIndex = curCellIndex + colSpan; |
499 |
|
- |
sacrificeCells = table.getColumnWidthInfo(sacrificeCellIndex, |
500 |
|
- |
numColumns - sacrificeCellIndex); |
|
1189 |
+ |
/** |
|
1190 |
+ |
* @return the cell spacing of the tables, in pixels |
|
1191 |
+ |
*/ |
|
1192 |
+ |
public int getCellSpacing() |
|
1193 |
+ |
{ |
|
1194 |
+ |
return dataTable.getCellSpacing(); |
|
1195 |
+ |
} |
501 |
1196 |
|
|
502 |
|
- |
// Start the timer and listen for changes |
503 |
|
- |
DOM.setCapture(table.headerWrapper); |
504 |
|
- |
resizeTimer.schedule(20); |
505 |
|
- |
} |
|
1197 |
+ |
/** |
|
1198 |
+ |
* @return the column resize policy |
|
1199 |
+ |
*/ |
|
1200 |
+ |
public ColumnResizePolicy getColumnResizePolicy() |
|
1201 |
+ |
{ |
|
1202 |
+ |
return columnResizePolicy; |
506 |
1203 |
|
} |
507 |
1204 |
|
|
508 |
1205 |
|
/** |
509 |
|
- |
* Stop resizing the current cell. |
510 |
|
- |
* |
511 |
|
- |
* @param event the mouse event |
|
1206 |
+ |
* Return the column width for a given column index. |
|
1207 |
+ |
* |
|
1208 |
+ |
* @param column the column index |
|
1209 |
+ |
* |
|
1210 |
+ |
* @return the column width in pixels |
512 |
1211 |
|
*/ |
513 |
|
- |
public void stopResizing(Event event) { |
514 |
|
- |
if (curCell != null && resizing) { |
515 |
|
- |
curCell.getStyle().setProperty("cursor", ""); |
516 |
|
- |
curCell = null; |
517 |
|
- |
resizing = false; |
518 |
|
- |
DOM.releaseCapture(table.headerWrapper); |
519 |
|
- |
resizeTimer.cancel(); |
520 |
|
- |
resizeColumn(); |
521 |
|
- |
curCells = null; |
522 |
|
- |
sacrificeCells = null; |
523 |
|
- |
table.resizeTablesVertically(); |
524 |
|
- |
} |
|
1212 |
+ |
public int getColumnWidth( int column ) |
|
1213 |
+ |
{ |
|
1214 |
+ |
return dataTable.getColumnWidth( column ); |
525 |
1215 |
|
} |
526 |
1216 |
|
|
527 |
1217 |
|
/** |
528 |
|
- |
* Get the scroll table. |
529 |
|
- |
* |
530 |
|
- |
* @return the scroll table |
|
1218 |
+ |
* @return the data table |
531 |
1219 |
|
*/ |
532 |
|
- |
protected AbstractScrollTable getScrollTable() { |
533 |
|
- |
return table; |
|
1220 |
+ |
public FixedWidthGrid getDataTable() |
|
1221 |
+ |
{ |
|
1222 |
+ |
return dataTable; |
534 |
1223 |
|
} |
535 |
1224 |
|
|
536 |
1225 |
|
/** |
537 |
|
- |
* Get the actual cell index of a cell in the header table. |
538 |
|
- |
* |
539 |
|
- |
* @param cell the cell element |
540 |
|
- |
* @return the cell index |
|
1226 |
+ |
* @return the footer table |
541 |
1227 |
|
*/ |
542 |
|
- |
private int getCellIndex(Element cell) { |
543 |
|
- |
int row = OverrideDOM.getRowIndex(DOM.getParent(cell)) - 1; |
544 |
|
- |
int column = OverrideDOM.getCellIndex(cell); |
545 |
|
- |
return table.headerTable.getColumnIndex(row, column) |
546 |
|
- |
- table.getHeaderOffset(); |
|
1228 |
+ |
public FixedWidthFlexTable getFooterTable() |
|
1229 |
+ |
{ |
|
1230 |
+ |
return footerTable; |
547 |
1231 |
|
} |
548 |
1232 |
|
|
549 |
1233 |
|
/** |
550 |
|
- |
* Helper method that actually sets the column size. This method is called |
551 |
|
- |
* periodically by a timer. |
|
1234 |
+ |
* @return the header table |
552 |
1235 |
|
*/ |
553 |
|
- |
private void resizeColumn() { |
554 |
|
- |
if (mouseXLast != mouseXCurrent) { |
555 |
|
- |
mouseXLast = mouseXCurrent; |
|
1236 |
+ |
public FixedWidthFlexTable getHeaderTable() |
|
1237 |
+ |
{ |
|
1238 |
+ |
return headerTable; |
|
1239 |
+ |
} |
556 |
1240 |
|
|
557 |
|
- |
// Distribute to the cells being resized |
558 |
|
- |
int totalDelta = mouseXCurrent - mouseXStart; |
559 |
|
- |
if (LocaleInfo.getCurrentLocale().isRTL()) { |
560 |
|
- |
totalDelta *= -1; |
561 |
|
- |
} |
562 |
|
- |
totalDelta -= table.columnResizer.distributeWidth(curCells, totalDelta); |
|
1241 |
+ |
/** |
|
1242 |
+ |
* Get the absolute maximum width of a column. |
|
1243 |
+ |
* |
|
1244 |
+ |
* @param column the column index |
|
1245 |
+ |
* |
|
1246 |
+ |
* @return the maximum allowable width of the column |
|
1247 |
+ |
*/ |
|
1248 |
+ |
public abstract int getMaximumColumnWidth( int column ); |
563 |
1249 |
|
|
564 |
|
- |
// Distribute to the sacrifice cells |
565 |
|
- |
if (table.resizePolicy.isSacrificial()) { |
566 |
|
- |
int remaining = table.columnResizer.distributeWidth(sacrificeCells, |
567 |
|
- |
-totalDelta); |
|
1250 |
+ |
/** |
|
1251 |
+ |
* Get the absolute minimum width of a column. |
|
1252 |
+ |
* |
|
1253 |
+ |
* @param column the column index |
|
1254 |
+ |
* |
|
1255 |
+ |
* @return the minimum allowable width of the column |
|
1256 |
+ |
*/ |
|
1257 |
+ |
public abstract int getMinimumColumnWidth( int column ); |
568 |
1258 |
|
|
569 |
|
- |
// We don't have enough to sacrifice, redistribute the width |
570 |
|
- |
if (remaining != 0 && table.resizePolicy.isFixedWidth()) { |
571 |
|
- |
totalDelta += remaining; |
572 |
|
- |
table.columnResizer.distributeWidth(curCells, totalDelta); |
573 |
|
- |
} |
|
1259 |
+ |
/** |
|
1260 |
+ |
* Get the minimum offset width of the largest inner table given the |
|
1261 |
+ |
* constraints on the minimum and ideal column widths. Note that this does not |
|
1262 |
+ |
* account for the vertical scroll bar. |
|
1263 |
+ |
* |
|
1264 |
+ |
* @return the tables minimum offset width, or -1 if it cannot be calculated |
|
1265 |
+ |
*/ |
|
1266 |
+ |
public int getMinimumOffsetWidth() |
|
1267 |
+ |
{ |
|
1268 |
+ |
if ( !isAttached() ) |
|
1269 |
+ |
{ |
|
1270 |
+ |
return -1; |
|
1271 |
+ |
} |
574 |
1272 |
|
|
575 |
|
- |
// Apply the widths to the sacrifice column |
576 |
|
- |
table.applyNewColumnWidths(sacrificeCellIndex, sacrificeCells, true); |
|
1273 |
+ |
// Determine the width and column count of the largest table |
|
1274 |
+ |
TableWidthInfo redrawInfo = new TableWidthInfo( true ); |
|
1275 |
+ |
maybeRecalculateIdealColumnWidths( null ); |
|
1276 |
+ |
if ( redrawInfo.availableWidth < 1 ) |
|
1277 |
+ |
{ |
|
1278 |
+ |
return -1; |
577 |
1279 |
|
} |
578 |
1280 |
|
|
579 |
|
- |
// Set the new widths |
580 |
|
- |
table.applyNewColumnWidths(curCellIndex, curCells, true); |
|
1281 |
+ |
int scrollWidth = 0; |
|
1282 |
+ |
int numColumns = 0; |
|
1283 |
+ |
{ |
|
1284 |
+ |
int numHeaderCols = headerTable.getColumnCount() - getHeaderOffset(); |
|
1285 |
+ |
int numDataCols = dataTable.getColumnCount(); |
|
1286 |
+ |
int numFooterCols = (footerTable == null) ? -1 : footerTable.getColumnCount() - getHeaderOffset(); |
|
1287 |
+ |
if ( numHeaderCols >= numDataCols && numHeaderCols >= numFooterCols ) |
|
1288 |
+ |
{ |
|
1289 |
+ |
numColumns = numHeaderCols; |
|
1290 |
+ |
scrollWidth = redrawInfo.headerTableWidth; |
|
1291 |
+ |
} |
|
1292 |
+ |
else if ( numFooterCols >= numDataCols && numFooterCols >= numHeaderCols ) |
|
1293 |
+ |
{ |
|
1294 |
+ |
numColumns = numFooterCols; |
|
1295 |
+ |
scrollWidth = redrawInfo.footerTableWidth; |
|
1296 |
+ |
} |
|
1297 |
+ |
else if ( numDataCols > 0 ) |
|
1298 |
+ |
{ |
|
1299 |
+ |
numColumns = numDataCols; |
|
1300 |
+ |
scrollWidth = redrawInfo.dataTableWidth; |
|
1301 |
+ |
} |
|
1302 |
+ |
} |
|
1303 |
+ |
if ( numColumns <= 0 ) |
|
1304 |
+ |
{ |
|
1305 |
+ |
return -1; |
|
1306 |
+ |
} |
581 |
1307 |
|
|
582 |
|
- |
// Scroll to table back into alignment |
583 |
|
- |
table.scrollTables(false); |
584 |
|
- |
} |
|
1308 |
+ |
// Calculate the available diff |
|
1309 |
+ |
List<ColumnWidthInfo> colWidthInfos = getColumnWidthInfo( 0, numColumns ); |
|
1310 |
+ |
return -columnResizer.distributeWidth( colWidthInfos, -scrollWidth ); |
585 |
1311 |
|
} |
586 |
|
- |
} |
587 |
1312 |
|
|
588 |
|
- |
/** |
589 |
|
- |
* The Opera version of the mouse worker fixes an Opera bug where the cursor |
590 |
|
- |
* isn't updated if the mouse is hovering over an element DOM object when its |
591 |
|
- |
* cursor style is changed. |
592 |
|
- |
*/ |
593 |
|
- |
@SuppressWarnings("unused") |
594 |
|
- |
private static class MouseResizeWorkerOpera extends MouseResizeWorker { |
595 |
1313 |
|
/** |
596 |
|
- |
* A div used to force the cursor to update. |
|
1314 |
+ |
* Get the preferred width of a column. |
|
1315 |
+ |
* |
|
1316 |
+ |
* @param column the column index |
|
1317 |
+ |
* |
|
1318 |
+ |
* @return the preferred width of the column |
597 |
1319 |
|
*/ |
598 |
|
- |
private Element cursorUpdateDiv; |
|
1320 |
+ |
public abstract int getPreferredColumnWidth( int column ); |
599 |
1321 |
|
|
600 |
1322 |
|
/** |
601 |
|
- |
* Constructor. |
|
1323 |
+ |
* @return the resize policy |
602 |
1324 |
|
*/ |
603 |
|
- |
public MouseResizeWorkerOpera() { |
604 |
|
- |
cursorUpdateDiv = DOM.createDiv(); |
605 |
|
- |
DOM.setStyleAttribute(cursorUpdateDiv, "position", "absolute"); |
|
1325 |
+ |
public ResizePolicy getResizePolicy() |
|
1326 |
+ |
{ |
|
1327 |
+ |
return resizePolicy; |
606 |
1328 |
|
} |
607 |
1329 |
|
|
608 |
1330 |
|
/** |
609 |
|
- |
* Set the current cell that will be resized based on the mouse event. |
610 |
|
- |
* |
611 |
|
- |
* @param event the event that triggered the new cell |
612 |
|
- |
* @return true if the cell was actually changed |
|
1331 |
+ |
* @return the current scroll policy |
613 |
1332 |
|
*/ |
614 |
|
- |
@Override |
615 |
|
- |
public boolean setCurrentCell(Event event) { |
616 |
|
- |
// Check if cursor update div is active |
617 |
|
- |
if (DOM.eventGetTarget(event) == cursorUpdateDiv) { |
618 |
|
- |
removeCursorUpdateDiv(); |
619 |
|
- |
return false; |
620 |
|
- |
} |
621 |
|
- |
|
622 |
|
- |
// Use the parent method |
623 |
|
- |
boolean cellChanged = super.setCurrentCell(event); |
624 |
|
- |
|
625 |
|
- |
// Position a div that forces a cursor redraw in Opera |
626 |
|
- |
if (cellChanged) { |
627 |
|
- |
DOM.setCapture(getScrollTable().headerWrapper); |
628 |
|
- |
DOM.setStyleAttribute(cursorUpdateDiv, "height", |
629 |
|
- |
(Window.getClientHeight() - 1) + "px"); |
630 |
|
- |
DOM.setStyleAttribute(cursorUpdateDiv, "width", |
631 |
|
- |
(Window.getClientWidth() - 1) + "px"); |
632 |
|
- |
DOM.setStyleAttribute(cursorUpdateDiv, "left", "0px"); |
633 |
|
- |
DOM.setStyleAttribute(cursorUpdateDiv, "top", "0px"); |
634 |
|
- |
DOM.appendChild(RootPanel.getBodyElement(), cursorUpdateDiv); |
635 |
|
- |
} |
636 |
|
- |
return cellChanged; |
|
1333 |
+ |
public ScrollPolicy getScrollPolicy() |
|
1334 |
+ |
{ |
|
1335 |
+ |
return scrollPolicy; |
637 |
1336 |
|
} |
638 |
1337 |
|
|
639 |
1338 |
|
/** |
640 |
|
- |
* Start resizing the current cell. |
641 |
|
- |
* |
642 |
|
- |
* @param event the mouse event |
|
1339 |
+ |
* @return the current sort policy |
643 |
1340 |
|
*/ |
644 |
|
- |
@Override |
645 |
|
- |
public void startResizing(Event event) { |
646 |
|
- |
removeCursorUpdateDiv(); |
647 |
|
- |
super.startResizing(event); |
648 |
|
- |
} |
649 |
|
- |
|
650 |
|
- |
/** |
651 |
|
- |
* Remove the cursor update div from the page. |
652 |
|
- |
*/ |
653 |
|
- |
private void removeCursorUpdateDiv() { |
654 |
|
- |
if (DOM.getCaptureElement() != null) { |
655 |
|
- |
DOM.removeChild(RootPanel.getBodyElement(), cursorUpdateDiv); |
656 |
|
- |
DOM.releaseCapture(getScrollTable().headerWrapper); |
657 |
|
- |
} |
658 |
|
- |
} |
659 |
|
- |
} |
660 |
|
- |
|
661 |
|
- |
/** |
662 |
|
- |
* Information about the height of the inner tables. |
663 |
|
- |
*/ |
664 |
|
- |
private class TableHeightInfo { |
665 |
|
- |
private int headerTableHeight; |
666 |
|
- |
private int dataTableHeight; |
667 |
|
- |
private int footerTableHeight; |
668 |
|
- |
|
669 |
|
- |
/** |
670 |
|
- |
* Construct a new {@link TableHeightInfo}. |
671 |
|
- |
*/ |
672 |
|
- |
public TableHeightInfo() { |
673 |
|
- |
int totalHeight = DOM.getElementPropertyInt(getElement(), "clientHeight"); |
674 |
|
- |
headerTableHeight = headerTable.getOffsetHeight(); |
675 |
|
- |
if (footerTable != null) { |
676 |
|
- |
footerTableHeight = footerTable.getOffsetHeight(); |
677 |
|
- |
} |
678 |
|
- |
dataTableHeight = totalHeight - headerTableHeight - footerTableHeight; |
679 |
|
- |
} |
680 |
|
- |
} |
681 |
|
- |
|
682 |
|
- |
/** |
683 |
|
- |
* Information about the width of the inner tables. |
684 |
|
- |
*/ |
685 |
|
- |
private class TableWidthInfo { |
686 |
|
- |
private int headerTableWidth; |
687 |
|
- |
private int dataTableWidth; |
688 |
|
- |
private int footerTableWidth; |
689 |
|
- |
private int availableWidth; |
690 |
|
- |
|
691 |
|
- |
/** |
692 |
|
- |
* Construct a new {@link TableWidthInfo}. |
693 |
|
- |
* |
694 |
|
- |
* @param includeSpacer true to include spacer in calculations |
695 |
|
- |
*/ |
696 |
|
- |
public TableWidthInfo(boolean includeSpacer) { |
697 |
|
- |
availableWidth = getAvailableWidth(); |
698 |
|
- |
headerTableWidth = impl.getTableWidth(headerTable, includeSpacer); |
699 |
|
- |
dataTableWidth = dataTable.getElement().getScrollWidth(); |
700 |
|
- |
if (footerTable != null) { |
701 |
|
- |
footerTableWidth = impl.getTableWidth(footerTable, includeSpacer); |
702 |
|
- |
} |
703 |
|
- |
} |
704 |
|
- |
} |
705 |
|
- |
|
706 |
|
- |
/** |
707 |
|
- |
* An {@link ImageBundle} that provides images for {@link AbstractScrollTable} |
708 |
|
- |
* . |
709 |
|
- |
*/ |
710 |
|
- |
public static interface ScrollTableImages extends ImageBundle { |
711 |
|
- |
/** |
712 |
|
- |
* An image used to fill the available width. |
713 |
|
- |
* |
714 |
|
- |
* @return a prototype of this image |
715 |
|
- |
*/ |
716 |
|
- |
AbstractImagePrototype scrollTableFillWidth(); |
717 |
|
- |
|
718 |
|
- |
/** |
719 |
|
- |
* An image indicating that a column is sorted in ascending order. |
720 |
|
- |
* |
721 |
|
- |
* @return a prototype of this image |
722 |
|
- |
*/ |
723 |
|
- |
AbstractImagePrototype scrollTableAscending(); |
724 |
|
- |
|
725 |
|
- |
/** |
726 |
|
- |
* An image indicating a column is sorted in descending order. |
727 |
|
- |
* |
728 |
|
- |
* @return a prototype of this image |
729 |
|
- |
*/ |
730 |
|
- |
AbstractImagePrototype scrollTableDescending(); |
731 |
|
- |
} |
732 |
|
- |
|
733 |
|
- |
/** |
734 |
|
- |
* The default style name. |
735 |
|
- |
*/ |
736 |
|
- |
public static final String DEFAULT_STYLE_NAME = "gwt-ScrollTable"; |
737 |
|
- |
|
738 |
|
- |
/** |
739 |
|
- |
* The resize policies related to user resizing. |
740 |
|
- |
* |
741 |
|
- |
* <ul> |
742 |
|
- |
* <li>DISABLED - Columns cannot be resized by the user</li> |
743 |
|
- |
* <li>SINGLE_CELL - Only cells with a colspan of 1 can be resized</li> |
744 |
|
- |
* <li>MULTI_CELL - All cells can be resized by the user</li> |
745 |
|
- |
* </ul> |
746 |
|
- |
*/ |
747 |
|
- |
public static enum ColumnResizePolicy { |
748 |
|
- |
DISABLED, SINGLE_CELL, MULTI_CELL |
749 |
|
- |
} |
750 |
|
- |
|
751 |
|
- |
/** |
752 |
|
- |
* The resize policies of table cells. |
753 |
|
- |
* |
754 |
|
- |
* <ul> |
755 |
|
- |
* <li>UNCONSTRAINED - Columns shrink and expand independently of each other</li> |
756 |
|
- |
* <li>FLOW - As one column expands or shrinks, the columns to the right will |
757 |
|
- |
* do the opposite, trying to maintain the same size. The user can still |
758 |
|
- |
* expand the grid if there is no more space to take from the columns on the |
759 |
|
- |
* right.</li> |
760 |
|
- |
* <li>FIXED_WIDTH - As one column expands or shrinks, the columns to the |
761 |
|
- |
* right will do the opposite, trying to maintain the same size. The width of |
762 |
|
- |
* the grid will remain constant, ignoring column resizing that would result |
763 |
|
- |
* in the grid growing in size.</li> |
764 |
|
- |
* <li>FILL_WIDTH - Same as FIXED_WIDTH, but the grid will always fill the |
765 |
|
- |
* available width, even if the widget is resized.</li> |
766 |
|
- |
* </ul> |
767 |
|
- |
*/ |
768 |
|
- |
public static enum ResizePolicy { |
769 |
|
- |
UNCONSTRAINED(false, false), FLOW(false, true), FIXED_WIDTH(true, true), FILL_WIDTH( |
770 |
|
- |
true, true); |
771 |
|
- |
|
772 |
|
- |
private boolean isSacrificial; |
773 |
|
- |
private boolean isFixedWidth; |
774 |
|
- |
|
775 |
|
- |
private ResizePolicy(boolean isFixedWidth, boolean isSacrificial) { |
776 |
|
- |
this.isFixedWidth = isFixedWidth; |
777 |
|
- |
this.isSacrificial = isSacrificial; |
778 |
|
- |
} |
779 |
|
- |
|
780 |
|
- |
private boolean isFixedWidth() { |
781 |
|
- |
return isFixedWidth; |
782 |
|
- |
} |
783 |
|
- |
|
784 |
|
- |
private boolean isSacrificial() { |
785 |
|
- |
return isSacrificial; |
786 |
|
- |
} |
787 |
|
- |
} |
788 |
|
- |
|
789 |
|
- |
/** |
790 |
|
- |
* The scroll policy of the table. |
791 |
|
- |
* |
792 |
|
- |
* <ul> |
793 |
|
- |
* <li>HORIZONTAL - Only a horizontal scrollbar will be present.</li> |
794 |
|
- |
* <li>BOTH - Both a vertical and horizontal scrollbar will be present.</li> |
795 |
|
- |
* <li>DISABLED - Scrollbars will not appear, even if content doesn't fit</li> |
796 |
|
- |
* </ul> |
797 |
|
- |
*/ |
798 |
|
- |
public static enum ScrollPolicy { |
799 |
|
- |
HORIZONTAL, BOTH, DISABLED |
800 |
|
- |
} |
801 |
|
- |
|
802 |
|
- |
/** |
803 |
|
- |
* The sorting policies related to user column sorting. |
804 |
|
- |
* |
805 |
|
- |
* <ul> |
806 |
|
- |
* <li>DISABLED - Columns cannot be sorted by the user</li> |
807 |
|
- |
* <li>SINGLE_CELL - Only cells with a colspan of 1 can be sorted</li> |
808 |
|
- |
* <li>MULTI_CELL - All cells can be sorted by the user</li> |
809 |
|
- |
* </ul> |
810 |
|
- |
*/ |
811 |
|
- |
public static enum SortPolicy { |
812 |
|
- |
DISABLED, SINGLE_CELL, MULTI_CELL |
813 |
|
- |
} |
814 |
|
- |
|
815 |
|
- |
/** |
816 |
|
- |
* The div that wraps the table wrappers. |
817 |
|
- |
*/ |
818 |
|
- |
private Element absoluteElem; |
819 |
|
- |
|
820 |
|
- |
/** |
821 |
|
- |
* The helper class used to resize columns. |
822 |
|
- |
*/ |
823 |
|
- |
private ColumnResizer columnResizer = new ColumnResizer(); |
824 |
|
- |
|
825 |
|
- |
/** |
826 |
|
- |
* The policy applied to user actions that resize columns. |
827 |
|
- |
*/ |
828 |
|
- |
private ColumnResizePolicy columnResizePolicy = ColumnResizePolicy.MULTI_CELL; |
829 |
|
- |
|
830 |
|
- |
/** |
831 |
|
- |
* The data table. |
832 |
|
- |
*/ |
833 |
|
- |
private FixedWidthGrid dataTable; |
834 |
|
- |
|
835 |
|
- |
/** |
836 |
|
- |
* The scrollable wrapper div around the data table. |
837 |
|
- |
*/ |
838 |
|
- |
private Element dataWrapper; |
839 |
|
- |
|
840 |
|
- |
/** |
841 |
|
- |
* An image used to show a fill width button. |
842 |
|
- |
*/ |
843 |
|
- |
private Image fillWidthImage; |
844 |
|
- |
|
845 |
|
- |
/** |
846 |
|
- |
* A spacer used to stretch the footerTable area so we can scroll past the |
847 |
|
- |
* edge of the footer table. |
848 |
|
- |
*/ |
849 |
|
- |
private Element footerSpacer = null; |
850 |
|
- |
|
851 |
|
- |
/** |
852 |
|
- |
* The footer table. |
853 |
|
- |
*/ |
854 |
|
- |
private FixedWidthFlexTable footerTable = null; |
855 |
|
- |
|
856 |
|
- |
/** |
857 |
|
- |
* The non-scrollable wrapper div around the footer table. |
858 |
|
- |
*/ |
859 |
|
- |
private Element footerWrapper = null; |
860 |
|
- |
|
861 |
|
- |
/** |
862 |
|
- |
* A spacer used to stretch the headerTable area so we can scroll past the |
863 |
|
- |
* edge of the header table. |
864 |
|
- |
*/ |
865 |
|
- |
private Element headerSpacer; |
866 |
|
- |
|
867 |
|
- |
/** |
868 |
|
- |
* The header table. |
869 |
|
- |
*/ |
870 |
|
- |
|
871 |
|
- |
private FixedWidthFlexTable headerTable = null; |
872 |
|
- |
/** |
873 |
|
- |
* The non-scrollable wrapper div around the header table. |
874 |
|
- |
*/ |
875 |
|
- |
private Element headerWrapper; |
876 |
|
- |
|
877 |
|
- |
/** |
878 |
|
- |
* The images applied to the table. |
879 |
|
- |
*/ |
880 |
|
- |
private ScrollTableImages images; |
881 |
|
- |
|
882 |
|
- |
/** |
883 |
|
- |
* The implementation class for this widget. |
884 |
|
- |
*/ |
885 |
|
- |
private Impl impl = GWT.create(Impl.class); |
886 |
|
- |
|
887 |
|
- |
/** |
888 |
|
- |
* The last known height of this widget that the user set. |
889 |
|
- |
*/ |
890 |
|
- |
private String lastHeight = null; |
891 |
|
- |
|
892 |
|
- |
/** |
893 |
|
- |
* The last scrollLeft position. |
894 |
|
- |
*/ |
895 |
|
- |
private int lastScrollLeft; |
896 |
|
- |
|
897 |
|
- |
/** |
898 |
|
- |
* An element used to determine the width of the scroll bar. |
899 |
|
- |
*/ |
900 |
|
- |
private com.google.gwt.dom.client.Element mockScrollable; |
901 |
|
- |
|
902 |
|
- |
/** |
903 |
|
- |
* A boolean indicating whether or not the grid should try to maintain its |
904 |
|
- |
* width as much as possible. |
905 |
|
- |
*/ |
906 |
|
- |
private ResizePolicy resizePolicy = ResizePolicy.FLOW; |
907 |
|
- |
|
908 |
|
- |
/** |
909 |
|
- |
* The worker that helps with mouse resize events. |
910 |
|
- |
*/ |
911 |
|
- |
private MouseResizeWorker resizeWorker = GWT.create(MouseResizeWorker.class); |
912 |
|
- |
|
913 |
|
- |
/** |
914 |
|
- |
* The scrolling policy. |
915 |
|
- |
*/ |
916 |
|
- |
private ScrollPolicy scrollPolicy = ScrollPolicy.BOTH; |
917 |
|
- |
|
918 |
|
- |
/** |
919 |
|
- |
* The current {@link SortPolicy}. |
920 |
|
- |
*/ |
921 |
|
- |
private SortPolicy sortPolicy = SortPolicy.SINGLE_CELL; |
922 |
|
- |
|
923 |
|
- |
/** |
924 |
|
- |
* The cell index of the TD cell that initiated a column sort operation. |
925 |
|
- |
*/ |
926 |
|
- |
private int sortedCellIndex = -1; |
927 |
|
- |
|
928 |
|
- |
/** |
929 |
|
- |
* The row index of the TD cell that initiated a column sort operation. |
930 |
|
- |
*/ |
931 |
|
- |
private int sortedRowIndex = -1; |
932 |
|
- |
|
933 |
|
- |
/** |
934 |
|
- |
* The wrapper around the image indicator. |
935 |
|
- |
*/ |
936 |
|
- |
private Element sortedColumnWrapper = null; |
937 |
|
- |
|
938 |
|
- |
/** |
939 |
|
- |
* Constructor. |
940 |
|
- |
* |
941 |
|
- |
* @param dataTable the data table |
942 |
|
- |
* @param headerTable the header table |
943 |
|
- |
*/ |
944 |
|
- |
public AbstractScrollTable(FixedWidthGrid dataTable, |
945 |
|
- |
FixedWidthFlexTable headerTable) { |
946 |
|
- |
this(dataTable, headerTable, |
947 |
|
- |
(ScrollTableImages) GWT.create(ScrollTableImages.class)); |
948 |
|
- |
} |
949 |
|
- |
|
950 |
|
- |
/** |
951 |
|
- |
* Constructor. |
952 |
|
- |
* |
953 |
|
- |
* @param dataTable the data table |
954 |
|
- |
* @param headerTable the header table |
955 |
|
- |
* @param images the images to use in the table |
956 |
|
- |
*/ |
957 |
|
- |
public AbstractScrollTable(FixedWidthGrid dataTable, |
958 |
|
- |
final FixedWidthFlexTable headerTable, ScrollTableImages images) { |
959 |
|
- |
super(); |
960 |
|
- |
this.dataTable = dataTable; |
961 |
|
- |
this.headerTable = headerTable; |
962 |
|
- |
this.images = images; |
963 |
|
- |
resizeWorker.setScrollTable(this); |
964 |
|
- |
|
965 |
|
- |
// Prepare the header and data tables |
966 |
|
- |
prepareTable(dataTable, "dataTable"); |
967 |
|
- |
prepareTable(headerTable, "headerTable"); |
968 |
|
- |
if (dataTable.getSelectionPolicy().hasInputColumn()) { |
969 |
|
- |
headerTable.setColumnWidth(0, dataTable.getInputColumnWidth()); |
970 |
|
- |
} |
971 |
|
- |
|
972 |
|
- |
// Create the main div container |
973 |
|
- |
Element mainElem = DOM.createDiv(); |
974 |
|
- |
setElement(mainElem); |
975 |
|
- |
setStylePrimaryName(DEFAULT_STYLE_NAME); |
976 |
|
- |
DOM.setStyleAttribute(mainElem, "padding", "0px"); |
977 |
|
- |
DOM.setStyleAttribute(mainElem, "overflow", "hidden"); |
978 |
|
- |
DOM.setStyleAttribute(mainElem, "position", "relative"); |
979 |
|
- |
|
980 |
|
- |
// Wrap the table wrappers in another div |
981 |
|
- |
absoluteElem = DOM.createDiv(); |
982 |
|
- |
absoluteElem.getStyle().setProperty("position", "absolute"); |
983 |
|
- |
absoluteElem.getStyle().setProperty("top", "0px"); |
984 |
|
- |
absoluteElem.getStyle().setProperty("left", "0px"); |
985 |
|
- |
absoluteElem.getStyle().setProperty("width", "100%"); |
986 |
|
- |
absoluteElem.getStyle().setProperty("padding", "0px"); |
987 |
|
- |
absoluteElem.getStyle().setProperty("margin", "0px"); |
988 |
|
- |
absoluteElem.getStyle().setProperty("border", "0px"); |
989 |
|
- |
absoluteElem.getStyle().setProperty("overflow", "hidden"); |
990 |
|
- |
mainElem.appendChild(absoluteElem); |
991 |
|
- |
|
992 |
|
- |
// Create the table wrapper and spacer |
993 |
|
- |
headerWrapper = createWrapper("headerWrapper"); |
994 |
|
- |
headerSpacer = impl.createSpacer(headerTable, headerWrapper); |
995 |
|
- |
dataWrapper = createWrapper("dataWrapper"); |
996 |
|
- |
|
997 |
|
- |
// Create an element to determine the scroll bar width |
998 |
|
- |
mockScrollable = com.google.gwt.dom.client.Element.as(dataWrapper.cloneNode(false)); |
999 |
|
- |
mockScrollable.getStyle().setProperty("position", "absolute"); |
1000 |
|
- |
mockScrollable.getStyle().setProperty("top", "0px"); |
1001 |
|
- |
mockScrollable.getStyle().setProperty("left", "0px"); |
1002 |
|
- |
mockScrollable.getStyle().setProperty("width", "100px"); |
1003 |
|
- |
mockScrollable.getStyle().setProperty("height", "100px"); |
1004 |
|
- |
mockScrollable.getStyle().setProperty("visibility", "hidden"); |
1005 |
|
- |
mockScrollable.getStyle().setProperty("overflow", "scroll"); |
1006 |
|
- |
mockScrollable.getStyle().setProperty("zIndex", "-1"); |
1007 |
|
- |
absoluteElem.appendChild(mockScrollable); |
1008 |
|
- |
|
1009 |
|
- |
// Create image to fill width |
1010 |
|
- |
fillWidthImage = new Image() { |
1011 |
|
- |
@Override |
1012 |
|
- |
public void onBrowserEvent(Event event) { |
1013 |
|
- |
super.onBrowserEvent(event); |
1014 |
|
- |
if (DOM.eventGetType(event) == Event.ONCLICK) { |
1015 |
|
- |
fillWidth(); |
1016 |
|
- |
} |
1017 |
|
- |
} |
1018 |
|
- |
}; |
1019 |
|
- |
fillWidthImage.setTitle("Shrink/Expand to fill visible area"); |
1020 |
|
- |
images.scrollTableFillWidth().applyTo(fillWidthImage); |
1021 |
|
- |
Element fillWidthImageElem = fillWidthImage.getElement(); |
1022 |
|
- |
DOM.setStyleAttribute(fillWidthImageElem, "cursor", "pointer"); |
1023 |
|
- |
DOM.setStyleAttribute(fillWidthImageElem, "position", "absolute"); |
1024 |
|
- |
DOM.setStyleAttribute(fillWidthImageElem, "top", "0px"); |
1025 |
|
- |
DOM.setStyleAttribute(fillWidthImageElem, "right", "0px"); |
1026 |
|
- |
DOM.setStyleAttribute(fillWidthImageElem, "zIndex", "1"); |
1027 |
|
- |
add(fillWidthImage, getElement()); |
1028 |
|
- |
|
1029 |
|
- |
// Adopt the header and data tables into the panel |
1030 |
|
- |
adoptTable(headerTable, headerWrapper, 0); |
1031 |
|
- |
adoptTable(dataTable, dataWrapper, 1); |
1032 |
|
- |
|
1033 |
|
- |
// Create the sort indicator Image |
1034 |
|
- |
sortedColumnWrapper = DOM.createSpan(); |
1035 |
|
- |
|
1036 |
|
- |
// Add some event handling |
1037 |
|
- |
sinkEvents(Event.ONMOUSEOUT); |
1038 |
|
- |
DOM.setEventListener(dataWrapper, this); |
1039 |
|
- |
DOM.sinkEvents(dataWrapper, Event.ONSCROLL); |
1040 |
|
- |
DOM.setEventListener(headerWrapper, this); |
1041 |
|
- |
DOM.sinkEvents(headerWrapper, Event.ONMOUSEMOVE | Event.ONMOUSEDOWN |
1042 |
|
- |
| Event.ONMOUSEUP | Event.ONCLICK); |
1043 |
|
- |
|
1044 |
|
- |
// Listen for sorting events in the data table |
1045 |
|
- |
dataTable.addColumnSortHandler(new ColumnSortHandler() { |
1046 |
|
- |
public void onColumnSorted(ColumnSortEvent event) { |
1047 |
|
- |
// Get the primary column and sort order |
1048 |
|
- |
int column = -1; |
1049 |
|
- |
boolean ascending = true; |
1050 |
|
- |
ColumnSortList sortList = event.getColumnSortList(); |
1051 |
|
- |
if (sortList != null) { |
1052 |
|
- |
column = sortList.getPrimaryColumn(); |
1053 |
|
- |
ascending = sortList.isPrimaryAscending(); |
1054 |
|
- |
} |
1055 |
|
- |
|
1056 |
|
- |
// Remove the sorted column indicator |
1057 |
|
- |
if (isColumnSortable(column)) { |
1058 |
|
- |
Element parent = DOM.getParent(sortedColumnWrapper); |
1059 |
|
- |
if (parent != null) { |
1060 |
|
- |
parent.removeChild(sortedColumnWrapper); |
1061 |
|
- |
} |
1062 |
|
- |
|
1063 |
|
- |
// Re-add the sorted column indicator |
1064 |
|
- |
if (column < 0) { |
1065 |
|
- |
sortedCellIndex = -1; |
1066 |
|
- |
sortedRowIndex = -1; |
1067 |
|
- |
} else if (sortedCellIndex >= 0 && sortedRowIndex >= 0 |
1068 |
|
- |
&& headerTable.getRowCount() > sortedRowIndex |
1069 |
|
- |
&& headerTable.getCellCount(sortedRowIndex) > sortedCellIndex) { |
1070 |
|
- |
CellFormatter formatter = headerTable.getCellFormatter(); |
1071 |
|
- |
Element td = formatter.getElement(sortedRowIndex, sortedCellIndex); |
1072 |
|
- |
applySortedColumnIndicator(td, ascending); |
1073 |
|
- |
} |
1074 |
|
- |
} |
1075 |
|
- |
} |
1076 |
|
- |
}); |
1077 |
|
- |
} |
1078 |
|
- |
|
1079 |
|
- |
public HandlerRegistration addScrollHandler(ScrollHandler handler) { |
1080 |
|
- |
return addHandler(ScrollEvent.TYPE, handler); |
1081 |
|
- |
} |
1082 |
|
- |
|
1083 |
|
- |
/** |
1084 |
|
- |
* Adjust all column widths so they take up the maximum amount of space |
1085 |
|
- |
* without needing a horizontal scroll bar. The distribution will be |
1086 |
|
- |
* proportional to the current width of each column. |
1087 |
|
- |
* |
1088 |
|
- |
* The {@link AbstractScrollTable} must be visible on the page for this method |
1089 |
|
- |
* to work. |
1090 |
|
- |
*/ |
1091 |
|
- |
public void fillWidth() { |
1092 |
|
- |
List<ColumnWidthInfo> colWidths = getFillColumnWidths(null); |
1093 |
|
- |
applyNewColumnWidths(0, colWidths, false); |
1094 |
|
- |
scrollTables(false); |
1095 |
|
- |
} |
1096 |
|
- |
|
1097 |
|
- |
/** |
1098 |
|
- |
* @return the cell padding of the tables, in pixels |
1099 |
|
- |
*/ |
1100 |
|
- |
public int getCellPadding() { |
1101 |
|
- |
return dataTable.getCellPadding(); |
1102 |
|
- |
} |
1103 |
|
- |
|
1104 |
|
- |
/** |
1105 |
|
- |
* @return the cell spacing of the tables, in pixels |
1106 |
|
- |
*/ |
1107 |
|
- |
public int getCellSpacing() { |
1108 |
|
- |
return dataTable.getCellSpacing(); |
1109 |
|
- |
} |
1110 |
|
- |
|
1111 |
|
- |
/** |
1112 |
|
- |
* @return the column resize policy |
1113 |
|
- |
*/ |
1114 |
|
- |
public ColumnResizePolicy getColumnResizePolicy() { |
1115 |
|
- |
return columnResizePolicy; |
1116 |
|
- |
} |
1117 |
|
- |
|
1118 |
|
- |
/** |
1119 |
|
- |
* Return the column width for a given column index. |
1120 |
|
- |
* |
1121 |
|
- |
* @param column the column index |
1122 |
|
- |
* @return the column width in pixels |
1123 |
|
- |
*/ |
1124 |
|
- |
public int getColumnWidth(int column) { |
1125 |
|
- |
return dataTable.getColumnWidth(column); |
1126 |
|
- |
} |
1127 |
|
- |
|
1128 |
|
- |
/** |
1129 |
|
- |
* @return the data table |
1130 |
|
- |
*/ |
1131 |
|
- |
public FixedWidthGrid getDataTable() { |
1132 |
|
- |
return dataTable; |
1133 |
|
- |
} |
1134 |
|
- |
|
1135 |
|
- |
/** |
1136 |
|
- |
* @return the footer table |
1137 |
|
- |
*/ |
1138 |
|
- |
public FixedWidthFlexTable getFooterTable() { |
1139 |
|
- |
return footerTable; |
1140 |
|
- |
} |
1141 |
|
- |
|
1142 |
|
- |
/** |
1143 |
|
- |
* @return the header table |
1144 |
|
- |
*/ |
1145 |
|
- |
public FixedWidthFlexTable getHeaderTable() { |
1146 |
|
- |
return headerTable; |
1147 |
|
- |
} |
1148 |
|
- |
|
1149 |
|
- |
/** |
1150 |
|
- |
* Get the absolute maximum width of a column. |
1151 |
|
- |
* |
1152 |
|
- |
* @param column the column index |
1153 |
|
- |
* @return the maximum allowable width of the column |
1154 |
|
- |
*/ |
1155 |
|
- |
public abstract int getMaximumColumnWidth(int column); |
1156 |
|
- |
|
1157 |
|
- |
/** |
1158 |
|
- |
* Get the absolute minimum width of a column. |
1159 |
|
- |
* |
1160 |
|
- |
* @param column the column index |
1161 |
|
- |
* @return the minimum allowable width of the column |
1162 |
|
- |
*/ |
1163 |
|
- |
public abstract int getMinimumColumnWidth(int column); |
1164 |
|
- |
|
1165 |
|
- |
/** |
1166 |
|
- |
* Get the minimum offset width of the largest inner table given the |
1167 |
|
- |
* constraints on the minimum and ideal column widths. Note that this does not |
1168 |
|
- |
* account for the vertical scroll bar. |
1169 |
|
- |
* |
1170 |
|
- |
* @return the tables minimum offset width, or -1 if it cannot be calculated |
1171 |
|
- |
*/ |
1172 |
|
- |
public int getMinimumOffsetWidth() { |
1173 |
|
- |
if (!isAttached()) { |
1174 |
|
- |
return -1; |
1175 |
|
- |
} |
1176 |
|
- |
|
1177 |
|
- |
// Determine the width and column count of the largest table |
1178 |
|
- |
TableWidthInfo redrawInfo = new TableWidthInfo(true); |
1179 |
|
- |
maybeRecalculateIdealColumnWidths(null); |
1180 |
|
- |
if (redrawInfo.availableWidth < 1) { |
1181 |
|
- |
return -1; |
1182 |
|
- |
} |
1183 |
|
- |
|
1184 |
|
- |
int scrollWidth = 0; |
1185 |
|
- |
int numColumns = 0; |
1186 |
|
- |
{ |
1187 |
|
- |
int numHeaderCols = headerTable.getColumnCount() - getHeaderOffset(); |
1188 |
|
- |
int numDataCols = dataTable.getColumnCount(); |
1189 |
|
- |
int numFooterCols = (footerTable == null) ? -1 |
1190 |
|
- |
: footerTable.getColumnCount() - getHeaderOffset(); |
1191 |
|
- |
if (numHeaderCols >= numDataCols && numHeaderCols >= numFooterCols) { |
1192 |
|
- |
numColumns = numHeaderCols; |
1193 |
|
- |
scrollWidth = redrawInfo.headerTableWidth; |
1194 |
|
- |
} else if (numFooterCols >= numDataCols && numFooterCols >= numHeaderCols) { |
1195 |
|
- |
numColumns = numFooterCols; |
1196 |
|
- |
scrollWidth = redrawInfo.footerTableWidth; |
1197 |
|
- |
} else if (numDataCols > 0) { |
1198 |
|
- |
numColumns = numDataCols; |
1199 |
|
- |
scrollWidth = redrawInfo.dataTableWidth; |
1200 |
|
- |
} |
1201 |
|
- |
} |
1202 |
|
- |
if (numColumns <= 0) { |
1203 |
|
- |
return -1; |
1204 |
|
- |
} |
1205 |
|
- |
|
1206 |
|
- |
// Calculate the available diff |
1207 |
|
- |
List<ColumnWidthInfo> colWidthInfos = getColumnWidthInfo(0, numColumns); |
1208 |
|
- |
return -columnResizer.distributeWidth(colWidthInfos, -scrollWidth); |
1209 |
|
- |
} |
1210 |
|
- |
|
1211 |
|
- |
/** |
1212 |
|
- |
* Get the preferred width of a column. |
1213 |
|
- |
* |
1214 |
|
- |
* @param column the column index |
1215 |
|
- |
* @return the preferred width of the column |
1216 |
|
- |
*/ |
1217 |
|
- |
public abstract int getPreferredColumnWidth(int column); |
1218 |
|
- |
|
1219 |
|
- |
/** |
1220 |
|
- |
* @return the resize policy |
1221 |
|
- |
*/ |
1222 |
|
- |
public ResizePolicy getResizePolicy() { |
1223 |
|
- |
return resizePolicy; |
1224 |
|
- |
} |
1225 |
|
- |
|
1226 |
|
- |
/** |
1227 |
|
- |
* @return the current scroll policy |
1228 |
|
- |
*/ |
1229 |
|
- |
public ScrollPolicy getScrollPolicy() { |
1230 |
|
- |
return scrollPolicy; |
1231 |
|
- |
} |
1232 |
|
- |
|
1233 |
|
- |
/** |
1234 |
|
- |
* @return the current sort policy |
1235 |
|
- |
*/ |
1236 |
|
- |
public SortPolicy getSortPolicy() { |
1237 |
|
- |
return sortPolicy; |
1238 |
|
- |
} |
1239 |
|
- |
|
1240 |
|
- |
/** |
1241 |
|
- |
* Returns true if the specified column is sortable. |
1242 |
|
- |
* |
1243 |
|
- |
* @param column the column index |
1244 |
|
- |
* @return true if the column is sortable, false if it is not sortable |
1245 |
|
- |
*/ |
1246 |
|
- |
public abstract boolean isColumnSortable(int column); |
1247 |
|
- |
|
1248 |
|
- |
/** |
1249 |
|
- |
* Returns true if the specified column can be truncated. If it cannot be |
1250 |
|
- |
* truncated, its minimum width will be adjusted to ensure the cell content is |
1251 |
|
- |
* visible. |
1252 |
|
- |
* |
1253 |
|
- |
* @param column the column index |
1254 |
|
- |
* @return true if the column is truncatable, false if it is not |
1255 |
|
- |
*/ |
1256 |
|
- |
public abstract boolean isColumnTruncatable(int column); |
1257 |
|
- |
|
1258 |
|
- |
/** |
1259 |
|
- |
* Returns true if the specified column in the footer table can be truncated. |
1260 |
|
- |
* If it cannot be truncated, its minimum width will be adjusted to ensure the |
1261 |
|
- |
* cell content is visible. |
1262 |
|
- |
* |
1263 |
|
- |
* @param column the column index |
1264 |
|
- |
* @return true if the column is truncatable, false if it is not |
1265 |
|
- |
*/ |
1266 |
|
- |
public abstract boolean isFooterColumnTruncatable(int column); |
1267 |
|
- |
|
1268 |
|
- |
/** |
1269 |
|
- |
* Returns true if the specified column in the header table can be truncated. |
1270 |
|
- |
* If it cannot be truncated, its minimum width will be adjusted to ensure the |
1271 |
|
- |
* cell content is visible. |
1272 |
|
- |
* |
1273 |
|
- |
* @param column the column index |
1274 |
|
- |
* @return true if the column is truncatable, false if it is not |
1275 |
|
- |
*/ |
1276 |
|
- |
public abstract boolean isHeaderColumnTruncatable(int column); |
1277 |
|
- |
|
1278 |
|
- |
@Override |
1279 |
|
- |
public void onBrowserEvent(Event event) { |
1280 |
|
- |
super.onBrowserEvent(event); |
1281 |
|
- |
Element target = DOM.eventGetTarget(event); |
1282 |
|
- |
switch (DOM.eventGetType(event)) { |
1283 |
|
- |
case Event.ONSCROLL: |
1284 |
|
- |
// Reposition the tables on scroll |
1285 |
|
- |
lastScrollLeft = dataWrapper.getScrollLeft(); |
1286 |
|
- |
scrollTables(false); |
1287 |
|
- |
if (dataWrapper.isOrHasChild(target)) { |
1288 |
|
- |
fireEvent(new ScrollEvent(event)); |
1289 |
|
- |
} |
1290 |
|
- |
break; |
1291 |
|
- |
|
1292 |
|
- |
case Event.ONMOUSEDOWN: |
1293 |
|
- |
// Start resizing a header column |
1294 |
|
- |
if (DOM.eventGetButton(event) != Event.BUTTON_LEFT) { |
1295 |
|
- |
return; |
1296 |
|
- |
} |
1297 |
|
- |
if (resizeWorker.getCurrentCell() != null) { |
1298 |
|
- |
DOM.eventPreventDefault(event); |
1299 |
|
- |
DOM.eventCancelBubble(event, true); |
1300 |
|
- |
resizeWorker.startResizing(event); |
1301 |
|
- |
} |
1302 |
|
- |
break; |
1303 |
|
- |
case Event.ONMOUSEUP: |
1304 |
|
- |
if (DOM.eventGetButton(event) != Event.BUTTON_LEFT) { |
1305 |
|
- |
return; |
1306 |
|
- |
} |
1307 |
|
- |
// Stop resizing the header column |
1308 |
|
- |
if (resizeWorker.isResizing()) { |
1309 |
|
- |
resizeWorker.stopResizing(event); |
1310 |
|
- |
} else { |
1311 |
|
- |
// Scroll tables if needed |
1312 |
|
- |
if (DOM.isOrHasChild(headerWrapper, target)) { |
1313 |
|
- |
scrollTables(true); |
1314 |
|
- |
} else { |
1315 |
|
- |
scrollTables(false); |
1316 |
|
- |
} |
1317 |
|
- |
|
1318 |
|
- |
// Get the actual column index |
1319 |
|
- |
Element cellElem = headerTable.getEventTargetCell(event); |
1320 |
|
- |
if (cellElem != null) { |
1321 |
|
- |
// Sorting is disabled |
1322 |
|
- |
if (sortPolicy == SortPolicy.DISABLED) { |
1323 |
|
- |
return; |
1324 |
|
- |
} |
1325 |
|
- |
|
1326 |
|
- |
// Check the colSpan |
1327 |
|
- |
int colSpan = cellElem.getPropertyInt("colSpan"); |
1328 |
|
- |
if (colSpan > 1 && getSortPolicy() != SortPolicy.MULTI_CELL) { |
1329 |
|
- |
return; |
1330 |
|
- |
} |
1331 |
|
- |
|
1332 |
|
- |
// Sort the column |
1333 |
|
- |
sortedRowIndex = OverrideDOM.getRowIndex(DOM.getParent(cellElem)) - 1; |
1334 |
|
- |
sortedCellIndex = OverrideDOM.getCellIndex(cellElem); |
1335 |
|
- |
int column = headerTable.getColumnIndex(sortedRowIndex, |
1336 |
|
- |
sortedCellIndex) |
1337 |
|
- |
- getHeaderOffset(); |
1338 |
|
- |
if (column >= 0 && isColumnSortable(column)) { |
1339 |
|
- |
if (dataTable.getColumnCount() > column |
1340 |
|
- |
&& onHeaderSort(sortedRowIndex, column)) { |
1341 |
|
- |
dataTable.sortColumn(column); |
1342 |
|
- |
} |
1343 |
|
- |
} |
1344 |
|
- |
} |
1345 |
|
- |
} |
1346 |
|
- |
break; |
1347 |
|
- |
case Event.ONMOUSEMOVE: |
1348 |
|
- |
// Resize the header column |
1349 |
|
- |
if (resizeWorker.isResizing()) { |
1350 |
|
- |
resizeWorker.resizeColumn(event); |
1351 |
|
- |
} else { |
1352 |
|
- |
resizeWorker.setCurrentCell(event); |
1353 |
|
- |
} |
1354 |
|
- |
break; |
1355 |
|
- |
case Event.ONMOUSEOUT: |
1356 |
|
- |
// Unhighlight if the mouse leaves the table |
1357 |
|
- |
Element toElem = DOM.eventGetToElement(event); |
1358 |
|
- |
if (toElem == null || !dataWrapper.isOrHasChild(toElem)) { |
1359 |
|
- |
// Check that the coordinates are not directly over the table |
1360 |
|
- |
int clientX = event.getClientX() + Window.getScrollLeft(); |
1361 |
|
- |
int clientY = event.getClientY() + Window.getScrollTop(); |
1362 |
|
- |
int tableLeft = dataWrapper.getAbsoluteLeft(); |
1363 |
|
- |
int tableTop = dataWrapper.getAbsoluteTop(); |
1364 |
|
- |
int tableWidth = dataWrapper.getOffsetWidth(); |
1365 |
|
- |
int tableHeight = dataWrapper.getOffsetHeight(); |
1366 |
|
- |
int tableBottom = tableTop + tableHeight; |
1367 |
|
- |
int tableRight = tableLeft + tableWidth; |
1368 |
|
- |
if (clientX > tableLeft && clientX < tableRight && clientY > tableTop |
1369 |
|
- |
&& clientY < tableBottom) { |
1370 |
|
- |
return; |
1371 |
|
- |
} |
|
1341 |
+ |
public SortPolicy getSortPolicy() |
|
1342 |
+ |
{ |
|
1343 |
+ |
return sortPolicy; |
|
1344 |
+ |
} |
|
1345 |
+ |
|
|
1346 |
+ |
/** |
|
1347 |
+ |
* Returns true if the specified column is sortable. |
|
1348 |
+ |
* |
|
1349 |
+ |
* @param column the column index |
|
1350 |
+ |
* |
|
1351 |
+ |
* @return true if the column is sortable, false if it is not sortable |
|
1352 |
+ |
*/ |
|
1353 |
+ |
public abstract boolean isColumnSortable( int column ); |
|
1354 |
+ |
|
|
1355 |
+ |
/** |
|
1356 |
+ |
* Returns true if the specified column can be truncated. If it cannot be |
|
1357 |
+ |
* truncated, its minimum width will be adjusted to ensure the cell content is |
|
1358 |
+ |
* visible. |
|
1359 |
+ |
* |
|
1360 |
+ |
* @param column the column index |
|
1361 |
+ |
* |
|
1362 |
+ |
* @return true if the column is truncatable, false if it is not |
|
1363 |
+ |
*/ |
|
1364 |
+ |
public abstract boolean isColumnTruncatable( int column ); |
|
1365 |
+ |
|
|
1366 |
+ |
/** |
|
1367 |
+ |
* Returns true if the specified column in the footer table can be truncated. |
|
1368 |
+ |
* If it cannot be truncated, its minimum width will be adjusted to ensure the |
|
1369 |
+ |
* cell content is visible. |
|
1370 |
+ |
* |
|
1371 |
+ |
* @param column the column index |
|
1372 |
+ |
* |
|
1373 |
+ |
* @return true if the column is truncatable, false if it is not |
|
1374 |
+ |
*/ |
|
1375 |
+ |
public abstract boolean isFooterColumnTruncatable( int column ); |
1372 |
1376 |
|
|
1373 |
|
- |
dataTable.highlightCell(null); |
|
1377 |
+ |
/** |
|
1378 |
+ |
* Returns true if the specified column in the header table can be truncated. |
|
1379 |
+ |
* If it cannot be truncated, its minimum width will be adjusted to ensure the |
|
1380 |
+ |
* cell content is visible. |
|
1381 |
+ |
* |
|
1382 |
+ |
* @param column the column index |
|
1383 |
+ |
* |
|
1384 |
+ |
* @return true if the column is truncatable, false if it is not |
|
1385 |
+ |
*/ |
|
1386 |
+ |
public abstract boolean isHeaderColumnTruncatable( int column ); |
|
1387 |
+ |
|
|
1388 |
+ |
@Override |
|
1389 |
+ |
public void onBrowserEvent( Event event ) |
|
1390 |
+ |
{ |
|
1391 |
+ |
super.onBrowserEvent( event ); |
|
1392 |
+ |
Element target = DOM.eventGetTarget( event ); |
|
1393 |
+ |
switch ( DOM.eventGetType( event ) ) |
|
1394 |
+ |
{ |
|
1395 |
+ |
case Event.ONSCROLL: |
|
1396 |
+ |
// Reposition the tables on scroll |
|
1397 |
+ |
lastScrollLeft = dataWrapper.getScrollLeft(); |
|
1398 |
+ |
scrollTables( false ); |
|
1399 |
+ |
if ( dataWrapper.isOrHasChild( target ) ) |
|
1400 |
+ |
{ |
|
1401 |
+ |
fireEvent( new ScrollEvent( event ) ); |
|
1402 |
+ |
} |
|
1403 |
+ |
break; |
|
1404 |
+ |
|
|
1405 |
+ |
case Event.ONMOUSEDOWN: |
|
1406 |
+ |
// Start resizing a header column |
|
1407 |
+ |
if ( DOM.eventGetButton( event ) != Event.BUTTON_LEFT ) |
|
1408 |
+ |
{ |
|
1409 |
+ |
return; |
|
1410 |
+ |
} |
|
1411 |
+ |
if ( resizeWorker.getCurrentCell() != null ) |
|
1412 |
+ |
{ |
|
1413 |
+ |
DOM.eventPreventDefault( event ); |
|
1414 |
+ |
DOM.eventCancelBubble( event, true ); |
|
1415 |
+ |
resizeWorker.startResizing( event ); |
|
1416 |
+ |
} |
|
1417 |
+ |
break; |
|
1418 |
+ |
case Event.ONMOUSEUP: |
|
1419 |
+ |
if ( DOM.eventGetButton( event ) != Event.BUTTON_LEFT ) |
|
1420 |
+ |
{ |
|
1421 |
+ |
return; |
|
1422 |
+ |
} |
|
1423 |
+ |
// Stop resizing the header column |
|
1424 |
+ |
if ( resizeWorker.isResizing() ) |
|
1425 |
+ |
{ |
|
1426 |
+ |
resizeWorker.stopResizing( event ); |
|
1427 |
+ |
} |
|
1428 |
+ |
else |
|
1429 |
+ |
{ |
|
1430 |
+ |
// Scroll tables if needed |
|
1431 |
+ |
if ( DOM.isOrHasChild( headerWrapper, target ) ) |
|
1432 |
+ |
{ |
|
1433 |
+ |
scrollTables( true ); |
|
1434 |
+ |
} |
|
1435 |
+ |
else |
|
1436 |
+ |
{ |
|
1437 |
+ |
scrollTables( false ); |
|
1438 |
+ |
} |
|
1439 |
+ |
|
|
1440 |
+ |
// Get the actual column index |
|
1441 |
+ |
Element cellElem = headerTable.getEventTargetCell( event ); |
|
1442 |
+ |
if ( cellElem != null ) |
|
1443 |
+ |
{ |
|
1444 |
+ |
// Sorting is disabled |
|
1445 |
+ |
if ( sortPolicy == SortPolicy.DISABLED ) |
|
1446 |
+ |
{ |
|
1447 |
+ |
return; |
|
1448 |
+ |
} |
|
1449 |
+ |
|
|
1450 |
+ |
// Check the colSpan |
|
1451 |
+ |
int colSpan = cellElem.getPropertyInt( "colSpan" ); |
|
1452 |
+ |
if ( colSpan > 1 && getSortPolicy() != SortPolicy.MULTI_CELL ) |
|
1453 |
+ |
{ |
|
1454 |
+ |
return; |
|
1455 |
+ |
} |
|
1456 |
+ |
|
|
1457 |
+ |
// Sort the column |
|
1458 |
+ |
sortedRowIndex = OverrideDOM.getRowIndex( DOM.getParent( cellElem ) ) - 1; |
|
1459 |
+ |
sortedCellIndex = OverrideDOM.getCellIndex( cellElem ); |
|
1460 |
+ |
int column = headerTable.getColumnIndex( sortedRowIndex, sortedCellIndex ) - getHeaderOffset(); |
|
1461 |
+ |
if ( column >= 0 && isColumnSortable( column ) ) |
|
1462 |
+ |
{ |
|
1463 |
+ |
if ( dataTable.getColumnCount() > column && onHeaderSort( sortedRowIndex, column ) ) |
|
1464 |
+ |
{ |
|
1465 |
+ |
dataTable.sortColumn( column ); |
|
1466 |
+ |
} |
|
1467 |
+ |
} |
|
1468 |
+ |
} |
|
1469 |
+ |
} |
|
1470 |
+ |
break; |
|
1471 |
+ |
case Event.ONMOUSEMOVE: |
|
1472 |
+ |
// Resize the header column |
|
1473 |
+ |
if ( resizeWorker.isResizing() ) |
|
1474 |
+ |
{ |
|
1475 |
+ |
resizeWorker.resizeColumn( event ); |
|
1476 |
+ |
} |
|
1477 |
+ |
else |
|
1478 |
+ |
{ |
|
1479 |
+ |
resizeWorker.setCurrentCell( event ); |
|
1480 |
+ |
} |
|
1481 |
+ |
break; |
|
1482 |
+ |
case Event.ONMOUSEOUT: |
|
1483 |
+ |
// Unhighlight if the mouse leaves the table |
|
1484 |
+ |
Element toElem = DOM.eventGetToElement( event ); |
|
1485 |
+ |
if ( toElem == null || !dataWrapper.isOrHasChild( toElem ) ) |
|
1486 |
+ |
{ |
|
1487 |
+ |
// Check that the coordinates are not directly over the table |
|
1488 |
+ |
int clientX = event.getClientX() + Window.getScrollLeft(); |
|
1489 |
+ |
int clientY = event.getClientY() + Window.getScrollTop(); |
|
1490 |
+ |
int tableLeft = dataWrapper.getAbsoluteLeft(); |
|
1491 |
+ |
int tableTop = dataWrapper.getAbsoluteTop(); |
|
1492 |
+ |
int tableWidth = dataWrapper.getOffsetWidth(); |
|
1493 |
+ |
int tableHeight = dataWrapper.getOffsetHeight(); |
|
1494 |
+ |
int tableBottom = tableTop + tableHeight; |
|
1495 |
+ |
int tableRight = tableLeft + tableWidth; |
|
1496 |
+ |
if ( clientX > tableLeft && clientX < tableRight && clientY > tableTop && clientY < tableBottom ) |
|
1497 |
+ |
{ |
|
1498 |
+ |
return; |
|
1499 |
+ |
} |
|
1500 |
+ |
|
|
1501 |
+ |
dataTable.highlightCell( null ); |
|
1502 |
+ |
} |
|
1503 |
+ |
break; |
1374 |
1504 |
|
} |
1375 |
|
- |
break; |
1376 |
1505 |
|
} |
1377 |
|
- |
} |
1378 |
1506 |
|
|
1379 |
|
- |
/** |
1380 |
|
- |
* This method is called when the dimensions of the parent element change. |
1381 |
|
- |
* Subclasses should override this method as needed. |
1382 |
|
- |
* |
1383 |
|
- |
* @param width the new client width of the element |
1384 |
|
- |
* @param height the new client height of the element |
1385 |
|
- |
*/ |
1386 |
|
- |
public void onResize(int width, int height) { |
1387 |
|
- |
redraw(); |
1388 |
|
- |
} |
1389 |
|
- |
|
1390 |
|
- |
/** |
1391 |
|
- |
* Redraw the table. |
1392 |
|
- |
*/ |
1393 |
|
- |
public void redraw() { |
1394 |
|
- |
if (!isAttached()) { |
1395 |
|
- |
return; |
1396 |
|
- |
} |
1397 |
|
- |
|
1398 |
|
- |
// Create a command to execute while recalculating widths. Using this |
1399 |
|
- |
// command prevents an extra browser layout by grouping read operations. |
1400 |
|
- |
TableWidthInfo redrawInfo = new TableWidthInfo(false); |
1401 |
|
- |
Command command = new Command() { |
1402 |
|
- |
public void execute() { |
1403 |
|
- |
// We update the ResizableWidgetCollection before changing the size of |
1404 |
|
- |
// the ScrollTable, because change the size of the scroll table could |
1405 |
|
- |
// require an additional layout (ex. if window scroll bars show up). |
1406 |
|
- |
ResizableWidgetCollection.get().updateWidgetSize( |
1407 |
|
- |
AbstractScrollTable.this); |
1408 |
|
- |
} |
1409 |
|
- |
}; |
1410 |
|
- |
|
1411 |
|
- |
// Recalculate the ideal table widths of each column. |
1412 |
|
- |
maybeRecalculateIdealColumnWidths(command); |
1413 |
|
- |
|
1414 |
|
- |
// Calculate the new widths of the columns |
1415 |
|
- |
List<ColumnWidthInfo> colWidths = null; |
1416 |
|
- |
if (resizePolicy == ResizePolicy.FILL_WIDTH) { |
1417 |
|
- |
colWidths = getFillColumnWidths(redrawInfo); |
1418 |
|
- |
} else { |
1419 |
|
- |
colWidths = getBoundedColumnWidths(true); |
1420 |
|
- |
} |
1421 |
|
- |
applyNewColumnWidths(0, colWidths, true); |
1422 |
|
- |
|
1423 |
|
- |
// Update the overall height of the scroll table. This can only happen |
1424 |
|
- |
// after the widths have been set because setting the width of cells can |
1425 |
|
- |
// cause word wrap, which increases the height of the inner tables. |
1426 |
|
- |
resizeTablesVertically(); |
1427 |
|
- |
|
1428 |
|
- |
// Reset the scroll position, which might be lost when we change the layout. |
1429 |
|
- |
scrollTables(false); |
1430 |
|
- |
} |
1431 |
|
- |
|
1432 |
|
- |
/** |
1433 |
|
- |
* Unsupported. |
1434 |
|
- |
* |
1435 |
|
- |
* @param child the widget to be removed |
1436 |
|
- |
* @return false |
1437 |
|
- |
* @throws UnsupportedOperationException |
1438 |
|
- |
*/ |
1439 |
|
- |
@Override |
1440 |
|
- |
public boolean remove(Widget child) { |
1441 |
|
- |
throw new UnsupportedOperationException( |
1442 |
|
- |
"This panel does not support remove()"); |
1443 |
|
- |
} |
1444 |
|
- |
|
1445 |
|
- |
/** |
1446 |
|
- |
* Reset the widths of all columns to their preferred sizes. |
1447 |
|
- |
*/ |
1448 |
|
- |
public void resetColumnWidths() { |
1449 |
|
- |
applyNewColumnWidths(0, getBoundedColumnWidths(false), false); |
1450 |
|
- |
scrollTables(false); |
1451 |
|
- |
} |
1452 |
|
- |
|
1453 |
|
- |
/** |
1454 |
|
- |
* Sets the amount of padding to be added around all cells. |
1455 |
|
- |
* |
1456 |
|
- |
* @param padding the cell padding, in pixels |
1457 |
|
- |
*/ |
1458 |
|
- |
public void setCellPadding(int padding) { |
1459 |
|
- |
headerTable.setCellPadding(padding); |
1460 |
|
- |
dataTable.setCellPadding(padding); |
1461 |
|
- |
if (footerTable != null) { |
1462 |
|
- |
footerTable.setCellPadding(padding); |
1463 |
|
- |
} |
1464 |
|
- |
redraw(); |
1465 |
|
- |
} |
1466 |
|
- |
|
1467 |
|
- |
/** |
1468 |
|
- |
* Sets the amount of spacing to be added around all cells. |
1469 |
|
- |
* |
1470 |
|
- |
* @param spacing the cell spacing, in pixels |
1471 |
|
- |
*/ |
1472 |
|
- |
public void setCellSpacing(int spacing) { |
1473 |
|
- |
headerTable.setCellSpacing(spacing); |
1474 |
|
- |
dataTable.setCellSpacing(spacing); |
1475 |
|
- |
if (footerTable != null) { |
1476 |
|
- |
footerTable.setCellSpacing(spacing); |
1477 |
|
- |
} |
1478 |
|
- |
redraw(); |
1479 |
|
- |
} |
1480 |
|
- |
|
1481 |
|
- |
/** |
1482 |
|
- |
* Set the resize policy applied to user actions that resize columns. |
1483 |
|
- |
* |
1484 |
|
- |
* @param columnResizePolicy the resize policy |
1485 |
|
- |
*/ |
1486 |
|
- |
public void setColumnResizePolicy(ColumnResizePolicy columnResizePolicy) { |
1487 |
|
- |
this.columnResizePolicy = columnResizePolicy; |
1488 |
|
- |
updateFillWidthImage(); |
1489 |
|
- |
} |
1490 |
|
- |
|
1491 |
|
- |
/** |
1492 |
|
- |
* Set the width of a column. |
1493 |
|
- |
* |
1494 |
|
- |
* @param column the index of the column |
1495 |
|
- |
* @param width the width in pixels |
1496 |
|
- |
* @return the new column width |
1497 |
|
- |
*/ |
1498 |
|
- |
public int setColumnWidth(int column, int width) { |
1499 |
|
- |
// Constrain the size of the column |
1500 |
|
- |
ColumnWidthInfo info = getColumnWidthInfo(column); |
1501 |
|
- |
if (info.hasMaximumWidth()) { |
1502 |
|
- |
width = Math.min(width, info.getMaximumWidth()); |
1503 |
|
- |
} |
1504 |
|
- |
if (info.hasMinimumWidth()) { |
1505 |
|
- |
width = Math.max(width, info.getMinimumWidth()); |
1506 |
|
- |
} |
1507 |
|
- |
|
1508 |
|
- |
// Try to constrain the size of the grid |
1509 |
|
- |
if (resizePolicy.isSacrificial()) { |
1510 |
|
- |
// Get the sacrifice columns |
1511 |
|
- |
int sacrificeColumn = column + 1; |
1512 |
|
- |
int numColumns = dataTable.getColumnCount(); |
1513 |
|
- |
int remainingColumns = numColumns - sacrificeColumn; |
1514 |
|
- |
List<ColumnWidthInfo> infos = getColumnWidthInfo(sacrificeColumn, |
1515 |
|
- |
remainingColumns); |
1516 |
|
- |
|
1517 |
|
- |
// Distribute the width over the sacrifice columns |
1518 |
|
- |
int diff = width - getColumnWidth(column); |
1519 |
|
- |
int undistributed = columnResizer.distributeWidth(infos, -diff); |
1520 |
|
- |
|
1521 |
|
- |
// Set the new column widths |
1522 |
|
- |
applyNewColumnWidths(sacrificeColumn, infos, false); |
1523 |
|
- |
|
1524 |
|
- |
// Prevent over resizing |
1525 |
|
- |
if (resizePolicy.isFixedWidth()) { |
1526 |
|
- |
width += undistributed; |
1527 |
|
- |
} |
1528 |
|
- |
} |
1529 |
|
- |
|
1530 |
|
- |
// Resize the column |
1531 |
|
- |
int offset = getHeaderOffset(); |
1532 |
|
- |
dataTable.setColumnWidth(column, width); |
1533 |
|
- |
headerTable.setColumnWidth(column + offset, width); |
1534 |
|
- |
if (footerTable != null) { |
1535 |
|
- |
footerTable.setColumnWidth(column + offset, width); |
1536 |
|
- |
} |
1537 |
|
- |
|
1538 |
|
- |
// Reposition things as needed |
1539 |
|
- |
impl.repositionSpacer(this, false); |
1540 |
|
- |
resizeTablesVertically(); |
1541 |
|
- |
scrollTables(false); |
1542 |
|
- |
return width; |
1543 |
|
- |
} |
1544 |
|
- |
|
1545 |
|
- |
/** |
1546 |
|
- |
* Set the footer table that appears under the data table. If set to null, the |
1547 |
|
- |
* footer table will not be shown. |
1548 |
|
- |
* |
1549 |
|
- |
* @param footerTable the table to use in the footer |
1550 |
|
- |
*/ |
1551 |
|
- |
public void setFooterTable(FixedWidthFlexTable footerTable) { |
1552 |
|
- |
// Disown the old footer table |
1553 |
|
- |
if (this.footerTable != null) { |
1554 |
|
- |
super.remove(this.footerTable); |
1555 |
|
- |
DOM.removeChild(absoluteElem, footerWrapper); |
1556 |
|
- |
} |
1557 |
|
- |
|
1558 |
|
- |
// Set the new footer table |
1559 |
|
- |
this.footerTable = footerTable; |
1560 |
|
- |
if (footerTable != null) { |
1561 |
|
- |
footerTable.setCellSpacing(getCellSpacing()); |
1562 |
|
- |
footerTable.setCellPadding(getCellPadding()); |
1563 |
|
- |
prepareTable(footerTable, "footerTable"); |
1564 |
|
- |
if (dataTable.getSelectionPolicy().hasInputColumn()) { |
1565 |
|
- |
footerTable.setColumnWidth(0, dataTable.getInputColumnWidth()); |
1566 |
|
- |
} |
1567 |
|
- |
|
1568 |
|
- |
// Create the footer wrapper and spacer |
1569 |
|
- |
if (footerWrapper == null) { |
1570 |
|
- |
footerWrapper = createWrapper("footerWrapper"); |
1571 |
|
- |
footerSpacer = impl.createSpacer(footerTable, footerWrapper); |
1572 |
|
- |
DOM.setEventListener(footerWrapper, this); |
1573 |
|
- |
DOM.sinkEvents(footerWrapper, Event.ONMOUSEUP); |
1574 |
|
- |
} |
1575 |
|
- |
|
1576 |
|
- |
// Adopt the header table into the panel |
1577 |
|
- |
adoptTable(footerTable, footerWrapper, |
1578 |
|
- |
absoluteElem.getChildNodes().getLength()); |
1579 |
|
- |
} |
1580 |
|
- |
redraw(); |
1581 |
|
- |
} |
1582 |
|
- |
|
1583 |
|
- |
@Override |
1584 |
|
- |
public void setHeight(String height) { |
1585 |
|
- |
this.lastHeight = height; |
1586 |
|
- |
super.setHeight(height); |
1587 |
|
- |
resizeTablesVertically(); |
1588 |
|
- |
} |
1589 |
|
- |
|
1590 |
|
- |
/** |
1591 |
|
- |
* Set the resize policy of the table. |
1592 |
|
- |
* |
1593 |
|
- |
* @param resizePolicy the resize policy |
1594 |
|
- |
*/ |
1595 |
|
- |
public void setResizePolicy(ResizePolicy resizePolicy) { |
1596 |
|
- |
this.resizePolicy = resizePolicy; |
1597 |
|
- |
updateFillWidthImage(); |
1598 |
|
- |
redraw(); |
1599 |
|
- |
} |
1600 |
|
- |
|
1601 |
|
- |
/** |
1602 |
|
- |
* Set the scroll policy of the table. |
1603 |
|
- |
* |
1604 |
|
- |
* @param scrollPolicy the new scroll policy |
1605 |
|
- |
*/ |
1606 |
|
- |
public void setScrollPolicy(ScrollPolicy scrollPolicy) { |
1607 |
|
- |
if (scrollPolicy == this.scrollPolicy) { |
1608 |
|
- |
return; |
1609 |
|
- |
} |
1610 |
|
- |
this.scrollPolicy = scrollPolicy; |
1611 |
|
- |
|
1612 |
|
- |
// Clear the heights of the wrappers |
1613 |
|
- |
headerWrapper.getStyle().setProperty("height", ""); |
1614 |
|
- |
dataWrapper.getStyle().setProperty("height", ""); |
1615 |
|
- |
if (footerWrapper != null) { |
1616 |
|
- |
footerWrapper.getStyle().setProperty("height", ""); |
1617 |
|
- |
} |
1618 |
|
- |
|
1619 |
|
- |
if (scrollPolicy == ScrollPolicy.DISABLED) { |
1620 |
|
- |
// Disabled scroll bars |
1621 |
|
- |
dataWrapper.getStyle().setProperty("height", "auto"); |
1622 |
|
- |
dataWrapper.getStyle().setProperty("overflow", ""); |
1623 |
|
- |
} else if (scrollPolicy == ScrollPolicy.HORIZONTAL) { |
1624 |
|
- |
// Only show horizontal scroll bar |
1625 |
|
- |
dataWrapper.getStyle().setProperty("height", "auto"); |
1626 |
|
- |
dataWrapper.getStyle().setProperty("overflow", "auto"); |
1627 |
|
- |
} else if (scrollPolicy == ScrollPolicy.BOTH) { |
1628 |
|
- |
// Show both scroll bars |
1629 |
|
- |
if (lastHeight != null) { |
1630 |
|
- |
super.setHeight(lastHeight); |
1631 |
|
- |
} else { |
1632 |
|
- |
super.setHeight(""); |
1633 |
|
- |
} |
1634 |
|
- |
dataWrapper.getStyle().setProperty("overflow", "auto"); |
1635 |
|
- |
} |
1636 |
|
- |
|
1637 |
|
- |
// Resize the tables |
1638 |
|
- |
impl.repositionSpacer(this, true); |
1639 |
|
- |
redraw(); |
1640 |
|
- |
} |
1641 |
|
- |
|
1642 |
|
- |
/** |
1643 |
|
- |
* Set the {@link SortPolicy} that defines what columns users can sort. |
1644 |
|
- |
* |
1645 |
|
- |
* @param sortPolicy the {@link SortPolicy} |
1646 |
|
- |
*/ |
1647 |
|
- |
public void setSortPolicy(SortPolicy sortPolicy) { |
1648 |
|
- |
this.sortPolicy = sortPolicy; |
1649 |
|
- |
|
1650 |
|
- |
// Remove the sorted indicator image |
1651 |
|
- |
applySortedColumnIndicator(null, true); |
1652 |
|
- |
} |
1653 |
|
- |
|
1654 |
|
- |
/** |
1655 |
|
- |
* Apply the sorted column indicator to a specific table cell in the header |
1656 |
|
- |
* table. |
1657 |
|
- |
* |
1658 |
|
- |
* @param tdElem the cell in the header table, or null to remove it |
1659 |
|
- |
* @param ascending true to apply the ascending indicator, false for |
1660 |
|
- |
* descending |
1661 |
|
- |
*/ |
1662 |
|
- |
protected void applySortedColumnIndicator(Element tdElem, boolean ascending) { |
1663 |
|
- |
// Remove the sort indicator |
1664 |
|
- |
if (tdElem == null) { |
1665 |
|
- |
Element parent = DOM.getParent(sortedColumnWrapper); |
1666 |
|
- |
if (parent != null) { |
1667 |
|
- |
parent.removeChild(sortedColumnWrapper); |
1668 |
|
- |
headerTable.clearIdealWidths(); |
1669 |
|
- |
} |
1670 |
|
- |
return; |
|
1507 |
+ |
/** |
|
1508 |
+ |
* This method is called when the dimensions of the parent element change. |
|
1509 |
+ |
* Subclasses should override this method as needed. |
|
1510 |
+ |
* |
|
1511 |
+ |
* @param width the new client width of the element |
|
1512 |
+ |
* @param height the new client height of the element |
|
1513 |
+ |
*/ |
|
1514 |
+ |
public void onResize( int width, int height ) |
|
1515 |
+ |
{ |
|
1516 |
+ |
redraw(); |
1671 |
1517 |
|
} |
1672 |
1518 |
|
|
1673 |
|
- |
tdElem.appendChild(sortedColumnWrapper); |
1674 |
|
- |
if (ascending) { |
1675 |
|
- |
sortedColumnWrapper.setInnerHTML(" " |
1676 |
|
- |
+ images.scrollTableAscending().getHTML()); |
1677 |
|
- |
} else { |
1678 |
|
- |
sortedColumnWrapper.setInnerHTML(" " |
1679 |
|
- |
+ images.scrollTableDescending().getHTML()); |
1680 |
|
- |
} |
1681 |
|
- |
sortedRowIndex = -1; |
1682 |
|
- |
sortedCellIndex = -1; |
1683 |
|
- |
|
1684 |
|
- |
// The column with the indicator now has a new ideal width |
1685 |
|
- |
headerTable.clearIdealWidths(); |
1686 |
|
- |
redraw(); |
1687 |
|
- |
} |
1688 |
|
- |
|
1689 |
|
- |
/** |
1690 |
|
- |
* Create a wrapper element that will hold a table. |
1691 |
|
- |
* |
1692 |
|
- |
* @param cssName the style name added to the base name |
1693 |
|
- |
* @return a new wrapper element |
1694 |
|
- |
*/ |
1695 |
|
- |
protected Element createWrapper(String cssName) { |
1696 |
|
- |
Element wrapper = DOM.createDiv(); |
1697 |
|
- |
wrapper.getStyle().setProperty("width", "100%"); |
1698 |
|
- |
wrapper.getStyle().setProperty("overflow", "hidden"); |
1699 |
|
- |
wrapper.getStyle().setPropertyPx("padding", 0); |
1700 |
|
- |
wrapper.getStyle().setPropertyPx("margin", 0); |
1701 |
|
- |
wrapper.getStyle().setPropertyPx("border", 0); |
1702 |
|
- |
if (cssName != null) { |
1703 |
|
- |
setStyleName(wrapper, cssName); |
1704 |
|
- |
} |
1705 |
|
- |
return wrapper; |
1706 |
|
- |
} |
1707 |
|
- |
|
1708 |
|
- |
/** |
1709 |
|
- |
* @return the wrapper element around the data table |
1710 |
|
- |
*/ |
1711 |
|
- |
protected Element getDataWrapper() { |
1712 |
|
- |
return dataWrapper; |
1713 |
|
- |
} |
1714 |
|
- |
|
1715 |
|
- |
/** |
1716 |
|
- |
* Extend the columns to exactly fill the available space, if the current |
1717 |
|
- |
* {@link ResizePolicy} requires it. |
1718 |
|
- |
* |
1719 |
|
- |
* @deprecated use {@link #redraw()} instead |
1720 |
|
- |
*/ |
1721 |
|
- |
@Deprecated |
1722 |
|
- |
protected void maybeFillWidth() { |
1723 |
|
- |
redraw(); |
1724 |
|
- |
} |
1725 |
|
- |
|
1726 |
|
- |
/** |
1727 |
|
- |
* Called just before a column is sorted because of a user click on the header |
1728 |
|
- |
* row. |
1729 |
|
- |
* |
1730 |
|
- |
* @param row the row index that was clicked |
1731 |
|
- |
* @param column the column index that was clicked |
1732 |
|
- |
* @return true to sort, false to ignore |
1733 |
|
- |
*/ |
1734 |
|
- |
protected boolean onHeaderSort(int row, int column) { |
1735 |
|
- |
return true; |
1736 |
|
- |
} |
1737 |
|
- |
|
1738 |
|
- |
@Override |
1739 |
|
- |
protected void onLoad() { |
1740 |
|
- |
ResizableWidgetCollection.get().add(this); |
1741 |
|
- |
redraw(); |
1742 |
|
- |
} |
1743 |
|
- |
|
1744 |
|
- |
@Override |
1745 |
|
- |
protected void onUnload() { |
1746 |
|
- |
ResizableWidgetCollection.get().remove(this); |
1747 |
|
- |
} |
1748 |
|
- |
|
1749 |
|
- |
/** |
1750 |
|
- |
* Fixes the table heights so the header is visible and the data takes up the |
1751 |
|
- |
* remaining vertical space. |
1752 |
|
- |
*/ |
1753 |
|
- |
protected void resizeTablesVertically() { |
1754 |
|
- |
if (scrollPolicy == ScrollPolicy.DISABLED) { |
1755 |
|
- |
dataWrapper.getStyle().setProperty("overflow", "auto"); |
1756 |
|
- |
dataWrapper.getStyle().setProperty("overflow", ""); |
1757 |
|
- |
int height = Math.max(1, absoluteElem.getOffsetHeight()); |
1758 |
|
- |
super.setHeight(height + "px"); |
1759 |
|
- |
} else if (scrollPolicy == ScrollPolicy.HORIZONTAL) { |
1760 |
|
- |
dataWrapper.getStyle().setProperty("overflow", "hidden"); |
1761 |
|
- |
dataWrapper.getStyle().setProperty("overflow", "auto"); |
1762 |
|
- |
int height = Math.max(1, absoluteElem.getOffsetHeight()); |
1763 |
|
- |
super.setHeight(height + "px"); |
1764 |
|
- |
} else { |
1765 |
|
- |
applyTableWrapperSizes(getTableWrapperSizes()); |
1766 |
|
- |
dataWrapper.getStyle().setProperty("width", "100%"); |
1767 |
|
- |
} |
1768 |
|
- |
} |
1769 |
|
- |
|
1770 |
|
- |
/** |
1771 |
|
- |
* Helper method that actually performs the vertical resizing. |
1772 |
|
- |
* |
1773 |
|
- |
* @deprecated use {@link #redraw()} instead |
1774 |
|
- |
*/ |
1775 |
|
- |
@Deprecated |
1776 |
|
- |
protected void resizeTablesVerticallyNow() { |
1777 |
|
- |
redraw(); |
1778 |
|
- |
} |
1779 |
|
- |
|
1780 |
|
- |
/** |
1781 |
|
- |
* Sets the scroll property of the header and footers wrappers when scrolling |
1782 |
|
- |
* so that the header, footer, and data tables line up. |
1783 |
|
- |
* |
1784 |
|
- |
* @param baseHeader true to scroll the data table as well |
1785 |
|
- |
*/ |
1786 |
|
- |
protected void scrollTables(boolean baseHeader) { |
1787 |
|
- |
if (scrollPolicy == ScrollPolicy.DISABLED) { |
1788 |
|
- |
return; |
1789 |
|
- |
} |
1790 |
|
- |
|
1791 |
|
- |
if (lastScrollLeft >= 0) { |
1792 |
|
- |
headerWrapper.setScrollLeft(lastScrollLeft); |
1793 |
|
- |
if (baseHeader) { |
1794 |
|
- |
dataWrapper.setScrollLeft(lastScrollLeft); |
1795 |
|
- |
} |
1796 |
|
- |
if (footerWrapper != null) { |
1797 |
|
- |
footerWrapper.setScrollLeft(lastScrollLeft); |
1798 |
|
- |
} |
1799 |
|
- |
} |
1800 |
|
- |
} |
1801 |
|
- |
|
1802 |
|
- |
/** |
1803 |
|
- |
* @return the absolutely positioned wrapper element |
1804 |
|
- |
*/ |
1805 |
|
- |
Element getAbsoluteElement() { |
1806 |
|
- |
return absoluteElem; |
1807 |
|
- |
} |
1808 |
|
- |
|
1809 |
|
- |
/** |
1810 |
|
- |
* Adopt a table into this {@link AbstractScrollTable} within its wrapper. |
1811 |
|
- |
* |
1812 |
|
- |
* @param table the table to adopt |
1813 |
|
- |
* @param wrapper the wrapper element |
1814 |
|
- |
* @param index the index to insert the wrapper in the main element |
1815 |
|
- |
*/ |
1816 |
|
- |
private void adoptTable(Widget table, Element wrapper, int index) { |
1817 |
|
- |
DOM.insertChild(absoluteElem, wrapper, index); |
1818 |
|
- |
add(table, wrapper); |
1819 |
|
- |
} |
1820 |
|
- |
|
1821 |
|
- |
/** |
1822 |
|
- |
* Apply the new widths to a list of columns. |
1823 |
|
- |
* |
1824 |
|
- |
* @param startIndex the index of the first column |
1825 |
|
- |
* @param infos the new column width info |
1826 |
|
- |
* @param forced if false, only set column widths that have changed |
1827 |
|
- |
*/ |
1828 |
|
- |
private void applyNewColumnWidths(int startIndex, |
1829 |
|
- |
List<ColumnWidthInfo> infos, boolean forced) { |
1830 |
|
- |
// Infos can be null if the widths cannot be calculated |
1831 |
|
- |
if (infos == null) { |
1832 |
|
- |
return; |
1833 |
|
- |
} |
1834 |
|
- |
|
1835 |
|
- |
int offset = getHeaderOffset(); |
1836 |
|
- |
int numColumns = infos.size(); |
1837 |
|
- |
for (int i = 0; i < numColumns; i++) { |
1838 |
|
- |
ColumnWidthInfo info = infos.get(i); |
1839 |
|
- |
int newWidth = info.getNewWidth(); |
1840 |
|
- |
if (forced || info.getCurrentWidth() != newWidth) { |
1841 |
|
- |
dataTable.setColumnWidth(startIndex + i, newWidth); |
1842 |
|
- |
headerTable.setColumnWidth(startIndex + i + offset, newWidth); |
1843 |
|
- |
if (footerTable != null) { |
1844 |
|
- |
footerTable.setColumnWidth(startIndex + i + offset, newWidth); |
1845 |
|
- |
} |
1846 |
|
- |
} |
1847 |
|
- |
} |
1848 |
|
- |
impl.repositionSpacer(this, false); |
1849 |
|
- |
} |
1850 |
|
- |
|
1851 |
|
- |
/** |
1852 |
|
- |
* Apply the new sizes to the table wrappers. |
1853 |
|
- |
* |
1854 |
|
- |
* @param sizes the sizes to apply |
1855 |
|
- |
*/ |
1856 |
|
- |
private void applyTableWrapperSizes(TableHeightInfo sizes) { |
1857 |
|
- |
if (sizes == null) { |
1858 |
|
- |
return; |
1859 |
|
- |
} |
1860 |
|
- |
|
1861 |
|
- |
headerWrapper.getStyle().setPropertyPx("height", sizes.headerTableHeight); |
1862 |
|
- |
if (footerWrapper != null) { |
1863 |
|
- |
footerWrapper.getStyle().setPropertyPx("height", sizes.footerTableHeight); |
1864 |
|
- |
} |
1865 |
|
- |
dataWrapper.getStyle().setPropertyPx("height", |
1866 |
|
- |
Math.max(sizes.dataTableHeight, 0)); |
1867 |
|
- |
dataWrapper.getStyle().setProperty("overflow", "hidden"); |
1868 |
|
- |
dataWrapper.getStyle().setProperty("overflow", "auto"); |
1869 |
|
- |
} |
1870 |
|
- |
|
1871 |
|
- |
/** |
1872 |
|
- |
* Get the width available for the tables. |
1873 |
|
- |
* |
1874 |
|
- |
* @return the available width, or -1 if not defined |
1875 |
|
- |
*/ |
1876 |
|
- |
private int getAvailableWidth() { |
1877 |
|
- |
int clientWidth = absoluteElem.getPropertyInt("clientWidth"); |
1878 |
|
- |
if (scrollPolicy == ScrollPolicy.BOTH) { |
1879 |
|
- |
int scrollbarWidth = mockScrollable.getOffsetWidth() |
1880 |
|
- |
- mockScrollable.getPropertyInt("clientWidth"); |
1881 |
|
- |
clientWidth = absoluteElem.getPropertyInt("clientWidth") - scrollbarWidth |
1882 |
|
- |
- 1; |
1883 |
|
- |
} |
1884 |
|
- |
return Math.max(clientWidth, -1); |
1885 |
|
- |
} |
1886 |
|
- |
|
1887 |
|
- |
/** |
1888 |
|
- |
* Get the widths of all columns, either to their preferred sizes or just |
1889 |
|
- |
* ensure that they are within their min/max boundaries. |
1890 |
|
- |
* |
1891 |
|
- |
* @param boundsOnly true to only ensure the widths are within the bounds |
1892 |
|
- |
* @return the column widths |
1893 |
|
- |
*/ |
1894 |
|
- |
private List<ColumnWidthInfo> getBoundedColumnWidths(boolean boundsOnly) { |
1895 |
|
- |
if (!isAttached()) { |
1896 |
|
- |
return null; |
1897 |
|
- |
} |
1898 |
|
- |
|
1899 |
|
- |
// Calculate the new column widths |
1900 |
|
- |
int numColumns = dataTable.getColumnCount(); |
1901 |
|
- |
int totalWidth = 0; |
1902 |
|
- |
List<ColumnWidthInfo> colWidthInfos = getColumnWidthInfo(0, numColumns); |
1903 |
|
- |
|
1904 |
|
- |
// If we are reseting to original widths, set all widths to 0 |
1905 |
|
- |
if (!boundsOnly) { |
1906 |
|
- |
for (ColumnWidthInfo info : colWidthInfos) { |
1907 |
|
- |
totalWidth += info.getCurrentWidth(); |
1908 |
|
- |
info.setCurrentWidth(0); |
1909 |
|
- |
} |
1910 |
|
- |
} |
1911 |
|
- |
|
1912 |
|
- |
// Run the resize algorithm |
1913 |
|
- |
columnResizer.distributeWidth(colWidthInfos, totalWidth); |
1914 |
|
- |
|
1915 |
|
- |
// Set the new column widths |
1916 |
|
- |
return colWidthInfos; |
1917 |
|
- |
} |
1918 |
|
- |
|
1919 |
|
- |
/** |
1920 |
|
- |
* Get info about the width of a column. |
1921 |
|
- |
* |
1922 |
|
- |
* @param column the column index |
1923 |
|
- |
* @return the info about the column width |
1924 |
|
- |
*/ |
1925 |
|
- |
private ColumnWidthInfo getColumnWidthInfo(int column) { |
1926 |
|
- |
int minWidth = getMinimumColumnWidth(column); |
1927 |
|
- |
int maxWidth = getMaximumColumnWidth(column); |
1928 |
|
- |
int preferredWidth = getPreferredColumnWidth(column); |
1929 |
|
- |
int curWidth = getColumnWidth(column); |
1930 |
|
- |
|
1931 |
|
- |
// Adjust the widths if the columns are not truncatable, up to maxWidth |
1932 |
|
- |
if (!isColumnTruncatable(column)) { |
1933 |
|
- |
maybeRecalculateIdealColumnWidths(null); |
1934 |
|
- |
int idealWidth = getDataTable().getIdealColumnWidth(column); |
1935 |
|
- |
if (maxWidth != MaximumWidthProperty.NO_MAXIMUM_WIDTH) { |
1936 |
|
- |
idealWidth = Math.min(idealWidth, maxWidth); |
1937 |
|
- |
} |
1938 |
|
- |
minWidth = Math.max(minWidth, idealWidth); |
1939 |
|
- |
} |
1940 |
|
- |
if (!isHeaderColumnTruncatable(column)) { |
1941 |
|
- |
maybeRecalculateIdealColumnWidths(null); |
1942 |
|
- |
int idealWidth = getHeaderTable().getIdealColumnWidth( |
1943 |
|
- |
column + getHeaderOffset()); |
1944 |
|
- |
if (maxWidth != MaximumWidthProperty.NO_MAXIMUM_WIDTH) { |
1945 |
|
- |
idealWidth = Math.min(idealWidth, maxWidth); |
1946 |
|
- |
} |
1947 |
|
- |
minWidth = Math.max(minWidth, idealWidth); |
1948 |
|
- |
} |
1949 |
|
- |
if (footerTable != null && !isFooterColumnTruncatable(column)) { |
1950 |
|
- |
maybeRecalculateIdealColumnWidths(null); |
1951 |
|
- |
int idealWidth = getFooterTable().getIdealColumnWidth( |
1952 |
|
- |
column + getHeaderOffset()); |
1953 |
|
- |
if (maxWidth != MaximumWidthProperty.NO_MAXIMUM_WIDTH) { |
1954 |
|
- |
idealWidth = Math.min(idealWidth, maxWidth); |
1955 |
|
- |
} |
1956 |
|
- |
minWidth = Math.max(minWidth, idealWidth); |
1957 |
|
- |
} |
1958 |
|
- |
|
1959 |
|
- |
return new ColumnWidthInfo(minWidth, maxWidth, preferredWidth, curWidth); |
1960 |
|
- |
} |
1961 |
|
- |
|
1962 |
|
- |
/** |
1963 |
|
- |
* Get info about the width of multiple columns. |
1964 |
|
- |
* |
1965 |
|
- |
* @param column the start column index |
1966 |
|
- |
* @param numColumns the number of columns |
1967 |
|
- |
* @return the info about the column widths of the columns |
1968 |
|
- |
*/ |
1969 |
|
- |
private List<ColumnWidthInfo> getColumnWidthInfo(int column, int numColumns) { |
1970 |
|
- |
List<ColumnWidthInfo> infos = new ArrayList<ColumnWidthInfo>(); |
1971 |
|
- |
for (int i = 0; i < numColumns; i++) { |
1972 |
|
- |
infos.add(getColumnWidthInfo(column + i)); |
1973 |
|
- |
} |
1974 |
|
- |
return infos; |
1975 |
|
- |
} |
1976 |
|
- |
|
1977 |
|
- |
/** |
1978 |
|
- |
* Get the column widths needed to fill with available ScrollTable width. |
1979 |
|
- |
* |
1980 |
|
- |
* @param info the optional precomputed sizes |
1981 |
|
- |
* @return the column widths |
1982 |
|
- |
*/ |
1983 |
|
- |
private List<ColumnWidthInfo> getFillColumnWidths(TableWidthInfo info) { |
1984 |
|
- |
if (!isAttached()) { |
1985 |
|
- |
return null; |
1986 |
|
- |
} |
1987 |
|
- |
|
1988 |
|
- |
// Precompute some sizes |
1989 |
|
- |
if (info == null) { |
1990 |
|
- |
info = new TableWidthInfo(false); |
1991 |
|
- |
} |
1992 |
|
- |
|
1993 |
|
- |
// Calculate how much room we have to work with |
1994 |
|
- |
int clientWidth = info.availableWidth; |
1995 |
|
- |
if (clientWidth <= 0) { |
1996 |
|
- |
return null; |
1997 |
|
- |
} |
1998 |
|
- |
|
1999 |
|
- |
// Calculate the difference and number of column to resize |
2000 |
|
- |
int diff = 0; |
2001 |
|
- |
int numColumns = 0; |
2002 |
|
- |
{ |
2003 |
|
- |
// Calculate the number of columns in each table |
2004 |
|
- |
int numHeaderCols = 0; |
2005 |
|
- |
int numDataCols = 0; |
2006 |
|
- |
int numFooterCols = 0; |
2007 |
|
- |
if (info.headerTableWidth > 0) { |
2008 |
|
- |
numHeaderCols = headerTable.getColumnCount() - getHeaderOffset(); |
2009 |
|
- |
} |
2010 |
|
- |
if (info.dataTableWidth > 0) { |
2011 |
|
- |
numDataCols = dataTable.getColumnCount(); |
2012 |
|
- |
} |
2013 |
|
- |
if (footerTable != null && info.footerTableWidth > 0) { |
2014 |
|
- |
numFooterCols = footerTable.getColumnCount() - getHeaderOffset(); |
2015 |
|
- |
} |
2016 |
|
- |
|
2017 |
|
- |
// Determine the largest table |
2018 |
|
- |
if (numHeaderCols >= numDataCols && numHeaderCols >= numFooterCols) { |
2019 |
|
- |
numColumns = numHeaderCols; |
2020 |
|
- |
diff = clientWidth - info.headerTableWidth; |
2021 |
|
- |
} else if (numFooterCols >= numDataCols && numFooterCols >= numHeaderCols) { |
2022 |
|
- |
numColumns = numFooterCols; |
2023 |
|
- |
diff = clientWidth - info.footerTableWidth; |
2024 |
|
- |
} else if (numDataCols > 0) { |
2025 |
|
- |
numColumns = numDataCols; |
2026 |
|
- |
diff = clientWidth - info.dataTableWidth; |
2027 |
|
- |
} |
2028 |
|
- |
} |
2029 |
|
- |
if (numColumns <= 0) { |
2030 |
|
- |
return null; |
2031 |
|
- |
} |
2032 |
|
- |
|
2033 |
|
- |
// Calculate the new column widths |
2034 |
|
- |
List<ColumnWidthInfo> colWidthInfos = getColumnWidthInfo(0, numColumns); |
2035 |
|
- |
columnResizer.distributeWidth(colWidthInfos, diff); |
2036 |
|
- |
return colWidthInfos; |
2037 |
|
- |
} |
2038 |
|
- |
|
2039 |
|
- |
/** |
2040 |
|
- |
* Get the offset between the data and header and footer tables. An offset of |
2041 |
|
- |
* one means that the header and footer table indexes are one greater than the |
2042 |
|
- |
* data table indexes, probably because the data table contains a checkbox |
2043 |
|
- |
* column. |
2044 |
|
- |
* |
2045 |
|
- |
* @return the offset |
2046 |
|
- |
*/ |
2047 |
|
- |
private int getHeaderOffset() { |
2048 |
|
- |
if (dataTable.getSelectionPolicy().hasInputColumn()) { |
2049 |
|
- |
return 1; |
2050 |
|
- |
} |
2051 |
|
- |
return 0; |
2052 |
|
- |
} |
2053 |
|
- |
|
2054 |
|
- |
/** |
2055 |
|
- |
* Returns the new heights of the header, data, and footer tables based on the |
2056 |
|
- |
* {@link ScrollPolicy}. |
2057 |
|
- |
* |
2058 |
|
- |
* @return the new table heights, or null |
2059 |
|
- |
*/ |
2060 |
|
- |
private TableHeightInfo getTableWrapperSizes() { |
2061 |
|
- |
// If we aren't attached, return immediately |
2062 |
|
- |
if (!isAttached()) { |
2063 |
|
- |
return null; |
2064 |
|
- |
} |
2065 |
|
- |
|
2066 |
|
- |
// Heights only apply with vertical scrolling |
2067 |
|
- |
if (scrollPolicy == ScrollPolicy.DISABLED |
2068 |
|
- |
|| scrollPolicy == ScrollPolicy.HORIZONTAL) { |
2069 |
|
- |
return null; |
2070 |
|
- |
} |
2071 |
|
- |
|
2072 |
|
- |
// Give the data wrapper all remaining height |
2073 |
|
- |
return new TableHeightInfo(); |
2074 |
|
- |
} |
2075 |
|
- |
|
2076 |
|
- |
/** |
2077 |
|
- |
* Recalculate the ideal columns widths of all inner tables. |
2078 |
|
- |
* |
2079 |
|
- |
* @param command an optional command to execute while recalculating |
2080 |
|
- |
*/ |
2081 |
|
- |
private void maybeRecalculateIdealColumnWidths(Command command) { |
2082 |
|
- |
// Calculations require that we are attached |
2083 |
|
- |
if (!isAttached()) { |
2084 |
|
- |
return; |
2085 |
|
- |
} |
2086 |
|
- |
|
2087 |
|
- |
// Check if a recalculation is needed. |
2088 |
|
- |
if (headerTable.isIdealColumnWidthsCalculated() |
2089 |
|
- |
&& dataTable.isIdealColumnWidthsCalculated() |
2090 |
|
- |
&& (footerTable == null || footerTable.isIdealColumnWidthsCalculated())) { |
2091 |
|
- |
if (command != null) { |
2092 |
|
- |
command.execute(); |
2093 |
|
- |
} |
2094 |
|
- |
return; |
2095 |
|
- |
} |
2096 |
|
- |
|
2097 |
|
- |
impl.recalculateIdealColumnWidths(this, command); |
2098 |
|
- |
} |
2099 |
|
- |
|
2100 |
|
- |
/** |
2101 |
|
- |
* Prepare a table to be added to the {@link AbstractScrollTable}. |
2102 |
|
- |
* |
2103 |
|
- |
* @param table the table to prepare |
2104 |
|
- |
* @param cssName the style name added to the base name |
2105 |
|
- |
*/ |
2106 |
|
- |
private void prepareTable(Widget table, String cssName) { |
2107 |
|
- |
Element tableElem = table.getElement(); |
2108 |
|
- |
DOM.setStyleAttribute(tableElem, "margin", "0px"); |
2109 |
|
- |
DOM.setStyleAttribute(tableElem, "border", "0px"); |
2110 |
|
- |
table.addStyleName(cssName); |
2111 |
|
- |
} |
2112 |
|
- |
|
2113 |
|
- |
/** |
2114 |
|
- |
* Show or hide to fillWidthImage depending on current policies. |
2115 |
|
- |
*/ |
2116 |
|
- |
private void updateFillWidthImage() { |
2117 |
|
- |
if (columnResizePolicy == ColumnResizePolicy.DISABLED |
2118 |
|
- |
|| resizePolicy.isFixedWidth()) { |
2119 |
|
- |
fillWidthImage.setVisible(false); |
2120 |
|
- |
} else { |
2121 |
|
- |
fillWidthImage.setVisible(true); |
|
1519 |
+ |
/** |
|
1520 |
+ |
* Redraw the table. |
|
1521 |
+ |
*/ |
|
1522 |
+ |
public void redraw() |
|
1523 |
+ |
{ |
|
1524 |
+ |
if ( !isAttached() ) |
|
1525 |
+ |
{ |
|
1526 |
+ |
return; |
|
1527 |
+ |
} |
|
1528 |
+ |
|
|
1529 |
+ |
// Create a command to execute while recalculating widths. Using this |
|
1530 |
+ |
// command prevents an extra browser layout by grouping read operations. |
|
1531 |
+ |
TableWidthInfo redrawInfo = new TableWidthInfo( false ); |
|
1532 |
+ |
Command command = new Command() |
|
1533 |
+ |
{ |
|
1534 |
+ |
public void execute() |
|
1535 |
+ |
{ |
|
1536 |
+ |
// We update the ResizableWidgetCollection before changing the size of |
|
1537 |
+ |
// the ScrollTable, because change the size of the scroll table could |
|
1538 |
+ |
// require an additional layout (ex. if window scroll bars show up). |
|
1539 |
+ |
ResizableWidgetCollection.get().updateWidgetSize( AbstractScrollTable.this ); |
|
1540 |
+ |
} |
|
1541 |
+ |
}; |
|
1542 |
+ |
|
|
1543 |
+ |
// Recalculate the ideal table widths of each column. |
|
1544 |
+ |
maybeRecalculateIdealColumnWidths( command ); |
|
1545 |
+ |
|
|
1546 |
+ |
// Calculate the new widths of the columns |
|
1547 |
+ |
List<ColumnWidthInfo> colWidths = null; |
|
1548 |
+ |
if ( resizePolicy == ResizePolicy.FILL_WIDTH ) |
|
1549 |
+ |
{ |
|
1550 |
+ |
colWidths = getFillColumnWidths( redrawInfo ); |
|
1551 |
+ |
} |
|
1552 |
+ |
else |
|
1553 |
+ |
{ |
|
1554 |
+ |
colWidths = getBoundedColumnWidths( true ); |
|
1555 |
+ |
} |
|
1556 |
+ |
applyNewColumnWidths( 0, colWidths, true ); |
|
1557 |
+ |
|
|
1558 |
+ |
// Update the overall height of the scroll table. This can only happen |
|
1559 |
+ |
// after the widths have been set because setting the width of cells can |
|
1560 |
+ |
// cause word wrap, which increases the height of the inner tables. |
|
1561 |
+ |
resizeTablesVertically(); |
|
1562 |
+ |
|
|
1563 |
+ |
// Reset the scroll position, which might be lost when we change the layout. |
|
1564 |
+ |
scrollTables( false ); |
|
1565 |
+ |
} |
|
1566 |
+ |
|
|
1567 |
+ |
/** |
|
1568 |
+ |
* Unsupported. |
|
1569 |
+ |
* |
|
1570 |
+ |
* @param child the widget to be removed |
|
1571 |
+ |
* |
|
1572 |
+ |
* @return false |
|
1573 |
+ |
* |
|
1574 |
+ |
* @throws UnsupportedOperationException |
|
1575 |
+ |
*/ |
|
1576 |
+ |
@Override |
|
1577 |
+ |
public boolean remove( Widget child ) |
|
1578 |
+ |
{ |
|
1579 |
+ |
throw new UnsupportedOperationException( "This panel does not support remove()" ); |
|
1580 |
+ |
} |
|
1581 |
+ |
|
|
1582 |
+ |
/** |
|
1583 |
+ |
* Reset the widths of all columns to their preferred sizes. |
|
1584 |
+ |
*/ |
|
1585 |
+ |
public void resetColumnWidths() |
|
1586 |
+ |
{ |
|
1587 |
+ |
applyNewColumnWidths( 0, getBoundedColumnWidths( false ), false ); |
|
1588 |
+ |
scrollTables( false ); |
|
1589 |
+ |
} |
|
1590 |
+ |
|
|
1591 |
+ |
/** |
|
1592 |
+ |
* Sets the amount of padding to be added around all cells. |
|
1593 |
+ |
* |
|
1594 |
+ |
* @param padding the cell padding, in pixels |
|
1595 |
+ |
*/ |
|
1596 |
+ |
public void setCellPadding( int padding ) |
|
1597 |
+ |
{ |
|
1598 |
+ |
headerTable.setCellPadding( padding ); |
|
1599 |
+ |
dataTable.setCellPadding( padding ); |
|
1600 |
+ |
if ( footerTable != null ) |
|
1601 |
+ |
{ |
|
1602 |
+ |
footerTable.setCellPadding( padding ); |
|
1603 |
+ |
} |
|
1604 |
+ |
redraw(); |
|
1605 |
+ |
} |
|
1606 |
+ |
|
|
1607 |
+ |
/** |
|
1608 |
+ |
* Sets the amount of spacing to be added around all cells. |
|
1609 |
+ |
* |
|
1610 |
+ |
* @param spacing the cell spacing, in pixels |
|
1611 |
+ |
*/ |
|
1612 |
+ |
public void setCellSpacing( int spacing ) |
|
1613 |
+ |
{ |
|
1614 |
+ |
headerTable.setCellSpacing( spacing ); |
|
1615 |
+ |
dataTable.setCellSpacing( spacing ); |
|
1616 |
+ |
if ( footerTable != null ) |
|
1617 |
+ |
{ |
|
1618 |
+ |
footerTable.setCellSpacing( spacing ); |
|
1619 |
+ |
} |
|
1620 |
+ |
redraw(); |
|
1621 |
+ |
} |
|
1622 |
+ |
|
|
1623 |
+ |
/** |
|
1624 |
+ |
* Set the resize policy applied to user actions that resize columns. |
|
1625 |
+ |
* |
|
1626 |
+ |
* @param columnResizePolicy the resize policy |
|
1627 |
+ |
*/ |
|
1628 |
+ |
public void setColumnResizePolicy( ColumnResizePolicy columnResizePolicy ) |
|
1629 |
+ |
{ |
|
1630 |
+ |
this.columnResizePolicy = columnResizePolicy; |
|
1631 |
+ |
updateFillWidthImage(); |
|
1632 |
+ |
} |
|
1633 |
+ |
|
|
1634 |
+ |
/** |
|
1635 |
+ |
* Set the width of a column. |
|
1636 |
+ |
* |
|
1637 |
+ |
* @param column the index of the column |
|
1638 |
+ |
* @param width the width in pixels |
|
1639 |
+ |
* |
|
1640 |
+ |
* @return the new column width |
|
1641 |
+ |
*/ |
|
1642 |
+ |
public int setColumnWidth( int column, int width ) |
|
1643 |
+ |
{ |
|
1644 |
+ |
// Constrain the size of the column |
|
1645 |
+ |
ColumnWidthInfo info = getColumnWidthInfo( column ); |
|
1646 |
+ |
if ( info.hasMaximumWidth() ) |
|
1647 |
+ |
{ |
|
1648 |
+ |
width = Math.min( width, info.getMaximumWidth() ); |
|
1649 |
+ |
} |
|
1650 |
+ |
if ( info.hasMinimumWidth() ) |
|
1651 |
+ |
{ |
|
1652 |
+ |
width = Math.max( width, info.getMinimumWidth() ); |
|
1653 |
+ |
} |
|
1654 |
+ |
|
|
1655 |
+ |
// Try to constrain the size of the grid |
|
1656 |
+ |
if ( resizePolicy.isSacrificial() ) |
|
1657 |
+ |
{ |
|
1658 |
+ |
// Get the sacrifice columns |
|
1659 |
+ |
int sacrificeColumn = column + 1; |
|
1660 |
+ |
int numColumns = dataTable.getColumnCount(); |
|
1661 |
+ |
int remainingColumns = numColumns - sacrificeColumn; |
|
1662 |
+ |
List<ColumnWidthInfo> infos = getColumnWidthInfo( sacrificeColumn, remainingColumns ); |
|
1663 |
+ |
|
|
1664 |
+ |
// Distribute the width over the sacrifice columns |
|
1665 |
+ |
int diff = width - getColumnWidth( column ); |
|
1666 |
+ |
int undistributed = columnResizer.distributeWidth( infos, -diff ); |
|
1667 |
+ |
|
|
1668 |
+ |
// Set the new column widths |
|
1669 |
+ |
applyNewColumnWidths( sacrificeColumn, infos, false ); |
|
1670 |
+ |
|
|
1671 |
+ |
// Prevent over resizing |
|
1672 |
+ |
if ( resizePolicy.isFixedWidth() ) |
|
1673 |
+ |
{ |
|
1674 |
+ |
width += undistributed; |
|
1675 |
+ |
} |
|
1676 |
+ |
} |
|
1677 |
+ |
|
|
1678 |
+ |
// Resize the column |
|
1679 |
+ |
int offset = getHeaderOffset(); |
|
1680 |
+ |
dataTable.setColumnWidth( column, width ); |
|
1681 |
+ |
headerTable.setColumnWidth( column + offset, width ); |
|
1682 |
+ |
if ( footerTable != null ) |
|
1683 |
+ |
{ |
|
1684 |
+ |
footerTable.setColumnWidth( column + offset, width ); |
|
1685 |
+ |
} |
|
1686 |
+ |
|
|
1687 |
+ |
// Reposition things as needed |
|
1688 |
+ |
impl.repositionSpacer( this, false ); |
|
1689 |
+ |
resizeTablesVertically(); |
|
1690 |
+ |
scrollTables( false ); |
|
1691 |
+ |
return width; |
|
1692 |
+ |
} |
|
1693 |
+ |
|
|
1694 |
+ |
/** |
|
1695 |
+ |
* Set the footer table that appears under the data table. If set to null, the |
|
1696 |
+ |
* footer table will not be shown. |
|
1697 |
+ |
* |
|
1698 |
+ |
* @param footerTable the table to use in the footer |
|
1699 |
+ |
*/ |
|
1700 |
+ |
public void setFooterTable( FixedWidthFlexTable footerTable ) |
|
1701 |
+ |
{ |
|
1702 |
+ |
// Disown the old footer table |
|
1703 |
+ |
if ( this.footerTable != null ) |
|
1704 |
+ |
{ |
|
1705 |
+ |
super.remove( this.footerTable ); |
|
1706 |
+ |
DOM.removeChild( absoluteElem, footerWrapper ); |
|
1707 |
+ |
} |
|
1708 |
+ |
|
|
1709 |
+ |
// Set the new footer table |
|
1710 |
+ |
this.footerTable = footerTable; |
|
1711 |
+ |
if ( footerTable != null ) |
|
1712 |
+ |
{ |
|
1713 |
+ |
footerTable.setCellSpacing( getCellSpacing() ); |
|
1714 |
+ |
footerTable.setCellPadding( getCellPadding() ); |
|
1715 |
+ |
prepareTable( footerTable, "footerTable" ); |
|
1716 |
+ |
if ( dataTable.getSelectionPolicy().hasInputColumn() ) |
|
1717 |
+ |
{ |
|
1718 |
+ |
footerTable.setColumnWidth( 0, dataTable.getInputColumnWidth() ); |
|
1719 |
+ |
} |
|
1720 |
+ |
|
|
1721 |
+ |
// Create the footer wrapper and spacer |
|
1722 |
+ |
if ( footerWrapper == null ) |
|
1723 |
+ |
{ |
|
1724 |
+ |
footerWrapper = createWrapper( "footerWrapper" ); |
|
1725 |
+ |
footerSpacer = impl.createSpacer( footerTable, footerWrapper ); |
|
1726 |
+ |
DOM.setEventListener( footerWrapper, this ); |
|
1727 |
+ |
DOM.sinkEvents( footerWrapper, Event.ONMOUSEUP ); |
|
1728 |
+ |
} |
|
1729 |
+ |
|
|
1730 |
+ |
// Adopt the header table into the panel |
|
1731 |
+ |
adoptTable( footerTable, footerWrapper, absoluteElem.getChildNodes().getLength() ); |
|
1732 |
+ |
} |
|
1733 |
+ |
redraw(); |
|
1734 |
+ |
} |
|
1735 |
+ |
|
|
1736 |
+ |
@Override |
|
1737 |
+ |
public void setHeight( String height ) |
|
1738 |
+ |
{ |
|
1739 |
+ |
this.lastHeight = height; |
|
1740 |
+ |
super.setHeight( height ); |
|
1741 |
+ |
resizeTablesVertically(); |
|
1742 |
+ |
} |
|
1743 |
+ |
|
|
1744 |
+ |
/** |
|
1745 |
+ |
* Set the resize policy of the table. |
|
1746 |
+ |
* |
|
1747 |
+ |
* @param resizePolicy the resize policy |
|
1748 |
+ |
*/ |
|
1749 |
+ |
public void setResizePolicy( ResizePolicy resizePolicy ) |
|
1750 |
+ |
{ |
|
1751 |
+ |
this.resizePolicy = resizePolicy; |
|
1752 |
+ |
updateFillWidthImage(); |
|
1753 |
+ |
redraw(); |
|
1754 |
+ |
} |
|
1755 |
+ |
|
|
1756 |
+ |
/** |
|
1757 |
+ |
* Set the scroll policy of the table. |
|
1758 |
+ |
* |
|
1759 |
+ |
* @param scrollPolicy the new scroll policy |
|
1760 |
+ |
*/ |
|
1761 |
+ |
public void setScrollPolicy( ScrollPolicy scrollPolicy ) |
|
1762 |
+ |
{ |
|
1763 |
+ |
if ( scrollPolicy == this.scrollPolicy ) |
|
1764 |
+ |
{ |
|
1765 |
+ |
return; |
|
1766 |
+ |
} |
|
1767 |
+ |
this.scrollPolicy = scrollPolicy; |
|
1768 |
+ |
|
|
1769 |
+ |
// Clear the heights of the wrappers |
|
1770 |
+ |
headerWrapper.getStyle().setProperty( "height", "" ); |
|
1771 |
+ |
dataWrapper.getStyle().setProperty( "height", "" ); |
|
1772 |
+ |
if ( footerWrapper != null ) |
|
1773 |
+ |
{ |
|
1774 |
+ |
footerWrapper.getStyle().setProperty( "height", "" ); |
|
1775 |
+ |
} |
|
1776 |
+ |
|
|
1777 |
+ |
if ( scrollPolicy == ScrollPolicy.DISABLED ) |
|
1778 |
+ |
{ |
|
1779 |
+ |
// Disabled scroll bars |
|
1780 |
+ |
dataWrapper.getStyle().setProperty( "height", "auto" ); |
|
1781 |
+ |
dataWrapper.getStyle().setProperty( "overflow", "" ); |
|
1782 |
+ |
} |
|
1783 |
+ |
else if ( scrollPolicy == ScrollPolicy.HORIZONTAL ) |
|
1784 |
+ |
{ |
|
1785 |
+ |
// Only show horizontal scroll bar |
|
1786 |
+ |
dataWrapper.getStyle().setProperty( "height", "auto" ); |
|
1787 |
+ |
dataWrapper.getStyle().setProperty( "overflow", "auto" ); |
|
1788 |
+ |
} |
|
1789 |
+ |
else if ( scrollPolicy == ScrollPolicy.BOTH ) |
|
1790 |
+ |
{ |
|
1791 |
+ |
// Show both scroll bars |
|
1792 |
+ |
if ( lastHeight != null ) |
|
1793 |
+ |
{ |
|
1794 |
+ |
super.setHeight( lastHeight ); |
|
1795 |
+ |
} |
|
1796 |
+ |
else |
|
1797 |
+ |
{ |
|
1798 |
+ |
super.setHeight( "" ); |
|
1799 |
+ |
} |
|
1800 |
+ |
dataWrapper.getStyle().setProperty( "overflow", "auto" ); |
|
1801 |
+ |
} |
|
1802 |
+ |
|
|
1803 |
+ |
// Resize the tables |
|
1804 |
+ |
impl.repositionSpacer( this, true ); |
|
1805 |
+ |
redraw(); |
|
1806 |
+ |
} |
|
1807 |
+ |
|
|
1808 |
+ |
/** |
|
1809 |
+ |
* Set the {@link SortPolicy} that defines what columns users can sort. |
|
1810 |
+ |
* |
|
1811 |
+ |
* @param sortPolicy the {@link SortPolicy} |
|
1812 |
+ |
*/ |
|
1813 |
+ |
public void setSortPolicy( SortPolicy sortPolicy ) |
|
1814 |
+ |
{ |
|
1815 |
+ |
this.sortPolicy = sortPolicy; |
|
1816 |
+ |
|
|
1817 |
+ |
// Remove the sorted indicator image |
|
1818 |
+ |
applySortedColumnIndicator( null, true ); |
|
1819 |
+ |
} |
|
1820 |
+ |
|
|
1821 |
+ |
/** |
|
1822 |
+ |
* Apply the sorted column indicator to a specific table cell in the header |
|
1823 |
+ |
* table. |
|
1824 |
+ |
* |
|
1825 |
+ |
* @param tdElem the cell in the header table, or null to remove it |
|
1826 |
+ |
* @param ascending true to apply the ascending indicator, false for |
|
1827 |
+ |
* descending |
|
1828 |
+ |
*/ |
|
1829 |
+ |
protected void applySortedColumnIndicator( Element tdElem, boolean ascending ) |
|
1830 |
+ |
{ |
|
1831 |
+ |
// Remove the sort indicator |
|
1832 |
+ |
if ( tdElem == null ) |
|
1833 |
+ |
{ |
|
1834 |
+ |
Element parent = DOM.getParent( sortedColumnWrapper ); |
|
1835 |
+ |
if ( parent != null ) |
|
1836 |
+ |
{ |
|
1837 |
+ |
parent.removeChild( sortedColumnWrapper ); |
|
1838 |
+ |
headerTable.clearIdealWidths(); |
|
1839 |
+ |
} |
|
1840 |
+ |
return; |
|
1841 |
+ |
} |
|
1842 |
+ |
|
|
1843 |
+ |
tdElem.appendChild( sortedColumnWrapper ); |
|
1844 |
+ |
if ( ascending ) |
|
1845 |
+ |
{ |
|
1846 |
+ |
sortedColumnWrapper.setInnerHTML( " " + images.scrollTableAscending().getHTML() ); |
|
1847 |
+ |
} |
|
1848 |
+ |
else |
|
1849 |
+ |
{ |
|
1850 |
+ |
sortedColumnWrapper.setInnerHTML( " " + images.scrollTableDescending().getHTML() ); |
|
1851 |
+ |
} |
|
1852 |
+ |
sortedRowIndex = -1; |
|
1853 |
+ |
sortedCellIndex = -1; |
|
1854 |
+ |
|
|
1855 |
+ |
// The column with the indicator now has a new ideal width |
|
1856 |
+ |
headerTable.clearIdealWidths(); |
|
1857 |
+ |
redraw(); |
|
1858 |
+ |
} |
|
1859 |
+ |
|
|
1860 |
+ |
/** |
|
1861 |
+ |
* Create a wrapper element that will hold a table. |
|
1862 |
+ |
* |
|
1863 |
+ |
* @param cssName the style name added to the base name |
|
1864 |
+ |
* |
|
1865 |
+ |
* @return a new wrapper element |
|
1866 |
+ |
*/ |
|
1867 |
+ |
protected Element createWrapper( String cssName ) |
|
1868 |
+ |
{ |
|
1869 |
+ |
Element wrapper = DOM.createDiv(); |
|
1870 |
+ |
wrapper.getStyle().setProperty( "width", "100%" ); |
|
1871 |
+ |
wrapper.getStyle().setProperty( "overflow", "hidden" ); |
|
1872 |
+ |
wrapper.getStyle().setPropertyPx( "padding", 0 ); |
|
1873 |
+ |
wrapper.getStyle().setPropertyPx( "margin", 0 ); |
|
1874 |
+ |
wrapper.getStyle().setPropertyPx( "border", 0 ); |
|
1875 |
+ |
if ( cssName != null ) |
|
1876 |
+ |
{ |
|
1877 |
+ |
setStyleName( wrapper, cssName ); |
|
1878 |
+ |
} |
|
1879 |
+ |
return wrapper; |
|
1880 |
+ |
} |
|
1881 |
+ |
|
|
1882 |
+ |
/** |
|
1883 |
+ |
* @return the wrapper element around the data table |
|
1884 |
+ |
*/ |
|
1885 |
+ |
protected Element getDataWrapper() |
|
1886 |
+ |
{ |
|
1887 |
+ |
return dataWrapper; |
|
1888 |
+ |
} |
|
1889 |
+ |
|
|
1890 |
+ |
/** |
|
1891 |
+ |
* Extend the columns to exactly fill the available space, if the current |
|
1892 |
+ |
* {@link ResizePolicy} requires it. |
|
1893 |
+ |
* |
|
1894 |
+ |
* @deprecated use {@link #redraw()} instead |
|
1895 |
+ |
*/ |
|
1896 |
+ |
@Deprecated |
|
1897 |
+ |
protected void maybeFillWidth() |
|
1898 |
+ |
{ |
|
1899 |
+ |
redraw(); |
|
1900 |
+ |
} |
|
1901 |
+ |
|
|
1902 |
+ |
/** |
|
1903 |
+ |
* Called just before a column is sorted because of a user click on the header |
|
1904 |
+ |
* row. |
|
1905 |
+ |
* |
|
1906 |
+ |
* @param row the row index that was clicked |
|
1907 |
+ |
* @param column the column index that was clicked |
|
1908 |
+ |
* |
|
1909 |
+ |
* @return true to sort, false to ignore |
|
1910 |
+ |
*/ |
|
1911 |
+ |
protected boolean onHeaderSort( int row, int column ) |
|
1912 |
+ |
{ |
|
1913 |
+ |
return true; |
|
1914 |
+ |
} |
|
1915 |
+ |
|
|
1916 |
+ |
@Override |
|
1917 |
+ |
protected void onLoad() |
|
1918 |
+ |
{ |
|
1919 |
+ |
ResizableWidgetCollection.get().add( this ); |
|
1920 |
+ |
redraw(); |
|
1921 |
+ |
} |
|
1922 |
+ |
|
|
1923 |
+ |
@Override |
|
1924 |
+ |
protected void onUnload() |
|
1925 |
+ |
{ |
|
1926 |
+ |
ResizableWidgetCollection.get().remove( this ); |
|
1927 |
+ |
} |
|
1928 |
+ |
|
|
1929 |
+ |
/** |
|
1930 |
+ |
* Fixes the table heights so the header is visible and the data takes up the |
|
1931 |
+ |
* remaining vertical space. |
|
1932 |
+ |
*/ |
|
1933 |
+ |
protected void resizeTablesVertically() |
|
1934 |
+ |
{ |
|
1935 |
+ |
if ( scrollPolicy == ScrollPolicy.DISABLED ) |
|
1936 |
+ |
{ |
|
1937 |
+ |
dataWrapper.getStyle().setProperty( "overflow", "auto" ); |
|
1938 |
+ |
dataWrapper.getStyle().setProperty( "overflow", "" ); |
|
1939 |
+ |
int height = Math.max( 1, absoluteElem.getOffsetHeight() ); |
|
1940 |
+ |
super.setHeight( height + "px" ); |
|
1941 |
+ |
} |
|
1942 |
+ |
else if ( scrollPolicy == ScrollPolicy.HORIZONTAL ) |
|
1943 |
+ |
{ |
|
1944 |
+ |
dataWrapper.getStyle().setProperty( "overflow", "hidden" ); |
|
1945 |
+ |
dataWrapper.getStyle().setProperty( "overflow", "auto" ); |
|
1946 |
+ |
int height = Math.max( 1, absoluteElem.getOffsetHeight() ); |
|
1947 |
+ |
super.setHeight( height + "px" ); |
|
1948 |
+ |
} |
|
1949 |
+ |
else |
|
1950 |
+ |
{ |
|
1951 |
+ |
applyTableWrapperSizes( getTableWrapperSizes() ); |
|
1952 |
+ |
dataWrapper.getStyle().setProperty( "width", "100%" ); |
|
1953 |
+ |
} |
|
1954 |
+ |
} |
|
1955 |
+ |
|
|
1956 |
+ |
/** |
|
1957 |
+ |
* Helper method that actually performs the vertical resizing. |
|
1958 |
+ |
* |
|
1959 |
+ |
* @deprecated use {@link #redraw()} instead |
|
1960 |
+ |
*/ |
|
1961 |
+ |
@Deprecated |
|
1962 |
+ |
protected void resizeTablesVerticallyNow() |
|
1963 |
+ |
{ |
|
1964 |
+ |
redraw(); |
|
1965 |
+ |
} |
|
1966 |
+ |
|
|
1967 |
+ |
/** |
|
1968 |
+ |
* Sets the scroll property of the header and footers wrappers when scrolling |
|
1969 |
+ |
* so that the header, footer, and data tables line up. |
|
1970 |
+ |
* |
|
1971 |
+ |
* @param baseHeader true to scroll the data table as well |
|
1972 |
+ |
*/ |
|
1973 |
+ |
protected void scrollTables( boolean baseHeader ) |
|
1974 |
+ |
{ |
|
1975 |
+ |
if ( scrollPolicy == ScrollPolicy.DISABLED ) |
|
1976 |
+ |
{ |
|
1977 |
+ |
return; |
|
1978 |
+ |
} |
|
1979 |
+ |
|
|
1980 |
+ |
if ( lastScrollLeft >= 0 ) |
|
1981 |
+ |
{ |
|
1982 |
+ |
headerWrapper.setScrollLeft( lastScrollLeft ); |
|
1983 |
+ |
if ( baseHeader ) |
|
1984 |
+ |
{ |
|
1985 |
+ |
dataWrapper.setScrollLeft( lastScrollLeft ); |
|
1986 |
+ |
} |
|
1987 |
+ |
if ( footerWrapper != null ) |
|
1988 |
+ |
{ |
|
1989 |
+ |
footerWrapper.setScrollLeft( lastScrollLeft ); |
|
1990 |
+ |
} |
|
1991 |
+ |
} |
|
1992 |
+ |
} |
|
1993 |
+ |
|
|
1994 |
+ |
/** |
|
1995 |
+ |
* @return the absolutely positioned wrapper element |
|
1996 |
+ |
*/ |
|
1997 |
+ |
Element getAbsoluteElement() |
|
1998 |
+ |
{ |
|
1999 |
+ |
return absoluteElem; |
|
2000 |
+ |
} |
|
2001 |
+ |
|
|
2002 |
+ |
/** |
|
2003 |
+ |
* Adopt a table into this {@link AbstractScrollTable} within its wrapper. |
|
2004 |
+ |
* |
|
2005 |
+ |
* @param table the table to adopt |
|
2006 |
+ |
* @param wrapper the wrapper element |
|
2007 |
+ |
* @param index the index to insert the wrapper in the main element |
|
2008 |
+ |
*/ |
|
2009 |
+ |
private void adoptTable( Widget table, Element wrapper, int index ) |
|
2010 |
+ |
{ |
|
2011 |
+ |
DOM.insertChild( absoluteElem, wrapper, index ); |
|
2012 |
+ |
add( table, wrapper ); |
|
2013 |
+ |
} |
|
2014 |
+ |
|
|
2015 |
+ |
/** |
|
2016 |
+ |
* Apply the new widths to a list of columns. |
|
2017 |
+ |
* |
|
2018 |
+ |
* @param startIndex the index of the first column |
|
2019 |
+ |
* @param infos the new column width info |
|
2020 |
+ |
* @param forced if false, only set column widths that have changed |
|
2021 |
+ |
*/ |
|
2022 |
+ |
private void applyNewColumnWidths( int startIndex, List<ColumnWidthInfo> infos, boolean forced ) |
|
2023 |
+ |
{ |
|
2024 |
+ |
// Infos can be null if the widths cannot be calculated |
|
2025 |
+ |
if ( infos == null ) |
|
2026 |
+ |
{ |
|
2027 |
+ |
return; |
|
2028 |
+ |
} |
|
2029 |
+ |
|
|
2030 |
+ |
int offset = getHeaderOffset(); |
|
2031 |
+ |
int numColumns = infos.size(); |
|
2032 |
+ |
for ( int i = 0; i < numColumns; i++ ) |
|
2033 |
+ |
{ |
|
2034 |
+ |
ColumnWidthInfo info = infos.get( i ); |
|
2035 |
+ |
int newWidth = info.getNewWidth(); |
|
2036 |
+ |
if ( forced || info.getCurrentWidth() != newWidth ) |
|
2037 |
+ |
{ |
|
2038 |
+ |
dataTable.setColumnWidth( startIndex + i, newWidth ); |
|
2039 |
+ |
headerTable.setColumnWidth( startIndex + i + offset, newWidth ); |
|
2040 |
+ |
if ( footerTable != null ) |
|
2041 |
+ |
{ |
|
2042 |
+ |
footerTable.setColumnWidth( startIndex + i + offset, newWidth ); |
|
2043 |
+ |
} |
|
2044 |
+ |
} |
|
2045 |
+ |
} |
|
2046 |
+ |
impl.repositionSpacer( this, false ); |
|
2047 |
+ |
} |
|
2048 |
+ |
|
|
2049 |
+ |
/** |
|
2050 |
+ |
* Apply the new sizes to the table wrappers. |
|
2051 |
+ |
* |
|
2052 |
+ |
* @param sizes the sizes to apply |
|
2053 |
+ |
*/ |
|
2054 |
+ |
private void applyTableWrapperSizes( TableHeightInfo sizes ) |
|
2055 |
+ |
{ |
|
2056 |
+ |
if ( sizes == null ) |
|
2057 |
+ |
{ |
|
2058 |
+ |
return; |
|
2059 |
+ |
} |
|
2060 |
+ |
|
|
2061 |
+ |
headerWrapper.getStyle().setPropertyPx( "height", sizes.headerTableHeight ); |
|
2062 |
+ |
if ( footerWrapper != null ) |
|
2063 |
+ |
{ |
|
2064 |
+ |
footerWrapper.getStyle().setPropertyPx( "height", sizes.footerTableHeight ); |
|
2065 |
+ |
} |
|
2066 |
+ |
dataWrapper.getStyle().setPropertyPx( "height", Math.max( sizes.dataTableHeight, 0 ) ); |
|
2067 |
+ |
dataWrapper.getStyle().setProperty( "overflow", "hidden" ); |
|
2068 |
+ |
dataWrapper.getStyle().setProperty( "overflow", "auto" ); |
|
2069 |
+ |
} |
|
2070 |
+ |
|
|
2071 |
+ |
/** |
|
2072 |
+ |
* Get the width available for the tables. |
|
2073 |
+ |
* |
|
2074 |
+ |
* @return the available width, or -1 if not defined |
|
2075 |
+ |
*/ |
|
2076 |
+ |
private int getAvailableWidth() |
|
2077 |
+ |
{ |
|
2078 |
+ |
int clientWidth = absoluteElem.getPropertyInt( "clientWidth" ); |
|
2079 |
+ |
if ( scrollPolicy == ScrollPolicy.BOTH ) |
|
2080 |
+ |
{ |
|
2081 |
+ |
int scrollbarWidth = mockScrollable.getOffsetWidth() - mockScrollable.getPropertyInt( "clientWidth" ); |
|
2082 |
+ |
clientWidth = absoluteElem.getPropertyInt( "clientWidth" ) - scrollbarWidth - 1; |
|
2083 |
+ |
} |
|
2084 |
+ |
return Math.max( clientWidth, -1 ); |
|
2085 |
+ |
} |
|
2086 |
+ |
|
|
2087 |
+ |
/** |
|
2088 |
+ |
* Get the widths of all columns, either to their preferred sizes or just |
|
2089 |
+ |
* ensure that they are within their min/max boundaries. |
|
2090 |
+ |
* |
|
2091 |
+ |
* @param boundsOnly true to only ensure the widths are within the bounds |
|
2092 |
+ |
* |
|
2093 |
+ |
* @return the column widths |
|
2094 |
+ |
*/ |
|
2095 |
+ |
private List<ColumnWidthInfo> getBoundedColumnWidths( boolean boundsOnly ) |
|
2096 |
+ |
{ |
|
2097 |
+ |
if ( !isAttached() ) |
|
2098 |
+ |
{ |
|
2099 |
+ |
return null; |
|
2100 |
+ |
} |
|
2101 |
+ |
|
|
2102 |
+ |
// Calculate the new column widths |
|
2103 |
+ |
int numColumns = dataTable.getColumnCount(); |
|
2104 |
+ |
int totalWidth = 0; |
|
2105 |
+ |
List<ColumnWidthInfo> colWidthInfos = getColumnWidthInfo( 0, numColumns ); |
|
2106 |
+ |
|
|
2107 |
+ |
// If we are reseting to original widths, set all widths to 0 |
|
2108 |
+ |
if ( !boundsOnly ) |
|
2109 |
+ |
{ |
|
2110 |
+ |
for ( ColumnWidthInfo info : colWidthInfos ) |
|
2111 |
+ |
{ |
|
2112 |
+ |
totalWidth += info.getCurrentWidth(); |
|
2113 |
+ |
info.setCurrentWidth( 0 ); |
|
2114 |
+ |
} |
|
2115 |
+ |
} |
|
2116 |
+ |
|
|
2117 |
+ |
// Run the resize algorithm |
|
2118 |
+ |
columnResizer.distributeWidth( colWidthInfos, totalWidth ); |
|
2119 |
+ |
|
|
2120 |
+ |
// Set the new column widths |
|
2121 |
+ |
return colWidthInfos; |
|
2122 |
+ |
} |
|
2123 |
+ |
|
|
2124 |
+ |
/** |
|
2125 |
+ |
* Get info about the width of a column. |
|
2126 |
+ |
* |
|
2127 |
+ |
* @param column the column index |
|
2128 |
+ |
* |
|
2129 |
+ |
* @return the info about the column width |
|
2130 |
+ |
*/ |
|
2131 |
+ |
private ColumnWidthInfo getColumnWidthInfo( int column ) |
|
2132 |
+ |
{ |
|
2133 |
+ |
int minWidth = getMinimumColumnWidth( column ); |
|
2134 |
+ |
int maxWidth = getMaximumColumnWidth( column ); |
|
2135 |
+ |
int preferredWidth = getPreferredColumnWidth( column ); |
|
2136 |
+ |
int curWidth = getColumnWidth( column ); |
|
2137 |
+ |
|
|
2138 |
+ |
// Adjust the widths if the columns are not truncatable, up to maxWidth |
|
2139 |
+ |
if ( !isColumnTruncatable( column ) ) |
|
2140 |
+ |
{ |
|
2141 |
+ |
maybeRecalculateIdealColumnWidths( null ); |
|
2142 |
+ |
int idealWidth = getDataTable().getIdealColumnWidth( column ); |
|
2143 |
+ |
if ( maxWidth != MaximumWidthProperty.NO_MAXIMUM_WIDTH ) |
|
2144 |
+ |
{ |
|
2145 |
+ |
idealWidth = Math.min( idealWidth, maxWidth ); |
|
2146 |
+ |
} |
|
2147 |
+ |
minWidth = Math.max( minWidth, idealWidth ); |
|
2148 |
+ |
} |
|
2149 |
+ |
if ( !isHeaderColumnTruncatable( column ) ) |
|
2150 |
+ |
{ |
|
2151 |
+ |
maybeRecalculateIdealColumnWidths( null ); |
|
2152 |
+ |
int idealWidth = getHeaderTable().getIdealColumnWidth( column + getHeaderOffset() ); |
|
2153 |
+ |
if ( maxWidth != MaximumWidthProperty.NO_MAXIMUM_WIDTH ) |
|
2154 |
+ |
{ |
|
2155 |
+ |
idealWidth = Math.min( idealWidth, maxWidth ); |
|
2156 |
+ |
} |
|
2157 |
+ |
minWidth = Math.max( minWidth, idealWidth ); |
|
2158 |
+ |
} |
|
2159 |
+ |
if ( footerTable != null && !isFooterColumnTruncatable( column ) ) |
|
2160 |
+ |
{ |
|
2161 |
+ |
maybeRecalculateIdealColumnWidths( null ); |
|
2162 |
+ |
int idealWidth = getFooterTable().getIdealColumnWidth( column + getHeaderOffset() ); |
|
2163 |
+ |
if ( maxWidth != MaximumWidthProperty.NO_MAXIMUM_WIDTH ) |
|
2164 |
+ |
{ |
|
2165 |
+ |
idealWidth = Math.min( idealWidth, maxWidth ); |
|
2166 |
+ |
} |
|
2167 |
+ |
minWidth = Math.max( minWidth, idealWidth ); |
|
2168 |
+ |
} |
|
2169 |
+ |
|
|
2170 |
+ |
return new ColumnWidthInfo( minWidth, maxWidth, preferredWidth, curWidth ); |
|
2171 |
+ |
} |
|
2172 |
+ |
|
|
2173 |
+ |
/** |
|
2174 |
+ |
* Get info about the width of multiple columns. |
|
2175 |
+ |
* |
|
2176 |
+ |
* @param column the start column index |
|
2177 |
+ |
* @param numColumns the number of columns |
|
2178 |
+ |
* |
|
2179 |
+ |
* @return the info about the column widths of the columns |
|
2180 |
+ |
*/ |
|
2181 |
+ |
private List<ColumnWidthInfo> getColumnWidthInfo( int column, int numColumns ) |
|
2182 |
+ |
{ |
|
2183 |
+ |
List<ColumnWidthInfo> infos = new ArrayList<ColumnWidthInfo>(); |
|
2184 |
+ |
for ( int i = 0; i < numColumns; i++ ) |
|
2185 |
+ |
{ |
|
2186 |
+ |
infos.add( getColumnWidthInfo( column + i ) ); |
|
2187 |
+ |
} |
|
2188 |
+ |
return infos; |
|
2189 |
+ |
} |
|
2190 |
+ |
|
|
2191 |
+ |
/** |
|
2192 |
+ |
* Get the column widths needed to fill with available ScrollTable width. |
|
2193 |
+ |
* |
|
2194 |
+ |
* @param info the optional precomputed sizes |
|
2195 |
+ |
* |
|
2196 |
+ |
* @return the column widths |
|
2197 |
+ |
*/ |
|
2198 |
+ |
private List<ColumnWidthInfo> getFillColumnWidths( TableWidthInfo info ) |
|
2199 |
+ |
{ |
|
2200 |
+ |
if ( !isAttached() ) |
|
2201 |
+ |
{ |
|
2202 |
+ |
return null; |
|
2203 |
+ |
} |
|
2204 |
+ |
|
|
2205 |
+ |
// Precompute some sizes |
|
2206 |
+ |
if ( info == null ) |
|
2207 |
+ |
{ |
|
2208 |
+ |
info = new TableWidthInfo( false ); |
|
2209 |
+ |
} |
|
2210 |
+ |
|
|
2211 |
+ |
// Calculate how much room we have to work with |
|
2212 |
+ |
int clientWidth = info.availableWidth; |
|
2213 |
+ |
if ( clientWidth <= 0 ) |
|
2214 |
+ |
{ |
|
2215 |
+ |
return null; |
|
2216 |
+ |
} |
|
2217 |
+ |
|
|
2218 |
+ |
// Calculate the difference and number of column to resize |
|
2219 |
+ |
int diff = 0; |
|
2220 |
+ |
int numColumns = 0; |
|
2221 |
+ |
{ |
|
2222 |
+ |
// Calculate the number of columns in each table |
|
2223 |
+ |
int numHeaderCols = 0; |
|
2224 |
+ |
int numDataCols = 0; |
|
2225 |
+ |
int numFooterCols = 0; |
|
2226 |
+ |
if ( info.headerTableWidth > 0 ) |
|
2227 |
+ |
{ |
|
2228 |
+ |
numHeaderCols = headerTable.getColumnCount() - getHeaderOffset(); |
|
2229 |
+ |
} |
|
2230 |
+ |
if ( info.dataTableWidth > 0 ) |
|
2231 |
+ |
{ |
|
2232 |
+ |
numDataCols = dataTable.getColumnCount(); |
|
2233 |
+ |
} |
|
2234 |
+ |
if ( footerTable != null && info.footerTableWidth > 0 ) |
|
2235 |
+ |
{ |
|
2236 |
+ |
numFooterCols = footerTable.getColumnCount() - getHeaderOffset(); |
|
2237 |
+ |
} |
|
2238 |
+ |
|
|
2239 |
+ |
// Determine the largest table |
|
2240 |
+ |
if ( numHeaderCols >= numDataCols && numHeaderCols >= numFooterCols ) |
|
2241 |
+ |
{ |
|
2242 |
+ |
numColumns = numHeaderCols; |
|
2243 |
+ |
diff = clientWidth - info.headerTableWidth; |
|
2244 |
+ |
} |
|
2245 |
+ |
else if ( numFooterCols >= numDataCols && numFooterCols >= numHeaderCols ) |
|
2246 |
+ |
{ |
|
2247 |
+ |
numColumns = numFooterCols; |
|
2248 |
+ |
diff = clientWidth - info.footerTableWidth; |
|
2249 |
+ |
} |
|
2250 |
+ |
else if ( numDataCols > 0 ) |
|
2251 |
+ |
{ |
|
2252 |
+ |
numColumns = numDataCols; |
|
2253 |
+ |
diff = clientWidth - info.dataTableWidth; |
|
2254 |
+ |
} |
|
2255 |
+ |
} |
|
2256 |
+ |
if ( numColumns <= 0 ) |
|
2257 |
+ |
{ |
|
2258 |
+ |
return null; |
|
2259 |
+ |
} |
|
2260 |
+ |
|
|
2261 |
+ |
// Calculate the new column widths |
|
2262 |
+ |
List<ColumnWidthInfo> colWidthInfos = getColumnWidthInfo( 0, numColumns ); |
|
2263 |
+ |
columnResizer.distributeWidth( colWidthInfos, diff ); |
|
2264 |
+ |
return colWidthInfos; |
|
2265 |
+ |
} |
|
2266 |
+ |
|
|
2267 |
+ |
/** |
|
2268 |
+ |
* Get the offset between the data and header and footer tables. An offset of |
|
2269 |
+ |
* one means that the header and footer table indexes are one greater than the |
|
2270 |
+ |
* data table indexes, probably because the data table contains a checkbox |
|
2271 |
+ |
* column. |
|
2272 |
+ |
* |
|
2273 |
+ |
* @return the offset |
|
2274 |
+ |
*/ |
|
2275 |
+ |
private int getHeaderOffset() |
|
2276 |
+ |
{ |
|
2277 |
+ |
if ( dataTable.getSelectionPolicy().hasInputColumn() ) |
|
2278 |
+ |
{ |
|
2279 |
+ |
return 1; |
|
2280 |
+ |
} |
|
2281 |
+ |
return 0; |
|
2282 |
+ |
} |
|
2283 |
+ |
|
|
2284 |
+ |
/** |
|
2285 |
+ |
* Returns the new heights of the header, data, and footer tables based on the |
|
2286 |
+ |
* {@link ScrollPolicy}. |
|
2287 |
+ |
* |
|
2288 |
+ |
* @return the new table heights, or null |
|
2289 |
+ |
*/ |
|
2290 |
+ |
private TableHeightInfo getTableWrapperSizes() |
|
2291 |
+ |
{ |
|
2292 |
+ |
// If we aren't attached, return immediately |
|
2293 |
+ |
if ( !isAttached() ) |
|
2294 |
+ |
{ |
|
2295 |
+ |
return null; |
|
2296 |
+ |
} |
|
2297 |
+ |
|
|
2298 |
+ |
// Heights only apply with vertical scrolling |
|
2299 |
+ |
if ( scrollPolicy == ScrollPolicy.DISABLED || scrollPolicy == ScrollPolicy.HORIZONTAL ) |
|
2300 |
+ |
{ |
|
2301 |
+ |
return null; |
|
2302 |
+ |
} |
|
2303 |
+ |
|
|
2304 |
+ |
// Give the data wrapper all remaining height |
|
2305 |
+ |
return new TableHeightInfo(); |
|
2306 |
+ |
} |
|
2307 |
+ |
|
|
2308 |
+ |
/** |
|
2309 |
+ |
* Recalculate the ideal columns widths of all inner tables. |
|
2310 |
+ |
* |
|
2311 |
+ |
* @param command an optional command to execute while recalculating |
|
2312 |
+ |
*/ |
|
2313 |
+ |
private void maybeRecalculateIdealColumnWidths( Command command ) |
|
2314 |
+ |
{ |
|
2315 |
+ |
// Calculations require that we are attached |
|
2316 |
+ |
if ( !isAttached() ) |
|
2317 |
+ |
{ |
|
2318 |
+ |
return; |
|
2319 |
+ |
} |
|
2320 |
+ |
|
|
2321 |
+ |
// Check if a recalculation is needed. |
|
2322 |
+ |
if ( headerTable.isIdealColumnWidthsCalculated() && dataTable.isIdealColumnWidthsCalculated() && (footerTable == null || footerTable.isIdealColumnWidthsCalculated()) ) |
|
2323 |
+ |
{ |
|
2324 |
+ |
if ( command != null ) |
|
2325 |
+ |
{ |
|
2326 |
+ |
command.execute(); |
|
2327 |
+ |
} |
|
2328 |
+ |
return; |
|
2329 |
+ |
} |
|
2330 |
+ |
|
|
2331 |
+ |
impl.recalculateIdealColumnWidths( this, command ); |
|
2332 |
+ |
} |
|
2333 |
+ |
|
|
2334 |
+ |
/** |
|
2335 |
+ |
* Prepare a table to be added to the {@link AbstractScrollTable}. |
|
2336 |
+ |
* |
|
2337 |
+ |
* @param table the table to prepare |
|
2338 |
+ |
* @param cssName the style name added to the base name |
|
2339 |
+ |
*/ |
|
2340 |
+ |
private void prepareTable( Widget table, String cssName ) |
|
2341 |
+ |
{ |
|
2342 |
+ |
Element tableElem = table.getElement(); |
|
2343 |
+ |
DOM.setStyleAttribute( tableElem, "margin", "0px" ); |
|
2344 |
+ |
DOM.setStyleAttribute( tableElem, "border", "0px" ); |
|
2345 |
+ |
table.addStyleName( cssName ); |
|
2346 |
+ |
} |
|
2347 |
+ |
|
|
2348 |
+ |
/** |
|
2349 |
+ |
* Show or hide to fillWidthImage depending on current policies. |
|
2350 |
+ |
*/ |
|
2351 |
+ |
private void updateFillWidthImage() |
|
2352 |
+ |
{ |
|
2353 |
+ |
if ( columnResizePolicy == ColumnResizePolicy.DISABLED || resizePolicy.isFixedWidth() ) |
|
2354 |
+ |
{ |
|
2355 |
+ |
fillWidthImage.setVisible( false ); |
|
2356 |
+ |
} |
|
2357 |
+ |
else |
|
2358 |
+ |
{ |
|
2359 |
+ |
fillWidthImage.setVisible( true ); |
|
2360 |
+ |
} |
2122 |
2361 |
|
} |
2123 |
|
- |
} |
2124 |
2362 |
|
} |