io.heming.accountbook.ui.MainFrame.java Source code

Java tutorial

Introduction

Here is the source code for io.heming.accountbook.ui.MainFrame.java

Source

/*
 * Copyright 2015 heming.keh@gmail.com
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.heming.accountbook.ui;

import io.heming.accountbook.constants.Constants;
import io.heming.accountbook.entity.Category;
import io.heming.accountbook.entity.Record;
import io.heming.accountbook.facade.CategoryFacade;
import io.heming.accountbook.facade.FacadeUtil;
import io.heming.accountbook.facade.Pages;
import io.heming.accountbook.facade.RecordFacade;
import io.heming.accountbook.util.DateUtil;
import io.heming.accountbook.util.ZIPUtil;
import org.apache.commons.io.FileUtils;

import javax.swing.*;
import javax.swing.border.EtchedBorder;
import javax.swing.table.TableColumnModel;
import java.awt.*;
import java.awt.event.*;
import java.io.File;
import java.io.IOException;
import java.sql.Date;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.List;

/**
 * Created by Keh on 2015/5/18.
 */
public class MainFrame extends JFrame {

    private List<Category> categories;
    private CategoryFacade categoryFacade;
    private RecordFacade recordFacade;
    private Pages.Builder builder;
    private Pages pages;

    private JTable table;
    private TableModel model;

    // Menu bar
    private JMenuBar menuBar;

    // File menu
    private JMenu fileMenu;
    private JMenuItem importMenuItem;
    private JMenuItem exportMenuItem;
    private JMenuItem quitMenuItem;

    // Edit menu
    private JMenu editMenu;
    private JMenuItem addRecordMenuItem;
    //    private JMenuItem deleteRecordMenuItem;
    private JMenuItem categoryMenuItem;

    // View menu
    private JMenu viewMenu;
    private JMenuItem searchMenuItem;
    private JMenuItem firstPageMenuItem;
    private JMenuItem prevPageMenuItem;
    private JMenuItem nextPageMenuItem;
    private JMenuItem lastPageMenuItem;
    private JMenuItem keywordMenuItem;

    /**
     * Help menu.
     */
    private JMenu helpMenu;

    /**
     * Help menu item.
     */
    private JMenuItem helpMenuItem;

    /**
     * About menu item.
     */
    private JMenuItem aboutMenuItem;

    /**
     * Tool bar.
     */
    private JToolBar toolbar;

    /**
     * A button for add record.
     */
    private JButton addButton;

    /**
     * To select date range of current year.
     */
    private JToggleButton yearToggleButton;

    /**
     * To select date range of current month.
     */
    private JToggleButton monthToggleButton;

    /**
     * To select date range of current day.
     */
    private JToggleButton dayToggleButton;

    /**
     * To select all date range.
     */
    private JToggleButton customToggleButton;

    private static final String SHOW_DATE_CHOOSER = "SHOW DATE CHOOSER";
    private static final String HIDE_DATE_CHOOSER = "HIDE DATE CHOOSER";
    private JSpinner startDateSpinner;
    private JSpinner endDateSpinner;
    private JPanel cards;
    private boolean date_chooser_visible;

    /**
     * Keyword text field.
     */
    private JTextField keywordTextField;

    /**
     * Search button.
     */
    private JButton searchButton;

    /**
     * Status bar.
     */
    private Box statusBar;

    /**
     * A label to display the current status messages.
     */
    private JLabel statusLabel;

    /**
     * Go to the next page view button.
     */
    private JButton nextPageButton;

    /**
     * Go to the previous page view button.
     */
    private JButton prevPageButton;

    /**
     * Go to the first page view button.
     */
    private JButton firstPageButton;

    /**
     * Go to the last page view button.
     */
    private JButton lastPageButton;
    private JLabel infoLabel;
    private JLabel pageLabel;

    /**
     * ???????disable?
     */
    private boolean disable;

