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.

Table.java 110KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324
  1. /*
  2. @ITMillApache2LicenseForJavaFiles@
  3. */
  4. package com.vaadin.ui;
  5. import java.io.Serializable;
  6. import java.util.ArrayList;
  7. import java.util.Collection;
  8. import java.util.HashMap;
  9. import java.util.HashSet;
  10. import java.util.Iterator;
  11. import java.util.LinkedHashMap;
  12. import java.util.LinkedHashSet;
  13. import java.util.LinkedList;
  14. import java.util.Map;
  15. import java.util.Set;
  16. import java.util.StringTokenizer;
  17. import com.vaadin.data.Container;
  18. import com.vaadin.data.Item;
  19. import com.vaadin.data.Property;
  20. import com.vaadin.data.util.ContainerOrderedWrapper;
  21. import com.vaadin.data.util.IndexedContainer;
  22. import com.vaadin.event.Action;
  23. import com.vaadin.event.ItemClickEvent;
  24. import com.vaadin.event.Action.Handler;
  25. import com.vaadin.event.ItemClickEvent.ItemClickListener;
  26. import com.vaadin.event.ItemClickEvent.ItemClickSource;
  27. import com.vaadin.terminal.KeyMapper;
  28. import com.vaadin.terminal.PaintException;
  29. import com.vaadin.terminal.PaintTarget;
  30. import com.vaadin.terminal.Resource;
  31. import com.vaadin.terminal.gwt.client.MouseEventDetails;
  32. import com.vaadin.terminal.gwt.client.ui.VScrollTable;
  33. /**
  34. * <p>
  35. * <code>TableComponent</code> is used for representing data or components in
  36. * pageable and selectable table.
  37. * </p>
  38. *
  39. * <p>
  40. * Note! Since version 5, components in Table will not have their caption nor
  41. * icon rendered. In order to workaround this limitation, wrap your component in
  42. * a Layout.
  43. * </p>
  44. *
  45. * @author IT Mill Ltd.
  46. * @version
  47. * @VERSION@
  48. * @since 3.0
  49. */
  50. @SuppressWarnings("serial")
  51. @ClientWidget(VScrollTable.class)
  52. public class Table extends AbstractSelect implements Action.Container,
  53. Container.Ordered, Container.Sortable, ItemClickSource {
  54. private static final int CELL_KEY = 0;
  55. private static final int CELL_HEADER = 1;
  56. private static final int CELL_ICON = 2;
  57. private static final int CELL_ITEMID = 3;
  58. private static final int CELL_FIRSTCOL = 4;
  59. /**
  60. * Left column alignment. <b>This is the default behaviour. </b>
  61. */
  62. public static final String ALIGN_LEFT = "b";
  63. /**
  64. * Center column alignment.
  65. */
  66. public static final String ALIGN_CENTER = "c";
  67. /**
  68. * Right column alignment.
  69. */
  70. public static final String ALIGN_RIGHT = "e";
  71. /**
  72. * Column header mode: Column headers are hidden. <b>This is the default
  73. * behavior. </b>
  74. */
  75. public static final int COLUMN_HEADER_MODE_HIDDEN = -1;
  76. /**
  77. * Column header mode: Property ID:s are used as column headers.
  78. */
  79. public static final int COLUMN_HEADER_MODE_ID = 0;
  80. /**
  81. * Column header mode: Column headers are explicitly specified with
  82. * <code>setColumnHeaders</code>.
  83. */
  84. public static final int COLUMN_HEADER_MODE_EXPLICIT = 1;
  85. /**
  86. * Column header mode: Column headers are explicitly specified with
  87. * <code>setColumnHeaders</code>
  88. */
  89. public static final int COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID = 2;
  90. /**
  91. * Row caption mode: The row headers are hidden. <b>This is the default
  92. * mode. </b>
  93. */
  94. public static final int ROW_HEADER_MODE_HIDDEN = -1;
  95. /**
  96. * Row caption mode: Items Id-objects toString is used as row caption.
  97. */
  98. public static final int ROW_HEADER_MODE_ID = AbstractSelect.ITEM_CAPTION_MODE_ID;
  99. /**
  100. * Row caption mode: Item-objects toString is used as row caption.
  101. */
  102. public static final int ROW_HEADER_MODE_ITEM = AbstractSelect.ITEM_CAPTION_MODE_ITEM;
  103. /**
  104. * Row caption mode: Index of the item is used as item caption. The index
  105. * mode can only be used with the containers implementing Container.Indexed
  106. * interface.
  107. */
  108. public static final int ROW_HEADER_MODE_INDEX = AbstractSelect.ITEM_CAPTION_MODE_INDEX;
  109. /**
  110. * Row caption mode: Item captions are explicitly specified.
  111. */
  112. public static final int ROW_HEADER_MODE_EXPLICIT = AbstractSelect.ITEM_CAPTION_MODE_EXPLICIT;
  113. /**
  114. * Row caption mode: Item captions are read from property specified with
  115. * <code>setItemCaptionPropertyId</code>.
  116. */
  117. public static final int ROW_HEADER_MODE_PROPERTY = AbstractSelect.ITEM_CAPTION_MODE_PROPERTY;
  118. /**
  119. * Row caption mode: Only icons are shown, the captions are hidden.
  120. */
  121. public static final int ROW_HEADER_MODE_ICON_ONLY = AbstractSelect.ITEM_CAPTION_MODE_ICON_ONLY;
  122. /**
  123. * Row caption mode: Item captions are explicitly specified, but if the
  124. * caption is missing, the item id objects <code>toString()</code> is used
  125. * instead.
  126. */
  127. public static final int ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID = AbstractSelect.ITEM_CAPTION_MODE_EXPLICIT_DEFAULTS_ID;
  128. /**
  129. * The default rate that table caches rows for smooth scrolling.
  130. */
  131. private static final double CACHE_RATE_DEFAULT = 2;
  132. /* Private table extensions to Select */
  133. /**
  134. * True if column collapsing is allowed.
  135. */
  136. private boolean columnCollapsingAllowed = false;
  137. /**
  138. * True if reordering of columns is allowed on the client side.
  139. */
  140. private boolean columnReorderingAllowed = false;
  141. /**
  142. * Keymapper for column ids.
  143. */
  144. private final KeyMapper columnIdMap = new KeyMapper();
  145. /**
  146. * Holds visible column propertyIds - in order.
  147. */
  148. private LinkedList<Object> visibleColumns = new LinkedList<Object>();
  149. /**
  150. * Holds propertyIds of currently collapsed columns.
  151. */
  152. private final HashSet<Object> collapsedColumns = new HashSet<Object>();
  153. /**
  154. * Holds headers for visible columns (by propertyId).
  155. */
  156. private final HashMap<Object, String> columnHeaders = new HashMap<Object, String>();
  157. /**
  158. * Holds icons for visible columns (by propertyId).
  159. */
  160. private final HashMap<Object, Resource> columnIcons = new HashMap<Object, Resource>();
  161. /**
  162. * Holds alignments for visible columns (by propertyId).
  163. */
  164. private HashMap<Object, String> columnAlignments = new HashMap<Object, String>();
  165. /**
  166. * Holds column widths in pixels (Integer) or expand ratios (Float) for
  167. * visible columns (by propertyId).
  168. */
  169. private final HashMap<Object, Object> columnWidths = new HashMap<Object, Object>();
  170. /**
  171. * Holds column generators
  172. */
  173. private final HashMap<Object, ColumnGenerator> columnGenerators = new LinkedHashMap<Object, ColumnGenerator>();
  174. /**
  175. * Holds value of property pageLength. 0 disables paging.
  176. */
  177. private int pageLength = 15;
  178. /**
  179. * Id the first item on the current page.
  180. */
  181. private Object currentPageFirstItemId = null;
  182. /**
  183. * Index of the first item on the current page.
  184. */
  185. private int currentPageFirstItemIndex = 0;
  186. /**
  187. * Holds value of property selectable.
  188. */
  189. private boolean selectable = false;
  190. /**
  191. * Holds value of property columnHeaderMode.
  192. */
  193. private int columnHeaderMode = COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID;
  194. /**
  195. * True iff the row captions are hidden.
  196. */
  197. private boolean rowCaptionsAreHidden = true;
  198. /**
  199. * Page contents buffer used in buffered mode.
  200. */
  201. private Object[][] pageBuffer = null;
  202. /**
  203. * Set of properties listened - the list is kept to release the listeners
  204. * later.
  205. */
  206. private HashSet<Property> listenedProperties = null;
  207. /**
  208. * Set of visible components - the is used for needsRepaint calculation.
  209. */
  210. private HashSet<Component> visibleComponents = null;
  211. /**
  212. * List of action handlers.
  213. */
  214. private LinkedList<Handler> actionHandlers = null;
  215. /**
  216. * Action mapper.
  217. */
  218. private KeyMapper actionMapper = null;
  219. /**
  220. * Table cell editor factory.
  221. */
  222. private TableFieldFactory fieldFactory = DefaultFieldFactory.get();
  223. /**
  224. * Is table editable.
  225. */
  226. private boolean editable = false;
  227. /**
  228. * Current sorting direction.
  229. */
  230. private boolean sortAscending = true;
  231. /**
  232. * Currently table is sorted on this propertyId.
  233. */
  234. private Object sortContainerPropertyId = null;
  235. /**
  236. * Is table sorting disabled alltogether; even if some of the properties
  237. * would be sortable.
  238. */
  239. private boolean sortDisabled = false;
  240. /**
  241. * Number of rows explicitly requested by the client to be painted on next
  242. * paint. This is -1 if no request by the client is made. Painting the
  243. * component will automatically reset this to -1.
  244. */
  245. private int reqRowsToPaint = -1;
  246. /**
  247. * Index of the first rows explicitly requested by the client to be painted.
  248. * This is -1 if no request by the client is made. Painting the component
  249. * will automatically reset this to -1.
  250. */
  251. private int reqFirstRowToPaint = -1;
  252. private int firstToBeRenderedInClient = -1;
  253. private int lastToBeRenderedInClient = -1;
  254. private boolean isContentRefreshesEnabled = true;
  255. private int pageBufferFirstIndex;
  256. private boolean containerChangeToBeRendered = false;
  257. /**
  258. * Table cell specific style generator
  259. */
  260. private CellStyleGenerator cellStyleGenerator = null;
  261. private int clickListenerCount;
  262. /*
  263. * EXPERIMENTAL feature: will tell the client to re-calculate column widths
  264. * if set to true. Currently no setter: extend to enable.
  265. */
  266. protected boolean alwaysRecalculateColumnWidths = false;
  267. private double cacheRate = CACHE_RATE_DEFAULT;
  268. /* Table constructors */
  269. /**
  270. * Creates a new empty table.
  271. */
  272. public Table() {
  273. setRowHeaderMode(ROW_HEADER_MODE_HIDDEN);
  274. }
  275. /**
  276. * Creates a new empty table with caption.
  277. *
  278. * @param caption
  279. */
  280. public Table(String caption) {
  281. this();
  282. setCaption(caption);
  283. }
  284. /**
  285. * Creates a new table with caption and connect it to a Container.
  286. *
  287. * @param caption
  288. * @param dataSource
  289. */
  290. public Table(String caption, Container dataSource) {
  291. this();
  292. setCaption(caption);
  293. setContainerDataSource(dataSource);
  294. }
  295. /* Table functionality */
  296. /**
  297. * Gets the array of visible column id:s, including generated columns.
  298. *
  299. * <p>
  300. * The columns are show in the order of their appearance in this array.
  301. * </p>
  302. *
  303. * @return an array of currently visible propertyIds and generated column
  304. * ids.
  305. */
  306. public Object[] getVisibleColumns() {
  307. if (visibleColumns == null) {
  308. return null;
  309. }
  310. return visibleColumns.toArray();
  311. }
  312. /**
  313. * Sets the array of visible column property id:s.
  314. *
  315. * <p>
  316. * The columns are show in the order of their appearance in this array.
  317. * </p>
  318. *
  319. * @param visibleColumns
  320. * the Array of shown property id:s.
  321. */
  322. public void setVisibleColumns(Object[] visibleColumns) {
  323. // Visible columns must exist
  324. if (visibleColumns == null) {
  325. throw new NullPointerException(
  326. "Can not set visible columns to null value");
  327. }
  328. // Checks that the new visible columns contains no nulls and properties
  329. // exist
  330. final Collection properties = getContainerPropertyIds();
  331. for (int i = 0; i < visibleColumns.length; i++) {
  332. if (visibleColumns[i] == null) {
  333. throw new NullPointerException("Ids must be non-nulls");
  334. } else if (!properties.contains(visibleColumns[i])
  335. && !columnGenerators.containsKey(visibleColumns[i])) {
  336. throw new IllegalArgumentException(
  337. "Ids must exist in the Container or as a generated column , missing id: "
  338. + visibleColumns[i]);
  339. }
  340. }
  341. // If this is called before the constructor is finished, it might be
  342. // uninitialized
  343. final LinkedList<Object> newVC = new LinkedList<Object>();
  344. for (int i = 0; i < visibleColumns.length; i++) {
  345. newVC.add(visibleColumns[i]);
  346. }
  347. // Removes alignments, icons and headers from hidden columns
  348. if (this.visibleColumns != null) {
  349. boolean disabledHere = disableContentRefreshing();
  350. try {
  351. for (final Iterator<Object> i = this.visibleColumns.iterator(); i
  352. .hasNext();) {
  353. final Object col = i.next();
  354. if (!newVC.contains(col)) {
  355. setColumnHeader(col, null);
  356. setColumnAlignment(col, null);
  357. setColumnIcon(col, null);
  358. }
  359. }
  360. } finally {
  361. if (disabledHere) {
  362. enableContentRefreshing(false);
  363. }
  364. }
  365. }
  366. this.visibleColumns = newVC;
  367. // Assures visual refresh
  368. resetPageBuffer();
  369. refreshRenderedCells();
  370. }
  371. /**
  372. * Gets the headers of the columns.
  373. *
  374. * <p>
  375. * The headers match the property id:s given my the set visible column
  376. * headers. The table must be set in either
  377. * <code>COLUMN_HEADER_MODE_EXPLICIT</code> or
  378. * <code>COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID</code> mode to show the
  379. * headers. In the defaults mode any nulls in the headers array are replaced
  380. * with id.toString() outputs when rendering.
  381. * </p>
  382. *
  383. * @return the Array of column headers.
  384. */
  385. public String[] getColumnHeaders() {
  386. if (columnHeaders == null) {
  387. return null;
  388. }
  389. final String[] headers = new String[visibleColumns.size()];
  390. int i = 0;
  391. for (final Iterator<Object> it = visibleColumns.iterator(); it
  392. .hasNext(); i++) {
  393. headers[i] = columnHeaders.get(it.next());
  394. }
  395. return headers;
  396. }
  397. /**
  398. * Sets the headers of the columns.
  399. *
  400. * <p>
  401. * The headers match the property id:s given my the set visible column
  402. * headers. The table must be set in either
  403. * <code>COLUMN_HEADER_MODE_EXPLICIT</code> or
  404. * <code>COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID</code> mode to show the
  405. * headers. In the defaults mode any nulls in the headers array are replaced
  406. * with id.toString() outputs when rendering.
  407. * </p>
  408. *
  409. * @param columnHeaders
  410. * the Array of column headers that match the
  411. * <code>getVisibleColumns</code> method.
  412. */
  413. public void setColumnHeaders(String[] columnHeaders) {
  414. if (columnHeaders.length != visibleColumns.size()) {
  415. throw new IllegalArgumentException(
  416. "The length of the headers array must match the number of visible columns");
  417. }
  418. this.columnHeaders.clear();
  419. int i = 0;
  420. for (final Iterator<Object> it = visibleColumns.iterator(); it
  421. .hasNext()
  422. && i < columnHeaders.length; i++) {
  423. this.columnHeaders.put(it.next(), columnHeaders[i]);
  424. }
  425. // Assures the visual refresh
  426. resetPageBuffer();
  427. refreshRenderedCells();
  428. }
  429. /**
  430. * Gets the icons of the columns.
  431. *
  432. * <p>
  433. * The icons in headers match the property id:s given my the set visible
  434. * column headers. The table must be set in either
  435. * <code>COLUMN_HEADER_MODE_EXPLICIT</code> or
  436. * <code>COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID</code> mode to show the
  437. * headers with icons.
  438. * </p>
  439. *
  440. * @return the Array of icons that match the <code>getVisibleColumns</code>.
  441. */
  442. public Resource[] getColumnIcons() {
  443. if (columnIcons == null) {
  444. return null;
  445. }
  446. final Resource[] icons = new Resource[visibleColumns.size()];
  447. int i = 0;
  448. for (final Iterator<Object> it = visibleColumns.iterator(); it
  449. .hasNext(); i++) {
  450. icons[i] = columnIcons.get(it.next());
  451. }
  452. return icons;
  453. }
  454. /**
  455. * Sets the icons of the columns.
  456. *
  457. * <p>
  458. * The icons in headers match the property id:s given my the set visible
  459. * column headers. The table must be set in either
  460. * <code>COLUMN_HEADER_MODE_EXPLICIT</code> or
  461. * <code>COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID</code> mode to show the
  462. * headers with icons.
  463. * </p>
  464. *
  465. * @param columnIcons
  466. * the Array of icons that match the
  467. * <code>getVisibleColumns</code>.
  468. */
  469. public void setColumnIcons(Resource[] columnIcons) {
  470. if (columnIcons.length != visibleColumns.size()) {
  471. throw new IllegalArgumentException(
  472. "The length of the icons array must match the number of visible columns");
  473. }
  474. this.columnIcons.clear();
  475. int i = 0;
  476. for (final Iterator<Object> it = visibleColumns.iterator(); it
  477. .hasNext()
  478. && i < columnIcons.length; i++) {
  479. this.columnIcons.put(it.next(), columnIcons[i]);
  480. }
  481. // Assure visual refresh
  482. resetPageBuffer();
  483. refreshRenderedCells();
  484. }
  485. /**
  486. * Gets the array of column alignments.
  487. *
  488. * <p>
  489. * The items in the array must match the properties identified by
  490. * <code>getVisibleColumns()</code>. The possible values for the alignments
  491. * include:
  492. * <ul>
  493. * <li><code>ALIGN_LEFT</code>: Left alignment</li>
  494. * <li><code>ALIGN_CENTER</code>: Centered</li>
  495. * <li><code>ALIGN_RIGHT</code>: Right alignment</li>
  496. * </ul>
  497. * The alignments default to <code>ALIGN_LEFT</code>: any null values are
  498. * rendered as align lefts.
  499. * </p>
  500. *
  501. * @return the Column alignments array.
  502. */
  503. public String[] getColumnAlignments() {
  504. if (columnAlignments == null) {
  505. return null;
  506. }
  507. final String[] alignments = new String[visibleColumns.size()];
  508. int i = 0;
  509. for (final Iterator<Object> it = visibleColumns.iterator(); it
  510. .hasNext(); i++) {
  511. alignments[i++] = getColumnAlignment(it.next());
  512. }
  513. return alignments;
  514. }
  515. /**
  516. * Sets the column alignments.
  517. *
  518. * <p>
  519. * The items in the array must match the properties identified by
  520. * <code>getVisibleColumns()</code>. The possible values for the alignments
  521. * include:
  522. * <ul>
  523. * <li><code>ALIGN_LEFT</code>: Left alignment</li>
  524. * <li><code>ALIGN_CENTER</code>: Centered</li>
  525. * <li><code>ALIGN_RIGHT</code>: Right alignment</li>
  526. * </ul>
  527. * The alignments default to <code>ALIGN_LEFT</code>
  528. * </p>
  529. *
  530. * @param columnAlignments
  531. * the Column alignments array.
  532. */
  533. public void setColumnAlignments(String[] columnAlignments) {
  534. if (columnAlignments.length != visibleColumns.size()) {
  535. throw new IllegalArgumentException(
  536. "The length of the alignments array must match the number of visible columns");
  537. }
  538. // Checks all alignments
  539. for (int i = 0; i < columnAlignments.length; i++) {
  540. final String a = columnAlignments[i];
  541. if (a != null && !a.equals(ALIGN_LEFT) && !a.equals(ALIGN_CENTER)
  542. && !a.equals(ALIGN_RIGHT)) {
  543. throw new IllegalArgumentException("Column " + i
  544. + " aligment '" + a + "' is invalid");
  545. }
  546. }
  547. // Resets the alignments
  548. final HashMap<Object, String> newCA = new HashMap<Object, String>();
  549. int i = 0;
  550. for (final Iterator<Object> it = visibleColumns.iterator(); it
  551. .hasNext()
  552. && i < columnAlignments.length; i++) {
  553. newCA.put(it.next(), columnAlignments[i]);
  554. }
  555. this.columnAlignments = newCA;
  556. // Assures the visual refresh
  557. resetPageBuffer();
  558. refreshRenderedCells();
  559. }
  560. /**
  561. * Sets columns width (in pixels). Theme may not necessary respect very
  562. * small or very big values. Setting width to -1 (default) means that theme
  563. * will make decision of width.
  564. *
  565. *<p>
  566. * Column can either have a fixed width or expand ratio. The latter one set
  567. * is used. See @link {@link #setColumnExpandRatio(Object, float)}.
  568. *
  569. * @param columnId
  570. * colunmns property id
  571. * @param width
  572. * width to be reserved for colunmns content
  573. * @since 4.0.3
  574. */
  575. public void setColumnWidth(Object columnId, int width) {
  576. if (width < 0) {
  577. columnWidths.remove(columnId);
  578. } else {
  579. columnWidths.put(columnId, new Integer(width));
  580. }
  581. }
  582. /**
  583. * Sets the column expand ratio for given column.
  584. * <p>
  585. * Expand ratios can be defined to customize the way how excess space is
  586. * divided among columns. Table can have excess space if it has its width
  587. * defined and there is horizontally more space than columns consume
  588. * naturally. Excess space is the space that is not used by columns with
  589. * explicit width (see {@link #setColumnWidth(Object, int)}) or with natural
  590. * width (no width nor expand ratio).
  591. *
  592. * <p>
  593. * By default (without expand ratios) the excess space is divided
  594. * proportionally to columns natural widths.
  595. *
  596. * <p>
  597. * Only expand ratios of visible columns are used in final calculations.
  598. *
  599. * <p>
  600. * Column can either have a fixed width or expand ratio. The latter one set
  601. * is used.
  602. *
  603. * <p>
  604. * A column with expand ratio is considered to be minimum width by default
  605. * (if no excess space exists). The minimum width is defined by terminal
  606. * implementation.
  607. *
  608. * <p>
  609. * If terminal implementation supports re-sizeable columns the column
  610. * becomes fixed width column if users resizes the column.
  611. *
  612. * @param columnId
  613. * colunmns property id
  614. * @param expandRatio
  615. * the expandRatio used to divide excess space for this column
  616. */
  617. public void setColumnExpandRatio(Object columnId, float expandRatio) {
  618. if (expandRatio < 0) {
  619. columnWidths.remove(columnId);
  620. } else {
  621. columnWidths.put(columnId, new Float(expandRatio));
  622. }
  623. }
  624. public float getColumnExpandRatio(Object propertyId) {
  625. final Object width = columnWidths.get(propertyId);
  626. if (width == null || !(width instanceof Float)) {
  627. return -1;
  628. }
  629. final Float value = (Float) width;
  630. return value.floatValue();
  631. }
  632. /**
  633. * Gets the pixel width of column
  634. *
  635. * @param propertyId
  636. * @return width of colun or -1 when value not set
  637. */
  638. public int getColumnWidth(Object propertyId) {
  639. final Object width = columnWidths.get(propertyId);
  640. if (width == null || !(width instanceof Integer)) {
  641. return -1;
  642. }
  643. final Integer value = (Integer) width;
  644. return value.intValue();
  645. }
  646. /**
  647. * Gets the page length.
  648. *
  649. * <p>
  650. * Setting page length 0 disables paging.
  651. * </p>
  652. *
  653. * @return the Length of one page.
  654. */
  655. public int getPageLength() {
  656. return pageLength;
  657. }
  658. /**
  659. * Sets the page length.
  660. *
  661. * <p>
  662. * Setting page length 0 disables paging. The page length defaults to 15.
  663. * </p>
  664. *
  665. * <p>
  666. * If Table has width set ({@link #setWidth(float, int)} ) the client side
  667. * may update the page length automatically the correct value.
  668. * </p>
  669. *
  670. * @param pageLength
  671. * the length of one page.
  672. */
  673. public void setPageLength(int pageLength) {
  674. if (pageLength >= 0 && this.pageLength != pageLength) {
  675. this.pageLength = pageLength;
  676. // Assures the visual refresh
  677. resetPageBuffer();
  678. refreshRenderedCells();
  679. }
  680. }
  681. /**
  682. * This method adjusts a possible caching mechanism of table implementation.
  683. *
  684. * <p>
  685. * Table component may fetch and render some rows outside visible area. With
  686. * complex tables (for example containing layouts and components), the
  687. * client side may become unresponsive. Setting the value lower, UI will
  688. * become more responsive. With higher values scrolling in client will hit
  689. * server less frequently.
  690. *
  691. * <p>
  692. * The amount of cached rows will be cacheRate multiplied with pageLength (
  693. * {@link #setPageLength(int)} both below and above visible area..
  694. *
  695. * @param cacheRate
  696. * a value over 0 (fastest rendering time). Higher value will
  697. * cache more rows on server (smoother scrolling). Default value
  698. * is 2.
  699. */
  700. public void setCacheRate(double cacheRate) {
  701. if (cacheRate < 0) {
  702. throw new IllegalArgumentException(
  703. "cacheRate cannot be less than zero");
  704. }
  705. if (this.cacheRate != cacheRate) {
  706. this.cacheRate = cacheRate;
  707. requestRepaint();
  708. }
  709. }
  710. /**
  711. * @see #setCacheRate(double)
  712. *
  713. * @return the current cache rate value
  714. */
  715. public double getCacheRate() {
  716. return cacheRate;
  717. }
  718. /**
  719. * Getter for property currentPageFirstItem.
  720. *
  721. * @return the Value of property currentPageFirstItem.
  722. */
  723. public Object getCurrentPageFirstItemId() {
  724. // Priorise index over id if indexes are supported
  725. if (items instanceof Container.Indexed) {
  726. final int index = getCurrentPageFirstItemIndex();
  727. Object id = null;
  728. if (index >= 0 && index < size()) {
  729. id = ((Container.Indexed) items).getIdByIndex(index);
  730. }
  731. if (id != null && !id.equals(currentPageFirstItemId)) {
  732. currentPageFirstItemId = id;
  733. }
  734. }
  735. // If there is no item id at all, use the first one
  736. if (currentPageFirstItemId == null) {
  737. currentPageFirstItemId = ((Container.Ordered) items).firstItemId();
  738. }
  739. return currentPageFirstItemId;
  740. }
  741. /**
  742. * Setter for property currentPageFirstItemId.
  743. *
  744. * @param currentPageFirstItemId
  745. * the New value of property currentPageFirstItemId.
  746. */
  747. public void setCurrentPageFirstItemId(Object currentPageFirstItemId) {
  748. // Gets the corresponding index
  749. int index = -1;
  750. if (items instanceof Container.Indexed) {
  751. index = ((Container.Indexed) items)
  752. .indexOfId(currentPageFirstItemId);
  753. } else {
  754. // If the table item container does not have index, we have to
  755. // calculates the index by hand
  756. Object id = ((Container.Ordered) items).firstItemId();
  757. while (id != null && !id.equals(currentPageFirstItemId)) {
  758. index++;
  759. id = ((Container.Ordered) items).nextItemId(id);
  760. }
  761. if (id == null) {
  762. index = -1;
  763. }
  764. }
  765. // If the search for item index was successful
  766. if (index >= 0) {
  767. /*
  768. * The table is not capable of displaying an item in the container
  769. * as the first if there are not enough items following the selected
  770. * item so the whole table (pagelength) is filled.
  771. */
  772. int maxIndex = size() - pageLength;
  773. if (maxIndex < 0) {
  774. maxIndex = 0;
  775. }
  776. if (index > maxIndex) {
  777. setCurrentPageFirstItemIndex(maxIndex);
  778. return;
  779. }
  780. this.currentPageFirstItemId = currentPageFirstItemId;
  781. currentPageFirstItemIndex = index;
  782. }
  783. // Assures the visual refresh
  784. resetPageBuffer();
  785. refreshRenderedCells();
  786. }
  787. /**
  788. * Gets the icon Resource for the specified column.
  789. *
  790. * @param propertyId
  791. * the propertyId indentifying the column.
  792. * @return the icon for the specified column; null if the column has no icon
  793. * set, or if the column is not visible.
  794. */
  795. public Resource getColumnIcon(Object propertyId) {
  796. return columnIcons.get(propertyId);
  797. }
  798. /**
  799. * Sets the icon Resource for the specified column.
  800. * <p>
  801. * Throws IllegalArgumentException if the specified column is not visible.
  802. * </p>
  803. *
  804. * @param propertyId
  805. * the propertyId identifying the column.
  806. * @param icon
  807. * the icon Resource to set.
  808. */
  809. public void setColumnIcon(Object propertyId, Resource icon) {
  810. if (icon == null) {
  811. columnIcons.remove(propertyId);
  812. } else {
  813. columnIcons.put(propertyId, icon);
  814. }
  815. // Assures the visual refresh
  816. resetPageBuffer();
  817. refreshRenderedCells();
  818. }
  819. /**
  820. * Gets the header for the specified column.
  821. *
  822. * @param propertyId
  823. * the propertyId indentifying the column.
  824. * @return the header for the specifed column if it has one.
  825. */
  826. public String getColumnHeader(Object propertyId) {
  827. if (getColumnHeaderMode() == COLUMN_HEADER_MODE_HIDDEN) {
  828. return null;
  829. }
  830. String header = columnHeaders.get(propertyId);
  831. if ((header == null && getColumnHeaderMode() == COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID)
  832. || getColumnHeaderMode() == COLUMN_HEADER_MODE_ID) {
  833. header = propertyId.toString();
  834. }
  835. return header;
  836. }
  837. /**
  838. * Sets the column header for the specified column;
  839. *
  840. * @param propertyId
  841. * the propertyId indentifying the column.
  842. * @param header
  843. * the header to set.
  844. */
  845. public void setColumnHeader(Object propertyId, String header) {
  846. if (header == null) {
  847. columnHeaders.remove(propertyId);
  848. return;
  849. }
  850. columnHeaders.put(propertyId, header);
  851. // Assures the visual refresh
  852. refreshRenderedCells();
  853. }
  854. /**
  855. * Gets the specified column's alignment.
  856. *
  857. * @param propertyId
  858. * the propertyID identifying the column.
  859. * @return the specified column's alignment if it as one; null otherwise.
  860. */
  861. public String getColumnAlignment(Object propertyId) {
  862. final String a = columnAlignments.get(propertyId);
  863. return a == null ? ALIGN_LEFT : a;
  864. }
  865. /**
  866. * Sets the specified column's alignment.
  867. *
  868. * <p>
  869. * Throws IllegalArgumentException if the alignment is not one of the
  870. * following: ALIGN_LEFT, ALIGN_CENTER or ALIGN_RIGHT
  871. * </p>
  872. *
  873. * @param propertyId
  874. * the propertyID identifying the column.
  875. * @param alignment
  876. * the desired alignment.
  877. */
  878. public void setColumnAlignment(Object propertyId, String alignment) {
  879. // Checks for valid alignments
  880. if (alignment != null && !alignment.equals(ALIGN_LEFT)
  881. && !alignment.equals(ALIGN_CENTER)
  882. && !alignment.equals(ALIGN_RIGHT)) {
  883. throw new IllegalArgumentException("Column alignment '" + alignment
  884. + "' is not supported.");
  885. }
  886. if (alignment == null || alignment.equals(ALIGN_LEFT)) {
  887. columnAlignments.remove(propertyId);
  888. return;
  889. }
  890. columnAlignments.put(propertyId, alignment);
  891. // Assures the visual refresh
  892. refreshRenderedCells();
  893. }
  894. /**
  895. * Checks if the specified column is collapsed.
  896. *
  897. * @param propertyId
  898. * the propertyID identifying the column.
  899. * @return true if the column is collapsed; false otherwise;
  900. */
  901. public boolean isColumnCollapsed(Object propertyId) {
  902. return collapsedColumns != null
  903. && collapsedColumns.contains(propertyId);
  904. }
  905. /**
  906. * Sets whether the specified column is collapsed or not.
  907. *
  908. *
  909. * @param propertyId
  910. * the propertyID identifying the column.
  911. * @param collapsed
  912. * the desired collapsedness.
  913. * @throws IllegalAccessException
  914. */
  915. public void setColumnCollapsed(Object propertyId, boolean collapsed)
  916. throws IllegalAccessException {
  917. if (!isColumnCollapsingAllowed()) {
  918. throw new IllegalAccessException("Column collapsing not allowed!");
  919. }
  920. if (collapsed) {
  921. collapsedColumns.add(propertyId);
  922. } else {
  923. collapsedColumns.remove(propertyId);
  924. }
  925. // Assures the visual refresh
  926. resetPageBuffer();
  927. refreshRenderedCells();
  928. }
  929. /**
  930. * Checks if column collapsing is allowed.
  931. *
  932. * @return true if columns can be collapsed; false otherwise.
  933. */
  934. public boolean isColumnCollapsingAllowed() {
  935. return columnCollapsingAllowed;
  936. }
  937. /**
  938. * Sets whether column collapsing is allowed or not.
  939. *
  940. * @param collapsingAllowed
  941. * specifies whether column collapsing is allowed.
  942. */
  943. public void setColumnCollapsingAllowed(boolean collapsingAllowed) {
  944. columnCollapsingAllowed = collapsingAllowed;
  945. if (!collapsingAllowed) {
  946. collapsedColumns.clear();
  947. }
  948. // Assures the visual refresh
  949. refreshRenderedCells();
  950. }
  951. /**
  952. * Checks if column reordering is allowed.
  953. *
  954. * @return true if columns can be reordered; false otherwise.
  955. */
  956. public boolean isColumnReorderingAllowed() {
  957. return columnReorderingAllowed;
  958. }
  959. /**
  960. * Sets whether column reordering is allowed or not.
  961. *
  962. * @param reorderingAllowed
  963. * specifies whether column reordering is allowed.
  964. */
  965. public void setColumnReorderingAllowed(boolean reorderingAllowed) {
  966. columnReorderingAllowed = reorderingAllowed;
  967. // Assures the visual refresh
  968. refreshRenderedCells();
  969. }
  970. /*
  971. * Arranges visible columns according to given columnOrder. Silently ignores
  972. * colimnId:s that are not visible columns, and keeps the internal order of
  973. * visible columns left out of the ordering (trailing). Silently does
  974. * nothing if columnReordering is not allowed.
  975. */
  976. private void setColumnOrder(Object[] columnOrder) {
  977. if (columnOrder == null || !isColumnReorderingAllowed()) {
  978. return;
  979. }
  980. final LinkedList<Object> newOrder = new LinkedList<Object>();
  981. for (int i = 0; i < columnOrder.length; i++) {
  982. if (columnOrder[i] != null
  983. && visibleColumns.contains(columnOrder[i])) {
  984. visibleColumns.remove(columnOrder[i]);
  985. newOrder.add(columnOrder[i]);
  986. }
  987. }
  988. for (final Iterator<Object> it = visibleColumns.iterator(); it
  989. .hasNext();) {
  990. final Object columnId = it.next();
  991. if (!newOrder.contains(columnId)) {
  992. newOrder.add(columnId);
  993. }
  994. }
  995. visibleColumns = newOrder;
  996. // Assure visual refresh
  997. resetPageBuffer();
  998. refreshRenderedCells();
  999. }
  1000. /**
  1001. * Getter for property currentPageFirstItem.
  1002. *
  1003. * @return the Value of property currentPageFirstItem.
  1004. */
  1005. public int getCurrentPageFirstItemIndex() {
  1006. return currentPageFirstItemIndex;
  1007. }
  1008. private void setCurrentPageFirstItemIndex(int newIndex,
  1009. boolean needsPageBufferReset) {
  1010. if (newIndex < 0) {
  1011. newIndex = 0;
  1012. }
  1013. /*
  1014. * minimize Container.size() calls which may be expensive. For example
  1015. * it may cause sql query.
  1016. */
  1017. final int size = size();
  1018. /*
  1019. * The table is not capable of displaying an item in the container as
  1020. * the first if there are not enough items following the selected item
  1021. * so the whole table (pagelength) is filled.
  1022. */
  1023. int maxIndex = size - pageLength;
  1024. if (maxIndex < 0) {
  1025. maxIndex = 0;
  1026. }
  1027. // Ensures that the new value is valid
  1028. if (newIndex > maxIndex) {
  1029. newIndex = maxIndex;
  1030. }
  1031. // Refresh first item id
  1032. if (items instanceof Container.Indexed) {
  1033. try {
  1034. currentPageFirstItemId = ((Container.Indexed) items)
  1035. .getIdByIndex(newIndex);
  1036. } catch (final IndexOutOfBoundsException e) {
  1037. currentPageFirstItemId = null;
  1038. }
  1039. currentPageFirstItemIndex = newIndex;
  1040. } else {
  1041. // For containers not supporting indexes, we must iterate the
  1042. // container forwards / backwards
  1043. // next available item forward or backward
  1044. currentPageFirstItemId = ((Container.Ordered) items).firstItemId();
  1045. // Go forwards in the middle of the list (respect borders)
  1046. while (currentPageFirstItemIndex < newIndex
  1047. && !((Container.Ordered) items)
  1048. .isLastId(currentPageFirstItemId)) {
  1049. currentPageFirstItemIndex++;
  1050. currentPageFirstItemId = ((Container.Ordered) items)
  1051. .nextItemId(currentPageFirstItemId);
  1052. }
  1053. // If we did hit the border
  1054. if (((Container.Ordered) items).isLastId(currentPageFirstItemId)) {
  1055. currentPageFirstItemIndex = size - 1;
  1056. }
  1057. // Go backwards in the middle of the list (respect borders)
  1058. while (currentPageFirstItemIndex > newIndex
  1059. && !((Container.Ordered) items)
  1060. .isFirstId(currentPageFirstItemId)) {
  1061. currentPageFirstItemIndex--;
  1062. currentPageFirstItemId = ((Container.Ordered) items)
  1063. .prevItemId(currentPageFirstItemId);
  1064. }
  1065. // If we did hit the border
  1066. if (((Container.Ordered) items).isFirstId(currentPageFirstItemId)) {
  1067. currentPageFirstItemIndex = 0;
  1068. }
  1069. // Go forwards once more
  1070. while (currentPageFirstItemIndex < newIndex
  1071. && !((Container.Ordered) items)
  1072. .isLastId(currentPageFirstItemId)) {
  1073. currentPageFirstItemIndex++;
  1074. currentPageFirstItemId = ((Container.Ordered) items)
  1075. .nextItemId(currentPageFirstItemId);
  1076. }
  1077. // If for some reason we do hit border again, override
  1078. // the user index request
  1079. if (((Container.Ordered) items).isLastId(currentPageFirstItemId)) {
  1080. newIndex = currentPageFirstItemIndex = size - 1;
  1081. }
  1082. }
  1083. if (needsPageBufferReset) {
  1084. // Assures the visual refresh
  1085. resetPageBuffer();
  1086. refreshRenderedCells();
  1087. }
  1088. }
  1089. /**
  1090. * Setter for property currentPageFirstItem.
  1091. *
  1092. * @param newIndex
  1093. * the New value of property currentPageFirstItem.
  1094. */
  1095. public void setCurrentPageFirstItemIndex(int newIndex) {
  1096. setCurrentPageFirstItemIndex(newIndex, true);
  1097. }
  1098. /**
  1099. * Getter for property pageBuffering.
  1100. *
  1101. * @deprecated functionality is not needed in ajax rendering model
  1102. *
  1103. * @return the Value of property pageBuffering.
  1104. */
  1105. @Deprecated
  1106. public boolean isPageBufferingEnabled() {
  1107. return true;
  1108. }
  1109. /**
  1110. * Setter for property pageBuffering.
  1111. *
  1112. * @deprecated functionality is not needed in ajax rendering model
  1113. *
  1114. * @param pageBuffering
  1115. * the New value of property pageBuffering.
  1116. */
  1117. @Deprecated
  1118. public void setPageBufferingEnabled(boolean pageBuffering) {
  1119. }
  1120. /**
  1121. * Getter for property selectable.
  1122. *
  1123. * <p>
  1124. * The table is not selectable by default.
  1125. * </p>
  1126. *
  1127. * @return the Value of property selectable.
  1128. */
  1129. public boolean isSelectable() {
  1130. return selectable;
  1131. }
  1132. /**
  1133. * Setter for property selectable.
  1134. *
  1135. * <p>
  1136. * The table is not selectable by default.
  1137. * </p>
  1138. *
  1139. * @param selectable
  1140. * the New value of property selectable.
  1141. */
  1142. public void setSelectable(boolean selectable) {
  1143. if (this.selectable != selectable) {
  1144. this.selectable = selectable;
  1145. requestRepaint();
  1146. }
  1147. }
  1148. /**
  1149. * Getter for property columnHeaderMode.
  1150. *
  1151. * @return the Value of property columnHeaderMode.
  1152. */
  1153. public int getColumnHeaderMode() {
  1154. return columnHeaderMode;
  1155. }
  1156. /**
  1157. * Setter for property columnHeaderMode.
  1158. *
  1159. * @param columnHeaderMode
  1160. * the New value of property columnHeaderMode.
  1161. */
  1162. public void setColumnHeaderMode(int columnHeaderMode) {
  1163. if (columnHeaderMode >= COLUMN_HEADER_MODE_HIDDEN
  1164. && columnHeaderMode <= COLUMN_HEADER_MODE_EXPLICIT_DEFAULTS_ID) {
  1165. this.columnHeaderMode = columnHeaderMode;
  1166. }
  1167. // Assures the visual refresh
  1168. refreshRenderedCells();
  1169. }
  1170. /**
  1171. * Refreshes rendered rows
  1172. */
  1173. private void refreshRenderedCells() {
  1174. if (getParent() == null) {
  1175. return;
  1176. }
  1177. if (isContentRefreshesEnabled) {
  1178. HashSet<Property> oldListenedProperties = listenedProperties;
  1179. HashSet<Component> oldVisibleComponents = visibleComponents;
  1180. // initialize the listener collections
  1181. listenedProperties = new HashSet<Property>();
  1182. visibleComponents = new HashSet<Component>();
  1183. // Collects the basic facts about the table page
  1184. final Object[] colids = getVisibleColumns();
  1185. final int cols = colids.length;
  1186. final int pagelen = getPageLength();
  1187. int firstIndex = getCurrentPageFirstItemIndex();
  1188. int rows, totalRows;
  1189. rows = totalRows = size();
  1190. if (rows > 0 && firstIndex >= 0) {
  1191. rows -= firstIndex;
  1192. }
  1193. if (pagelen > 0 && pagelen < rows) {
  1194. rows = pagelen;
  1195. }
  1196. // If "to be painted next" variables are set, use them
  1197. if (lastToBeRenderedInClient - firstToBeRenderedInClient > 0) {
  1198. rows = lastToBeRenderedInClient - firstToBeRenderedInClient + 1;
  1199. }
  1200. Object id;
  1201. if (firstToBeRenderedInClient >= 0) {
  1202. if (firstToBeRenderedInClient < totalRows) {
  1203. firstIndex = firstToBeRenderedInClient;
  1204. } else {
  1205. firstIndex = totalRows - 1;
  1206. }
  1207. } else {
  1208. // initial load
  1209. firstToBeRenderedInClient = firstIndex;
  1210. }
  1211. if (totalRows > 0) {
  1212. if (rows + firstIndex > totalRows) {
  1213. rows = totalRows - firstIndex;
  1214. }
  1215. } else {
  1216. rows = 0;
  1217. }
  1218. Object[][] cells = new Object[cols + CELL_FIRSTCOL][rows];
  1219. if (rows == 0) {
  1220. pageBuffer = cells;
  1221. unregisterPropertiesAndComponents(oldListenedProperties,
  1222. oldVisibleComponents);
  1223. return;
  1224. }
  1225. // Gets the first item id
  1226. if (items instanceof Container.Indexed) {
  1227. id = ((Container.Indexed) items).getIdByIndex(firstIndex);
  1228. } else {
  1229. id = ((Container.Ordered) items).firstItemId();
  1230. for (int i = 0; i < firstIndex; i++) {
  1231. id = ((Container.Ordered) items).nextItemId(id);
  1232. }
  1233. }
  1234. final int headmode = getRowHeaderMode();
  1235. final boolean[] iscomponent = new boolean[cols];
  1236. for (int i = 0; i < cols; i++) {
  1237. iscomponent[i] = columnGenerators.containsKey(colids[i])
  1238. || Component.class.isAssignableFrom(getType(colids[i]));
  1239. }
  1240. int firstIndexNotInCache;
  1241. if (pageBuffer != null && pageBuffer[CELL_ITEMID].length > 0) {
  1242. firstIndexNotInCache = pageBufferFirstIndex
  1243. + pageBuffer[CELL_ITEMID].length;
  1244. } else {
  1245. firstIndexNotInCache = -1;
  1246. }
  1247. // Creates the page contents
  1248. int filledRows = 0;
  1249. for (int i = 0; i < rows && id != null; i++) {
  1250. cells[CELL_ITEMID][i] = id;
  1251. cells[CELL_KEY][i] = itemIdMapper.key(id);
  1252. if (headmode != ROW_HEADER_MODE_HIDDEN) {
  1253. switch (headmode) {
  1254. case ROW_HEADER_MODE_INDEX:
  1255. cells[CELL_HEADER][i] = String.valueOf(i + firstIndex
  1256. + 1);
  1257. break;
  1258. default:
  1259. cells[CELL_HEADER][i] = getItemCaption(id);
  1260. }
  1261. cells[CELL_ICON][i] = getItemIcon(id);
  1262. }
  1263. if (cols > 0) {
  1264. for (int j = 0; j < cols; j++) {
  1265. if (isColumnCollapsed(colids[j])) {
  1266. continue;
  1267. }
  1268. Property p = null;
  1269. Object value = "";
  1270. boolean isGenerated = columnGenerators
  1271. .containsKey(colids[j]);
  1272. if (!isGenerated) {
  1273. p = getContainerProperty(id, colids[j]);
  1274. }
  1275. // check in current pageBuffer already has row
  1276. int index = firstIndex + i;
  1277. if (p != null || isGenerated) {
  1278. if (index < firstIndexNotInCache
  1279. && index >= pageBufferFirstIndex) {
  1280. // we have data already in our cache,
  1281. // recycle it instead of fetching it via
  1282. // getValue/getPropertyValue
  1283. int indexInOldBuffer = index
  1284. - pageBufferFirstIndex;
  1285. value = pageBuffer[CELL_FIRSTCOL + j][indexInOldBuffer];
  1286. if (!isGenerated && iscomponent[j]
  1287. || !(value instanceof Component)) {
  1288. listenProperty(p, oldListenedProperties);
  1289. }
  1290. } else {
  1291. if (isGenerated) {
  1292. ColumnGenerator cg = columnGenerators
  1293. .get(colids[j]);
  1294. value = cg
  1295. .generateCell(this, id, colids[j]);
  1296. } else if (iscomponent[j]) {
  1297. value = p.getValue();
  1298. listenProperty(p, oldListenedProperties);
  1299. } else if (p != null) {
  1300. value = getPropertyValue(id, colids[j], p);
  1301. /*
  1302. * If returned value is Component (via
  1303. * fieldfactory or overridden
  1304. * getPropertyValue) we excpect it to listen
  1305. * property value changes. Otherwise if
  1306. * property emits value change events, table
  1307. * will start to listen them and refresh
  1308. * content when needed.
  1309. */
  1310. if (!(value instanceof Component)) {
  1311. listenProperty(p, oldListenedProperties);
  1312. }
  1313. } else {
  1314. value = getPropertyValue(id, colids[j],
  1315. null);
  1316. }
  1317. }
  1318. }
  1319. if (value instanceof Component) {
  1320. if (oldVisibleComponents == null
  1321. || !oldVisibleComponents.contains(value)) {
  1322. ((Component) value).setParent(this);
  1323. }
  1324. visibleComponents.add((Component) value);
  1325. }
  1326. cells[CELL_FIRSTCOL + j][i] = value;
  1327. }
  1328. }
  1329. // Gets the next item id
  1330. if (items instanceof Container.Indexed) {
  1331. Container.Indexed indexed = (Container.Indexed) items;
  1332. int index = firstIndex + i + 1;
  1333. if (index < indexed.size()) {
  1334. id = indexed.getIdByIndex(index);
  1335. } else {
  1336. id = null;
  1337. }
  1338. } else {
  1339. id = ((Container.Ordered) items).nextItemId(id);
  1340. }
  1341. filledRows++;
  1342. }
  1343. // Assures that all the rows of the cell-buffer are valid
  1344. if (filledRows != cells[0].length) {
  1345. final Object[][] temp = new Object[cells.length][filledRows];
  1346. for (int i = 0; i < cells.length; i++) {
  1347. for (int j = 0; j < filledRows; j++) {
  1348. temp[i][j] = cells[i][j];
  1349. }
  1350. }
  1351. cells = temp;
  1352. }
  1353. pageBufferFirstIndex = firstIndex;
  1354. // Saves the results to internal buffer
  1355. pageBuffer = cells;
  1356. unregisterPropertiesAndComponents(oldListenedProperties,
  1357. oldVisibleComponents);
  1358. requestRepaint();
  1359. }
  1360. }
  1361. private void listenProperty(Property p,
  1362. HashSet<Property> oldListenedProperties) {
  1363. if (p instanceof Property.ValueChangeNotifier) {
  1364. if (oldListenedProperties == null
  1365. || !oldListenedProperties.contains(p)) {
  1366. ((Property.ValueChangeNotifier) p).addListener(this);
  1367. }
  1368. /*
  1369. * register listened properties, so we can do proper cleanup to free
  1370. * memory. Essential if table has loads of data and it is used for a
  1371. * long time.
  1372. */
  1373. listenedProperties.add(p);
  1374. }
  1375. }
  1376. /**
  1377. * Helper method to remove listeners and maintain correct component
  1378. * hierarchy. Detaches properties and components if those are no more
  1379. * rendered in client.
  1380. *
  1381. * @param oldListenedProperties
  1382. * set of properties that where listened in last render
  1383. * @param oldVisibleComponents
  1384. * set of components that where attached in last render
  1385. */
  1386. private void unregisterPropertiesAndComponents(
  1387. HashSet<Property> oldListenedProperties,
  1388. HashSet<Component> oldVisibleComponents) {
  1389. if (oldVisibleComponents != null) {
  1390. for (final Iterator<Component> i = oldVisibleComponents.iterator(); i
  1391. .hasNext();) {
  1392. Component c = i.next();
  1393. if (!visibleComponents.contains(c)) {
  1394. c.setParent(null);
  1395. }
  1396. }
  1397. }
  1398. if (oldListenedProperties != null) {
  1399. for (final Iterator<Property> i = oldListenedProperties.iterator(); i
  1400. .hasNext();) {
  1401. Property.ValueChangeNotifier o = (ValueChangeNotifier) i.next();
  1402. if (!listenedProperties.contains(o)) {
  1403. o.removeListener(this);
  1404. }
  1405. }
  1406. }
  1407. }
  1408. /**
  1409. * Refreshes the current page contents.
  1410. *
  1411. * @deprecated should not need to be used
  1412. */
  1413. @Deprecated
  1414. public void refreshCurrentPage() {
  1415. }
  1416. /**
  1417. * Sets the row header mode.
  1418. * <p>
  1419. * The mode can be one of the following ones:
  1420. * <ul>
  1421. * <li><code>ROW_HEADER_MODE_HIDDEN</code>: The row captions are hidden.</li>
  1422. * <li><code>ROW_HEADER_MODE_ID</code>: Items Id-objects
  1423. * <code>toString()</code> is used as row caption.
  1424. * <li><code>ROW_HEADER_MODE_ITEM</code>: Item-objects
  1425. * <code>toString()</code> is used as row caption.
  1426. * <li><code>ROW_HEADER_MODE_PROPERTY</code>: Property set with
  1427. * <code>setItemCaptionPropertyId()</code> is used as row header.
  1428. * <li><code>ROW_HEADER_MODE_EXPLICIT_DEFAULTS_ID</code>: Items Id-objects
  1429. * <code>toString()</code> is used as row header. If caption is explicitly
  1430. * specified, it overrides the id-caption.
  1431. * <li><code>ROW_HEADER_MODE_EXPLICIT</code>: The row headers must be
  1432. * explicitly specified.</li>
  1433. * <li><code>ROW_HEADER_MODE_INDEX</code>: The index of the item is used as
  1434. * row caption. The index mode can only be used with the containers
  1435. * implementing <code>Container.Indexed</code> interface.</li>
  1436. * </ul>
  1437. * The default value is <code>ROW_HEADER_MODE_HIDDEN</code>
  1438. * </p>
  1439. *
  1440. * @param mode
  1441. * the One of the modes listed above.
  1442. */
  1443. public void setRowHeaderMode(int mode) {
  1444. if (ROW_HEADER_MODE_HIDDEN == mode) {
  1445. rowCaptionsAreHidden = true;
  1446. } else {
  1447. rowCaptionsAreHidden = false;
  1448. setItemCaptionMode(mode);
  1449. }
  1450. // Assure visual refresh
  1451. refreshRenderedCells();
  1452. }
  1453. /**
  1454. * Gets the row header mode.
  1455. *
  1456. * @return the Row header mode.
  1457. * @see #setRowHeaderMode(int)
  1458. */
  1459. public int getRowHeaderMode() {
  1460. return rowCaptionsAreHidden ? ROW_HEADER_MODE_HIDDEN
  1461. : getItemCaptionMode();
  1462. }
  1463. /**
  1464. * Adds the new row to table and fill the visible cells (except generated
  1465. * columns) with given values.
  1466. *
  1467. * @param cells
  1468. * the Object array that is used for filling the visible cells
  1469. * new row. The types must be settable to visible column property
  1470. * types.
  1471. * @param itemId
  1472. * the Id the new row. If null, a new id is automatically
  1473. * assigned. If given, the table cant already have a item with
  1474. * given id.
  1475. * @return Returns item id for the new row. Returns null if operation fails.
  1476. */
  1477. public Object addItem(Object[] cells, Object itemId)
  1478. throws UnsupportedOperationException {
  1479. // remove generated columns from the list of columns being assigned
  1480. final LinkedList<Object> availableCols = new LinkedList<Object>();
  1481. for (Iterator<Object> it = visibleColumns.iterator(); it.hasNext();) {
  1482. Object id = it.next();
  1483. if (!columnGenerators.containsKey(id)) {
  1484. availableCols.add(id);
  1485. }
  1486. }
  1487. // Checks that a correct number of cells are given
  1488. if (cells.length != availableCols.size()) {
  1489. return null;
  1490. }
  1491. // Creates new item
  1492. Item item;
  1493. if (itemId == null) {
  1494. itemId = items.addItem();
  1495. if (itemId == null) {
  1496. return null;
  1497. }
  1498. item = items.getItem(itemId);
  1499. } else {
  1500. item = items.addItem(itemId);
  1501. }
  1502. if (item == null) {
  1503. return null;
  1504. }
  1505. // Fills the item properties
  1506. for (int i = 0; i < availableCols.size(); i++) {
  1507. item.getItemProperty(availableCols.get(i)).setValue(cells[i]);
  1508. }
  1509. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  1510. resetPageBuffer();
  1511. refreshRenderedCells();
  1512. }
  1513. return itemId;
  1514. }
  1515. /* Overriding select behavior */
  1516. @Override
  1517. public void setValue(Object newValue) throws ReadOnlyException,
  1518. ConversionException {
  1519. // external selection change, need to truncate pageBuffer
  1520. resetPageBuffer();
  1521. refreshRenderedCells();
  1522. super.setValue(newValue);
  1523. }
  1524. /**
  1525. * Sets the Container that serves as the data source of the viewer.
  1526. *
  1527. * As a side-effect Table's value (selection) is set to null due old
  1528. * selection not necessary exists in new Container.
  1529. *
  1530. * @see com.vaadin.data.Container.Viewer#setContainerDataSource(Container)
  1531. */
  1532. @Override
  1533. public void setContainerDataSource(Container newDataSource) {
  1534. disableContentRefreshing();
  1535. if (newDataSource == null) {
  1536. newDataSource = new IndexedContainer();
  1537. }
  1538. // Assures that the data source is ordered by making unordered
  1539. // containers ordered by wrapping them
  1540. if (newDataSource instanceof Container.Ordered) {
  1541. super.setContainerDataSource(newDataSource);
  1542. } else {
  1543. super.setContainerDataSource(new ContainerOrderedWrapper(
  1544. newDataSource));
  1545. }
  1546. // Resets page position
  1547. currentPageFirstItemId = null;
  1548. currentPageFirstItemIndex = 0;
  1549. // Resets column properties
  1550. if (collapsedColumns != null) {
  1551. collapsedColumns.clear();
  1552. }
  1553. // columnGenerators 'override' properties, don't add the same id twice
  1554. Collection<Object> col = new LinkedList<Object>();
  1555. for (Iterator it = getContainerPropertyIds().iterator(); it.hasNext();) {
  1556. Object id = it.next();
  1557. if (columnGenerators == null || !columnGenerators.containsKey(id)) {
  1558. col.add(id);
  1559. }
  1560. }
  1561. // generators added last
  1562. if (columnGenerators != null && columnGenerators.size() > 0) {
  1563. col.addAll(columnGenerators.keySet());
  1564. }
  1565. setVisibleColumns(col.toArray());
  1566. // null value as we may not be sure that currently selected identifier
  1567. // exits in new ds
  1568. setValue(null);
  1569. // Assure visual refresh
  1570. resetPageBuffer();
  1571. enableContentRefreshing(true);
  1572. }
  1573. /* Component basics */
  1574. /**
  1575. * Invoked when the value of a variable has changed.
  1576. *
  1577. * @see com.vaadin.ui.Select#changeVariables(java.lang.Object,
  1578. * java.util.Map)
  1579. */
  1580. @Override
  1581. public void changeVariables(Object source, Map variables) {
  1582. boolean clientNeedsContentRefresh = false;
  1583. handleClickEvent(variables);
  1584. disableContentRefreshing();
  1585. if (!isSelectable() && variables.containsKey("selected")) {
  1586. // Not-selectable is a special case, AbstractSelect does not support
  1587. // TODO could be optimized.
  1588. variables = new HashMap(variables);
  1589. variables.remove("selected");
  1590. }
  1591. super.changeVariables(source, variables);
  1592. // Client might update the pagelength if Table height is fixed
  1593. if (variables.containsKey("pagelength")) {
  1594. // Sets pageLength directly to avoid repaint that setter causes
  1595. pageLength = (Integer) variables.get("pagelength");
  1596. }
  1597. // Page start index
  1598. if (variables.containsKey("firstvisible")) {
  1599. final Integer value = (Integer) variables.get("firstvisible");
  1600. if (value != null) {
  1601. setCurrentPageFirstItemIndex(value.intValue(), false);
  1602. }
  1603. }
  1604. // Sets requested firstrow and rows for the next paint
  1605. if (variables.containsKey("reqfirstrow")
  1606. || variables.containsKey("reqrows")) {
  1607. try {
  1608. firstToBeRenderedInClient = ((Integer) variables
  1609. .get("firstToBeRendered")).intValue();
  1610. lastToBeRenderedInClient = ((Integer) variables
  1611. .get("lastToBeRendered")).intValue();
  1612. } catch (Exception e) {
  1613. // FIXME: Handle exception
  1614. e.printStackTrace();
  1615. }
  1616. // respect suggested rows only if table is not otherwise updated
  1617. // (row caches emptied by other event)
  1618. if (!containerChangeToBeRendered) {
  1619. Integer value = (Integer) variables.get("reqfirstrow");
  1620. if (value != null) {
  1621. reqFirstRowToPaint = value.intValue();
  1622. }
  1623. value = (Integer) variables.get("reqrows");
  1624. if (value != null) {
  1625. reqRowsToPaint = value.intValue();
  1626. // sanity check
  1627. if (reqFirstRowToPaint + reqRowsToPaint > size()) {
  1628. reqRowsToPaint = size() - reqFirstRowToPaint;
  1629. }
  1630. }
  1631. }
  1632. clientNeedsContentRefresh = true;
  1633. }
  1634. if (!sortDisabled) {
  1635. // Sorting
  1636. boolean doSort = false;
  1637. if (variables.containsKey("sortcolumn")) {
  1638. final String colId = (String) variables.get("sortcolumn");
  1639. if (colId != null && !"".equals(colId) && !"null".equals(colId)) {
  1640. final Object id = columnIdMap.get(colId);
  1641. setSortContainerPropertyId(id, false);
  1642. doSort = true;
  1643. }
  1644. }
  1645. if (variables.containsKey("sortascending")) {
  1646. final boolean state = ((Boolean) variables.get("sortascending"))
  1647. .booleanValue();
  1648. if (state != sortAscending) {
  1649. setSortAscending(state, false);
  1650. doSort = true;
  1651. }
  1652. }
  1653. if (doSort) {
  1654. this.sort();
  1655. resetPageBuffer();
  1656. }
  1657. }
  1658. // Dynamic column hide/show and order
  1659. // Update visible columns
  1660. if (isColumnCollapsingAllowed()) {
  1661. if (variables.containsKey("collapsedcolumns")) {
  1662. try {
  1663. final Object[] ids = (Object[]) variables
  1664. .get("collapsedcolumns");
  1665. for (final Iterator<Object> it = visibleColumns.iterator(); it
  1666. .hasNext();) {
  1667. setColumnCollapsed(it.next(), false);
  1668. }
  1669. for (int i = 0; i < ids.length; i++) {
  1670. setColumnCollapsed(columnIdMap.get(ids[i].toString()),
  1671. true);
  1672. }
  1673. } catch (final Exception e) {
  1674. // FIXME: Handle exception
  1675. e.printStackTrace();
  1676. }
  1677. clientNeedsContentRefresh = true;
  1678. }
  1679. }
  1680. if (isColumnReorderingAllowed()) {
  1681. if (variables.containsKey("columnorder")) {
  1682. try {
  1683. final Object[] ids = (Object[]) variables
  1684. .get("columnorder");
  1685. for (int i = 0; i < ids.length; i++) {
  1686. ids[i] = columnIdMap.get(ids[i].toString());
  1687. }
  1688. setColumnOrder(ids);
  1689. } catch (final Exception e) {
  1690. // FIXME: Handle exception
  1691. e.printStackTrace();
  1692. }
  1693. clientNeedsContentRefresh = true;
  1694. }
  1695. }
  1696. enableContentRefreshing(clientNeedsContentRefresh);
  1697. // Actions
  1698. if (variables.containsKey("action")) {
  1699. final StringTokenizer st = new StringTokenizer((String) variables
  1700. .get("action"), ",");
  1701. if (st.countTokens() == 2) {
  1702. final Object itemId = itemIdMapper.get(st.nextToken());
  1703. final Action action = (Action) actionMapper.get(st.nextToken());
  1704. if (action != null && containsId(itemId)
  1705. && actionHandlers != null) {
  1706. for (final Iterator<Handler> i = actionHandlers.iterator(); i
  1707. .hasNext();) {
  1708. (i.next()).handleAction(action, this, itemId);
  1709. }
  1710. }
  1711. }
  1712. }
  1713. }
  1714. /**
  1715. * Handles click event
  1716. *
  1717. * @param variables
  1718. */
  1719. private void handleClickEvent(Map variables) {
  1720. if (clickListenerCount > 0) {
  1721. if (variables.containsKey("clickEvent")) {
  1722. String key = (String) variables.get("clickedKey");
  1723. Object itemId = itemIdMapper.get(key);
  1724. Object propertyId = null;
  1725. String colkey = (String) variables.get("clickedColKey");
  1726. // click is not necessary on a property
  1727. if (colkey != null) {
  1728. propertyId = columnIdMap.get(colkey);
  1729. }
  1730. MouseEventDetails evt = MouseEventDetails
  1731. .deserialize((String) variables.get("clickEvent"));
  1732. Item item = getItem(itemId);
  1733. if (item != null) {
  1734. fireEvent(new ItemClickEvent(this, item, itemId,
  1735. propertyId, evt));
  1736. }
  1737. }
  1738. }
  1739. }
  1740. /**
  1741. * Go to mode where content updates are not done. This is due we want to
  1742. * bypass expensive content for some reason (like when we know we may have
  1743. * other content changes on their way).
  1744. *
  1745. * @return true if content refresh flag was enabled prior this call
  1746. */
  1747. protected boolean disableContentRefreshing() {
  1748. boolean wasDisabled = isContentRefreshesEnabled;
  1749. isContentRefreshesEnabled = false;
  1750. return wasDisabled;
  1751. }
  1752. /**
  1753. * Go to mode where content content refreshing has effect.
  1754. *
  1755. * @param refreshContent
  1756. * true if content refresh needs to be done
  1757. */
  1758. protected void enableContentRefreshing(boolean refreshContent) {
  1759. isContentRefreshesEnabled = true;
  1760. if (refreshContent) {
  1761. refreshRenderedCells();
  1762. // Ensure that client gets a response
  1763. requestRepaint();
  1764. }
  1765. }
  1766. /*
  1767. * (non-Javadoc)
  1768. *
  1769. * @see com.vaadin.ui.AbstractSelect#paintContent(com.vaadin.
  1770. * terminal.PaintTarget)
  1771. */
  1772. @Override
  1773. public void paintContent(PaintTarget target) throws PaintException {
  1774. // The tab ordering number
  1775. if (getTabIndex() > 0) {
  1776. target.addAttribute("tabindex", getTabIndex());
  1777. }
  1778. // Initialize temps
  1779. final Object[] colids = getVisibleColumns();
  1780. final int cols = colids.length;
  1781. final int first = getCurrentPageFirstItemIndex();
  1782. int total = size();
  1783. final int pagelen = getPageLength();
  1784. final int colHeadMode = getColumnHeaderMode();
  1785. final boolean colheads = colHeadMode != COLUMN_HEADER_MODE_HIDDEN;
  1786. final boolean rowheads = getRowHeaderMode() != ROW_HEADER_MODE_HIDDEN;
  1787. final Object[][] cells = getVisibleCells();
  1788. final boolean iseditable = isEditable();
  1789. int rows;
  1790. if (reqRowsToPaint >= 0) {
  1791. rows = reqRowsToPaint;
  1792. } else {
  1793. rows = cells[0].length;
  1794. if (alwaysRecalculateColumnWidths) {
  1795. // TODO experimental feature for now: tell the client to
  1796. // recalculate column widths.
  1797. // We'll only do this for paints that do not originate from
  1798. // table scroll/cache requests (i.e when reqRowsToPaint<0)
  1799. target.addAttribute("recalcWidths", true);
  1800. }
  1801. }
  1802. if (!isNullSelectionAllowed() && getNullSelectionItemId() != null
  1803. && containsId(getNullSelectionItemId())) {
  1804. total--;
  1805. rows--;
  1806. }
  1807. // selection support
  1808. LinkedList<String> selectedKeys = new LinkedList<String>();
  1809. if (isMultiSelect()) {
  1810. // only paint selections that are currently visible in the client
  1811. HashSet sel = new HashSet((Set) getValue());
  1812. Collection vids = getVisibleItemIds();
  1813. for (Iterator it = vids.iterator(); it.hasNext();) {
  1814. Object id = it.next();
  1815. if (sel.contains(id)) {
  1816. selectedKeys.add(itemIdMapper.key(id));
  1817. }
  1818. }
  1819. } else {
  1820. Object value = getValue();
  1821. if (value == null) {
  1822. value = getNullSelectionItemId();
  1823. }
  1824. if (value != null) {
  1825. selectedKeys.add(itemIdMapper.key(value));
  1826. }
  1827. }
  1828. // Table attributes
  1829. if (isSelectable()) {
  1830. target.addAttribute("selectmode", (isMultiSelect() ? "multi"
  1831. : "single"));
  1832. } else {
  1833. target.addAttribute("selectmode", "none");
  1834. }
  1835. if (clickListenerCount > 0) {
  1836. target.addAttribute("listenClicks", true);
  1837. }
  1838. if (cacheRate != CACHE_RATE_DEFAULT) {
  1839. target.addAttribute("cr", cacheRate);
  1840. }
  1841. target.addAttribute("cols", cols);
  1842. target.addAttribute("rows", rows);
  1843. target.addAttribute("firstrow",
  1844. (reqFirstRowToPaint >= 0 ? reqFirstRowToPaint
  1845. : firstToBeRenderedInClient));
  1846. target.addAttribute("totalrows", total);
  1847. if (pagelen != 0) {
  1848. target.addAttribute("pagelength", pagelen);
  1849. }
  1850. if (colheads) {
  1851. target.addAttribute("colheaders", true);
  1852. }
  1853. if (rowheads) {
  1854. target.addAttribute("rowheaders", true);
  1855. }
  1856. // Visible column order
  1857. final Collection sortables = getSortableContainerPropertyIds();
  1858. final ArrayList<String> visibleColOrder = new ArrayList<String>();
  1859. for (final Iterator<Object> it = visibleColumns.iterator(); it
  1860. .hasNext();) {
  1861. final Object columnId = it.next();
  1862. if (!isColumnCollapsed(columnId)) {
  1863. visibleColOrder.add(columnIdMap.key(columnId));
  1864. }
  1865. }
  1866. target.addAttribute("vcolorder", visibleColOrder.toArray());
  1867. // Rows
  1868. final Set<Action> actionSet = new LinkedHashSet<Action>();
  1869. final boolean selectable = isSelectable();
  1870. final boolean[] iscomponent = new boolean[visibleColumns.size()];
  1871. int iscomponentIndex = 0;
  1872. for (final Iterator<Object> it = visibleColumns.iterator(); it
  1873. .hasNext()
  1874. && iscomponentIndex < iscomponent.length;) {
  1875. final Object columnId = it.next();
  1876. if (columnGenerators.containsKey(columnId)) {
  1877. iscomponent[iscomponentIndex++] = true;
  1878. } else {
  1879. final Class colType = getType(columnId);
  1880. iscomponent[iscomponentIndex++] = colType != null
  1881. && Component.class.isAssignableFrom(colType);
  1882. }
  1883. }
  1884. target.startTag("rows");
  1885. // cells array contains all that are supposed to be visible on client,
  1886. // but we'll start from the one requested by client
  1887. int start = 0;
  1888. if (reqFirstRowToPaint != -1 && firstToBeRenderedInClient != -1) {
  1889. start = reqFirstRowToPaint - firstToBeRenderedInClient;
  1890. }
  1891. int end = cells[0].length;
  1892. if (reqRowsToPaint != -1) {
  1893. end = start + reqRowsToPaint;
  1894. }
  1895. // sanity check
  1896. if (lastToBeRenderedInClient != -1 && lastToBeRenderedInClient < end) {
  1897. end = lastToBeRenderedInClient + 1;
  1898. }
  1899. if (start > cells[CELL_ITEMID].length || start < 0) {
  1900. start = 0;
  1901. }
  1902. for (int i = start; i < end; i++) {
  1903. final Object itemId = cells[CELL_ITEMID][i];
  1904. if (!isNullSelectionAllowed() && getNullSelectionItemId() != null
  1905. && itemId == getNullSelectionItemId()) {
  1906. // Remove null selection item if null selection is not allowed
  1907. continue;
  1908. }
  1909. target.startTag("tr");
  1910. // tr attributes
  1911. if (rowheads) {
  1912. if (cells[CELL_ICON][i] != null) {
  1913. target.addAttribute("icon", (Resource) cells[CELL_ICON][i]);
  1914. }
  1915. if (cells[CELL_HEADER][i] != null) {
  1916. target.addAttribute("caption",
  1917. (String) cells[CELL_HEADER][i]);
  1918. }
  1919. }
  1920. target.addAttribute("key", Integer.parseInt(cells[CELL_KEY][i]
  1921. .toString()));
  1922. if (isSelected(itemId)) {
  1923. target.addAttribute("selected", true);
  1924. }
  1925. // Actions
  1926. if (actionHandlers != null) {
  1927. final ArrayList<String> keys = new ArrayList<String>();
  1928. for (final Iterator<Handler> ahi = actionHandlers.iterator(); ahi
  1929. .hasNext();) {
  1930. final Action[] aa = (ahi.next()).getActions(itemId, this);
  1931. if (aa != null) {
  1932. for (int ai = 0; ai < aa.length; ai++) {
  1933. final String key = actionMapper.key(aa[ai]);
  1934. actionSet.add(aa[ai]);
  1935. keys.add(key);
  1936. }
  1937. }
  1938. }
  1939. target.addAttribute("al", keys.toArray());
  1940. }
  1941. /*
  1942. * For each row, if a cellStyleGenerator is specified, get the
  1943. * specific style for the cell, using null as propertyId. If there
  1944. * is any, add it to the target.
  1945. */
  1946. if (cellStyleGenerator != null) {
  1947. String rowStyle = cellStyleGenerator.getStyle(itemId, null);
  1948. if (rowStyle != null && !rowStyle.equals("")) {
  1949. target.addAttribute("rowstyle", rowStyle);
  1950. }
  1951. }
  1952. // cells
  1953. int currentColumn = 0;
  1954. for (final Iterator<Object> it = visibleColumns.iterator(); it
  1955. .hasNext(); currentColumn++) {
  1956. final Object columnId = it.next();
  1957. if (columnId == null || isColumnCollapsed(columnId)) {
  1958. continue;
  1959. }
  1960. /*
  1961. * For each cell, if a cellStyleGenerator is specified, get the
  1962. * specific style for the cell. If there is any, add it to the
  1963. * target.
  1964. */
  1965. if (cellStyleGenerator != null) {
  1966. String cellStyle = cellStyleGenerator.getStyle(itemId,
  1967. columnId);
  1968. if (cellStyle != null && !cellStyle.equals("")) {
  1969. target.addAttribute("style-"
  1970. + columnIdMap.key(columnId), cellStyle);
  1971. }
  1972. }
  1973. if ((iscomponent[currentColumn] || iseditable)
  1974. && Component.class.isInstance(cells[CELL_FIRSTCOL
  1975. + currentColumn][i])) {
  1976. final Component c = (Component) cells[CELL_FIRSTCOL
  1977. + currentColumn][i];
  1978. if (c == null) {
  1979. target.addText("");
  1980. } else {
  1981. c.paint(target);
  1982. }
  1983. } else {
  1984. target
  1985. .addText((String) cells[CELL_FIRSTCOL
  1986. + currentColumn][i]);
  1987. }
  1988. }
  1989. target.endTag("tr");
  1990. }
  1991. target.endTag("rows");
  1992. // The select variable is only enabled if selectable
  1993. if (selectable) {
  1994. target.addVariable(this, "selected", selectedKeys
  1995. .toArray(new String[selectedKeys.size()]));
  1996. }
  1997. // The cursors are only shown on pageable table
  1998. if (first != 0 || getPageLength() > 0) {
  1999. target.addVariable(this, "firstvisible", first);
  2000. }
  2001. // Sorting
  2002. if (getContainerDataSource() instanceof Container.Sortable) {
  2003. target.addVariable(this, "sortcolumn", columnIdMap
  2004. .key(sortContainerPropertyId));
  2005. target.addVariable(this, "sortascending", sortAscending);
  2006. }
  2007. // Resets and paints "to be painted next" variables. Also reset
  2008. // pageBuffer
  2009. reqFirstRowToPaint = -1;
  2010. reqRowsToPaint = -1;
  2011. containerChangeToBeRendered = false;
  2012. target.addVariable(this, "reqrows", reqRowsToPaint);
  2013. target.addVariable(this, "reqfirstrow", reqFirstRowToPaint);
  2014. // Actions
  2015. if (!actionSet.isEmpty()) {
  2016. target.addVariable(this, "action", "");
  2017. target.startTag("actions");
  2018. for (final Iterator<Action> it = actionSet.iterator(); it.hasNext();) {
  2019. final Action a = it.next();
  2020. target.startTag("action");
  2021. if (a.getCaption() != null) {
  2022. target.addAttribute("caption", a.getCaption());
  2023. }
  2024. if (a.getIcon() != null) {
  2025. target.addAttribute("icon", a.getIcon());
  2026. }
  2027. target.addAttribute("key", actionMapper.key(a));
  2028. target.endTag("action");
  2029. }
  2030. target.endTag("actions");
  2031. }
  2032. if (columnReorderingAllowed) {
  2033. final String[] colorder = new String[visibleColumns.size()];
  2034. int i = 0;
  2035. for (final Iterator<Object> it = visibleColumns.iterator(); it
  2036. .hasNext()
  2037. && i < colorder.length;) {
  2038. colorder[i++] = columnIdMap.key(it.next());
  2039. }
  2040. target.addVariable(this, "columnorder", colorder);
  2041. }
  2042. // Available columns
  2043. if (columnCollapsingAllowed) {
  2044. final HashSet<Object> ccs = new HashSet<Object>();
  2045. for (final Iterator<Object> i = visibleColumns.iterator(); i
  2046. .hasNext();) {
  2047. final Object o = i.next();
  2048. if (isColumnCollapsed(o)) {
  2049. ccs.add(o);
  2050. }
  2051. }
  2052. final String[] collapsedkeys = new String[ccs.size()];
  2053. int nextColumn = 0;
  2054. for (final Iterator<Object> it = visibleColumns.iterator(); it
  2055. .hasNext()
  2056. && nextColumn < collapsedkeys.length;) {
  2057. final Object columnId = it.next();
  2058. if (isColumnCollapsed(columnId)) {
  2059. collapsedkeys[nextColumn++] = columnIdMap.key(columnId);
  2060. }
  2061. }
  2062. target.addVariable(this, "collapsedcolumns", collapsedkeys);
  2063. }
  2064. target.startTag("visiblecolumns");
  2065. int i = 0;
  2066. for (final Iterator<Object> it = visibleColumns.iterator(); it
  2067. .hasNext(); i++) {
  2068. final Object columnId = it.next();
  2069. if (columnId != null) {
  2070. target.startTag("column");
  2071. target.addAttribute("cid", columnIdMap.key(columnId));
  2072. final String head = getColumnHeader(columnId);
  2073. target.addAttribute("caption", (head != null ? head : ""));
  2074. if (isColumnCollapsed(columnId)) {
  2075. target.addAttribute("collapsed", true);
  2076. }
  2077. if (colheads) {
  2078. if (getColumnIcon(columnId) != null) {
  2079. target.addAttribute("icon", getColumnIcon(columnId));
  2080. }
  2081. if (sortables.contains(columnId)) {
  2082. target.addAttribute("sortable", true);
  2083. }
  2084. }
  2085. if (!ALIGN_LEFT.equals(getColumnAlignment(columnId))) {
  2086. target.addAttribute("align", getColumnAlignment(columnId));
  2087. }
  2088. if (columnWidths.containsKey(columnId)) {
  2089. if (getColumnWidth(columnId) > -1) {
  2090. target.addAttribute("width", String
  2091. .valueOf(getColumnWidth(columnId)));
  2092. } else {
  2093. target.addAttribute("er",
  2094. getColumnExpandRatio(columnId));
  2095. }
  2096. }
  2097. target.endTag("column");
  2098. }
  2099. }
  2100. target.endTag("visiblecolumns");
  2101. }
  2102. /**
  2103. * Gets the cached visible table contents.
  2104. *
  2105. * @return the cached visible table contents.
  2106. */
  2107. private Object[][] getVisibleCells() {
  2108. if (pageBuffer == null) {
  2109. refreshRenderedCells();
  2110. }
  2111. return pageBuffer;
  2112. }
  2113. /**
  2114. * Gets the value of property.
  2115. *
  2116. * By default if the table is editable the fieldFactory is used to create
  2117. * editors for table cells. Otherwise formatPropertyValue is used to format
  2118. * the value representation.
  2119. *
  2120. * @param rowId
  2121. * the Id of the row (same as item Id).
  2122. * @param colId
  2123. * the Id of the column.
  2124. * @param property
  2125. * the Property to be presented.
  2126. * @return Object Either formatted value or Component for field.
  2127. * @see #setTableFieldFactory(TableFieldFactory)
  2128. */
  2129. protected Object getPropertyValue(Object rowId, Object colId,
  2130. Property property) {
  2131. if (isEditable() && fieldFactory != null) {
  2132. final Field f = fieldFactory.createField(getContainerDataSource(),
  2133. rowId, colId, this);
  2134. if (f != null) {
  2135. f.setPropertyDataSource(property);
  2136. return f;
  2137. }
  2138. }
  2139. return formatPropertyValue(rowId, colId, property);
  2140. }
  2141. /**
  2142. * Formats table cell property values. By default the property.toString()
  2143. * and return a empty string for null properties.
  2144. *
  2145. * @param rowId
  2146. * the Id of the row (same as item Id).
  2147. * @param colId
  2148. * the Id of the column.
  2149. * @param property
  2150. * the Property to be formatted.
  2151. * @return the String representation of property and its value.
  2152. * @since 3.1
  2153. */
  2154. protected String formatPropertyValue(Object rowId, Object colId,
  2155. Property property) {
  2156. if (property == null) {
  2157. return "";
  2158. }
  2159. return property.toString();
  2160. }
  2161. /* Action container */
  2162. /**
  2163. * Registers a new action handler for this container
  2164. *
  2165. * @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler)
  2166. */
  2167. public void addActionHandler(Action.Handler actionHandler) {
  2168. if (actionHandler != null) {
  2169. if (actionHandlers == null) {
  2170. actionHandlers = new LinkedList<Handler>();
  2171. actionMapper = new KeyMapper();
  2172. }
  2173. if (!actionHandlers.contains(actionHandler)) {
  2174. actionHandlers.add(actionHandler);
  2175. requestRepaint();
  2176. }
  2177. }
  2178. }
  2179. /**
  2180. * Removes a previously registered action handler for the contents of this
  2181. * container.
  2182. *
  2183. * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler)
  2184. */
  2185. public void removeActionHandler(Action.Handler actionHandler) {
  2186. if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
  2187. actionHandlers.remove(actionHandler);
  2188. if (actionHandlers.isEmpty()) {
  2189. actionHandlers = null;
  2190. actionMapper = null;
  2191. }
  2192. requestRepaint();
  2193. }
  2194. }
  2195. /* Property value change listening support */
  2196. /**
  2197. * Notifies this listener that the Property's value has changed.
  2198. *
  2199. * Also listens changes in rendered items to refresh content area.
  2200. *
  2201. * @see com.vaadin.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent)
  2202. */
  2203. @Override
  2204. public void valueChange(Property.ValueChangeEvent event) {
  2205. if (event.getProperty() == this) {
  2206. super.valueChange(event);
  2207. } else {
  2208. resetPageBuffer();
  2209. refreshRenderedCells();
  2210. containerChangeToBeRendered = true;
  2211. }
  2212. requestRepaint();
  2213. }
  2214. private void resetPageBuffer() {
  2215. firstToBeRenderedInClient = -1;
  2216. lastToBeRenderedInClient = -1;
  2217. reqFirstRowToPaint = -1;
  2218. reqRowsToPaint = -1;
  2219. pageBuffer = null;
  2220. }
  2221. /**
  2222. * Notifies the component that it is connected to an application.
  2223. *
  2224. * @see com.vaadin.ui.Component#attach()
  2225. */
  2226. @Override
  2227. public void attach() {
  2228. super.attach();
  2229. refreshRenderedCells();
  2230. if (visibleComponents != null) {
  2231. for (final Iterator<Component> i = visibleComponents.iterator(); i
  2232. .hasNext();) {
  2233. i.next().attach();
  2234. }
  2235. }
  2236. }
  2237. /**
  2238. * Notifies the component that it is detached from the application
  2239. *
  2240. * @see com.vaadin.ui.Component#detach()
  2241. */
  2242. @Override
  2243. public void detach() {
  2244. super.detach();
  2245. if (visibleComponents != null) {
  2246. for (final Iterator<Component> i = visibleComponents.iterator(); i
  2247. .hasNext();) {
  2248. i.next().detach();
  2249. }
  2250. }
  2251. }
  2252. /**
  2253. * Removes all Items from the Container.
  2254. *
  2255. * @see com.vaadin.data.Container#removeAllItems()
  2256. */
  2257. @Override
  2258. public boolean removeAllItems() {
  2259. currentPageFirstItemId = null;
  2260. currentPageFirstItemIndex = 0;
  2261. return super.removeAllItems();
  2262. }
  2263. /**
  2264. * Removes the Item identified by <code>ItemId</code> from the Container.
  2265. *
  2266. * @see com.vaadin.data.Container#removeItem(Object)
  2267. */
  2268. @Override
  2269. public boolean removeItem(Object itemId) {
  2270. final Object nextItemId = ((Container.Ordered) items)
  2271. .nextItemId(itemId);
  2272. final boolean ret = super.removeItem(itemId);
  2273. if (ret && (itemId != null) && (itemId.equals(currentPageFirstItemId))) {
  2274. currentPageFirstItemId = nextItemId;
  2275. }
  2276. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  2277. resetPageBuffer();
  2278. refreshRenderedCells();
  2279. }
  2280. return ret;
  2281. }
  2282. /**
  2283. * Removes a Property specified by the given Property ID from the Container.
  2284. *
  2285. * @see com.vaadin.data.Container#removeContainerProperty(Object)
  2286. */
  2287. @Override
  2288. public boolean removeContainerProperty(Object propertyId)
  2289. throws UnsupportedOperationException {
  2290. // If a visible property is removed, remove the corresponding column
  2291. visibleColumns.remove(propertyId);
  2292. columnAlignments.remove(propertyId);
  2293. columnIcons.remove(propertyId);
  2294. columnHeaders.remove(propertyId);
  2295. return super.removeContainerProperty(propertyId);
  2296. }
  2297. /**
  2298. * Adds a new property to the table and show it as a visible column.
  2299. *
  2300. * @param propertyId
  2301. * the Id of the proprty.
  2302. * @param type
  2303. * the class of the property.
  2304. * @param defaultValue
  2305. * the default value given for all existing items.
  2306. * @see com.vaadin.data.Container#addContainerProperty(Object, Class,
  2307. * Object)
  2308. */
  2309. @Override
  2310. public boolean addContainerProperty(Object propertyId, Class type,
  2311. Object defaultValue) throws UnsupportedOperationException {
  2312. boolean visibleColAdded = false;
  2313. if (!visibleColumns.contains(propertyId)) {
  2314. visibleColumns.add(propertyId);
  2315. visibleColAdded = true;
  2316. }
  2317. if (!super.addContainerProperty(propertyId, type, defaultValue)) {
  2318. if (visibleColAdded) {
  2319. visibleColumns.remove(propertyId);
  2320. }
  2321. return false;
  2322. }
  2323. if (!(items instanceof Container.PropertySetChangeNotifier)) {
  2324. resetPageBuffer();
  2325. refreshRenderedCells();
  2326. }
  2327. return true;
  2328. }
  2329. /**
  2330. * Adds a new property to the table and show it as a visible column.
  2331. *
  2332. * @param propertyId
  2333. * the Id of the proprty
  2334. * @param type
  2335. * the class of the property
  2336. * @param defaultValue
  2337. * the default value given for all existing items
  2338. * @param columnHeader
  2339. * the Explicit header of the column. If explicit header is not
  2340. * needed, this should be set null.
  2341. * @param columnIcon
  2342. * the Icon of the column. If icon is not needed, this should be
  2343. * set null.
  2344. * @param columnAlignment
  2345. * the Alignment of the column. Null implies align left.
  2346. * @throws UnsupportedOperationException
  2347. * if the operation is not supported.
  2348. * @see com.vaadin.data.Container#addContainerProperty(Object, Class,
  2349. * Object)
  2350. */
  2351. public boolean addContainerProperty(Object propertyId, Class type,
  2352. Object defaultValue, String columnHeader, Resource columnIcon,
  2353. String columnAlignment) throws UnsupportedOperationException {
  2354. if (!this.addContainerProperty(propertyId, type, defaultValue)) {
  2355. return false;
  2356. }
  2357. setColumnAlignment(propertyId, columnAlignment);
  2358. setColumnHeader(propertyId, columnHeader);
  2359. setColumnIcon(propertyId, columnIcon);
  2360. return true;
  2361. }
  2362. /**
  2363. * Adds a generated column to the Table.
  2364. * <p>
  2365. * A generated column is a column that exists only in the Table, not as a
  2366. * property in the underlying Container. It shows up just as a regular
  2367. * column.
  2368. * </p>
  2369. * <p>
  2370. * A generated column will override a property with the same id, so that the
  2371. * generated column is shown instead of the column representing the
  2372. * property. Note that getContainerProperty() will still get the real
  2373. * property.
  2374. * </p>
  2375. * <p>
  2376. * Also note that getVisibleColumns() will return the generated columns,
  2377. * while getContainerPropertyIds() will not.
  2378. * </p>
  2379. *
  2380. * @param id
  2381. * the id of the column to be added
  2382. * @param generatedColumn
  2383. * the {@link ColumnGenerator} to use for this column
  2384. */
  2385. public void addGeneratedColumn(Object id, ColumnGenerator generatedColumn) {
  2386. if (generatedColumn == null) {
  2387. throw new IllegalArgumentException(
  2388. "Can not add null as a GeneratedColumn");
  2389. }
  2390. if (columnGenerators.containsKey(id)) {
  2391. throw new IllegalArgumentException(
  2392. "Can not add the same GeneratedColumn twice, id:" + id);
  2393. } else {
  2394. columnGenerators.put(id, generatedColumn);
  2395. /*
  2396. * add to visible column list unless already there (overriding
  2397. * column from DS)
  2398. */
  2399. if (!visibleColumns.contains(id)) {
  2400. visibleColumns.add(id);
  2401. }
  2402. resetPageBuffer();
  2403. refreshRenderedCells();
  2404. }
  2405. }
  2406. /**
  2407. * Removes a generated column previously added with addGeneratedColumn.
  2408. *
  2409. * @param columnId
  2410. * id of the generated column to remove
  2411. * @return true if the column could be removed (existed in the Table)
  2412. */
  2413. public boolean removeGeneratedColumn(Object columnId) {
  2414. if (columnGenerators.containsKey(columnId)) {
  2415. columnGenerators.remove(columnId);
  2416. // remove column from visibleColumns list unless it exists in
  2417. // container (generator previously overrode this column)
  2418. if (!items.getContainerPropertyIds().contains(columnId)) {
  2419. visibleColumns.remove(columnId);
  2420. }
  2421. resetPageBuffer();
  2422. refreshRenderedCells();
  2423. return true;
  2424. } else {
  2425. return false;
  2426. }
  2427. }
  2428. /**
  2429. * Returns the list of items on the current page
  2430. *
  2431. * @see com.vaadin.ui.Select#getVisibleItemIds()
  2432. */
  2433. @Override
  2434. public Collection getVisibleItemIds() {
  2435. final LinkedList<Object> visible = new LinkedList<Object>();
  2436. final Object[][] cells = getVisibleCells();
  2437. for (int i = 0; i < cells[CELL_ITEMID].length; i++) {
  2438. visible.add(cells[CELL_ITEMID][i]);
  2439. }
  2440. return visible;
  2441. }
  2442. /**
  2443. * Container datasource item set change. Table must flush its buffers on
  2444. * change.
  2445. *
  2446. * @see com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent)
  2447. */
  2448. @Override
  2449. public void containerItemSetChange(Container.ItemSetChangeEvent event) {
  2450. super.containerItemSetChange(event);
  2451. if (event instanceof IndexedContainer.ItemSetChangeEvent) {
  2452. IndexedContainer.ItemSetChangeEvent evt = (IndexedContainer.ItemSetChangeEvent) event;
  2453. // if the event is not a global one and the added item is outside
  2454. // the visible/buffered area, no need to do anything
  2455. if (evt.getAddedItemIndex() != -1
  2456. && (firstToBeRenderedInClient >= 0)
  2457. && (lastToBeRenderedInClient >= 0)
  2458. && (firstToBeRenderedInClient > evt.getAddedItemIndex() || lastToBeRenderedInClient < evt
  2459. .getAddedItemIndex())) {
  2460. return;
  2461. }
  2462. }
  2463. // ensure that page still has first item in page
  2464. setCurrentPageFirstItemIndex(getCurrentPageFirstItemIndex());
  2465. resetPageBuffer();
  2466. refreshRenderedCells();
  2467. }
  2468. /**
  2469. * Container datasource property set change. Table must flush its buffers on
  2470. * change.
  2471. *
  2472. * @see com.vaadin.data.Container.PropertySetChangeListener#containerPropertySetChange(com.vaadin.data.Container.PropertySetChangeEvent)
  2473. */
  2474. @Override
  2475. public void containerPropertySetChange(
  2476. Container.PropertySetChangeEvent event) {
  2477. super.containerPropertySetChange(event);
  2478. resetPageBuffer();
  2479. refreshRenderedCells();
  2480. }
  2481. /**
  2482. * Adding new items is not supported.
  2483. *
  2484. * @throws UnsupportedOperationException
  2485. * if set to true.
  2486. * @see com.vaadin.ui.Select#setNewItemsAllowed(boolean)
  2487. */
  2488. @Override
  2489. public void setNewItemsAllowed(boolean allowNewOptions)
  2490. throws UnsupportedOperationException {
  2491. if (allowNewOptions) {
  2492. throw new UnsupportedOperationException();
  2493. }
  2494. }
  2495. /**
  2496. * Focusing to this component is not supported.
  2497. *
  2498. * @throws UnsupportedOperationException
  2499. * if invoked.
  2500. * @see com.vaadin.ui.AbstractField#focus()
  2501. */
  2502. @Override
  2503. public void focus() throws UnsupportedOperationException {
  2504. throw new UnsupportedOperationException();
  2505. }
  2506. /**
  2507. * Gets the ID of the Item following the Item that corresponds to itemId.
  2508. *
  2509. * @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object)
  2510. */
  2511. public Object nextItemId(Object itemId) {
  2512. return ((Container.Ordered) items).nextItemId(itemId);
  2513. }
  2514. /**
  2515. * Gets the ID of the Item preceding the Item that corresponds to the
  2516. * itemId.
  2517. *
  2518. * @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object)
  2519. */
  2520. public Object prevItemId(Object itemId) {
  2521. return ((Container.Ordered) items).prevItemId(itemId);
  2522. }
  2523. /**
  2524. * Gets the ID of the first Item in the Container.
  2525. *
  2526. * @see com.vaadin.data.Container.Ordered#firstItemId()
  2527. */
  2528. public Object firstItemId() {
  2529. return ((Container.Ordered) items).firstItemId();
  2530. }
  2531. /**
  2532. * Gets the ID of the last Item in the Container.
  2533. *
  2534. * @see com.vaadin.data.Container.Ordered#lastItemId()
  2535. */
  2536. public Object lastItemId() {
  2537. return ((Container.Ordered) items).lastItemId();
  2538. }
  2539. /**
  2540. * Tests if the Item corresponding to the given Item ID is the first Item in
  2541. * the Container.
  2542. *
  2543. * @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object)
  2544. */
  2545. public boolean isFirstId(Object itemId) {
  2546. return ((Container.Ordered) items).isFirstId(itemId);
  2547. }
  2548. /**
  2549. * Tests if the Item corresponding to the given Item ID is the last Item in
  2550. * the Container.
  2551. *
  2552. * @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object)
  2553. */
  2554. public boolean isLastId(Object itemId) {
  2555. return ((Container.Ordered) items).isLastId(itemId);
  2556. }
  2557. /**
  2558. * Adds new item after the given item.
  2559. *
  2560. * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object)
  2561. */
  2562. public Object addItemAfter(Object previousItemId)
  2563. throws UnsupportedOperationException {
  2564. Object itemId = ((Container.Ordered) items)
  2565. .addItemAfter(previousItemId);
  2566. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  2567. resetPageBuffer();
  2568. refreshRenderedCells();
  2569. }
  2570. return itemId;
  2571. }
  2572. /**
  2573. * Adds new item after the given item.
  2574. *
  2575. * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object,
  2576. * java.lang.Object)
  2577. */
  2578. public Item addItemAfter(Object previousItemId, Object newItemId)
  2579. throws UnsupportedOperationException {
  2580. Item item = ((Container.Ordered) items).addItemAfter(previousItemId,
  2581. newItemId);
  2582. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  2583. resetPageBuffer();
  2584. refreshRenderedCells();
  2585. }
  2586. return item;
  2587. }
  2588. /**
  2589. * Sets the TableFieldFactory that is used to create editor for table cells.
  2590. *
  2591. * The TableFieldFactory is only used if the Table is editable. By default
  2592. * the DefaultFieldFactory is used.
  2593. *
  2594. * @param fieldFactory
  2595. * the field factory to set.
  2596. * @see #isEditable
  2597. * @see DefaultFieldFactory
  2598. */
  2599. public void setTableFieldFactory(TableFieldFactory fieldFactory) {
  2600. this.fieldFactory = fieldFactory;
  2601. }
  2602. /**
  2603. * Gets the TableFieldFactory that is used to create editor for table cells.
  2604. *
  2605. * The FieldFactory is only used if the Table is editable.
  2606. *
  2607. * @return TableFieldFactory used to create the Field instances.
  2608. * @see #isEditable
  2609. */
  2610. public TableFieldFactory getTableFieldFactory() {
  2611. return fieldFactory;
  2612. }
  2613. /**
  2614. * Gets the FieldFactory that is used to create editor for table cells.
  2615. *
  2616. * The FieldFactory is only used if the Table is editable.
  2617. *
  2618. * @return FieldFactory used to create the Field instances.
  2619. * @see #isEditable
  2620. * @deprecated use {@link #getTableFieldFactory()} instead
  2621. */
  2622. @Deprecated
  2623. public FieldFactory getFieldFactory() {
  2624. if (fieldFactory instanceof FieldFactory) {
  2625. return (FieldFactory) fieldFactory;
  2626. }
  2627. return null;
  2628. }
  2629. /**
  2630. * Sets the FieldFactory that is used to create editor for table cells.
  2631. *
  2632. * The FieldFactory is only used if the Table is editable. By default the
  2633. * BaseFieldFactory is used.
  2634. *
  2635. * @param fieldFactory
  2636. * the field factory to set.
  2637. * @see #isEditable
  2638. * @see BaseFieldFactory
  2639. * @deprecated use {@link #setTableFieldFactory(TableFieldFactory)} instead
  2640. */
  2641. @Deprecated
  2642. public void setFieldFactory(FieldFactory fieldFactory) {
  2643. this.fieldFactory = fieldFactory;
  2644. // Assure visual refresh
  2645. resetPageBuffer();
  2646. refreshRenderedCells();
  2647. }
  2648. /**
  2649. * Is table editable.
  2650. *
  2651. * If table is editable a editor of type Field is created for each table
  2652. * cell. The assigned FieldFactory is used to create the instances.
  2653. *
  2654. * To provide custom editors for table cells create a class implementins the
  2655. * FieldFactory interface, and assign it to table, and set the editable
  2656. * property to true.
  2657. *
  2658. * @return true if table is editable, false oterwise.
  2659. * @see Field
  2660. * @see FieldFactory
  2661. *
  2662. */
  2663. public boolean isEditable() {
  2664. return editable;
  2665. }
  2666. /**
  2667. * Sets the editable property.
  2668. *
  2669. * If table is editable a editor of type Field is created for each table
  2670. * cell. The assigned FieldFactory is used to create the instances.
  2671. *
  2672. * To provide custom editors for table cells create a class implementins the
  2673. * FieldFactory interface, and assign it to table, and set the editable
  2674. * property to true.
  2675. *
  2676. * @param editable
  2677. * true if table should be editable by user.
  2678. * @see Field
  2679. * @see FieldFactory
  2680. *
  2681. */
  2682. public void setEditable(boolean editable) {
  2683. this.editable = editable;
  2684. // Assure visual refresh
  2685. resetPageBuffer();
  2686. refreshRenderedCells();
  2687. }
  2688. /**
  2689. * Sorts the table.
  2690. *
  2691. * @throws UnsupportedOperationException
  2692. * if the container data source does not implement
  2693. * Container.Sortable
  2694. * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[],
  2695. * boolean[])
  2696. *
  2697. */
  2698. public void sort(Object[] propertyId, boolean[] ascending)
  2699. throws UnsupportedOperationException {
  2700. final Container c = getContainerDataSource();
  2701. if (c instanceof Container.Sortable) {
  2702. final int pageIndex = getCurrentPageFirstItemIndex();
  2703. ((Container.Sortable) c).sort(propertyId, ascending);
  2704. setCurrentPageFirstItemIndex(pageIndex);
  2705. resetPageBuffer();
  2706. refreshRenderedCells();
  2707. } else if (c != null) {
  2708. throw new UnsupportedOperationException(
  2709. "Underlying Data does not allow sorting");
  2710. }
  2711. }
  2712. /**
  2713. * Sorts the table by currently selected sorting column.
  2714. *
  2715. * @throws UnsupportedOperationException
  2716. * if the container data source does not implement
  2717. * Container.Sortable
  2718. */
  2719. public void sort() {
  2720. if (getSortContainerPropertyId() == null) {
  2721. return;
  2722. }
  2723. sort(new Object[] { sortContainerPropertyId },
  2724. new boolean[] { sortAscending });
  2725. }
  2726. /**
  2727. * Gets the container property IDs, which can be used to sort the item.
  2728. *
  2729. * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds()
  2730. */
  2731. public Collection getSortableContainerPropertyIds() {
  2732. final Container c = getContainerDataSource();
  2733. if (c instanceof Container.Sortable && !isSortDisabled()) {
  2734. return ((Container.Sortable) c).getSortableContainerPropertyIds();
  2735. } else {
  2736. return new LinkedList();
  2737. }
  2738. }
  2739. /**
  2740. * Gets the currently sorted column property ID.
  2741. *
  2742. * @return the Container property id of the currently sorted column.
  2743. */
  2744. public Object getSortContainerPropertyId() {
  2745. return sortContainerPropertyId;
  2746. }
  2747. /**
  2748. * Sets the currently sorted column property id.
  2749. *
  2750. * @param propertyId
  2751. * the Container property id of the currently sorted column.
  2752. */
  2753. public void setSortContainerPropertyId(Object propertyId) {
  2754. setSortContainerPropertyId(propertyId, true);
  2755. }
  2756. /**
  2757. * Internal method to set currently sorted column property id. With doSort
  2758. * flag actual sorting may be bypassed.
  2759. *
  2760. * @param propertyId
  2761. * @param doSort
  2762. */
  2763. private void setSortContainerPropertyId(Object propertyId, boolean doSort) {
  2764. if ((sortContainerPropertyId != null && !sortContainerPropertyId
  2765. .equals(propertyId))
  2766. || (sortContainerPropertyId == null && propertyId != null)) {
  2767. sortContainerPropertyId = propertyId;
  2768. if (doSort) {
  2769. sort();
  2770. // Assures the visual refresh
  2771. refreshRenderedCells();
  2772. }
  2773. }
  2774. }
  2775. /**
  2776. * Is the table currently sorted in ascending order.
  2777. *
  2778. * @return <code>true</code> if ascending, <code>false</code> if descending.
  2779. */
  2780. public boolean isSortAscending() {
  2781. return sortAscending;
  2782. }
  2783. /**
  2784. * Sets the table in ascending order.
  2785. *
  2786. * @param ascending
  2787. * <code>true</code> if ascending, <code>false</code> if
  2788. * descending.
  2789. */
  2790. public void setSortAscending(boolean ascending) {
  2791. setSortAscending(ascending, true);
  2792. }
  2793. /**
  2794. * Internal method to set sort ascending. With doSort flag actual sort can
  2795. * be bypassed.
  2796. *
  2797. * @param ascending
  2798. * @param doSort
  2799. */
  2800. private void setSortAscending(boolean ascending, boolean doSort) {
  2801. if (sortAscending != ascending) {
  2802. sortAscending = ascending;
  2803. if (doSort) {
  2804. sort();
  2805. }
  2806. }
  2807. // Assures the visual refresh
  2808. refreshRenderedCells();
  2809. }
  2810. /**
  2811. * Is sorting disabled altogether.
  2812. *
  2813. * True iff no sortable columns are given even in the case where data source
  2814. * would support this.
  2815. *
  2816. * @return True iff sorting is disabled.
  2817. */
  2818. public boolean isSortDisabled() {
  2819. return sortDisabled;
  2820. }
  2821. /**
  2822. * Disables the sorting altogether.
  2823. *
  2824. * To disable sorting altogether, set to true. In this case no sortable
  2825. * columns are given even in the case where datasource would support this.
  2826. *
  2827. * @param sortDisabled
  2828. * True iff sorting is disabled.
  2829. */
  2830. public void setSortDisabled(boolean sortDisabled) {
  2831. if (this.sortDisabled != sortDisabled) {
  2832. this.sortDisabled = sortDisabled;
  2833. refreshRenderedCells();
  2834. }
  2835. }
  2836. /**
  2837. * Table does not support lazy options loading mode. Setting this true will
  2838. * throw UnsupportedOperationException.
  2839. *
  2840. * @see com.vaadin.ui.Select#setLazyLoading(boolean)
  2841. */
  2842. public void setLazyLoading(boolean useLazyLoading) {
  2843. if (useLazyLoading) {
  2844. throw new UnsupportedOperationException(
  2845. "Lazy options loading is not supported by Table.");
  2846. }
  2847. }
  2848. /*
  2849. * Override abstract fields to string method to avoid non-informative null's
  2850. * in debugger
  2851. */
  2852. @Override
  2853. public String toString() {
  2854. return "Table:" + getContainerPropertyIds() + ", rows "
  2855. + getContainerDataSource().size() + " ,value:"
  2856. + super.toString();
  2857. }
  2858. /**
  2859. * Used to create "generated columns"; columns that exist only in the Table,
  2860. * not in the underlying Container. Implement this interface and pass it to
  2861. * Table.addGeneratedColumn along with an id for the column to be generated.
  2862. *
  2863. */
  2864. public interface ColumnGenerator extends Serializable {
  2865. /**
  2866. * Called by Table when a cell in a generated column needs to be
  2867. * generated.
  2868. *
  2869. * @param source
  2870. * the source Table
  2871. * @param itemId
  2872. * the itemId (aka rowId) for the of the cell to be generated
  2873. * @param columnId
  2874. * the id for the generated column (as specified in
  2875. * addGeneratedColumn)
  2876. * @return
  2877. */
  2878. public abstract Component generateCell(Table source, Object itemId,
  2879. Object columnId);
  2880. }
  2881. /**
  2882. * Set cell style generator for Table.
  2883. *
  2884. * @param cellStyleGenerator
  2885. * New cell style generator or null to remove generator.
  2886. */
  2887. public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) {
  2888. this.cellStyleGenerator = cellStyleGenerator;
  2889. requestRepaint();
  2890. }
  2891. /**
  2892. * Get the current cell style generator.
  2893. *
  2894. */
  2895. public CellStyleGenerator getCellStyleGenerator() {
  2896. return cellStyleGenerator;
  2897. }
  2898. /**
  2899. * Allow to define specific style on cells (and rows) contents. Implements
  2900. * this interface and pass it to Table.setCellStyleGenerator. Row styles are
  2901. * generated when porpertyId is null. The CSS class name that will be added
  2902. * to the cell content is <tt>v-table-cell-content-[style name]</tt>, and
  2903. * the row style will be <tt>v-table-row-[style name]</tt>.
  2904. */
  2905. public interface CellStyleGenerator extends Serializable {
  2906. /**
  2907. * Called by Table when a cell (and row) is painted.
  2908. *
  2909. * @param itemId
  2910. * The itemId of the painted cell
  2911. * @param propertyId
  2912. * The propertyId of the cell, null when getting row style
  2913. * @return The style name to add to this cell or row. (the CSS class
  2914. * name will be v-table-cell-content-[style name], or
  2915. * v-table-row-[style name] for rows)
  2916. */
  2917. public abstract String getStyle(Object itemId, Object propertyId);
  2918. }
  2919. public void addListener(ItemClickListener listener) {
  2920. addListener(ItemClickEvent.class, listener,
  2921. ItemClickEvent.ITEM_CLICK_METHOD);
  2922. clickListenerCount++;
  2923. // repaint needed only if click listening became necessary
  2924. if (clickListenerCount == 1) {
  2925. requestRepaint();
  2926. }
  2927. }
  2928. public void removeListener(ItemClickListener listener) {
  2929. removeListener(ItemClickEvent.class, listener,
  2930. ItemClickEvent.ITEM_CLICK_METHOD);
  2931. clickListenerCount--;
  2932. // repaint needed only if click listening is not needed in client
  2933. // anymore
  2934. if (clickListenerCount == 0) {
  2935. requestRepaint();
  2936. }
  2937. }
  2938. // Identical to AbstractCompoenentContainer.setEnabled();
  2939. @Override
  2940. public void setEnabled(boolean enabled) {
  2941. super.setEnabled(enabled);
  2942. if (getParent() != null && !getParent().isEnabled()) {
  2943. // some ancestor still disabled, don't update children
  2944. return;
  2945. } else {
  2946. requestRepaintAll();
  2947. }
  2948. }
  2949. // Virtually identical to AbstractCompoenentContainer.setEnabled();
  2950. public void requestRepaintAll() {
  2951. requestRepaint();
  2952. if (visibleComponents != null) {
  2953. for (Iterator<Component> childIterator = visibleComponents
  2954. .iterator(); childIterator.hasNext();) {
  2955. Component c = childIterator.next();
  2956. if (c instanceof Form) {
  2957. // Form has children in layout, but is not
  2958. // ComponentContainer
  2959. c.requestRepaint();
  2960. ((Form) c).getLayout().requestRepaintAll();
  2961. } else if (c instanceof Table) {
  2962. ((Table) c).requestRepaintAll();
  2963. } else if (c instanceof ComponentContainer) {
  2964. ((ComponentContainer) c).requestRepaintAll();
  2965. } else {
  2966. c.requestRepaint();
  2967. }
  2968. }
  2969. }
  2970. }
  2971. }