package bluej.groupwork.ui;

import java.io.File;
import java.io.FileFilter;
import java.util.HashMap;
import java.util.Map;

import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.ReadOnlyStringWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.ButtonType;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableColumn.SortType;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;

import bluej.Config;
import bluej.collect.DataCollector;
import bluej.groupwork.Repository;
import bluej.groupwork.StatusHandle;
import bluej.groupwork.StatusListener;
import bluej.groupwork.TeamStatusInfo;
import bluej.groupwork.TeamUtils;
import bluej.groupwork.TeamViewFilter;
import bluej.groupwork.TeamworkCommand;
import bluej.groupwork.TeamworkCommandResult;
import bluej.pkgmgr.Project;
import bluej.utility.FXWorker;
import bluej.utility.javafx.FXCustomizedDialog;
import bluej.utility.javafx.JavaFXUtil;

import threadchecker.OnThread;
import threadchecker.Tag;


| Main frame for CVS Status Dialog | | @author bquig | @author Amjad Altadmri | @OnThread(Tag.FXPlatform) public class StatusFrame extends FXCustomizedDialog<Void>{ private Project project; private Repository repository; private StatusTableModel statusModel; private Button refreshButton; private ActivityIndicator progressBar; private StatusWorker worker; private static final int MAX_ENTRIES = 20; private final boolean isDVCS; private TableView<TeamStatusInfo> statusTable;
| Creates a new instance of StatusFrame. Called via factory method | getStatusWindow. | public StatusFrame(Project project) { super(null, "team.status", "team-status"); this.project = project; isDVCS = project.getTeamSettingsController().isDVCS(); getDialogPane().setContent(makeMainPane()); prepareButtonPane(); } @Override protected Node wrapButtonBar(Node original) { makeRefreshPaneComponents(); BorderPane borderPane = new BorderPane(progressBar, null, original, null, refreshButton); JavaFXUtil.addStyleClass(borderPane, "replacement-button-bar"); return borderPane; } private Node makeMainPane() { statusModel = isDVCS ? new StatusTableModelDVCS(project, estimateInitialEntries()) : new StatusTableModelNonDVCS(project, estimateInitialEntries()); statusTable = new TableView<>(statusModel.getResources()); TableColumn<TeamStatusInfo, String> firstColumn = new TableColumn<>(statusModel.getColumnName(0)); JavaFXUtil.addStyleClass(firstColumn, "team-status-firstColumn"); firstColumn.prefWidthProperty().bind(statusTable.widthProperty().multiply(0.49)); firstColumn.setCellValueFactory(v -> new ReadOnlyStringWrapper(ResourceDescriptor.getResource(project, v.getValue(), false))); TableColumn<TeamStatusInfo, Object> secondColumn = new TableColumn<>(statusModel.getColumnName(1)); JavaFXUtil.addStyleClass(secondColumn, "team-status-secondColumn"); secondColumn.prefWidthProperty().bind(statusTable.widthProperty().multiply(0.249)); secondColumn.setCellValueFactory(v -> new ReadOnlyObjectWrapper<>(getValueAt(v.getValue(), 1))); secondColumn.setCellFactory(col -> new StatusTableCell(isDVCS, 1)); TableColumn<TeamStatusInfo, Object> thirdColumn = new TableColumn<>(statusModel.getColumnName(2)); JavaFXUtil.addStyleClass(thirdColumn, "team-status-thirdColumn"); thirdColumn.prefWidthProperty().bind(statusTable.widthProperty().multiply(0.249)); thirdColumn.setCellValueFactory(v -> new ReadOnlyObjectWrapper<>(getValueAt(v.getValue(), 2))); thirdColumn.setCellFactory(col -> new StatusTableCell(isDVCS, 2)); statusTable.getColumns().setAll(firstColumn, secondColumn, thirdColumn); statusTable.setColumnResizePolicy(TableView.UNCONSTRAINED_RESIZE_POLICY); statusTable.setBackground(null); JavaFXUtil.addStyleClass(statusTable, "status-table"); return new BorderPane(statusTable); }
| Create the Refresh button and status progress bar | private void makeRefreshPaneComponents() { progressBar = new ActivityIndicator(); progressBar.setRunning(false); BorderPane.setAlignment(progressBar, Pos.CENTER); refreshButton = new Button(Config.getString("team.status.refresh")); refreshButton.setDisable(true); refreshButton.setOnAction(event -> update()); refreshButton.requestFocus(); }
| Set up the buttons panel to contain a close button, and register the close action. | private void prepareButtonPane() { getDialogPane().getButtonTypes().setAll(ButtonType.CLOSE); this.setOnCloseRequest(event -> { if (worker != null) { worker.abort(); } }); }
| Try and estimate the number of entries in status table to avoid resizing | once repository has responded. | private int estimateInitialEntries() { int initialEntries = project.getFilesInProject(true, false).size() + 1; if (initialEntries > MAX_ENTRIES) { initialEntries = MAX_ENTRIES; } return initialEntries; }
| Refresh the status window. | public void update() { repository = project.getRepository(); if (repository != null) { progressBar.setRunning(true); refreshButton.setDisable(true); worker = new StatusWorker(); worker.start(); } else { hide(); } }
| Find the table entry at a particular column for a specific info object (row). | | @param info the info object which occupies a row | @param col the table column number | @return the Object at that location in the table | public Object getValueAt(TeamStatusInfo info, int col) { switch (col) { case 1: return isDVCS ? info.getStatus() : info.getLocalVersion(); case 2: return info.getStatus(!isDVCS); default: break; } return null; }
| Inner class to do the actual cvs status call to ensure that the UI is not | blocked during remote call | class StatusWorker extends FXWorker implements StatusListener { ObservableList<TeamStatusInfo> resources; TeamworkCommand command; TeamworkCommandResult result; boolean aborted; FileFilter filter = project.getTeamSettingsController().getFileFilter(true, true); public StatusWorker() { super(); resources = FXCollections.observableArrayList(); command = repository.getStatus(this, filter, true); } public void abort() { command.cancel(); aborted = true; } @OnThread(Tag.Worker) public Object construct() { result = command.getResult(); return resources; } @OnThread(Tag.Any) public void gotStatus(TeamStatusInfo info) { resources.add(info); } @OnThread(Tag.Any) public void statusComplete(StatusHandle commitHandle) { } public void finished() { progressBar.setRunning(false); if (! aborted) { if (result.isError()) { StatusFrame.this.dialogThenHide(() -> TeamUtils.handleServerResponseFX(result, StatusFrame.this.asWindow())); } else { resources.sort((info0, info1) -> info1.getStatus().ordinal() - info0.getStatus().ordinal()); TeamViewFilter filter = new TeamViewFilter(); resources.removeIf(info -> !filter.accept(info)); statusModel.setStatusData(resources); Map<File, String> statusMap = new HashMap<>(); for (TeamStatusInfo s : resources) { statusMap.put(s.getFile(), s.getStatus().getStatusString()); } DataCollector.teamStatusProject(project, repository, statusMap); } refreshButton.setDisable(false); if (statusTable.getItems() != null ) { statusTable.getItems().clear(); } statusTable.refresh(); statusTable.setItems(resources); TableColumn<TeamStatusInfo,?> secondColumn = statusTable.getColumns().get(1); statusTable.getSortOrder().add(secondColumn); secondColumn.setSortType(SortType.DESCENDING); } } } }

.   StatusFrame
.   wrapButtonBar
.   makeMainPane
.   makeRefreshPaneComponents
.   prepareButtonPane
.   estimateInitialEntries
.   update
.   getValueAt

top, use, map, class StatusWorker

.   StatusWorker
.   abort
.   construct
.   gotStatus
.   statusComplete
.   finished




322 neLoCode + 16 LoComm