diff --git a/owlplug-client/src/main/java/com/owlplug/core/components/CoreTaskFactory.java b/owlplug-client/src/main/java/com/owlplug/core/components/CoreTaskFactory.java index fde6e9e4..ce0402f7 100644 --- a/owlplug-client/src/main/java/com/owlplug/core/components/CoreTaskFactory.java +++ b/owlplug-client/src/main/java/com/owlplug/core/components/CoreTaskFactory.java @@ -18,11 +18,13 @@ package com.owlplug.core.components; +import com.owlplug.core.dao.FileStatDAO; import com.owlplug.core.dao.PluginDAO; import com.owlplug.core.dao.PluginFootprintDAO; import com.owlplug.core.dao.SymlinkDAO; import com.owlplug.core.model.Plugin; import com.owlplug.core.services.NativeHostService; +import com.owlplug.core.tasks.FileSyncTask; import com.owlplug.core.tasks.PluginRemoveTask; import com.owlplug.core.tasks.PluginSyncTask; import com.owlplug.core.tasks.SimpleEventListener; @@ -30,6 +32,8 @@ import com.owlplug.core.tasks.plugins.discovery.PluginSyncTaskParameters; import com.owlplug.core.utils.FileUtils; import java.util.ArrayList; +import java.util.Set; +import java.util.TreeSet; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -49,6 +53,9 @@ public class CoreTaskFactory extends BaseTaskFactory { @Autowired private NativeHostService nativeHostService; + @Autowired + private FileStatDAO fileStatDAO; + private ArrayList syncPluginsListeners = new ArrayList<>(); @@ -89,7 +96,7 @@ public TaskExecutionContext createPluginSyncTask(String directoryScope) { parameters.setDirectoryScope(FileUtils.convertPath(directoryScope)); } - PluginSyncTask task = new PluginSyncTask(parameters, + PluginSyncTask task = new PluginSyncTask(parameters, pluginDAO, pluginFootprintDAO, symlinkDAO, @@ -97,9 +104,43 @@ public TaskExecutionContext createPluginSyncTask(String directoryScope) { task.setOnSucceeded(e -> { notifyListeners(syncPluginsListeners); + if (directoryScope != null) { + createFileStatSyncTask(directoryScope).scheduleNow(); + } else { + createFileStatSyncTask().scheduleNow(); + } }); return create(task); } + + public TaskExecutionContext createFileStatSyncTask() { + Set directorySet = new TreeSet<>(); + + if (prefs.getBoolean(ApplicationDefaults.VST2_DISCOVERY_ENABLED_KEY, false)) { + directorySet.add(prefs.get(ApplicationDefaults.VST_DIRECTORY_KEY, "")); + directorySet.addAll(prefs.getList(ApplicationDefaults.VST2_EXTRA_DIRECTORY_KEY)); + } + if (prefs.getBoolean(ApplicationDefaults.VST3_DISCOVERY_ENABLED_KEY, false)) { + directorySet.add(prefs.get(ApplicationDefaults.VST3_DIRECTORY_KEY, "")); + directorySet.addAll(prefs.getList(ApplicationDefaults.VST3_EXTRA_DIRECTORY_KEY)); + } + if (prefs.getBoolean(ApplicationDefaults.AU_DISCOVERY_ENABLED_KEY, false)) { + directorySet.add(prefs.get(ApplicationDefaults.AU_DIRECTORY_KEY, "")); + directorySet.addAll(prefs.getList(ApplicationDefaults.AU_EXTRA_DIRECTORY_KEY)); + } + if (prefs.getBoolean(ApplicationDefaults.LV2_DISCOVERY_ENABLED_KEY, false)) { + directorySet.add(prefs.get(ApplicationDefaults.LV2_DIRECTORY_KEY, "")); + directorySet.addAll(prefs.getList(ApplicationDefaults.LV2_EXTRA_DIRECTORY_KEY)); + } + FileSyncTask task = new FileSyncTask(fileStatDAO, directorySet.stream().toList()); + + return create(task); + } + + public TaskExecutionContext createFileStatSyncTask(String directoryScope) { + FileSyncTask task = new FileSyncTask(fileStatDAO, directoryScope); + return create(task); + } /** diff --git a/owlplug-client/src/main/java/com/owlplug/core/controllers/DirectoryInfoController.java b/owlplug-client/src/main/java/com/owlplug/core/controllers/DirectoryInfoController.java index 9312b12c..732d460e 100644 --- a/owlplug-client/src/main/java/com/owlplug/core/controllers/DirectoryInfoController.java +++ b/owlplug-client/src/main/java/com/owlplug/core/controllers/DirectoryInfoController.java @@ -23,14 +23,28 @@ import com.jfoenix.controls.JFXDialogLayout; import com.jfoenix.controls.JFXListView; import com.owlplug.core.components.CoreTaskFactory; +import com.owlplug.core.dao.FileStatDAO; +import com.owlplug.core.model.FileStat; import com.owlplug.core.model.Plugin; import com.owlplug.core.model.PluginDirectory; import com.owlplug.core.tasks.DirectoryRemoveTask; +import com.owlplug.core.ui.DoughnutChart; import com.owlplug.core.ui.PluginListCellFactory; +import com.owlplug.core.utils.FileUtils; import com.owlplug.core.utils.PlatformUtils; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; import javafx.fxml.FXML; +import javafx.scene.Node; +import javafx.scene.chart.PieChart; import javafx.scene.control.Label; import javafx.scene.image.ImageView; +import javafx.scene.layout.Priority; +import javafx.scene.layout.VBox; +import javafx.scene.text.Text; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; @@ -39,15 +53,23 @@ public class DirectoryInfoController extends BaseController { @Autowired private CoreTaskFactory taskFactory; + @Autowired + private FileStatDAO fileStatDAO; @FXML private Label directoryPathLabel; @FXML + private Label directoryMetricsLabel; + @FXML private JFXListView pluginDirectoryListView; @FXML private JFXButton openDirectoryButton; @FXML private JFXButton deleteDirectoryButton; + @FXML + private VBox pieChartContainer; + + private PieChart pieChart; private PluginDirectory pluginDirectory; @@ -56,7 +78,6 @@ public class DirectoryInfoController extends BaseController { */ public void initialize() { - openDirectoryButton.setGraphic(new ImageView(this.getApplicationDefaults().directoryImage)); openDirectoryButton.setOnAction(e -> { PlatformUtils.openDirectoryExplorer(pluginDirectory.getPath()); }); @@ -82,7 +103,7 @@ public void initialize() { dialog.close(); taskFactory.create(new DirectoryRemoveTask(pluginDirectory)) .setOnSucceeded(x -> taskFactory.createPluginSyncTask(pluginDirectory.getPath()).schedule()) - .schedule(); + .schedule(); }); removeButton.getStyleClass().add("button-danger"); @@ -90,12 +111,88 @@ public void initialize() { dialog.setContent(layout); dialog.show(); }); + + pieChart = new DoughnutChart() { + @Override + protected void layoutChartChildren(double top, double left, double contentWidth, double contentHeight) { + if (getLabelsVisible()) { + getData().forEach(d -> { + Optional opTextNode = this.lookupAll(".chart-pie-label").stream().filter( + n -> n instanceof Text && ((Text) n).getText().equals(d.getName())).findAny(); + if (opTextNode.isPresent()) { + String label = ellipsisString(d.getName(), 15, 3) + + " - " + FileUtils.humanReadableByteCount((long) d.getPieValue(), true); + ((Text) opTextNode.get()).setText(label); + } + }); + } + super.layoutChartChildren(top, left, contentWidth, contentHeight); + } + }; + + pieChartContainer.getChildren().add(pieChart); + VBox.setVgrow(pieChart, Priority.ALWAYS); + } public void setPluginDirectory(PluginDirectory pluginDirectory) { this.pluginDirectory = pluginDirectory; directoryPathLabel.setText(pluginDirectory.getPath()); pluginDirectoryListView.getItems().setAll(pluginDirectory.getPluginList()); + + + String path = pluginDirectory.getPath(); + if (path.endsWith("/")) { + path = path.substring(0, path.length() - 1); + } + + List directoryMetrics = new ArrayList<>(); + Optional directoryStat = fileStatDAO.findByPath(path); + directoryStat.ifPresent(fileStat -> directoryMetrics.add( + FileUtils.humanReadableByteCount(fileStat.getLength(), true))); + directoryMetrics.add(pluginDirectory.getPluginList().size() + " plugin(s)"); + + List fileStats = fileStatDAO.findByParentPathOrderByLengthDesc(path); + if (fileStats.size() > 0) { + directoryMetrics.add(fileStats.size() + " file(s)"); + } + + pieChart.setData(createStatChartBuckets(fileStats)); + pieChart.layout(); + + directoryMetricsLabel.setText(String.join(" | ", directoryMetrics)); + + } + + private ObservableList createStatChartBuckets(List fileStats) { + ObservableList chartData = FXCollections.observableArrayList(); + int i = 0; + int maxBucket = 7; + while (i < fileStats.size() && i < maxBucket) { + chartData.add(new PieChart.Data(fileStats.get(i).getName(), fileStats.get(i).getLength())); + i = i + 1; + } + + if (i < fileStats.size()) { + long groupLength = 0; + while (i < fileStats.size()) { + groupLength += fileStats.get(i).getLength(); + i = i + 1; + } + chartData.add(new PieChart.Data("Others", groupLength)); + } + + return chartData; + } + + private String ellipsisString(String input, int maxLength, int clearEndLength) { + if (input == null || input.length() <= maxLength + || clearEndLength >= maxLength) { + return input; + } else { + String truncatedString = input.substring(0, maxLength - clearEndLength); + return truncatedString + "..." + input.substring(input.length() - clearEndLength); + } } } diff --git a/owlplug-client/src/main/java/com/owlplug/core/dao/FileStatDAO.java b/owlplug-client/src/main/java/com/owlplug/core/dao/FileStatDAO.java new file mode 100644 index 00000000..67b37e75 --- /dev/null +++ b/owlplug-client/src/main/java/com/owlplug/core/dao/FileStatDAO.java @@ -0,0 +1,36 @@ +/* OwlPlug + * Copyright (C) 2021 Arthur + * + * This file is part of OwlPlug. + * + * OwlPlug is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 + * as published by the Free Software Foundation. + * + * OwlPlug is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OwlPlug. If not, see . + */ + +package com.owlplug.core.dao; + +import com.owlplug.core.model.FileStat; +import jakarta.transaction.Transactional; +import java.util.List; +import java.util.Optional; +import org.springframework.data.repository.CrudRepository; + +public interface FileStatDAO extends CrudRepository { + + Optional findByPath(String path); + + List findByParentPathOrderByLengthDesc(String parentPath); + + @Transactional + List deleteByPath(String path); + +} diff --git a/owlplug-client/src/main/java/com/owlplug/core/model/FileStat.java b/owlplug-client/src/main/java/com/owlplug/core/model/FileStat.java new file mode 100644 index 00000000..12a0428c --- /dev/null +++ b/owlplug-client/src/main/java/com/owlplug/core/model/FileStat.java @@ -0,0 +1,105 @@ +/* OwlPlug + * Copyright (C) 2021 Arthur + * + * This file is part of OwlPlug. + * + * OwlPlug is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 + * as published by the Free Software Foundation. + * + * OwlPlug is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OwlPlug. If not, see . + */ + +package com.owlplug.core.model; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import jakarta.persistence.OneToMany; +import java.util.HashSet; +import java.util.Set; + +@Entity +public class FileStat { + + @Id + @GeneratedValue(strategy = GenerationType.AUTO) + private Long id; + + private String name; + + private String path; + + @ManyToOne + @JoinColumn(name = "parent_id") + private FileStat parent; + + private String parentPath; + private long length; + + @OneToMany(mappedBy = "parent", cascade = CascadeType.ALL) + private Set childs = new HashSet<>(); + + public Long getId() { + return id; + } + + public void setId(Long id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public FileStat getParent() { + return parent; + } + + public void setParent(FileStat parent) { + this.parent = parent; + } + + public String getParentPath() { + return parentPath; + } + + public void setParentPath(String parentPath) { + this.parentPath = parentPath; + } + + public long getLength() { + return length; + } + + public void setLength(long length) { + this.length = length; + } + + public Set getChilds() { + return childs; + } + +} diff --git a/owlplug-client/src/main/java/com/owlplug/core/services/OptionsService.java b/owlplug-client/src/main/java/com/owlplug/core/services/OptionsService.java index c1767fcd..ff8592e6 100644 --- a/owlplug-client/src/main/java/com/owlplug/core/services/OptionsService.java +++ b/owlplug-client/src/main/java/com/owlplug/core/services/OptionsService.java @@ -23,6 +23,7 @@ import com.owlplug.core.components.ApplicationDefaults; import com.owlplug.core.components.ApplicationPreferences; import com.owlplug.core.components.ImageCache; +import com.owlplug.core.dao.FileStatDAO; import com.owlplug.core.dao.PluginDAO; import com.owlplug.core.model.PluginFormat; import com.owlplug.core.model.platform.OperatingSystem; @@ -51,6 +52,8 @@ public class OptionsService extends BaseService { @Autowired private RemotePackageDAO productDAO; @Autowired + private FileStatDAO fileStatDAO; + @Autowired private ImageCache imageCache; @PostConstruct @@ -103,6 +106,7 @@ public boolean clearAllUserData() { userAccountDAO.deleteAll(); productDAO.deleteAll(); remoteSourceDAO.deleteAll(); + fileStatDAO.deleteAll(); clearCache(); diff --git a/owlplug-client/src/main/java/com/owlplug/core/services/PluginService.java b/owlplug-client/src/main/java/com/owlplug/core/services/PluginService.java index 80d1c94f..1541fbec 100644 --- a/owlplug-client/src/main/java/com/owlplug/core/services/PluginService.java +++ b/owlplug-client/src/main/java/com/owlplug/core/services/PluginService.java @@ -56,6 +56,10 @@ public void syncPlugins() { taskFactory.createPluginSyncTask().schedule(); } + public void syncFiles() { + taskFactory.createFileStatSyncTask().schedule(); + } + public Iterable getAllPlugins() { return pluginDAO.findAll(); } diff --git a/owlplug-client/src/main/java/com/owlplug/core/tasks/FileSyncTask.java b/owlplug-client/src/main/java/com/owlplug/core/tasks/FileSyncTask.java new file mode 100644 index 00000000..dfef6beb --- /dev/null +++ b/owlplug-client/src/main/java/com/owlplug/core/tasks/FileSyncTask.java @@ -0,0 +1,113 @@ +/* OwlPlug + * Copyright (C) 2021 Arthur + * + * This file is part of OwlPlug. + * + * OwlPlug is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 + * as published by the Free Software Foundation. + * + * OwlPlug is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OwlPlug. If not, see . + */ + +package com.owlplug.core.tasks; + +import com.owlplug.core.dao.FileStatDAO; +import com.owlplug.core.model.FileStat; +import com.owlplug.core.utils.FileUtils; +import java.io.File; +import java.util.Arrays; +import java.util.List; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class FileSyncTask extends AbstractTask { + + private final Logger log = LoggerFactory.getLogger(this.getClass()); + + private FileStatDAO fileStatDAO; + + private List directories; + + public FileSyncTask(FileStatDAO fileStatDAO, String directoryPath) { + this.fileStatDAO = fileStatDAO; + directories = Arrays.asList(directoryPath); + } + + public FileSyncTask(FileStatDAO fileStatDAO, List directories) { + this.fileStatDAO = fileStatDAO; + this.directories = directories; + } + + + @Override + protected TaskResult call() throws Exception { + + this.updateProgress(1, 3); + + long length = 0; + for (String directoryPath : directories) { + try { + log.info("Starting file sync task on directory {}", directoryPath); + File directory = new File(directoryPath); + if (directory.exists() && directory.isDirectory()) { + length = extractFolderSize(directory, null); + log.info("Completed file sync task on directory {}, computed length: {}", directoryPath, length); + } + + } catch (Exception e) { + log.error("An error occurred during file sync task execution", e); + throw new TaskException(e); + } + } + this.updateMessage("Plugins and files metrics synchronized."); + this.updateProgress(3, 3); + return success(); + } + + public long extractFolderSize(File directory, FileStat parent) { + long length = 0; + + this.updateMessage("Collecting file metrics on directory: " + directory.getAbsolutePath()); + fileStatDAO.deleteByPath(FileUtils.convertPath(directory.getAbsolutePath())); + + FileStat directoryStat = new FileStat(); + directoryStat.setName(directory.getName()); + directoryStat.setPath(FileUtils.convertPath(directory.getAbsolutePath())); + + if (parent != null) { + directoryStat.setParentPath(parent.getPath()); + directoryStat.setParent(parent); + } + directoryStat.setLength(0); + fileStatDAO.save(directoryStat); + + for (File file : directory.listFiles()) { + if (file.isFile()) { + FileStat fileStat = new FileStat(); + fileStat.setName(file.getName()); + fileStat.setPath(FileUtils.convertPath(file.getAbsolutePath())); + fileStat.setParentPath(FileUtils.convertPath(directory.getAbsolutePath())); + fileStat.setLength(file.length()); + fileStat.setParent(directoryStat); + + directoryStat.getChilds().add(fileStat); + length += fileStat.getLength(); + + } else { + length += extractFolderSize(file, directoryStat); + } + } + + directoryStat.setLength(length); + fileStatDAO.save(directoryStat); + return length; + + } +} diff --git a/owlplug-client/src/main/java/com/owlplug/core/ui/DoughnutChart.java b/owlplug-client/src/main/java/com/owlplug/core/ui/DoughnutChart.java new file mode 100644 index 00000000..d2792328 --- /dev/null +++ b/owlplug-client/src/main/java/com/owlplug/core/ui/DoughnutChart.java @@ -0,0 +1,84 @@ +/* OwlPlug + * Copyright (C) 2021 Arthur + * + * This file is part of OwlPlug. + * + * OwlPlug is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 + * as published by the Free Software Foundation. + * + * OwlPlug is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with OwlPlug. If not, see . + */ + +package com.owlplug.core.ui; + +import javafx.geometry.Bounds; +import javafx.scene.Node; +import javafx.scene.chart.PieChart; +import javafx.scene.layout.Pane; +import javafx.scene.paint.Color; +import javafx.scene.shape.Circle; + +public class DoughnutChart extends PieChart { + private final Circle innerCircle; + + public DoughnutChart() { + super(); + + innerCircle = new Circle(); + innerCircle.getStyleClass().add("doughnut-inner-circle"); + } + + @Override + protected void layoutChartChildren(double top, double left, double contentWidth, double contentHeight) { + super.layoutChartChildren(top, left, contentWidth, contentHeight); + + addInnerCircleIfNotPresent(); + updateInnerCircleLayout(); + } + + private void addInnerCircleIfNotPresent() { + if (getData().size() > 0) { + Node pie = getData().get(0).getNode(); + if (pie.getParent() instanceof Pane parent + && !parent.getChildren().contains(innerCircle)) { + parent.getChildren().add(innerCircle); + } + } + } + + private void updateInnerCircleLayout() { + double minX = Double.MAX_VALUE; + double minY = Double.MAX_VALUE; + double maxX = Double.MIN_VALUE; + double maxY = Double.MIN_VALUE; + for (PieChart.Data data: getData()) { + Node node = data.getNode(); + + Bounds bounds = node.getBoundsInParent(); + if (bounds.getMinX() < minX) { + minX = bounds.getMinX(); + } + if (bounds.getMinY() < minY) { + minY = bounds.getMinY(); + } + if (bounds.getMaxX() > maxX) { + maxX = bounds.getMaxX(); + } + if (bounds.getMaxY() > maxY) { + maxY = bounds.getMaxY(); + } + } + + innerCircle.setCenterX(minX + (maxX - minX) / 2); + innerCircle.setCenterY(minY + (maxY - minY) / 2); + innerCircle.setRadius((maxX - minX) / 4); + innerCircle.toFront(); + } +} \ No newline at end of file diff --git a/owlplug-client/src/main/resources/application.properties b/owlplug-client/src/main/resources/application.properties index 193583a4..f5e70478 100644 --- a/owlplug-client/src/main/resources/application.properties +++ b/owlplug-client/src/main/resources/application.properties @@ -25,9 +25,9 @@ spring.datasource.username=sa spring.datasource.password= spring.datasource.driver-class-name=org.h2.Driver - spring.jpa.hibernate.ddl-auto = update spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.H2Dialect +spring.jpa.properties.hibernate.auto_quote_keyword=true # WebServer # Disable webserver initialization on startup diff --git a/owlplug-client/src/main/resources/fxml/DirectoryInfoView.fxml b/owlplug-client/src/main/resources/fxml/DirectoryInfoView.fxml index ccc6db0c..b605ef1c 100644 --- a/owlplug-client/src/main/resources/fxml/DirectoryInfoView.fxml +++ b/owlplug-client/src/main/resources/fxml/DirectoryInfoView.fxml @@ -1,15 +1,14 @@ - - + - + - + - + - + - + + + + - + - + + + + + + + + + + + + + + + + + @@ -44,11 +58,12 @@ - + + diff --git a/owlplug-client/src/main/resources/icons/chart-white-16.png b/owlplug-client/src/main/resources/icons/chart-white-16.png new file mode 100644 index 00000000..c73872b8 Binary files /dev/null and b/owlplug-client/src/main/resources/icons/chart-white-16.png differ diff --git a/owlplug-client/src/main/resources/icons/share-white-16.png b/owlplug-client/src/main/resources/icons/share-white-16.png new file mode 100644 index 00000000..7ece773d Binary files /dev/null and b/owlplug-client/src/main/resources/icons/share-white-16.png differ diff --git a/owlplug-client/src/main/resources/owlplug.css b/owlplug-client/src/main/resources/owlplug.css index 4c804e6b..dd37f9bd 100644 --- a/owlplug-client/src/main/resources/owlplug.css +++ b/owlplug-client/src/main/resources/owlplug.css @@ -450,6 +450,32 @@ ProgressIndicator{ -fx-effect: dropshadow(three-pass-box, rgba(0,0,0,0.8), 10, 0, 0, 0); } + +/**************************************************************** + JavaFx Charts theme +****************************************************************/ + +.default-color0.chart-pie { -fx-pie-color: #116A7B; } +.default-color1.chart-pie { -fx-pie-color: #85586F; } +.default-color2.chart-pie { -fx-pie-color: #7F8B52; } +.default-color3.chart-pie { -fx-pie-color: #FFDBA4; } +.default-color4.chart-pie { -fx-pie-color: #47A992; } +.default-color5.chart-pie { -fx-pie-color: #C84B31; } +.default-color6.chart-pie { -fx-pie-color: #BCCC9A; } +.default-color7.chart-pie { -fx-pie-color: #968C83; } + +.chart-pie { + -fx-border-width: 0px; +} + +.chart-legend { + -fx-background-color: card-pane-color; +} + +.doughnut-inner-circle { + -fx-fill: card-pane-color; +} + /**************************************************************** * * Utilities