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

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