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 95KB

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