view src/junotu/TabEdit.java @ 52:7cf2788649a7

Added some tooltips
author Fox
date Fri, 25 Nov 2022 21:16:38 +0100
parents 1cad42247892
children a2696310fa8c
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 javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
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 junotu.Main;
import junotu.Database;
import junotu.Window.Tab;
import junotu.GUIToolbox;
import junotu.Card;

public class TabEdit extends JPanel implements ActionListener {

    private class TagWidget extends JButton implements ActionListener {
	
	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.");

	    addActionListener(this);
	    
	    update();
	}

	public void update()
	{
	    if( value != null ) {
		setText( tag+": "+value.toString() );
	    } else {
		setText( tag );
	    }	    
	}

	public void actionPerformed( ActionEvent e )
	{
	    /* Not exactly sure how this works, but it does. */
	    tagEdit( this );
	}
	
    }

    private final String KEY_ACTION_BACK = "back";
    private final String KEY_ACTION_SAVE = "save";
    
    private Card card = null;
    private boolean newCard = true;

    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 save;

    
    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("Cancel");
	delete = new JButton("Delete");
	save   = new JButton("Save");

	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( save );

	//scroll.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
	
	back.addActionListener(this);
	delete.addActionListener(this);
	save.addActionListener(this);
	addTag.addActionListener(this);

	editedTagField.addFocusListener(
					new FocusListener()
					{
					    @Override
					    public void focusGained(FocusEvent e)
					    {
					    }
					    
					    @Override
					    public void focusLost(FocusEvent e)
					    {
						tagCommit();
					    }
					}
					);

	title.getDocument().addDocumentListener(
						new DocumentListener()
						{
						    @Override
						    public void changedUpdate( DocumentEvent e )
						    {
							updateTitle();
						    }
						    @Override
						    public void removeUpdate( DocumentEvent e )
						    {
							updateTitle();
						    }
						    @Override
						    public void insertUpdate( DocumentEvent e )
						    {
							updateTitle();
						    }
						}
						);

	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.");
	save.setToolTipText("Save and go back. Shift-click to save without exiting. Can also use [CTRL]+[S].");

	addTag.setToolTipText("Add new tag.");
	
    }

    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;
	card = new Card();
	updateTitle();
	delete.setVisible(false);
	updateTags();
    }
    
    public void cardEdit( Card card )
    {
	newCard = false;
	this.card = card;
	title.setText( card.titleGet() );
	content.setText( card.contentGet() );
	updateTitle();
	delete.setVisible(true);
	updateTags();
	SwingUtilities.invokeLater(
				   new Runnable()
				   {
				       public void run()
				       {
					   scrollTop();
				       }
				   }
				   );
    }

    private void reset()
    {
	title.setText("");
	content.setText("");
	card = null;
    }

    private void updateTitle()
    {
	Window window = (Window)this.getTopLevelAncestor();

	String text = title.getText();
	String action = newCard ? "Create" : "Edit";

	if( text.length() > 0 ) {
	    window.setTitle( window.preferredTitle( action+": "+text ) );
	} else {
	    window.setTitle( window.preferredTitle( action ) );
	}
	
    }
    
    private void updateTags()
    {
	tags.removeAll();
	for( String tag : card.tagNames() ) {
	    if( tag.startsWith("_") ) {
		continue;
	    }
	    for( Object value : card.tagValues( tag ) ) {
		tags.add( new TagWidget( tag, value ) );
	    }
	}
	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 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];
	    newValue = split.length > 1 ? split[1] : 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) ) {
		card.tagValueReplace( oldTag, oldValue, newValue );
		editedTag.value = newValue;
		editedTag.update();
	    } 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 );
		}
		
	    }

	    editedTag.setVisible( true );
	    
	} else { /* Adding new tag (value). */
	    if( !newTag.equals("") ) {
		card.tagValueAdd( newTag, newValue );
		tags.add( new TagWidget( newTag, newValue ), GUIToolbox.componentGetIndex( addTag )-1 );
		logTagChange( "", null, newTag, newValue );
	    } 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;
	}
	
	try {
	    Main.database.cardDelete( card.identifierGet() );
	} catch( Exception e ) {
	    throw new RuntimeException(e);
	}
	
	Window window = (Window)this.getTopLevelAncestor();
        window.tabSearch.search();
	reset();
	window.tabSwitch( Tab.SEARCH );
	
    }
    
    private void buttonClickedSave( boolean noSwitch )
    {

	card.titleSet( title.getText() );
	card.contentSet( content.getText() );

	try {
	    if( newCard ) {
		Main.database.cardAdd( card );
	    } else {
		Main.database.cardUpdate( card );
	    }
	} catch( Exception e ) {
	    throw new RuntimeException(e);
	}

	Window window = (Window)this.getTopLevelAncestor();
	window.tabSearch.search();
	
	if( noSwitch ) {
	    if( newCard ) {
		cardEdit( this.card );
	    }
	} else {
	    reset();
	    window.tabSwitch( Tab.SEARCH );
	}
    }

    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 );
		try {
		    Main.database.databaseCommit();
		} catch( Exception ex ) {
		    System.out.print( "Failed to write database: "+ex.getMessage()+"\n" );
		}
		break;
	    }
		
	    }
	} else if( source == back ) {
	    buttonClickedBack();
        } else if( source == delete ) {
	    buttonClickedDelete();
	} else if( source == save ) {
	    boolean noSwitch = (e.getModifiers() & ActionEvent.SHIFT_MASK) != 0;
	    buttonClickedSave( noSwitch );
	} else if( source == addTag ) {
	    tagAdd();
	}
    }
    
}