Skip to content

Commit ceff03d

Browse files
author
Jan Schraff
committed
Merge branch 'develop' into feature/delete_work_item
2 parents 1a7bd00 + cd1c5fa commit ceff03d

File tree

7 files changed

+275
-172
lines changed

7 files changed

+275
-172
lines changed

src/main/java/de/doubleslash/keeptime/view/ReportController.java

Lines changed: 146 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -31,39 +31,44 @@
3131
import com.sun.javafx.scene.control.skin.DatePickerSkin;
3232

3333
import de.doubleslash.keeptime.common.DateFormatter;
34-
import de.doubleslash.keeptime.common.FontProvider;
3534
import de.doubleslash.keeptime.common.Resources;
3635
import de.doubleslash.keeptime.common.Resources.RESOURCE;
3736
import de.doubleslash.keeptime.controller.Controller;
3837
import de.doubleslash.keeptime.exceptions.FXMLLoaderException;
3938
import de.doubleslash.keeptime.model.Model;
4039
import de.doubleslash.keeptime.model.Project;
4140
import 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;
4245
import javafx.event.ActionEvent;
46+
import javafx.event.EventHandler;
4347
import javafx.fxml.FXML;
4448
import javafx.fxml.FXMLLoader;
45-
import javafx.geometry.Insets;
46-
import javafx.geometry.Pos;
4749
import javafx.scene.Node;
4850
import javafx.scene.canvas.Canvas;
4951
import javafx.scene.control.Alert;
5052
import javafx.scene.control.Alert.AlertType;
5153
import javafx.scene.control.Button;
5254
import javafx.scene.control.ButtonType;
53-
import javafx.scene.control.ContextMenu;
5455
import javafx.scene.control.DateCell;
5556
import javafx.scene.control.DatePicker;
5657
import javafx.scene.control.Dialog;
5758
import 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;
6064
import javafx.scene.input.Clipboard;
6165
import javafx.scene.input.ClipboardContent;
6266
import javafx.scene.layout.AnchorPane;
6367
import javafx.scene.layout.BorderPane;
6468
import javafx.scene.layout.GridPane;
6569
import javafx.scene.layout.HBox;
6670
import javafx.scene.shape.Circle;
71+
import javafx.scene.text.Text;
6772
import javafx.stage.Stage;
6873
import 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

Comments
 (0)