• (3 Pages)
  • +
  • 1
  • 2
  • 3

Creating a calendar viewer application GUIs, renderers, JTable tutorial Rate Topic: ***** 3 Votes

#31 Novhf  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 08-December 12

Posted 08 December 2012 - 04:40 PM

Thanks, helps a lot.
Was This Post Helpful? 0
  • +
  • -

#32 joel amos  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 1
  • Joined: 13-February 13

Posted 13 February 2013 - 03:00 PM

In my opinion, this calendar would be much more useful as an extended JPanel. So, I took the liberty to do that.

import javax.swing.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;

public class CalendarPanel extends JPanel {

    private static final long serialVersionUID = 1L;
    static JLabel lblMonth, lblYear;
    static JButton btnPrev, btnNext;
    static JTable tblCalendar;
    static DefaultTableModel mtblCalendar;
    static JScrollPane stblCalendar;
    static int realYear, realMonth, realDay, currentYear, currentMonth;

    public CalendarPanel() {

	setLayout(null);
	setLookAndFeel();
	createControls();
	setBorder();
	registerActionListeners();
	addControls();
	setBounds();
	setDate();
	addHeaders();
	setBackground();
	setTableProperties();
	populateTable();
	refreshCalendar(realMonth, realYear);

    }

    private void setLookAndFeel() {
	try {
	    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
	} catch (ClassNotFoundException e) {
	} catch (InstantiationException e) {
	} catch (IllegalAccessException e) {
	} catch (UnsupportedLookAndFeelException e) {
	}
    }

    private void createControls() {
	lblMonth = new JLabel("January");
	lblYear = new JLabel("Change year:");
	cmbYear = new JComboBox();
	btnPrev = new JButton("<");
	btnNext = new JButton(">");
	mtblCalendar = new DefaultTableModel() {

	    private static final long serialVersionUID = 1L;

	    public boolean isCellEditable(int rowIndex, int mColIndex) {
		return false;
	    }
	};
	tblCalendar = new JTable(mtblCalendar);
	stblCalendar = new JScrollPane(tblCalendar);
    }

    private void setBorder() {
	this.setBorder(BorderFactory.createTitledBorder("Calendar"));
    }

    private void registerActionListeners() {
	btnPrev.addActionListener(new btnPrev_Action());
	btnNext.addActionListener(new btnNext_Action());
	cmbYear.addActionListener(new cmbYear_Action());
    }

    private void addControls() {
	this.add(lblMonth);
	this.add(lblYear);
	this.add(cmbYear);
	this.add(btnPrev);
	this.add(btnNext);
	this.add(stblCalendar);
    }

    private void setBounds() {
	this.setBounds(0, 0, 320, 335);
	lblMonth.setBounds(160 - lblMonth.getPreferredSize().width / 2, 25, 100, 25);
	lblYear.setBounds(10, 305, 80, 20);
	cmbYear.setBounds(230, 305, 80, 20);
	btnPrev.setBounds(10, 25, 50, 25);
	btnNext.setBounds(260, 25, 50, 25);
	stblCalendar.setBounds(10, 50, 300, 250);
    }

    private void setDate() {
	GregorianCalendar cal = new GregorianCalendar(); // Create calendar
	realDay = cal.get(GregorianCalendar.DAY_OF_MONTH); // Get day
	realMonth = cal.get(GregorianCalendar.MONTH); // Get month
	realYear = cal.get(GregorianCalendar.YEAR); // Get year
	currentMonth = realMonth; // Match month and year
	currentYear = realYear;
    }

    private void addHeaders() {
	String[] headers = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
	for (int i = 0; i < 7; i++) {
	    mtblCalendar.addColumn(headers[i]);
	}
    }

    private void setBackground() {
	tblCalendar.getParent().setBackground(tblCalendar.getBackground());
    }

