You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

Grid.java 97KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880
  1. /*
  2. * Copyright 2000-2014 Vaadin Ltd.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License"); you may not
  5. * use this file except in compliance with the License. You may obtain a copy of
  6. * the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
  12. * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
  13. * License for the specific language governing permissions and limitations under
  14. * the License.
  15. */
  16. package com.vaadin.client.ui.grid;
  17. import java.util.ArrayList;
  18. import java.util.Arrays;
  19. import java.util.Collection;
  20. import java.util.Collections;
  21. import java.util.HashSet;
  22. import java.util.List;
  23. import java.util.Set;
  24. import java.util.logging.Level;
  25. import java.util.logging.Logger;
  26. import com.google.gwt.core.client.Scheduler;
  27. import com.google.gwt.core.client.Scheduler.ScheduledCommand;
  28. import com.google.gwt.core.shared.GWT;
  29. import com.google.gwt.dom.client.BrowserEvents;
  30. import com.google.gwt.dom.client.Element;
  31. import com.google.gwt.dom.client.EventTarget;
  32. import com.google.gwt.dom.client.TableCellElement;
  33. import com.google.gwt.dom.client.TableRowElement;
  34. import com.google.gwt.dom.client.Touch;
  35. import com.google.gwt.event.dom.client.KeyCodeEvent;
  36. import com.google.gwt.event.dom.client.KeyCodes;
  37. import com.google.gwt.event.shared.HandlerRegistration;
  38. import com.google.gwt.touch.client.Point;
  39. import com.google.gwt.user.client.DOM;
  40. import com.google.gwt.user.client.Event;
  41. import com.google.gwt.user.client.Timer;
  42. import com.google.gwt.user.client.ui.Composite;
  43. import com.google.gwt.user.client.ui.HasVisibility;
  44. import com.google.gwt.user.client.ui.Widget;
  45. import com.vaadin.client.Util;
  46. import com.vaadin.client.data.DataChangeHandler;
  47. import com.vaadin.client.data.DataSource;
  48. import com.vaadin.client.ui.SubPartAware;
  49. import com.vaadin.client.ui.grid.EditorRow.State;
  50. import com.vaadin.client.ui.grid.GridFooter.FooterRow;
  51. import com.vaadin.client.ui.grid.GridHeader.HeaderRow;
  52. import com.vaadin.client.ui.grid.GridStaticSection.StaticCell;
  53. import com.vaadin.client.ui.grid.events.AbstractGridKeyEventHandler;
  54. import com.vaadin.client.ui.grid.events.BodyKeyDownHandler;
  55. import com.vaadin.client.ui.grid.events.BodyKeyPressHandler;
  56. import com.vaadin.client.ui.grid.events.BodyKeyUpHandler;
  57. import com.vaadin.client.ui.grid.events.FooterKeyDownHandler;
  58. import com.vaadin.client.ui.grid.events.FooterKeyPressHandler;
  59. import com.vaadin.client.ui.grid.events.FooterKeyUpHandler;
  60. import com.vaadin.client.ui.grid.events.GridKeyDownEvent;
  61. import com.vaadin.client.ui.grid.events.GridKeyPressEvent;
  62. import com.vaadin.client.ui.grid.events.GridKeyUpEvent;
  63. import com.vaadin.client.ui.grid.events.HeaderKeyDownHandler;
  64. import com.vaadin.client.ui.grid.events.HeaderKeyPressHandler;
  65. import com.vaadin.client.ui.grid.events.HeaderKeyUpHandler;
  66. import com.vaadin.client.ui.grid.events.ScrollEvent;
  67. import com.vaadin.client.ui.grid.events.ScrollHandler;
  68. import com.vaadin.client.ui.grid.renderers.ComplexRenderer;
  69. import com.vaadin.client.ui.grid.renderers.WidgetRenderer;
  70. import com.vaadin.client.ui.grid.selection.HasSelectionChangeHandlers;
  71. import com.vaadin.client.ui.grid.selection.SelectionChangeEvent;
  72. import com.vaadin.client.ui.grid.selection.SelectionChangeHandler;
  73. import com.vaadin.client.ui.grid.selection.SelectionModel;
  74. import com.vaadin.client.ui.grid.selection.SelectionModelMulti;
  75. import com.vaadin.client.ui.grid.selection.SelectionModelNone;
  76. import com.vaadin.client.ui.grid.selection.SelectionModelSingle;
  77. import com.vaadin.client.ui.grid.sort.Sort;
  78. import com.vaadin.client.ui.grid.sort.SortEvent;
  79. import com.vaadin.client.ui.grid.sort.SortEventHandler;
  80. import com.vaadin.client.ui.grid.sort.SortOrder;
  81. import com.vaadin.shared.ui.grid.GridConstants;
  82. import com.vaadin.shared.ui.grid.GridStaticCellType;
  83. import com.vaadin.shared.ui.grid.HeightMode;
  84. import com.vaadin.shared.ui.grid.Range;
  85. import com.vaadin.shared.ui.grid.ScrollDestination;
  86. import com.vaadin.shared.ui.grid.SortDirection;
  87. import com.vaadin.shared.ui.grid.SortEventOriginator;
  88. /**
  89. * A data grid view that supports columns and lazy loading of data rows from a
  90. * data source.
  91. *
  92. * <h3>Columns</h3>
  93. * <p>
  94. * The {@link GridColumn} class defines the renderer used to render a cell in
  95. * the grid. Implement {@link GridColumn#getValue(Object)} to retrieve the cell
  96. * value from the row object and return the cell renderer to render that cell.
  97. * </p>
  98. * <p>
  99. * {@link GridColumn}s contain other properties like the width of the column and
  100. * the visiblity of the column. If you want to change a column's properties
  101. * after it has been added to the grid you can get a column object for a
  102. * specific column index using {@link Grid#getColumn(int)}.
  103. * </p>
  104. * <p>
  105. *
  106. * TODO Explain about headers/footers once the multiple header/footer api has
  107. * been implemented
  108. *
  109. * <h3>Data sources</h3>
  110. * <p>
  111. * TODO Explain about what a data source is and how it should be implemented.
  112. * </p>
  113. *
  114. * @param <T>
  115. * The row type of the grid. The row type is the POJO type from where
  116. * the data is retrieved into the column cells.
  117. * @since
  118. * @author Vaadin Ltd
  119. */
  120. public class Grid<T> extends Composite implements
  121. HasSelectionChangeHandlers<T>, SubPartAware {
  122. public static abstract class AbstractGridKeyEvent<HANDLER extends AbstractGridKeyEventHandler>
  123. extends KeyCodeEvent<HANDLER> {
  124. /**
  125. * Enum describing different section of Grid.
  126. */
  127. public enum GridSection {
  128. HEADER, BODY, FOOTER
  129. }
  130. private Grid<?> grid;
  131. protected Cell activeCell;
  132. private final Type<HANDLER> associatedType = new Type<HANDLER>(
  133. getBrowserEventType(), this);
  134. public AbstractGridKeyEvent(Grid<?> grid) {
  135. this.grid = grid;
  136. }
  137. protected abstract String getBrowserEventType();
  138. /**
  139. * Gets the Grid instance for this event.
  140. *
  141. * @return grid
  142. */
  143. public Grid<?> getGrid() {
  144. return grid;
  145. }
  146. /**
  147. * Gets the active cell for this event.
  148. *
  149. * @return active cell
  150. */
  151. public Cell getActiveCell() {
  152. return activeCell;
  153. }
  154. @Override
  155. protected void dispatch(HANDLER handler) {
  156. EventTarget target = getNativeEvent().getEventTarget();
  157. if (Element.is(target)
  158. && Util.findWidget(Element.as(target), null) == grid) {
  159. activeCell = grid.activeCellHandler.getActiveCell();
  160. GridSection section = GridSection.FOOTER;
  161. final RowContainer container = grid.activeCellHandler.container;
  162. if (container == grid.escalator.getHeader()) {
  163. section = GridSection.HEADER;
  164. } else if (container == grid.escalator.getBody()) {
  165. section = GridSection.BODY;
  166. }
  167. doDispatch(handler, section);
  168. }
  169. }
  170. protected abstract void doDispatch(HANDLER handler, GridSection seciton);
  171. @Override
  172. public Type<HANDLER> getAssociatedType() {
  173. return associatedType;
  174. }
  175. }
  176. private GridKeyDownEvent keyDown = new GridKeyDownEvent(this);
  177. private GridKeyUpEvent keyUp = new GridKeyUpEvent(this);
  178. private GridKeyPressEvent keyPress = new GridKeyPressEvent(this);
  179. private class ActiveCellHandler {
  180. private RowContainer container = escalator.getBody();
  181. private int activeRow = 0;
  182. private Range activeCellRange = Range.withLength(0, 1);
  183. private int lastActiveBodyRow = 0;
  184. private int lastActiveHeaderRow = 0;
  185. private int lastActiveFooterRow = 0;
  186. private TableCellElement cellWithActiveStyle = null;
  187. private TableRowElement rowWithActiveStyle = null;
  188. public ActiveCellHandler() {
  189. sinkEvents(getNavigationEvents());
  190. }
  191. private Cell getActiveCell() {
  192. return new Cell(activeRow, activeCellRange.getStart(),
  193. cellWithActiveStyle);
  194. }
  195. /**
  196. * Sets style names for given cell when needed.
  197. */
  198. public void updateActiveCellStyle(FlyweightCell cell,
  199. RowContainer cellContainer) {
  200. int cellRow = cell.getRow();
  201. int cellColumn = cell.getColumn();
  202. int colSpan = cell.getColSpan();
  203. boolean columnActive = Range.withLength(cellColumn, colSpan)
  204. .intersects(activeCellRange);
  205. if (cellContainer == container) {
  206. // Cell is in the current container
  207. if (cellRow == activeRow && columnActive) {
  208. if (cellWithActiveStyle != cell.getElement()) {
  209. // Cell is correct but it does not have active style
  210. if (cellWithActiveStyle != null) {
  211. // Remove old active style
  212. setStyleName(cellWithActiveStyle,
  213. cellActiveStyleName, false);
  214. }
  215. cellWithActiveStyle = cell.getElement();
  216. // Add active style to correct cell.
  217. setStyleName(cellWithActiveStyle, cellActiveStyleName,
  218. true);
  219. }
  220. } else if (cellWithActiveStyle == cell.getElement()) {
  221. // Due to escalator reusing cells, a new cell has the same
  222. // element but is not the active cell.
  223. setStyleName(cellWithActiveStyle, cellActiveStyleName,
  224. false);
  225. cellWithActiveStyle = null;
  226. }
  227. }
  228. if (cellContainer == escalator.getHeader()
  229. || cellContainer == escalator.getFooter()) {
  230. // Correct header and footer column also needs highlighting
  231. setStyleName(cell.getElement(), headerFooterActiveStyleName,
  232. columnActive);
  233. }
  234. }
  235. /**
  236. * Sets active row style name for given row if needed.
  237. *
  238. * @param row
  239. * a row object
  240. */
  241. public void updateActiveRowStyle(Row row) {
  242. if (activeRow == row.getRow() && container == escalator.getBody()) {
  243. if (row.getElement() != rowWithActiveStyle) {
  244. // Row should have active style but does not have it.
  245. if (rowWithActiveStyle != null) {
  246. setStyleName(rowWithActiveStyle, rowActiveStyleName,
  247. false);
  248. }
  249. rowWithActiveStyle = row.getElement();
  250. setStyleName(rowWithActiveStyle, rowActiveStyleName, true);
  251. }
  252. } else if (rowWithActiveStyle == row.getElement()
  253. || (container != escalator.getBody() && rowWithActiveStyle != null)) {
  254. // Remove active style.
  255. setStyleName(rowWithActiveStyle, rowActiveStyleName, false);
  256. rowWithActiveStyle = null;
  257. }
  258. }
  259. /**
  260. * Sets currently active cell to a cell in given container with given
  261. * indices.
  262. *
  263. * @param row
  264. * new active row
  265. * @param column
  266. * new active column
  267. * @param container
  268. * new container
  269. */
  270. private void setActiveCell(int row, int column, RowContainer container) {
  271. if (row == activeRow && activeCellRange.contains(column)
  272. && container == this.container) {
  273. refreshRow(activeRow);
  274. return;
  275. }
  276. int oldRow = activeRow;
  277. activeRow = row;
  278. Range oldRange = activeCellRange;
  279. if (container == escalator.getBody()) {
  280. scrollToRow(activeRow);
  281. activeCellRange = Range.withLength(column, 1);
  282. } else {
  283. int i = 0;
  284. Element cell = container.getRowElement(activeRow)
  285. .getFirstChildElement();
  286. do {
  287. int colSpan = cell
  288. .getPropertyInt(FlyweightCell.COLSPAN_ATTR);
  289. Range cellRange = Range.withLength(i, colSpan);
  290. if (cellRange.contains(column)) {
  291. activeCellRange = cellRange;
  292. break;
  293. }
  294. cell = cell.getNextSiblingElement();
  295. ++i;
  296. } while (cell != null);
  297. }
  298. if (column >= escalator.getColumnConfiguration()
  299. .getFrozenColumnCount()) {
  300. escalator.scrollToColumn(column, ScrollDestination.ANY, 10);
  301. }
  302. if (this.container == container) {
  303. if (oldRange.equals(activeCellRange) && oldRow != activeRow) {
  304. refreshRow(oldRow);
  305. } else {
  306. refreshHeader();
  307. refreshFooter();
  308. }
  309. } else {
  310. RowContainer oldContainer = this.container;
  311. this.container = container;
  312. if (oldContainer == escalator.getBody()) {
  313. lastActiveBodyRow = oldRow;
  314. } else if (oldContainer == escalator.getHeader()) {
  315. lastActiveHeaderRow = oldRow;
  316. } else {
  317. lastActiveFooterRow = oldRow;
  318. }
  319. if (!oldRange.equals(activeCellRange)) {
  320. refreshHeader();
  321. refreshFooter();
  322. if (oldContainer == escalator.getBody()) {
  323. oldContainer.refreshRows(oldRow, 1);
  324. }
  325. } else {
  326. oldContainer.refreshRows(oldRow, 1);
  327. }
  328. }
  329. refreshRow(activeRow);
  330. }
  331. /**
  332. * Sets currently active cell used for keyboard navigation. Note that
  333. * active cell is not JavaScript {@code document.activeElement}.
  334. *
  335. * @param cell
  336. * a cell object
  337. */
  338. public void setActiveCell(Cell cell) {
  339. setActiveCell(cell.getRow(), cell.getColumn(),
  340. escalator.findRowContainer(cell.getElement()));
  341. }
  342. /**
  343. * Gets list of events that can be used for active cell navigation.
  344. *
  345. * @return list of navigation related event types
  346. */
  347. public Collection<String> getNavigationEvents() {
  348. return Arrays.asList(BrowserEvents.KEYDOWN, BrowserEvents.CLICK);
  349. }
  350. /**
  351. * Handle events that can change the currently active cell.
  352. */
  353. public void handleNavigationEvent(Event event, Cell cell) {
  354. if (event.getType().equals(BrowserEvents.CLICK)) {
  355. setActiveCell(cell);
  356. // Grid should have focus when clicked.
  357. getElement().focus();
  358. } else if (event.getType().equals(BrowserEvents.KEYDOWN)) {
  359. int newRow = activeRow;
  360. RowContainer newContainer = container;
  361. int newColumn = activeCellRange.getStart();
  362. switch (event.getKeyCode()) {
  363. case KeyCodes.KEY_DOWN:
  364. ++newRow;
  365. break;
  366. case KeyCodes.KEY_UP:
  367. --newRow;
  368. break;
  369. case KeyCodes.KEY_RIGHT:
  370. if (activeCellRange.getEnd() >= getVisibleColumnIndices()
  371. .size()) {
  372. return;
  373. }
  374. ++newColumn;
  375. break;
  376. case KeyCodes.KEY_LEFT:
  377. if (newColumn == 0) {
  378. return;
  379. }
  380. --newColumn;
  381. break;
  382. case KeyCodes.KEY_TAB:
  383. if (event.getShiftKey()) {
  384. newContainer = getPreviousContainer(container);
  385. } else {
  386. newContainer = getNextContainer(container);
  387. }
  388. if (newContainer == container) {
  389. return;
  390. }
  391. break;
  392. default:
  393. return;
  394. }
  395. if (newContainer != container) {
  396. if (newContainer == escalator.getBody()) {
  397. newRow = lastActiveBodyRow;
  398. } else if (newContainer == escalator.getHeader()) {
  399. newRow = lastActiveHeaderRow;
  400. } else {
  401. newRow = lastActiveFooterRow;
  402. }
  403. } else if (newRow < 0) {
  404. newContainer = getPreviousContainer(newContainer);
  405. if (newContainer == container) {
  406. newRow = 0;
  407. } else if (newContainer == escalator.getBody()) {
  408. newRow = getLastVisibleRowIndex();
  409. } else {
  410. newRow = newContainer.getRowCount() - 1;
  411. }
  412. } else if (newRow >= container.getRowCount()) {
  413. newContainer = getNextContainer(newContainer);
  414. if (newContainer == container) {
  415. newRow = container.getRowCount() - 1;
  416. } else if (newContainer == escalator.getBody()) {
  417. newRow = getFirstVisibleRowIndex();
  418. } else {
  419. newRow = 0;
  420. }
  421. }
  422. if (newContainer.getRowCount() == 0) {
  423. // There are no rows in the container. Can't change the
  424. // active cell.
  425. return;
  426. }
  427. event.preventDefault();
  428. event.stopPropagation();
  429. setActiveCell(newRow, newColumn, newContainer);
  430. }
  431. }
  432. private RowContainer getPreviousContainer(RowContainer current) {
  433. if (current == escalator.getFooter()) {
  434. current = escalator.getBody();
  435. } else if (current == escalator.getBody()) {
  436. current = escalator.getHeader();
  437. } else {
  438. return current;
  439. }
  440. if (current.getRowCount() == 0) {
  441. return getPreviousContainer(current);
  442. }
  443. return current;
  444. }
  445. private RowContainer getNextContainer(RowContainer current) {
  446. if (current == escalator.getHeader()) {
  447. current = escalator.getBody();
  448. } else if (current == escalator.getBody()) {
  449. current = escalator.getFooter();
  450. } else {
  451. return current;
  452. }
  453. if (current.getRowCount() == 0) {
  454. return getNextContainer(current);
  455. }
  456. return current;
  457. }
  458. private void refreshRow(int row) {
  459. container.refreshRows(row, 1);
  460. }
  461. /**
  462. * Offset active cell range by given integer.
  463. *
  464. * @param offset
  465. * offset for fixing active cell range
  466. */
  467. public void offsetRangeBy(int offset) {
  468. activeCellRange = activeCellRange.offsetBy(offset);
  469. }
  470. /*
  471. * Informs ActiveCellHandler that certain range of rows has been added.
  472. * ActiveCellHandler will fix indices accordingly.
  473. *
  474. * @param added a range of added rows
  475. */
  476. public void rowsAdded(Range added) {
  477. if (added.getStart() <= activeRow) {
  478. setActiveCell(activeRow + added.length(),
  479. activeCellRange.getStart(), container);
  480. }
  481. }
  482. /**
  483. * Informs ActiveCellHandler that certain range of rows has been
  484. * removed. ActiveCellHandler will fix indices accordingly.
  485. *
  486. * @param removed
  487. * a range of removed rows
  488. */
  489. public void rowsRemoved(Range removed) {
  490. int activeColumn = activeCellRange.getStart();
  491. if (container != escalator.getBody()) {
  492. return;
  493. } else if (!removed.contains(activeRow)) {
  494. if (removed.getStart() > activeRow) {
  495. return;
  496. }
  497. setActiveCell(activeRow - removed.length(), activeColumn,
  498. container);
  499. } else {
  500. if (container.getRowCount() > removed.getEnd()) {
  501. setActiveCell(removed.getStart(), activeColumn, container);
  502. } else if (removed.getStart() > 0) {
  503. setActiveCell(removed.getStart() - 1, activeColumn,
  504. container);
  505. } else {
  506. if (escalator.getHeader().getRowCount() > 0) {
  507. setActiveCell(lastActiveHeaderRow, activeColumn,
  508. escalator.getHeader());
  509. } else if (escalator.getFooter().getRowCount() > 0) {
  510. setActiveCell(lastActiveFooterRow, activeColumn,
  511. escalator.getFooter());
  512. }
  513. }
  514. }
  515. }
  516. }
  517. private class SelectionColumn extends GridColumn<Boolean, T> {
  518. private boolean initDone = false;
  519. public SelectionColumn(final Renderer<Boolean> selectColumnRenderer) {
  520. super(selectColumnRenderer);
  521. }
  522. public void initDone() {
  523. initDone = true;
  524. }
  525. @Override
  526. public void setVisible(boolean visible) {
  527. if (!visible && initDone) {
  528. throw new UnsupportedOperationException("The selection "
  529. + "column cannot be modified after init");
  530. } else {
  531. super.setVisible(visible);
  532. }
  533. }
  534. @Override
  535. public void setWidth(int pixels) {
  536. if (pixels != getWidth() && initDone) {
  537. throw new UnsupportedOperationException("The selection "
  538. + "column cannot be modified after init");
  539. } else {
  540. super.setWidth(pixels);
  541. }
  542. }
  543. @Override
  544. public Boolean getValue(T row) {
  545. return Boolean.valueOf(isSelected(row));
  546. }
  547. }
  548. /**
  549. * Helper class for performing sorting through the user interface. Controls
  550. * the sort() method, reporting USER as the event originator. This is a
  551. * completely internal class, and is, as such, safe to re-name should a more
  552. * descriptive name come to mind.
  553. */
  554. private final class UserSorter {
  555. private final Timer timer;
  556. private Cell scheduledCell;
  557. private boolean scheduledMultisort;
  558. private UserSorter() {
  559. timer = new Timer() {
  560. @Override
  561. public void run() {
  562. UserSorter.this.sort(scheduledCell, scheduledMultisort);
  563. }
  564. };
  565. }
  566. /**
  567. * Toggle sorting for a cell. If the multisort parameter is set to true,
  568. * the cell's sort order is modified as a natural part of a multi-sort
  569. * chain. If false, the sorting order is set to ASCENDING for that
  570. * cell's column. If that column was already the only sorted column in
  571. * the Grid, the sort direction is flipped.
  572. *
  573. * @param cell
  574. * a valid cell reference
  575. * @param multisort
  576. * whether the sort command should act as a multi-sort stack
  577. * or not
  578. */
  579. public void sort(Cell cell, boolean multisort) {
  580. final GridColumn<?, T> column = getColumnFromVisibleIndex(cell
  581. .getColumn());
  582. final SortOrder so = getSortOrder(column);
  583. if (multisort) {
  584. // If the sort order exists, replace existing value with its
  585. // opposite
  586. if (so != null) {
  587. final int idx = sortOrder.indexOf(so);
  588. sortOrder.set(idx, so.getOpposite());
  589. } else {
  590. // If it doesn't, just add a new sort order to the end of
  591. // the list
  592. sortOrder.add(new SortOrder(column));
  593. }
  594. } else {
  595. // Since we're doing single column sorting, first clear the
  596. // list. Then, if the sort order existed, add its opposite,
  597. // otherwise just add a new sort value
  598. int items = sortOrder.size();
  599. sortOrder.clear();
  600. if (so != null && items == 1) {
  601. sortOrder.add(so.getOpposite());
  602. } else {
  603. sortOrder.add(new SortOrder(column));
  604. }
  605. }
  606. // sortOrder has been changed; tell the Grid to re-sort itself by
  607. // user request.
  608. Grid.this.sort(SortEventOriginator.USER);
  609. }
  610. /**
  611. * Perform a sort after a delay.
  612. *
  613. * @param delay
  614. * delay, in milliseconds
  615. */
  616. public void sortAfterDelay(int delay, Cell cell, boolean multisort) {
  617. scheduledCell = cell;
  618. scheduledMultisort = multisort;
  619. timer.schedule(delay);
  620. }
  621. /**
  622. * Check if a delayed sort command has been issued but not yet carried
  623. * out.
  624. *
  625. * @return a boolean value
  626. */
  627. public boolean isDelayedSortScheduled() {
  628. return timer.isRunning();
  629. }
  630. /**
  631. * Cancel a scheduled sort.
  632. */
  633. public void cancelDelayedSort() {
  634. timer.cancel();
  635. }
  636. }
  637. /**
  638. * Escalator used internally by grid to render the rows
  639. */
  640. private Escalator escalator = GWT.create(Escalator.class);
  641. private final GridHeader header = GWT.create(GridHeader.class);
  642. private final GridFooter footer = GWT.create(GridFooter.class);
  643. /**
  644. * List of columns in the grid. Order defines the visible order.
  645. */
  646. private final List<GridColumn<?, T>> columns = new ArrayList<GridColumn<?, T>>();
  647. /**
  648. * The datasource currently in use. <em>Note:</em> it is <code>null</code>
  649. * on initialization, but not after that.
  650. */
  651. private DataSource<T> dataSource;
  652. /**
  653. * Currently available row range in DataSource.
  654. */
  655. private Range currentDataAvailable = Range.withLength(0, 0);
  656. /**
  657. * The last column frozen counter from the left
  658. */
  659. private GridColumn<?, T> lastFrozenColumn;
  660. /**
  661. * Current sort order. The (private) sort() method reads this list to
  662. * determine the order in which to present rows.
  663. */
  664. private List<SortOrder> sortOrder = new ArrayList<SortOrder>();
  665. private Renderer<Boolean> selectColumnRenderer = null;
  666. private SelectionColumn selectionColumn;
  667. private String rowHasDataStyleName;
  668. private String rowSelectedStyleName;
  669. private String cellActiveStyleName;
  670. private String rowActiveStyleName;
  671. private String headerFooterActiveStyleName;
  672. /**
  673. * Current selection model.
  674. */
  675. private SelectionModel<T> selectionModel;
  676. protected final ActiveCellHandler activeCellHandler;
  677. private final UserSorter sorter = new UserSorter();
  678. private final EditorRow<T> editorRow = GWT.create(EditorRow.class);
  679. /**
  680. * Enumeration for easy setting of selection mode.
  681. */
  682. public enum SelectionMode {
  683. /**
  684. * Shortcut for {@link SelectionModelSingle}.
  685. */
  686. SINGLE {
  687. @Override
  688. protected <T> SelectionModel<T> createModel() {
  689. return new SelectionModelSingle<T>();
  690. }
  691. },
  692. /**
  693. * Shortcut for {@link SelectionModelMulti}.
  694. */
  695. MULTI {
  696. @Override
  697. protected <T> SelectionModel<T> createModel() {
  698. return new SelectionModelMulti<T>();
  699. }
  700. },
  701. /**
  702. * Shortcut for {@link SelectionModelNone}.
  703. */
  704. NONE {
  705. @Override
  706. protected <T> SelectionModel<T> createModel() {
  707. return new SelectionModelNone<T>();
  708. }
  709. };
  710. protected abstract <T> SelectionModel<T> createModel();
  711. }
  712. /**
  713. * Base class for grid columns internally used by the Grid. The user should
  714. * use {@link GridColumn} when creating new columns.
  715. *
  716. * @param <C>
  717. * the column type
  718. *
  719. * @param <T>
  720. * the row type
  721. */
  722. static abstract class AbstractGridColumn<C, T> implements HasVisibility {
  723. /**
  724. * the column is associated with
  725. */
  726. private Grid<T> grid;
  727. /**
  728. * Should the column be visible in the grid
  729. */
  730. private boolean visible = true;
  731. /**
  732. * Width of column in pixels
  733. */
  734. private int width = 100;
  735. /**
  736. * Renderer for rendering a value into the cell
  737. */
  738. private Renderer<? super C> bodyRenderer;
  739. private boolean sortable = false;
  740. /**
  741. * Constructs a new column with a custom renderer.
  742. *
  743. * @param renderer
  744. * The renderer to use for rendering the cells
  745. */
  746. public AbstractGridColumn(Renderer<? super C> renderer) {
  747. if (renderer == null) {
  748. throw new IllegalArgumentException("Renderer cannot be null.");
  749. }
  750. bodyRenderer = renderer;
  751. }
  752. /**
  753. * Internally used by the grid to set itself
  754. *
  755. * @param grid
  756. */
  757. private void setGrid(Grid<T> grid) {
  758. if (this.grid != null && grid != null) {
  759. // Trying to replace grid
  760. throw new IllegalStateException(
  761. "Column already is attached to grid. Remove the column first from the grid and then add it.");
  762. }
  763. this.grid = grid;
  764. }
  765. /**
  766. * Is the column visible. By default all columns are visible.
  767. *
  768. * @return <code>true</code> if the column is visible
  769. */
  770. @Override
  771. public boolean isVisible() {
  772. return visible;
  773. }
  774. /**
  775. * Sets a column as visible in the grid.
  776. *
  777. * @param visible
  778. * <code>true</code> if the column should be displayed in the
  779. * grid
  780. */
  781. @Override
  782. public void setVisible(boolean visible) {
  783. if (this.visible == visible) {
  784. return;
  785. }
  786. /*
  787. * We need to guarantee that both insertColumns and removeColumns
  788. * have this particular column accessible. Therefore, if we're
  789. * turning the column visible, it's set before the other logic.
  790. * Analogously, if we're turning the column invisible, we do that
  791. * only after the logic has been performed.
  792. */
  793. if (visible) {
  794. this.visible = true;
  795. }
  796. if (grid != null) {
  797. int index = findIndexOfColumn();
  798. ColumnConfiguration conf = grid.escalator
  799. .getColumnConfiguration();
  800. if (visible) {
  801. conf.insertColumns(index, 1);
  802. } else {
  803. conf.removeColumns(index, 1);
  804. }
  805. }
  806. if (!visible) {
  807. this.visible = false;
  808. }
  809. if (grid != null) {
  810. for (HeaderRow row : grid.getHeader().getRows()) {
  811. row.calculateColspans();
  812. }
  813. for (FooterRow row : grid.getFooter().getRows()) {
  814. row.calculateColspans();
  815. }
  816. }
  817. }
  818. /**
  819. * Returns the data that should be rendered into the cell. By default
  820. * returning Strings and Widgets are supported. If the return type is a
  821. * String then it will be treated as preformatted text.
  822. * <p>
  823. * To support other types you will need to pass a custom renderer to the
  824. * column via the column constructor.
  825. *
  826. * @param row
  827. * The row object that provides the cell content.
  828. *
  829. * @return The cell content
  830. */
  831. public abstract C getValue(T row);
  832. /**
  833. * The renderer to render the cell width. By default renders the data as
  834. * a String or adds the widget into the cell if the column type is of
  835. * widget type.
  836. *
  837. * @return The renderer to render the cell content with
  838. */
  839. public Renderer<? super C> getRenderer() {
  840. return bodyRenderer;
  841. }
  842. /**
  843. * Finds the index of this column instance
  844. *
  845. */
  846. private int findIndexOfColumn() {
  847. return grid.findVisibleColumnIndex((GridColumn<?, T>) this);
  848. }
  849. /**
  850. * Sets the pixel width of the column. Use a negative value for the grid
  851. * to autosize column based on content and available space
  852. *
  853. * @param pixels
  854. * the width in pixels or negative for auto sizing
  855. */
  856. public void setWidth(int pixels) {
  857. width = pixels;
  858. if (grid != null && isVisible()) {
  859. int index = findIndexOfColumn();
  860. ColumnConfiguration conf = grid.escalator
  861. .getColumnConfiguration();
  862. conf.setColumnWidth(index, pixels);
  863. }
  864. }
  865. /**
  866. * Returns the pixel width of the column
  867. *
  868. * @return pixel width of the column
  869. */
  870. public int getWidth() {
  871. return width;
  872. }
  873. /**
  874. * Enables sort indicators for the grid.
  875. * <p>
  876. * <b>Note:</b>The API can still sort the column even if this is set to
  877. * <code>false</code>.
  878. *
  879. * @param sortable
  880. * <code>true</code> when column sort indicators are visible.
  881. */
  882. public void setSortable(boolean sortable) {
  883. if (this.sortable != sortable) {
  884. this.sortable = sortable;
  885. grid.refreshHeader();
  886. }
  887. }
  888. /**
  889. * Are sort indicators shown for the column.
  890. *
  891. * @return <code>true</code> if the column is sortable
  892. */
  893. public boolean isSortable() {
  894. return sortable;
  895. }
  896. }
  897. protected class BodyUpdater implements EscalatorUpdater {
  898. @Override
  899. public void preAttach(Row row, Iterable<FlyweightCell> cellsToAttach) {
  900. for (FlyweightCell cell : cellsToAttach) {
  901. Renderer<?> renderer = findRenderer(cell);
  902. if (renderer instanceof ComplexRenderer) {
  903. ((ComplexRenderer<?>) renderer).init(cell);
  904. }
  905. }
  906. }
  907. @Override
  908. public void postAttach(Row row, Iterable<FlyweightCell> attachedCells) {
  909. for (FlyweightCell cell : attachedCells) {
  910. Renderer<?> renderer = findRenderer(cell);
  911. if (renderer instanceof WidgetRenderer) {
  912. WidgetRenderer<?, ?> widgetRenderer = (WidgetRenderer<?, ?>) renderer;
  913. Widget widget = widgetRenderer.createWidget();
  914. assert widget != null : "WidgetRenderer.createWidget() returned null. It should return a widget.";
  915. assert widget.getParent() == null : "WidgetRenderer.createWidget() returned a widget which already is attached.";
  916. assert cell.getElement().getChildCount() == 0 : "Cell content should be empty when adding Widget";
  917. // Physical attach
  918. cell.getElement().appendChild(widget.getElement());
  919. // Logical attach
  920. setParent(widget, Grid.this);
  921. }
  922. }
  923. }
  924. @Override
  925. public void update(Row row, Iterable<FlyweightCell> cellsToUpdate) {
  926. int rowIndex = row.getRow();
  927. TableRowElement rowElement = row.getElement();
  928. T rowData = dataSource.getRow(rowIndex);
  929. boolean hasData = rowData != null;
  930. // Assign stylename for rows with data
  931. boolean usedToHaveData = rowElement
  932. .hasClassName(rowHasDataStyleName);
  933. if (usedToHaveData != hasData) {
  934. setStyleName(rowElement, rowHasDataStyleName, hasData);
  935. }
  936. // Assign stylename for selected rows
  937. if (hasData) {
  938. setStyleName(rowElement, rowSelectedStyleName,
  939. isSelected(rowData));
  940. } else if (usedToHaveData) {
  941. setStyleName(rowElement, rowSelectedStyleName, false);
  942. }
  943. activeCellHandler.updateActiveRowStyle(row);
  944. for (FlyweightCell cell : cellsToUpdate) {
  945. GridColumn<?, T> column = getColumnFromVisibleIndex(cell
  946. .getColumn());
  947. assert column != null : "Column was not found from cell ("
  948. + cell.getColumn() + "," + cell.getRow() + ")";
  949. activeCellHandler.updateActiveCellStyle(cell,
  950. escalator.getBody());
  951. Renderer renderer = column.getRenderer();
  952. if (renderer instanceof ComplexRenderer) {
  953. // Hide cell content if needed
  954. ComplexRenderer clxRenderer = (ComplexRenderer) renderer;
  955. if (hasData) {
  956. if (!usedToHaveData) {
  957. // Prepare cell for rendering
  958. clxRenderer.setContentVisible(cell, true);
  959. }
  960. Object value = column.getValue(rowData);
  961. clxRenderer.render(cell, value);
  962. } else {
  963. // Prepare cell for no data
  964. clxRenderer.setContentVisible(cell, false);
  965. }
  966. } else if (hasData) {
  967. // Simple renderers just render
  968. Object value = column.getValue(rowData);
  969. renderer.render(cell, value);
  970. } else {
  971. // Clear cell if there is no data
  972. cell.getElement().removeAllChildren();
  973. }
  974. }
  975. }
  976. @Override
  977. public void preDetach(Row row, Iterable<FlyweightCell> cellsToDetach) {
  978. for (FlyweightCell cell : cellsToDetach) {
  979. Renderer renderer = findRenderer(cell);
  980. if (renderer instanceof WidgetRenderer) {
  981. Widget w = Util.findWidget(cell.getElement()
  982. .getFirstChildElement(), Widget.class);
  983. if (w != null) {
  984. // Logical detach
  985. setParent(w, null);
  986. // Physical detach
  987. cell.getElement().removeChild(w.getElement());
  988. }
  989. }
  990. }
  991. }
  992. @Override
  993. public void postDetach(Row row, Iterable<FlyweightCell> detachedCells) {
  994. for (FlyweightCell cell : detachedCells) {
  995. Renderer renderer = findRenderer(cell);
  996. if (renderer instanceof ComplexRenderer) {
  997. ((ComplexRenderer) renderer).destroy(cell);
  998. }
  999. }
  1000. }
  1001. }
  1002. protected class StaticSectionUpdater implements EscalatorUpdater {
  1003. private GridStaticSection<?> section;
  1004. private RowContainer container;
  1005. public StaticSectionUpdater(GridStaticSection<?> section,
  1006. RowContainer container) {
  1007. super();
  1008. this.section = section;
  1009. this.container = container;
  1010. }
  1011. @Override
  1012. public void update(Row row, Iterable<FlyweightCell> cellsToUpdate) {
  1013. GridStaticSection.StaticRow<?> staticRow = section.getRow(row
  1014. .getRow());
  1015. final List<Integer> columnIndices = getVisibleColumnIndices();
  1016. for (FlyweightCell cell : cellsToUpdate) {
  1017. int index = columnIndices.get(cell.getColumn());
  1018. final StaticCell metadata = staticRow.getCell(index);
  1019. // Decorate default row with sorting indicators
  1020. if (staticRow instanceof HeaderRow) {
  1021. addSortingIndicatorsToHeaderRow((HeaderRow) staticRow, cell);
  1022. }
  1023. // Assign colspan to cell before rendering
  1024. cell.setColSpan(metadata.getColspan());
  1025. switch (metadata.getType()) {
  1026. case TEXT:
  1027. cell.getElement().setInnerText(metadata.getText());
  1028. break;
  1029. case HTML:
  1030. cell.getElement().setInnerHTML(metadata.getHtml());
  1031. break;
  1032. case WIDGET:
  1033. preDetach(row, Arrays.asList(cell));
  1034. cell.getElement().setInnerHTML("");
  1035. postAttach(row, Arrays.asList(cell));
  1036. break;
  1037. }
  1038. activeCellHandler.updateActiveCellStyle(cell, container);
  1039. }
  1040. }
  1041. private void addSortingIndicatorsToHeaderRow(HeaderRow headerRow,
  1042. FlyweightCell cell) {
  1043. cleanup(cell);
  1044. GridColumn<?, ?> column = getColumnFromVisibleIndex(cell
  1045. .getColumn());
  1046. SortOrder sortingOrder = getSortOrder(column);
  1047. if (!headerRow.isDefault() || !column.isSortable()
  1048. || sortingOrder == null) {
  1049. // Only apply sorting indicators to sortable header columns in
  1050. // the default header row
  1051. return;
  1052. }
  1053. Element cellElement = cell.getElement();
  1054. if (SortDirection.ASCENDING == sortingOrder.getDirection()) {
  1055. cellElement.addClassName("sort-asc");
  1056. } else {
  1057. cellElement.addClassName("sort-desc");
  1058. }
  1059. int sortIndex = Grid.this.getSortOrder().indexOf(sortingOrder);
  1060. if (sortIndex > -1 && Grid.this.getSortOrder().size() > 1) {
  1061. // Show sort order indicator if column is
  1062. // sorted and other sorted columns also exists.
  1063. cellElement.setAttribute("sort-order",
  1064. String.valueOf(sortIndex + 1));
  1065. }
  1066. }
  1067. /**
  1068. * Finds the sort order for this column
  1069. */
  1070. private SortOrder getSortOrder(GridColumn<?, ?> column) {
  1071. for (SortOrder order : Grid.this.getSortOrder()) {
  1072. if (order.getColumn() == column) {
  1073. return order;
  1074. }
  1075. }
  1076. return null;
  1077. }
  1078. private void cleanup(FlyweightCell cell) {
  1079. Element cellElement = cell.getElement();
  1080. cellElement.removeAttribute("sort-order");
  1081. cellElement.removeClassName("sort-desc");
  1082. cellElement.removeClassName("sort-asc");
  1083. }
  1084. @Override
  1085. public void preAttach(Row row, Iterable<FlyweightCell> cellsToAttach) {
  1086. }
  1087. @Override
  1088. public void postAttach(Row row, Iterable<FlyweightCell> attachedCells) {
  1089. GridStaticSection.StaticRow<?> gridRow = section.getRow(row
  1090. .getRow());
  1091. List<Integer> columnIndices = getVisibleColumnIndices();
  1092. for (FlyweightCell cell : attachedCells) {
  1093. int index = columnIndices.get(cell.getColumn());
  1094. StaticCell metadata = gridRow.getCell(index);
  1095. /*
  1096. * If the cell contains widgets that are not currently attach
  1097. * then attach them now.
  1098. */
  1099. if (GridStaticCellType.WIDGET.equals(metadata.getType())) {
  1100. final Widget widget = metadata.getWidget();
  1101. final Element cellElement = cell.getElement();
  1102. if (!widget.isAttached()) {
  1103. // Physical attach
  1104. cellElement.appendChild(widget.getElement());
  1105. // Logical attach
  1106. setParent(widget, Grid.this);
  1107. }
  1108. }
  1109. }
  1110. }
  1111. @Override
  1112. public void preDetach(Row row, Iterable<FlyweightCell> cellsToDetach) {
  1113. if (section.getRowCount() > row.getRow()) {
  1114. GridStaticSection.StaticRow<?> gridRow = section.getRow(row
  1115. .getRow());
  1116. List<Integer> columnIndices = getVisibleColumnIndices();
  1117. for (FlyweightCell cell : cellsToDetach) {
  1118. int index = columnIndices.get(cell.getColumn());
  1119. StaticCell metadata = gridRow.getCell(index);
  1120. if (GridStaticCellType.WIDGET.equals(metadata.getType())
  1121. && metadata.getWidget().isAttached()) {
  1122. Widget widget = metadata.getWidget();
  1123. // Logical detach
  1124. setParent(widget, null);
  1125. // Physical detach
  1126. widget.getElement().removeFromParent();
  1127. }
  1128. }
  1129. }
  1130. }
  1131. @Override
  1132. public void postDetach(Row row, Iterable<FlyweightCell> detachedCells) {
  1133. }
  1134. };
  1135. /**
  1136. * Creates a new instance.
  1137. */
  1138. public Grid() {
  1139. initWidget(escalator);
  1140. getElement().setTabIndex(0);
  1141. activeCellHandler = new ActiveCellHandler();
  1142. setStylePrimaryName("v-grid");
  1143. escalator.getHeader().setEscalatorUpdater(createHeaderUpdater());
  1144. escalator.getBody().setEscalatorUpdater(createBodyUpdater());
  1145. escalator.getFooter().setEscalatorUpdater(createFooterUpdater());
  1146. header.setGrid(this);
  1147. HeaderRow defaultRow = header.appendRow();
  1148. header.setDefaultRow(defaultRow);
  1149. footer.setGrid(this);
  1150. editorRow.setGrid(this);
  1151. setSelectionMode(SelectionMode.SINGLE);
  1152. escalator.addScrollHandler(new ScrollHandler() {
  1153. @Override
  1154. public void onScroll(ScrollEvent event) {
  1155. fireEvent(new ScrollEvent());
  1156. }
  1157. });
  1158. escalator
  1159. .addRowVisibilityChangeHandler(new RowVisibilityChangeHandler() {
  1160. @Override
  1161. public void onRowVisibilityChange(
  1162. RowVisibilityChangeEvent event) {
  1163. if (dataSource != null) {
  1164. dataSource.ensureAvailability(
  1165. event.getFirstVisibleRow(),
  1166. event.getVisibleRowCount());
  1167. }
  1168. }
  1169. });
  1170. // Default action on SelectionChangeEvents. Refresh the body so changed
  1171. // become visible.
  1172. addSelectionChangeHandler(new SelectionChangeHandler<T>() {
  1173. @Override
  1174. public void onSelectionChange(SelectionChangeEvent<T> event) {
  1175. refreshBody();
  1176. }
  1177. });
  1178. // Sink header events and key events
  1179. sinkEvents(getHeader().getConsumedEvents());
  1180. sinkEvents(Arrays.asList(BrowserEvents.KEYDOWN, BrowserEvents.KEYUP,
  1181. BrowserEvents.KEYPRESS, BrowserEvents.DBLCLICK));
  1182. // Make ENTER and SHIFT+ENTER in the header perform sorting
  1183. addHeaderKeyUpHandler(new HeaderKeyUpHandler() {
  1184. @Override
  1185. public void onKeyUp(GridKeyUpEvent event) {
  1186. if (event.getNativeKeyCode() != KeyCodes.KEY_ENTER) {
  1187. return;
  1188. }
  1189. sorter.sort(event.getActiveCell(), event.isShiftKeyDown());
  1190. }
  1191. });
  1192. }
  1193. @Override
  1194. public void setStylePrimaryName(String style) {
  1195. super.setStylePrimaryName(style);
  1196. escalator.setStylePrimaryName(style);
  1197. editorRow.setStylePrimaryName(style);
  1198. rowHasDataStyleName = getStylePrimaryName() + "-row-has-data";
  1199. rowSelectedStyleName = getStylePrimaryName() + "-row-selected";
  1200. cellActiveStyleName = getStylePrimaryName() + "-cell-active";
  1201. headerFooterActiveStyleName = getStylePrimaryName() + "-header-active";
  1202. rowActiveStyleName = getStylePrimaryName() + "-row-active";
  1203. if (isAttached()) {
  1204. refreshHeader();
  1205. refreshBody();
  1206. refreshFooter();
  1207. }
  1208. }
  1209. /**
  1210. * Creates the escalator updater used to update the header rows in this
  1211. * grid. The updater is invoked when header rows or columns are added or
  1212. * removed, or the content of existing header cells is changed.
  1213. *
  1214. * @return the new header updater instance
  1215. *
  1216. * @see GridHeader
  1217. * @see Grid#getHeader()
  1218. */
  1219. protected EscalatorUpdater createHeaderUpdater() {
  1220. return new StaticSectionUpdater(header, escalator.getHeader());
  1221. }
  1222. /**
  1223. * Creates the escalator updater used to update the body rows in this grid.
  1224. * The updater is invoked when body rows or columns are added or removed,
  1225. * the content of body cells is changed, or the body is scrolled to expose
  1226. * previously hidden content.
  1227. *
  1228. * @return the new body updater instance
  1229. */
  1230. protected EscalatorUpdater createBodyUpdater() {
  1231. return new BodyUpdater();
  1232. }
  1233. /**
  1234. * Creates the escalator updater used to update the footer rows in this
  1235. * grid. The updater is invoked when header rows or columns are added or
  1236. * removed, or the content of existing header cells is changed.
  1237. *
  1238. * @return the new footer updater instance
  1239. *
  1240. * @see GridFooter
  1241. * @see #getFooter()
  1242. */
  1243. protected EscalatorUpdater createFooterUpdater() {
  1244. return new StaticSectionUpdater(footer, escalator.getFooter());
  1245. }
  1246. /**
  1247. * Refreshes header or footer rows on demand
  1248. *
  1249. * @param rows
  1250. * The row container
  1251. * @param firstRowIsVisible
  1252. * is the first row visible
  1253. * @param isHeader
  1254. * <code>true</code> if we refreshing the header, else assumed
  1255. * the footer
  1256. */
  1257. private void refreshRowContainer(RowContainer rows,
  1258. GridStaticSection<?> section) {
  1259. // Add or Remove rows on demand
  1260. int rowDiff = section.getVisibleRowCount() - rows.getRowCount();
  1261. if (rowDiff > 0) {
  1262. rows.insertRows(0, rowDiff);
  1263. } else if (rowDiff < 0) {
  1264. rows.removeRows(0, -rowDiff);
  1265. }
  1266. // Refresh all the rows
  1267. if (rows.getRowCount() > 0) {
  1268. rows.refreshRows(0, rows.getRowCount());
  1269. }
  1270. }
  1271. /**
  1272. * Refreshes all header rows
  1273. */
  1274. void refreshHeader() {
  1275. refreshRowContainer(escalator.getHeader(), header);
  1276. }
  1277. /**
  1278. * Refreshes all body rows
  1279. */
  1280. private void refreshBody() {
  1281. escalator.getBody().refreshRows(0, escalator.getBody().getRowCount());
  1282. }
  1283. /**
  1284. * Refreshes all footer rows
  1285. */
  1286. void refreshFooter() {
  1287. refreshRowContainer(escalator.getFooter(), footer);
  1288. }
  1289. /**
  1290. * Adds a column as the last column in the grid.
  1291. *
  1292. * @param column
  1293. * the column to add
  1294. */
  1295. public void addColumn(GridColumn<?, T> column) {
  1296. addColumn(column, getColumnCount());
  1297. }
  1298. /**
  1299. * Inserts a column into a specific position in the grid.
  1300. *
  1301. * @param index
  1302. * the index where the column should be inserted into
  1303. * @param column
  1304. * the column to add
  1305. * @throws IllegalStateException
  1306. * if Grid's current selection model renders a selection column,
  1307. * and {@code index} is 0.
  1308. */
  1309. public void addColumn(GridColumn<?, T> column, int index) {
  1310. if (column == selectionColumn) {
  1311. throw new IllegalArgumentException("The selection column many "
  1312. + "not be added manually");
  1313. } else if (selectionColumn != null && index == 0) {
  1314. throw new IllegalStateException("A column cannot be inserted "
  1315. + "before the selection column");
  1316. }
  1317. addColumnSkipSelectionColumnCheck(column, index);
  1318. }
  1319. private void addColumnSkipSelectionColumnCheck(GridColumn<?, T> column,
  1320. int index) {
  1321. // Register column with grid
  1322. columns.add(index, column);
  1323. header.addColumn(column, index);
  1324. footer.addColumn(column, index);
  1325. // Register this grid instance with the column
  1326. ((AbstractGridColumn<?, T>) column).setGrid(this);
  1327. // Insert column into escalator
  1328. if (column.isVisible()) {
  1329. int visibleIndex = findVisibleColumnIndex(column);
  1330. ColumnConfiguration conf = escalator.getColumnConfiguration();
  1331. // Insert column
  1332. conf.insertColumns(visibleIndex, 1);
  1333. // Transfer column width from column object to escalator
  1334. conf.setColumnWidth(visibleIndex, column.getWidth());
  1335. }
  1336. if (lastFrozenColumn != null
  1337. && ((AbstractGridColumn<?, T>) lastFrozenColumn)
  1338. .findIndexOfColumn() < index) {
  1339. refreshFrozenColumns();
  1340. }
  1341. // Sink all renderer events
  1342. Set<String> events = new HashSet<String>();
  1343. events.addAll(getConsumedEventsForRenderer(column.getRenderer()));
  1344. sinkEvents(events);
  1345. }
  1346. private void sinkEvents(Collection<String> events) {
  1347. assert events != null;
  1348. int eventsToSink = 0;
  1349. for (String typeName : events) {
  1350. int typeInt = Event.getTypeInt(typeName);
  1351. if (typeInt < 0) {
  1352. // Type not recognized by typeInt
  1353. sinkBitlessEvent(typeName);
  1354. } else {
  1355. eventsToSink |= typeInt;
  1356. }
  1357. }
  1358. if (eventsToSink > 0) {
  1359. sinkEvents(eventsToSink);
  1360. }
  1361. }
  1362. protected int findVisibleColumnIndex(GridColumn<?, T> column) {
  1363. int idx = 0;
  1364. for (GridColumn<?, T> c : columns) {
  1365. if (c == column) {
  1366. return idx;
  1367. } else if (c.isVisible()) {
  1368. idx++;
  1369. }
  1370. }
  1371. return -1;
  1372. }
  1373. protected GridColumn<?, T> getColumnFromVisibleIndex(int index) {
  1374. int idx = -1;
  1375. for (GridColumn<?, T> c : columns) {
  1376. if (c.isVisible()) {
  1377. idx++;
  1378. }
  1379. if (index == idx) {
  1380. return c;
  1381. }
  1382. }
  1383. return null;
  1384. }
  1385. private Renderer<?> findRenderer(FlyweightCell cell) {
  1386. GridColumn<?, T> column = getColumnFromVisibleIndex(cell.getColumn());
  1387. assert column != null : "Could not find column at index:"
  1388. + cell.getColumn();
  1389. return column.getRenderer();
  1390. }
  1391. /**
  1392. * Removes a column from the grid.
  1393. *
  1394. * @param column
  1395. * the column to remove
  1396. */
  1397. public void removeColumn(GridColumn<?, T> column) {
  1398. if (column != null && column.equals(selectionColumn)) {
  1399. throw new IllegalArgumentException(
  1400. "The selection column may not be removed manually.");
  1401. }
  1402. removeColumnSkipSelectionColumnCheck(column);
  1403. }
  1404. private void removeColumnSkipSelectionColumnCheck(GridColumn<?, T> column) {
  1405. int columnIndex = columns.indexOf(column);
  1406. int visibleIndex = findVisibleColumnIndex(column);
  1407. columns.remove(columnIndex);
  1408. header.removeColumn(columnIndex);
  1409. footer.removeColumn(columnIndex);
  1410. // de-register column with grid
  1411. ((AbstractGridColumn<?, T>) column).setGrid(null);
  1412. if (column.isVisible()) {
  1413. ColumnConfiguration conf = escalator.getColumnConfiguration();
  1414. conf.removeColumns(visibleIndex, 1);
  1415. }
  1416. if (column.equals(lastFrozenColumn)) {
  1417. setLastFrozenColumn(null);
  1418. } else {
  1419. refreshFrozenColumns();
  1420. }
  1421. }
  1422. /**
  1423. * Returns the amount of columns in the grid.
  1424. *
  1425. * @return The number of columns in the grid
  1426. */
  1427. public int getColumnCount() {
  1428. return columns.size();
  1429. }
  1430. /**
  1431. * Returns a list of columns in the grid.
  1432. *
  1433. * @return A unmodifiable list of the columns in the grid
  1434. */
  1435. public List<GridColumn<?, T>> getColumns() {
  1436. return Collections.unmodifiableList(new ArrayList<GridColumn<?, T>>(
  1437. columns));
  1438. }
  1439. /**
  1440. * Returns a column by its index in the grid.
  1441. *
  1442. * @param index
  1443. * the index of the column
  1444. * @return The column in the given index
  1445. * @throws IllegalArgumentException
  1446. * if the column index does not exist in the grid
  1447. */
  1448. public GridColumn<?, T> getColumn(int index)
  1449. throws IllegalArgumentException {
  1450. if (index < 0 || index >= columns.size()) {
  1451. throw new IllegalStateException("Column not found.");
  1452. }
  1453. return columns.get(index);
  1454. }
  1455. /**
  1456. * Returns a list of column indices that are currently visible.
  1457. *
  1458. * @return a list of indices
  1459. */
  1460. private List<Integer> getVisibleColumnIndices() {
  1461. List<Integer> indices = new ArrayList<Integer>(getColumnCount());
  1462. for (int i = 0; i < getColumnCount(); i++) {
  1463. if (getColumn(i).isVisible()) {
  1464. indices.add(i);
  1465. }
  1466. }
  1467. return indices;
  1468. }
  1469. /**
  1470. * Returns the header section of this grid. The default header contains a
  1471. * single row displaying the column captions.
  1472. *
  1473. * @return the header
  1474. */
  1475. public GridHeader getHeader() {
  1476. return header;
  1477. }
  1478. /**
  1479. * Returns the footer section of this grid. The default footer is empty.
  1480. *
  1481. * @return the footer
  1482. */
  1483. public GridFooter getFooter() {
  1484. return footer;
  1485. }
  1486. public EditorRow<T> getEditorRow() {
  1487. return editorRow;
  1488. }
  1489. protected Escalator getEscalator() {
  1490. return escalator;
  1491. }
  1492. /**
  1493. * {@inheritDoc}
  1494. * <p>
  1495. * <em>Note:</em> This method will change the widget's size in the browser
  1496. * only if {@link #getHeightMode()} returns {@link HeightMode#CSS}.
  1497. *
  1498. * @see #setHeightMode(HeightMode)
  1499. */
  1500. @Override
  1501. public void setHeight(String height) {
  1502. escalator.setHeight(height);
  1503. }
  1504. @Override
  1505. public void setWidth(String width) {
  1506. escalator.setWidth(width);
  1507. }
  1508. /**
  1509. * Sets the data source used by this grid.
  1510. *
  1511. * @param dataSource
  1512. * the data source to use, not null
  1513. * @throws IllegalArgumentException
  1514. * if <code>dataSource</code> is <code>null</code>
  1515. */
  1516. public void setDataSource(DataSource<T> dataSource)
  1517. throws IllegalArgumentException {
  1518. if (dataSource == null) {
  1519. throw new IllegalArgumentException("dataSource can't be null.");
  1520. }
  1521. selectionModel.reset();
  1522. if (this.dataSource != null) {
  1523. this.dataSource.setDataChangeHandler(null);
  1524. }
  1525. this.dataSource = dataSource;
  1526. dataSource.setDataChangeHandler(new DataChangeHandler() {
  1527. @Override
  1528. public void dataUpdated(int firstIndex, int numberOfItems) {
  1529. escalator.getBody().refreshRows(firstIndex, numberOfItems);
  1530. }
  1531. @Override
  1532. public void dataRemoved(int firstIndex, int numberOfItems) {
  1533. escalator.getBody().removeRows(firstIndex, numberOfItems);
  1534. Range removed = Range.withLength(firstIndex, numberOfItems);
  1535. activeCellHandler.rowsRemoved(removed);
  1536. }
  1537. @Override
  1538. public void dataAdded(int firstIndex, int numberOfItems) {
  1539. escalator.getBody().insertRows(firstIndex, numberOfItems);
  1540. Range added = Range.withLength(firstIndex, numberOfItems);
  1541. activeCellHandler.rowsAdded(added);
  1542. }
  1543. @Override
  1544. public void dataAvailable(int firstIndex, int numberOfItems) {
  1545. currentDataAvailable = Range.withLength(firstIndex,
  1546. numberOfItems);
  1547. fireEvent(new DataAvailableEvent(currentDataAvailable));
  1548. }
  1549. });
  1550. int previousRowCount = escalator.getBody().getRowCount();
  1551. if (previousRowCount != 0) {
  1552. escalator.getBody().removeRows(0, previousRowCount);
  1553. }
  1554. int estimatedSize = dataSource.getEstimatedSize();
  1555. if (estimatedSize > 0) {
  1556. escalator.getBody().insertRows(0, estimatedSize);
  1557. }
  1558. }
  1559. /**
  1560. * Gets the {@Link DataSource} for this Grid.
  1561. *
  1562. * @return the data source used by this grid
  1563. */
  1564. public DataSource<T> getDataSource() {
  1565. return dataSource;
  1566. }
  1567. /**
  1568. * Sets the rightmost frozen column in the grid.
  1569. * <p>
  1570. * All columns up to and including the given column will be frozen in place
  1571. * when the grid is scrolled sideways.
  1572. *
  1573. * @param lastFrozenColumn
  1574. * the rightmost column to freeze, or <code>null</code> to not
  1575. * have any columns frozen
  1576. * @throws IllegalArgumentException
  1577. * if {@code lastFrozenColumn} is not a column from this grid
  1578. */
  1579. public void setLastFrozenColumn(GridColumn<?, T> lastFrozenColumn) {
  1580. this.lastFrozenColumn = lastFrozenColumn;
  1581. refreshFrozenColumns();
  1582. }
  1583. private void refreshFrozenColumns() {
  1584. final int frozenCount;
  1585. if (lastFrozenColumn != null) {
  1586. frozenCount = columns.indexOf(lastFrozenColumn) + 1;
  1587. if (frozenCount == 0) {
  1588. throw new IllegalArgumentException(
  1589. "The given column isn't attached to this grid");
  1590. }
  1591. } else {
  1592. frozenCount = 0;
  1593. }
  1594. escalator.getColumnConfiguration().setFrozenColumnCount(frozenCount);
  1595. }
  1596. /**
  1597. * Gets the rightmost frozen column in the grid.
  1598. * <p>
  1599. * <em>Note:</em> Most usually, this method returns the very value set with
  1600. * {@link #setLastFrozenColumn(GridColumn)}. This value, however, can be
  1601. * reset to <code>null</code> if the column is removed from this grid.
  1602. *
  1603. * @return the rightmost frozen column in the grid, or <code>null</code> if
  1604. * no columns are frozen.
  1605. */
  1606. public GridColumn<?, T> getLastFrozenColumn() {
  1607. return lastFrozenColumn;
  1608. }
  1609. public HandlerRegistration addRowVisibilityChangeHandler(
  1610. RowVisibilityChangeHandler handler) {
  1611. /*
  1612. * Reusing Escalator's RowVisibilityChangeHandler, since a scroll
  1613. * concept is too abstract. e.g. the event needs to be re-sent when the
  1614. * widget is resized.
  1615. */
  1616. return escalator.addRowVisibilityChangeHandler(handler);
  1617. }
  1618. /**
  1619. * Scrolls to a certain row, using {@link ScrollDestination#ANY}.
  1620. *
  1621. * @param rowIndex
  1622. * zero-based index of the row to scroll to.
  1623. * @throws IllegalArgumentException
  1624. * if rowIndex is below zero, or above the maximum value
  1625. * supported by the data source.
  1626. */
  1627. public void scrollToRow(int rowIndex) throws IllegalArgumentException {
  1628. scrollToRow(rowIndex, ScrollDestination.ANY,
  1629. GridConstants.DEFAULT_PADDING);
  1630. }
  1631. /**
  1632. * Scrolls to a certain row, using user-specified scroll destination.
  1633. *
  1634. * @param rowIndex
  1635. * zero-based index of the row to scroll to.
  1636. * @param destination
  1637. * desired destination placement of scrolled-to-row. See
  1638. * {@link ScrollDestination} for more information.
  1639. * @throws IllegalArgumentException
  1640. * if rowIndex is below zero, or above the maximum value
  1641. * supported by the data source.
  1642. */
  1643. public void scrollToRow(int rowIndex, ScrollDestination destination)
  1644. throws IllegalArgumentException {
  1645. scrollToRow(rowIndex, destination,
  1646. destination == ScrollDestination.MIDDLE ? 0
  1647. : GridConstants.DEFAULT_PADDING);
  1648. }
  1649. /**
  1650. * Scrolls to a certain row using only user-specified parameters.
  1651. *
  1652. * @param rowIndex
  1653. * zero-based index of the row to scroll to.
  1654. * @param destination
  1655. * desired destination placement of scrolled-to-row. See
  1656. * {@link ScrollDestination} for more information.
  1657. * @param paddingPx
  1658. * number of pixels to overscroll. Behavior depends on
  1659. * destination.
  1660. * @throws IllegalArgumentException
  1661. * if {@code destination} is {@link ScrollDestination#MIDDLE}
  1662. * and padding is nonzero, because having a padding on a
  1663. * centered row is undefined behavior, or if rowIndex is below
  1664. * zero or above the row count of the data source.
  1665. */
  1666. private void scrollToRow(int rowIndex, ScrollDestination destination,
  1667. int paddingPx) throws IllegalArgumentException {
  1668. int maxsize = escalator.getBody().getRowCount() - 1;
  1669. if (rowIndex < 0) {
  1670. throw new IllegalArgumentException("Row index (" + rowIndex
  1671. + ") is below zero!");
  1672. }
  1673. if (rowIndex > maxsize) {
  1674. throw new IllegalArgumentException("Row index (" + rowIndex
  1675. + ") is above maximum (" + maxsize + ")!");
  1676. }
  1677. escalator.scrollToRow(rowIndex, destination, paddingPx);
  1678. }
  1679. /**
  1680. * Scrolls to the beginning of the very first row.
  1681. */
  1682. public void scrollToStart() {
  1683. scrollToRow(0, ScrollDestination.START);
  1684. }
  1685. /**
  1686. * Scrolls to the end of the very last row.
  1687. */
  1688. public void scrollToEnd() {
  1689. scrollToRow(escalator.getBody().getRowCount() - 1,
  1690. ScrollDestination.END);
  1691. }
  1692. /**
  1693. * Sets the vertical scroll offset.
  1694. *
  1695. * @param px
  1696. * the number of pixels this grid should be scrolled down
  1697. */
  1698. public void setScrollTop(double px) {
  1699. escalator.setScrollTop(px);
  1700. }
  1701. /**
  1702. * Gets the vertical scroll offset
  1703. *
  1704. * @return the number of pixels this grid is scrolled down
  1705. */
  1706. public double getScrollTop() {
  1707. return escalator.getScrollTop();
  1708. }
  1709. /**
  1710. * Gets the horizontal scroll offset
  1711. *
  1712. * @return the number of pixels this grid is scrolled to the right
  1713. */
  1714. public double getScrollLeft() {
  1715. return escalator.getScrollLeft();
  1716. }
  1717. private static final Logger getLogger() {
  1718. return Logger.getLogger(Grid.class.getName());
  1719. }
  1720. /**
  1721. * Sets the number of rows that should be visible in Grid's body, while
  1722. * {@link #getHeightMode()} is {@link HeightMode#ROW}.
  1723. * <p>
  1724. * If Grid is currently not in {@link HeightMode#ROW}, the given value is
  1725. * remembered, and applied once the mode is applied.
  1726. *
  1727. * @param rows
  1728. * The height in terms of number of rows displayed in Grid's
  1729. * body. If Grid doesn't contain enough rows, white space is
  1730. * displayed instead.
  1731. * @throws IllegalArgumentException
  1732. * if {@code rows} is zero or less
  1733. * @throws IllegalArgumentException
  1734. * if {@code rows} is {@link Double#isInifinite(double)
  1735. * infinite}
  1736. * @throws IllegalArgumentException
  1737. * if {@code rows} is {@link Double#isNaN(double) NaN}
  1738. *
  1739. * @see #setHeightMode(HeightMode)
  1740. */
  1741. public void setHeightByRows(double rows) throws IllegalArgumentException {
  1742. escalator.setHeightByRows(rows);
  1743. }
  1744. /**
  1745. * Gets the amount of rows in Grid's body that are shown, while
  1746. * {@link #getHeightMode()} is {@link HeightMode#ROW}.
  1747. * <p>
  1748. * By default, it is {@value Escalator#DEFAULT_HEIGHT_BY_ROWS}.
  1749. *
  1750. * @return the amount of rows that should be shown in Grid's body, while in
  1751. * {@link HeightMode#ROW}.
  1752. * @see #setHeightByRows(double)
  1753. */
  1754. public double getHeightByRows() {
  1755. return escalator.getHeightByRows();
  1756. }
  1757. /**
  1758. * Defines the mode in which the Grid widget's height is calculated.
  1759. * <p>
  1760. * If {@link HeightMode#CSS} is given, Grid will respect the values given
  1761. * via {@link #setHeight(String)}, and behave as a traditional Widget.
  1762. * <p>
  1763. * If {@link HeightMode#ROW} is given, Grid will make sure that the body
  1764. * will display as many rows as {@link #getHeightByRows()} defines.
  1765. * <em>Note:</em> If headers/footers are inserted or removed, the widget
  1766. * will resize itself to still display the required amount of rows in its
  1767. * body. It also takes the horizontal scrollbar into account.
  1768. *
  1769. * @param heightMode
  1770. * the mode in to which Grid should be set
  1771. */
  1772. public void setHeightMode(HeightMode heightMode) {
  1773. /*
  1774. * This method is a workaround for the fact that Vaadin re-applies
  1775. * widget dimensions (height/width) on each state change event. The
  1776. * original design was to have setHeight an setHeightByRow be equals,
  1777. * and whichever was called the latest was considered in effect.
  1778. *
  1779. * But, because of Vaadin always calling setHeight on the widget, this
  1780. * approach doesn't work.
  1781. */
  1782. escalator.setHeightMode(heightMode);
  1783. }
  1784. /**
  1785. * Returns the current {@link HeightMode} the Grid is in.
  1786. * <p>
  1787. * Defaults to {@link HeightMode#CSS}.
  1788. *
  1789. * @return the current HeightMode
  1790. */
  1791. public HeightMode getHeightMode() {
  1792. return escalator.getHeightMode();
  1793. }
  1794. private Set<String> getConsumedEventsForRenderer(Renderer<?> renderer) {
  1795. Set<String> events = new HashSet<String>();
  1796. if (renderer instanceof ComplexRenderer) {
  1797. Collection<String> consumedEvents = ((ComplexRenderer<?>) renderer)
  1798. .getConsumedEvents();
  1799. if (consumedEvents != null) {
  1800. events.addAll(consumedEvents);
  1801. }
  1802. }
  1803. return events;
  1804. }
  1805. @Override
  1806. public void onBrowserEvent(Event event) {
  1807. super.onBrowserEvent(event);
  1808. EventTarget target = event.getEventTarget();
  1809. if (!Element.is(target)) {
  1810. return;
  1811. }
  1812. Element e = Element.as(target);
  1813. RowContainer container = escalator.findRowContainer(e);
  1814. Cell cell;
  1815. boolean isGrid = Util.findWidget(e, null) == this;
  1816. if (container == null) {
  1817. cell = activeCellHandler.getActiveCell();
  1818. container = activeCellHandler.container;
  1819. } else {
  1820. cell = container.getCell(e);
  1821. }
  1822. if (isGrid) {
  1823. if (handleEditorRowEvent(event, container, cell)) {
  1824. return;
  1825. }
  1826. if (handleHeaderDefaultRowEvent(event, container, cell)) {
  1827. return;
  1828. }
  1829. if (handleRendererEvent(event, container, cell)) {
  1830. return;
  1831. }
  1832. if (handleNavigationEvent(event, container, cell)) {
  1833. return;
  1834. }
  1835. if (handleActiveCellEvent(event, container, cell)) {
  1836. return;
  1837. }
  1838. }
  1839. }
  1840. private boolean handleEditorRowEvent(Event event, RowContainer container,
  1841. Cell cell) {
  1842. if (editorRow.getState() != State.INACTIVE) {
  1843. if (event.getTypeInt() == Event.ONKEYDOWN
  1844. && event.getKeyCode() == EditorRow.KEYCODE_HIDE) {
  1845. editorRow.cancel();
  1846. }
  1847. return true;
  1848. }
  1849. if (editorRow.isEnabled()) {
  1850. if (event.getTypeInt() == Event.ONDBLCLICK) {
  1851. if (container == escalator.getBody() && cell != null) {
  1852. editorRow.editRow(cell.getRow());
  1853. return true;
  1854. }
  1855. } else if (event.getTypeInt() == Event.ONKEYDOWN
  1856. && event.getKeyCode() == EditorRow.KEYCODE_SHOW) {
  1857. editorRow.editRow(activeCellHandler.activeRow);
  1858. return true;
  1859. }
  1860. }
  1861. return false;
  1862. }
  1863. private boolean handleRendererEvent(Event event, RowContainer container,
  1864. Cell cell) {
  1865. if (container == escalator.getBody() && cell != null) {
  1866. GridColumn<?, T> gridColumn = getColumnFromVisibleIndex(cell
  1867. .getColumn());
  1868. boolean enterKey = event.getType().equals(BrowserEvents.KEYDOWN)
  1869. && event.getKeyCode() == KeyCodes.KEY_ENTER;
  1870. boolean doubleClick = event.getType()
  1871. .equals(BrowserEvents.DBLCLICK);
  1872. if (gridColumn.getRenderer() instanceof ComplexRenderer) {
  1873. ComplexRenderer<?> cplxRenderer = (ComplexRenderer<?>) gridColumn
  1874. .getRenderer();
  1875. if (cplxRenderer.getConsumedEvents().contains(event.getType())) {
  1876. if (cplxRenderer.onBrowserEvent(cell, event)) {
  1877. return true;
  1878. }
  1879. }
  1880. // Calls onActivate if KeyDown and Enter or double click
  1881. if ((enterKey || doubleClick) && cplxRenderer.onActivate(cell)) {
  1882. return true;
  1883. }
  1884. }
  1885. }
  1886. return false;
  1887. }
  1888. private boolean handleActiveCellEvent(Event event, RowContainer container,
  1889. Cell cell) {
  1890. Collection<String> navigation = activeCellHandler.getNavigationEvents();
  1891. if (navigation.contains(event.getType())) {
  1892. activeCellHandler.handleNavigationEvent(event, cell);
  1893. }
  1894. return false;
  1895. }
  1896. private boolean handleNavigationEvent(Event event, RowContainer unused,
  1897. Cell cell) {
  1898. if (!event.getType().equals(BrowserEvents.KEYDOWN)) {
  1899. // Only handle key downs
  1900. return false;
  1901. }
  1902. int newRow = -1;
  1903. RowContainer container = escalator.getBody();
  1904. switch (event.getKeyCode()) {
  1905. case KeyCodes.KEY_HOME:
  1906. if (container.getRowCount() > 0) {
  1907. newRow = 0;
  1908. }
  1909. break;
  1910. case KeyCodes.KEY_END:
  1911. if (container.getRowCount() > 0) {
  1912. newRow = container.getRowCount() - 1;
  1913. }
  1914. break;
  1915. case KeyCodes.KEY_PAGEUP: {
  1916. Range range = escalator.getVisibleRowRange();
  1917. if (!range.isEmpty()) {
  1918. int firstIndex = getFirstVisibleRowIndex();
  1919. newRow = firstIndex - range.length();
  1920. if (newRow < 0) {
  1921. newRow = 0;
  1922. }
  1923. }
  1924. break;
  1925. }
  1926. case KeyCodes.KEY_PAGEDOWN: {
  1927. Range range = escalator.getVisibleRowRange();
  1928. if (!range.isEmpty()) {
  1929. int lastIndex = getLastVisibleRowIndex();
  1930. newRow = lastIndex + range.length();
  1931. if (newRow >= container.getRowCount()) {
  1932. newRow = container.getRowCount() - 1;
  1933. }
  1934. }
  1935. break;
  1936. }
  1937. default:
  1938. return false;
  1939. }
  1940. scrollToRow(newRow);
  1941. return true;
  1942. }
  1943. private Point rowEventTouchStartingPoint;
  1944. private boolean handleHeaderDefaultRowEvent(Event event,
  1945. RowContainer container, final Cell cell) {
  1946. if (container != escalator.getHeader()) {
  1947. return false;
  1948. }
  1949. if (!getHeader().getRow(cell.getRow()).isDefault()) {
  1950. return false;
  1951. }
  1952. if (!getColumn(cell.getColumn()).isSortable()) {
  1953. // Only handle sorting events if the column is sortable
  1954. return false;
  1955. }
  1956. if (BrowserEvents.TOUCHSTART.equals(event.getType())) {
  1957. if (event.getTouches().length() > 1) {
  1958. return false;
  1959. }
  1960. event.preventDefault();
  1961. Touch touch = event.getChangedTouches().get(0);
  1962. rowEventTouchStartingPoint = new Point(touch.getClientX(),
  1963. touch.getClientY());
  1964. sorter.sortAfterDelay(GridConstants.LONG_TAP_DELAY, cell, true);
  1965. return true;
  1966. } else if (BrowserEvents.TOUCHMOVE.equals(event.getType())) {
  1967. if (event.getTouches().length() > 1) {
  1968. return false;
  1969. }
  1970. event.preventDefault();
  1971. Touch touch = event.getChangedTouches().get(0);
  1972. double diffX = Math.abs(touch.getClientX()
  1973. - rowEventTouchStartingPoint.getX());
  1974. double diffY = Math.abs(touch.getClientY()
  1975. - rowEventTouchStartingPoint.getY());
  1976. // Cancel long tap if finger strays too far from
  1977. // starting point
  1978. if (diffX > GridConstants.LONG_TAP_THRESHOLD
  1979. || diffY > GridConstants.LONG_TAP_THRESHOLD) {
  1980. sorter.cancelDelayedSort();
  1981. }
  1982. return true;
  1983. } else if (BrowserEvents.TOUCHEND.equals(event.getType())) {
  1984. if (event.getTouches().length() > 1) {
  1985. return false;
  1986. }
  1987. if (sorter.isDelayedSortScheduled()) {
  1988. // Not a long tap yet, perform single sort
  1989. sorter.cancelDelayedSort();
  1990. sorter.sort(cell, false);
  1991. }
  1992. return true;
  1993. } else if (BrowserEvents.TOUCHCANCEL.equals(event.getType())) {
  1994. if (event.getTouches().length() > 1) {
  1995. return false;
  1996. }
  1997. sorter.cancelDelayedSort();
  1998. return true;
  1999. } else if (BrowserEvents.CLICK.equals(event.getType())) {
  2000. sorter.sort(cell, event.getShiftKey());
  2001. // Click events should go onward to active cell logic
  2002. return false;
  2003. } else {
  2004. return false;
  2005. }
  2006. }
  2007. @Override
  2008. public com.google.gwt.user.client.Element getSubPartElement(String subPart) {
  2009. // Parse SubPart string to type and indices
  2010. String[] splitArgs = subPart.split("\\[");
  2011. String type = splitArgs[0];
  2012. int[] indices = new int[splitArgs.length - 1];
  2013. for (int i = 0; i < indices.length; ++i) {
  2014. String tmp = splitArgs[i + 1];
  2015. indices[i] = Integer.parseInt(tmp.substring(0, tmp.length() - 1));
  2016. }
  2017. // Get correct RowContainer for type from Escalator
  2018. RowContainer container = null;
  2019. if (type.equalsIgnoreCase("header")) {
  2020. container = escalator.getHeader();
  2021. } else if (type.equalsIgnoreCase("cell")) {
  2022. // If wanted row is not visible, we need to scroll there.
  2023. Range visibleRowRange = escalator.getVisibleRowRange();
  2024. if (indices.length > 0 && !visibleRowRange.contains(indices[0])) {
  2025. try {
  2026. scrollToRow(indices[0]);
  2027. } catch (IllegalArgumentException e) {
  2028. getLogger().log(Level.SEVERE, e.getMessage());
  2029. }
  2030. // Scrolling causes a lazy loading event. No element can
  2031. // currently be retrieved.
  2032. return null;
  2033. }
  2034. container = escalator.getBody();
  2035. } else if (type.equalsIgnoreCase("footer")) {
  2036. container = escalator.getFooter();
  2037. }
  2038. if (null != container) {
  2039. if (indices.length == 0) {
  2040. // No indexing. Just return the wanted container element
  2041. return DOM.asOld(container.getElement());
  2042. } else {
  2043. try {
  2044. return DOM.asOld(getSubPart(container, indices));
  2045. } catch (Exception e) {
  2046. getLogger().log(Level.SEVERE, e.getMessage());
  2047. }
  2048. }
  2049. }
  2050. return null;
  2051. }
  2052. private Element getSubPart(RowContainer container, int[] indices) {
  2053. // Scroll wanted column to view if able
  2054. if (indices.length > 1
  2055. && escalator.getColumnConfiguration().getFrozenColumnCount() <= indices[1]) {
  2056. escalator.scrollToColumn(indices[1], ScrollDestination.ANY, 0);
  2057. }
  2058. Element targetElement = container.getRowElement(indices[0]);
  2059. for (int i = 1; i < indices.length && targetElement != null; ++i) {
  2060. targetElement = (Element) targetElement.getChild(indices[i]);
  2061. }
  2062. return targetElement;
  2063. }
  2064. @Override
  2065. public String getSubPartName(com.google.gwt.user.client.Element subElement) {
  2066. // Containers and matching SubPart types
  2067. List<RowContainer> containers = Arrays.asList(escalator.getHeader(),
  2068. escalator.getBody(), escalator.getFooter());
  2069. List<String> containerType = Arrays.asList("header", "cell", "footer");
  2070. for (int i = 0; i < containers.size(); ++i) {
  2071. RowContainer container = containers.get(i);
  2072. boolean containerRow = (subElement.getTagName().equalsIgnoreCase(
  2073. "tr") && subElement.getParentElement() == container
  2074. .getElement());
  2075. if (containerRow) {
  2076. // Wanted SubPart is row that is a child of containers root
  2077. // To get indices, we use a cell that is a child of this row
  2078. subElement = DOM.asOld(subElement.getFirstChildElement());
  2079. }
  2080. Cell cell = container.getCell(subElement);
  2081. if (cell != null) {
  2082. // Skip the column index if subElement was a child of root
  2083. return containerType.get(i) + "[" + cell.getRow()
  2084. + (containerRow ? "]" : "][" + cell.getColumn() + "]");
  2085. }
  2086. }
  2087. return null;
  2088. }
  2089. private void setSelectColumnRenderer(
  2090. final Renderer<Boolean> selectColumnRenderer) {
  2091. if (this.selectColumnRenderer == selectColumnRenderer) {
  2092. return;
  2093. }
  2094. if (this.selectColumnRenderer != null) {
  2095. removeColumnSkipSelectionColumnCheck(selectionColumn);
  2096. activeCellHandler.offsetRangeBy(-1);
  2097. }
  2098. this.selectColumnRenderer = selectColumnRenderer;
  2099. if (selectColumnRenderer != null) {
  2100. activeCellHandler.offsetRangeBy(1);
  2101. selectionColumn = new SelectionColumn(selectColumnRenderer);
  2102. // FIXME: this needs to be done elsewhere, requires design...
  2103. selectionColumn.setWidth(25);
  2104. addColumnSkipSelectionColumnCheck(selectionColumn, 0);
  2105. selectionColumn.initDone();
  2106. } else {
  2107. selectionColumn = null;
  2108. refreshBody();
  2109. }
  2110. }
  2111. /**
  2112. * Accesses the package private method Widget#setParent()
  2113. *
  2114. * @param widget
  2115. * The widget to access
  2116. * @param parent
  2117. * The parent to set
  2118. */
  2119. static native final void setParent(Widget widget, Widget parent)
  2120. /*-{
  2121. widget.@com.google.gwt.user.client.ui.Widget::setParent(Lcom/google/gwt/user/client/ui/Widget;)(parent);
  2122. }-*/;
  2123. /**
  2124. * Sets the current selection model.
  2125. * <p>
  2126. * This function will call {@link SelectionModel#setGrid(Grid)}.
  2127. *
  2128. * @param selectionModel
  2129. * a selection model implementation.
  2130. * @throws IllegalArgumentException
  2131. * if selection model argument is null
  2132. */
  2133. public void setSelectionModel(SelectionModel<T> selectionModel) {
  2134. if (selectionModel == null) {
  2135. throw new IllegalArgumentException("Selection model can't be null");
  2136. }
  2137. if (selectColumnRenderer != null
  2138. && selectColumnRenderer instanceof ComplexRenderer) {
  2139. ((ComplexRenderer<?>) selectColumnRenderer).destroy();
  2140. }
  2141. this.selectionModel = selectionModel;
  2142. selectionModel.setGrid(this);
  2143. setSelectColumnRenderer(this.selectionModel
  2144. .getSelectionColumnRenderer());
  2145. }
  2146. /**
  2147. * Gets a reference to the current selection model.
  2148. *
  2149. * @return the currently used SelectionModel instance.
  2150. */
  2151. public SelectionModel<T> getSelectionModel() {
  2152. return selectionModel;
  2153. }
  2154. /**
  2155. * Sets current selection mode.
  2156. * <p>
  2157. * This is a shorthand method for {@link Grid#setSelectionModel}.
  2158. *
  2159. * @param mode
  2160. * a selection mode value
  2161. * @see {@link SelectionMode}.
  2162. */
  2163. public void setSelectionMode(SelectionMode mode) {
  2164. SelectionModel<T> model = mode.createModel();
  2165. setSelectionModel(model);
  2166. }
  2167. /**
  2168. * Test if a row is selected.
  2169. *
  2170. * @param row
  2171. * a row object
  2172. * @return true, if the current selection model considers the provided row
  2173. * object selected.
  2174. */
  2175. public boolean isSelected(T row) {
  2176. return selectionModel.isSelected(row);
  2177. }
  2178. /**
  2179. * Select a row using the current selection model.
  2180. * <p>
  2181. * Only selection models implementing {@link SelectionModel.Single} and
  2182. * {@link SelectionModel.Multi} are supported; for anything else, an
  2183. * exception will be thrown.
  2184. *
  2185. * @param row
  2186. * a row object
  2187. * @return <code>true</code> iff the current selection changed
  2188. * @throws IllegalStateException
  2189. * if the current selection model is not an instance of
  2190. * {@link SelectionModel.Single} or {@link SelectionModel.Multi}
  2191. */
  2192. @SuppressWarnings("unchecked")
  2193. public boolean select(T row) {
  2194. if (selectionModel instanceof SelectionModel.Single<?>) {
  2195. return ((SelectionModel.Single<T>) selectionModel).select(row);
  2196. } else if (selectionModel instanceof SelectionModel.Multi<?>) {
  2197. return ((SelectionModel.Multi<T>) selectionModel).select(row);
  2198. } else {
  2199. throw new IllegalStateException("Unsupported selection model");
  2200. }
  2201. }
  2202. /**
  2203. * Deselect a row using the current selection model.
  2204. * <p>
  2205. * Only selection models implementing {@link SelectionModel.Single} and
  2206. * {@link SelectionModel.Multi} are supported; for anything else, an
  2207. * exception will be thrown.
  2208. *
  2209. * @param row
  2210. * a row object
  2211. * @return <code>true</code> iff the current selection changed
  2212. * @throws IllegalStateException
  2213. * if the current selection model is not an instance of
  2214. * {@link SelectionModel.Single} or {@link SelectionModel.Multi}
  2215. */
  2216. @SuppressWarnings("unchecked")
  2217. public boolean deselect(T row) {
  2218. if (selectionModel instanceof SelectionModel.Single<?>) {
  2219. return ((SelectionModel.Single<T>) selectionModel).deselect(row);
  2220. } else if (selectionModel instanceof SelectionModel.Multi<?>) {
  2221. return ((SelectionModel.Multi<T>) selectionModel).deselect(row);
  2222. } else {
  2223. throw new IllegalStateException("Unsupported selection model");
  2224. }
  2225. }
  2226. /**
  2227. * Gets last selected row from the current SelectionModel.
  2228. * <p>
  2229. * Only selection models implementing {@link SelectionModel.Single} are
  2230. * valid for this method; for anything else, use the
  2231. * {@link Grid#getSelectedRows()} method.
  2232. *
  2233. * @return a selected row reference, or null, if no row is selected
  2234. * @throws IllegalStateException
  2235. * if the current selection model is not an instance of
  2236. * {@link SelectionModel.Single}
  2237. */
  2238. public T getSelectedRow() {
  2239. if (selectionModel instanceof SelectionModel.Single<?>) {
  2240. return ((SelectionModel.Single<T>) selectionModel).getSelectedRow();
  2241. } else {
  2242. throw new IllegalStateException(
  2243. "Unsupported selection model; can not get single selected row");
  2244. }
  2245. }
  2246. /**
  2247. * Gets currently selected rows from the current selection model.
  2248. *
  2249. * @return a non-null collection containing all currently selected rows.
  2250. */
  2251. public Collection<T> getSelectedRows() {
  2252. return selectionModel.getSelectedRows();
  2253. }
  2254. @Override
  2255. public HandlerRegistration addSelectionChangeHandler(
  2256. final SelectionChangeHandler<T> handler) {
  2257. return addHandler(handler, SelectionChangeEvent.getType());
  2258. }
  2259. /**
  2260. * Sets the current sort order using the fluid Sort API. Read the
  2261. * documentation for {@link Sort} for more information.
  2262. *
  2263. * @param s
  2264. * a sort instance
  2265. */
  2266. public void sort(Sort s) {
  2267. setSortOrder(s.build());
  2268. }
  2269. /**
  2270. * Sorts the Grid data in ascending order along one column.
  2271. *
  2272. * @param column
  2273. * a grid column reference
  2274. */
  2275. public <C> void sort(GridColumn<C, T> column) {
  2276. sort(column, SortDirection.ASCENDING);
  2277. }
  2278. /**
  2279. * Sorts the Grid data along one column.
  2280. *
  2281. * @param column
  2282. * a grid column reference
  2283. * @param direction
  2284. * a sort direction value
  2285. */
  2286. public <C> void sort(GridColumn<C, T> column, SortDirection direction) {
  2287. sort(Sort.by(column, direction));
  2288. }
  2289. /**
  2290. * Sets the sort order to use. Setting this causes the Grid to re-sort
  2291. * itself.
  2292. *
  2293. * @param order
  2294. * a sort order list. If set to null, the sort order is cleared.
  2295. */
  2296. public void setSortOrder(List<SortOrder> order) {
  2297. setSortOrder(order, SortEventOriginator.API);
  2298. }
  2299. private void setSortOrder(List<SortOrder> order,
  2300. SortEventOriginator originator) {
  2301. if (order != sortOrder) {
  2302. sortOrder.clear();
  2303. if (order != null) {
  2304. sortOrder.addAll(order);
  2305. }
  2306. }
  2307. sort(originator);
  2308. }
  2309. /**
  2310. * Get a copy of the current sort order array.
  2311. *
  2312. * @return a copy of the current sort order array
  2313. */
  2314. public List<SortOrder> getSortOrder() {
  2315. return Collections.unmodifiableList(sortOrder);
  2316. }
  2317. /**
  2318. * Finds the sorting order for this column
  2319. */
  2320. private SortOrder getSortOrder(GridColumn<?, ?> column) {
  2321. for (SortOrder order : getSortOrder()) {
  2322. if (order.getColumn() == column) {
  2323. return order;
  2324. }
  2325. }
  2326. return null;
  2327. }
  2328. /**
  2329. * Register a GWT event handler for a sorting event. This handler gets
  2330. * called whenever this Grid needs its data source to provide data sorted in
  2331. * a specific order.
  2332. *
  2333. * @param handler
  2334. * a sort event handler
  2335. * @return the registration for the event
  2336. */
  2337. public HandlerRegistration addSortHandler(SortEventHandler<T> handler) {
  2338. return addHandler(handler, SortEvent.getType());
  2339. }
  2340. /**
  2341. * Register a GWT event handler for a data available event. This handler
  2342. * gets called whenever the {@link DataSource} for this Grid has new data
  2343. * available.
  2344. * <p>
  2345. * This handle will be fired with the current available data after
  2346. * registration is done.
  2347. *
  2348. * @param handler
  2349. * a data available event handler
  2350. * @return the registartion for the event
  2351. */
  2352. public HandlerRegistration addDataAvailableHandler(
  2353. final DataAvailableHandler handler) {
  2354. // Deferred call to handler with current row range
  2355. Scheduler.get().scheduleFinally(new ScheduledCommand() {
  2356. @Override
  2357. public void execute() {
  2358. handler.onDataAvailable(new DataAvailableEvent(
  2359. currentDataAvailable));
  2360. }
  2361. });
  2362. return addHandler(handler, DataAvailableEvent.TYPE);
  2363. }
  2364. /**
  2365. * Register a BodyKeyDownHandler to this Grid. The event for this handler is
  2366. * fired when a KeyDown event occurs while active cell is in the Body of
  2367. * this Grid.
  2368. *
  2369. * @param handler
  2370. * the key handler to register
  2371. * @return the registration for the event
  2372. */
  2373. public HandlerRegistration addBodyKeyDownHandler(BodyKeyDownHandler handler) {
  2374. return addHandler(handler, keyDown.getAssociatedType());
  2375. }
  2376. /**
  2377. * Register a BodyKeyUpHandler to this Grid. The event for this handler is
  2378. * fired when a KeyUp event occurs while active cell is in the Body of this
  2379. * Grid.
  2380. *
  2381. * @param handler
  2382. * the key handler to register
  2383. * @return the registration for the event
  2384. */
  2385. public HandlerRegistration addBodyKeyUpHandler(BodyKeyUpHandler handler) {
  2386. return addHandler(handler, keyUp.getAssociatedType());
  2387. }
  2388. /**
  2389. * Register a BodyKeyPressHandler to this Grid. The event for this handler
  2390. * is fired when a KeyPress event occurs while active cell is in the Body of
  2391. * this Grid.
  2392. *
  2393. * @param handler
  2394. * the key handler to register
  2395. * @return the registration for the event
  2396. */
  2397. public HandlerRegistration addBodyKeyPressHandler(
  2398. BodyKeyPressHandler handler) {
  2399. return addHandler(handler, keyPress.getAssociatedType());
  2400. }
  2401. /**
  2402. * Register a HeaderKeyDownHandler to this Grid. The event for this handler
  2403. * is fired when a KeyDown event occurs while active cell is in the Header
  2404. * of this Grid.
  2405. *
  2406. * @param handler
  2407. * the key handler to register
  2408. * @return the registration for the event
  2409. */
  2410. public HandlerRegistration addHeaderKeyDownHandler(
  2411. HeaderKeyDownHandler handler) {
  2412. return addHandler(handler, keyDown.getAssociatedType());
  2413. }
  2414. /**
  2415. * Register a HeaderKeyUpHandler to this Grid. The event for this handler is
  2416. * fired when a KeyUp event occurs while active cell is in the Header of
  2417. * this Grid.
  2418. *
  2419. * @param handler
  2420. * the key handler to register
  2421. * @return the registration for the event
  2422. */
  2423. public HandlerRegistration addHeaderKeyUpHandler(HeaderKeyUpHandler handler) {
  2424. return addHandler(handler, keyUp.getAssociatedType());
  2425. }
  2426. /**
  2427. * Register a HeaderKeyPressHandler to this Grid. The event for this handler
  2428. * is fired when a KeyPress event occurs while active cell is in the Header
  2429. * of this Grid.
  2430. *
  2431. * @param handler
  2432. * the key handler to register
  2433. * @return the registration for the event
  2434. */
  2435. public HandlerRegistration addHeaderKeyPressHandler(
  2436. HeaderKeyPressHandler handler) {
  2437. return addHandler(handler, keyPress.getAssociatedType());
  2438. }
  2439. /**
  2440. * Register a FooterKeyDownHandler to this Grid. The event for this handler
  2441. * is fired when a KeyDown event occurs while active cell is in the Footer
  2442. * of this Grid.
  2443. *
  2444. * @param handler
  2445. * the key handler to register
  2446. * @return the registration for the event
  2447. */
  2448. public HandlerRegistration addFooterKeyDownHandler(
  2449. FooterKeyDownHandler handler) {
  2450. return addHandler(handler, keyDown.getAssociatedType());
  2451. }
  2452. /**
  2453. * Register a FooterKeyUpHandler to this Grid. The event for this handler is
  2454. * fired when a KeyUp event occurs while active cell is in the Footer of
  2455. * this Grid.
  2456. *
  2457. * @param handler
  2458. * the key handler to register
  2459. * @return the registration for the event
  2460. */
  2461. public HandlerRegistration addFooterKeyUpHandler(FooterKeyUpHandler handler) {
  2462. return addHandler(handler, keyUp.getAssociatedType());
  2463. }
  2464. /**
  2465. * Register a FooterKeyPressHandler to this Grid. The event for this handler
  2466. * is fired when a KeyPress event occurs while active cell is in the Footer
  2467. * of this Grid.
  2468. *
  2469. * @param handler
  2470. * the key handler to register
  2471. * @return the registration for the event
  2472. */
  2473. public HandlerRegistration addFooterKeyPressHandler(
  2474. FooterKeyPressHandler handler) {
  2475. return addHandler(handler, keyPress.getAssociatedType());
  2476. }
  2477. /**
  2478. * Apply sorting to data source.
  2479. */
  2480. private void sort(SortEventOriginator originator) {
  2481. refreshHeader();
  2482. fireEvent(new SortEvent<T>(this,
  2483. Collections.unmodifiableList(sortOrder), originator));
  2484. }
  2485. private int getLastVisibleRowIndex() {
  2486. int lastRowIndex = escalator.getVisibleRowRange().getEnd();
  2487. int footerTop = escalator.getFooter().getElement().getAbsoluteTop();
  2488. Element lastRow;
  2489. do {
  2490. lastRow = escalator.getBody().getRowElement(--lastRowIndex);
  2491. } while (lastRow.getAbsoluteBottom() > footerTop);
  2492. return lastRowIndex;
  2493. }
  2494. private int getFirstVisibleRowIndex() {
  2495. int firstRowIndex = escalator.getVisibleRowRange().getStart();
  2496. int headerBottom = escalator.getHeader().getElement()
  2497. .getAbsoluteBottom();
  2498. Element firstRow = escalator.getBody().getRowElement(firstRowIndex);
  2499. while (firstRow.getAbsoluteTop() < headerBottom) {
  2500. firstRow = escalator.getBody().getRowElement(++firstRowIndex);
  2501. }
  2502. return firstRowIndex;
  2503. }
  2504. /**
  2505. * Adds a scroll handler to this grid
  2506. *
  2507. * @param handler
  2508. * the scroll handler to add
  2509. * @return a handler registration for the registered scroll handler
  2510. */
  2511. public HandlerRegistration addScrollHandler(ScrollHandler handler) {
  2512. return addHandler(handler, ScrollEvent.TYPE);
  2513. }
  2514. }