From 014a19dc51b0175c452029e6d2cff8674f0202c4 Mon Sep 17 00:00:00 2001 From: kruland2607 Date: Tue, 10 Jan 2012 15:48:19 +0000 Subject: [PATCH] Using pl.polidea.treeview for the component tree list. This code is under 2-clause BSD and available from code.google.com/p/tree-view-list-android. Unfortunately, the only way to effectively use this code is to include the source within the project (instead of jar) because it was poorly written and depends on a bunch of resources. git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@330 180e2498-e6e9-4542-8430-84ac67f01cd8 --- android/res/drawable-hdpi/collapsed.png | Bin 0 -> 455 bytes android/res/drawable-hdpi/expanded.png | Bin 0 -> 361 bytes android/res/drawable-ldpi/collapsed.png | Bin 0 -> 313 bytes android/res/drawable-ldpi/expanded.png | Bin 0 -> 291 bytes android/res/drawable-mdpi/collapsed.png | Bin 0 -> 272 bytes android/res/drawable-mdpi/expanded.png | Bin 0 -> 254 bytes android/res/drawable/divider.xml | 6 + .../res/drawable/list_selector_background.xml | 45 +++ .../list_selector_background_disabled.9.png | Bin 0 -> 1252 bytes .../list_selector_background_focus.9.png | Bin 0 -> 11006 bytes .../list_selector_background_longpress.9.png | Bin 0 -> 3017 bytes .../list_selector_background_pressed.9.png | Bin 0 -> 11006 bytes .../list_selector_background_transition.xml | 20 + android/res/layout/component_list_item.xml | 18 + android/res/layout/openrocketviewer.xml | 46 ++- android/res/layout/tree_list_item_wrapper.xml | 27 ++ android/res/values/attrs.xml | 42 ++ android/res/values/styles.xml | 7 + .../android/rocket/OpenRocketViewer.java | 89 +++-- .../rocket/RocketComponentTreeAdapter.java | 74 ++++ .../treeview/AbstractTreeViewAdapter.java | 316 +++++++++++++++ .../pl/polidea/treeview/InMemoryTreeNode.java | 116 ++++++ .../treeview/InMemoryTreeStateManager.java | 374 ++++++++++++++++++ .../treeview/NodeAlreadyInTreeException.java | 14 + .../treeview/NodeNotInTreeException.java | 15 + .../src/pl/polidea/treeview/TreeBuilder.java | 126 ++++++ .../treeview/TreeConfigurationException.java | 15 + .../src/pl/polidea/treeview/TreeNodeInfo.java | 69 ++++ .../pl/polidea/treeview/TreeStateManager.java | 193 +++++++++ .../src/pl/polidea/treeview/TreeViewList.java | 199 ++++++++++ android/src/pl/polidea/treeview/license.txt | 19 + .../src/pl/polidea/treeview/package-info.java | 4 + 32 files changed, 1787 insertions(+), 47 deletions(-) create mode 100644 android/res/drawable-hdpi/collapsed.png create mode 100644 android/res/drawable-hdpi/expanded.png create mode 100644 android/res/drawable-ldpi/collapsed.png create mode 100644 android/res/drawable-ldpi/expanded.png create mode 100644 android/res/drawable-mdpi/collapsed.png create mode 100644 android/res/drawable-mdpi/expanded.png create mode 100644 android/res/drawable/divider.xml create mode 100644 android/res/drawable/list_selector_background.xml create mode 100644 android/res/drawable/list_selector_background_disabled.9.png create mode 100644 android/res/drawable/list_selector_background_focus.9.png create mode 100644 android/res/drawable/list_selector_background_longpress.9.png create mode 100644 android/res/drawable/list_selector_background_pressed.9.png create mode 100644 android/res/drawable/list_selector_background_transition.xml create mode 100644 android/res/layout/component_list_item.xml create mode 100644 android/res/layout/tree_list_item_wrapper.xml create mode 100644 android/res/values/attrs.xml create mode 100644 android/res/values/styles.xml create mode 100644 android/src/net/sf/openrocket/android/rocket/RocketComponentTreeAdapter.java create mode 100644 android/src/pl/polidea/treeview/AbstractTreeViewAdapter.java create mode 100644 android/src/pl/polidea/treeview/InMemoryTreeNode.java create mode 100644 android/src/pl/polidea/treeview/InMemoryTreeStateManager.java create mode 100644 android/src/pl/polidea/treeview/NodeAlreadyInTreeException.java create mode 100644 android/src/pl/polidea/treeview/NodeNotInTreeException.java create mode 100644 android/src/pl/polidea/treeview/TreeBuilder.java create mode 100644 android/src/pl/polidea/treeview/TreeConfigurationException.java create mode 100644 android/src/pl/polidea/treeview/TreeNodeInfo.java create mode 100644 android/src/pl/polidea/treeview/TreeStateManager.java create mode 100644 android/src/pl/polidea/treeview/TreeViewList.java create mode 100644 android/src/pl/polidea/treeview/license.txt create mode 100644 android/src/pl/polidea/treeview/package-info.java diff --git a/android/res/drawable-hdpi/collapsed.png b/android/res/drawable-hdpi/collapsed.png new file mode 100644 index 0000000000000000000000000000000000000000..c1e1f3d5161c0edc790a37380da0db0b81adbe83 GIT binary patch literal 455 zcmeAS@N?(olHy`uVBq!ia0vp^av;nBBpBqPKh^;$rX+877Y2q^y~;*F9%q3^WHAE+ zw=f7ZGR&GI!N9=C>FMGaqH%uf<=tI}97Gx(?s+OE*3|KtLCma7tlQP==(Q?V#jr|7 zRfWtPo*NQ-e2(4enGJ5e7R_&@76i_YPP=@q!#$5 z{PcQe(q3Tt=8R3lq^m}UGas+p_iOFOx4U-FIN38@xcG*9@4Evg&*Kz7*Ijg;8}@kd zO}C6KY%?dX%{_bU?z87tHqB9n)UpoJ-;N#ypKbVVGH=e#*^F%;*^NnbE$J6JT z8BhIxDKn*Jtwgx%XOBIOOs*@n&g(tu^pH5ZYLWK`hTx5^QGD*RO@JY#TH+c}l9E`G zYL#4+3Zxi}3=9o*4UBaSOhXLKtqd)#42-o646O_d9=zqtLD7(#pOTqYiCe?O4cjgN PH86O(`njxgN@xNAudlK* literal 0 HcmV?d00001 diff --git a/android/res/drawable-hdpi/expanded.png b/android/res/drawable-hdpi/expanded.png new file mode 100644 index 0000000000000000000000000000000000000000..cec254aa7fb3e42a98f787776471023e5a827ff3 GIT binary patch literal 361 zcmeAS@N?(olHy`uVBq!ia0vp^av;nBBpBqPKh^;$rX+877Y2q^y~;*F9%q3^WHAE+ zw=f7ZGR&GI0Tf*A>EaloasKXQd#*zUBF8`8yQW;h<$f@l!>W?eQ9xXrRopdql9}(U zN%p>zR|#%v2@gMd{=>BQ-yTeVDxdmr0pt4~1IdPq+R3L}nO91lEOObgO?JuD)Dwq( z$vraR-TCqt`;pnN3*HX1a!7)iEBhj zN@7W>RdP`(kYX@0Ff`OPFxE9N4KXygGPJNVHP$vTv@$SoKc8)eq9HdwB{QuOw+6FG Sm%D%(7(8A5T-G@yGywo2v48mh literal 0 HcmV?d00001 diff --git a/android/res/drawable-ldpi/collapsed.png b/android/res/drawable-ldpi/collapsed.png new file mode 100644 index 0000000000000000000000000000000000000000..a52e846afa9e10c440191d1933470fd96bbea71c GIT binary patch literal 313 zcmeAS@N?(olHy`uVBq!ia0vp^{2Tr)5S4_<9g^BL#_q`p4Nx+E?>QA{Y7DKYd3pAap47F5l$@*R$ouk zYkX{P?OFCcbakA^G-G-^S6X=mXF2O>!H8WNH;a2FPQ0|&-|(B+=d4@jd$Pl4p1hd- z#y51zu8r$YZd-Qc>yFyrp4V^MU+0;5GB0UQUZ?XXzvUu-m>s0gUurVE`~c_-)e_f; zl9a@fRIB8oR3OD*WMF8hYhbKvU>ag*Ze?g;Wn`>vU}$Av;LDYykD?(rKP5A*61N7M T@LQ9C8W=oX{an^LB{Ts5*T`oi literal 0 HcmV?d00001 diff --git a/android/res/drawable-ldpi/expanded.png b/android/res/drawable-ldpi/expanded.png new file mode 100644 index 0000000000000000000000000000000000000000..d8b7ab894ede111d6565161befce37412be81ab5 GIT binary patch literal 291 zcmeAS@N?(olHy`uVBq!ia0vp^{22%IkD z|M#nG=Om&1W{MME#NRf&{O?%K0s)|fswJ)wB`Jv|saDBFsX&Us$iUE0*T7iUz%<0r v+{)0x%EU<9z|hLTKvyV121P?|eoAIqC2kEd{x=o?H86O(`njxgN@xNAXi8W~ literal 0 HcmV?d00001 diff --git a/android/res/drawable-mdpi/collapsed.png b/android/res/drawable-mdpi/collapsed.png new file mode 100644 index 0000000000000000000000000000000000000000..b23555145f7ef7191ac7257b854a432cd5c4466d GIT binary patch literal 272 zcmeAS@N?(olHy`uVBq!ia0vp^A|T8GBp6maa=Hkln3BBRT^JZv^(q?yd7K3vk;M!Q z+`=Ht$S`Y;1W+)@)5S4F;_}=HhI|JMIGBH`oImXy@mJhE<@|w5e6d|lS2fJ%=``x< z2$ac7zIU2Gai*S3VQSJG52cpt3=UYW@Tz&Wnu`?!0;l&3aEj>)78&qol`;+0OsCOi~s-t literal 0 HcmV?d00001 diff --git a/android/res/drawable-mdpi/expanded.png b/android/res/drawable-mdpi/expanded.png new file mode 100644 index 0000000000000000000000000000000000000000..f7377cb30cfd739a509e8e69a10890dabc58b936 GIT binary patch literal 254 zcmeAS@N?(olHy`uVBq!ia0vp^A|T8GBp6maa=Hkln3BBRT^JZv^(q?yd7K3vk;M!Q z+`=Ht$S`Y;1W?ey)5S4F;&O6DAFF{){l{0PS`}ZL9{yaNzdzvM_Hdr_@&AAIbJgu# zw*A3pWq}Vekp<-%EN|3S6djQ1H?`T=V#8p-RLv8zKKUciEY%X%h?11Vl2ohYqEsNo zU}RuusB2)XYhW5;Xl`X_WMyQcZD43+U@*V`Oa_XE-29Zxv`Q2WW>%(_R>sB<4Mhdg QCO{1gp00i_>zopr0NjsCE&u=k literal 0 HcmV?d00001 diff --git a/android/res/drawable/divider.xml b/android/res/drawable/divider.xml new file mode 100644 index 00000000..5f379499 --- /dev/null +++ b/android/res/drawable/divider.xml @@ -0,0 +1,6 @@ + + diff --git a/android/res/drawable/list_selector_background.xml b/android/res/drawable/list_selector_background.xml new file mode 100644 index 00000000..2b21b0c1 --- /dev/null +++ b/android/res/drawable/list_selector_background.xml @@ -0,0 +1,45 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/res/drawable/list_selector_background_disabled.9.png b/android/res/drawable/list_selector_background_disabled.9.png new file mode 100644 index 0000000000000000000000000000000000000000..bf970b0509b81f2d7237d865e350e88787b16df2 GIT binary patch literal 1252 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 T|JePN_htX$mA&s@dH33XGOeYN literal 0 HcmV?d00001 diff --git a/android/res/drawable/list_selector_background_longpress.9.png b/android/res/drawable/list_selector_background_longpress.9.png new file mode 100644 index 0000000000000000000000000000000000000000..5cbb251a7dbf1e7186a9aae78a786b6d667de6ca GIT binary patch literal 3017 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 LNkvXXu0mjflqZiH literal 0 HcmV?d00001 diff --git a/android/res/drawable/list_selector_background_pressed.9.png b/android/res/drawable/list_selector_background_pressed.9.png new file mode 100644 index 0000000000000000000000000000000000000000..02b4e9a536fedd342ecef1d0153ef97a3fe69452 GIT binary patch literal 11006 zcmds-yNg{_7{&J;lNmE+EFwbC#@gD>#>j+ZP=kmFVk1_TiYWvOL937^Vkd&>ECl}p zOItxi{3C3{CK%&f_c~|)-0Oa`&*R>?8HhWav%bgr9_yTWgqgYZ`i*M``_JsBln#!r zAHI?C1znHt=KGHyKl^)z-S=L(c9@p>clU>Hzs!*fA6$R?qm-U_O4lNN_4VZ(+`D=7 z+SR?^E?!D6UjF6h&tFaHxpZ`R<<0lcFK#}6``5*Cxy-DGdlrjDHzdzmJlshsb%MV8 zaIxIUqZZp3w{DLuJWsuo<9xk!C#7?GVh0!Uo6Od^$2NF=2mm)|(Ul+!T%*r}e^UDE zL0`}bdio%xC$p&C^E0}zxX^7%iQ!M73&0k-J<;R--zoihuP1eaB<9k^{7I-!(bi!} zM1bOY&iz)ORxB>=KEgcK=yC6U4R(U`W+Y~RuO|MpSU#c%#IPM@&pB~fM5q=!Vxun~ zU7estz7Mgma3^2*%3=$3BKFbg`;Z{T)e|wt)`}VnVGavoVHZNoQJwJVDu2#lX>nOx zwb;J6_K=S-&*da zkgL!H!-ABhU=G4}ls$CR6CvV6-|Lj)z!P$j%UA`G6zaG?$ELCw+Fh}os8k>cq2!F$+e-+ePl;y}ppEv^}o<;<Ow-4#+&mGC|*2 z#t%&$y%Dy$IA^@;NiKaNuGzPLLo7luO<9UONZgTJMCZ88({}Vcw)#bmVqDJ4c6~#U zAfjTNXv2?j@jXU;L^vRI^0}yq9O7P6V)b*#Np5>H8hD-jXD z`0?ynK96~Ch_622S#HQoaANj6K@&}y>9GaIa?NJ*gVW!KG~Bf?SAfmUmQhe3p@qT?RG5IkNGwTj;_XP1}M*q&Y(Te}Kv%g5`xe_`NrM zXO5%n=(`YdjqbyOMD{|2Aw)TBab1x7xrZ5!(U$MmNbhk$Dx?w1J1Q52;ExPlNvVfU z3z-e}%+dR_Aj)a#f|Nr5=rL-C5E05_J2G>qz7d}8yAR;{C_z_v0IDSO(GjV-E xG;R%^5gSDrymS20H+O#eLeJvp>W#ziUw-$qe*spuA{hVx literal 0 HcmV?d00001 diff --git a/android/res/drawable/list_selector_background_transition.xml b/android/res/drawable/list_selector_background_transition.xml new file mode 100644 index 00000000..0dec1335 --- /dev/null +++ b/android/res/drawable/list_selector_background_transition.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/android/res/layout/component_list_item.xml b/android/res/layout/component_list_item.xml new file mode 100644 index 00000000..6956b2d0 --- /dev/null +++ b/android/res/layout/component_list_item.xml @@ -0,0 +1,18 @@ + + + + + + + \ No newline at end of file diff --git a/android/res/layout/openrocketviewer.xml b/android/res/layout/openrocketviewer.xml index 6b6a8a2c..8ddc4c4f 100644 --- a/android/res/layout/openrocketviewer.xml +++ b/android/res/layout/openrocketviewer.xml @@ -32,81 +32,89 @@ android:orientation="vertical" > + android:text="Rocket Name" /> + + + android:text="Designer" /> + + + android:text="CG" /> + + + android:text="Length" /> + + + android:text="Mass" /> + + + android:text="Stage Count" /> + + + android:text="Comment" /> + - + - + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/res/values/attrs.xml b/android/res/values/attrs.xml new file mode 100644 index 00000000..30060a4c --- /dev/null +++ b/android/res/values/attrs.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/android/res/values/styles.xml b/android/res/values/styles.xml new file mode 100644 index 00000000..005c6007 --- /dev/null +++ b/android/res/values/styles.xml @@ -0,0 +1,7 @@ + + + + diff --git a/android/src/net/sf/openrocket/android/rocket/OpenRocketViewer.java b/android/src/net/sf/openrocket/android/rocket/OpenRocketViewer.java index 1037d1b8..ad2d3e05 100644 --- a/android/src/net/sf/openrocket/android/rocket/OpenRocketViewer.java +++ b/android/src/net/sf/openrocket/android/rocket/OpenRocketViewer.java @@ -7,6 +7,7 @@ import net.sf.openrocket.R; import net.sf.openrocket.android.Application; import net.sf.openrocket.android.PreferencesActivity; import net.sf.openrocket.android.motor.MotorHierarchicalBrowser; +import net.sf.openrocket.android.rocket.RocketComponentTreeAdapter.RocketComponentWithId; import net.sf.openrocket.android.simulation.SimulationViewer; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; @@ -16,6 +17,10 @@ import net.sf.openrocket.rocketcomponent.RocketUtils; import net.sf.openrocket.unit.Unit; import net.sf.openrocket.unit.UnitGroup; import net.sf.openrocket.util.Coordinate; +import pl.polidea.treeview.InMemoryTreeStateManager; +import pl.polidea.treeview.TreeBuilder; +import pl.polidea.treeview.TreeStateManager; +import pl.polidea.treeview.TreeViewList; import android.app.Activity; import android.app.ProgressDialog; import android.content.Intent; @@ -33,6 +38,7 @@ import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; +import android.widget.ListAdapter; import android.widget.ListView; import android.widget.TabHost; import android.widget.TextView; @@ -45,7 +51,7 @@ implements SharedPreferences.OnSharedPreferenceChangeListener private ProgressDialog progress; - private ListView componentList; + private TreeViewList componentTree; private ListView simulationList; private Application app; @@ -79,7 +85,7 @@ implements SharedPreferences.OnSharedPreferenceChangeListener tabs.addTab(spec); spec=tabs.newTabSpec("tag2"); - spec.setContent(R.id.openrocketviewerComponentList); + spec.setContent(R.id.openrocketviewerComponentTree); spec.setIndicator("Components"); tabs.addTab(spec); @@ -88,7 +94,8 @@ implements SharedPreferences.OnSharedPreferenceChangeListener spec.setIndicator("Simulations"); tabs.addTab(spec); - componentList = (ListView) findViewById(R.id.openrocketviewerComponentList); + + componentTree = (TreeViewList) findViewById(R.id.openrocketviewerComponentTree); simulationList = (ListView) findViewById(R.id.openrocketviewerSimulationList); Intent i = getIntent(); @@ -127,7 +134,7 @@ implements SharedPreferences.OnSharedPreferenceChangeListener break; } } - + private void loadOrkFile( Uri file ) { Log.d(TAG,"Use ork file: " + file); String path = file.getPath(); @@ -166,12 +173,12 @@ implements SharedPreferences.OnSharedPreferenceChangeListener OpenRocketDocument rocketDocument = app.getRocketDocument(); Rocket rocket = rocketDocument.getRocket(); - + setTitle(rocket.getName()); - + Unit LengthUnit = UnitGroup.UNITS_LENGTH.getDefaultUnit(); Unit MassUnit = UnitGroup.UNITS_MASS.getDefaultUnit(); - + Coordinate cg = RocketUtils.getCG(rocket); double length = RocketUtils.getLength(rocket); ((TextView) findViewById(R.id.openrocketviewerRocketName)).setText( rocket.getName()); @@ -210,31 +217,11 @@ implements SharedPreferences.OnSharedPreferenceChangeListener }); simulationList.setAdapter(sims); - ArrayAdapter comps = new ArrayAdapter(this, android.R.layout.simple_list_item_1,rocket.getChildren()) { + componentTree.setAdapter( buildAdapter( rocket ) ); - /* (non-Javadoc) - * @see android.widget.ArrayAdapter#getView(int, android.view.View, android.view.ViewGroup) - */ - @Override - public View getView(int position, View convertView, ViewGroup parent) { - View v = convertView; - if ( v == null ) { - LayoutInflater li = getLayoutInflater(); - v = li.inflate(android.R.layout.simple_list_item_1,null); - } - RocketComponent comp = this.getItem(position); - ((TextView)v.findViewById(android.R.id.text1)).setText( comp.getName() ); - return v; - } - - - }; - componentList.setAdapter(comps); - if ( progress.isShowing() ) { progress.dismiss(); } - } @Override @@ -265,6 +252,52 @@ implements SharedPreferences.OnSharedPreferenceChangeListener startActivity(i); } + private ListAdapter buildAdapter( Rocket rocket ) { + /* + final int[] DEMO_NODES = new int[] { 0, 0, 1, 1, 1, 2, 2, 1, + 1, 2, 1, 0, 0, 0, 1, 2, 3, 2, 0, 0, 1, 2, 0, 1, 2, 0, 1 }; + final int LEVEL_NUMBER = 4; + + TreeStateManager manager = new InMemoryTreeStateManager(); + final TreeBuilder treeBuilder = new TreeBuilder(manager); + for (int i = 0; i < DEMO_NODES.length; i++) { + treeBuilder.sequentiallyAddNextNode((long) i, DEMO_NODES[i]); + } + + return new SimpleStandardAdapter(this, manager, LEVEL_NUMBER); + */ + + TreeStateManager manager = new InMemoryTreeStateManager(); + TreeBuilder treeBuilder = new TreeBuilder(manager); + + int depth = buildRecursive( rocket, treeBuilder, 0 ); + return new RocketComponentTreeAdapter(this, manager, depth+1); + } + + long id = 0; + private int buildRecursive( RocketComponent comp, TreeBuilder builder, int depth ) { + + + int maxDepth = depth; + + RocketComponentWithId rcid = new RocketComponentWithId(comp, id++); + + // Add this component. + builder.sequentiallyAddNextNode(rcid, depth); + + if ( comp.allowsChildren() ) { + + for( RocketComponent child : comp.getChildren() ) { + int childDepth = buildRecursive( child, builder, depth+1); + if ( childDepth > maxDepth) { + maxDepth = childDepth; + } + } + + } + + return maxDepth; + } } diff --git a/android/src/net/sf/openrocket/android/rocket/RocketComponentTreeAdapter.java b/android/src/net/sf/openrocket/android/rocket/RocketComponentTreeAdapter.java new file mode 100644 index 00000000..9cf216be --- /dev/null +++ b/android/src/net/sf/openrocket/android/rocket/RocketComponentTreeAdapter.java @@ -0,0 +1,74 @@ +package net.sf.openrocket.android.rocket; + +import net.sf.openrocket.R; +import net.sf.openrocket.android.rocket.RocketComponentTreeAdapter.RocketComponentWithId; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import pl.polidea.treeview.AbstractTreeViewAdapter; +import pl.polidea.treeview.InMemoryTreeStateManager; +import pl.polidea.treeview.TreeNodeInfo; +import pl.polidea.treeview.TreeStateManager; +import android.app.Activity; +import android.view.View; +import android.view.ViewGroup; +import android.widget.LinearLayout; +import android.widget.TextView; + +/** + * This is a very simple adapter that provides very basic tree view with a + * checkboxes and simple item description. + * + */ +public class RocketComponentTreeAdapter extends AbstractTreeViewAdapter { + + static class RocketComponentWithId { + RocketComponent component; + long id; + public RocketComponentWithId( RocketComponent c, long id ) { + this.component = c; + this.id = id; + } + } + + public RocketComponentTreeAdapter(final Activity treeViewListDemo, + TreeStateManager manager, + final int numberOfLevels) { + super(treeViewListDemo, manager, numberOfLevels); + } + + private String getDescription(final RocketComponentWithId id) { + return id.component.getName(); + } + + @Override + public View getNewChildView(final TreeNodeInfo treeNodeInfo) { + final View viewLayout = getActivity().getLayoutInflater().inflate(R.layout.component_list_item, null); + return updateView(viewLayout, treeNodeInfo); + } + + @Override + public View updateView(final View view, + final TreeNodeInfo treeNodeInfo) { + final View viewLayout = view; + final TextView descriptionView = (TextView) viewLayout.findViewById(android.R.id.text1); + descriptionView.setText(getDescription(treeNodeInfo.getId())); + return viewLayout; + } + + @Override + public void handleItemClick(final View view, final Object id) { + final RocketComponentWithId longId = (RocketComponentWithId) id; + final TreeNodeInfo info = getManager().getNodeInfo(longId); + if (info.isWithChildren()) { + super.handleItemClick(view, id); + } else { + final ViewGroup vg = (ViewGroup) view; + // perform click on child item + } + } + + @Override + public long getItemId(final int position) { + RocketComponentWithId rcid = getTreeId(position); + return rcid.id; + } +} \ No newline at end of file diff --git a/android/src/pl/polidea/treeview/AbstractTreeViewAdapter.java b/android/src/pl/polidea/treeview/AbstractTreeViewAdapter.java new file mode 100644 index 00000000..e8e7071f --- /dev/null +++ b/android/src/pl/polidea/treeview/AbstractTreeViewAdapter.java @@ -0,0 +1,316 @@ +package pl.polidea.treeview; + +import net.sf.openrocket.R; +import android.app.Activity; +import android.content.Context; +import android.database.DataSetObserver; +import android.graphics.drawable.Drawable; +import android.view.LayoutInflater; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.FrameLayout; +import android.widget.FrameLayout.LayoutParams; +import android.widget.ImageView; +import android.widget.ImageView.ScaleType; +import android.widget.LinearLayout; +import android.widget.ListAdapter; + +/** + * Adapter used to feed the table view. + * + * @param + * class for ID of the tree + */ +public abstract class AbstractTreeViewAdapter extends BaseAdapter implements + ListAdapter { + private final TreeStateManager treeStateManager; + private final int numberOfLevels; + private final LayoutInflater layoutInflater; + + private int indentWidth = 0; + private int indicatorGravity = 0; + private Drawable collapsedDrawable; + private Drawable expandedDrawable; + private Drawable indicatorBackgroundDrawable; + private Drawable rowBackgroundDrawable; + + private final OnClickListener indicatorClickListener = new OnClickListener() { + @Override + public void onClick(final View v) { + @SuppressWarnings("unchecked") + final T id = (T) v.getTag(); + expandCollapse(id); + } + }; + + private boolean collapsible; + private final Activity activity; + + public Activity getActivity() { + return activity; + } + + protected TreeStateManager getManager() { + return treeStateManager; + } + + protected void expandCollapse(final T id) { + final TreeNodeInfo info = treeStateManager.getNodeInfo(id); + if (!info.isWithChildren()) { + // ignore - no default action + return; + } + if (info.isExpanded()) { + treeStateManager.collapseChildren(id); + } else { + treeStateManager.expandDirectChildren(id); + } + } + + private void calculateIndentWidth() { + if (expandedDrawable != null) { + indentWidth = Math.max(getIndentWidth(), + expandedDrawable.getIntrinsicWidth()); + } + if (collapsedDrawable != null) { + indentWidth = Math.max(getIndentWidth(), + collapsedDrawable.getIntrinsicWidth()); + } + } + + public AbstractTreeViewAdapter(final Activity activity, + final TreeStateManager treeStateManager, final int numberOfLevels) { + this.activity = activity; + this.treeStateManager = treeStateManager; + this.layoutInflater = (LayoutInflater) activity + .getSystemService(Context.LAYOUT_INFLATER_SERVICE); + this.numberOfLevels = numberOfLevels; + this.collapsedDrawable = null; + this.expandedDrawable = null; + this.rowBackgroundDrawable = null; + this.indicatorBackgroundDrawable = null; + } + + @Override + public void registerDataSetObserver(final DataSetObserver observer) { + treeStateManager.registerDataSetObserver(observer); + } + + @Override + public void unregisterDataSetObserver(final DataSetObserver observer) { + treeStateManager.unregisterDataSetObserver(observer); + } + + @Override + public int getCount() { + return treeStateManager.getVisibleCount(); + } + + @Override + public Object getItem(final int position) { + return getItemId(position); + } + + public T getTreeId(final int position) { + return treeStateManager.getVisibleList().get(position); + } + + public TreeNodeInfo getTreeNodeInfo(final int position) { + return treeStateManager.getNodeInfo(getTreeId(position)); + } + + @Override + public boolean hasStableIds() { // NOPMD + return true; + } + + @Override + public int getItemViewType(final int position) { + return getTreeNodeInfo(position).getLevel(); + } + + @Override + public int getViewTypeCount() { + return numberOfLevels; + } + + @Override + public boolean isEmpty() { + return getCount() == 0; + } + + @Override + public boolean areAllItemsEnabled() { // NOPMD + return true; + } + + @Override + public boolean isEnabled(final int position) { // NOPMD + return true; + } + + protected int getTreeListItemWrapperId() { + return R.layout.tree_list_item_wrapper; + } + + @Override + public final View getView(final int position, final View convertView, + final ViewGroup parent) { + final TreeNodeInfo nodeInfo = getTreeNodeInfo(position); + if (convertView == null) { + final LinearLayout layout = (LinearLayout) layoutInflater.inflate( + getTreeListItemWrapperId(), null); + return populateTreeItem(layout, getNewChildView(nodeInfo), + nodeInfo, true); + } else { + final LinearLayout linear = (LinearLayout) convertView; + final FrameLayout frameLayout = (FrameLayout) linear + .findViewById(R.id.treeview_list_item_frame); + final View childView = frameLayout.getChildAt(0); + updateView(childView, nodeInfo); + return populateTreeItem(linear, childView, nodeInfo, false); + } + } + + /** + * Called when new view is to be created. + * + * @param treeNodeInfo + * node info + * @return view that should be displayed as tree content + */ + public abstract View getNewChildView(TreeNodeInfo treeNodeInfo); + + /** + * Called when new view is going to be reused. You should update the view + * and fill it in with the data required to display the new information. You + * can also create a new view, which will mean that the old view will not be + * reused. + * + * @param view + * view that should be updated with the new values + * @param treeNodeInfo + * node info used to populate the view + * @return view to used as row indented content + */ + public abstract View updateView(View view, TreeNodeInfo treeNodeInfo); + + /** + * Retrieves background drawable for the node. + * + * @param treeNodeInfo + * node info + * @return drawable returned as background for the whole row. Might be null, + * then default background is used + */ + public Drawable getBackgroundDrawable(final TreeNodeInfo treeNodeInfo) { // NOPMD + return null; + } + + private Drawable getDrawableOrDefaultBackground(final Drawable r) { + if (r == null) { + return activity.getResources() + .getDrawable(R.drawable.list_selector_background).mutate(); + } else { + return r; + } + } + + public final LinearLayout populateTreeItem(final LinearLayout layout, + final View childView, final TreeNodeInfo nodeInfo, + final boolean newChildView) { + final Drawable individualRowDrawable = getBackgroundDrawable(nodeInfo); + layout.setBackgroundDrawable(individualRowDrawable == null ? getDrawableOrDefaultBackground(rowBackgroundDrawable) + : individualRowDrawable); + final LinearLayout.LayoutParams indicatorLayoutParams = new LinearLayout.LayoutParams( + calculateIndentation(nodeInfo), LayoutParams.FILL_PARENT); + final LinearLayout indicatorLayout = (LinearLayout) layout + .findViewById(R.id.treeview_list_item_image_layout); + indicatorLayout.setGravity(indicatorGravity); + indicatorLayout.setLayoutParams(indicatorLayoutParams); + final ImageView image = (ImageView) layout + .findViewById(R.id.treeview_list_item_image); + image.setImageDrawable(getDrawable(nodeInfo)); + image.setBackgroundDrawable(getDrawableOrDefaultBackground(indicatorBackgroundDrawable)); + image.setScaleType(ScaleType.CENTER); + image.setTag(nodeInfo.getId()); + if (nodeInfo.isWithChildren() && collapsible) { + image.setOnClickListener(indicatorClickListener); + } else { + image.setOnClickListener(null); + } + layout.setTag(nodeInfo.getId()); + final FrameLayout frameLayout = (FrameLayout) layout + .findViewById(R.id.treeview_list_item_frame); + final FrameLayout.LayoutParams childParams = new FrameLayout.LayoutParams( + LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); + if (newChildView) { + frameLayout.addView(childView, childParams); + } + frameLayout.setTag(nodeInfo.getId()); + return layout; + } + + protected int calculateIndentation(final TreeNodeInfo nodeInfo) { + return getIndentWidth() * (nodeInfo.getLevel() + (collapsible ? 1 : 0)); + } + + private Drawable getDrawable(final TreeNodeInfo nodeInfo) { + if (!nodeInfo.isWithChildren() || !collapsible) { + return getDrawableOrDefaultBackground(indicatorBackgroundDrawable); + } + if (nodeInfo.isExpanded()) { + return expandedDrawable; + } else { + return collapsedDrawable; + } + } + + public void setIndicatorGravity(final int indicatorGravity) { + this.indicatorGravity = indicatorGravity; + } + + public void setCollapsedDrawable(final Drawable collapsedDrawable) { + this.collapsedDrawable = collapsedDrawable; + calculateIndentWidth(); + } + + public void setExpandedDrawable(final Drawable expandedDrawable) { + this.expandedDrawable = expandedDrawable; + calculateIndentWidth(); + } + + public void setIndentWidth(final int indentWidth) { + this.indentWidth = indentWidth; + calculateIndentWidth(); + } + + public void setRowBackgroundDrawable(final Drawable rowBackgroundDrawable) { + this.rowBackgroundDrawable = rowBackgroundDrawable; + } + + public void setIndicatorBackgroundDrawable( + final Drawable indicatorBackgroundDrawable) { + this.indicatorBackgroundDrawable = indicatorBackgroundDrawable; + } + + public void setCollapsible(final boolean collapsible) { + this.collapsible = collapsible; + } + + public void refresh() { + treeStateManager.refresh(); + } + + private int getIndentWidth() { + return indentWidth; + } + + @SuppressWarnings("unchecked") + public void handleItemClick(final View view, final Object id) { + expandCollapse((T) id); + } + +} diff --git a/android/src/pl/polidea/treeview/InMemoryTreeNode.java b/android/src/pl/polidea/treeview/InMemoryTreeNode.java new file mode 100644 index 00000000..6035369f --- /dev/null +++ b/android/src/pl/polidea/treeview/InMemoryTreeNode.java @@ -0,0 +1,116 @@ +package pl.polidea.treeview; + +import java.io.Serializable; +import java.util.LinkedList; +import java.util.List; + +/** + * Node. It is package protected so that it cannot be used outside. + * + * @param + * type of the identifier used by the tree + */ +class InMemoryTreeNode implements Serializable { + private static final long serialVersionUID = 1L; + private final T id; + private final T parent; + private final int level; + private boolean visible = true; + private final List> children = new LinkedList>(); + private List childIdListCache = null; + + public InMemoryTreeNode(final T id, final T parent, final int level, + final boolean visible) { + super(); + this.id = id; + this.parent = parent; + this.level = level; + this.visible = visible; + } + + public int indexOf(final T id) { + return getChildIdList().indexOf(id); + } + + /** + * Cache is built lasily only if needed. The cache is cleaned on any + * structure change for that node!). + * + * @return list of ids of children + */ + public synchronized List getChildIdList() { + if (childIdListCache == null) { + childIdListCache = new LinkedList(); + for (final InMemoryTreeNode n : children) { + childIdListCache.add(n.getId()); + } + } + return childIdListCache; + } + + public boolean isVisible() { + return visible; + } + + public void setVisible(final boolean visible) { + this.visible = visible; + } + + public int getChildrenListSize() { + return children.size(); + } + + public synchronized InMemoryTreeNode add(final int index, final T child, + final boolean visible) { + childIdListCache = null; + // Note! top levell children are always visible (!) + final InMemoryTreeNode newNode = new InMemoryTreeNode(child, + getId(), getLevel() + 1, getId() == null ? true : visible); + children.add(index, newNode); + return newNode; + } + + /** + * Note. This method should technically return unmodifiable collection, but + * for performance reason on small devices we do not do it. + * + * @return children list + */ + public List> getChildren() { + return children; + } + + public synchronized void clearChildren() { + children.clear(); + childIdListCache = null; + } + + public synchronized void removeChild(final T child) { + final int childIndex = indexOf(child); + if (childIndex != -1) { + children.remove(childIndex); + childIdListCache = null; + } + } + + @Override + public String toString() { + return "InMemoryTreeNode [id=" + getId() + ", parent=" + getParent() + + ", level=" + getLevel() + ", visible=" + visible + + ", children=" + children + ", childIdListCache=" + + childIdListCache + "]"; + } + + T getId() { + return id; + } + + T getParent() { + return parent; + } + + int getLevel() { + return level; + } + +} \ No newline at end of file diff --git a/android/src/pl/polidea/treeview/InMemoryTreeStateManager.java b/android/src/pl/polidea/treeview/InMemoryTreeStateManager.java new file mode 100644 index 00000000..bae675bf --- /dev/null +++ b/android/src/pl/polidea/treeview/InMemoryTreeStateManager.java @@ -0,0 +1,374 @@ +package pl.polidea.treeview; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import android.database.DataSetObserver; + +/** + * In-memory manager of tree state. + * + * @param + * type of identifier + */ +public class InMemoryTreeStateManager implements TreeStateManager { + private static final long serialVersionUID = 1L; + private final Map> allNodes = new HashMap>(); + private final InMemoryTreeNode topSentinel = new InMemoryTreeNode( + null, null, -1, true); + private transient List visibleListCache = null; // lasy initialised + private transient List unmodifiableVisibleList = null; + private boolean visibleByDefault = true; + private final transient Set observers = new HashSet(); + + private synchronized void internalDataSetChanged() { + visibleListCache = null; + unmodifiableVisibleList = null; + for (final DataSetObserver observer : observers) { + observer.onChanged(); + } + } + + /** + * If true new nodes are visible by default. + * + * @param visibleByDefault + * if true, then newly added nodes are expanded by default + */ + public void setVisibleByDefault(final boolean visibleByDefault) { + this.visibleByDefault = visibleByDefault; + } + + private InMemoryTreeNode getNodeFromTreeOrThrow(final T id) { + if (id == null) { + throw new NodeNotInTreeException("(null)"); + } + final InMemoryTreeNode node = allNodes.get(id); + if (node == null) { + throw new NodeNotInTreeException(id.toString()); + } + return node; + } + + private InMemoryTreeNode getNodeFromTreeOrThrowAllowRoot(final T id) { + if (id == null) { + return topSentinel; + } + return getNodeFromTreeOrThrow(id); + } + + private void expectNodeNotInTreeYet(final T id) { + final InMemoryTreeNode node = allNodes.get(id); + if (node != null) { + throw new NodeAlreadyInTreeException(id.toString(), node.toString()); + } + } + + @Override + public synchronized TreeNodeInfo getNodeInfo(final T id) { + final InMemoryTreeNode node = getNodeFromTreeOrThrow(id); + final List> children = node.getChildren(); + boolean expanded = false; + if (!children.isEmpty() && children.get(0).isVisible()) { + expanded = true; + } + return new TreeNodeInfo(id, node.getLevel(), !children.isEmpty(), + node.isVisible(), expanded); + } + + @Override + public synchronized List getChildren(final T id) { + final InMemoryTreeNode node = getNodeFromTreeOrThrowAllowRoot(id); + return node.getChildIdList(); + } + + @Override + public synchronized T getParent(final T id) { + final InMemoryTreeNode node = getNodeFromTreeOrThrowAllowRoot(id); + return node.getParent(); + } + + private boolean getChildrenVisibility(final InMemoryTreeNode node) { + boolean visibility; + final List> children = node.getChildren(); + if (children.isEmpty()) { + visibility = visibleByDefault; + } else { + visibility = children.get(0).isVisible(); + } + return visibility; + } + + @Override + public synchronized void addBeforeChild(final T parent, final T newChild, + final T beforeChild) { + expectNodeNotInTreeYet(newChild); + final InMemoryTreeNode node = getNodeFromTreeOrThrowAllowRoot(parent); + final boolean visibility = getChildrenVisibility(node); + // top nodes are always expanded. + if (beforeChild == null) { + final InMemoryTreeNode added = node.add(0, newChild, visibility); + allNodes.put(newChild, added); + } else { + final int index = node.indexOf(beforeChild); + final InMemoryTreeNode added = node.add(index == -1 ? 0 : index, + newChild, visibility); + allNodes.put(newChild, added); + } + if (visibility) { + internalDataSetChanged(); + } + } + + @Override + public synchronized void addAfterChild(final T parent, final T newChild, + final T afterChild) { + expectNodeNotInTreeYet(newChild); + final InMemoryTreeNode node = getNodeFromTreeOrThrowAllowRoot(parent); + final boolean visibility = getChildrenVisibility(node); + if (afterChild == null) { + final InMemoryTreeNode added = node.add( + node.getChildrenListSize(), newChild, visibility); + allNodes.put(newChild, added); + } else { + final int index = node.indexOf(afterChild); + final InMemoryTreeNode added = node.add( + index == -1 ? node.getChildrenListSize() : index, newChild, + visibility); + allNodes.put(newChild, added); + } + if (visibility) { + internalDataSetChanged(); + } + } + + @Override + public synchronized void removeNodeRecursively(final T id) { + final InMemoryTreeNode node = getNodeFromTreeOrThrowAllowRoot(id); + final boolean visibleNodeChanged = removeNodeRecursively(node); + final T parent = node.getParent(); + final InMemoryTreeNode parentNode = getNodeFromTreeOrThrowAllowRoot(parent); + parentNode.removeChild(id); + if (visibleNodeChanged) { + internalDataSetChanged(); + } + } + + private boolean removeNodeRecursively(final InMemoryTreeNode node) { + boolean visibleNodeChanged = false; + for (final InMemoryTreeNode child : node.getChildren()) { + if (removeNodeRecursively(child)) { + visibleNodeChanged = true; + } + } + node.clearChildren(); + if (node.getId() != null) { + allNodes.remove(node.getId()); + if (node.isVisible()) { + visibleNodeChanged = true; + } + } + return visibleNodeChanged; + } + + private void setChildrenVisibility(final InMemoryTreeNode node, + final boolean visible, final boolean recursive) { + for (final InMemoryTreeNode child : node.getChildren()) { + child.setVisible(visible); + if (recursive) { + setChildrenVisibility(child, visible, true); + } + } + } + + @Override + public synchronized void expandDirectChildren(final T id) { + final InMemoryTreeNode node = getNodeFromTreeOrThrowAllowRoot(id); + setChildrenVisibility(node, true, false); + internalDataSetChanged(); + } + + @Override + public synchronized void expandEverythingBelow(final T id) { + final InMemoryTreeNode node = getNodeFromTreeOrThrowAllowRoot(id); + setChildrenVisibility(node, true, true); + internalDataSetChanged(); + } + + @Override + public synchronized void collapseChildren(final T id) { + final InMemoryTreeNode node = getNodeFromTreeOrThrowAllowRoot(id); + if (node == topSentinel) { + for (final InMemoryTreeNode n : topSentinel.getChildren()) { + setChildrenVisibility(n, false, true); + } + } else { + setChildrenVisibility(node, false, true); + } + internalDataSetChanged(); + } + + @Override + public synchronized T getNextSibling(final T id) { + final T parent = getParent(id); + final InMemoryTreeNode parentNode = getNodeFromTreeOrThrowAllowRoot(parent); + boolean returnNext = false; + for (final InMemoryTreeNode child : parentNode.getChildren()) { + if (returnNext) { + return child.getId(); + } + if (child.getId().equals(id)) { + returnNext = true; + } + } + return null; + } + + @Override + public synchronized T getPreviousSibling(final T id) { + final T parent = getParent(id); + final InMemoryTreeNode parentNode = getNodeFromTreeOrThrowAllowRoot(parent); + final T previousSibling = null; + for (final InMemoryTreeNode child : parentNode.getChildren()) { + if (child.getId().equals(id)) { + return previousSibling; + } + } + return null; + } + + @Override + public synchronized boolean isInTree(final T id) { + return allNodes.containsKey(id); + } + + @Override + public synchronized int getVisibleCount() { + return getVisibleList().size(); + } + + @Override + public synchronized List getVisibleList() { + T currentId = null; + if (visibleListCache == null) { + visibleListCache = new ArrayList(allNodes.size()); + do { + currentId = getNextVisible(currentId); + if (currentId == null) { + break; + } else { + visibleListCache.add(currentId); + } + } while (true); + } + if (unmodifiableVisibleList == null) { + unmodifiableVisibleList = Collections + .unmodifiableList(visibleListCache); + } + return unmodifiableVisibleList; + } + + public synchronized T getNextVisible(final T id) { + final InMemoryTreeNode node = getNodeFromTreeOrThrowAllowRoot(id); + if (!node.isVisible()) { + return null; + } + final List> children = node.getChildren(); + if (!children.isEmpty()) { + final InMemoryTreeNode firstChild = children.get(0); + if (firstChild.isVisible()) { + return firstChild.getId(); + } + } + final T sibl = getNextSibling(id); + if (sibl != null) { + return sibl; + } + T parent = node.getParent(); + do { + if (parent == null) { + return null; + } + final T parentSibling = getNextSibling(parent); + if (parentSibling != null) { + return parentSibling; + } + parent = getNodeFromTreeOrThrow(parent).getParent(); + } while (true); + } + + @Override + public synchronized void registerDataSetObserver( + final DataSetObserver observer) { + observers.add(observer); + } + + @Override + public synchronized void unregisterDataSetObserver( + final DataSetObserver observer) { + observers.remove(observer); + } + + @Override + public int getLevel(final T id) { + return getNodeFromTreeOrThrow(id).getLevel(); + } + + @Override + public Integer[] getHierarchyDescription(final T id) { + final int level = getLevel(id); + final Integer[] hierarchy = new Integer[level + 1]; + int currentLevel = level; + T currentId = id; + T parent = getParent(currentId); + while (currentLevel >= 0) { + hierarchy[currentLevel--] = getChildren(parent).indexOf(currentId); + currentId = parent; + parent = getParent(parent); + } + return hierarchy; + } + + private void appendToSb(final StringBuilder sb, final T id) { + if (id != null) { + final TreeNodeInfo node = getNodeInfo(id); + final int indent = node.getLevel() * 4; + final char[] indentString = new char[indent]; + Arrays.fill(indentString, ' '); + sb.append(indentString); + sb.append(node.toString()); + sb.append(Arrays.asList(getHierarchyDescription(id)).toString()); + sb.append("\n"); + } + final List children = getChildren(id); + for (final T child : children) { + appendToSb(sb, child); + } + } + + @Override + public synchronized String toString() { + final StringBuilder sb = new StringBuilder(); + appendToSb(sb, null); + return sb.toString(); + } + + @Override + public synchronized void clear() { + allNodes.clear(); + topSentinel.clearChildren(); + internalDataSetChanged(); + } + + @Override + public void refresh() { + internalDataSetChanged(); + } + +} diff --git a/android/src/pl/polidea/treeview/NodeAlreadyInTreeException.java b/android/src/pl/polidea/treeview/NodeAlreadyInTreeException.java new file mode 100644 index 00000000..680d0266 --- /dev/null +++ b/android/src/pl/polidea/treeview/NodeAlreadyInTreeException.java @@ -0,0 +1,14 @@ +package pl.polidea.treeview; + +/** + * The node being added is already in the tree. + * + */ +public class NodeAlreadyInTreeException extends RuntimeException { + private static final long serialVersionUID = 1L; + + public NodeAlreadyInTreeException(final String id, final String oldNode) { + super("The node has already been added to the tree: " + id + ". Old node is:" + oldNode); + } + +} diff --git a/android/src/pl/polidea/treeview/NodeNotInTreeException.java b/android/src/pl/polidea/treeview/NodeNotInTreeException.java new file mode 100644 index 00000000..f20450e1 --- /dev/null +++ b/android/src/pl/polidea/treeview/NodeNotInTreeException.java @@ -0,0 +1,15 @@ +package pl.polidea.treeview; + +/** + * This exception is thrown when the tree does not contain node requested. + * + */ +public class NodeNotInTreeException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public NodeNotInTreeException(final String id) { + super("The tree does not contain the node specified: " + id); + } + +} diff --git a/android/src/pl/polidea/treeview/TreeBuilder.java b/android/src/pl/polidea/treeview/TreeBuilder.java new file mode 100644 index 00000000..13b499c4 --- /dev/null +++ b/android/src/pl/polidea/treeview/TreeBuilder.java @@ -0,0 +1,126 @@ +package pl.polidea.treeview; + +import android.util.Log; + +/** + * Allows to build tree easily in sequential mode (you have to know levels of + * all the tree elements upfront). You should rather use this class rather than + * manager if you build initial tree from some external data source. + * + * @param + */ +public class TreeBuilder { + private static final String TAG = TreeBuilder.class.getSimpleName(); + + private final TreeStateManager manager; + + private T lastAddedId = null; + private int lastLevel = -1; + + public TreeBuilder(final TreeStateManager manager) { + this.manager = manager; + } + + public void clear() { + manager.clear(); + } + + /** + * Adds new relation to existing tree. Child is set as the last child of the + * parent node. Parent has to already exist in the tree, child cannot yet + * exist. This method is mostly useful in case you add entries layer by + * layer - i.e. first top level entries, then children for all parents, then + * grand-children and so on. + * + * @param parent + * parent id + * @param child + * child id + */ + public synchronized void addRelation(final T parent, final T child) { + Log.d(TAG, "Adding relation parent:" + parent + " -> child: " + child); + manager.addAfterChild(parent, child, null); + lastAddedId = child; + lastLevel = manager.getLevel(child); + } + + /** + * Adds sequentially new node. Using this method is the simplest way of + * building tree - if you have all the elements in the sequence as they + * should be displayed in fully-expanded tree. You can combine it with add + * relation - for example you can add information about few levels using + * {@link addRelation} and then after the right level is added as parent, + * you can continue adding them using sequential operation. + * + * @param id + * id of the node + * @param level + * its level + */ + public synchronized void sequentiallyAddNextNode(final T id, final int level) { + Log.d(TAG, "Adding sequentiall node " + id + " at level " + level); + if (lastAddedId == null) { + addNodeToParentOneLevelDown(null, id, level); + } else { + if (level <= lastLevel) { + final T parent = findParentAtLevel(lastAddedId, level - 1); + addNodeToParentOneLevelDown(parent, id, level); + } else { + addNodeToParentOneLevelDown(lastAddedId, id, level); + } + } + } + + /** + * Find parent of the node at the level specified. + * + * @param node + * node from which we start + * @param levelToFind + * level which we are looking for + * @return the node found (null if it is topmost node). + */ + private T findParentAtLevel(final T node, final int levelToFind) { + T parent = manager.getParent(node); + while (parent != null) { + if (manager.getLevel(parent) == levelToFind) { + break; + } + parent = manager.getParent(parent); + } + return parent; + } + + /** + * Adds note to parent at the level specified. But it verifies that the + * level is one level down than the parent! + * + * @param parent + * parent parent + * @param id + * new node id + * @param level + * should always be parent's level + 1 + */ + private void addNodeToParentOneLevelDown(final T parent, final T id, + final int level) { + if (parent == null && level != 0) { + throw new TreeConfigurationException("Trying to add new id " + id + + " to top level with level != 0 (" + level + ")"); + } + if (parent != null && manager.getLevel(parent) != level - 1) { + throw new TreeConfigurationException("Trying to add new id " + id + + " <" + level + "> to " + parent + " <" + + manager.getLevel(parent) + + ">. The difference in levels up is bigger than 1."); + } + manager.addAfterChild(parent, id, null); + setLastAdded(id, level); + } + + private void setLastAdded(final T id, final int level) { + lastAddedId = id; + lastLevel = level; + } + +} diff --git a/android/src/pl/polidea/treeview/TreeConfigurationException.java b/android/src/pl/polidea/treeview/TreeConfigurationException.java new file mode 100644 index 00000000..1fa72e04 --- /dev/null +++ b/android/src/pl/polidea/treeview/TreeConfigurationException.java @@ -0,0 +1,15 @@ +package pl.polidea.treeview; + +/** + * Exception thrown when there is a problem with configuring tree. + * + */ +public class TreeConfigurationException extends RuntimeException { + + private static final long serialVersionUID = 1L; + + public TreeConfigurationException(final String detailMessage) { + super(detailMessage); + } + +} diff --git a/android/src/pl/polidea/treeview/TreeNodeInfo.java b/android/src/pl/polidea/treeview/TreeNodeInfo.java new file mode 100644 index 00000000..32d18dd4 --- /dev/null +++ b/android/src/pl/polidea/treeview/TreeNodeInfo.java @@ -0,0 +1,69 @@ +package pl.polidea.treeview; + +/** + * Information about the node. + * + * @param + * type of the id for the tree + */ +public class TreeNodeInfo { + private final T id; + private final int level; + private final boolean withChildren; + private final boolean visible; + private final boolean expanded; + + /** + * Creates the node information. + * + * @param id + * id of the node + * @param level + * level of the node + * @param withChildren + * whether the node has children. + * @param visible + * whether the tree node is visible. + * @param expanded + * whether the tree node is expanded + * + */ + public TreeNodeInfo(final T id, final int level, + final boolean withChildren, final boolean visible, + final boolean expanded) { + super(); + this.id = id; + this.level = level; + this.withChildren = withChildren; + this.visible = visible; + this.expanded = expanded; + } + + public T getId() { + return id; + } + + public boolean isWithChildren() { + return withChildren; + } + + public boolean isVisible() { + return visible; + } + + public boolean isExpanded() { + return expanded; + } + + public int getLevel() { + return level; + } + + @Override + public String toString() { + return "TreeNodeInfo [id=" + id + ", level=" + level + + ", withChildren=" + withChildren + ", visible=" + visible + + ", expanded=" + expanded + "]"; + } + +} \ No newline at end of file diff --git a/android/src/pl/polidea/treeview/TreeStateManager.java b/android/src/pl/polidea/treeview/TreeStateManager.java new file mode 100644 index 00000000..781b70e5 --- /dev/null +++ b/android/src/pl/polidea/treeview/TreeStateManager.java @@ -0,0 +1,193 @@ +package pl.polidea.treeview; + +import java.io.Serializable; +import java.util.List; + +import android.database.DataSetObserver; + +/** + * Manages information about state of the tree. It only keeps information about + * tree elements, not the elements themselves. + * + * @param + * type of the identifier for nodes in the tree + */ +public interface TreeStateManager extends Serializable { + + /** + * Returns array of integers showing the location of the node in hierarchy. + * It corresponds to heading numbering. {0,0,0} in 3 level node is the first + * node {0,0,1} is second leaf (assuming that there are two leaves in first + * subnode of the first node). + * + * @param id + * id of the node + * @return textual description of the hierarchy in tree for the node. + */ + Integer[] getHierarchyDescription(T id); + + /** + * Returns level of the node. + * + * @param id + * id of the node + * @return level in the tree + */ + int getLevel(T id); + + /** + * Returns information about the node. + * + * @param id + * node id + * @return node info + */ + TreeNodeInfo getNodeInfo(T id); + + /** + * Returns children of the node. + * + * @param id + * id of the node or null if asking for top nodes + * @return children of the node + */ + List getChildren(T id); + + /** + * Returns parent of the node. + * + * @param id + * id of the node + * @return parent id or null if no parent + */ + T getParent(T id); + + /** + * Adds the node before child or at the beginning. + * + * @param parent + * id of the parent node. If null - adds at the top level + * @param newChild + * new child to add if null - adds at the beginning. + * @param beforeChild + * child before which to add the new child + */ + void addBeforeChild(T parent, T newChild, T beforeChild); + + /** + * Adds the node after child or at the end. + * + * @param parent + * id of the parent node. If null - adds at the top level. + * @param newChild + * new child to add. If null - adds at the end. + * @param afterChild + * child after which to add the new child + */ + void addAfterChild(T parent, T newChild, T afterChild); + + /** + * Removes the node and all children from the tree. + * + * @param id + * id of the node to remove or null if all nodes are to be + * removed. + */ + void removeNodeRecursively(T id); + + /** + * Expands all children of the node. + * + * @param id + * node which children should be expanded. cannot be null (top + * nodes are always expanded!). + */ + void expandDirectChildren(T id); + + /** + * Expands everything below the node specified. Might be null - then expands + * all. + * + * @param id + * node which children should be expanded or null if all nodes + * are to be expanded. + */ + void expandEverythingBelow(T id); + + /** + * Collapse children. + * + * @param id + * id collapses everything below node specified. If null, + * collapses everything but top-level nodes. + */ + void collapseChildren(T id); + + /** + * Returns next sibling of the node (or null if no further sibling). + * + * @param id + * node id + * @return the sibling (or null if no next) + */ + T getNextSibling(T id); + + /** + * Returns previous sibling of the node (or null if no previous sibling). + * + * @param id + * node id + * @return the sibling (or null if no previous) + */ + T getPreviousSibling(T id); + + /** + * Checks if given node is already in tree. + * + * @param id + * id of the node + * @return true if node is already in tree. + */ + boolean isInTree(T id); + + /** + * Count visible elements. + * + * @return number of currently visible elements. + */ + int getVisibleCount(); + + /** + * Returns visible node list. + * + * @return return the list of all visible nodes in the right sequence + */ + List getVisibleList(); + + /** + * Registers observers with the manager. + * + * @param observer + * observer + */ + void registerDataSetObserver(final DataSetObserver observer); + + /** + * Unregisters observers with the manager. + * + * @param observer + * observer + */ + void unregisterDataSetObserver(final DataSetObserver observer); + + /** + * Cleans tree stored in manager. After this operation the tree is empty. + * + */ + void clear(); + + /** + * Refreshes views connected to the manager. + */ + void refresh(); +} diff --git a/android/src/pl/polidea/treeview/TreeViewList.java b/android/src/pl/polidea/treeview/TreeViewList.java new file mode 100644 index 00000000..637a63cf --- /dev/null +++ b/android/src/pl/polidea/treeview/TreeViewList.java @@ -0,0 +1,199 @@ +package pl.polidea.treeview; + +import net.sf.openrocket.R; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import android.view.Gravity; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ListAdapter; +import android.widget.ListView; + +/** + * Tree view, expandable multi-level. + * + *
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_collapsible
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_src_expanded
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_src_collapsed
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_indent_width
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_handle_trackball_press
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_indicator_gravity
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_indicator_background
+ * attr ref pl.polidea.treeview.R.styleable#TreeViewList_row_background
+ * 
+ */ +public class TreeViewList extends ListView { + private static final int DEFAULT_COLLAPSED_RESOURCE = R.drawable.collapsed; + private static final int DEFAULT_EXPANDED_RESOURCE = R.drawable.expanded; + private static final int DEFAULT_INDENT = 0; + private static final int DEFAULT_GRAVITY = Gravity.LEFT + | Gravity.CENTER_VERTICAL; + private Drawable expandedDrawable; + private Drawable collapsedDrawable; + private Drawable rowBackgroundDrawable; + private Drawable indicatorBackgroundDrawable; + private int indentWidth = 0; + private int indicatorGravity = 0; + private AbstractTreeViewAdapter< ? > treeAdapter; + private boolean collapsible; + private boolean handleTrackballPress; + + public TreeViewList(final Context context, final AttributeSet attrs) { + this(context, attrs, R.style.treeViewListStyle); + } + + public TreeViewList(final Context context) { + this(context, null); + } + + public TreeViewList(final Context context, final AttributeSet attrs, + final int defStyle) { + super(context, attrs, defStyle); + parseAttributes(context, attrs); + } + + private void parseAttributes(final Context context, final AttributeSet attrs) { + final TypedArray a = context.obtainStyledAttributes(attrs, + R.styleable.TreeViewList); + expandedDrawable = a.getDrawable(R.styleable.TreeViewList_src_expanded); + if (expandedDrawable == null) { + expandedDrawable = context.getResources().getDrawable( + DEFAULT_EXPANDED_RESOURCE); + } + collapsedDrawable = a + .getDrawable(R.styleable.TreeViewList_src_collapsed); + if (collapsedDrawable == null) { + collapsedDrawable = context.getResources().getDrawable( + DEFAULT_COLLAPSED_RESOURCE); + } + indentWidth = a.getDimensionPixelSize( + R.styleable.TreeViewList_indent_width, DEFAULT_INDENT); + indicatorGravity = a.getInteger( + R.styleable.TreeViewList_indicator_gravity, DEFAULT_GRAVITY); + indicatorBackgroundDrawable = a + .getDrawable(R.styleable.TreeViewList_indicator_background); + rowBackgroundDrawable = a + .getDrawable(R.styleable.TreeViewList_row_background); + collapsible = a.getBoolean(R.styleable.TreeViewList_collapsible, true); + handleTrackballPress = a.getBoolean( + R.styleable.TreeViewList_handle_trackball_press, true); + } + + @Override + public void setAdapter(final ListAdapter adapter) { + if (!(adapter instanceof AbstractTreeViewAdapter)) { + throw new TreeConfigurationException( + "The adapter is not of TreeViewAdapter type"); + } + treeAdapter = (AbstractTreeViewAdapter< ? >) adapter; + syncAdapter(); + super.setAdapter(treeAdapter); + } + + private void syncAdapter() { + treeAdapter.setCollapsedDrawable(collapsedDrawable); + treeAdapter.setExpandedDrawable(expandedDrawable); + treeAdapter.setIndicatorGravity(indicatorGravity); + treeAdapter.setIndentWidth(indentWidth); + treeAdapter.setIndicatorBackgroundDrawable(indicatorBackgroundDrawable); + treeAdapter.setRowBackgroundDrawable(rowBackgroundDrawable); + treeAdapter.setCollapsible(collapsible); + if (handleTrackballPress) { + setOnItemClickListener(new OnItemClickListener() { + @Override + public void onItemClick(final AdapterView< ? > parent, + final View view, final int position, final long id) { + treeAdapter.handleItemClick(view, view.getTag()); + } + }); + } else { + setOnClickListener(null); + } + + } + + public void setExpandedDrawable(final Drawable expandedDrawable) { + this.expandedDrawable = expandedDrawable; + syncAdapter(); + treeAdapter.refresh(); + } + + public void setCollapsedDrawable(final Drawable collapsedDrawable) { + this.collapsedDrawable = collapsedDrawable; + syncAdapter(); + treeAdapter.refresh(); + } + + public void setRowBackgroundDrawable(final Drawable rowBackgroundDrawable) { + this.rowBackgroundDrawable = rowBackgroundDrawable; + syncAdapter(); + treeAdapter.refresh(); + } + + public void setIndicatorBackgroundDrawable( + final Drawable indicatorBackgroundDrawable) { + this.indicatorBackgroundDrawable = indicatorBackgroundDrawable; + syncAdapter(); + treeAdapter.refresh(); + } + + public void setIndentWidth(final int indentWidth) { + this.indentWidth = indentWidth; + syncAdapter(); + treeAdapter.refresh(); + } + + public void setIndicatorGravity(final int indicatorGravity) { + this.indicatorGravity = indicatorGravity; + syncAdapter(); + treeAdapter.refresh(); + } + + public void setCollapsible(final boolean collapsible) { + this.collapsible = collapsible; + syncAdapter(); + treeAdapter.refresh(); + } + + public void setHandleTrackballPress(final boolean handleTrackballPress) { + this.handleTrackballPress = handleTrackballPress; + syncAdapter(); + treeAdapter.refresh(); + } + + public Drawable getExpandedDrawable() { + return expandedDrawable; + } + + public Drawable getCollapsedDrawable() { + return collapsedDrawable; + } + + public Drawable getRowBackgroundDrawable() { + return rowBackgroundDrawable; + } + + public Drawable getIndicatorBackgroundDrawable() { + return indicatorBackgroundDrawable; + } + + public int getIndentWidth() { + return indentWidth; + } + + public int getIndicatorGravity() { + return indicatorGravity; + } + + public boolean isCollapsible() { + return collapsible; + } + + public boolean isHandleTrackballPress() { + return handleTrackballPress; + } + +} diff --git a/android/src/pl/polidea/treeview/license.txt b/android/src/pl/polidea/treeview/license.txt new file mode 100644 index 00000000..1fd37cff --- /dev/null +++ b/android/src/pl/polidea/treeview/license.txt @@ -0,0 +1,19 @@ +Android tree-view-list is 2-clause BSD licensed. + +http://code.google.com/p/tree-view-list-android/ + +Copyright (c) , +All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the +following conditions are met: + + Redistributions of source code must retain the above copyright notice, this list of conditions and + the following disclaimer. + Redistributions in binary form must reproduce the above copyright notice, this list of conditions and + the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, +INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/android/src/pl/polidea/treeview/package-info.java b/android/src/pl/polidea/treeview/package-info.java new file mode 100644 index 00000000..a622e091 --- /dev/null +++ b/android/src/pl/polidea/treeview/package-info.java @@ -0,0 +1,4 @@ +/** + * Provides expandable Tree View implementation. + */ +package pl.polidea.treeview; \ No newline at end of file -- 2.47.2