    private void setTableProperties() {
	// No resize/reorder
	tblCalendar.getTableHeader().setResizingAllowed(false);
	tblCalendar.getTableHeader().setReorderingAllowed(false);

	// Single cell selection
	tblCalendar.setColumnSelectionAllowed(true);
	tblCalendar.setRowSelectionAllowed(true);
	tblCalendar.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

	// Set row/column count
	tblCalendar.setRowHeight(38);
	mtblCalendar.setColumnCount(7);
	mtblCalendar.setRowCount(6);
    }

    private void populateTable() {
	for (int i = realYear - 100; i <= realYear + 100; i++) {
	    cmbYear.addItem(String.valueOf(i));
	}
    }

    private static void refreshCalendar(int month, int year) {
	// Variables
	String[] months = { "January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December" };
	int nod, som; // Number Of Days, Start Of Month

	// Allow/disallow buttons
	btnPrev.setEnabled(true);
	btnNext.setEnabled(true);
	if (month == 0 && year <= realYear - 10) {
	    btnPrev.setEnabled(false);
	} // Too early
	if (month == 11 && year >= realYear + 100) {
	    btnNext.setEnabled(false);
	} // Too late
	lblMonth.setText(months[month]); // Refresh the month label (at the top)
	
	// Re-align label with calendar
	lblMonth.setBounds(160 - lblMonth.getPreferredSize().width / 2, 25, 180, 25); 
	// Select the correct year in the combo box
	cmbYear.setSelectedItem(String.valueOf(year)); 

	// Clear table
	for (int i = 0; i < 6; i++) {
	    for (int j = 0; j < 7; j++) {
		mtblCalendar.setValueAt(null, i, j);
	    }
	}

	// Get first day of month and number of days
	GregorianCalendar cal = new GregorianCalendar(year, month, 1);
	nod = cal.getActualMaximum(GregorianCalendar.DAY_OF_MONTH);
	som = cal.get(GregorianCalendar.DAY_OF_WEEK);

	// Draw calendar
	for (int i = 1; i <= nod; i++) {
	    int row = new Integer((i + som - 2) / 7);
	    int column = (i + som - 2) % 7;
	    mtblCalendar.setValueAt(i, row, column);
	}

	// Apply renderers
	tblCalendar.setDefaultRenderer(tblCalendar.getColumnClass(0), new tblCalendarRenderer());
    }

    static class tblCalendarRenderer extends DefaultTableCellRenderer {
	private static final long serialVersionUID = 1L;

	public Component getTableCellRendererComponent(JTable table, Object value, boolean selected, boolean focused, int row, int column) {
	    super.getTableCellRendererComponent(table, value, selected, focused, row, column);
	    if (column == 0 || column == 6) { // Week-end
		setBackground(new Color(255, 220, 220));
	    }
	    else { // Week
		setBackground(new Color(255, 255, 255));
	    }
	    if (value != null) {
		if (Integer.parseInt(value.toString()) == realDay && currentMonth == realMonth && currentYear == realYear) { // Today
		    setBackground(new Color(220, 220, 255));
		}
	    }
	    setBorder(null);
	    setForeground(Color.black);
	    return this;
	}
    }

    static class btnPrev_Action implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    if (currentMonth == 0) { // Back one year
		currentMonth = 11;
		currentYear -= 1;
	    }
	    else { // Back one month
		currentMonth -= 1;
	    }
	    refreshCalendar(currentMonth, currentYear);
	}
    }

    static class btnNext_Action implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    if (currentMonth == 11) { // Forward one year
		currentMonth = 0;
		currentYear += 1;
	    }
	    else { // Forward one month
		currentMonth += 1;
	    }
	    refreshCalendar(currentMonth, currentYear);
	}
    }

    static class cmbYear_Action implements ActionListener {
	public void actionPerformed(ActionEvent e) {
	    if (cmbYear.getSelectedItem() != null) {
		String b = cmbYear.getSelectedItem().toString();
		currentYear = Integer.parseInt(B)/>/>;
		refreshCalendar(currentMonth, currentYear);
	    }
	}
    }
}

Was This Post Helpful? 0
  • +
  • -

#33 Kson  Icon User is offline

  • New D.I.C Head

