├── img
└── ganttchart.png
├── GanttChartFX
├── src
│ └── main
│ │ ├── java
│ │ ├── module-info.java
│ │ └── com
│ │ │ └── flyf
│ │ │ ├── App.java
│ │ │ ├── GanttChartActivity.java
│ │ │ └── GanttChartViewController.java
│ │ └── resources
│ │ └── com
│ │ └── flyf
│ │ ├── ganttchartview.fxml
│ │ └── default.css
└── pom.xml
├── LICENSE
└── README.md
/img/ganttchart.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hry625/GanttChartFX/HEAD/img/ganttchart.png
--------------------------------------------------------------------------------
/GanttChartFX/src/main/java/module-info.java:
--------------------------------------------------------------------------------
1 | module com.flyf {
2 | requires javafx.controls;
3 | requires javafx.fxml;
4 |
5 | opens com.flyf to javafx.fxml;
6 | exports com.flyf;
7 | }
--------------------------------------------------------------------------------
/GanttChartFX/src/main/resources/com/flyf/ganttchartview.fxml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2021 flyf
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/GanttChartFX/src/main/java/com/flyf/App.java:
--------------------------------------------------------------------------------
1 | package com.flyf;
2 |
3 | import javafx.application.Application;
4 | import javafx.fxml.FXMLLoader;
5 | import javafx.scene.Parent;
6 | import javafx.scene.Scene;
7 | import javafx.stage.Stage;
8 |
9 | import java.io.IOException;
10 |
11 | /**
12 | * JavaFX App
13 | */
14 | public class App extends Application {
15 |
16 | private static Scene scene;
17 |
18 | @Override
19 | public void start(Stage stage) throws IOException {
20 | scene = new Scene(loadFXML("ganttchartview"), 640, 480);
21 | scene.getStylesheets().add(getClass().getResource("default.css").toExternalForm());
22 |
23 | stage.setScene(scene);
24 | stage.show();
25 | }
26 |
27 | static void setRoot(String fxml) throws IOException {
28 | scene.setRoot(loadFXML(fxml));
29 | }
30 |
31 | private static Parent loadFXML(String fxml) throws IOException {
32 | FXMLLoader fxmlLoader = new FXMLLoader(App.class.getResource(fxml + ".fxml"));
33 | return fxmlLoader.load();
34 | }
35 |
36 | public static void main(String[] args) {
37 | launch();
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/GanttChartFX/src/main/resources/com/flyf/default.css:
--------------------------------------------------------------------------------
1 | .gantt-chart-week-column {
2 | -fx-text-fill: #000;
3 | -fx-font-weight: normal;
4 | -fx-font-size: 0.8em;
5 | }
6 |
7 | .gantt-chart-day-column {
8 | -fx-text-fill: #000;
9 | -fx-font-weight: normal;
10 | -fx-font-size: 0.7em;
11 | }
12 |
13 | .gantt-chart-day-column-weekend {
14 | -fx-background-color: #e9ecef;
15 | -fx-fill: #e56767;
16 | -fx-text-fill: #e56767;
17 | -fx-font-weight: normal;
18 | -fx-font-size: 0.8em;
19 | }
20 |
21 | .gantt-chart-day-column-weekday {
22 | -fx-background-color: #fff;
23 | -fx-text-fill: #3b4654;
24 | -fx-font-weight: normal;
25 | -fx-font-size: 0.8em;
26 | }
27 |
28 | .gantt-chart-cell-empty {
29 | -fx-background-color: #fff;
30 | }
31 |
32 | .gantt-chart-cell-holiday {
33 | -fx-background-color: #e56767;
34 | }
35 |
36 | .gantt-chart-cell {
37 | -fx-background-color: #e0effa;
38 | -fx-text-fill: #e0effa;
39 | }
40 |
41 | .gantt-chart-cell1 {
42 | -fx-background-color: #1f77b4;
43 | -fx-text-fill: #1f77b4;
44 | }
45 |
46 | .gantt-chart-cell2 {
47 | -fx-background-color: #2ca02c;
48 | -fx-text-fill: #2ca02c;
49 | }
50 |
51 | .gantt-chart-cell3 {
52 | -fx-background-color: #d62728;
53 | -fx-text-fill: #d62728;
54 | }
55 |
56 | .gantt-chart-cell4 {
57 | -fx-background-color: #8c564b;
58 | -fx-text-fill: #8c564b;
59 | }
60 |
61 | .gantt-chart-cell5 {
62 | -fx-background-color: #bcbd22;
63 | -fx-text-fill: #bcbd22;
64 | }
65 |
66 | .gantt-chart-cell6 {
67 | -fx-background-color: #17becf;
68 | -fx-text-fill: #17becf;
69 | }
70 |
71 | .gantt-chart-cell7 {
72 | -fx-background-color: #ff7f0e;
73 | -fx-text-fill: #ff7f0e;
74 | }
--------------------------------------------------------------------------------
/GanttChartFX/pom.xml:
--------------------------------------------------------------------------------
1 |
3 | 4.0.0
4 | com.flyf
5 | GanttChartFX
6 | 1.0-SNAPSHOT
7 |
8 | UTF-8
9 | 11
10 | 11
11 |
12 |
13 |
14 | org.openjfx
15 | javafx-controls
16 | 13
17 |
18 |
19 | org.openjfx
20 | javafx-fxml
21 | 13
22 |
23 |
24 |
25 |
26 |
27 | org.apache.maven.plugins
28 | maven-compiler-plugin
29 | 3.8.0
30 |
31 | 11
32 |
33 |
34 |
35 | org.openjfx
36 | javafx-maven-plugin
37 | 0.0.4
38 |
39 | com.flyf.App
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/GanttChartFX/src/main/java/com/flyf/GanttChartActivity.java:
--------------------------------------------------------------------------------
1 | package com.flyf;
2 |
3 | import java.time.LocalDate;
4 |
5 | public class GanttChartActivity {
6 | private String title;
7 | private LocalDate startDate;
8 | private LocalDate endDate;
9 | private int topLevelID;
10 | private int middleLevelID;
11 | private int bottomLevelID;
12 |
13 | public GanttChartActivity(String title, LocalDate startDate, LocalDate endDate, int topLevelID, int middleLevelID,
14 | int bottomLevelID) {
15 | this.title = title;
16 | this.startDate = startDate;
17 | this.endDate = endDate;
18 | this.topLevelID = topLevelID;
19 | this.middleLevelID = middleLevelID;
20 | this.bottomLevelID = bottomLevelID;
21 | }
22 |
23 | public String getTitle() {
24 | return title;
25 | }
26 |
27 | public void setTitle(String title) {
28 | this.title = title;
29 | }
30 |
31 | public LocalDate getStartDate() {
32 | return startDate;
33 | }
34 |
35 | public void setStartDate(LocalDate startDate) {
36 | this.startDate = startDate;
37 | }
38 |
39 | public LocalDate getEndDate() {
40 | return endDate;
41 | }
42 |
43 | public void setEndDate(LocalDate endDate) {
44 | this.endDate = endDate;
45 | }
46 |
47 | public int getTopLevelID() {
48 | return topLevelID;
49 | }
50 |
51 | public void setTopLevelID(int topLevelID) {
52 | this.topLevelID = topLevelID;
53 | }
54 |
55 | public int getMiddleLevelID() {
56 | return middleLevelID;
57 | }
58 |
59 | public void setMiddleLevelID(int middleLevelID) {
60 | this.middleLevelID = middleLevelID;
61 | }
62 |
63 | public int getBottomLevelID() {
64 | return bottomLevelID;
65 | }
66 |
67 | public void setBottomLevelID(int bottomLevelID) {
68 | this.bottomLevelID = bottomLevelID;
69 | }
70 |
71 | }
72 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GanttChartFX
2 | A GanttChart table written by JavaFX
3 | 
4 |
5 |
6 | ## How to modify the Gantt chart to fit your need:
7 | 1. You may want to change the projectActivity to fit your project.
8 | 2. Using convertActivityToGanttChartActivity() function to convert your own activity class to fit GanttChartActivity.
9 | 3. Modify the default.css file to style the Gantt chart as you prefer.
10 |
11 |
12 | ## Known issue
13 | You may find the date part of activity row becomes empty after you collapse and re-extend the children activity rows.
14 | I have no clue how to solve this problem.
15 | If someone figure out how to fix it, please let me know.
16 |
17 |
18 | ## Special Thanks
19 | For Altansukh Tumenjargal and his excellent work with the css file styling.
20 |
21 |
22 | # License
23 | MIT License
24 |
25 | Copyright (c) 2021 flyf
26 |
27 | Permission is hereby granted, free of charge, to any person obtaining a copy
28 | of this software and associated documentation files (the "Software"), to deal
29 | in the Software without restriction, including without limitation the rights
30 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
31 | copies of the Software, and to permit persons to whom the Software is
32 | furnished to do so, subject to the following conditions:
33 |
34 | The above copyright notice and this permission notice shall be included in all
35 | copies or substantial portions of the Software.
36 |
37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
38 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
39 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
40 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
41 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
42 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
43 | SOFTWARE.
--------------------------------------------------------------------------------
/GanttChartFX/src/main/java/com/flyf/GanttChartViewController.java:
--------------------------------------------------------------------------------
1 | package com.flyf;
2 |
3 | import java.net.URL;
4 | import java.time.DayOfWeek;
5 | import java.time.LocalDate;
6 | import java.time.format.DateTimeFormatter;
7 | import java.time.temporal.ChronoUnit;
8 | import java.time.temporal.WeekFields;
9 | import java.util.ArrayList;
10 | import java.util.Locale;
11 | import java.util.ResourceBundle;
12 |
13 | import javafx.fxml.FXML;
14 | import javafx.fxml.Initializable;
15 | import javafx.scene.control.Label;
16 | import javafx.scene.control.TreeItem;
17 | import javafx.scene.control.TreeTableCell;
18 | import javafx.scene.control.TreeTableColumn;
19 | import javafx.scene.control.TreeTableView;
20 | import javafx.scene.control.cell.TreeItemPropertyValueFactory;
21 | import javafx.scene.paint.Color;
22 | import javafx.util.Callback;
23 |
24 | public class GanttChartViewController implements Initializable {
25 | private static int columnCount = 0;
26 |
27 | ArrayList activityList = new ArrayList<>();
28 | ArrayList topLevelList;
29 | ArrayList midLevelList;
30 | ArrayList bottomLevelList;
31 | GanttChartActivity projectActivity;
32 | private static LocalDate calenderStartDate;
33 | private static LocalDate calenderEndDate;
34 | private static int numberOfCalenderDays;
35 |
36 | @FXML
37 | private Label titleLabel;
38 | @FXML
39 | private TreeTableView ganttChartTreeTableView;
40 |
41 | @Override
42 | public void initialize(URL arg0, ResourceBundle arg1) {
43 | // Create a place holder activity for the project.
44 | GanttChartActivity projectActivity = new GanttChartActivity("Project", LocalDate.of(2021, 01, 01),
45 | LocalDate.of(2021, 03, 01), 0, 0, 0);
46 |
47 | if (projectActivity.getStartDate().getDayOfWeek().equals(DayOfWeek.MONDAY)) {
48 | calenderStartDate = projectActivity.getStartDate();
49 | } else {
50 | calenderStartDate = projectActivity.getStartDate().with(DayOfWeek.MONDAY);
51 | }
52 | if (projectActivity.getEndDate().getDayOfWeek().equals(DayOfWeek.SUNDAY)) {
53 | calenderEndDate = projectActivity.getEndDate();
54 | } else {
55 | calenderEndDate = projectActivity.getEndDate().plusWeeks(1).with(DayOfWeek.SUNDAY);
56 | }
57 |
58 | numberOfCalenderDays = (int) ChronoUnit.DAYS.between(calenderStartDate, calenderEndDate) + 1;
59 |
60 | topLevelList = new ArrayList<>();
61 | midLevelList = new ArrayList<>();
62 | bottomLevelList = new ArrayList<>();
63 |
64 | TreeItem projectItem = new TreeItem<>(projectActivity);
65 | convertActivityToGanttChartActivity();
66 |
67 | for (GanttChartActivity topGanttChartActivity : topLevelList) {
68 | TreeItem topLevelItem = new TreeItem<>(topGanttChartActivity);
69 | for (GanttChartActivity middleGanttChartActivity : midLevelList) {
70 | if (middleGanttChartActivity.getTopLevelID() == topGanttChartActivity.getTopLevelID()) {
71 | TreeItem middleLevelItem = new TreeItem<>(middleGanttChartActivity);
72 | for (GanttChartActivity bottomGanttChartActivity : bottomLevelList) {
73 | if (middleGanttChartActivity.getTopLevelID() == bottomGanttChartActivity.getTopLevelID()
74 | && middleGanttChartActivity.getMiddleLevelID() == bottomGanttChartActivity
75 | .getMiddleLevelID()) {
76 | TreeItem bottomLevelItem = new TreeItem<>(bottomGanttChartActivity);
77 | middleLevelItem.getChildren().add(bottomLevelItem);
78 | middleLevelItem.setExpanded(true);
79 | }
80 | }
81 | topLevelItem.getChildren().add(middleLevelItem);
82 | topLevelItem.setExpanded(true);
83 | }
84 | }
85 | projectItem.getChildren().add(topLevelItem);
86 | projectItem.setExpanded(true);
87 |
88 | }
89 |
90 | TreeTableColumn treeTableColumn1 = new TreeTableColumn<>("Name");
91 | treeTableColumn1.setPrefWidth(180.0);
92 | treeTableColumn1.setMinWidth(180.0);
93 | TreeTableColumn treeTableColumn2 = new TreeTableColumn<>("Start Date");
94 | treeTableColumn2.setPrefWidth(80);
95 | treeTableColumn2.setMinWidth(80.0);
96 | TreeTableColumn treeTableColumn3 = new TreeTableColumn<>("End Date");
97 | treeTableColumn3.setPrefWidth(80.0);
98 | treeTableColumn3.setMinWidth(80.0);
99 |
100 | treeTableColumn1.setCellValueFactory(new TreeItemPropertyValueFactory<>("title"));
101 | treeTableColumn2.setCellValueFactory(new TreeItemPropertyValueFactory<>("startDate"));
102 | treeTableColumn3.setCellValueFactory(new TreeItemPropertyValueFactory<>("endDate"));
103 |
104 | ganttChartTreeTableView.getColumns().add(treeTableColumn1);
105 | ganttChartTreeTableView.getColumns().add(treeTableColumn2);
106 | ganttChartTreeTableView.getColumns().add(treeTableColumn3);
107 |
108 | LocalDate currentDate = calenderStartDate;
109 | TreeTableColumn currentWeekColumn;
110 | Locale locale = new Locale("EN");
111 | while (currentDate.isBefore(calenderEndDate)) {
112 |
113 | int currentWeek = currentDate.get(WeekFields.of(locale).weekOfYear());
114 | int currentYear = currentDate.getYear();
115 |
116 | String firstDayOfWeekString = currentDate.format(DateTimeFormatter.ofPattern("LLL d"));
117 |
118 | currentWeekColumn = new TreeTableColumn<>(
119 | currentYear + " Week " + (currentWeek) + " (" + firstDayOfWeekString + ")");
120 | currentWeekColumn.getStyleClass().add("gantt-chart-week-column");
121 |
122 | LocalDate tempDate = currentDate;
123 |
124 | for (int i = 0; i < 7; i++) {
125 |
126 | TreeTableColumn currentDateColumn = new TreeTableColumn<>(
127 | tempDate.format(DateTimeFormatter.ofPattern("dd")));
128 |
129 | if (tempDate.getDayOfWeek().equals(DayOfWeek.SATURDAY)) {
130 | currentDateColumn.getStyleClass().add("gantt-chart-day-column-weekend");
131 | } else if (tempDate.getDayOfWeek().equals(DayOfWeek.SUNDAY)) {
132 | currentDateColumn.getStyleClass().add("gantt-chart-day-column-weekend");
133 | currentDateColumn.setStyle("-fx-border-color: transparent red transparent transparent");
134 |
135 | } else {
136 | currentDateColumn.getStyleClass().add("gantt-chart-day-column-weekday");
137 | }
138 |
139 | currentWeekColumn.getColumns().add(currentDateColumn);
140 | currentDateColumn.setCellFactory(
141 | new Callback, TreeTableCell>() {
142 | @Override
143 | public TreeTableCell call(
144 | TreeTableColumn param) {
145 | return new TreeTableCell() {
146 |
147 | @Override
148 | protected void updateItem(String item, boolean empty) {
149 | setText(null);
150 | getStyleClass().clear();
151 | super.updateItem(item, empty);
152 | GanttChartActivity activity = getTreeTableRow().getItem();
153 | int columnIndex = columnCount % numberOfCalenderDays;
154 | if (activity != null) {
155 | int daysBeforeActivityStart = (int) ChronoUnit.DAYS
156 | .between(calenderStartDate, activity.getStartDate());
157 | int daysUntilActivityEnd = (int) ChronoUnit.DAYS.between(calenderStartDate,
158 | activity.getEndDate());
159 |
160 | int rowIndex = getTreeTableRow().getIndex() % 7 + 1;
161 |
162 | if (columnIndex >= daysBeforeActivityStart
163 | && columnIndex <= daysUntilActivityEnd) {
164 | int dayOffSets = (int) ChronoUnit.DAYS.between(calenderStartDate,
165 | projectActivity.getStartDate());
166 | setText(Integer.toString(columnIndex - dayOffSets));
167 | setTextFill(Color.GREEN);
168 | getStyleClass().add("gantt-chart-cell" + rowIndex);
169 |
170 | } else if (columnIndex % 7 == 6 || columnIndex % 7 == 5) {
171 |
172 | getStyleClass().add("gantt-chart-day-column-weekend");
173 |
174 | } else {
175 | getStyleClass().add("gantt-chart-day-column-weekday");
176 | }
177 | } else if (columnIndex % 7 == 6 || columnIndex % 7 == 5) {
178 | getStyleClass().add("gantt-chart-day-column-weekend");
179 |
180 | }
181 | columnCount++;
182 | if (columnCount == numberOfCalenderDays) {
183 | columnCount = 0;
184 | }
185 |
186 | }
187 |
188 | };
189 | }
190 | });
191 | tempDate = tempDate.plusDays(1);
192 | }
193 |
194 | ganttChartTreeTableView.getColumns().add(currentWeekColumn);
195 | currentDate = currentDate.plusDays(7);
196 | }
197 |
198 | ganttChartTreeTableView.setRoot(projectItem);
199 |
200 | }
201 |
202 | public void convertActivityToGanttChartActivity() {
203 | // fake data:
204 | GanttChartActivity activity1 = new GanttChartActivity("Test Activity", LocalDate.of(2021, 01, 02),
205 | LocalDate.of(2021, 02, 01), 1, 0, 0);
206 | GanttChartActivity activity2 = new GanttChartActivity("Test Activity", LocalDate.of(2021, 01, 02),
207 | LocalDate.of(2021, 01, 20), 1, 1, 0);
208 | GanttChartActivity activity3 = new GanttChartActivity("Test Activity", LocalDate.of(2021, 01, 02),
209 | LocalDate.of(2021, 01, 14), 1, 1, 1);
210 | GanttChartActivity activity4 = new GanttChartActivity("Test Activity", LocalDate.of(2021, 01, 15),
211 | LocalDate.of(2021, 02, 01), 1, 2, 0);
212 | GanttChartActivity activity5 = new GanttChartActivity("Test Activity", LocalDate.of(2021, 02, 02),
213 | LocalDate.of(2021, 02, 15), 1, 3, 0);
214 | GanttChartActivity activity6 = new GanttChartActivity("Test Activity", LocalDate.of(2021, 01, 02),
215 | LocalDate.of(2021, 02, 01), 2, 0, 0);
216 | GanttChartActivity activity7 = new GanttChartActivity("Test Activity", LocalDate.of(2021, 01, 02),
217 | LocalDate.of(2021, 02, 20), 3, 0, 0);
218 | activityList.add(activity1);
219 | activityList.add(activity2);
220 | activityList.add(activity3);
221 | activityList.add(activity4);
222 | activityList.add(activity5);
223 | activityList.add(activity6);
224 | activityList.add(activity7);
225 | // You could convert your activity class to the ganttchart activity in this
226 | // function
227 | // I use arraylist of GanttChartActivity for convinience, you can change
228 | // activitylist as an arraylist of your own activity class
229 | // I am using topLevelID, midLevelID and bottomLevelID to indicate the
230 | // relationship of different activities:
231 | // -Activity 1 1.0.0
232 | // --subActivity 1.1.0
233 | // ---grandChildActivity 1.1.1
234 |
235 | for (GanttChartActivity activity : activityList) {
236 | if (activity.getMiddleLevelID() == 0 && activity.getBottomLevelID() == 0) {
237 | topLevelList.add(activity);
238 | } else if (activity.getBottomLevelID() == 0) {
239 | midLevelList.add(activity);
240 | } else {
241 | bottomLevelList.add(activity);
242 | }
243 | }
244 |
245 | }
246 |
247 | }
248 |
--------------------------------------------------------------------------------