3131import com .sun .javafx .scene .control .skin .DatePickerSkin ;
3232
3333import de .doubleslash .keeptime .common .DateFormatter ;
34- import de .doubleslash .keeptime .common .FontProvider ;
3534import de .doubleslash .keeptime .common .Resources ;
3635import de .doubleslash .keeptime .common .Resources .RESOURCE ;
3736import de .doubleslash .keeptime .controller .Controller ;
3837import de .doubleslash .keeptime .exceptions .FXMLLoaderException ;
3938import de .doubleslash .keeptime .model .Model ;
4039import de .doubleslash .keeptime .model .Project ;
4140import de .doubleslash .keeptime .model .Work ;
41+ import de .doubleslash .keeptime .view .worktable .ProjectTableRow ;
42+ import de .doubleslash .keeptime .view .worktable .TableRow ;
43+ import de .doubleslash .keeptime .view .worktable .WorkTableRow ;
44+ import javafx .beans .property .ReadOnlyObjectWrapper ;
4245import javafx .event .ActionEvent ;
46+ import javafx .event .EventHandler ;
4347import javafx .fxml .FXML ;
4448import javafx .fxml .FXMLLoader ;
45- import javafx .geometry .Insets ;
46- import javafx .geometry .Pos ;
4749import javafx .scene .Node ;
4850import javafx .scene .canvas .Canvas ;
4951import javafx .scene .control .Button ;
5052import javafx .scene .control .ButtonType ;
51- import javafx .scene .control .ContextMenu ;
5253import javafx .scene .control .DateCell ;
5354import javafx .scene .control .DatePicker ;
5455import javafx .scene .control .Dialog ;
5556import javafx .scene .control .Label ;
56- import javafx .scene .control .MenuItem ;
57- import javafx .scene .control .ScrollPane ;
57+ import javafx .scene .control .TreeItem ;
58+ import javafx .scene .control .TreeTableCell ;
59+ import javafx .scene .control .TreeTableColumn ;
60+ import javafx .scene .control .TreeTableView ;
61+ import javafx .scene .control .cell .TreeItemPropertyValueFactory ;
5862import javafx .scene .input .Clipboard ;
5963import javafx .scene .input .ClipboardContent ;
6064import javafx .scene .layout .AnchorPane ;
6165import javafx .scene .layout .BorderPane ;
6266import javafx .scene .layout .GridPane ;
6367import javafx .scene .layout .HBox ;
6468import javafx .scene .shape .Circle ;
69+ import javafx .scene .text .Text ;
6570import javafx .stage .Stage ;
6671import javafx .util .Callback ;
6772
@@ -86,10 +91,7 @@ public class ReportController {
8691 private Label currentDayTimeLabel ;
8792
8893 @ FXML
89- private GridPane gridPane ;
90-
91- @ FXML
92- private ScrollPane scrollPane ;
94+ private TreeTableView <TableRow > workTableTreeView ;
9395
9496 @ FXML
9597 private AnchorPane reportRoot ;
@@ -113,120 +115,115 @@ public class ReportController {
113115 private void initialize () {
114116 LOG .info ("Init reportController" );
115117 currentReportDate = LocalDate .now ();
116-
118+ initTableView ();
117119 colorTimeLine = new ColorTimeLine (colorTimeLineCanvas );
118120 }
119121
122+ private void initTableView () {
123+ final TreeTableColumn <TableRow , TableRow > noteColumn = new TreeTableColumn <>("Notes" );
124+ noteColumn .setCellFactory (new Callback <TreeTableColumn <TableRow , TableRow >, TreeTableCell <TableRow , TableRow >>() {
125+ @ Override
126+ public TreeTableCell <TableRow , TableRow > call (final TreeTableColumn <TableRow , TableRow > column ) {
127+ return new TreeTableCell <TableRow , TableRow >() {
128+ @ Override
129+ protected void updateItem (final TableRow item , final boolean empty ) {
130+ super .updateItem (item , empty );
131+ if (item == null || empty ) {
132+ setGraphic (null );
133+ setText (null );
134+ } else {
135+ final Text text = new Text (item .getNotes ());
136+ text .wrappingWidthProperty ().bind (noteColumn .widthProperty ().subtract (35 ));
137+ text .setUnderline (item .isUnderlined ());
138+ this .setGraphic (text );
139+ }
140+ }
141+ };
142+ }
143+
144+ });
145+ noteColumn .setCellValueFactory (
146+ (final TreeTableColumn .CellDataFeatures <TableRow , TableRow > entry ) -> new ReadOnlyObjectWrapper <>(
147+ entry .getValue ().getValue ()));
148+ noteColumn .setMinWidth (200 );
149+ noteColumn .impl_setReorderable (false );
150+ this .workTableTreeView .getColumns ().add (noteColumn );
151+
152+ final TreeTableColumn <TableRow , String > timeRangeColumn = new TreeTableColumn <>("Timeslot" );
153+ timeRangeColumn .setCellValueFactory (new TreeItemPropertyValueFactory <TableRow , String >("timeRange" ));
154+ timeRangeColumn .setMinWidth (120 );
155+ timeRangeColumn .impl_setReorderable (false );
156+ this .workTableTreeView .getColumns ().add (timeRangeColumn );
157+
158+ final TreeTableColumn <TableRow , String > timeSumColumn = new TreeTableColumn <>("Duration" );
159+ timeSumColumn .setCellValueFactory (new TreeItemPropertyValueFactory <TableRow , String >("timeSum" ));
160+ timeSumColumn .setMinWidth (60 );
161+ timeSumColumn .impl_setReorderable (false );
162+ this .workTableTreeView .getColumns ().add (timeSumColumn );
163+
164+ final TreeTableColumn <TableRow , Button > buttonColumn = new TreeTableColumn <>("Controls" );
165+ buttonColumn .setCellValueFactory (new TreeItemPropertyValueFactory <TableRow , Button >("buttonBox" ));
166+ buttonColumn .setMinWidth (100 );
167+ buttonColumn .setSortable (false );
168+ buttonColumn .impl_setReorderable (false );
169+ this .workTableTreeView .getColumns ().add (buttonColumn );
170+
171+ workTableTreeView .setShowRoot (false );
172+
173+ }
174+
120175 private void updateReport (final LocalDate dateToShow ) {
121176 this .currentReportDate = dateToShow ;
122177 this .loadCalenderWidget ();
123178 reportRoot .requestFocus ();
124179
125- this .currentDayLabel .setText (DateFormatter .toDayDateString (dateToShow ));
126- final List <Work > currentWorkItems = model .getWorkRepository ().findByCreationDateOrderByStartTimeAsc (dateToShow );
180+ this .currentDayLabel .setText (DateFormatter .toDayDateString (this .currentReportDate ));
181+ final List <Work > currentWorkItems = model .getWorkRepository ()
182+ .findByCreationDateOrderByStartTimeAsc (this .currentReportDate );
127183
128184 colorTimeLine .update (currentWorkItems , controller .calcSeconds (currentWorkItems ));
129185
130186 final SortedSet <Project > workedProjectsSet = currentWorkItems .stream ().map (Work ::getProject )
131- .collect (Collectors .toCollection (() -> new TreeSet <>(Comparator .comparing (Project ::getName ))));
132-
133- this .gridPane .getChildren ().clear ();
134- this .gridPane .getRowConstraints ().clear ();
135- this .gridPane .getColumnConstraints ().get (0 ).setPrefWidth (300 );
187+ .collect (Collectors .toCollection (() -> new TreeSet <>(Comparator .comparing (Project ::getIndex ))));
136188
137- int rowIndex = 0 ;
138189 long currentWorkSeconds = 0 ;
139190 long currentSeconds = 0 ;
140191
141- for (final Project project : workedProjectsSet ) {
142- final Label projectName = new Label (project .getName ());
143- projectName .setFont (FontProvider .getBoldFont ());
144- projectName .setUnderline (project .isWork ());
145- final Circle circle = new Circle (5 , project .getColor ());
146-
147- final HBox projectNameHBox = new HBox ();
148- projectNameHBox .setAlignment (Pos .CENTER_LEFT );
149- projectNameHBox .setPadding (new Insets (0 , 0 , 0 , 5 ));
150- projectNameHBox .setSpacing (5 );
151-
152- projectNameHBox .getChildren ().add (circle );
153- projectNameHBox .getChildren ().add (projectName );
154-
155- this .gridPane .add (projectNameHBox , 0 , rowIndex );
192+ final TreeItem <TableRow > root = new TreeItem <>();
156193
194+ for (final Project project : workedProjectsSet ) {
157195 final List <Work > onlyCurrentProjectWork = currentWorkItems .stream ().filter (w -> w .getProject () == project )
158196 .collect (Collectors .toList ());
159197
160- final long todaysWorkSeconds = controller .calcSeconds (onlyCurrentProjectWork );
198+ final long projectWorkSeconds = controller .calcSeconds (onlyCurrentProjectWork );
161199
162- currentSeconds += todaysWorkSeconds ;
200+ currentSeconds += projectWorkSeconds ;
163201 if (project .isWork ()) {
164- currentWorkSeconds += todaysWorkSeconds ;
202+ currentWorkSeconds += projectWorkSeconds ;
165203 }
166204
167- final Label workedTimeLabel = new Label (DateFormatter .secondsToHHMMSS (todaysWorkSeconds ));
168- workedTimeLabel .setFont (FontProvider .getBoldFont ());
169- this .gridPane .add (workedTimeLabel , 2 , rowIndex );
170-
171- // text will be set later
172- final Button bProjectReport = createProjectReport ();
173- this .gridPane .add (bProjectReport , 1 , rowIndex );
174-
175- rowIndex ++;
176-
177- final ProjectReport pr = new ProjectReport (onlyCurrentProjectWork .size ());
178- for (int j = 0 ; j < onlyCurrentProjectWork .size (); j ++) {
179- final Work work = onlyCurrentProjectWork .get (j );
180- final String workedHours = DateFormatter
181- .secondsToHHMMSS (DateFormatter .getSecondsBewtween (work .getStartTime (), work .getEndTime ()));
182-
183- final String currentWorkNote = work .getNotes ();
184- pr .appendToWorkNotes (currentWorkNote );
185- final Label commentLabel = new Label (currentWorkNote );
186- commentLabel .setFont (FontProvider .getDefaultFont ());
187- commentLabel .setWrapText (true );
188- this .gridPane .add (commentLabel , 0 , rowIndex );
189-
190- final Label fromTillLabel = new Label (DateFormatter .toTimeString (work .getStartTime ()) + " - "
191- + DateFormatter .toTimeString (work .getEndTime ()));
192- fromTillLabel .setFont (FontProvider .getDefaultFont ());
193- fromTillLabel .setWrapText (true );
194- this .gridPane .add (fromTillLabel , 1 , rowIndex );
195-
196- final Label workedHoursLabel = new Label (workedHours );
197- workedHoursLabel .setFont (FontProvider .getDefaultFont ());
198- this .gridPane .add (workedHoursLabel , 2 , rowIndex );
205+ final HBox projectButtonBox = new HBox ();
206+ projectButtonBox .getChildren ().add (createProjectReportButton (onlyCurrentProjectWork ));
199207
200- final HBox clickDummy = new HBox ();
201- final ContextMenu contextMenu = new ContextMenu ();
202- final MenuItem editMenuItem = new MenuItem ("edit" );
208+ final Circle circle = new Circle (6 , project .getColor ());
203209
204- editMenuItem .setOnAction (e -> {
205- LOG .info (EDIT_WORK_DIALOG_TITLE );
206- final Dialog <Work > dialog = setupEditWorkDialog (work );
210+ final TreeItem <TableRow > projectRow = new TreeItem <>(
211+ new ProjectTableRow (project , projectWorkSeconds , projectButtonBox ), circle );
207212
208- final Optional <Work > result = dialog .showAndWait ();
209-
210- result .ifPresent (editedWork -> {
211- controller .editWork (work , editedWork );
212-
213- this .update ();
214- });
215- });
216-
217- contextMenu .getItems ().add (editMenuItem );
218-
219- clickDummy .setOnContextMenuRequested (
220- event -> contextMenu .show (clickDummy , event .getScreenX (), event .getScreenY ()));
213+ for (final Work w : onlyCurrentProjectWork ) {
214+ final HBox workButtonBox = new HBox ();
215+ workButtonBox .getChildren ().add (createEditWorkButton (w ));
216+ final TreeItem <TableRow > workRow = new TreeItem <>(new WorkTableRow (w , workButtonBox ));
217+ projectRow .getChildren ().add (workRow );
218+ }
221219
222- this .gridPane .add (clickDummy , 0 , rowIndex , 3 , 1 );
220+ projectRow .setExpanded (true );
221+ root .getChildren ().add (projectRow );
223222
224- rowIndex ++;
225- }
226- bProjectReport .setUserData (pr .getNotes (true ));
227223 }
228- this .scrollPane .setVvalue (0 ); // scroll to the top
229224
225+ root .setExpanded (true );
226+ workTableTreeView .setRoot (root );
230227 this .currentDayTimeLabel .setText (DateFormatter .secondsToHHMMSS (currentSeconds ));
231228 this .currentDayWorkTimeLabel .setText (DateFormatter .secondsToHHMMSS (currentWorkSeconds ));
232229
@@ -260,6 +257,23 @@ public void updateItem(final LocalDate item, final boolean empty) {
260257
261258 }
262259
260+ private Button createEditWorkButton (final Work work ) {
261+ final Button bProjectReport = new Button ("edit" );
262+ bProjectReport .setOnAction (e -> {
263+ LOG .info ("Edit work clicked." );
264+ final Dialog <Work > dialog = setupEditWorkDialog (work );
265+
266+ final Optional <Work > result = dialog .showAndWait ();
267+
268+ result .ifPresent (editedWork -> {
269+ controller .editWork (work , editedWork );
270+
271+ this .update ();
272+ });
273+ });
274+ return bProjectReport ;
275+ }
276+
263277 private Dialog <Work > setupEditWorkDialog (final Work work ) {
264278 final Dialog <Work > dialog = new Dialog <>();
265279 dialog .initOwner (stage );
@@ -295,27 +309,29 @@ private GridPane setUpEditWorkGridPane(final Work work, final Dialog<Work> dialo
295309 return grid ;
296310 }
297311
298- private Button createProjectReport ( ) {
312+ private Button createProjectReportButton ( final List < Work > projectWork ) {
299313 final Button bProjectReport = new Button ("Copy to clipboard" );
300-
301- bProjectReport .setOnAction ((final ActionEvent event ) -> {
302- final Object source = event .getSource ();
303- final Button btn = (Button ) source ;
304- final Object userData = btn .getUserData ();
305- final String notes = (String ) userData ;
306-
314+ final EventHandler <ActionEvent > eventListener = actionEvent -> {
315+ LOG .debug ("Copy to Clipboard clicked." );
316+ final ProjectReport pr = new ProjectReport (projectWork .size ());
317+ for (int j = 0 ; j < projectWork .size (); j ++) {
318+ final Work work = projectWork .get (j );
319+ final String currentWorkNote = work .getNotes ();
320+ pr .appendToWorkNotes (currentWorkNote );
321+ }
307322 final Clipboard clipboard = Clipboard .getSystemClipboard ();
308323 final ClipboardContent content = new ClipboardContent ();
309- content .putString (notes );
324+ content .putString (pr . getNotes ( true ) );
310325 clipboard .setContent (content );
311- });
326+ };
327+
328+ bProjectReport .setOnAction (eventListener );
312329 return bProjectReport ;
330+
313331 }
314332
315333 public void setModel (final Model model ) {
316334 this .model = model ;
317-
318- this .loadCalenderWidget ();
319335 }
320336
321337 public void update () {
0 commit comments