Reputation: 0
  • View blog
  • Posts: 2
  • Joined: 03-May 10

Posted 08 December 2013 - 03:46 AM

I have the entire program working, but the btnPrev and btnNext doesn't work. I am trying this code in Netbeans but it is stuck on December. I have rechecked and it's the same as the tutorial but it doesn't work anymore. This is my code from netbeans but it doesn't move.

package calendarProgram;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;



public class calendarProgram {

static JLabel lblMonth, lblYear;
static JButton btnPrev, btnNext;
static JTable tblCalendar;
static JComboBox cmbYear;
static JFrame frmMain;
static Container pane;
static DefaultTableModel mtblCalendar; //Table model
/**Every JTable must have a model in order to work.
* Make sure you understand fully the above code. **/
static JScrollPane stblCalendar; // The scrollpane
static JPanel pnlCalendar; //The panel
static int realDay, realMonth, realYear, currentMonth, currentYear;


public static void main(String args[]){
try {UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());}
catch (ClassNotFoundException e) {}
catch (InstantiationException e) {}
catch (IllegalAccessException e) {}
catch (UnsupportedLookAndFeelException e) {}

//Prepare frame
frmMain = new JFrame("Calendar application");
frmMain.setSize(330,375); // Two arguments: width and height
pane = frmMain.getContentPane();
pane.setLayout(null); //Apply the null layout
frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//Close when X is clicked

//Create controls
lblMonth = new JLabel("January");
lblYear = new JLabel("Change year:");
cmbYear = new JComboBox();
btnPrev = new JButton ("<<");
btnNext = new JButton (">>");
mtblCalendar = new DefaultTableModel(){public boolean isCellEditable(int rowIndex, int mColIndex){return false;}};
tblCalendar = new JTable (mtblCalendar);//Table using the above model
stblCalendar = new JScrollPane(tblCalendar);//The scrollpane of the above table.
pnlCalendar = new JPanel(null);//Create the "panel' to place components
pnlCalendar.setBorder(BorderFactory.createTitledBorder("Calendar")); //Set border

//Register action listeners
btnPrev.addActionListener(new btnPrev_Action());
btnPrev.addActionListener(new btnNext_Action());
cmbYear.addActionListener(new cmbYear_Action());

//Add controls to pane
pane.add(pnlCalendar);
pnlCalendar.add(lblMonth);
pnlCalendar.add(lblYear);
pnlCalendar.add(cmbYear);
pnlCalendar.add(btnPrev);
pnlCalendar.add(btnNext);
pnlCalendar.add(stblCalendar);

//Set bounds
pnlCalendar.setBounds(0,0,320,335);
lblMonth.setBounds(160-lblMonth.getPreferredSize().width/2,25,100,25);
lblYear.setBounds(10,305,80,20);
cmbYear.setBounds(230,305,80,20);
btnPrev.setBounds(10,25,50,25);
btnNext.setBounds(260, 25, 50,25);
stblCalendar.setBounds(10,50,300,250);

frmMain.setResizable(false);
frmMain.setVisible(true);

//Get real month and year
GregorianCalendar cal = new GregorianCalendar(); //Create calendar
realDay = cal.get(GregorianCalendar.DAY_OF_MONTH); //Get day
realMonth = cal.get(GregorianCalendar.MONTH);// Get month
realYear = cal.get(GregorianCalendar.YEAR);
currentMonth = realMonth; //Match month and year
currentYear = realYear;

//Add headers
String[] headers = {"Sun","Mon","Tue","Wed","Thu","Fri","Sat"};
//All headers
for(int i= 0; i < 7; i++){
mtblCalendar.addColumn(headers[i]);
}

tblCalendar.getParent().setBackground(tblCalendar.getBackground()); //Set background

//No resize/reorder
tblCalendar.getTableHeader().setResizingAllowed(false);
tblCalendar.getTableHeader().setReorderingAllowed(false);

//Single cell selection
tblCalendar.setColumnSelectionAllowed(true);
tblCalendar.setRowSelectionAllowed(true);
tblCalendar.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

//Set row/column count
tblCalendar.setRowHeight(38);
mtblCalendar.setColumnCount(7);
mtblCalendar.setRowCount(6);

//Prepare calendar
for(int i = realYear-100; i<=realYear+100; i++){
cmbYear.addItem(String.valueOf(i));
}
refreshCalendar(realMonth, realYear); //Refresh calendar
}

