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

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334
  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 transient 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. // Actions
  1635. if (variables.containsKey("action")) {
  1636. final StringTokenizer st = new StringTokenizer((String) variables
  1637. .get("action"), ",");
  1638. if (st.countTokens() == 2) {
  1639. final Object itemId = itemIdMapper.get(st.nextToken());
  1640. final Action action = (Action) actionMapper.get(st.nextToken());
  1641. if (action != null && containsId(itemId)
  1642. && actionHandlers != null) {
  1643. for (final Iterator<Handler> i = actionHandlers.iterator(); i
  1644. .hasNext();) {
  1645. (i.next()).handleAction(action, this, itemId);
  1646. }
  1647. }
  1648. }
  1649. }
  1650. if (!sortDisabled) {
  1651. // Sorting
  1652. boolean doSort = false;
  1653. if (variables.containsKey("sortcolumn")) {
  1654. final String colId = (String) variables.get("sortcolumn");
  1655. if (colId != null && !"".equals(colId) && !"null".equals(colId)) {
  1656. final Object id = columnIdMap.get(colId);
  1657. setSortContainerPropertyId(id, false);
  1658. doSort = true;
  1659. }
  1660. }
  1661. if (variables.containsKey("sortascending")) {
  1662. final boolean state = ((Boolean) variables.get("sortascending"))
  1663. .booleanValue();
  1664. if (state != sortAscending) {
  1665. setSortAscending(state, false);
  1666. doSort = true;
  1667. }
  1668. }
  1669. if (doSort) {
  1670. this.sort();
  1671. resetPageBuffer();
  1672. }
  1673. }
  1674. // Dynamic column hide/show and order
  1675. // Update visible columns
  1676. if (isColumnCollapsingAllowed()) {
  1677. if (variables.containsKey("collapsedcolumns")) {
  1678. try {
  1679. final Object[] ids = (Object[]) variables
  1680. .get("collapsedcolumns");
  1681. for (final Iterator<Object> it = visibleColumns.iterator(); it
  1682. .hasNext();) {
  1683. setColumnCollapsed(it.next(), false);
  1684. }
  1685. for (int i = 0; i < ids.length; i++) {
  1686. setColumnCollapsed(columnIdMap.get(ids[i].toString()),
  1687. true);
  1688. }
  1689. } catch (final Exception e) {
  1690. // FIXME: Handle exception
  1691. e.printStackTrace();
  1692. }
  1693. clientNeedsContentRefresh = true;
  1694. }
  1695. }
  1696. if (isColumnReorderingAllowed()) {
  1697. if (variables.containsKey("columnorder")) {
  1698. try {
  1699. final Object[] ids = (Object[]) variables
  1700. .get("columnorder");
  1701. for (int i = 0; i < ids.length; i++) {
  1702. ids[i] = columnIdMap.get(ids[i].toString());
  1703. }
  1704. setColumnOrder(ids);
  1705. } catch (final Exception e) {
  1706. // FIXME: Handle exception
  1707. e.printStackTrace();
  1708. }
  1709. clientNeedsContentRefresh = true;
  1710. }
  1711. }
  1712. enableContentRefreshing(clientNeedsContentRefresh);
  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 (actionHandlers != null || isSelectable()) {
  1923. if (isSelected(itemId)) {
  1924. target.addAttribute("selected", true);
  1925. }
  1926. }
  1927. // Actions
  1928. if (actionHandlers != null) {
  1929. final ArrayList<String> keys = new ArrayList<String>();
  1930. for (final Iterator<Handler> ahi = actionHandlers.iterator(); ahi
  1931. .hasNext();) {
  1932. final Action[] aa = (ahi.next()).getActions(itemId, this);
  1933. if (aa != null) {
  1934. for (int ai = 0; ai < aa.length; ai++) {
  1935. final String key = actionMapper.key(aa[ai]);
  1936. actionSet.add(aa[ai]);
  1937. keys.add(key);
  1938. }
  1939. }
  1940. }
  1941. target.addAttribute("al", keys.toArray());
  1942. }
  1943. /*
  1944. * For each row, if a cellStyleGenerator is specified, get the
  1945. * specific style for the cell, using null as propertyId. If there
  1946. * is any, add it to the target.
  1947. */
  1948. if (cellStyleGenerator != null) {
  1949. String rowStyle = cellStyleGenerator.getStyle(itemId, null);
  1950. if (rowStyle != null && !rowStyle.equals("")) {
  1951. target.addAttribute("rowstyle", rowStyle);
  1952. }
  1953. }
  1954. // cells
  1955. int currentColumn = 0;
  1956. for (final Iterator<Object> it = visibleColumns.iterator(); it
  1957. .hasNext(); currentColumn++) {
  1958. final Object columnId = it.next();
  1959. if (columnId == null || isColumnCollapsed(columnId)) {
  1960. continue;
  1961. }
  1962. /*
  1963. * For each cell, if a cellStyleGenerator is specified, get the
  1964. * specific style for the cell. If there is any, add it to the
  1965. * target.
  1966. */
  1967. if (cellStyleGenerator != null) {
  1968. String cellStyle = cellStyleGenerator.getStyle(itemId,
  1969. columnId);
  1970. if (cellStyle != null && !cellStyle.equals("")) {
  1971. target.addAttribute("style-"
  1972. + columnIdMap.key(columnId), cellStyle);
  1973. }
  1974. }
  1975. if ((iscomponent[currentColumn] || iseditable)
  1976. && Component.class.isInstance(cells[CELL_FIRSTCOL
  1977. + currentColumn][i])) {
  1978. final Component c = (Component) cells[CELL_FIRSTCOL
  1979. + currentColumn][i];
  1980. if (c == null) {
  1981. target.addText("");
  1982. } else {
  1983. c.paint(target);
  1984. }
  1985. } else {
  1986. target
  1987. .addText((String) cells[CELL_FIRSTCOL
  1988. + currentColumn][i]);
  1989. }
  1990. }
  1991. target.endTag("tr");
  1992. }
  1993. target.endTag("rows");
  1994. // The select variable is only enabled if selectable
  1995. if (selectable) {
  1996. target.addVariable(this, "selected", selectedKeys
  1997. .toArray(new String[selectedKeys.size()]));
  1998. }
  1999. // The cursors are only shown on pageable table
  2000. if (first != 0 || getPageLength() > 0) {
  2001. target.addVariable(this, "firstvisible", first);
  2002. }
  2003. // Sorting
  2004. if (getContainerDataSource() instanceof Container.Sortable) {
  2005. target.addVariable(this, "sortcolumn", columnIdMap
  2006. .key(sortContainerPropertyId));
  2007. target.addVariable(this, "sortascending", sortAscending);
  2008. }
  2009. // Resets and paints "to be painted next" variables. Also reset
  2010. // pageBuffer
  2011. reqFirstRowToPaint = -1;
  2012. reqRowsToPaint = -1;
  2013. containerChangeToBeRendered = false;
  2014. target.addVariable(this, "reqrows", reqRowsToPaint);
  2015. target.addVariable(this, "reqfirstrow", reqFirstRowToPaint);
  2016. // Actions
  2017. if (!actionSet.isEmpty()) {
  2018. target.addVariable(this, "action", "");
  2019. target.startTag("actions");
  2020. for (final Iterator<Action> it = actionSet.iterator(); it.hasNext();) {
  2021. final Action a = it.next();
  2022. target.startTag("action");
  2023. if (a.getCaption() != null) {
  2024. target.addAttribute("caption", a.getCaption());
  2025. }
  2026. if (a.getIcon() != null) {
  2027. target.addAttribute("icon", a.getIcon());
  2028. }
  2029. target.addAttribute("key", actionMapper.key(a));
  2030. target.endTag("action");
  2031. }
  2032. target.endTag("actions");
  2033. }
  2034. if (columnReorderingAllowed) {
  2035. final String[] colorder = new String[visibleColumns.size()];
  2036. int i = 0;
  2037. for (final Iterator<Object> it = visibleColumns.iterator(); it
  2038. .hasNext()
  2039. && i < colorder.length;) {
  2040. colorder[i++] = columnIdMap.key(it.next());
  2041. }
  2042. target.addVariable(this, "columnorder", colorder);
  2043. }
  2044. // Available columns
  2045. if (columnCollapsingAllowed) {
  2046. final HashSet<Object> ccs = new HashSet<Object>();
  2047. for (final Iterator<Object> i = visibleColumns.iterator(); i
  2048. .hasNext();) {
  2049. final Object o = i.next();
  2050. if (isColumnCollapsed(o)) {
  2051. ccs.add(o);
  2052. }
  2053. }
  2054. final String[] collapsedkeys = new String[ccs.size()];
  2055. int nextColumn = 0;
  2056. for (final Iterator<Object> it = visibleColumns.iterator(); it
  2057. .hasNext()
  2058. && nextColumn < collapsedkeys.length;) {
  2059. final Object columnId = it.next();
  2060. if (isColumnCollapsed(columnId)) {
  2061. collapsedkeys[nextColumn++] = columnIdMap.key(columnId);
  2062. }
  2063. }
  2064. target.addVariable(this, "collapsedcolumns", collapsedkeys);
  2065. }
  2066. target.startTag("visiblecolumns");
  2067. int i = 0;
  2068. for (final Iterator<Object> it = visibleColumns.iterator(); it
  2069. .hasNext(); i++) {
  2070. final Object columnId = it.next();
  2071. if (columnId != null) {
  2072. target.startTag("column");
  2073. target.addAttribute("cid", columnIdMap.key(columnId));
  2074. final String head = getColumnHeader(columnId);
  2075. target.addAttribute("caption", (head != null ? head : ""));
  2076. if (isColumnCollapsed(columnId)) {
  2077. target.addAttribute("collapsed", true);
  2078. }
  2079. if (colheads) {
  2080. if (getColumnIcon(columnId) != null) {
  2081. target.addAttribute("icon", getColumnIcon(columnId));
  2082. }
  2083. if (sortables.contains(columnId)) {
  2084. target.addAttribute("sortable", true);
  2085. }
  2086. }
  2087. if (!ALIGN_LEFT.equals(getColumnAlignment(columnId))) {
  2088. target.addAttribute("align", getColumnAlignment(columnId));
  2089. }
  2090. if (columnWidths.containsKey(columnId)) {
  2091. if (getColumnWidth(columnId) > -1) {
  2092. target.addAttribute("width", String
  2093. .valueOf(getColumnWidth(columnId)));
  2094. } else {
  2095. target.addAttribute("er",
  2096. getColumnExpandRatio(columnId));
  2097. }
  2098. }
  2099. target.endTag("column");
  2100. }
  2101. }
  2102. target.endTag("visiblecolumns");
  2103. }
  2104. /*
  2105. * (non-Javadoc)
  2106. *
  2107. * @see com.vaadin.ui.AbstractSelect#getTag()
  2108. */
  2109. @Override
  2110. public String getTag() {
  2111. return "table";
  2112. }
  2113. /**
  2114. * Gets the cached visible table contents.
  2115. *
  2116. * @return the cached visible table contents.
  2117. */
  2118. private Object[][] getVisibleCells() {
  2119. if (pageBuffer == null) {
  2120. refreshRenderedCells();
  2121. }
  2122. return pageBuffer;
  2123. }
  2124. /**
  2125. * Gets the value of property.
  2126. *
  2127. * By default if the table is editable the fieldFactory is used to create
  2128. * editors for table cells. Otherwise formatPropertyValue is used to format
  2129. * the value representation.
  2130. *
  2131. * @param rowId
  2132. * the Id of the row (same as item Id).
  2133. * @param colId
  2134. * the Id of the column.
  2135. * @param property
  2136. * the Property to be presented.
  2137. * @return Object Either formatted value or Component for field.
  2138. * @see #setTableFieldFactory(TableFieldFactory)
  2139. */
  2140. protected Object getPropertyValue(Object rowId, Object colId,
  2141. Property property) {
  2142. if (isEditable() && fieldFactory != null) {
  2143. final Field f = fieldFactory.createField(getContainerDataSource(),
  2144. rowId, colId, this);
  2145. if (f != null) {
  2146. f.setPropertyDataSource(property);
  2147. return f;
  2148. }
  2149. }
  2150. return formatPropertyValue(rowId, colId, property);
  2151. }
  2152. /**
  2153. * Formats table cell property values. By default the property.toString()
  2154. * and return a empty string for null properties.
  2155. *
  2156. * @param rowId
  2157. * the Id of the row (same as item Id).
  2158. * @param colId
  2159. * the Id of the column.
  2160. * @param property
  2161. * the Property to be formatted.
  2162. * @return the String representation of property and its value.
  2163. * @since 3.1
  2164. */
  2165. protected String formatPropertyValue(Object rowId, Object colId,
  2166. Property property) {
  2167. if (property == null) {
  2168. return "";
  2169. }
  2170. return property.toString();
  2171. }
  2172. /* Action container */
  2173. /**
  2174. * Registers a new action handler for this container
  2175. *
  2176. * @see com.vaadin.event.Action.Container#addActionHandler(Action.Handler)
  2177. */
  2178. public void addActionHandler(Action.Handler actionHandler) {
  2179. if (actionHandler != null) {
  2180. if (actionHandlers == null) {
  2181. actionHandlers = new LinkedList<Handler>();
  2182. actionMapper = new KeyMapper();
  2183. }
  2184. if (!actionHandlers.contains(actionHandler)) {
  2185. actionHandlers.add(actionHandler);
  2186. requestRepaint();
  2187. }
  2188. }
  2189. }
  2190. /**
  2191. * Removes a previously registered action handler for the contents of this
  2192. * container.
  2193. *
  2194. * @see com.vaadin.event.Action.Container#removeActionHandler(Action.Handler)
  2195. */
  2196. public void removeActionHandler(Action.Handler actionHandler) {
  2197. if (actionHandlers != null && actionHandlers.contains(actionHandler)) {
  2198. actionHandlers.remove(actionHandler);
  2199. if (actionHandlers.isEmpty()) {
  2200. actionHandlers = null;
  2201. actionMapper = null;
  2202. }
  2203. requestRepaint();
  2204. }
  2205. }
  2206. /* Property value change listening support */
  2207. /**
  2208. * Notifies this listener that the Property's value has changed.
  2209. *
  2210. * Also listens changes in rendered items to refresh content area.
  2211. *
  2212. * @see com.vaadin.data.Property.ValueChangeListener#valueChange(Property.ValueChangeEvent)
  2213. */
  2214. @Override
  2215. public void valueChange(Property.ValueChangeEvent event) {
  2216. if (event.getProperty() == this) {
  2217. super.valueChange(event);
  2218. } else {
  2219. resetPageBuffer();
  2220. refreshRenderedCells();
  2221. containerChangeToBeRendered = true;
  2222. }
  2223. requestRepaint();
  2224. }
  2225. private void resetPageBuffer() {
  2226. firstToBeRenderedInClient = -1;
  2227. lastToBeRenderedInClient = -1;
  2228. reqFirstRowToPaint = -1;
  2229. reqRowsToPaint = -1;
  2230. pageBuffer = null;
  2231. }
  2232. /**
  2233. * Notifies the component that it is connected to an application.
  2234. *
  2235. * @see com.vaadin.ui.Component#attach()
  2236. */
  2237. @Override
  2238. public void attach() {
  2239. super.attach();
  2240. refreshRenderedCells();
  2241. if (visibleComponents != null) {
  2242. for (final Iterator<Component> i = visibleComponents.iterator(); i
  2243. .hasNext();) {
  2244. i.next().attach();
  2245. }
  2246. }
  2247. }
  2248. /**
  2249. * Notifies the component that it is detached from the application
  2250. *
  2251. * @see com.vaadin.ui.Component#detach()
  2252. */
  2253. @Override
  2254. public void detach() {
  2255. super.detach();
  2256. if (visibleComponents != null) {
  2257. for (final Iterator<Component> i = visibleComponents.iterator(); i
  2258. .hasNext();) {
  2259. i.next().detach();
  2260. }
  2261. }
  2262. }
  2263. /**
  2264. * Removes all Items from the Container.
  2265. *
  2266. * @see com.vaadin.data.Container#removeAllItems()
  2267. */
  2268. @Override
  2269. public boolean removeAllItems() {
  2270. currentPageFirstItemId = null;
  2271. currentPageFirstItemIndex = 0;
  2272. return super.removeAllItems();
  2273. }
  2274. /**
  2275. * Removes the Item identified by <code>ItemId</code> from the Container.
  2276. *
  2277. * @see com.vaadin.data.Container#removeItem(Object)
  2278. */
  2279. @Override
  2280. public boolean removeItem(Object itemId) {
  2281. final Object nextItemId = ((Container.Ordered) items)
  2282. .nextItemId(itemId);
  2283. final boolean ret = super.removeItem(itemId);
  2284. if (ret && (itemId != null) && (itemId.equals(currentPageFirstItemId))) {
  2285. currentPageFirstItemId = nextItemId;
  2286. }
  2287. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  2288. resetPageBuffer();
  2289. refreshRenderedCells();
  2290. }
  2291. return ret;
  2292. }
  2293. /**
  2294. * Removes a Property specified by the given Property ID from the Container.
  2295. *
  2296. * @see com.vaadin.data.Container#removeContainerProperty(Object)
  2297. */
  2298. @Override
  2299. public boolean removeContainerProperty(Object propertyId)
  2300. throws UnsupportedOperationException {
  2301. // If a visible property is removed, remove the corresponding column
  2302. visibleColumns.remove(propertyId);
  2303. columnAlignments.remove(propertyId);
  2304. columnIcons.remove(propertyId);
  2305. columnHeaders.remove(propertyId);
  2306. return super.removeContainerProperty(propertyId);
  2307. }
  2308. /**
  2309. * Adds a new property to the table and show it as a visible column.
  2310. *
  2311. * @param propertyId
  2312. * the Id of the proprty.
  2313. * @param type
  2314. * the class of the property.
  2315. * @param defaultValue
  2316. * the default value given for all existing items.
  2317. * @see com.vaadin.data.Container#addContainerProperty(Object, Class,
  2318. * Object)
  2319. */
  2320. @Override
  2321. public boolean addContainerProperty(Object propertyId, Class type,
  2322. Object defaultValue) throws UnsupportedOperationException {
  2323. boolean visibleColAdded = false;
  2324. if (!visibleColumns.contains(propertyId)) {
  2325. visibleColumns.add(propertyId);
  2326. visibleColAdded = true;
  2327. }
  2328. if (!super.addContainerProperty(propertyId, type, defaultValue)) {
  2329. if (visibleColAdded) {
  2330. visibleColumns.remove(propertyId);
  2331. }
  2332. return false;
  2333. }
  2334. if (!(items instanceof Container.PropertySetChangeNotifier)) {
  2335. resetPageBuffer();
  2336. refreshRenderedCells();
  2337. }
  2338. return true;
  2339. }
  2340. /**
  2341. * Adds a new property to the table and show it as a visible column.
  2342. *
  2343. * @param propertyId
  2344. * the Id of the proprty
  2345. * @param type
  2346. * the class of the property
  2347. * @param defaultValue
  2348. * the default value given for all existing items
  2349. * @param columnHeader
  2350. * the Explicit header of the column. If explicit header is not
  2351. * needed, this should be set null.
  2352. * @param columnIcon
  2353. * the Icon of the column. If icon is not needed, this should be
  2354. * set null.
  2355. * @param columnAlignment
  2356. * the Alignment of the column. Null implies align left.
  2357. * @throws UnsupportedOperationException
  2358. * if the operation is not supported.
  2359. * @see com.vaadin.data.Container#addContainerProperty(Object, Class,
  2360. * Object)
  2361. */
  2362. public boolean addContainerProperty(Object propertyId, Class type,
  2363. Object defaultValue, String columnHeader, Resource columnIcon,
  2364. String columnAlignment) throws UnsupportedOperationException {
  2365. if (!this.addContainerProperty(propertyId, type, defaultValue)) {
  2366. return false;
  2367. }
  2368. setColumnAlignment(propertyId, columnAlignment);
  2369. setColumnHeader(propertyId, columnHeader);
  2370. setColumnIcon(propertyId, columnIcon);
  2371. return true;
  2372. }
  2373. /**
  2374. * Adds a generated column to the Table.
  2375. * <p>
  2376. * A generated column is a column that exists only in the Table, not as a
  2377. * property in the underlying Container. It shows up just as a regular
  2378. * column.
  2379. * </p>
  2380. * <p>
  2381. * A generated column will override a property with the same id, so that the
  2382. * generated column is shown instead of the column representing the
  2383. * property. Note that getContainerProperty() will still get the real
  2384. * property.
  2385. * </p>
  2386. * <p>
  2387. * Also note that getVisibleColumns() will return the generated columns,
  2388. * while getContainerPropertyIds() will not.
  2389. * </p>
  2390. *
  2391. * @param id
  2392. * the id of the column to be added
  2393. * @param generatedColumn
  2394. * the {@link ColumnGenerator} to use for this column
  2395. */
  2396. public void addGeneratedColumn(Object id, ColumnGenerator generatedColumn) {
  2397. if (generatedColumn == null) {
  2398. throw new IllegalArgumentException(
  2399. "Can not add null as a GeneratedColumn");
  2400. }
  2401. if (columnGenerators.containsKey(id)) {
  2402. throw new IllegalArgumentException(
  2403. "Can not add the same GeneratedColumn twice, id:" + id);
  2404. } else {
  2405. columnGenerators.put(id, generatedColumn);
  2406. /*
  2407. * add to visible column list unless already there (overriding
  2408. * column from DS)
  2409. */
  2410. if (!visibleColumns.contains(id)) {
  2411. visibleColumns.add(id);
  2412. }
  2413. resetPageBuffer();
  2414. refreshRenderedCells();
  2415. }
  2416. }
  2417. /**
  2418. * Removes a generated column previously added with addGeneratedColumn.
  2419. *
  2420. * @param columnId
  2421. * id of the generated column to remove
  2422. * @return true if the column could be removed (existed in the Table)
  2423. */
  2424. public boolean removeGeneratedColumn(Object columnId) {
  2425. if (columnGenerators.containsKey(columnId)) {
  2426. columnGenerators.remove(columnId);
  2427. // remove column from visibleColumns list unless it exists in
  2428. // container (generator previously overrode this column)
  2429. if (!items.getContainerPropertyIds().contains(columnId)) {
  2430. visibleColumns.remove(columnId);
  2431. }
  2432. resetPageBuffer();
  2433. refreshRenderedCells();
  2434. return true;
  2435. } else {
  2436. return false;
  2437. }
  2438. }
  2439. /**
  2440. * Returns the list of items on the current page
  2441. *
  2442. * @see com.vaadin.ui.Select#getVisibleItemIds()
  2443. */
  2444. @Override
  2445. public Collection getVisibleItemIds() {
  2446. final LinkedList<Object> visible = new LinkedList<Object>();
  2447. final Object[][] cells = getVisibleCells();
  2448. for (int i = 0; i < cells[CELL_ITEMID].length; i++) {
  2449. visible.add(cells[CELL_ITEMID][i]);
  2450. }
  2451. return visible;
  2452. }
  2453. /**
  2454. * Container datasource item set change. Table must flush its buffers on
  2455. * change.
  2456. *
  2457. * @see com.vaadin.data.Container.ItemSetChangeListener#containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent)
  2458. */
  2459. @Override
  2460. public void containerItemSetChange(Container.ItemSetChangeEvent event) {
  2461. super.containerItemSetChange(event);
  2462. if (event instanceof IndexedContainer.ItemSetChangeEvent) {
  2463. IndexedContainer.ItemSetChangeEvent evt = (IndexedContainer.ItemSetChangeEvent) event;
  2464. // if the event is not a global one and the added item is outside
  2465. // the visible/buffered area, no need to do anything
  2466. if (evt.getAddedItemIndex() != -1
  2467. && (firstToBeRenderedInClient >= 0)
  2468. && (lastToBeRenderedInClient >= 0)
  2469. && (firstToBeRenderedInClient > evt.getAddedItemIndex() || lastToBeRenderedInClient < evt
  2470. .getAddedItemIndex())) {
  2471. return;
  2472. }
  2473. }
  2474. // ensure that page still has first item in page
  2475. setCurrentPageFirstItemIndex(getCurrentPageFirstItemIndex());
  2476. resetPageBuffer();
  2477. refreshRenderedCells();
  2478. }
  2479. /**
  2480. * Container datasource property set change. Table must flush its buffers on
  2481. * change.
  2482. *
  2483. * @see com.vaadin.data.Container.PropertySetChangeListener#containerPropertySetChange(com.vaadin.data.Container.PropertySetChangeEvent)
  2484. */
  2485. @Override
  2486. public void containerPropertySetChange(
  2487. Container.PropertySetChangeEvent event) {
  2488. super.containerPropertySetChange(event);
  2489. resetPageBuffer();
  2490. refreshRenderedCells();
  2491. }
  2492. /**
  2493. * Adding new items is not supported.
  2494. *
  2495. * @throws UnsupportedOperationException
  2496. * if set to true.
  2497. * @see com.vaadin.ui.Select#setNewItemsAllowed(boolean)
  2498. */
  2499. @Override
  2500. public void setNewItemsAllowed(boolean allowNewOptions)
  2501. throws UnsupportedOperationException {
  2502. if (allowNewOptions) {
  2503. throw new UnsupportedOperationException();
  2504. }
  2505. }
  2506. /**
  2507. * Focusing to this component is not supported.
  2508. *
  2509. * @throws UnsupportedOperationException
  2510. * if invoked.
  2511. * @see com.vaadin.ui.AbstractField#focus()
  2512. */
  2513. @Override
  2514. public void focus() throws UnsupportedOperationException {
  2515. throw new UnsupportedOperationException();
  2516. }
  2517. /**
  2518. * Gets the ID of the Item following the Item that corresponds to itemId.
  2519. *
  2520. * @see com.vaadin.data.Container.Ordered#nextItemId(java.lang.Object)
  2521. */
  2522. public Object nextItemId(Object itemId) {
  2523. return ((Container.Ordered) items).nextItemId(itemId);
  2524. }
  2525. /**
  2526. * Gets the ID of the Item preceding the Item that corresponds to the
  2527. * itemId.
  2528. *
  2529. * @see com.vaadin.data.Container.Ordered#prevItemId(java.lang.Object)
  2530. */
  2531. public Object prevItemId(Object itemId) {
  2532. return ((Container.Ordered) items).prevItemId(itemId);
  2533. }
  2534. /**
  2535. * Gets the ID of the first Item in the Container.
  2536. *
  2537. * @see com.vaadin.data.Container.Ordered#firstItemId()
  2538. */
  2539. public Object firstItemId() {
  2540. return ((Container.Ordered) items).firstItemId();
  2541. }
  2542. /**
  2543. * Gets the ID of the last Item in the Container.
  2544. *
  2545. * @see com.vaadin.data.Container.Ordered#lastItemId()
  2546. */
  2547. public Object lastItemId() {
  2548. return ((Container.Ordered) items).lastItemId();
  2549. }
  2550. /**
  2551. * Tests if the Item corresponding to the given Item ID is the first Item in
  2552. * the Container.
  2553. *
  2554. * @see com.vaadin.data.Container.Ordered#isFirstId(java.lang.Object)
  2555. */
  2556. public boolean isFirstId(Object itemId) {
  2557. return ((Container.Ordered) items).isFirstId(itemId);
  2558. }
  2559. /**
  2560. * Tests if the Item corresponding to the given Item ID is the last Item in
  2561. * the Container.
  2562. *
  2563. * @see com.vaadin.data.Container.Ordered#isLastId(java.lang.Object)
  2564. */
  2565. public boolean isLastId(Object itemId) {
  2566. return ((Container.Ordered) items).isLastId(itemId);
  2567. }
  2568. /**
  2569. * Adds new item after the given item.
  2570. *
  2571. * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object)
  2572. */
  2573. public Object addItemAfter(Object previousItemId)
  2574. throws UnsupportedOperationException {
  2575. Object itemId = ((Container.Ordered) items)
  2576. .addItemAfter(previousItemId);
  2577. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  2578. resetPageBuffer();
  2579. refreshRenderedCells();
  2580. }
  2581. return itemId;
  2582. }
  2583. /**
  2584. * Adds new item after the given item.
  2585. *
  2586. * @see com.vaadin.data.Container.Ordered#addItemAfter(java.lang.Object,
  2587. * java.lang.Object)
  2588. */
  2589. public Item addItemAfter(Object previousItemId, Object newItemId)
  2590. throws UnsupportedOperationException {
  2591. Item item = ((Container.Ordered) items).addItemAfter(previousItemId,
  2592. newItemId);
  2593. if (!(items instanceof Container.ItemSetChangeNotifier)) {
  2594. resetPageBuffer();
  2595. refreshRenderedCells();
  2596. }
  2597. return item;
  2598. }
  2599. /**
  2600. * Sets the TableFieldFactory that is used to create editor for table cells.
  2601. *
  2602. * The TableFieldFactory is only used if the Table is editable. By default
  2603. * the DefaultFieldFactory is used.
  2604. *
  2605. * @param fieldFactory
  2606. * the field factory to set.
  2607. * @see #isEditable
  2608. * @see DefaultFieldFactory
  2609. */
  2610. public void setTableFieldFactory(TableFieldFactory fieldFactory) {
  2611. this.fieldFactory = fieldFactory;
  2612. }
  2613. /**
  2614. * Gets the TableFieldFactory that is used to create editor for table cells.
  2615. *
  2616. * The FieldFactory is only used if the Table is editable.
  2617. *
  2618. * @return TableFieldFactory used to create the Field instances.
  2619. * @see #isEditable
  2620. */
  2621. public TableFieldFactory getTableFieldFactory() {
  2622. return fieldFactory;
  2623. }
  2624. /**
  2625. * Gets the FieldFactory that is used to create editor for table cells.
  2626. *
  2627. * The FieldFactory is only used if the Table is editable.
  2628. *
  2629. * @return FieldFactory used to create the Field instances.
  2630. * @see #isEditable
  2631. * @deprecated use {@link #getTableFieldFactory()} instead
  2632. */
  2633. @Deprecated
  2634. public FieldFactory getFieldFactory() {
  2635. if (fieldFactory instanceof FieldFactory) {
  2636. return (FieldFactory) fieldFactory;
  2637. }
  2638. return null;
  2639. }
  2640. /**
  2641. * Sets the FieldFactory that is used to create editor for table cells.
  2642. *
  2643. * The FieldFactory is only used if the Table is editable. By default the
  2644. * BaseFieldFactory is used.
  2645. *
  2646. * @param fieldFactory
  2647. * the field factory to set.
  2648. * @see #isEditable
  2649. * @see BaseFieldFactory
  2650. * @deprecated use {@link #setTableFieldFactory(TableFieldFactory)} instead
  2651. */
  2652. @Deprecated
  2653. public void setFieldFactory(FieldFactory fieldFactory) {
  2654. this.fieldFactory = fieldFactory;
  2655. // Assure visual refresh
  2656. resetPageBuffer();
  2657. refreshRenderedCells();
  2658. }
  2659. /**
  2660. * Is table editable.
  2661. *
  2662. * If table is editable a editor of type Field is created for each table
  2663. * cell. The assigned FieldFactory is used to create the instances.
  2664. *
  2665. * To provide custom editors for table cells create a class implementins the
  2666. * FieldFactory interface, and assign it to table, and set the editable
  2667. * property to true.
  2668. *
  2669. * @return true if table is editable, false oterwise.
  2670. * @see Field
  2671. * @see FieldFactory
  2672. *
  2673. */
  2674. public boolean isEditable() {
  2675. return editable;
  2676. }
  2677. /**
  2678. * Sets the editable property.
  2679. *
  2680. * If table is editable a editor of type Field is created for each table
  2681. * cell. The assigned FieldFactory is used to create the instances.
  2682. *
  2683. * To provide custom editors for table cells create a class implementins the
  2684. * FieldFactory interface, and assign it to table, and set the editable
  2685. * property to true.
  2686. *
  2687. * @param editable
  2688. * true if table should be editable by user.
  2689. * @see Field
  2690. * @see FieldFactory
  2691. *
  2692. */
  2693. public void setEditable(boolean editable) {
  2694. this.editable = editable;
  2695. // Assure visual refresh
  2696. resetPageBuffer();
  2697. refreshRenderedCells();
  2698. }
  2699. /**
  2700. * Sorts the table.
  2701. *
  2702. * @throws UnsupportedOperationException
  2703. * if the container data source does not implement
  2704. * Container.Sortable
  2705. * @see com.vaadin.data.Container.Sortable#sort(java.lang.Object[],
  2706. * boolean[])
  2707. *
  2708. */
  2709. public void sort(Object[] propertyId, boolean[] ascending)
  2710. throws UnsupportedOperationException {
  2711. final Container c = getContainerDataSource();
  2712. if (c instanceof Container.Sortable) {
  2713. final int pageIndex = getCurrentPageFirstItemIndex();
  2714. ((Container.Sortable) c).sort(propertyId, ascending);
  2715. setCurrentPageFirstItemIndex(pageIndex);
  2716. resetPageBuffer();
  2717. refreshRenderedCells();
  2718. } else if (c != null) {
  2719. throw new UnsupportedOperationException(
  2720. "Underlying Data does not allow sorting");
  2721. }
  2722. }
  2723. /**
  2724. * Sorts the table by currently selected sorting column.
  2725. *
  2726. * @throws UnsupportedOperationException
  2727. * if the container data source does not implement
  2728. * Container.Sortable
  2729. */
  2730. public void sort() {
  2731. if (getSortContainerPropertyId() == null) {
  2732. return;
  2733. }
  2734. sort(new Object[] { sortContainerPropertyId },
  2735. new boolean[] { sortAscending });
  2736. }
  2737. /**
  2738. * Gets the container property IDs, which can be used to sort the item.
  2739. *
  2740. * @see com.vaadin.data.Container.Sortable#getSortableContainerPropertyIds()
  2741. */
  2742. public Collection getSortableContainerPropertyIds() {
  2743. final Container c = getContainerDataSource();
  2744. if (c instanceof Container.Sortable && !isSortDisabled()) {
  2745. return ((Container.Sortable) c).getSortableContainerPropertyIds();
  2746. } else {
  2747. return new LinkedList();
  2748. }
  2749. }
  2750. /**
  2751. * Gets the currently sorted column property ID.
  2752. *
  2753. * @return the Container property id of the currently sorted column.
  2754. */
  2755. public Object getSortContainerPropertyId() {
  2756. return sortContainerPropertyId;
  2757. }
  2758. /**
  2759. * Sets the currently sorted column property id.
  2760. *
  2761. * @param propertyId
  2762. * the Container property id of the currently sorted column.
  2763. */
  2764. public void setSortContainerPropertyId(Object propertyId) {
  2765. setSortContainerPropertyId(propertyId, true);
  2766. }
  2767. /**
  2768. * Internal method to set currently sorted column property id. With doSort
  2769. * flag actual sorting may be bypassed.
  2770. *
  2771. * @param propertyId
  2772. * @param doSort
  2773. */
  2774. private void setSortContainerPropertyId(Object propertyId, boolean doSort) {
  2775. if ((sortContainerPropertyId != null && !sortContainerPropertyId
  2776. .equals(propertyId))
  2777. || (sortContainerPropertyId == null && propertyId != null)) {
  2778. sortContainerPropertyId = propertyId;
  2779. if (doSort) {
  2780. sort();
  2781. // Assures the visual refresh
  2782. refreshRenderedCells();
  2783. }
  2784. }
  2785. }
  2786. /**
  2787. * Is the table currently sorted in ascending order.
  2788. *
  2789. * @return <code>true</code> if ascending, <code>false</code> if descending.
  2790. */
  2791. public boolean isSortAscending() {
  2792. return sortAscending;
  2793. }
  2794. /**
  2795. * Sets the table in ascending order.
  2796. *
  2797. * @param ascending
  2798. * <code>true</code> if ascending, <code>false</code> if
  2799. * descending.
  2800. */
  2801. public void setSortAscending(boolean ascending) {
  2802. setSortAscending(ascending, true);
  2803. }
  2804. /**
  2805. * Internal method to set sort ascending. With doSort flag actual sort can
  2806. * be bypassed.
  2807. *
  2808. * @param ascending
  2809. * @param doSort
  2810. */
  2811. private void setSortAscending(boolean ascending, boolean doSort) {
  2812. if (sortAscending != ascending) {
  2813. sortAscending = ascending;
  2814. if (doSort) {
  2815. sort();
  2816. }
  2817. }
  2818. // Assures the visual refresh
  2819. refreshRenderedCells();
  2820. }
  2821. /**
  2822. * Is sorting disabled altogether.
  2823. *
  2824. * True iff no sortable columns are given even in the case where data source
  2825. * would support this.
  2826. *
  2827. * @return True iff sorting is disabled.
  2828. */
  2829. public boolean isSortDisabled() {
  2830. return sortDisabled;
  2831. }
  2832. /**
  2833. * Disables the sorting altogether.
  2834. *
  2835. * To disable sorting altogether, set to true. In this case no sortable
  2836. * columns are given even in the case where datasource would support this.
  2837. *
  2838. * @param sortDisabled
  2839. * True iff sorting is disabled.
  2840. */
  2841. public void setSortDisabled(boolean sortDisabled) {
  2842. if (this.sortDisabled != sortDisabled) {
  2843. this.sortDisabled = sortDisabled;
  2844. refreshRenderedCells();
  2845. }
  2846. }
  2847. /**
  2848. * Table does not support lazy options loading mode. Setting this true will
  2849. * throw UnsupportedOperationException.
  2850. *
  2851. * @see com.vaadin.ui.Select#setLazyLoading(boolean)
  2852. */
  2853. public void setLazyLoading(boolean useLazyLoading) {
  2854. if (useLazyLoading) {
  2855. throw new UnsupportedOperationException(
  2856. "Lazy options loading is not supported by Table.");
  2857. }
  2858. }
  2859. /*
  2860. * Override abstract fields to string method to avoid non-informative null's
  2861. * in debugger
  2862. */
  2863. @Override
  2864. public String toString() {
  2865. return "Table:" + getContainerPropertyIds() + ", rows "
  2866. + getContainerDataSource().size() + " ,value:"
  2867. + super.toString();
  2868. }
  2869. /**
  2870. * Used to create "generated columns"; columns that exist only in the Table,
  2871. * not in the underlying Container. Implement this interface and pass it to
  2872. * Table.addGeneratedColumn along with an id for the column to be generated.
  2873. *
  2874. */
  2875. public interface ColumnGenerator extends Serializable {
  2876. /**
  2877. * Called by Table when a cell in a generated column needs to be
  2878. * generated.
  2879. *
  2880. * @param source
  2881. * the source Table
  2882. * @param itemId
  2883. * the itemId (aka rowId) for the of the cell to be generated
  2884. * @param columnId
  2885. * the id for the generated column (as specified in
  2886. * addGeneratedColumn)
  2887. * @return
  2888. */
  2889. public abstract Component generateCell(Table source, Object itemId,
  2890. Object columnId);
  2891. }
  2892. /**
  2893. * Set cell style generator for Table.
  2894. *
  2895. * @param cellStyleGenerator
  2896. * New cell style generator or null to remove generator.
  2897. */
  2898. public void setCellStyleGenerator(CellStyleGenerator cellStyleGenerator) {
  2899. this.cellStyleGenerator = cellStyleGenerator;
  2900. requestRepaint();
  2901. }
  2902. /**
  2903. * Get the current cell style generator.
  2904. *
  2905. */
  2906. public CellStyleGenerator getCellStyleGenerator() {
  2907. return cellStyleGenerator;
  2908. }
  2909. /**
  2910. * Allow to define specific style on cells (and rows) contents. Implements
  2911. * this interface and pass it to Table.setCellStyleGenerator. Row styles are
  2912. * generated when porpertyId is null. The CSS class name that will be added
  2913. * to the cell content is <tt>v-table-cell-content-[style name]</tt>, and
  2914. * the row style will be <tt>v-table-row-[style name]</tt>.
  2915. */
  2916. public interface CellStyleGenerator extends Serializable {
  2917. /**
  2918. * Called by Table when a cell (and row) is painted.
  2919. *
  2920. * @param itemId
  2921. * The itemId of the painted cell
  2922. * @param propertyId
  2923. * The propertyId of the cell, null when getting row style
  2924. * @return The style name to add to this cell or row. (the CSS class
  2925. * name will be v-table-cell-content-[style name], or
  2926. * v-table-row-[style name] for rows)
  2927. */
  2928. public abstract String getStyle(Object itemId, Object propertyId);
  2929. }
  2930. public void addListener(ItemClickListener listener) {
  2931. addListener(ItemClickEvent.class, listener,
  2932. ItemClickEvent.ITEM_CLICK_METHOD);
  2933. clickListenerCount++;
  2934. // repaint needed only if click listening became necessary
  2935. if (clickListenerCount == 1) {
  2936. requestRepaint();
  2937. }
  2938. }
  2939. public void removeListener(ItemClickListener listener) {
  2940. removeListener(ItemClickEvent.class, listener,
  2941. ItemClickEvent.ITEM_CLICK_METHOD);
  2942. clickListenerCount--;
  2943. // repaint needed only if click listening is not needed in client
  2944. // anymore
  2945. if (clickListenerCount == 0) {
  2946. requestRepaint();
  2947. }
  2948. }
  2949. // Identical to AbstractCompoenentContainer.setEnabled();
  2950. @Override
  2951. public void setEnabled(boolean enabled) {
  2952. super.setEnabled(enabled);
  2953. if (getParent() != null && !getParent().isEnabled()) {
  2954. // some ancestor still disabled, don't update children
  2955. return;
  2956. } else {
  2957. requestRepaintAll();
  2958. }
  2959. }
  2960. // Virtually identical to AbstractCompoenentContainer.setEnabled();
  2961. public void requestRepaintAll() {
  2962. requestRepaint();
  2963. if (visibleComponents != null) {
  2964. for (Iterator<Component> childIterator = visibleComponents
  2965. .iterator(); childIterator.hasNext();) {
  2966. Component c = childIterator.next();
  2967. if (c instanceof Form) {
  2968. // Form has children in layout, but is not
  2969. // ComponentContainer
  2970. c.requestRepaint();
  2971. ((Form) c).getLayout().requestRepaintAll();
  2972. } else if (c instanceof Table) {
  2973. ((Table) c).requestRepaintAll();
  2974. } else if (c instanceof ComponentContainer) {
  2975. ((ComponentContainer) c).requestRepaintAll();
  2976. } else {
  2977. c.requestRepaint();
  2978. }
  2979. }
  2980. }
  2981. }
  2982. }