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 .Alert ;
5052import javafx .scene .control .Alert .AlertType ;
5153import javafx .scene .control .Button ;
5254import javafx .scene .control .ButtonType ;
53- import javafx .scene .control .ContextMenu ;
5455import javafx .scene .control .DateCell ;
5556import javafx .scene .control .DatePicker ;
5657import javafx .scene .control .Dialog ;
5758import javafx .scene .control .Label ;
58- import javafx .scene .control .MenuItem ;
59- import javafx .scene .control .ScrollPane ;
59+ import javafx .scene .control .TreeItem ;
60+ import javafx .scene .control .TreeTableCell ;
61+ import javafx .scene .control .TreeTableColumn ;
62+ import javafx .scene .control .TreeTableView ;
63+ import javafx .scene .control .cell .TreeItemPropertyValueFactory ;
6064import javafx .scene .input .Clipboard ;
6165import javafx .scene .input .ClipboardContent ;
6266import javafx .scene .layout .AnchorPane ;
6367import javafx .scene .layout .BorderPane ;
6468import javafx .scene .layout .GridPane ;
6569import javafx .scene .layout .HBox ;
6670import javafx .scene .shape .Circle ;
71+ import javafx .scene .text .Text ;
6772import javafx .stage .Stage ;
6873import javafx .util .Callback ;
6974
@@ -88,10 +93,7 @@ public class ReportController {
8893 private Label currentDayTimeLabel ;
8994
9095 @ FXML
91- private GridPane gridPane ;
92-
93- @ FXML
94- private ScrollPane scrollPane ;
96+ private TreeTableView <TableRow > workTableTreeView ;
9597
9698 @ FXML
9799 private AnchorPane reportRoot ;
@@ -115,142 +117,116 @@ public class ReportController {
115117 private void initialize () {
116118 LOG .info ("Init reportController" );
117119 currentReportDate = LocalDate .now ();
118-
120+ initTableView ();
119121 colorTimeLine = new ColorTimeLine (colorTimeLineCanvas );
120122 }
121123
124+ private void initTableView () {
125+ final TreeTableColumn <TableRow , TableRow > noteColumn = new TreeTableColumn <>("Notes" );
126+ noteColumn .setCellFactory (new Callback <TreeTableColumn <TableRow , TableRow >, TreeTableCell <TableRow , TableRow >>() {
127+ @ Override
128+ public TreeTableCell <TableRow , TableRow > call (final TreeTableColumn <TableRow , TableRow > column ) {
129+ return new TreeTableCell <TableRow , TableRow >() {
130+ @ Override
131+ protected void updateItem (final TableRow item , final boolean empty ) {
132+ super .updateItem (item , empty );
133+ if (item == null || empty ) {
134+ setGraphic (null );
135+ setText (null );
136+ } else {
137+ final Text text = new Text (item .getNotes ());
138+ text .wrappingWidthProperty ().bind (noteColumn .widthProperty ().subtract (35 ));
139+ text .setUnderline (item .isUnderlined ());
140+ this .setGraphic (text );
141+ }
142+ }
143+ };
144+ }
145+
146+ });
147+ noteColumn .setCellValueFactory (
148+ (final TreeTableColumn .CellDataFeatures <TableRow , TableRow > entry ) -> new ReadOnlyObjectWrapper <>(
149+ entry .getValue ().getValue ()));
150+ noteColumn .setMinWidth (200 );
151+ noteColumn .impl_setReorderable (false );
152+ this .workTableTreeView .getColumns ().add (noteColumn );
153+
154+ final TreeTableColumn <TableRow , String > timeRangeColumn = new TreeTableColumn <>("Timeslot" );
155+ timeRangeColumn .setCellValueFactory (new TreeItemPropertyValueFactory <TableRow , String >("timeRange" ));
156+ timeRangeColumn .setMinWidth (120 );
157+ timeRangeColumn .impl_setReorderable (false );
158+ this .workTableTreeView .getColumns ().add (timeRangeColumn );
159+
160+ final TreeTableColumn <TableRow , String > timeSumColumn = new TreeTableColumn <>("Duration" );
161+ timeSumColumn .setCellValueFactory (new TreeItemPropertyValueFactory <TableRow , String >("timeSum" ));
162+ timeSumColumn .setMinWidth (60 );
163+ timeSumColumn .impl_setReorderable (false );
164+ this .workTableTreeView .getColumns ().add (timeSumColumn );
165+
166+ final TreeTableColumn <TableRow , Button > buttonColumn = new TreeTableColumn <>("Controls" );
167+ buttonColumn .setCellValueFactory (new TreeItemPropertyValueFactory <TableRow , Button >("buttonBox" ));
168+ buttonColumn .setMinWidth (100 );
169+ buttonColumn .setSortable (false );
170+ buttonColumn .impl_setReorderable (false );
171+ this .workTableTreeView .getColumns ().add (buttonColumn );
172+
173+ workTableTreeView .setShowRoot (false );
174+
175+ }
176+
122177 private void updateReport (final LocalDate dateToShow ) {
123178 this .currentReportDate = dateToShow ;
124179 this .loadCalenderWidget ();
125180 reportRoot .requestFocus ();
126181
127- this .currentDayLabel .setText (DateFormatter .toDayDateString (dateToShow ));
128- final List <Work > currentWorkItems = model .getWorkRepository ().findByCreationDateOrderByStartTimeAsc (dateToShow );
182+ this .currentDayLabel .setText (DateFormatter .toDayDateString (this .currentReportDate ));
183+ final List <Work > currentWorkItems = model .getWorkRepository ()
184+ .findByCreationDateOrderByStartTimeAsc (this .currentReportDate );
129185
130186 colorTimeLine .update (currentWorkItems , controller .calcSeconds (currentWorkItems ));
131187
132188 final SortedSet <Project > workedProjectsSet = currentWorkItems .stream ().map (Work ::getProject )
133- .collect (Collectors .toCollection (() -> new TreeSet <>(Comparator .comparing (Project ::getName ))));
134-
135- this .gridPane .getChildren ().clear ();
136- this .gridPane .getRowConstraints ().clear ();
137- this .gridPane .getColumnConstraints ().get (0 ).setPrefWidth (300 );
189+ .collect (Collectors .toCollection (() -> new TreeSet <>(Comparator .comparing (Project ::getIndex ))));
138190
139- int rowIndex = 0 ;
140191 long currentWorkSeconds = 0 ;
141192 long currentSeconds = 0 ;
142193
143- for (final Project project : workedProjectsSet ) {
144- final Label projectName = new Label (project .getName ());
145- projectName .setFont (FontProvider .getBoldFont ());
146- projectName .setUnderline (project .isWork ());
147- final Circle circle = new Circle (5 , project .getColor ());
148-
149- final HBox projectNameHBox = new HBox ();
150- projectNameHBox .setAlignment (Pos .CENTER_LEFT );
151- projectNameHBox .setPadding (new Insets (0 , 0 , 0 , 5 ));
152- projectNameHBox .setSpacing (5 );
153-
154- projectNameHBox .getChildren ().add (circle );
155- projectNameHBox .getChildren ().add (projectName );
156-
157- this .gridPane .add (projectNameHBox , 0 , rowIndex );
194+ final TreeItem <TableRow > root = new TreeItem <>();
158195
196+ for (final Project project : workedProjectsSet ) {
159197 final List <Work > onlyCurrentProjectWork = currentWorkItems .stream ().filter (w -> w .getProject () == project )
160198 .collect (Collectors .toList ());
161199
162- final long todaysWorkSeconds = controller .calcSeconds (onlyCurrentProjectWork );
200+ final long projectWorkSeconds = controller .calcSeconds (onlyCurrentProjectWork );
163201
164- currentSeconds += todaysWorkSeconds ;
202+ currentSeconds += projectWorkSeconds ;
165203 if (project .isWork ()) {
166- currentWorkSeconds += todaysWorkSeconds ;
204+ currentWorkSeconds += projectWorkSeconds ;
167205 }
168206
169- final Label workedTimeLabel = new Label (DateFormatter .secondsToHHMMSS (todaysWorkSeconds ));
170- workedTimeLabel .setFont (FontProvider .getBoldFont ());
171- this .gridPane .add (workedTimeLabel , 2 , rowIndex );
172-
173- // text will be set later
174- final Button bProjectReport = createProjectReport ();
175- this .gridPane .add (bProjectReport , 1 , rowIndex );
176-
177- rowIndex ++;
178-
179- final ProjectReport pr = new ProjectReport (onlyCurrentProjectWork .size ());
180- for (int j = 0 ; j < onlyCurrentProjectWork .size (); j ++) {
181- final Work work = onlyCurrentProjectWork .get (j );
182- final String workedHours = DateFormatter
183- .secondsToHHMMSS (DateFormatter .getSecondsBewtween (work .getStartTime (), work .getEndTime ()));
184-
185- final String currentWorkNote = work .getNotes ();
186- pr .appendToWorkNotes (currentWorkNote );
187- final Label commentLabel = new Label (currentWorkNote );
188- commentLabel .setFont (FontProvider .getDefaultFont ());
189- commentLabel .setWrapText (true );
190- this .gridPane .add (commentLabel , 0 , rowIndex );
191-
192- final Label fromTillLabel = new Label (DateFormatter .toTimeString (work .getStartTime ()) + " - "
193- + DateFormatter .toTimeString (work .getEndTime ()));
194- fromTillLabel .setFont (FontProvider .getDefaultFont ());
195- fromTillLabel .setWrapText (true );
196- this .gridPane .add (fromTillLabel , 1 , rowIndex );
197-
198- final Label workedHoursLabel = new Label (workedHours );
199- workedHoursLabel .setFont (FontProvider .getDefaultFont ());
200- this .gridPane .add (workedHoursLabel , 2 , rowIndex );
201-
202- final HBox clickDummy = new HBox ();
203- final ContextMenu contextMenu = new ContextMenu ();
204- final MenuItem editMenuItem = new MenuItem ("edit" );
205-
206- editMenuItem .setOnAction (e -> {
207- LOG .info (EDIT_WORK_DIALOG_TITLE );
208- final Dialog <Work > dialog = setupEditWorkDialog (work );
209-
210- final Optional <Work > result = dialog .showAndWait ();
211-
212- result .ifPresent (editedWork -> {
213- controller .editWork (work , editedWork );
214-
215- this .update ();
216- });
217- });
218-
219- contextMenu .getItems ().add (editMenuItem );
207+ final HBox projectButtonBox = new HBox ();
208+ projectButtonBox .getChildren ().add (createProjectReportButton (onlyCurrentProjectWork ));
220209
221- final MenuItem deleteMenuItem = new MenuItem ( "delete" );
210+ final Circle circle = new Circle ( 6 , project . getColor () );
222211
223- deleteMenuItem .setOnAction (e -> {
224- final Alert alert = new Alert (AlertType .CONFIRMATION );
225- alert .setTitle ("Delete Work" );
226- alert .setHeaderText ("You are abaout too delete the work:\n " + work .toString ());
227- alert .setContentText ("Are you sure?" );
228- alert .initOwner (stage );
212+ final TreeItem <TableRow > projectRow = new TreeItem <>(
213+ new ProjectTableRow (project , projectWorkSeconds , projectButtonBox ), circle );
229214
230- final Optional <ButtonType > result = alert .showAndWait ();
231-
232- result .ifPresent (buttonType -> {
233- if (buttonType == ButtonType .OK ) {
234- controller .deleteWork (work );
235- }
236-
237- this .update ();
238- });
239- });
240-
241- contextMenu .getItems ().add (deleteMenuItem );
242-
243- clickDummy .setOnContextMenuRequested (
244- event -> contextMenu .show (clickDummy , event .getScreenX (), event .getScreenY ()));
215+ for (final Work w : onlyCurrentProjectWork ) {
216+ final HBox workButtonBox = new HBox ();
217+ workButtonBox .getChildren ().add (createEditWorkButton (w ));
218+ workButtonBox .getChildren ().add (createDeleteWorkButton (w ));
219+ final TreeItem <TableRow > workRow = new TreeItem <>(new WorkTableRow (w , workButtonBox ));
220+ projectRow .getChildren ().add (workRow );
221+ }
245222
246- this .gridPane .add (clickDummy , 0 , rowIndex , 3 , 1 );
223+ projectRow .setExpanded (true );
224+ root .getChildren ().add (projectRow );
247225
248- rowIndex ++;
249- }
250- bProjectReport .setUserData (pr .getNotes (true ));
251226 }
252- this .scrollPane .setVvalue (0 ); // scroll to the top
253227
228+ root .setExpanded (true );
229+ workTableTreeView .setRoot (root );
254230 this .currentDayTimeLabel .setText (DateFormatter .secondsToHHMMSS (currentSeconds ));
255231 this .currentDayWorkTimeLabel .setText (DateFormatter .secondsToHHMMSS (currentWorkSeconds ));
256232
@@ -284,6 +260,47 @@ public void updateItem(final LocalDate item, final boolean empty) {
284260
285261 }
286262
263+ private Button createDeleteWorkButton (final Work w ) {
264+
265+ // XXX Auto-generated method stub
266+ final Button editButton = new Button ("delete" );
267+ editButton .setOnAction (e -> {
268+ LOG .info ("Delete work clicked." );
269+ final Alert alert = new Alert (AlertType .CONFIRMATION );
270+ alert .setTitle ("Delete Work" );
271+ alert .setHeaderText ("You are about to delete this Work:" );
272+ alert .setContentText (w .toString ());
273+ alert .initOwner (stage );
274+
275+ final Optional <ButtonType > result = alert .showAndWait ();
276+
277+ result .ifPresent (buType -> {
278+ if (buType .equals (ButtonType .OK )) {
279+ controller .deleteWork (w );
280+ this .update ();
281+ }
282+ });
283+ });
284+ return editButton ;
285+ }
286+
287+ private Button createEditWorkButton (final Work work ) {
288+ final Button editButton = new Button ("edit" );
289+ editButton .setOnAction (e -> {
290+ LOG .info ("Edit work clicked." );
291+ final Dialog <Work > dialog = setupEditWorkDialog (work );
292+
293+ final Optional <Work > result = dialog .showAndWait ();
294+
295+ result .ifPresent (editedWork -> {
296+ controller .editWork (work , editedWork );
297+
298+ this .update ();
299+ });
300+ });
301+ return editButton ;
302+ }
303+
287304 private Dialog <Work > setupEditWorkDialog (final Work work ) {
288305 final Dialog <Work > dialog = new Dialog <>();
289306 dialog .initOwner (stage );
@@ -319,27 +336,29 @@ private GridPane setUpEditWorkGridPane(final Work work, final Dialog<Work> dialo
319336 return grid ;
320337 }
321338
322- private Button createProjectReport ( ) {
339+ private Button createProjectReportButton ( final List < Work > projectWork ) {
323340 final Button bProjectReport = new Button ("Copy to clipboard" );
324-
325- bProjectReport .setOnAction ((final ActionEvent event ) -> {
326- final Object source = event .getSource ();
327- final Button btn = (Button ) source ;
328- final Object userData = btn .getUserData ();
329- final String notes = (String ) userData ;
330-
341+ final EventHandler <ActionEvent > eventListener = actionEvent -> {
342+ LOG .debug ("Copy to Clipboard clicked." );
343+ final ProjectReport pr = new ProjectReport (projectWork .size ());
344+ for (int j = 0 ; j < projectWork .size (); j ++) {
345+ final Work work = projectWork .get (j );
346+ final String currentWorkNote = work .getNotes ();
347+ pr .appendToWorkNotes (currentWorkNote );
348+ }
331349 final Clipboard clipboard = Clipboard .getSystemClipboard ();
332350 final ClipboardContent content = new ClipboardContent ();
333- content .putString (notes );
351+ content .putString (pr . getNotes ( true ) );
334352 clipboard .setContent (content );
335- });
353+ };
354+
355+ bProjectReport .setOnAction (eventListener );
336356 return bProjectReport ;
357+
337358 }
338359
339360 public void setModel (final Model model ) {
340361 this .model = model ;
341-
342- this .loadCalenderWidget ();
343362 }
344363
345364 public void update () {
0 commit comments