/*
* Copyright (C) 2011 4th Line GmbH, Switzerland
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.fourthline.konto.client.ledger.view;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.resources.client.CssResource;
import com.google.gwt.uibinder.client.UiBinder;
import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.uibinder.client.UiHandler;
import com.google.gwt.user.cellview.client.CellTable;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.*;
import org.fourthline.konto.client.bundle.Bundle;
import org.fourthline.konto.client.currency.CurrencyPlace;
import org.fourthline.konto.client.ledger.LedgerPlace;
import org.fourthline.konto.client.ledger.account.AccountPlace;
import org.fourthline.konto.shared.LedgerCoordinates;
import org.fourthline.konto.shared.entity.Account;
import org.fourthline.konto.shared.result.LedgerLine;
import org.fourthline.konto.shared.result.LedgerLines;
import org.seamless.gwt.component.client.widget.DateRangeSelect;
import org.seamless.gwt.component.client.widget.GhostedTextBox;
import org.seamless.gwt.component.client.widget.ImageTextButton;
import org.seamless.gwt.component.client.widget.ResizableSplitLayoutPanel;
import org.seamless.gwt.theme.shared.client.ThemeBundle;
import org.seamless.gwt.theme.shared.client.ThemeStyle;
import org.seamless.util.time.DateFormat;
import org.seamless.util.time.DateRange;
import javax.inject.Inject;
/**
* TODO: Sorting of table http://code.google.com/p/google-web-toolkit/source/detail?r=9493
*
* @author Christian Bauer
*/
public class LedgerViewImpl extends Composite implements LedgerView {
public static final int SMALL_SIZE_PX = 140;
public static final int LARGE_SIZE_PX = 365;
interface UI extends UiBinder<DockLayoutPanel, LedgerViewImpl> {
}
private UI ui = GWT.create(UI.class);
interface Style extends CssResource, LedgerLineTable.Style {
String dateColumn();
String descriptionColumn();
String amountColumn();
String accountColumn();
String accountCell();
String infoLabel();
String rowInFuture();
String filteredBalance();
}
@UiField(provided = true)
final Bundle bundle;
@UiField(provided = true)
final ThemeBundle themeBundle;
@UiField
Style style;
@UiField
Label accountLabel;
@UiField
Anchor accountCurrencyAnchor;
@UiField
Label accountInitialBalanceLabel;
@UiField(provided = true)
final DateRangeSelect dateRangeSelect;
@UiField(provided = true)
GhostedTextBox descriptionFilterTextBox;
@UiField
Button descriptionFilterClearButton;
@UiField
ImageTextButton addEntryButton;
@UiField
ImageTextButton editAccountButton;
@UiField(provided = true)
final DockLayoutPanel ledgerEditSplitPanel;
@UiField
ScrollPanel ledgerPanel;
@UiField
ScrollPanel editPanel;
final LedgerLineTable ledgerLineTable;
double ledgerEditSplitPanelSize;
Presenter presenter;
Account account;
Timer filterTimer;
@Inject
public LedgerViewImpl(Bundle bundle,
DateRangeSelect dateRangeSelect) {
this.bundle = bundle;
this.themeBundle = bundle.themeBundle().create();
this.ledgerEditSplitPanel = new ResizableSplitLayoutPanel();
this.dateRangeSelect = dateRangeSelect;
dateRangeSelect.addValueChangeHandler(new ValueChangeHandler<DateRange>() {
@Override
public void onValueChange(ValueChangeEvent<DateRange> event) {
presenter.filterEffectiveOn(event.getValue());
}
});
this.descriptionFilterTextBox =
new GhostedTextBox(getFilterDescriptionLabel(), ThemeStyle.GhostedTextBox()) {
@Override
public void onKeyUp(KeyUpEvent event) {
super.onKeyUp(event);
final String enteredValue = getValue();
if (enteredValue.length() > 0) {
descriptionFilterClearButton.setEnabled(true);
scheduleFilterReqest(new Timer() {
public void run() {
if (presenter != null) {
presenter.filterDescription(enteredValue);
}
}
});
} else {
descriptionFilterClearButton.setEnabled(false);
if (filterTimer != null) filterTimer.cancel();
if (presenter != null) {
presenter.filterDescription(null);
}
}
}
};
initWidget(ui.createAndBindUi(this));
CellTable.Resources cellTableResource =
bundle.themeBundle().create().cellTableResources().create();
ledgerLineTable = new LedgerLineTable(cellTableResource, style) {
@Override
protected String getDebitLabel() {
return account != null ? account.getType().getDebitLabel() : "";
}
@Override
protected String getCreditLabel() {
return account != null ? account.getType().getCreditLabel() : "";
}
@Override
protected String getCurrentDescriptionFilter() {
return descriptionFilterTextBox.getValue();
}
@Override
protected void onSelection(LedgerLine line) {
presenter.selectLine(line);
}
@Override
protected void onSelection(Account account) {
presenter.goTo(new LedgerPlace(new LedgerCoordinates(account.getId())));
}
};
}
public ResizableSplitLayoutPanel getLedgerEditSplitPanel() {
// The UIBinder parser of DockLayoutPanel is buggy and prevents us from
// having this in XML. We need to instantiate and cast it.
return (ResizableSplitLayoutPanel) ledgerEditSplitPanel;
}
@Override
public void setPresenter(Presenter presenter) {
this.presenter = presenter;
dateRangeSelect.reset();
descriptionFilterTextBox.clear();
}
@Override
public void setDateFormat(DateFormat dateFormat) {
ledgerLineTable.setDateFormat(dateFormat);
dateRangeSelect.setDateFormat(dateFormat);
}
@Override
public void focus() {
addEntryButton.setFocus(true);
}
@Override
public void setLedgerLines(LedgerLines lines, Long selectEntryId, Long selectSplitId) {
this.account = lines.getAccount();
accountLabel.setText(account.getLabel(true, true, true, false));
accountCurrencyAnchor.setText("Currency: " + account.getMonetaryUnit().getCurrencyCode());
accountCurrencyAnchor.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
presenter.goTo(new CurrencyPlace(account.getMonetaryUnitId()));
}
});
if (account.getInitialBalance().signum() != 0) {
accountInitialBalanceLabel.setText(
"Initial Balance: " + account.getInitialBalance().getReportString(true, false)
);
} else {
accountInitialBalanceLabel.setText(
"Initial Balance: -"
);
}
dateRangeSelect.setValue(lines.getEffectiveOn());
if (lines.size() == 0) {
String noLinesString;
if (!dateRangeSelect.isAllSelected() && descriptionFilterTextBox.getValue().length() > 0) {
noLinesString = "No entries in date range with description '" + descriptionFilterTextBox.getValue() + "' found.";
} else if (!dateRangeSelect.isAllSelected()) {
noLinesString = "No entries in selected date range.";
} else if (descriptionFilterTextBox.getValue().length() > 0) {
noLinesString = "No entries with description '" + descriptionFilterTextBox.getValue() + "' found.";
} else {
noLinesString = "Please add entries.";
}
Label noLines = new Label(noLinesString);
noLines.addStyleName(style.infoLabel());
ledgerPanel.setWidget(noLines);
} else {
ledgerPanel.setWidget(ledgerLineTable);
ledgerLineTable.setRowCount(lines.size(), true);
ledgerLineTable.setRowData(0, lines);
ledgerLineTable.select(selectEntryId, selectSplitId);
}
}
@Override
public AcceptsOneWidget getEntryEditContainer() {
return editPanel;
}
@Override
public void showEntryEditContainer(boolean large, boolean scrollBottom) {
// We try our best to memorize the size of the edit panel if the user changed it
// even though we don't have a ResizeHandler API
double desiredSize = large ? LARGE_SIZE_PX : SMALL_SIZE_PX;
double currentSize = getLedgerEditSplitPanel().getSplitPosition(editPanel);
// If the current size is not one of the default sizes, store it for later re-use
ledgerEditSplitPanelSize =
(currentSize > 0 && !(currentSize == SMALL_SIZE_PX || currentSize == LARGE_SIZE_PX))
? currentSize
: ledgerEditSplitPanelSize;
// If there is a non-zero stored size, use that
if (ledgerEditSplitPanelSize > 0) {
desiredSize = ledgerEditSplitPanelSize;
}
getLedgerEditSplitPanel().setSplitPosition(editPanel, desiredSize, false);
editPanel.setVisible(true);
if (scrollBottom) {
editPanel.scrollToBottom();
}
}
@Override
public void hideEntryEditContainer() {
double currentSize = getLedgerEditSplitPanel().setSplitPosition(editPanel, 0, false);
// Store the size only if it's not one of the default sizes
if (currentSize > 0 && !(currentSize == SMALL_SIZE_PX || currentSize == LARGE_SIZE_PX)) {
ledgerEditSplitPanelSize = currentSize;
}
editPanel.setVisible(false);
focus();
}
@UiHandler("addEntryButton")
void onClickAddEntryButton(ClickEvent e) {
if (presenter != null)
presenter.addLine();
}
@UiHandler("descriptionFilterClearButton")
void onClickDescriptionFilterClear(ClickEvent e) {
descriptionFilterTextBox.clear();
descriptionFilterClearButton.setEnabled(false);
if (filterTimer != null) filterTimer.cancel();
if (presenter != null) {
presenter.filterDescription(null);
}
}
@UiHandler("editAccountButton")
void onClickEditAccountButton(ClickEvent e) {
if (presenter != null)
presenter.goTo(new AccountPlace(account.getId()));
}
protected String getFilterDescriptionLabel() {
return "Filter description...";
}
protected void scheduleFilterReqest(Timer timer) {
if (filterTimer != null) {
filterTimer.cancel();
}
filterTimer = timer;
filterTimer.schedule(1000);
}
}