public static void refreshCalendar(int month, int year){
String[] months = {"January","February","March","April",
"May","June","July","August","September","October","November","December"};
int nod,som; //Number of Days, Start of Month
btnPrev.setEnabled(true);//Enable buttons at first
btnNext.setEnabled(true);
if(month == 0 && year <= realYear-10) {btnPrev.setEnabled(false);} // too early
if(month == 11 && year >= realYear+100){btnNext.setEnabled(false);}// Too late
lblMonth.setText(months[month]); //Refresh the month label (at the top)
lblMonth.setBounds(160-lblMonth.getPreferredSize().width/2, 25, 180, 25);
cmbYear.setSelectedItem(String.valueOf(year)); //Select the correct year in the combo box

//Clear table
for(int i= 0; i < 6; i++){
for (int j=0; j<7; j++){
mtblCalendar.setValueAt(null, i, j);
}
}

//Get first day of month and number of days
GregorianCalendar cal = new GregorianCalendar(year, month, 1);
nod = cal.getActualMaximum(GregorianCalendar.DAY_OF_MONTH);
som = cal.get(GregorianCalendar.DAY_OF_WEEK);


//Draw calendar
for (int i =1; i <= nod; i++){
int row = new Integer((i+som-2)/7);
int column = (i+som-2)%7;
mtblCalendar.setValueAt(i, row, column);
}

tblCalendar.setDefaultRenderer(tblCalendar.getColumnClass(0),new tblCalendarRenderer()); //Apply renderer
}



static class tblCalendarRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent (JTable table, Object value, boolean selected,
boolean focused, int row, int column) {
super.getTableCellRendererComponent(table, value, selected, focused, row, column);
if(column == 0 || column == 6){// Week-end
setBackground(new Color(255,220,220));
}
else{ //Week
setBackground(new Color (255,255,255));
}
if(value !=null){
if(Integer.parseInt(value.toString()) == realDay && currentMonth == realMonth && currentYear == realYear) { //Today
setBackground(new Color(220,220,255));
}
}
setBorder(null);
setForeground(Color.black);
return this;
}
}

static class btnPrev_Action implements ActionListener{
public void actionPerformed(ActionEvent e){
if(currentMonth == 0){ //Back one year
currentMonth = 11;
currentYear -= 1;
}
else{//Back one month
currentMonth -= 1;
}
refreshCalendar(currentMonth,currentYear);
}

}

static class btnNext_Action implements ActionListener{
public void actionPerformed(ActionEvent e){
if(currentMonth == 11){ //Forward one year
currentMonth = 0;
currentYear += 1;
}
else{//Forward one month
currentMonth += 1;
}
refreshCalendar(currentMonth,currentYear);
}
}

static class cmbYear_Action implements ActionListener{
public void actionPerformed (ActionEvent e){
if(cmbYear.getSelectedItem() != null ){
String b = cmbYear.getSelectedItem().toString();
currentYear = Integer.parseInt(B); //Get the numeric value
refreshCalendar(currentMonth, currentYear);//Refresh
}
}

}
}
Was This Post Helpful? 0
  • +
  • -

#34 g00se  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2720
  • View blog
  • Posts: 11,438
  • Joined: 20-September 08

Posted 08 December 2013 - 04:56 AM

package calendarProgram;

import java.awt.*;
import java.awt.event.*;

import java.util.*;

import javax.swing.*;
import javax.swing.event.*;
import javax.swing.table.*;


public class calendarProgram {
    static JLabel lblMonth;
    static JLabel lblYear;
    static JButton btnPrev;
    static JButton btnNext;
    static JTable tblCalendar;
    static JComboBox cmbYear;
    static JFrame frmMain;
    static Container pane;
    static DefaultTableModel mtblCalendar; //Table model

