Mercurial Hosting > junotu
view src/junotu/TabCalendarBoard.java @ 122:ad6ad9f695bb default tip
TabEdit: Started on card options pop-up menu
author | Fox |
---|---|
date | Sat, 25 Nov 2023 16:40:05 +0100 |
parents | 652027516bd6 |
children |
line wrap: on
line source
package junotu; /* TODO: Clean-up imports. */ import java.lang.RuntimeException; import java.util.Date; import java.util.Calendar; import java.text.DateFormat; import java.text.SimpleDateFormat; import org.apache.lucene.search.Query; import org.apache.lucene.search.WildcardQuery; import org.apache.lucene.search.TermQuery; import org.apache.lucene.index.Term; import java.awt.Dimension; import java.awt.Font; import java.awt.GridBagConstraints; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseEvent; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseListener; import java.awt.event.MouseAdapter; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.event.DocumentListener; import javax.swing.event.DocumentEvent; import javax.swing.event.ChangeListener; import javax.swing.event.ChangeEvent; import javax.swing.SpinnerDateModel; import javax.swing.JPanel; import javax.swing.Box; import java.awt.BorderLayout; import java.awt.GridBagLayout; import javax.swing.BoxLayout; import java.awt.Component; import javax.swing.JButton; import javax.swing.JSpinner; import javax.swing.JLabel; import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.JScrollPane; import javax.swing.JScrollBar; import javax.swing.JPopupMenu; import javax.swing.JMenuItem; import javax.swing.JCheckBoxMenuItem; import javax.swing.BorderFactory; import javax.swing.border.TitledBorder; import static java.lang.Math.min; import static java.lang.Math.max; import junotu.Main; import junotu.Window.Tab; import junotu.Window.TabInterface; import junotu.Card; import junotu.ColumnCardWidget; public class TabCalendarBoard extends JPanel implements TabInterface, ActionListener, MouseListener, ChangeListener { final static int COLUMN_CONTENT_WIDTH = 256; final static int COLUMN_WIDTH = COLUMN_CONTENT_WIDTH+16; final static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); final static SimpleDateFormat TITLE_DATE_FORMAT = new SimpleDateFormat("dd MMMM, yyyy"); final static SimpleDateFormat DAY_OF_THE_WEEK_FORMAT = new SimpleDateFormat("EEEE"); final static SimpleDateFormat DEBUG_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); public final String KEY_ACTION_BACK = "back"; public final String KEY_ACTION_COMMIT = "commit"; public final String KEY_ACTION_CARD_UP = "card_up"; public final String KEY_ACTION_CARD_DOWN = "card_down"; public final String KEY_ACTION_CARD_FULL_UP = "card_full_up"; public final String KEY_ACTION_CARD_FULL_DOWN = "card_full_down"; public final String KEY_ACTION_CARD_LEFT = "card_left"; public final String KEY_ACTION_CARD_RIGHT = "card_right"; public final String KEY_ACTION_CARD_FULL_LEFT = "card_full_left"; public final String KEY_ACTION_CARD_FULL_RIGHT = "card_full_right"; private class ColumnWidget extends JPanel implements ActionListener, MouseListener { long identifier; boolean newCard; Date date; TabCalendarBoard parent; TitledBorder titledBorder; Box cards; JButton addCard; public ColumnWidget( TabCalendarBoard parent_, Card card, Date date_ ) { newCard = card == null; date = date_; parent = parent_; this.setLayout( new GridBagLayout() ); cards = Box.createVerticalBox(); addCard = new JButton("+"); addCard.setFont( new Font( "Monospaced", Font.BOLD, 32 ) ); GridBagConstraints constraints = new GridBagConstraints(); constraints.anchor = GridBagConstraints.NORTHWEST; constraints.fill = GridBagConstraints.HORIZONTAL; constraints.weightx = 1.0; constraints.weighty = 0.0; constraints.gridx = 0; constraints.gridy = 0; this.add( cards, constraints ); constraints.gridy++; this.add( addCard, constraints ); constraints.gridy++; constraints.weighty = 1.0; this.add( Box.createVerticalGlue(), constraints ); addCard.setPreferredSize( new Dimension( COLUMN_CONTENT_WIDTH, 64 ) ); addCard.setMaximumSize( new Dimension( COLUMN_CONTENT_WIDTH, 64 ) ); addCard.setAlignmentX( JButton.CENTER_ALIGNMENT ); //this.setPreferredSize( new Dimension( COLUMN_WIDTH, 384 ) ); this.setMaximumSize( new Dimension( COLUMN_WIDTH, 1000000 ) ); titledBorder = BorderFactory.createTitledBorder( BorderFactory.createEtchedBorder(), "", TitledBorder.LEADING, TitledBorder.TOP, new Font( "Monospaced", Font.BOLD, 16 ) ); this.setBorder( BorderFactory.createCompoundBorder( BorderFactory.createEmptyBorder( 0, 8, 0, 8 ), titledBorder ) ); addMouseListener(this); registerKeyboardAction( this, KEY_ACTION_CARD_UP, KeyStroke.getKeyStroke( KeyEvent.VK_UP, InputEvent.ALT_DOWN_MASK ), WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); registerKeyboardAction( this, KEY_ACTION_CARD_DOWN, KeyStroke.getKeyStroke( KeyEvent.VK_DOWN, InputEvent.ALT_DOWN_MASK ), WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); registerKeyboardAction( this, KEY_ACTION_CARD_FULL_UP, KeyStroke.getKeyStroke( KeyEvent.VK_PAGE_UP, InputEvent.ALT_DOWN_MASK ), WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); registerKeyboardAction( this, KEY_ACTION_CARD_FULL_DOWN, KeyStroke.getKeyStroke( KeyEvent.VK_PAGE_DOWN, InputEvent.ALT_DOWN_MASK ), WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); registerKeyboardAction( parent, KEY_ACTION_CARD_LEFT, KeyStroke.getKeyStroke( KeyEvent.VK_LEFT, InputEvent.ALT_DOWN_MASK ), WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); registerKeyboardAction( parent, KEY_ACTION_CARD_RIGHT, KeyStroke.getKeyStroke( KeyEvent.VK_RIGHT, InputEvent.ALT_DOWN_MASK ), WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); registerKeyboardAction( parent, KEY_ACTION_CARD_FULL_LEFT, KeyStroke.getKeyStroke( KeyEvent.VK_HOME, InputEvent.ALT_DOWN_MASK ), WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); registerKeyboardAction( parent, KEY_ACTION_CARD_FULL_RIGHT, KeyStroke.getKeyStroke( KeyEvent.VK_END, InputEvent.ALT_DOWN_MASK ), WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ); addCard.addActionListener(this); addCard.setToolTipText("Add card."); this.setToolTipText( DAY_OF_THE_WEEK_FORMAT.format(date) ); if( newCard ) { return; } identifier = card.identifierGet().longValue(); titleSet(card.titleGet()); String cardsString = card.<String>tagGetAsOr( Card.TAG_CALENDAR_BOARD_COLUMN_CARDS, "" ); Card[] cardsSplit = TagUtility.parseCardList(cardsString); for( int i = 0; i < cardsSplit.length; i++ ) { if( cardsSplit[i] == null ) { System.out.print("Column '"+card.titleGet()+"', identifier "+Long.toString(card.identifierGet())+": Failed to retrieve card identifier by index "+Integer.toString(i)+". Full cards tag: '"+cardsString+"'\n"); insertCard( null, -1 ); continue; } insertCard( cardsSplit[i], -1 ); } } public void titleSet( String title ) { titledBorder.setTitle(title); repaint(); } public String titleGet() { return titledBorder.getTitle(); } public void insertCard( Card card, int at ) { ColumnCardWidget cardWidget = new ColumnCardWidget( card ); insertCardRaw( cardWidget, at ); } public void insertCardRaw( ColumnCardWidget cardWidget, int at ) { if( at == -1 ) { at = cards.getComponentCount(); } cardWidget.addMouseListener(this); /* TODO: Check if works properly. */ cards.add( cardWidget, at ); cards.revalidate(); } public void moveCard( int at, int to ) { if( at < 0 ) { return; } ColumnCardWidget cardWidget = (ColumnCardWidget)cards.getComponent(at); boolean focused = cardWidget.isSelected(); cards.remove(at); cards.add( cardWidget, to ); cards.revalidate(); if( focused ) { cardWidget.select(); } } public ColumnCardWidget popCard( int at ) { if( at < 0 ) { return null; } ColumnCardWidget cardWidget = (ColumnCardWidget)cards.getComponent(at); cards.remove(at); cardWidget.removeMouseListener(this); checkCardCount(); return cardWidget; } public int selectedCard() { Component[] cardList = cards.getComponents(); for( int i = 0; i < cardList.length; i++ ) { if( ((ColumnCardWidget)cardList[i]).isSelected() ) { return i; } } System.out.print("Selected card not found."); return -1; } public int cardCount() { return cards.getComponentCount(); } public void save() { Component[] cardList = cards.getComponents(); String cardIdentifiers = ""; for( int i = 0; i < cardList.length; i++ ) { ColumnCardWidget cardWidget = (ColumnCardWidget)cardList[i]; cardWidget.save(); if( cardIdentifiers.length() > 0 ) { cardIdentifiers += " "; } cardIdentifiers += Long.toString(cardWidget.identifier); } if( cardList.length == 0 ) { if( !newCard ) { Main.database.cardDeleteByIdentifier(identifier); identifier = -1; newCard = true; } return; } Card card; if( newCard ) { card = new Card(); } else { try { card = Main.database.cardGetByIdentifier(identifier); } catch( Exception e ) { throw new RuntimeException(e); } if( card == null ) { throw new RuntimeException("Board column update: card not found."); } } card.titleSet( titleGet() ); card.tagValueSetOnly( Card.TAG_CALENDAR_BOARD_COLUMN, null ); card.tagValueSetOnly( Card.TAG_CALENDAR_BOARD_COLUMN_DATE, DATE_FORMAT.format(date) ); card.tagValueSetOnly( Card.TAG_CALENDAR_BOARD_COLUMN_CARDS, cardIdentifiers ); try { if( newCard ) { identifier = Main.database.cardAdd( card ); newCard = false; } else { Main.database.cardUpdate( card, false ); } } catch( Exception e ) { throw new RuntimeException(e); } } public void delete() { save(); parent = null; if( !newCard ) { Main.database.cardDeleteByIdentifier(identifier); } } public void checkCardCount() { if( cardCount() == 0 ) { columnIsEmpty(this); } } public void actionPerformed( ActionEvent e ) { Object source = e.getSource(); if( source == this ) { int selected = selectedCard(); int length = cards.getComponentCount(); switch( e.getActionCommand() ) { case KEY_ACTION_CARD_UP: { System.out.print("Move card up.\n"); moveCard( selected, max(selected-1, 0) ); break; } case KEY_ACTION_CARD_DOWN: { System.out.print("Move card down.\n"); moveCard( selected, min(selected+1, cards.getComponentCount()-1) ); break; } case KEY_ACTION_CARD_FULL_UP: { System.out.print("Move card full up.\n"); moveCard( selected, 0 ); break; } case KEY_ACTION_CARD_FULL_DOWN: { System.out.print("Move card full down.\n"); moveCard( selected, cards.getComponentCount()-1 ); break; } } } else if( source == addCard ) { insertCard( null, -1 ); } } public void mouseClicked( MouseEvent e ) { Object source = e.getSource(); if( source instanceof ColumnCardWidget ) { if( e.getButton() == MouseEvent.BUTTON2 ) { ColumnCardWidget cardWidget = (ColumnCardWidget)e.getSource(); cardWidget.delete(); cards.remove(cardWidget); cards.revalidate(); checkCardCount(); } } } public void mouseEntered( MouseEvent e ) {} public void mouseExited( MouseEvent e ) {} public void mousePressed( MouseEvent e ) {} public void mouseReleased( MouseEvent e ) {} } long identifier = -1; Box columns; JScrollPane scroll; boolean optionOnlyFilledColumns; JButton back; JButton options; JSpinner dateRangeBegin; JSpinner dateRangeEnd; JPopupMenu menu; JCheckBoxMenuItem menu_onlyFilled; public TabCalendarBoard() { this.setLayout( new BorderLayout() ); back = new JButton("Back"); options = new JButton("="); dateRangeBegin = new JSpinner( new SpinnerDateModel() ); dateRangeEnd = new JSpinner( new SpinnerDateModel() ); JSpinner.DateEditor dateRangeBeginEditor = (JSpinner.DateEditor)dateRangeBegin.getEditor(); JSpinner.DateEditor dateRangeEndEditor = (JSpinner.DateEditor)dateRangeEnd.getEditor(); Calendar calendar = Calendar.getInstance(); menu = new JPopupMenu("Options"); menu_onlyFilled = new JCheckBoxMenuItem("Show only filled days"); menu.add(menu_onlyFilled); Box bottom = Box.createHorizontalBox(); columns = Box.createHorizontalBox(); scroll = new JScrollPane( columns ); bottom.add( back ); bottom.add( Box.createHorizontalGlue() ); bottom.add( dateRangeBegin ); bottom.add( dateRangeEnd ); bottom.add( options ); this.add( scroll, BorderLayout.CENTER ); this.add( bottom, BorderLayout.SOUTH ); calendar.set( Calendar.HOUR_OF_DAY, 0 ); calendar.set( Calendar.MINUTE, 0 ); calendar.set( Calendar.SECOND, 0 ); calendar.set( Calendar.MILLISECOND, 0 ); dateRangeBeginEditor.getFormat().applyPattern("yyyy-MM-dd EEEE"); dateRangeEndEditor.getFormat().applyPattern("yyyy-MM-dd EEEE"); dateRangeBeginEditor.getModel().setValue(calendar.getTime()); calendar.add( Calendar.DAY_OF_MONTH, 6 ); dateRangeEndEditor.getModel().setValue(calendar.getTime()); dateRangeBeginEditor.getModel().setCalendarField( Calendar.DAY_OF_MONTH ); dateRangeEndEditor.getModel().setCalendarField( Calendar.DAY_OF_MONTH ); scroll.getHorizontalScrollBar().setUnitIncrement(COLUMN_WIDTH); scroll.getVerticalScrollBar().setUnitIncrement(64); /* TODO: FIXME: Magic number. */ dateRangeBegin.addChangeListener(this); dateRangeEnd.addChangeListener(this); back.addActionListener(this); options.addActionListener(this); menu_onlyFilled.addActionListener(this); registerKeyboardAction( this, KEY_ACTION_BACK, KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ), WHEN_IN_FOCUSED_WINDOW ); registerKeyboardAction( this, KEY_ACTION_COMMIT, KeyStroke.getKeyStroke( KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK ), WHEN_IN_FOCUSED_WINDOW ); back.setToolTipText("Go back to where the calendar board was accessed from. Can also use [ESC]."); options.setToolTipText("Show calendar options."); menu_onlyFilled.setToolTipText("If checked, days with no cards will be hidden."); } public void boardEdit() { if( identifier != -1 ) { populateColumns(); return; } Card card; Query query = new WildcardQuery( new Term(Card.TAG_CALENDAR_BOARD, "*") ); Card cards[] = Main.database.searchCustom( query, 1, false ).cards; if( cards.length == 0 ) { card = new Card(); identifier = Main.database.cardAdd(card); boardSave(); } else if( cards.length == 1 ) { card = cards[0]; identifier = card.identifierGet().longValue(); } else { throw new RuntimeException(); } optionOnlyFilledColumns = card.optionGetBool( Card.TAG_CALENDAR_BOARD_OPTION_ONLY_FILLED, false ); menu_onlyFilled.setSelected(optionOnlyFilledColumns); populateColumns(); scroll.getHorizontalScrollBar().setValue(0); } public Card boardSave() { Card card; card = Main.database.cardGetByIdentifier(identifier); if( card == null ) { throw new RuntimeException(""+identifier); } card.titleSet( "JUnotu calendar board" ); card.tagValueSetOnly( Card.TAG_CALENDAR_BOARD, null ); card.optionSetBool( Card.TAG_CALENDAR_BOARD_OPTION_ONLY_FILLED, optionOnlyFilledColumns ); Component[] columnsList = columns.getComponents(); for( int i = 0; i < columnsList.length; i++ ) { ColumnWidget column = (ColumnWidget)columnsList[i]; column.save(); } Main.database.cardUpdate( card, true ); Main.refreshSearches(); return card; } public void boardReset() { columns.removeAll(); } public void populateColumns() { JSpinner.DateEditor dateRangeBeginEditor = (JSpinner.DateEditor)dateRangeBegin.getEditor(); JSpinner.DateEditor dateRangeEndEditor = (JSpinner.DateEditor)dateRangeEnd.getEditor(); Date begin = dateRangeBeginEditor.getModel().getDate(); Date end = dateRangeEndEditor.getModel().getDate(); Component[] columnList = columns.getComponents(); if( begin.after(end) ) { ColumnWidget columnWidget; for( int i = 0; i < columnList.length; i++ ) { columnWidget = (ColumnWidget)columnList[i]; columnWidget.save(); } columns.removeAll(); columns.validate(); columns.repaint(); return; } for( int i = 0; i < columnList.length; i++ ) { ColumnWidget columnWidget = (ColumnWidget)columnList[i]; if( columnWidget.date.before(begin) || columnWidget.date.after(end) ) { columnWidget.save(); columns.remove( columnWidget ); } } Term term = new Term(Card.TAG_CALENDAR_BOARD_COLUMN_DATE); Calendar cur = Calendar.getInstance(); cur.setTime(begin); Date curTime = begin; int insertPosition = 0; for( ; (curTime = cur.getTime()).before(end) || curTime.equals(end); cur.add( Calendar.DAY_OF_MONTH, 1 ) ) { int column = findColumn(curTime); if( column != -1 ) { ColumnWidget columnWidget = getColumnByIndex(column); if( !optionOnlyFilledColumns || columnWidget.cardCount() != 0 ) { insertPosition = column+1; } else { columnWidget.save(); columns.remove( columnWidget ); } continue; } Card card = null; term = term.createTerm( DATE_FORMAT.format(curTime) ); Card cards[] = Main.database.searchCustom( new TermQuery(term), 1, false ).cards; if( cards.length != 0 ) { card = cards[0]; } if( optionOnlyFilledColumns && card == null ) { continue; } insertColumn( curTime, card, insertPosition ); insertPosition++; } columns.validate(); columns.repaint(); } public void insertColumn( Date date, Card card, int insertPosition ) { ColumnWidget column = new ColumnWidget(this, card, date); column.titleSet( TITLE_DATE_FORMAT.format(date) ); insertColumnRaw(column, insertPosition); } public void insertColumnRaw( ColumnWidget column, int insertPosition ) { column.addMouseListener(this); columns.add(column, insertPosition); columns.revalidate(); } public int findColumn( ColumnWidget columnWidget ) { Component[] columnsList = columns.getComponents(); for( int i = 0; i < columnsList.length; i++ ) { if( columnsList[i] == columnWidget ) { return i; } } return -1; } public int findColumn( Date date ) { Component[] columnsList = columns.getComponents(); for( int i = 0; i < columnsList.length; i++ ) { if( ((ColumnWidget)columnsList[i]).date.equals(date) ) { return i; } } return -1; } public ColumnWidget getColumnByIndex( int index ) { Component[] columnsList = columns.getComponents(); return (ColumnWidget)columnsList[index]; } public void removeColumn( ColumnWidget columnWidget ) { columnWidget.delete(); columns.remove( columnWidget ); columns.validate(); columns.repaint(); } public void columnIsEmpty( ColumnWidget columnWidget ) { if( optionOnlyFilledColumns ) { removeColumn(columnWidget); } } public void moveCard( int from, int at, int to ) { ColumnWidget fromColumn = (ColumnWidget)columns.getComponent(from); ColumnWidget toColumn = (ColumnWidget)columns.getComponent(to); ColumnCardWidget cardWidget = fromColumn.popCard(at); if( cardWidget == null ) { return; } toColumn.insertCardRaw(cardWidget, min(at, toColumn.cardCount()) ); cardWidget.select(); } public void buttonClickedAsCard() { Card card = boardSave(); Window window = (Window)this.getTopLevelAncestor(); window.tabEdit.cardEdit(card); window.tabSwitch( Tab.EDIT ); } public void buttonClickedBack() { boardSave(); boardReset(); Window window = (Window)this.getTopLevelAncestor(); window.tabSwitch( Tab.SEARCH ); } public void onSwitchedTo() { boardEdit(); } public void actionPerformed( ActionEvent e ) { Object source = e.getSource(); if( source == this ){ switch( e.getActionCommand() ) { case KEY_ACTION_COMMIT: { boardSave(); return; } case KEY_ACTION_BACK: { buttonClickedBack(); return; } } } else if( source == back ) { buttonClickedBack(); return; } else if( source == options ) { menu.show( (Component)source, 0, 0 ); return; } else if( source == menu_onlyFilled ) { optionOnlyFilledColumns = menu_onlyFilled.isSelected(); populateColumns(); return; } if( source instanceof ColumnWidget ) { ColumnWidget sourceColumn = (ColumnWidget)e.getSource(); int columnIndex = findColumn(sourceColumn); switch( e.getActionCommand() ){ case KEY_ACTION_CARD_LEFT: { moveCard( columnIndex, sourceColumn.selectedCard(), max( columnIndex-1, 0) ); break; } case KEY_ACTION_CARD_RIGHT: { moveCard( columnIndex, sourceColumn.selectedCard(), min( columnIndex+1, columns.getComponentCount()-1) ); break; } case KEY_ACTION_CARD_FULL_LEFT: { moveCard( columnIndex, sourceColumn.selectedCard(), 0 ); break; } case KEY_ACTION_CARD_FULL_RIGHT: { moveCard( columnIndex, sourceColumn.selectedCard(), columns.getComponentCount()-1 ); break; } } } } public void mouseClicked( MouseEvent e ) { if( e.getButton() == MouseEvent.BUTTON2 ) { if( !(e.getSource() instanceof ColumnWidget) ) return; removeColumn( (ColumnWidget)e.getSource() ); } } public void mouseEntered( MouseEvent e ) {} public void mouseExited( MouseEvent e ) {} public void mousePressed( MouseEvent e ) {} public void mouseReleased( MouseEvent e ) {} public void stateChanged( ChangeEvent e ) { Object source = e.getSource(); if( source == dateRangeBegin || source == dateRangeEnd ) { populateColumns(); } } }