Mercurial Hosting > junotu
view src/junotu/TabEdit.java @ 109:c5bd33a186fa
Changed 'Cancel' button to just 'Back' if no changes were made
author | Fox |
---|---|
date | Mon, 29 May 2023 01:36:38 +0200 |
parents | 33f090b497c8 |
children | d63c1d41f364 |
line wrap: on
line source
package junotu; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.MenuElement; import javax.swing.event.DocumentListener; import javax.swing.event.DocumentEvent; import javax.swing.JPanel; import java.awt.BorderLayout; import java.awt.FlowLayout; import javax.swing.Box; import javax.swing.JButton; import javax.swing.JTextField; import javax.swing.JTextArea; import javax.swing.JScrollPane; import javax.swing.JScrollBar; import javax.swing.JPopupMenu; import javax.swing.JMenuItem; import junotu.Main; import junotu.Database; import junotu.Window.Tab; import junotu.GUIToolbox; import junotu.Card; public class TabEdit extends JPanel implements ActionListener, MouseListener { private class TagWidget extends JButton { public String tag; public Object value; public TagWidget( String tag, Object value ) { super(); this.tag = tag; this.value = value; this.setToolTipText("Click to edit this tag. Use ':' to delimit name and value."); update(); } public void update() { if( value != null ) { setText( tag+": "+value.toString() ); } else { setText( tag ); } } } private final String KEY_ACTION_BACK = "back"; private final String KEY_ACTION_SAVE = "save"; public Card card = null; public boolean newCard = true; public boolean dirty = true; /* Means has unsaved changes. */ public boolean ghost = false; /* Means was deleted, but still remains in current tab. Can only be new, and has special saving rules. */ private TagWidget editedTag = null; private JTextField editedTagField; private JScrollPane scroll; private JTextField title; private JTextArea content; private JPanel tags; private JButton addTag; private JButton back; private JButton delete; private JButton editAsBoard; private JButton save; private JPopupMenu tagMenu; private JMenuItem tagMenu_OpenUri; private JMenuItem tagMenu_CopyValue; public TabEdit() { this.setLayout( new BorderLayout() ); JPanel scrollContent = new JPanel( new GUIToolbox.CardEditLayout() ); scroll = new JScrollPane( scrollContent ); title = new JTextField(); content = new JTextArea(); tags = new JPanel(); editedTagField = new JTextField(); addTag = new JButton("+"); Box bottom = Box.createHorizontalBox(); back = new JButton(); /* Text set in 'updateStatus()'. */ delete = new JButton("Delete"); editAsBoard = new JButton("As board"); save = new JButton("Save"); tagMenu = new JPopupMenu("Tag menu"); tagMenu_OpenUri = tagMenu.add("Open as URI"); tagMenu_CopyValue = tagMenu.add("Copy value"); tags.setLayout( new FlowLayout() ); title.setFont( new Font( "Monospaced", Font.PLAIN, 32 ) ); content.setFont( new Font( "Monospaced", Font.PLAIN, 16 ) ); editedTagField.setFont( new Font( "Monospaced", Font.PLAIN, 12 ) ); scroll.getVerticalScrollBar().setUnitIncrement(16); title.setMaximumSize( new Dimension( Integer.MAX_VALUE, 64 ) ); //content.setMaximumSize( new Dimension( Integer.MAX_VALUE, Integer.MAX_VALUE ) ); content.setLineWrap( true ); /* TODO: Figure out tags layout mess. */ //tags.setPreferredSize( new Dimension( 16, 256 ) ); //tags.setMaximumSize( new Dimension( Integer.MAX_VALUE, Integer.MAX_VALUE ) ); this.add( scroll, BorderLayout.CENTER ); scrollContent.add( title ); //scrollContent.add( Box.createVerticalStrut(10) ); scrollContent.add( content ); scrollContent.add( tags ); this.add( bottom, BorderLayout.SOUTH ); bottom.add( back ); bottom.add( Box.createHorizontalGlue() ); bottom.add( delete ); bottom.add( editAsBoard ); bottom.add( save ); //scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); back.addActionListener(this); delete.addActionListener(this); editAsBoard.addActionListener(this); save.addActionListener(this); addTag.addActionListener(this); MenuElement tagMenuElements[] = tagMenu.getSubElements(); for( int i = 0; i < tagMenuElements.length; i++ ) { if( tagMenuElements[i] instanceof JMenuItem ) { ((JMenuItem)tagMenuElements[i]).addActionListener(this); } } editedTagField.addFocusListener( new FocusListener() { @Override public void focusGained(FocusEvent e) { } @Override public void focusLost(FocusEvent e) { tagCommit(); } } ); DocumentListener documentListener = new DocumentListener() { @Override public void changedUpdate( DocumentEvent e ) { markDirty(); } @Override public void removeUpdate( DocumentEvent e ) { markDirty(); } @Override public void insertUpdate( DocumentEvent e ) { markDirty(); } } ; title.getDocument().addDocumentListener(documentListener); content.getDocument().addDocumentListener(documentListener); registerKeyboardAction( this, KEY_ACTION_BACK, KeyStroke.getKeyStroke( KeyEvent.VK_ESCAPE, 0 ), WHEN_IN_FOCUSED_WINDOW ); registerKeyboardAction( this, KEY_ACTION_SAVE, KeyStroke.getKeyStroke( KeyEvent.VK_S, InputEvent.CTRL_DOWN_MASK ), WHEN_IN_FOCUSED_WINDOW ); title.setToolTipText("Card title."); back.setToolTipText("Go back without saving. Can also use [ESC]."); delete.setToolTipText("Delete the card. There is no confirmation, nor going back."); editAsBoard.setToolTipText("Edit this card as board."); save.setToolTipText("Save and go back. Shift-click to save without exiting. Can also use [CTRL]+[S]."); addTag.setToolTipText("Add new tag."); tagMenu_OpenUri.setToolTipText("Open tag's value as URI; most commonly a file path ('file://') or URL ('http://')."); tagMenu_CopyValue.setToolTipText("Copy tag's value to clipboard."); } private void scrollTop() { JScrollBar scrollbar = scroll.getVerticalScrollBar(); scrollbar.setValue(0); } private void scrollBottom() { JScrollBar scrollbar = scroll.getVerticalScrollBar(); int maximum = scrollbar.getMaximum()-scrollbar.getVisibleAmount(); scrollbar.setValue(maximum); } public void cardCreate() { newCard = true; ghost = false; card = new Card(); dirty = true; updateStatus(); delete.setVisible(false); updateTags(); } public void cardEdit( Card card ) { newCard = false; ghost = false; this.card = card; title.setText( card.titleGet() ); content.setText( card.contentGet() ); dirty = false; updateStatus(); delete.setVisible(true); updateTags(); /* TODO: Is this needed? */ SwingUtilities.invokeLater( new Runnable() { public void run() { scrollTop(); } } ); } public void markDirty() { dirty = true; updateStatus(); } public void markGhost() { ghost = true; newCard = true; dirty = true; updateStatus(); } public void linkGhost( Card card ) { if( !ghost ) { throw new RuntimeException(); } newCard = false; ghost = false; dirty = true; card.tagValueSetOnly( Card.TAG_IDENTIFIER, card.identifierGet() ); } private void reset() { title.setText(""); content.setText(""); card = null; ghost = false; } private void updateStatus() { Window window = (Window)this.getTopLevelAncestor(); String text = title.getText(); String action; if( newCard ) { action = "Create"; } else if( dirty ) { action = "Edit*"; } else { action = "Edit"; } if( text.length() > 0 ) { window.setTitle( window.preferredTitle( action+": "+text ) ); } else { window.setTitle( window.preferredTitle( action ) ); } if( !dirty ) { back.setText("Back"); back.revalidate(); /* Seems slow to change to 'back' otherwise. */ } else { back.setText("Cancel"); } } private void updateTags() { tags.removeAll(); TagWidget newWidget; for( String tag : card.tagNames() ) { if( tag.startsWith("_") ) { continue; } for( Object value : card.tagValues( tag ) ) { newWidget = new TagWidget( tag, value ); tags.add( newWidget ); newWidget.addActionListener(this); newWidget.addMouseListener(this); } } tags.add( addTag ); tags.validate(); tags.repaint(); } private void tagAdd() { tags.add( editedTagField, GUIToolbox.componentGetIndex( addTag ) ); editedTagField.setText( "" ); editedTagField.setColumns(12); editedTagField.grabFocus(); tags.validate(); tags.repaint(); System.out.print( "Opened new tag for editing.\n" ); } private void tagRemove( TagWidget tagWidget ) { System.out.print( "Removed tag '"+tagWidget.tag+"' with value '"+tagWidget.value+"'.\n" ); card.tagValueRemove( tagWidget.tag, tagWidget.value ); tags.remove( tagWidget ); markDirty(); tags.validate(); tags.repaint(); } private void tagEdit( TagWidget tagWidget ) { editedTag = tagWidget; tags.add( editedTagField, GUIToolbox.componentGetIndex( editedTag ) ); if( editedTag.value != null ) { editedTagField.setText( editedTag.tag+":"+editedTag.value.toString() ); } else { editedTagField.setText( editedTag.tag ); } editedTagField.setColumns(0); editedTagField.grabFocus(); editedTag.setVisible(false); tags.validate(); tags.repaint(); System.out.print( "Opened existing tag for editing.\n" ); } private void tagCommit() { String newTag; Object newValue; { String[] split = editedTagField.getText().split( ":", 2 ); newTag = split[0]; if( split.length > 1 && !split[1].equals("") ) { newValue = split[1]; } else { newValue = null; } } /* Either editing tag, or adding a new one. */ if( editedTag != null ) { String oldTag = editedTag.tag; Object oldValue = editedTag.value; logTagChange( oldTag, oldValue, newTag, newValue ); if( oldTag.equals(newTag) ) { if( ( oldValue != null && !oldValue.equals(newValue) ) || ( oldValue == null && oldValue != newValue ) ) { card.tagValueReplace( oldTag, oldValue, newValue ); editedTag.value = newValue; editedTag.update(); markDirty(); } } else { /* Replace tag with another one. */ card.tagValueRemove( oldTag, oldValue ); card.tagValueAdd( newTag, newValue ); if( !newTag.equals("") ) { editedTag.tag = newTag; editedTag.value = newValue; editedTag.update(); } else { tags.remove( editedTag ); } markDirty(); } editedTag.setVisible( true ); } else { /* Adding new tag (value). */ if( !newTag.equals("") ) { /* Only add new tag button if the tag value is unique. */ if( card.tagValueAdd( newTag, newValue ) ) { TagWidget newWidget = new TagWidget( newTag, newValue ); tags.add( newWidget, GUIToolbox.componentGetIndex( addTag )-1 ); newWidget.addActionListener(this); newWidget.addMouseListener(this); logTagChange( "", null, newTag, newValue ); } else { logTagChange( "", null, "", null ); } markDirty(); } else { logTagChange( "", null, "", null ); } } editedTagField.setText( "" ); tags.remove( editedTagField ); editedTag = null; tags.validate(); tags.repaint(); } private void logTagChange( String oldTag, Object oldValue, String newTag, Object newValue ) { System.out.print( "Comitted changes to tag: " ); if( oldTag.equals("") ) { /* Creating tag. */ if( !newTag.equals("") ) { if( newValue == null ) { /* No value. */ System.out.print( "Added tag '"+newTag+"' (with no value)\n" ); } else { /* Has value. */ System.out.print( "Added tag '"+newTag+"' with value '"+newValue.toString()+"'\n" ); } } else { System.out.print( "No changes.\n" ); } } else { /* Editing tag. */ if( oldTag.equals(newTag) ) { /* Same tag. */ if( oldValue == null ) { if( newValue == null ) { /* Clear before, clear now. */ System.out.print( "No changes (tag '"+oldTag+"' with no value)\n" ); } else { /* Assigned value. */ System.out.print( "Assigned value of tag '"+oldTag+"' to '"+newValue.toString()+"'\n" ); } } else { if( newValue == null ) { /* Clearing value. */ System.out.print( "Cleared value of tag '"+oldTag+"' (was '" +oldValue.toString()+"')\n" ); } else if( oldValue.equals(newValue) ) { /* Value is the same. */ System.out.print( "No changes (tag '"+oldTag+"' with value '"+oldValue+"')\n" ); } else { /* Changing value. */ System.out.print( "Changed value of tag '"+oldTag+"' from '" +oldValue.toString()+"' to '"+newValue.toString()+"'\n" ); } } } else { /* Replaced tag. */ if( newTag.equals("") ) { /* Removing tag. */ if( oldValue == null ) { System.out.print( "Removed tag '"+oldTag+"' (with no value)\n" ); } else { System.out.print( "Removed tag '"+oldTag+"' with value '"+oldValue.toString()+"'\n" ); } } else { if( oldValue == null ) { if( newValue == null ) { System.out.print( "Renamed tag '"+oldTag+"' -> '" +newTag+"'\n" ); } else { System.out.print( "Changed tag '"+oldTag+"' -> '" +newTag+"': '"+newValue.toString()+"'\n" ); } } else { if( newValue == null ) { System.out.print( "Changed tag '"+oldTag+"': '"+oldValue.toString()+"' -> '" +newTag+"'\n" ); } else { System.out.print( "Changed tag '"+oldTag+"': '"+oldValue.toString()+"' -> '" +newTag+"': '"+newValue.toString()+"'\n" ); } } } } } } private void buttonClickedBack() { Window window = (Window)this.getTopLevelAncestor(); reset(); window.tabSwitch( Tab.SEARCH ); } private void buttonClickedDelete() { if( newCard ) { return; } Main.database.cardDeleteByIdentifier( card.identifierGet().longValue() ); Window window = (Window)this.getTopLevelAncestor(); Main.actionCardDeleted( window, card ); reset(); window.tabSwitch( Tab.SEARCH ); } private void buttonClickedEditAsBoard() { Window window = (Window)this.getTopLevelAncestor(); buttonClickedSave(true); window.tabBoard.boardEdit(card); reset(); window.tabSwitch( Tab.BOARD ); } private void buttonClickedSave( boolean noSwitch ) { Window window = (Window)this.getTopLevelAncestor(); card.titleSet( title.getText() ); card.contentSet( content.getText() ); if( newCard ) { if( !ghost ) { card.tagRemove( Card.TAG_IDENTIFIER ); /* Make sure to use unique identifier. */ } Main.database.cardAdd( card ); if( ghost ) { Main.actionCardLinkGhosts( window, card ); } } else { Main.database.cardUpdate( card, true ); } dirty = false; Main.actionCardSaved( window, card ); if( noSwitch ) { if( newCard ) { cardEdit( this.card ); } else { updateStatus(); /* Dirty state might have changed. */ } } else { reset(); window.tabSwitch( Tab.SEARCH ); } } private boolean possiblyShowTagContextMenu( MouseEvent e ) { if( e.isPopupTrigger() ) { TagWidget tagWidget = (TagWidget)e.getSource(); tagMenu_OpenUri.setEnabled( tagWidget.value instanceof String ); tagMenu_CopyValue.setEnabled( tagWidget.value != null ); tagMenu.show( tagWidget, e.getX(), e.getY() ); return true; } else { return false; } } public void actionPerformed( ActionEvent e ) { Object source = e.getSource(); if( source == this ) { switch( e.getActionCommand() ) { case KEY_ACTION_BACK: { buttonClickedBack(); break; } case KEY_ACTION_SAVE: { buttonClickedSave( true ); Main.database.databaseCommit(); break; } } } else if( source == back ) { buttonClickedBack(); } else if( source == editAsBoard ) { buttonClickedEditAsBoard(); } else if( source == delete ) { buttonClickedDelete(); } else if( source == save ) { boolean noSwitch = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0; buttonClickedSave( noSwitch ); } else if( source == addTag ) { tagAdd(); } else if( source instanceof TagWidget ) { tagEdit( (TagWidget)source ); } else if( source == tagMenu_OpenUri ) { TagWidget tagWidget = (TagWidget)tagMenu.getInvoker(); Main.actionOpenURI( (String)tagWidget.value ); } else if( source == tagMenu_CopyValue ) { TagWidget tagWidget = (TagWidget)tagMenu.getInvoker(); Main.clipboardSet( String.valueOf(tagWidget.value) ); } } public void mouseEntered( MouseEvent e ) {} public void mouseExited( MouseEvent e ) {} public void mouseClicked( MouseEvent e ) { Object source = e.getSource(); if( e.getButton() == MouseEvent.BUTTON2 && source instanceof TagWidget ) { tagRemove( (TagWidget)source ); } } public void mousePressed( MouseEvent e ) { if( possiblyShowTagContextMenu(e) ) { return; } } public void mouseReleased( MouseEvent e ) { if( possiblyShowTagContextMenu(e) ) { return; } } }