    /**Every JTable must have a model in order to work.
    * Make sure you understand fully the above code. **/
    static JScrollPane stblCalendar; // The scrollpane
    static JPanel pnlCalendar; //The panel
    static int realDay;
    static int realMonth;
    static int realYear;
    static int currentMonth;
    static int currentYear;

    public static void main(String[] args) {
        try {
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        } catch (ClassNotFoundException e) {
        } catch (InstantiationException e) {
        } catch (IllegalAccessException e) {
        } catch (UnsupportedLookAndFeelException e) {
        }

        //Prepare frame
        frmMain = new JFrame("Calendar application");
        frmMain.setSize(330, 375); // Two arguments: width and height
        pane = frmMain.getContentPane();
        pane.setLayout(null); //Apply the null layout
        frmMain.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //Close when X is clicked

        //Create controls
        lblMonth = new JLabel("January");
        lblYear = new JLabel("Change year:");
        cmbYear = new JComboBox();
        btnPrev = new JButton("<<");
        btnNext = new JButton(">>");
        mtblCalendar = new DefaultTableModel() {
                    public boolean isCellEditable(int rowIndex, int mColIndex) {
                        return false;
                    }
                };
        tblCalendar = new JTable(mtblCalendar); //Table using the above model
        stblCalendar = new JScrollPane(tblCalendar); //The scrollpane of the above table.
        pnlCalendar = new JPanel(null); //Create the "panel' to place components
        pnlCalendar.setBorder(BorderFactory.createTitledBorder("Calendar")); //Set border

        //Register action listeners
        btnPrev.addActionListener(new btnPrev_Action());
        btnPrev.addActionListener(new btnNext_Action());
        cmbYear.addActionListener(new cmbYear_Action());

        //Add controls to pane
        pane.add(pnlCalendar);
        pnlCalendar.add(lblMonth);
        pnlCalendar.add(lblYear);
        pnlCalendar.add(cmbYear);
        pnlCalendar.add(btnPrev);
        pnlCalendar.add(btnNext);
        pnlCalendar.add(stblCalendar);

        //Set bounds
        pnlCalendar.setBounds(0, 0, 320, 335);
        lblMonth.setBounds(160 - (lblMonth.getPreferredSize().width / 2), 25,
            100, 25);
        lblYear.setBounds(10, 305, 80, 20);
        cmbYear.setBounds(230, 305, 80, 20);
        btnPrev.setBounds(10, 25, 50, 25);
        btnNext.setBounds(260, 25, 50, 25);
        stblCalendar.setBounds(10, 50, 300, 250);

        frmMain.setResizable(false);
        frmMain.setVisible(true);

        //Get real month and year
        GregorianCalendar cal = new GregorianCalendar(); //Create calendar
        realDay = cal.get(GregorianCalendar.DAY_OF_MONTH); //Get day
        realMonth = cal.get(GregorianCalendar.MONTH); // Get month
        realYear = cal.get(GregorianCalendar.YEAR);
        currentMonth = realMonth; //Match month and year
        currentYear = realYear;

        //Add headers
        String[] headers = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };

        //All headers
        for (int i = 0; i < 7; i++) {
            mtblCalendar.addColumn(headers[i]);
        }

        tblCalendar.getParent().setBackground(tblCalendar.getBackground()); //Set background

        //No resize/reorder
        tblCalendar.getTableHeader().setResizingAllowed(false);
        tblCalendar.getTableHeader().setReorderingAllowed(false);

        //Single cell selection
        tblCalendar.setColumnSelectionAllowed(true);
        tblCalendar.setRowSelectionAllowed(true);
        tblCalendar.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        //Set row/column count
        tblCalendar.setRowHeight(38);
        mtblCalendar.setColumnCount(7);
        mtblCalendar.setRowCount(6);

        //Prepare calendar
        for (int i = realYear - 100; i <= (realYear + 100); i++) {
            cmbYear.addItem(String.valueOf(i));
        }