    public MainFrame() throws Exception {
        categoryFacade = new CategoryFacade();
        recordFacade = new RecordFacade();
        builder = new Pages.Builder();
        builder.order("id", false);
        pages = builder.make();
        date_chooser_visible = false;
        disable = false;

        // Create widgets
        model = new TableModel();
        table = new JTable(model) {
            /**
             * tip.
             *
             * @param evt
             * @return
             */
            @Override
            public String getToolTipText(MouseEvent evt) {
                String tip = null;
                java.awt.Point point = evt.getPoint();
                int row = rowAtPoint(point);
                int col = columnAtPoint(point);
                Rectangle bounds = getCellRect(row, col, false);
                Component comp = prepareRenderer(getCellRenderer(row, col), row, col);

                try {
                    if (comp.getPreferredSize().width > bounds.width) {
                        tip = getValueAt(row, col).toString();
                    }
                } catch (RuntimeException e) {
                    //catch null pointer exception if mouse is over an empty line
                }
                return tip;
            }
        };
        if (pages.hasNext()) {
            model.setRecords(pages.next());
        }

        // ?
        table.setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
        TableColumnModel columnModel = table.getColumnModel();
        columnModel.getColumn(0).setPreferredWidth(80);
        columnModel.getColumn(1).setPreferredWidth(160);
        columnModel.getColumn(2).setPreferredWidth(80);
        columnModel.getColumn(3).setPreferredWidth(100);
        columnModel.getColumn(4).setPreferredWidth(100);
        columnModel.getColumn(5).setPreferredWidth(360);

        // Layout
        JScrollPane pane = new JScrollPane(table);
        add(pane, BorderLayout.CENTER);

        table.getTableHeader().setReorderingAllowed(false);
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        // table header
        table.getTableHeader().addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                int col = table.columnAtPoint(e.getPoint());
                String name = table.getColumnName(col);
                // TODO
            }
        });

        // ?
        addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                quit();
            }
        });

        categories = categoryFacade.listBySale();

        initMenuBar();
        initToolBar();
        initStatusBar();
        initTablePopupMenu();

        searchRecords();
        setIconImage(new ImageIcon(getClass().getResource("64.png")).getImage());
        setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        setTitle("");
        setSize(860, 620);
    }

    private void setStatusText(String text) {
        statusLabel.setText(text);
    }

    private void importRecords() {
        final JFileChooser chooser = new JFileChooser();
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        chooser.addChoosableFileFilter(new AbbFileFilter());
        chooser.setAcceptAllFileFilterUsed(false);
        int value = chooser.showOpenDialog(this);
        if (value == JFileChooser.APPROVE_OPTION) {
            // ?
            disableAllControls();
            new Thread() {
                @Override
                public void run() {
                    try {
                        statusLabel.setIcon(new ImageIcon(getClass().getResource("loader.gif")));
                        setStatusText("?...");
                        FacadeUtil.shutdown();
                        File file = chooser.getSelectedFile();
                        setStatusText("??...");
                        FileUtils.cleanDirectory(new File(Constants.DATA_DIR));
                        setStatusText("??...");
                        ZIPUtil.decompress(file, new File(Constants.HOME_DIR));
                        setStatusText("?...");
                        FacadeUtil.restart();
                        // ??
                        setStatusText("????...");
                        categories = categoryFacade.listBySale();
                        // ????
                        setStatusText("?...");
                        searchRecords();
                        enableAllControls();
                        setStatusText("");
                        statusLabel.setIcon(new ImageIcon(getClass().getResource("stopped-loader.png")));
                        JOptionPane.showMessageDialog(MainFrame.this, "???", "?",
                                JOptionPane.INFORMATION_MESSAGE);
                    } catch (Exception e) {
                        JOptionPane.showMessageDialog(MainFrame.this, e.getMessage(), "",
                                JOptionPane.ERROR_MESSAGE);
                    }
                }
            }.start();
        }
    }

    private void exportRecords() {
        final JFileChooser chooser = new JFileChooser();
        chooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
        chooser.addChoosableFileFilter(new AbbFileFilter());
        chooser.setAcceptAllFileFilterUsed(false);
        int value = chooser.showSaveDialog(this);
        if (value == JFileChooser.APPROVE_OPTION) {
            final File file = chooser.getSelectedFile();
            disableAllControls();
            new Thread() {
                @Override
                public void run() {
                    try {
                        statusLabel.setIcon(new ImageIcon(getClass().getResource("loader.gif")));
                        setStatusText("?...");
                        FacadeUtil.shutdown();
                        // .abb?
                        setStatusText("?...");
                        List<String> exclusion = Arrays.asList();
                        if (!file.getName().endsWith(".abb")) {
                            ZIPUtil.compress(new File(Constants.DATA_DIR),
                                    new File(file.getAbsolutePath() + ".abb"), exclusion);
                        } else {
                            ZIPUtil.compress(new File(Constants.DATA_DIR), file, exclusion);
                        }
                        setStatusText("??...");
                        FacadeUtil.restart();
                        enableAllControls();
                        setStatusText("");
                        statusLabel.setIcon(new ImageIcon(getClass().getResource("stopped-loader.png")));
                        JOptionPane.showMessageDialog(MainFrame.this, String.format("?"), "?",
                                JOptionPane.INFORMATION_MESSAGE);
                    } catch (IOException e) {
                        JOptionPane.showMessageDialog(MainFrame.this, String.format(""), "",
                                JOptionPane.ERROR_MESSAGE);
                    }
                }
            }.start();
        }
    }

    private void quit() {
        if (disable)
            return;
        disableAllControls();
        statusLabel.setIcon(new ImageIcon(getClass().getResource("loader.gif")));
        statusLabel.setText("...");
        new Thread() {
            @Override
            public void run() {
                FacadeUtil.shutdown();
                System.exit(0);
            }
        }.start();
    }

    public java.util.List<Category> getCategories() {
        return this.categories;
    }

    public void addRecord(Record record) throws Exception {
        recordFacade.add(record);
        searchRecords();
    }

    public void addCategory(Category category) throws Exception {
        categoryFacade.add(category);
        categories.add(category);
    }

    public void deleteCategory(Category category) throws Exception {
        categoryFacade.delete(category);
        categories.remove(category);
    }

    public void updateRecord(Record record) throws Exception {
        recordFacade.update(record);
        searchRecords();
    }

    private void showAddRecordDialog() {
        if (categories.isEmpty()) {
            JOptionPane.showMessageDialog(this, "???????",
                    "", JOptionPane.ERROR_MESSAGE);
            return;
        }
        System.out.println("add record");
        try {
            Record record = new Record();
            record.setCategory(categories.get(0));
            record.setPrice(48.0);
            record.setDate(new Date(Calendar.getInstance().getTimeInMillis()));
            RecordDialog dialog = new RecordDialog(record, this, false);
            dialog.setSize(400, 250);
            dialog.setLocationRelativeTo(this);
            dialog.setIconImage(new ImageIcon(getClass().getResource("edit-add-3.png")).getImage());
            dialog.setVisible(true);
        } catch (Exception e) {
            JOptionPane.showMessageDialog(this, e.getMessage(), "", JOptionPane.ERROR_MESSAGE);
            e.printStackTrace();
        }
    }

    private void showUpdateRecordDialog(Record record) {
        try {
            RecordDialog dialog = new RecordDialog(record, this, true);
            dialog.setSize(400, 250);
            dialog.setLocationRelativeTo(this);
            dialog.setIconImage(new ImageIcon(getClass().getResource("edit-4.png")).getImage());
            dialog.setVisible(true);
        } catch (Exception e) {
            JOptionPane.showMessageDialog(this, e.getMessage(), "", JOptionPane.ERROR_MESSAGE);
            e.printStackTrace();
        }
    }

    private void deleteRecord() {
        int selectedRow = table.getSelectedRow();
        if (selectedRow < 0) {
            JOptionPane.showMessageDialog(MainFrame.this, "", "",
                    JOptionPane.ERROR_MESSAGE);
            return;
        }
        Record record = model.getRecord(table.convertRowIndexToModel(selectedRow));
        System.out.println(record);
        if (JOptionPane.showConfirmDialog(MainFrame.this,
                String.format("??%d?", record.getId()), "",
                JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE) == JOptionPane.OK_OPTION) {
            try {
                recordFacade.delete(record);
                searchRecords();
            } catch (Exception e1) {
                e1.printStackTrace();
                JOptionPane.showMessageDialog(MainFrame.this, e1.getMessage(), "", JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    public void refreshCategories() throws Exception {
        categories = categoryFacade.list();
    }

    private void showCategoryDialog() {
        CategoryDialog dialog = new CategoryDialog(this);
        dialog.setSize(400, 250);
        dialog.setLocationRelativeTo(this);
        dialog.setVisible(true);
    }

    private void searchRecords() {
        String keyword = keywordTextField.getText().trim();
        builder.keyword(keyword);
        if (customToggleButton.isSelected()) {
            builder.start(new Date(((java.util.Date) startDateSpinner.getValue()).getTime()))
                    .end(new Date(((java.util.Date) endDateSpinner.getValue()).getTime()));
        } else if (yearToggleButton.isSelected()) {
            builder.year();
        } else if (monthToggleButton.isSelected()) {
            builder.month();
        } else {
            builder.day();
        }
        try {
            pages = builder.make();
            if (pages.hasNext()) {
                List<Record> records = pages.next();
                model.setRecords(records);
            } else {
                // ?
                model.setRecords(new ArrayList<>());
            }
            infoLabel.setText(pages.getInfo());
            updateNavStatus();
        } catch (Exception e) {
            JOptionPane.showMessageDialog(this, e.getMessage(), "", JOptionPane.ERROR_MESSAGE);
            e.printStackTrace();
        }
    }

    private void prevPage() {
        if (pages != null && pages.hasPrev()) {
            try {
                List<Record> records = pages.prev();
                model.setRecords(records);
                updateNavStatus();
            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, e.getMessage(), "", JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    private void nextPage() {
        if (pages != null && pages.hasNext()) {
            try {
                List<Record> records = pages.next();
                model.setRecords(records);
                updateNavStatus();
            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, e.getMessage(), "", JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    private void lastPage() {
        if (pages != null && pages.hasNext()) {
            try {
                List<Record> records = pages.last();
                model.setRecords(records);
                updateNavStatus();
            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, e.getMessage(), "", JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    private void firstPage() {
        if (pages != null && pages.hasPrev()) {
            try {
                List<Record> records = pages.first();
                model.setRecords(records);
                updateNavStatus();
            } catch (Exception e) {
                e.printStackTrace();
                JOptionPane.showMessageDialog(this, e.getMessage(), "", JOptionPane.ERROR_MESSAGE);
            }
        }
    }

    private void chooseDateRange() {
        CardLayout layout = (CardLayout) cards.getLayout();
        if (date_chooser_visible) {
            layout.show(cards, HIDE_DATE_CHOOSER);
            date_chooser_visible = false;
            customToggleButton.setSelected(true);
            searchRecords();
        } else {
            layout.show(cards, SHOW_DATE_CHOOSER);
            date_chooser_visible = true;
        }
    }

    private void about() {
        JOptionPane.showMessageDialog(this,
                "\nheming_keh\n" + "heming.keh@gmail.com\n"
                        + "https://github.com/heming-keh/AccountBook",
                "", JOptionPane.PLAIN_MESSAGE, new ImageIcon(getClass().getResource("64.png")));
    }

    private void help() {
        try {
            new ProcessBuilder("cmd", "/C", ".chm").start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private void updateNavStatus() {
        if (pages == null) {
            lastPageButton.setEnabled(false);
            lastPageMenuItem.setEnabled(false);
            firstPageButton.setEnabled(false);
            firstPageMenuItem.setEnabled(false);
            nextPageButton.setEnabled(false);
            nextPageMenuItem.setEnabled(false);
            prevPageButton.setEnabled(false);
            prevPageMenuItem.setEnabled(false);
        } else {
            if (!pages.hasNext()) {
                lastPageButton.setEnabled(false);
                lastPageMenuItem.setEnabled(false);
                nextPageButton.setEnabled(false);
                nextPageMenuItem.setEnabled(false);
            } else {
                lastPageButton.setEnabled(true);
                lastPageMenuItem.setEnabled(true);
                nextPageButton.setEnabled(true);
                nextPageMenuItem.setEnabled(true);
            }
            if (!pages.hasPrev()) {
                firstPageButton.setEnabled(false);
                firstPageMenuItem.setEnabled(false);
                prevPageButton.setEnabled(false);
                prevPageMenuItem.setEnabled(false);
            } else {
                firstPageButton.setEnabled(true);
                firstPageMenuItem.setEnabled(true);
                prevPageButton.setEnabled(true);
                prevPageMenuItem.setEnabled(true);
            }
        }
        pageLabel.setText(pages.getPageString());
    }

    private void disableAllControls() {
        importMenuItem.setEnabled(false);
        exportMenuItem.setEnabled(false);
        quitMenuItem.setEnabled(false);

        addRecordMenuItem.setEnabled(false);
        addButton.setEnabled(false);
        // deleteRecordMenuItem.setEnabled(false);
        categoryMenuItem.setEnabled(false);

        searchMenuItem.setEnabled(false);
        searchButton.setEnabled(false);
        keywordTextField.setEnabled(false);
        keywordMenuItem.setEnabled(false);

        prevPageMenuItem.setEnabled(false);
        prevPageButton.setEnabled(false);
        nextPageMenuItem.setEnabled(false);
        nextPageButton.setEnabled(false);
        firstPageMenuItem.setEnabled(false);
        firstPageButton.setEnabled(false);
        lastPageMenuItem.setEnabled(false);
        lastPageButton.setEnabled(false);

        yearToggleButton.setEnabled(false);
        monthToggleButton.setEnabled(false);
        dayToggleButton.setEnabled(false);
        customToggleButton.setEnabled(false);
        disable = true;
    }

    private void enableAllControls() {
        importMenuItem.setEnabled(true);
        exportMenuItem.setEnabled(true);
        quitMenuItem.setEnabled(true);

        addRecordMenuItem.setEnabled(true);
        addButton.setEnabled(true);
        // deleteRecordMenuItem.setEnabled(true);
        categoryMenuItem.setEnabled(true);

        searchMenuItem.setEnabled(true);
        searchButton.setEnabled(true);
        keywordTextField.setEnabled(true);
        keywordMenuItem.setEnabled(true);

        //        prevPageMenuItem.setEnabled(true);
        //        prevPageButton.setEnabled(true);
        //        nextPageMenuItem.setEnabled(true);
        //        nextPageButton.setEnabled(true);
        //        firstPageMenuItem.setEnabled(true);
        //        firstPageButton.setEnabled(true);
        //        lastPageMenuItem.setEnabled(true);
        //        lastPageButton.setEnabled(true);
        updateNavStatus();

        yearToggleButton.setEnabled(true);
        monthToggleButton.setEnabled(true);
        dayToggleButton.setEnabled(true);
        customToggleButton.setEnabled(true);
        disable = false;
    }

    private void initToolBar() {
        toolbar = new JToolBar("ToolBar", JToolBar.HORIZONTAL);

        // Add Button
        addButton = new JButton();
        addButton.setActionCommand("");
        addButton.setToolTipText("");
        addButton.setIcon(new ImageIcon(getClass().getResource("edit-add-3.png")));

        // Search date range radio buttons
        ButtonGroup group = new ButtonGroup();
        yearToggleButton = new JToggleButton();
        yearToggleButton.setToolTipText("");
        yearToggleButton.setIcon(new ImageIcon(getClass().getResource("year-s.png")));
        group.add(yearToggleButton);
        monthToggleButton = new JToggleButton();
        monthToggleButton.setToolTipText("");
        monthToggleButton.setIcon(new ImageIcon(getClass().getResource("month-s.png")));
        group.add(monthToggleButton);
        dayToggleButton = new JToggleButton();
        dayToggleButton.setToolTipText("");
        dayToggleButton.setIcon(new ImageIcon(getClass().getResource("day-s.png")));
        group.add(dayToggleButton);
        customToggleButton = new JToggleButton();
        customToggleButton.setToolTipText("");
        customToggleButton.setIcon(new ImageIcon(getClass().getResource("all-s.png")));
        group.add(customToggleButton);

        // ??Checkbox
        monthToggleButton.setSelected(true);

        Calendar earliestCalendar = Calendar.getInstance();
        earliestCalendar.add(Calendar.YEAR, -100);
        java.util.Date earliestDate = earliestCalendar.getTime();

        Calendar latestCalendar = Calendar.getInstance();
        latestCalendar.add(Calendar.YEAR, 100);
        java.util.Date latestDate = latestCalendar.getTime();
        // 3?
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.MONTH, -1);
        calendar.add(Calendar.DAY_OF_MONTH, 1);
        java.util.Date initDate = calendar.getTime();

        SpinnerDateModel startDateModel = new SpinnerDateModel(DateUtil.getStartOfWeek(), earliestDate, latestDate,
                Calendar.MONTH);
        startDateSpinner = new JSpinner(startDateModel);
        JSpinner.DateEditor startDateEditor = new JSpinner.DateEditor(startDateSpinner, "yyyy-MM-dd");
        startDateSpinner.setEditor(startDateEditor);

        calendar.add(Calendar.MONTH, 1);
        calendar.add(Calendar.DAY_OF_MONTH, -1);
        initDate = calendar.getTime();
        SpinnerDateModel endDateModel = new SpinnerDateModel(initDate, earliestDate, latestDate, Calendar.MONTH);
        endDateSpinner = new JSpinner(endDateModel);
        JSpinner.DateEditor endDateEditor = new JSpinner.DateEditor(endDateSpinner, "yyyy-MM-dd");
        endDateSpinner.setEditor(endDateEditor);

        // Keyword text field
        keywordTextField = new JTextField();
        keywordTextField.setPreferredSize(new Dimension(250, 20));
        keywordTextField.setMaximumSize(keywordTextField.getPreferredSize());

        // Search Button
        searchButton = new JButton();
        searchButton.setActionCommand("?");
        searchButton.setToolTipText("?");
        searchButton.setIcon(new ImageIcon(getClass().getResource("edit-find-5.png")));

        // Layout
        cards = new JPanel();
        cards.setLayout(new CardLayout());
        Box box = Box.createHorizontalBox();
        box.add(Box.createHorizontalGlue());

        startDateSpinner.setPreferredSize(new Dimension(90, 20));
        startDateSpinner.setMaximumSize(startDateSpinner.getPreferredSize());
        box.add(startDateSpinner);

        box.add(Box.createHorizontalStrut(6));

        endDateSpinner.setPreferredSize(new Dimension(90, 20));
        endDateSpinner.setMaximumSize(endDateSpinner.getPreferredSize());
        box.add(endDateSpinner);

        cards.add(new JPanel(), HIDE_DATE_CHOOSER);
        cards.add(box, SHOW_DATE_CHOOSER);

        Box dateRangeBox = Box.createHorizontalBox();
        dateRangeBox.add(Box.createHorizontalGlue());
        dateRangeBox.add(cards);

        toolbar.add(addButton);
        toolbar.add(dateRangeBox);
        toolbar.add(customToggleButton);
        toolbar.add(yearToggleButton);
        toolbar.add(monthToggleButton);
        toolbar.add(dayToggleButton);
        toolbar.add(keywordTextField);
        toolbar.add(searchButton);

        add(toolbar, BorderLayout.NORTH);

        // Event listener
        addButton.addActionListener(e -> showAddRecordDialog());

        ActionListener listener = e -> searchRecords();
        yearToggleButton.addActionListener(listener);
        monthToggleButton.addActionListener(listener);
        dayToggleButton.addActionListener(listener);
        customToggleButton.addActionListener(listener);
        keywordTextField.addActionListener(listener);
        searchButton.addActionListener(listener);

        customToggleButton.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (SwingUtilities.isRightMouseButton(e)) {
                    chooseDateRange();
                }
            }
        });
    }

    private void initMenuBar() {
        JMenuBar menuBar = new JMenuBar();

        // File menu
        fileMenu = new JMenu("(F)");
        fileMenu.setMnemonic('F');

        exportMenuItem = new JMenuItem("(B)...");
        exportMenuItem.setIcon(new ImageIcon(getClass().getResource("database-save.png")));
        exportMenuItem.setMnemonic('B');
        exportMenuItem.addActionListener(e -> exportRecords());
        fileMenu.add(exportMenuItem);

        importMenuItem = new JMenuItem("??(R)...");
        importMenuItem.setIcon(new ImageIcon(getClass().getResource("document-import-2.png")));
        importMenuItem.setMnemonic('R');
        importMenuItem.addActionListener(e -> importRecords());
        fileMenu.add(importMenuItem);

        fileMenu.addSeparator();

        quitMenuItem = new JMenuItem("(X)");
        quitMenuItem.setIcon(new ImageIcon(getClass().getResource("application-exit-2.png")));
        quitMenuItem.setMnemonic('X');
        quitMenuItem.addActionListener(e -> quit());
        fileMenu.add(quitMenuItem);

        // View menu
        viewMenu = new JMenu("(V)");
        viewMenu.setMnemonic('V');

        searchMenuItem = new JMenuItem("?(S)");
        searchMenuItem.setIcon(new ImageIcon(getClass().getResource("edit-find-5.png")));
        searchMenuItem.setMnemonic('S');
        searchMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F3, 0));
        searchMenuItem.addActionListener(e -> searchRecords());
        viewMenu.add(searchMenuItem);

        keywordMenuItem = new JMenuItem("(K)");
        keywordMenuItem.setIcon(new ImageIcon(getClass().getResource("text-field.png")));
        keywordMenuItem.setMnemonic('K');
        keywordMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F, Event.CTRL_MASK));
        keywordMenuItem.addActionListener(e -> {
            keywordTextField.requestFocus();
            keywordTextField.selectAll();
        });
        viewMenu.add(keywordMenuItem);

        viewMenu.addSeparator();

        prevPageMenuItem = new JMenuItem("?(P)");
        prevPageMenuItem.setIcon(new ImageIcon(getClass().getResource("go-previous-2.png")));
        prevPageMenuItem.setMnemonic('P');
        prevPageMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, Event.CTRL_MASK));
        prevPageMenuItem.addActionListener(e -> prevPage());
        viewMenu.add(prevPageMenuItem);

        nextPageMenuItem = new JMenuItem("(N)");
        nextPageMenuItem.setIcon(new ImageIcon(getClass().getResource("go-next-2.png")));
        nextPageMenuItem.setMnemonic('N');
        nextPageMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, Event.CTRL_MASK));
        nextPageMenuItem.addActionListener(e -> nextPage());
        viewMenu.add(nextPageMenuItem);

        firstPageMenuItem = new JMenuItem("(F)");
        firstPageMenuItem.setIcon(new ImageIcon(getClass().getResource("go-first.png")));
        firstPageMenuItem.setMnemonic('F');
        firstPageMenuItem
                .setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_LEFT, Event.SHIFT_MASK + Event.CTRL_MASK));
        firstPageMenuItem.addActionListener(e -> firstPage());
        viewMenu.add(firstPageMenuItem);

        lastPageMenuItem = new JMenuItem("(L)");
        lastPageMenuItem.setIcon(new ImageIcon(getClass().getResource("go-last.png")));
        lastPageMenuItem.setMnemonic('L');
        lastPageMenuItem
                .setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_RIGHT, Event.SHIFT_MASK + Event.CTRL_MASK));
        lastPageMenuItem.addActionListener(e -> lastPage());
        viewMenu.add(lastPageMenuItem);

        // Edit menu
        editMenu = new JMenu("(E)");
        editMenu.setMnemonic('E');

        addRecordMenuItem = new JMenuItem("(A)...");
        addRecordMenuItem.setIcon(new ImageIcon(getClass().getResource("edit-add-3.png")));
        addRecordMenuItem.setMnemonic('A');
        addRecordMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, Event.CTRL_MASK));
        addRecordMenuItem.addActionListener(e -> showAddRecordDialog());
        editMenu.add(addRecordMenuItem);

        //        deleteRecordMenuItem = new JMenuItem("(D)");
        //        deleteRecordMenuItem.setIcon(new ImageIcon(getClass().getResource("edit-delete-6.png")));
        //        deleteRecordMenuItem.setMnemonic('D');
        //        deleteRecordMenuItem.addActionListener(e -> deleteRecord());
        //        editMenu.add(deleteRecordMenuItem);

        editMenu.addSeparator();

        categoryMenuItem = new JMenuItem("???(T)...");
        categoryMenuItem.setIcon(new ImageIcon(getClass().getResource("view-list-icons-2.png")));
        categoryMenuItem.setMnemonic('T');
        categoryMenuItem.addActionListener(e -> showCategoryDialog());
        editMenu.add(categoryMenuItem);

        // Help menu
        helpMenu = new JMenu("(H)");
        helpMenu.setMnemonic('H');

        helpMenuItem = new JMenuItem("(I)");
        helpMenuItem.setIcon(new ImageIcon(getClass().getResource("help-2.png")));
        helpMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F1, 0));
        helpMenuItem.setMnemonic('I');
        helpMenuItem.addActionListener(e -> help());
        helpMenu.add(helpMenuItem);

        aboutMenuItem = new JMenuItem("(A)");
        aboutMenuItem.setIcon(new ImageIcon(getClass().getResource("help-about-2.png")));
        aboutMenuItem.setMnemonic('A');
        aboutMenuItem.addActionListener(e -> about());
        helpMenu.add(aboutMenuItem);

        // Add menu to menu bar
        menuBar.add(fileMenu);
        menuBar.add(editMenu);
        menuBar.add(viewMenu);
        menuBar.add(helpMenu);
        setJMenuBar(menuBar);
    }

    private void initStatusBar() {
        statusBar = Box.createHorizontalBox();
        statusBar.setPreferredSize(new Dimension(getWidth(), 26));
        statusBar.setBorder(BorderFactory.createCompoundBorder(BorderFactory.createEmptyBorder(2, 0, 0, 0),
                BorderFactory.createEtchedBorder(EtchedBorder.LOWERED)));

        ImageIcon loading = new ImageIcon(getClass().getResource("stopped-loader.png"));
        statusLabel = new JLabel("", loading, JLabel.LEFT);
        statusLabel.setHorizontalAlignment(SwingConstants.LEFT);

        statusBar.add(statusLabel);
        statusBar.add(Box.createHorizontalGlue());

        infoLabel = new JLabel();
        infoLabel.setForeground(Color.GRAY);
        infoLabel.setPreferredSize(new Dimension(270, 20));
        infoLabel.setMaximumSize(infoLabel.getPreferredSize());
        statusBar.add(infoLabel);
        statusBar.add(Box.createHorizontalStrut(20));

        pageLabel = new JLabel();
        pageLabel.setForeground(Color.GRAY);
        pageLabel.setPreferredSize(new Dimension(80, 20));
        pageLabel.setMaximumSize(pageLabel.getPreferredSize());
        statusBar.add(pageLabel);

        Insets insets = new Insets(2, 10, 2, 10);
        firstPageButton = new JButton();
        firstPageButton.setMargin(insets);
        firstPageButton.setActionCommand("");
        firstPageButton.setToolTipText("");
        firstPageButton.setIcon(new ImageIcon(getClass().getResource("go-first.png")));
        firstPageButton.addActionListener(e -> firstPage());

        prevPageButton = new JButton();
        prevPageButton.setMargin(insets);
        prevPageButton.setActionCommand("?");
        prevPageButton.setToolTipText("?");
        prevPageButton.setIcon(new ImageIcon(getClass().getResource("go-previous-2.png")));
        prevPageButton.addActionListener(e -> prevPage());

        nextPageButton = new JButton();
        nextPageButton.setMargin(insets);
        nextPageButton.setActionCommand("");
        nextPageButton.setToolTipText("");
        nextPageButton.setIcon(new ImageIcon(getClass().getResource("go-next-2.png")));
        nextPageButton.addActionListener(e -> nextPage());

        lastPageButton = new JButton();
        lastPageButton.setMargin(insets);
        lastPageButton.setActionCommand("");
        lastPageButton.setToolTipText("");
        lastPageButton.setIcon(new ImageIcon(getClass().getResource("go-last.png")));
        lastPageButton.addActionListener(e -> lastPage());

        statusBar.add(firstPageButton);
        statusBar.add(prevPageButton);
        statusBar.add(nextPageButton);
        statusBar.add(lastPageButton);

        add(statusBar, BorderLayout.SOUTH);
    }

    private void initTablePopupMenu() {
        JPopupMenu popupMenu = new JPopupMenu();

        JMenuItem deleteMenuItem = new JMenuItem("(D)",
                new ImageIcon(getClass().getResource("edit-delete-6.png")));
        deleteMenuItem.setMnemonic('D');
        popupMenu.add(deleteMenuItem);
        deleteMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                deleteRecord();
            }
        });

        popupMenu.addSeparator();

        JMenuItem editMenuItem = new JMenuItem("(E)", new ImageIcon(getClass().getResource("edit-4.png")));
        editMenuItem.setMnemonic('E');
        popupMenu.add(editMenuItem);
        editMenuItem.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                Record record = model.getRecord(table.convertRowIndexToModel(table.getSelectedRow()));
                showUpdateRecordDialog(record);
            }
        });

        // ??popup menu
        table.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (disable)
                    return;
                JTable table = (JTable) e.getSource();
                Point point = e.getPoint();
                int row = table.rowAtPoint(point);
                int col = table.columnAtPoint(e.getPoint());
                if (SwingUtilities.isRightMouseButton(e)) {
                    if (row >= 0 && col >= 0) {
                        table.setRowSelectionInterval(row, row);
                    }
                    popupMenu.show(e.getComponent(), e.getX(), e.getY());
                } else if (SwingUtilities.isLeftMouseButton(e)) {
                    if (e.getClickCount() == 2) {
                        if (row >= 0 && col >= 0) {
                            //                            Record record = model.getRecord(table.convertRowIndexToModel(row));
                            //                            showUpdateRecordDialog(record);
                        }
                    }
                }
            }

        });

        table.getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT)
                .put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "Enter");
        table.getActionMap().put("Enter", new AbstractAction() {
            @Override
            public void actionPerformed(ActionEvent ae) {
                if (disable)
                    return;
                //do something on JTable enter pressed
                int row = table.getSelectedRow();
                if (row >= 0) {
                    Record record = model.getRecord(table.convertRowIndexToModel(row));
                    showUpdateRecordDialog(record);
                }
            }
        });

    }

}