        refreshCalendar(realMonth, realYear); //Refresh calendar
    }

    public static void refreshCalendar(int month, int year) {
        String[] months = {
                "January", "February", "March", "April", "May", "June", "July",
                "August", "September", "October", "November", "December"
            };
        int nod; //Number of Days, Start of Month
        int som; //Number of Days, Start of Month
        btnPrev.setEnabled(true); //Enable buttons at first
        btnNext.setEnabled(true);

        if ((month == 0) && (year <= (realYear - 10))) {
            btnPrev.setEnabled(false);
        } // too early

        if ((month == 11) && (year >= (realYear + 100))) {
            btnNext.setEnabled(false);
        } // Too late

        lblMonth.setText(months[month]); //Refresh the month label (at the top)
        lblMonth.setBounds(160 - (lblMonth.getPreferredSize().width / 2), 25,
            180, 25);
        cmbYear.setSelectedItem(String.valueOf(year)); //Select the correct year in the combo box

        //Clear table
        for (int i = 0; i < 6; i++) {
            for (int j = 0; j < 7; j++) {
                mtblCalendar.setValueAt(null, i, j);
            }
        }

        //Get first day of month and number of days
        GregorianCalendar cal = new GregorianCalendar(year, month, 1);
        nod = cal.getActualMaximum(GregorianCalendar.DAY_OF_MONTH);
        som = cal.get(GregorianCalendar.DAY_OF_WEEK);

        //Draw calendar
        for (int i = 1; i <= nod; i++) {
            int row = new Integer(((i + som) - 2) / 7);
            int column = ((i + som) - 2) % 7;
            mtblCalendar.setValueAt(i, row, column);
        }

        tblCalendar.setDefaultRenderer(tblCalendar.getColumnClass(0),
            new tblCalendarRenderer()); //Apply renderer
    }

    static class tblCalendarRenderer extends DefaultTableCellRenderer {
        public Component getTableCellRendererComponent(JTable table,
            Object value, boolean selected, boolean focused, int row, int column) {
            super.getTableCellRendererComponent(table, value, selected,
                focused, row, column);

            if ((column == 0) || (column == 6)) { // Week-end
                setBackground(new Color(255, 220, 220));
            } else { //Week
                setBackground(new Color(255, 255, 255));
            }

            if (value != null) {
                if ((Integer.parseInt(value.toString()) == realDay) &&
                        (currentMonth == realMonth) &&
                        (currentYear == realYear)) { //Today
                    setBackground(new Color(220, 220, 255));
                }
            }

            setBorder(null);
            setForeground(Color.black);

            return this;
        }
    }

    static class btnPrev_Action implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (currentMonth == 0) { //Back one year
                currentMonth = 11;
                currentYear -= 1;
            } else { //Back one month
                currentMonth -= 1;
            }

            refreshCalendar(currentMonth, currentYear);
        }
    }

    static class btnNext_Action implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (currentMonth == 11) { //Forward one year
                currentMonth = 0;
                currentYear += 1;
            } else { //Forward one month
                currentMonth += 1;
            }

            refreshCalendar(currentMonth, currentYear);
        }
    }

    static class cmbYear_Action implements ActionListener {
        public void actionPerformed(ActionEvent e) {
            if (cmbYear.getSelectedItem() != null) {
                String b = cmbYear.getSelectedItem().toString();
                currentYear = Integer.parseInt(B)/>/>; //Get the numeric value
                refreshCalendar(currentMonth, currentYear); //Refresh
            }
        }
    }
}


Fixed indent and code tags (EDIT BUTTONS NOT SHOWING!)

You have not added any listeners to the buttons - so they are not going to work. btw, class names begin upper case in Java. There's no reason for your listener classes to be static. But they can and probably should be private

This post has been edited by g00se: 08 December 2013 - 04:57 AM

Was This Post Helpful? 0
  • +
  • -

  • (3 Pages)
  • +
  • 1
  • 2
  • 3