Best Practice Shared Actions , Referenzen in Gui halten ?

kaoZ

Top Contributor
Aloha ich hätte da mal eine kleine Frage zu dem Wiederverwenden von Actions

ich habe folgende Situation :

Eine Action :

Java:
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import editor.enums.ClientMenu;
import editor.gui.Gui;
import editor.gui.components.ClientPanel;


@SuppressWarnings("serial")
public class ClientAction extends AbstractAction{
	
	private Gui gui;
	
	public ClientAction(Gui gui){
		super(ClientMenu.MAINTENANCE.value());
		this.gui = gui;
	}
	
	public ClientAction(Gui gui, ImageIcon icon){
		super(ClientMenu.MAINTENANCE.value(), icon);
		this.gui = gui;
	}
	
	@Override
	public void actionPerformed(ActionEvent e){
		JFrame frame = new JFrame(ClientPanel.TITLE);
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		frame.setContentPane(new ClientPanel(frame, gui));
		frame.pack();
		frame.setResizable(false);
		frame.setLocationRelativeTo(gui.getFrame());
		frame.setVisible(true);
	}
}

in der MenuBar und in der ToolBar :



MenuBar
Java:
JMenuItem clientMaintenance = new JMenuItem(new ClientAction(gui));

ToolBar
Java:
btns[5].setAction(new ClientAction(gui, new ImageIcon(clientImage)));

Hier wird aber pro klick auf einen der Buttons je eine Instanz erzeugt, wenn ich nun die Action teilen will müsste ich ja eine Referenz in der Gui halten und diese dann an die ToolBar und die MenuBar übergeben.

( diese sind jeweils in einer eigenen Klasse definiert)

würde es dann nicht doch Sinn machen , der Einfachheit halber die toolbar und die MenuBar in der Gui zu realisieren , sonst müsste ich ja wieder für jedes element einen setter bereitstellen um dann die Referenz auf die Action zu übergeben. bzw. getter um mir die elemente der Toolbar sowie Menubar zu holen und dann die Actions zu setzen.

Was wäre hier der Sinnvolllste Weg ?

Nächste frage wäre dann , wie ist es "standard" erzeuge ich schon sämtliche Frames in der Gui und mache diese bei ausführen der Action nur sichtbar, den zzt. erzeuge ich pro klick ja ein neuen JFrame welchem ich dann als contentPane einfach den gewünschten panel zuweise. ( was ich irgendwie doch als ineffizient / störend empfinde )

Ist es der gängige Weg alle Komponenten zu initialisieren und einfach erst dann sichtbar zu machen wenn man diese anzeigen will ?

 
Zuletzt bearbeitet:

kaoZ

Top Contributor
Zudem muss es doch einen schöneren Weg geben als das verwenden des Command patterns hier :

welches zu einer ( noch wachsenden ) Klassenanzahl führt :



Wie würde man diese Actions mit dem Observer-pattern umsetzen ? oder gibt es dort noch einen sinnvolleren weg ?
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
Mal angenommen ich würde das Design überdenken, und würde eine Schnittstelle bereitstellen ( mache ich augenscheinlich wirklich noch nicht oft genug....) über welche nun Die Actions mit der Gui kommunizieren könnten, wie würde sowas dann aussehen ? im normalfall würde ja das Event welches die Action behandelt, dann einfach an die Gui delegiert werden und diese würde dann die Fallentscheidung treffen ( instanceof / getClass ) und dementsprechende Komponenten Sichtbar machen / die jeweilige gewünschten Aktion ausführen.

So wie es zzt ist muss ich ja immer eine Referenz auf die Gui durchreichen... hätte einer da ein Beispiel zu der Lösung mit dem Interface ?
 

kaoZ

Top Contributor
Vielleicht so etwas in der Art, an Anlehnung an das Observer pattern ?

Java:
public interface Controllable{

	public void controll(AbstractAction a);
}

Die Gui implementiert dann das Interface und hält Referenzen auf die AbstractActions

Java:
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;


@SuppressWarnings("serial")
public class Gui extends JFrame implements Controllable{
	
	TestAction testAction;
	DefaultAction defaultAction;
	
	public Gui(){
		JMenuBar bar = new JMenuBar();
		JMenu menu = new JMenu("Datei");
		
		testAction = new TestAction(this);
		defaultAction = new DefaultAction(this);
		
		JMenuItem item = new JMenuItem(testAction);
		JMenuItem item2 = new JMenuItem(defaultAction);
		bar.add(menu);
		
		menu.add(item);
		menu.add(item2);
		
		setSize(300,300);
		setDefaultCloseOperation(2);
		setJMenuBar(bar);
		setLocationRelativeTo(null);
		
	}	

	@Override
	public void controll(AbstractAction a){
		if(a instanceof TestAction) {
			System.out.println("Test");
		}
		if(a instanceof DefaultAction) {
			System.out.println("Default");
		}
	}
	
	public static void main(String[] args){
		new Gui().setVisible(true);
	}

}

den Actions selbst würde ich dann anstelle der Referenz auf die Gui , einfach das Interface übergeben :

Java:
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;



@SuppressWarnings("serial")
public class TestAction extends AbstractAction{

	Controllable c;
	
	public TestAction(Controllable c){
		super("Klick mich");
		this.c = c;
	}
	
	@Override
	public void actionPerformed(ActionEvent e){
		c.controll(this);
	}

}

mal abgesehen von der Namensgebung würde mich interessieren ob es da noch einen anderen Weg gibt die Aktionen zu händeln , einen ggf. Sinnvolleren?!

Mit obigem Beispiel würde ich zumindest gegen ein Interface anstatt gegen eine Implementierung programmieren, die Frage ist nun macht das Sinn , dies würde allerdings immernoch nicht das Problem mit der steigenden Klassenanzahl durch nutzen des Command-Patterns bei den Actions lösen.

Vielleicht schmeißt ja doch mal einer eine Antwort hier rein ^^
 
Zuletzt bearbeitet:

Harry Kane

Top Contributor
Ehrlich gesagt, sieht dein letzter Ansatz wie ein komplizierter Weg aus, um die Logik der Ereignisbehandlung von den Actions wieder zurück in die Gui zu schieben.

Eine Action ist für die Erledigung einer bestimmten Aufgabe zuständig und muss natürlich Zugriff auf alle Komponenten und Informationen haben, die zur Erledigung der Aufgabe notwendig sind. Wenn eine Action einen Container mit neuem Inhalt versorgen soll, muss sie den Container natürlich kennen. Ich sehe keinen Sinn darin, den Container lediglich über das Auslösen des Ereignisses zu informieren, ihn dann den Typ des Ereignisses bestimmen zu lassen, und basierend darauf eine Reaktion auszulösen.

Und wenn du viele verschiedenen Aktionen ausführen lassen möchtest, braucht es eben entsprechend viele Actions. Ob du die alle als separate Klassen realisierst, oder als Enum, spielt IMHO keine wirkliche Rolle. Wenn verschiedene Actions aber etwas ähnliches tun und einen gemeinsamen Satz von Informationen brauchen, sollten sie natürlich von einer gemeinsamen Oberklasse abgeleitet werden. Ob das bei dir der Fall ist, kann ich nicht sagen.

Und zu der Frage aus deinem ersten Post: es macht IMHO keinen Sinn, die Erstellung der JMenuBar und JToolBar in eine separate Klasse auszulagern, höchstens dann, wenn du diese Klasse in einem anderen Projekt wiederverwenden möchtest. Bei der Auslagerung werden Dinge auseinandergerissen, die eigentlich zusammengehören, und müssen dann durch Referenzen aufeinander wieder miteinander bekannt gemacht werden (wie du ja selber erkannt hast). Den Wunsch nach einem kompakten Gui Code erachte ich als nicht ausreichend für eine solche Trennung. Natürlich kann man jetzt wieder ein paar Buzzwords in die Arena schmeissen ("Separation of concerns", "Trennung von Gui und Logik", "Delegation", "Kapselung"), aber die Frage ist, ob die vermeitliche Beachtung von solchen Konzepten den Code wirklich besser macht. Die ganzen Patterns wurden ja nicht entwickelt, damit man sich daran hält, sondern weil sie einen Weg aufzeigen, wie man bestimmte Probleme geschickt löst.
 

kaoZ

Top Contributor
Zu aller erst, erstmal danke für deine Antwort.

Ehrlich gesagt, sieht dein letzter Ansatz wie ein komplizierter Weg aus, um die Logik der Ereignisbehandlung von den Actions wieder zurück in die Gui zu schieben.

Jap, genau das habe ich mir dann im nach-hinein auch gedacht, und du hast recht.
Ich hatte mich hierbei daran Orientiert die Gui lediglich über ein Event außerhalb dieser zu benachrichtigen und dann dementsprechend darauf in der Gui reagieren zu können.

Eine Action ist für die Erledigung einer bestimmten Aufgabe zuständig und muss natürlich Zugriff auf alle Komponenten und Informationen haben, die zur Erledigung der Aufgabe notwendig sind.

Auch hier hast du Recht, da ich mich im Moment intensiver mit dieser Thematik beschäftige habe ich aber gelesen das diese Art ( eben eine Referenz auf die Komponente mit in die Action zu übergeben ) als "messy" also als schlechter Stil angesehen ist , deshalb ja auch die Überlegung wie man es sonst lösen könnte.

Und zu der Frage aus deinem ersten Post: es macht IMHO keinen Sinn, die Erstellung der JMenuBar und JToolBar in eine separate Klasse auszulagern, höchstens dann, wenn du diese Klasse in einem anderen Projekt wiederverwenden möchtest

Das werde ich mir mal zu Herzen nehmen, ich dachte so könnte ich zumindest den Source Code der einzelnen Klassen doch recht kurz halten , aber hier hast du auch recht , ich muss dann wieder beigehen und Referenzen hin und her reichen.

Ob du die alle als separate Klassen realisierst, oder als Enum, spielt IMHO keine wirkliche Rolle.

Das klingt interessant, könntest du mir ggf. ein kleines Beispiel aufzeigen wie man diese Actions als Enum realisieren würde ? das höre ich zum ersten mal , arbeite zwar sonst auch mit Enums aber auf die Idee wäre ich wahrscheinlich nicht gekommen :)

Die ganzen Patterns wurden ja nicht entwickelt, damit man sich daran hält, sondern weil sie einen Weg aufzeigen, wie man bestimmte Probleme geschickt löst.

Jap, richtig , sonder man sollte sie eben nur da einsetzen wo es wirklich Sinn macht , und nicht unter falschen Voraussetzungen zum Anti-pattern umfunktionieren. :)

Alles in allem erstmal vielen Dank , wenn du mir zu dem Enum noch ein Beispiel liefern könntest wäre das super.
 

Harry Kane

Top Contributor
Vergiss das mal mit den enums. Das war ohne zu denken hingeschrieben. Da alle enums von Enum erben, können sie nicht gleichzeitig von AbstractAction erben. Sie müssten aber imstande sein, ActionListener zu implementieren, was dir aber nicht sonderlich helfen dürfte.
Alternativ könnte etwas enum-ähnliches wie das hier funktionieren:
Java:
public abstract class CustomerAction extends AbstractAction{
    private Customer customer;
    public static final CustomerAction EDIT = new CustomerAction("Ändern", null){
        public void actionPerformed(ActionEvent ae){
            // z. B. einen JDialog aanzeigen, mit dem der aktuelle Customer verändert werden kann
        }
    }
    private CustomerAction(String name, Icon icon){
        super(name, icon);
    }
    public void setCustomer(Customer customer){
        this.customer = customer;
    }
    public Customer getCustomer(){
        return this.customer;
    }

}
Das würde auch gleichzeitig das Problem der Wiederverwendbarkeit lösen, da es nur eine Instanz von CustomerAction.EDIT geben kann. Allerdings wäre in dieser Variante der Konstruktor der Actions nicht mehr zugänglich, und alle Referenzen, die eine Action noch halten sollte (z. B. auf einen Container oder eine Gui), könnten nur über setter eingebracht werden, was aber auch nicht das schlechteste sein muss.
Das ist ungetestet, da ich z. Z. auf der Arbeit bin.
 

kaoZ

Top Contributor
ich habe nun erstmal die Toolbar und Menubar wieder in die Gui klasse eingebunden , das ganze ist dadurch natürlich etwas wirr, aber vielleicht kannst du ja mal drüber schauen und mit nen paar tipps geben wie man es noch etwas besser unterteilen könnte, sämtliche Aktionen welche noch nicht belegt sind sind noch mit den enums belegt welche ich dann in den Actions selbst nutze.

Vielleicht schaust du ja einfach mal drüber:)

Java:
package editor.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.JToolBar;
import editor.App;
import editor.actions.ClientAction;
import editor.actions.CloseAction;
import editor.actions.DocumentAction;
import editor.actions.EstimateAction;
import editor.actions.ExitAction;
import editor.actions.ExportAction;
import editor.actions.HelpAction;
import editor.actions.ImportAction;
import editor.actions.InfoAction;
import editor.actions.InvoiceAction;
import editor.actions.OpenAction;
import editor.actions.PrintAction;
import editor.actions.SaveAction;
import editor.actions.SearchAction;
import editor.actions.ShortCutAction;
import editor.enums.ClientMenu;
import editor.enums.EditMenu;
import editor.enums.FileMenu;
import editor.enums.HelpMenu;
import editor.enums.MenuEntry;
import editor.enums.OptionMenu;
import editor.listener.FrameListener;

public class Gui{
	
	private ArrayList<JFrame> frameList;
	
	private JFrame frame;
	private JMenuBar menuBar;
	private JToolBar toolBar;
	private WorkBench workBench;
	
	public Gui(){
		init();
		initComponents();
		initFrame();
		addComponents();
		setState();
	}
	
	private void init(){
		frameList = new ArrayList<>();
	}
	
	private void initComponents(){
		workBench	 = new WorkBench(this);
		menuBar		 = createMenuBar();
		toolBar		 = createToolBar();
	}

	private void initFrame(){
		frame = new JFrame(App.TITLE + " " + App.VERSION);
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		frame.setJMenuBar(menuBar);
		frame.addWindowListener(new FrameListener(this));
	}
	
//	JMENUBAR	
	
	private JMenuBar createMenuBar(){
		JMenuBar bar = new JMenuBar();
		
		bar.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY));
		
		JMenu fileMenu	 = new JMenu(MenuEntry.FILE.value());
		JMenu editMenu	 = new JMenu(MenuEntry.EDIT.value());
		JMenu optionMenu = new JMenu(MenuEntry.OPTIONS.value());
		JMenu clientMenu = new JMenu(MenuEntry.CLIENTS.value());
		JMenu helpMenu	 = new JMenu(MenuEntry.HELP.value());
		
		fileMenu.setMnemonic(KeyEvent.VK_D);
		editMenu.setMnemonic(KeyEvent.VK_B);
		optionMenu.setMnemonic(KeyEvent.VK_E);
		clientMenu.setMnemonic(KeyEvent.VK_K);
		helpMenu.setMnemonic(KeyEvent.VK_H);
		
//		FileMenu
		
		JMenu fileSubMenu	 = new JMenu(FileMenu.NEW.value());
		JMenuItem invoice	 = new JMenuItem(new InvoiceAction(this));
		JMenuItem estimate	 = new JMenuItem(new EstimateAction(this));
		addMenuItems(fileSubMenu, invoice, estimate);
		
		JMenuItem open		 = new JMenuItem(new OpenAction(this));
		JMenuItem close		 = new JMenuItem(new CloseAction(this));
		JMenuItem print		 = new JMenuItem(new PrintAction(this));
		JMenuItem save 		 = new JMenuItem(new SaveAction(this));
		JMenuItem importData = new JMenuItem(new ImportAction());
		JMenuItem exportData = new JMenuItem(new ExportAction());
		JMenuItem exit		 = new JMenuItem(new ExitAction(this));
		addMenuItems(fileMenu, fileSubMenu, new JSeparator(), open, close, save, new JSeparator());
		addMenuItems(fileMenu, print, new JSeparator(), importData, exportData, new JSeparator(), exit);
		
		
//		EditMenu
		
		JMenuItem cut	 = new JMenuItem(EditMenu.CUT.value());
		JMenuItem copy	 = new JMenuItem(EditMenu.COPY.value());
		JMenuItem paste	 = new JMenuItem(EditMenu.PASTE.value());
		JMenuItem delete = new JMenuItem(EditMenu.DELETE.value());
		JMenuItem search = new JMenuItem(new SearchAction(this));
		JMenuItem sort	 = new JMenuItem(EditMenu.SORT.value());
		addMenuItems(editMenu, cut, copy, paste, new JSeparator());
		addMenuItems(editMenu, delete, search, sort);
		
//		OptionMenu
		
		JMenu display	= new JMenu(OptionMenu.DISPLAY.value());
		JMenuItem small = new JMenuItem(OptionMenu.SMALL.value());
		JMenuItem mid 	= new JMenuItem(OptionMenu.MID.value());
		JMenuItem big	= new JMenuItem(OptionMenu.BIG.value());
		JMenuItem full 	= new JMenuItem(OptionMenu.FULL.value());
		addMenuItems(display, small, mid, big, full);
		
		JMenuItem user		 = new JMenuItem(OptionMenu.USER.value());
		JMenuItem options	 = new JMenuItem(OptionMenu.OPTIONS.value());
		addMenuItems(optionMenu, display, new JSeparator(), user, options);

//		ClientMenu
		
		JMenuItem clientMaintenance = new JMenuItem(new ClientAction(this));
		JMenuItem saveClient = new JMenuItem(ClientMenu.SAVE.value());
		addMenuItems(clientMenu, clientMaintenance, new JSeparator(), saveClient);
			
//		HelpMenu
		
		JMenuItem shortcuts	 = new JMenuItem(new ShortCutAction(this));
		JMenuItem about		 = new JMenuItem(new InfoAction(this));
		JMenuItem contact 	 = new JMenuItem(HelpMenu.CONTACT.value());
		JMenuItem docu		 = new JMenuItem(HelpMenu.DOCU.value());
		JMenuItem update	 = new JMenuItem(HelpMenu.UPDATE.value());
		addMenuItems(helpMenu, shortcuts,new JSeparator(), about, contact, new JSeparator(), docu, update);
		
		
		bar.add(fileMenu);
		bar.add(editMenu);
		bar.add(optionMenu);
		bar.add(clientMenu);
		bar.add(helpMenu);
		
		return bar;
	}
	
	private void addMenuItems(JMenu menu, JComponent...components){
		for (JComponent component : components) {
			menu.add(component);
		}
	}
	
//	JTOOLBAR
	
	private JToolBar createToolBar(){
		JToolBar bar = new JToolBar();
		JButton[] btns = new JButton[7];
		
		final int BAR_WIDTH	 = 0;
		final int BAR_HEIGHT = 38;
		
		bar.setFloatable(false);
		bar.setPreferredSize(new Dimension(BAR_WIDTH, BAR_HEIGHT));
		
		Image fileImage		 = null;
		Image openImage		 = null;
		Image closeImage	 = null;
		Image saveImage		 = null;
		Image searchImage	 = null;
		Image clientImage 	 = null;
		Image helpImage 	 = null;
		
		try {
			fileImage	 = ImageIO.read(getClass().getResourceAsStream("/images/newFile.png"));
			openImage	 = ImageIO.read(getClass().getResourceAsStream("/images/file.png"));
			closeImage	 = ImageIO.read(getClass().getResourceAsStream("/images/close.png"));
			saveImage	 = ImageIO.read(getClass().getResourceAsStream("/images/save.png"));
			searchImage  = ImageIO.read(getClass().getResourceAsStream("/images/search.png"));
			clientImage  = ImageIO.read(getClass().getResourceAsStream("/images/client.png"));
			helpImage 	 = ImageIO.read(getClass().getResourceAsStream("/images/help.png"));
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		
		btns = new JButton[7];
		for (int i = 0; i < btns.length; i++) {
			btns[i] = new JButton();
			btns[i].setHideActionText(true);
		}
		
		btns[0].setAction(new DocumentAction(this, new ImageIcon(fileImage)));
		btns[1].setAction(new OpenAction(this, new ImageIcon(openImage)));
		btns[2].setAction(new CloseAction(this, new ImageIcon(closeImage)));
		btns[3].setAction(new SaveAction(this, new ImageIcon(saveImage)));
		btns[4].setAction(new SearchAction(this, new ImageIcon(searchImage)));
		btns[5].setAction(new ClientAction(this, new ImageIcon(clientImage)));
		btns[6].setAction(new HelpAction(this, new ImageIcon(helpImage)));
		
		for (JButton btn : btns) {
			bar.add(btn);
		}
		
		return bar;
	}
	
	private void addComponents(){
		frame.add(BorderLayout.PAGE_START, toolBar);
		frame.add(BorderLayout.CENTER, workBench);
	}
	
	private void setState(){
		frame.setExtendedState(frame.getExtendedState());
		if(frame.getExtendedState() == JFrame.NORMAL) {
			frame.pack();
			frame.setLocationRelativeTo(null);
		}
	}
	
	public void setFrameState(int state){
		frame.setExtendedState(state);
		frame.revalidate();
	}
	
	public void addChild(JFrame frame){
		frameList.add(frame);
	}
	
	public void removeChild(JFrame frame){
		frameList.remove(frame);
	}
	
	public void loadFrameState(){
		frame.setExtendedState(App.PREFS.getInt("extendedState", JFrame.DISPOSE_ON_CLOSE));
	}
	
	public void saveFrameState(){
		App.PREFS.putInt("extendedState", frame.getExtendedState());
	}
	
	public void show(){
		frame.setVisible(true);
	}
	
	public void dispose(){
		frame.dispose();
	}
	
	public void disposeAll(){
		for (JFrame frame : frameList) {
			frame.dispose();
		}
	}

	public JFrame getFrame()					{return frame;}
	public WorkBench getWorkBench()				{return workBench;}
	public JToolBar getToolBar()				{return toolBar;}
	public JMenuBar getMenuBar()				{return menuBar;}
	public ArrayList<JFrame> getFrameList()		{return frameList;}
}

EDIT : Entschuldige die seltsame Formatierung die das Forum hier neuerdings daraus macht......

Hier habe ich natürlich die Actions noch nicht vorher instantiiert und dann einfach die Referenz an toolbar und menubar gegeben . (shared)
 
Zuletzt bearbeitet:

Harry Kane

Top Contributor
Sieht gut aus.
Das einzige was ich ändern würde: ich halte es für etwas gefährlich, wenn du die originale frameList zurückgibst. Das führt dazu, daß sie jedes Objekt, das eine Referenz auf dein Gui bekommt, ändern kann. ich würde den ZUgriff auf die Inhalte der frameList auf 2 methoden aufsplitten:
Java:
public int getFrameCount(){
    return frameList.size();
}
public JFrame getFrame(int index){
    return frameList.get(index);
}
So kannst später, wenn dies notwendig sein sollte, eine andere List, eine andere Collection oder auch ein JFrame[] verwenden, ohne an der Methode was ändern zu müssen.
 

kaoZ

Top Contributor
Gut das schaue ich mir an , was mich nun noch beschäftigt ist die Workbench, diese schaut momentan so aus, und ist ja nun auch bestandteil der Gui

Java:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.JToolBar;
import editor.App;
import editor.actions.ClientAction;
import editor.actions.CloseAction;
import editor.actions.DocumentAction;
import editor.actions.EstimateAction;
import editor.actions.ExitAction;
import editor.actions.ExportAction;
import editor.actions.HelpAction;
import editor.actions.ImportAction;
import editor.actions.InfoAction;
import editor.actions.InvoiceAction;
import editor.actions.OpenAction;
import editor.actions.PrintAction;
import editor.actions.SaveAction;
import editor.actions.SearchAction;
import editor.actions.ShortCutAction;
import editor.enums.ClientMenu;
import editor.enums.EditMenu;
import editor.enums.FileMenu;
import editor.enums.HelpMenu;
import editor.enums.MenuEntry;
import editor.enums.OptionMenu;

public class Gui{
	
	private ArrayList<JFrame> frameList;
	
	private JFrame frame;
	private JMenuBar menuBar;
	private JToolBar toolBar;
	private WorkBench workBench;
	
	public Gui(){
		init();
		initComponents();
		initFrame();
		addComponents();
		setState();
	}
	
	private void init(){
		frameList = new ArrayList<>();
	}
	
	private void initComponents(){
		workBench	 = new WorkBench(this);
		menuBar		 = createMenuBar();
		toolBar		 = createToolBar();
	}

	private void initFrame(){
		frame = new JFrame(App.TITLE + " " + App.VERSION);
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		frame.setJMenuBar(menuBar);
		frame.addWindowListener(new FrameListener(this));
	}
	
//	JMENUBAR	
	
	private JMenuBar createMenuBar(){
		JMenuBar bar = new JMenuBar();
		
		bar.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY));
		
		JMenu fileMenu	 = new JMenu(MenuEntry.FILE.value());
		JMenu editMenu	 = new JMenu(MenuEntry.EDIT.value());
		JMenu optionMenu = new JMenu(MenuEntry.OPTIONS.value());
		JMenu clientMenu = new JMenu(MenuEntry.CLIENTS.value());
		JMenu helpMenu	 = new JMenu(MenuEntry.HELP.value());
		
		fileMenu.setMnemonic(KeyEvent.VK_D);
		editMenu.setMnemonic(KeyEvent.VK_B);
		optionMenu.setMnemonic(KeyEvent.VK_E);
		clientMenu.setMnemonic(KeyEvent.VK_K);
		helpMenu.setMnemonic(KeyEvent.VK_H);
		
//		FileMenu
		
		JMenu fileSubMenu	 = new JMenu(FileMenu.NEW.value());
		JMenuItem invoice	 = new JMenuItem(new InvoiceAction(this));
		JMenuItem estimate	 = new JMenuItem(new EstimateAction(this));
		addMenuItems(fileSubMenu, invoice, estimate);
		
		JMenuItem open		 = new JMenuItem(new OpenAction(this));
		JMenuItem close		 = new JMenuItem(new CloseAction(this));
		JMenuItem print		 = new JMenuItem(new PrintAction(this));
		JMenuItem save 		 = new JMenuItem(new SaveAction(this));
		JMenuItem importData = new JMenuItem(new ImportAction());
		JMenuItem exportData = new JMenuItem(new ExportAction());
		JMenuItem exit		 = new JMenuItem(new ExitAction(this));
		addMenuItems(fileMenu, fileSubMenu, new JSeparator(), open, close, save, new JSeparator());
		addMenuItems(fileMenu, print, new JSeparator(), importData, exportData, new JSeparator(), exit);
		
//		EditMenu
		
		JMenuItem cut	 = new JMenuItem(EditMenu.CUT.value());
		JMenuItem copy	 = new JMenuItem(EditMenu.COPY.value());
		JMenuItem paste	 = new JMenuItem(EditMenu.PASTE.value());
		JMenuItem delete = new JMenuItem(EditMenu.DELETE.value());
		JMenuItem search = new JMenuItem(new SearchAction(this));
		JMenuItem sort	 = new JMenuItem(EditMenu.SORT.value());
		addMenuItems(editMenu, cut, copy, paste, new JSeparator());
		addMenuItems(editMenu, delete, search, sort);
		
//		OptionMenu
		
		JMenu display	= new JMenu(OptionMenu.DISPLAY.value());
		JMenuItem small = new JMenuItem(OptionMenu.SMALL.value());
		JMenuItem mid 	= new JMenuItem(OptionMenu.MID.value());
		JMenuItem big	= new JMenuItem(OptionMenu.BIG.value());
		JMenuItem full 	= new JMenuItem(OptionMenu.FULL.value());
		addMenuItems(display, small, mid, big, full);
		
		JMenuItem user		 = new JMenuItem(OptionMenu.USER.value());
		JMenuItem options	 = new JMenuItem(OptionMenu.OPTIONS.value());
		addMenuItems(optionMenu, display, new JSeparator(), user, options);

//		ClientMenu
		
		JMenuItem clientMaintenance = new JMenuItem(new ClientAction(this));
		JMenuItem saveClient = new JMenuItem(ClientMenu.SAVE.value());
		addMenuItems(clientMenu, clientMaintenance, new JSeparator(), saveClient);
			
//		HelpMenu
		
		JMenuItem shortcuts	 = new JMenuItem(new ShortCutAction(this));
		JMenuItem about		 = new JMenuItem(new InfoAction(this));
		JMenuItem contact 	 = new JMenuItem(HelpMenu.CONTACT.value());
		JMenuItem docu		 = new JMenuItem(HelpMenu.DOCU.value());
		JMenuItem update	 = new JMenuItem(HelpMenu.UPDATE.value());
		addMenuItems(helpMenu, shortcuts,new JSeparator(), about, contact, new JSeparator(), docu, update);
		
		bar.add(fileMenu);
		bar.add(editMenu);
		bar.add(optionMenu);
		bar.add(clientMenu);
		bar.add(helpMenu);
		
		return bar;
	}
	
	private void addMenuItems(JMenu menu, JComponent...components){
		for (JComponent component : components) {
			menu.add(component);
		}
	}
	
//	JTOOLBAR
	
	private JToolBar createToolBar(){
		JToolBar bar = new JToolBar();
		JButton[] btns = new JButton[7];
		
		final int BAR_WIDTH	 = 0;
		final int BAR_HEIGHT = 38;
		
		bar.setFloatable(false);
		bar.setPreferredSize(new Dimension(BAR_WIDTH, BAR_HEIGHT));
		
		Image fileImage		 = null;
		Image openImage		 = null;
		Image closeImage	 = null;
		Image saveImage		 = null;
		Image searchImage	 = null;
		Image clientImage 	 = null;
		Image helpImage 	 = null;
		
		try {
			fileImage	 = ImageIO.read(getClass().getResourceAsStream("/images/newFile.png"));
			openImage	 = ImageIO.read(getClass().getResourceAsStream("/images/file.png"));
			closeImage	 = ImageIO.read(getClass().getResourceAsStream("/images/close.png"));
			saveImage	 = ImageIO.read(getClass().getResourceAsStream("/images/save.png"));
			searchImage  = ImageIO.read(getClass().getResourceAsStream("/images/search.png"));
			clientImage  = ImageIO.read(getClass().getResourceAsStream("/images/client.png"));
			helpImage 	 = ImageIO.read(getClass().getResourceAsStream("/images/help.png"));
		}
		catch (Exception e) {
			e.printStackTrace();
		}
		
		btns = new JButton[7];
		for (int i = 0; i < btns.length; i++) {
			btns[i] = new JButton();
			btns[i].setHideActionText(true);
		}
		
		btns[0].setAction(new DocumentAction(this, new ImageIcon(fileImage)));
		btns[1].setAction(new OpenAction(this, new ImageIcon(openImage)));
		btns[2].setAction(new CloseAction(this, new ImageIcon(closeImage)));
		btns[3].setAction(new SaveAction(this, new ImageIcon(saveImage)));
		btns[4].setAction(new SearchAction(this, new ImageIcon(searchImage)));
		btns[5].setAction(new ClientAction(this, new ImageIcon(clientImage)));
		btns[6].setAction(new HelpAction(this, new ImageIcon(helpImage)));
		
		for (JButton btn : btns) {
			bar.add(btn);
		}
		
		return bar;
	}
	
	private void addComponents(){
		frame.add(BorderLayout.PAGE_START, toolBar);
		frame.add(BorderLayout.CENTER, workBench);
	}
	
	private void setState(){
		
		int state = frame.getExtendedState();
		
		frame.setExtendedState(state);
		if(state == JFrame.NORMAL) {
			frame.pack();
			frame.setLocationRelativeTo(null);
		}
	}
	
	private void loadFrameState(){
		frame.setExtendedState(App.PREFS.getInt("extendedState", JFrame.DISPOSE_ON_CLOSE));
	}
	
	private void saveFrameState(){
		App.PREFS.putInt("extendedState", frame.getExtendedState());
	}
	
	public void setFrameState(int state){
		frame.setExtendedState(state);
		frame.revalidate();
	}
	
	public void addChild(JFrame frame){
		frameList.add(frame);
	}
	
	public void removeChild(JFrame frame){
		frameList.remove(frame);
	}
	
	public void setVisible(){
		frame.setVisible(true);
	}
	
	public void dispose(){
		frame.dispose();
	}
	
	public void disposeAll(){
		for (JFrame frame : frameList) {
			frame.dispose();
		}
	}

	public JFrame getFrame()					{return frame;}
	public WorkBench getWorkBench()				{return workBench;}
	public JToolBar getToolBar()				{return toolBar;}
	public JMenuBar getMenuBar()				{return menuBar;}
	public int getFrameCount()					{return frameList.size();}
	public JFrame getFrame(int index)			{return frameList.get(index);}
	
//	INNER CLASS LISTENER
	
	class FrameListener extends WindowAdapter{
		
		private Gui gui;
		
		public FrameListener(Gui gui){
			this.gui = gui;
		}
		
		@Override
		public void windowOpened(WindowEvent e){
			gui.loadFrameState();
		}

		@Override
		public void windowClosing(WindowEvent e){
			gui.disposeAll();
		}

		@Override
		public void windowClosed(WindowEvent e){
			gui.saveFrameState();
		}
	}
}

sind nun ca 306 Zeilen Source Code , ich meine mal gelesen zu haben bis 500 Zeilen pro Klasse sei noch vollkommen legitim , alles darüber wäre doch eher schlecht durchdacht :) ?!
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
EDIT : Da nun auch wieder alles in der Gui ist kann ich das Exeption Handling auch Zentralisieren und einfach Fehler werfen und direkt an die Gui delegieren. ist natürlich im vorherigen Post noch nicht der Fall, ebenso wie die shared Actions. :)
 

kaoZ

Top Contributor
Ich hab nochmal etwas modifiziert, die Workbench in form eines JTabbedPane in der Gui realisiert, und eine zusätzliche Methode zum initialisieren der Actions hinzugefügt, die allerdings noch nicht implementiert ist.

Java:
package editor.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.JToolBar;
import editor.App;
import editor.actions.ClientAction;
import editor.actions.CloseAction;
import editor.actions.DocumentAction;
import editor.actions.EstimateAction;
import editor.actions.ExitAction;
import editor.actions.ExportAction;
import editor.actions.HelpAction;
import editor.actions.ImportAction;
import editor.actions.InfoAction;
import editor.actions.InvoiceAction;
import editor.actions.OpenAction;
import editor.actions.OptionAction;
import editor.actions.PrintAction;
import editor.actions.SaveAction;
import editor.actions.SearchAction;
import editor.actions.ShortCutAction;
import editor.enums.ClientMenu;
import editor.enums.EditMenu;
import editor.enums.FileMenu;
import editor.enums.HelpMenu;
import editor.enums.MenuEntry;
import editor.enums.OptionMenu;

public class Gui{
	
	private ArrayList<JFrame> frameList;
	
	private JFrame frame;
	private JMenuBar menuBar;
	private JToolBar toolBar;
	private JTabbedPane workBench;
	
	public Gui(){
		try {
			init();
			initActions();
			initComponents();
			initFrame();
			addComponents();
			setState();
		}
		catch (NullPointerException | IOException | IllegalArgumentException e) {
			if(e instanceof NullPointerException) {
				System.out.println("null");
//				FIXME
			}
			if(e instanceof IOException) {
				System.out.println("source loading failed");
//				FIXME
			}
			if(e instanceof IllegalArgumentException) {
				System.out.println("Check Parameters !");
//				FIXME
			}
			e.printStackTrace();
		}
	}
	
	private void init(){
		frameList = new ArrayList<>();
	}
	
	private void initActions(){
//		FIXME
	}
	
	private void initComponents() throws IOException, NullPointerException, IllegalArgumentException{
			workBench	 = createWorkBench();
			menuBar		 = createMenuBar();
			toolBar		 = createToolBar();
	}

	private void initFrame(){
		frame = new JFrame(App.TITLE + " " + App.VERSION);
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		frame.setJMenuBar(menuBar);
		frame.addWindowListener(new FrameListener(this));
	}
	
//	WORKBENCH	
	
	private JTabbedPane createWorkBench(){
		JTabbedPane pane = new JTabbedPane();
		
		pane.setPreferredSize(new Dimension(App.DEFAULT_WIDTH, App.DEFAULT_HEIGHT));
		
		return pane;
	}
	
//	JMENUBAR	
	
	private JMenuBar createMenuBar(){
		JMenuBar bar = new JMenuBar();
		
		bar.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY));
		
		JMenu fileMenu	 = new JMenu(MenuEntry.FILE.value());
		JMenu editMenu	 = new JMenu(MenuEntry.EDIT.value());
		JMenu optionMenu = new JMenu(MenuEntry.OPTIONS.value());
		JMenu clientMenu = new JMenu(MenuEntry.CLIENTS.value());
		JMenu helpMenu	 = new JMenu(MenuEntry.HELP.value());
		
		fileMenu.setMnemonic(KeyEvent.VK_D);
		editMenu.setMnemonic(KeyEvent.VK_B);
		optionMenu.setMnemonic(KeyEvent.VK_E);
		clientMenu.setMnemonic(KeyEvent.VK_K);
		helpMenu.setMnemonic(KeyEvent.VK_H);
		
//		FileMenu
		
		JMenu fileSubMenu	 = new JMenu(FileMenu.NEW.value());
		JMenuItem invoice	 = new JMenuItem(new InvoiceAction(this));
		JMenuItem estimate	 = new JMenuItem(new EstimateAction(this));
		addMenuItems(fileSubMenu, invoice, estimate);
		
		JMenuItem open		 = new JMenuItem(new OpenAction(this));
		JMenuItem close		 = new JMenuItem(new CloseAction(this));
		JMenuItem print		 = new JMenuItem(new PrintAction(this));
		JMenuItem save 		 = new JMenuItem(new SaveAction(this));
		JMenuItem importData = new JMenuItem(new ImportAction());
		JMenuItem exportData = new JMenuItem(new ExportAction());
		JMenuItem exit		 = new JMenuItem(new ExitAction(this));
		addMenuItems(fileMenu, fileSubMenu, new JSeparator(), open, close, save, new JSeparator());
		addMenuItems(fileMenu, print, new JSeparator(), importData, exportData, new JSeparator(), exit);
		
//		EditMenu
		
		JMenuItem cut	 = new JMenuItem(EditMenu.CUT.value());
		JMenuItem copy	 = new JMenuItem(EditMenu.COPY.value());
		JMenuItem paste	 = new JMenuItem(EditMenu.PASTE.value());
		JMenuItem delete = new JMenuItem(EditMenu.DELETE.value());
		JMenuItem search = new JMenuItem(new SearchAction(this));
		JMenuItem sort	 = new JMenuItem(EditMenu.SORT.value());
		addMenuItems(editMenu, cut, copy, paste, new JSeparator());
		addMenuItems(editMenu, delete, search, sort);
		
//		OptionMenu
		
		JMenu display	= new JMenu(OptionMenu.DISPLAY.value());
		JMenuItem small = new JMenuItem(OptionMenu.SMALL.value());
		JMenuItem mid 	= new JMenuItem(OptionMenu.MID.value());
		JMenuItem big	= new JMenuItem(OptionMenu.BIG.value());
		JMenuItem full 	= new JMenuItem(OptionMenu.FULL.value());
		addMenuItems(display, small, mid, big, full);
		
		JMenuItem user		 = new JMenuItem(OptionMenu.USER.value());
		JMenuItem options	 = new JMenuItem(new OptionAction(this));
		addMenuItems(optionMenu, display, new JSeparator(), user, options);

//		ClientMenu
		
		JMenuItem clientMaintenance = new JMenuItem(new ClientAction(this));
		JMenuItem saveClient = new JMenuItem(ClientMenu.SAVE.value());
		addMenuItems(clientMenu, clientMaintenance, new JSeparator(), saveClient);
			
//		HelpMenu
		
		JMenuItem shortcuts	 = new JMenuItem(new ShortCutAction(this));
		JMenuItem about		 = new JMenuItem(new InfoAction(this));
		JMenuItem contact 	 = new JMenuItem(HelpMenu.CONTACT.value());
		JMenuItem docu		 = new JMenuItem(HelpMenu.DOCU.value());
		JMenuItem errorlog 	 = new JMenuItem(HelpMenu.ERRORLOG.value());
		JMenuItem update	 = new JMenuItem(HelpMenu.UPDATE.value());
		addMenuItems(helpMenu, shortcuts,new JSeparator(), about, contact);
		addMenuItems(helpMenu, new JSeparator(), docu, errorlog, update);
		
		bar.add(fileMenu);
		bar.add(editMenu);
		bar.add(optionMenu);
		bar.add(clientMenu);
		bar.add(helpMenu);
		
		return bar;
	}
	
	private void addMenuItems(JMenu menu, JComponent...components){
		for (JComponent component : components) {
			menu.add(component);
		}
	}
	
//	JTOOLBAR
	
	private JToolBar createToolBar() throws NullPointerException, IOException , IllegalArgumentException{
		JToolBar bar = new JToolBar();
		JButton[] btns = new JButton[7];
		
		Image fileImage		 = ImageIO.read(getClass().getResourceAsStream("/images/newFile_small.png"));
		Image openImage		 = ImageIO.read(getClass().getResourceAsStream("/images/file_small.png"));
		Image closeImage	 = ImageIO.read(getClass().getResourceAsStream("/images/close_small.png"));
		Image saveImage		 = ImageIO.read(getClass().getResourceAsStream("/images/save_small.png"));
		Image searchImage	 = ImageIO.read(getClass().getResourceAsStream("/images/search_small.png"));
		Image clientImage 	 = ImageIO.read(getClass().getResourceAsStream("/images/client_small.png"));
		Image optionImage	 = ImageIO.read(getClass().getResourceAsStream("/images/options_small.png"));
		Image helpImage 	 = ImageIO.read(getClass().getResourceAsStream("/images/help_small.png"));
		
		
		btns = new JButton[8];
		for (int i = 0; i < btns.length; i++) {
			btns[i] = new JButton();
			btns[i].setHideActionText(true);
		}
		
		btns[0].setAction(new DocumentAction(this, new ImageIcon(fileImage)));
		btns[1].setAction(new OpenAction(this, new ImageIcon(openImage)));
		btns[2].setAction(new CloseAction(this, new ImageIcon(closeImage)));
		btns[3].setAction(new SaveAction(this, new ImageIcon(saveImage)));
		btns[4].setAction(new SearchAction(this, new ImageIcon(searchImage)));
		btns[5].setAction(new ClientAction(this, new ImageIcon(clientImage)));
		btns[6].setAction(new OptionAction(this, new ImageIcon(optionImage)));
		btns[7].setAction(new HelpAction(this, new ImageIcon(helpImage)));
		
		bar.add(btns[0]);
		bar.add(btns[1]);
		bar.add(btns[2]);
		bar.addSeparator();
		bar.add(btns[3]);
		bar.add(btns[4]);
		bar.add(btns[5]);
		bar.addSeparator();
		bar.add(btns[6]);
		bar.add(btns[7]);
		
		return bar;
	}
	
	private void addComponents(){
		frame.add(BorderLayout.PAGE_START, toolBar);
		frame.add(BorderLayout.CENTER, workBench);
	}
	
	private void setState(){
		
		int state = frame.getExtendedState();
		
		frame.setExtendedState(state);
		if(state == JFrame.NORMAL) {
			frame.pack();
			frame.setLocationRelativeTo(null);
		}
	}
	
	private void loadFrameState(){
		frame.setExtendedState(App.PREFS.getInt("extendedState", JFrame.DISPOSE_ON_CLOSE));
	}
	
	private void saveFrameState(){
		App.PREFS.putInt("extendedState", frame.getExtendedState());
	}
	
	public void setFrameState(int state){
		frame.setExtendedState(state);
		frame.revalidate();
	}
	
	public void addTab(JComponent tab){
		workBench.add(tab);
	}
	
	public void removeTab(JComponent tab){
		workBench.remove(tab);
	}
	
	public void addChild(JFrame frame){
		frameList.add(frame);
	}
	
	public void removeChild(JFrame frame){
		frameList.remove(frame);
	}
	
	public void setVisible(){
		frame.setVisible(true);
	}
	
	public void dispose(){
		frame.dispose();
	}
	
	public void disposeAll(){
		for (JFrame frame : frameList) {
			frame.dispose();
		}
	}
	
	public int getFrameCount()					{return frameList.size();}
	public JFrame getFrame()					{return frame;}
	public JTabbedPane getWorkBench()			{return workBench;}
	public JToolBar getToolBar()				{return toolBar;}
	public JMenuBar getMenuBar()				{return menuBar;}
	public JFrame getChild(int index)			{return frameList.get(index);}
	
//	INNER CLASS LISTENER
	
	class FrameListener extends WindowAdapter{
		
		private Gui gui;
		
		public FrameListener(Gui gui){
			this.gui = gui;
		}
		
		@Override
		public void windowOpened(WindowEvent e){
			gui.loadFrameState();
		}

		@Override
		public void windowClosing(WindowEvent e){
			gui.disposeAll();
		}

		@Override
		public void windowClosed(WindowEvent e){
			gui.saveFrameState();
		}
	}
}

zudem habe ich einen Multicatch im Konstruktor hinzugefügt, die Frage ist nun macht es sinn an dieser Stelle die Exceptions später abzufangen / zu loggen ? wenn es zu einer NPE oder ähnliches führt startet die Anwendung dann ja nicht da es sonst zu inkonsistenz führt, ist ja eigentlich recht sinnvoll oder ?
so kann ich aber auch bequem z.B eine Meldung über ein JOptionPane ausgeben da ich ja so alle Exeptions an die Gui delegieren kann.

Das mit dem state des Frames werde ich noch modifizieren, und dann die bounds in den preferences speichern, dann kann man das Fenster egal wo und wie groß speichern und wiederherstellen, und ich denke das kommt dem User später eher entgegen als immer nur ein entweder maximiertes oder zentriertes Frame zu haben :).

Konstruktive Kritik und oder Ratschläge nehme ich gern entgegen :)
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
Kurzes Update falls es wenn interessiert :

ich bin nun dazu übergegangen alle Actions und Komponenten in der Gui zu instanziieren,

und einfach bei bedarf sichtbar zu machen , das sieht bis jetzt folgendermaßen aus :

Java:
package editor.gui;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.event.WindowStateListener;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.JToolBar;
import editor.App;
import editor.actions.ClientAction;
import editor.actions.CloseAction;
import editor.actions.DocumentAction;
import editor.actions.EstimateAction;
import editor.actions.ExitAction;
import editor.actions.ExportAction;
import editor.actions.HelpAction;
import editor.actions.ImportAction;
import editor.actions.InfoAction;
import editor.actions.InvoiceAction;
import editor.actions.OpenAction;
import editor.actions.OptionAction;
import editor.actions.PrintAction;
import editor.actions.SaveAction;
import editor.actions.SearchAction;
import editor.actions.ShortcutAction;
import editor.documents.Document;
import editor.documents.DocumentHandler;
import editor.enums.ClientMenu;
import editor.enums.EditMenu;
import editor.enums.FileMenu;
import editor.enums.HelpMenu;
import editor.enums.MenuEntry;
import editor.enums.OptionMenu;
import editor.gui.components.ClientPanel;
import editor.gui.components.GuiComponent;
import editor.gui.components.InfoPanel;
import editor.gui.components.OptionPanel;
import editor.gui.components.SearchPanel;
import editor.gui.components.ShortcutPanel;

public class Gui{

//	components
	
	private JFrame frame;
	private JMenuBar menuBar;
	private JToolBar toolBar;
	private JTabbedPane workBench;
	private DocumentHandler handler;
	
	private GuiComponent[] components;
	
	
//	actions
	
	private DocumentAction documentAction;
	private OpenAction openAction;
	private CloseAction closeAction;
	private PrintAction printAction;
	private SaveAction saveAction;
	private ImportAction importAction;
	private ExportAction exportAction;
	private ExitAction exitAction;
	private InfoAction infoAction;
	private ShortcutAction shortcutAction;
	private SearchAction searchAction;
	private ClientAction clientAction;
	private OptionAction optionAction;
	private HelpAction helpAction;
	private InvoiceAction invoiceAction;
	private EstimateAction estimateAction;
	
	public Gui(){
		try {
			init();
			initActions();
			initComponents();
			initFrame();
			addComponents();
		}
		catch (NullPointerException | IOException | IllegalArgumentException e) {
			if(e instanceof NullPointerException) {
				System.out.println("null");
//				FIXME
			}
			if(e instanceof IOException) {
				System.out.println("source loading failed");
//				FIXME
			}
			if(e instanceof IllegalArgumentException) {
				System.out.println("Check Parameters !");
//				FIXME
			}
			e.printStackTrace();
		}
	}
	
	private void init(){
		handler		 = new DocumentHandler(this);
	}
	
//	ACTIONS
	
	private void initActions() throws IOException{
		
		Image fileImage		 = ImageIO.read(getClass().getResourceAsStream("/images/newFile_small.png"));
		Image openImage		 = ImageIO.read(getClass().getResourceAsStream("/images/file_small.png"));
		Image closeImage	 = ImageIO.read(getClass().getResourceAsStream("/images/close_small.png"));
		Image saveImage		 = ImageIO.read(getClass().getResourceAsStream("/images/save_small.png"));
		Image searchImage	 = ImageIO.read(getClass().getResourceAsStream("/images/search_small.png"));
		Image clientImage 	 = ImageIO.read(getClass().getResourceAsStream("/images/client_small.png"));
		Image optionImage	 = ImageIO.read(getClass().getResourceAsStream("/images/options_small.png"));
		Image helpImage 	 = ImageIO.read(getClass().getResourceAsStream("/images/help_small.png"));
		
		documentAction	 = new DocumentAction(this, new ImageIcon(fileImage));
		invoiceAction 	 = new InvoiceAction(this, null);
		estimateAction 	 = new EstimateAction(this, null);
		openAction		 = new OpenAction(this, new ImageIcon(openImage));
		closeAction		 = new CloseAction(this, new ImageIcon(closeImage));
		printAction		 = new PrintAction(this, null);
		saveAction		 = new SaveAction(this, new ImageIcon(saveImage));
		importAction	 = new ImportAction(this, null);
		exportAction	 = new ExportAction(this, null);
		exitAction		 = new ExitAction(this, null);
		infoAction		 = new InfoAction(this, null);
		shortcutAction	 = new ShortcutAction(this, null);
		searchAction	 = new SearchAction(this, new ImageIcon(searchImage));
		clientAction	 = new ClientAction(this, new ImageIcon(clientImage));
		optionAction	 = new OptionAction(this, new ImageIcon(optionImage));
		helpAction		 = new HelpAction(this, new ImageIcon(helpImage));
	}
	
	private void initComponents() throws IOException, NullPointerException, IllegalArgumentException{
			workBench	= createWorkBench();
			menuBar		= createMenuBar();
			toolBar		= createToolBar();
			
			components = new GuiComponent[5];
			
			components[0] = new ClientPanel(this);
			components[1] = new InfoPanel(this);
			components[2] = new OptionPanel(this);
			components[3] = new SearchPanel(this);
			components[4] = new ShortcutPanel(this);
			
	}

	private void initFrame(){
		frame = new JFrame(App.TITLE + " " + App.VERSION);
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		
		FrameListener listener = new FrameListener();
		
		frame.addWindowListener(listener);
		frame.addWindowStateListener(listener);
	}
	
//	WORKBENCH	
	
	private JTabbedPane createWorkBench(){
		JTabbedPane pane = new JTabbedPane();

		return pane;
	}
	
//	JMENUBAR	
	
	private JMenuBar createMenuBar(){
		JMenuBar bar = new JMenuBar();
		
		bar.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0, Color.LIGHT_GRAY));
		
		JMenu fileMenu	 = new JMenu(MenuEntry.FILE.value());
		JMenu editMenu	 = new JMenu(MenuEntry.EDIT.value());
		JMenu optionMenu = new JMenu(MenuEntry.OPTIONS.value());
		JMenu clientMenu = new JMenu(MenuEntry.CLIENTS.value());
		JMenu helpMenu	 = new JMenu(MenuEntry.HELP.value());
		
		fileMenu.setMnemonic(KeyEvent.VK_D);
		editMenu.setMnemonic(KeyEvent.VK_B);
		optionMenu.setMnemonic(KeyEvent.VK_E);
		clientMenu.setMnemonic(KeyEvent.VK_K);
		helpMenu.setMnemonic(KeyEvent.VK_H);
		
//		FileMenu
		
		JMenu fileSubMenu	 = new JMenu(FileMenu.NEW.value());
		JMenuItem invoice	 = new JMenuItem(invoiceAction);
		JMenuItem estimate	 = new JMenuItem(estimateAction);
		addMenuItems(fileSubMenu, invoice, estimate);
		
		JMenuItem open		 = new JMenuItem(openAction);
		JMenuItem close		 = new JMenuItem(closeAction);
		JMenuItem print		 = new JMenuItem(printAction);
		JMenuItem save 		 = new JMenuItem(saveAction);
		JMenuItem importData = new JMenuItem(importAction);
		JMenuItem exportData = new JMenuItem(exportAction);
		JMenuItem exit		 = new JMenuItem(exitAction);
		addMenuItems(fileMenu, fileSubMenu, new JSeparator(), open, close, save, new JSeparator());
		addMenuItems(fileMenu, print, new JSeparator(), importData, exportData, new JSeparator(), exit);
		
//		EditMenu
		
		JMenuItem cut	 = new JMenuItem(EditMenu.CUT.value());
		JMenuItem copy	 = new JMenuItem(EditMenu.COPY.value());
		JMenuItem paste	 = new JMenuItem(EditMenu.PASTE.value());
		JMenuItem delete = new JMenuItem(EditMenu.DELETE.value());
		JMenuItem search = new JMenuItem(searchAction);
		JMenuItem sort	 = new JMenuItem(EditMenu.SORT.value());
		addMenuItems(editMenu, cut, copy, paste, new JSeparator());
		addMenuItems(editMenu, delete, search, sort);
		
//		OptionMenu
		
		JMenu display	= new JMenu(OptionMenu.DISPLAY.value());
		JMenuItem small = new JMenuItem(OptionMenu.SMALL.value());
		JMenuItem mid 	= new JMenuItem(OptionMenu.MID.value());
		JMenuItem big	= new JMenuItem(OptionMenu.BIG.value());
		JMenuItem full 	= new JMenuItem(OptionMenu.FULL.value());
		addMenuItems(display, small, mid, big, full);
		
		JMenuItem user		 = new JMenuItem(OptionMenu.USER.value());
		JMenuItem options	 = new JMenuItem(optionAction);
		addMenuItems(optionMenu, display, new JSeparator(), user, options);

//		ClientMenu
		
		JMenuItem clientMaintenance	 = new JMenuItem(new ClientAction(this));
		JMenuItem saveClient		 = new JMenuItem(ClientMenu.SAVE.value());
		addMenuItems(clientMenu, clientMaintenance, new JSeparator(), saveClient);
			
//		HelpMenu
		
		JMenuItem shortcuts	 = new JMenuItem(shortcutAction);
		JMenuItem about		 = new JMenuItem(infoAction);
		JMenuItem contact 	 = new JMenuItem(HelpMenu.CONTACT.value());
		JMenuItem docu		 = new JMenuItem(HelpMenu.DOCU.value());
		JMenuItem errorlog 	 = new JMenuItem(HelpMenu.ERRORLOG.value());
		JMenuItem update	 = new JMenuItem(HelpMenu.UPDATE.value());
		addMenuItems(helpMenu, shortcuts,new JSeparator(), about, contact);
		addMenuItems(helpMenu, new JSeparator(), docu, errorlog, update);
		
		bar.add(fileMenu);
		bar.add(editMenu);
		bar.add(optionMenu);
		bar.add(clientMenu);
		bar.add(helpMenu);
		
		return bar;
	}
	
	private void addMenuItems(JMenu menu, JComponent...components){
		for (JComponent component : components) {
			menu.add(component);
		}
	}
	
//	JTOOLBAR
	
	private JToolBar createToolBar(){
		JToolBar bar = new JToolBar();
		JButton[] btns = new JButton[7];
		
		btns = new JButton[8];
		for (int i = 0; i < btns.length; i++) {
			btns[i] = new JButton();
			btns[i].setHideActionText(true);
		}
		
		btns[0].setAction(documentAction);
		btns[1].setAction(openAction);
		btns[2].setAction(closeAction);
		btns[3].setAction(saveAction);
		btns[4].setAction(searchAction);
		btns[5].setAction(clientAction);
		btns[6].setAction(optionAction);
		btns[7].setAction(helpAction);
		
		bar.add(btns[0]);
		bar.add(btns[1]);
		bar.add(btns[2]);
		bar.addSeparator();
		bar.add(btns[3]);
		bar.add(btns[4]);
		bar.add(btns[5]);
		bar.addSeparator();
		bar.add(btns[6]);
		bar.add(btns[7]);
		
		return bar;
	}
	
	private void addComponents(){
		frame.setJMenuBar(menuBar);
		frame.add(BorderLayout.PAGE_START, toolBar);
		frame.add(BorderLayout.CENTER, workBench);
	}
	
	private void loadPreferences(){
		
//		window
		
		int x		 = App.PREFS.getInt("xPos", 0);
		int y		 = App.PREFS.getInt("yPos", 0);
		int width	 = App.PREFS.getInt("width", App.DEFAULT_WIDTH);
		int height	 = App.PREFS.getInt("height", App.DEFAULT_HEIGHT);
			
		Rectangle rect = new Rectangle(x, y, width, height);

		frame.setBounds(rect);

//		toolbar
			
		toolBar.setFloatable(App.PREFS.getBoolean("toolBar", true));
	}
	
	private void savePreferences(){

//		window

		Rectangle rect = frame.getBounds();
		
		App.PREFS.putInt("xPos", rect.x);
		App.PREFS.putInt("yPos", rect.y);
		App.PREFS.putInt("width", rect.width);
		App.PREFS.putInt("height", rect.height);
	
//	 	toolbar
			
		App.PREFS.putBoolean("toolBar", toolBar.isFloatable());
	}
	
	public void addTab(JComponent tab){
		workBench.add(tab, tab.getName());
		handler.add((Document) tab);
	}
	
	public void removeTab(JComponent tab){
		workBench.remove(tab);
		handler.remove(workBench.getSelectedIndex());
	}
	
	public void setVisible(){
		frame.setVisible(true);
	}
	
	public void dispose(){
		frame.dispose();
	}
	
	public void disposeAll(){
		for (GuiComponent component : components) {
			component.dispose();
		}
	}
	
	public int getFrameCount()					{return components.length;}
	public JFrame getFrame()					{return frame;}
	public JTabbedPane getWorkBench()			{return workBench;}
	public JToolBar getToolBar()				{return toolBar;}
	public JMenuBar getMenuBar()				{return menuBar;}
	public JComponent getDocument(int index)	{return handler.get(index);}
	public GuiComponent getComponent(int index)	{return components[index];}
	
//	INNER CLASS LISTENER
	
	class FrameListener implements WindowListener, WindowStateListener{

	@Override
	public void windowOpened(WindowEvent e){
		loadPreferences();
	}

	@Override
	public void windowClosing(WindowEvent e){
		disposeAll();
	}

	@Override
	public void windowClosed(WindowEvent e){
		savePreferences();
	}

	@Override
	public void windowIconified(WindowEvent e){}

	@Override
	public void windowDeiconified(WindowEvent e){}

	@Override
	public void windowActivated(WindowEvent e){}

	@Override
	public void windowDeactivated(WindowEvent e){}

	@Override
	public void windowStateChanged(WindowEvent e){}
	}
}

Hier noch eine dazugehörige Action :

Java:
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import editor.enums.HelpMenu;
import editor.gui.Gui;


@SuppressWarnings("serial")
public class ShortcutAction extends AbstractAction{

	private Gui gui;
	
	public ShortcutAction(Gui gui, ImageIcon icon){
		super(HelpMenu.SHORTCUTS.value(), icon);
		this.gui = gui;
	}
	
	@Override
	public void actionPerformed(ActionEvent e){
		gui.getComponent(4).setVisible();
	}
}

und der dazu gehörige dialog :

Java:
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import editor.gui.Gui;


@SuppressWarnings("serial")
public class ShortcutPanel extends GuiComponent{
	
	public final static String TITLE = "Tastenkürzel";
	private BufferedImage image;
	
	public ShortcutPanel(Gui gui){
		super(gui);
		setTitle(TITLE);
	}
	
	@Override
	protected void init(){
		setBackground(Color.WHITE);
	}
	
	@Override
	protected void initComponents(){
		try {
			image = ImageIO.read(getClass().getResourceAsStream("/images/shortcuts.png"));
		}
		catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	@Override
	protected void addComponents(){
		add(new JLabel(new ImageIcon(image)));
	}
}

Vielleicht kann mir ja einer sagen ob das der richtige weg ist , scheint zumindest vom aufbau her sehr angenehm / logisch zu sein.
 

Harry Kane

Top Contributor
Wenn du keine direkte Refernz auf die ganzen Actions benötigt, könntest du sie auch in einem Action[] ablegen und das Befüllen der tool bar in Zeile 286-306 mit ein paar Iterationen über das arrays bzw. über einen Teilbereich umsetzen. Dieser Stil liegt mir persönlich mehr.
Ausserdem habe ich es mir angewöhnt, soweit wie möglich Redundanzen zu vermeiden. Offenbar haben alle Actions eine private Referenz auf die Gui. Ich würde mir eine gemeinsame Oberklasse definieren, die diese Referenz hält, und sie über einen protected getter in Unterklassen verfügbar machen.
Zusätzlich zu der ShortcutAction gibt es bestimmtnoch weitere Actions, die ebenfalls eine componente der Gui sichtbar machen. Auch hierfür würde ich mir eine Oberklasse erstellen, mit einer actionPerformed der Form
Java:
public void actionPerformed(ActionEvent e){
    gui.getComponent(getIndex()).setVisible();
}
,
und mit einer getIndex() Methode in den Unterklasse, die den jeweiligen Index zurücklieferen.
Ich glaube ich bin etwas allergisch gegen copy-paste-code.;)
 

kaoZ

Top Contributor
Ich muss sagen das ich es bereits nach dem post genau so umgesetzt habe ^^
Ich hab mir eine Superklasse kreiert, ein Array von Actions angelegt und greife über den Index darauf zu, ja das es noch sehr redundant War habe ich schon bei den Komponenten gemerkt..... dort habe ich es ebenfalls so umgesetzt.

Zudem bin ich dazu übergegangen und leite die Superklasse aller Komponenten von JDialog anstelle von JPanel an, da ich so gleich einen umschließenden Container habe und nicht extra Alles noch in ein frame verpacken muss, es wird also langsam ;)
Ich werd die Zwischenstände hier einfach mal Posten, vielleicht siehst du ja ab und zu noch stellen die du anders machen würdest ;)
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
bei den Actions hab ich das ganze nochmal überdacht , und mit index ist es doch recht kompliziert dann die richtige action zu erwischen.

ich hab mich da nun auf eine HashMap versteift.

Java:
//	ACTIONS
	
	private void initActions() throws IOException{
		
		actionMap = new HashMap<>();
		
		Image fileImage		 = ImageIO.read(getClass().getResourceAsStream("/images/newFile_small.png"));
		Image openImage		 = ImageIO.read(getClass().getResourceAsStream("/images/file_small.png"));
		Image closeImage	 = ImageIO.read(getClass().getResourceAsStream("/images/close_small.png"));
		Image saveImage		 = ImageIO.read(getClass().getResourceAsStream("/images/save_small.png"));
		Image searchImage	 = ImageIO.read(getClass().getResourceAsStream("/images/search_small.png"));
		Image clientImage 	 = ImageIO.read(getClass().getResourceAsStream("/images/client_small.png"));
		Image optionImage	 = ImageIO.read(getClass().getResourceAsStream("/images/options_small.png"));
		Image helpImage 	 = ImageIO.read(getClass().getResourceAsStream("/images/help_small.png"));
		
		actionMap.put("document", new DocumentAction(this, new ImageIcon(fileImage)));
		actionMap.put("invoice",  new InvoiceAction(this, null));
		actionMap.put("estimate", new EstimateAction(this, null));
		actionMap.put("open", new OpenAction(this, new ImageIcon(openImage)));
		actionMap.put("close", new CloseAction(this, new ImageIcon(closeImage)));
		actionMap.put("print", new PrintAction(this, null));
		actionMap.put("save", new SaveAction(this, new ImageIcon(saveImage)));
		actionMap.put("import", new ImportAction(this, null));
		actionMap.put("export", new ExportAction(this, null));
		actionMap.put("exit", new ExitAction(this, null));
		actionMap.put("info", new InfoAction(this, null));
		actionMap.put("shortcuts", new ShortcutAction(this, null));
		actionMap.put("search", new SearchAction(this, new ImageIcon(searchImage)));
		actionMap.put("client", new ClientAction(this, new ImageIcon(clientImage)));
		actionMap.put("options", new OptionAction(this, new ImageIcon(optionImage)));
		actionMap.put("help", new HelpAction(this, new ImageIcon(helpImage)));
	}

so kann ich mir die Actions dann später bequem über Konstanten oder ein Enum ( die hier noch nicht erstellt sind) holen.

klappt wunderbar ;)
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
Kompliziert im Sinne von unübersichtlich ^^

beim ersten Versuche musste ich unzählige male nachschauen welche Action nun welchen index hat, da finde ich die Lösung mit der map irgendwie sinniger , Das mit der standardimplementierung der actionPerformed Methode habe ich mir angeschaut und aber auch wieder verworfen , auch wenn die Idee nicht schlecht war macht es a) keinen Unterschied ob ich nun in allen klassen actionPerformed überschreibe, oder getIndex(), zumal nur die wenigsten der ganzen Actions dafür zuständig sind Elemente Sichtbar zu machen. :)

Sonst hab ich hier 20 Actions mit einer leeren getIndex() implementierung ^^

tool bar in Zeile 286-306 mit ein paar Iterationen über das arrays bzw. über einen Teilbereich umsetzen. Dieser Stil liegt mir persönlich mehr.

Das wäre in diesem Fall natürlich angenehmer, da ich auch ein Freund der iterativen Lösung bin (insofern möglich), aber die Indexierung ist aus oben genannten gründen für mich zumindest zzt. keine Option, da es einfach zu viel aufwand wäre immer wieder zu schauen welchen index welche action hat....

Da ist bis jetzt aber nur 8 Buttons , und eben auch nur 8 zugewiesene Actions sind , ist das ganze so :

Java:
	private JToolBar createToolBar(){
		JToolBar bar = new JToolBar();

		JButton[] btns = new JButton[8];
		for (int i = 0; i < btns.length; i++) {
			btns[i] = new JButton();
			btns[i].setHideActionText(true);
		}

		btns[0].setAction(actionMap.get("document"));
		btns[1].setAction(actionMap.get("open"));
		btns[2].setAction(actionMap.get("close"));
		btns[3].setAction(actionMap.get("search"));
		btns[4].setAction(actionMap.get("save"));
		btns[5].setAction(actionMap.get("client"));
		btns[6].setAction(actionMap.get("options"));
		btns[7].setAction(actionMap.get("help"));

		bar.add(btns[0]);
		bar.add(btns[1]);
		bar.add(btns[2]);
		bar.addSeparator();
		bar.add(btns[3]);
		bar.add(btns[4]);
		bar.add(btns[5]);
		bar.addSeparator();
		bar.add(btns[6]);
		bar.add(btns[7]);

		return bar;
	}

doch eigentlich auch recht angenehm. ( den unteren teil werde ich ggf. noch über eine Hilfsmethode oder eine Schleife lösen)

wobei ich hier wieder weiß worauf du mit redundanz anspielst ^^

Java:
btn.set...
btn.set...
btn.set...

//....

bar.add....
bar.add....
bar.add...

das ginge mit einer Indexierung natrürlich auch wesentlich kürzer , allerdings müsste ich dann auch immer gewährleisten das alle actions immer, ihren zugewiesenen index (im Action[] array) behalten, und das kann ich jetzt noch nicht so sagen.
 
Zuletzt bearbeitet:

Harry Kane

Top Contributor
Das mit der standardimplementierung der actionPerformed Methode habe ich mir angeschaut und aber auch wieder verworfen , auch wenn die Idee nicht schlecht war macht es a) keinen Unterschied ob ich nun in allen klassen actionPerformed überschreibe, oder getIndex(), zumal nur die wenigsten der ganzen Actions dafür zuständig sind Elemente Sichtbar zu machen. :)

Sonst hab ich hier 20 Actions mit einer leeren getIndex() implementierung
So war das natürlich nicht gemeint, daß alle deine Actions von einer Oberklasse mit getIndex Methode erben, sondern natürlich nur die, die eine Komponente sichtbar machen.
 

kaoZ

Top Contributor
Sprich du meinst Quasi die Actions nochmals Spezialisieren z.B in ComponentAction welche dann die getIndex() methode anbietet ?

Quasi solche eine Hierarchie

(abstract)Action->(abstract)ComponentAction-> | InfoAction | usw..
 

Harry Kane

Top Contributor
Jep, und als Oberklasse von ComponentAction noch eine abstrakte Klasse GuiAction, die eine private Referenz auf die Gui hält. Wie oben angemerkt, halte ich es für ein sauberes Design, das zig verwandte Klassen alle eine private Referenz auf denselben Objekttyp haben.
 

kaoZ

Top Contributor
Zzt. sieht das ganze so aus :

Java:
import javax.swing.AbstractAction;
import javax.swing.ImageIcon;
import editor.gui.Gui;


@SuppressWarnings("serial")
public abstract class Action extends AbstractAction{
	
	protected Gui gui;
	
	public Action(Gui gui, String name, ImageIcon icon){
		super(name, icon);
		this.gui = gui;
	}	
}

und eine der Spezialisierungen

Java:
import java.awt.event.ActionEvent;
import javax.swing.ImageIcon;
import editor.enums.ClientMenu;
import editor.gui.Gui;


@SuppressWarnings("serial")
public class ClientAction extends Action{

	public ClientAction(Gui gui, ImageIcon icon){
		super(gui, ClientMenu.MAINTENANCE.value(), icon);
	}
	
	@Override
	public void actionPerformed(ActionEvent e){
		gui.getComponent(0).setVisible();
	}
}


hier sind die acitons natürlich noch nicht unterteilt, aber die Referenz wird nur in der Basisklasse gehalten und durchgereicht.
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
Ich tüftel immernoch an der Gui , bin allerdings dazu übergegangen vorerst HashMap's zur Datenhaltung zu verwenden , da mir dies das ganze um einiges einfacher macht, und ich nicht immer schauen muss welche Komponente welchen index hat ^^

Java:
package editor.gui;

import java.awt.BorderLayout;
import java.awt.Image;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.event.WindowStateListener;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map.Entry;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.JToolBar;
import editor.App;
import editor.actions.Action;
import editor.actions.ClientAction;
import editor.actions.CloseAction;
import editor.actions.CutAction;
import editor.actions.DocumentAction;
import editor.actions.EstimateAction;
import editor.actions.ExitAction;
import editor.actions.ExportAction;
import editor.actions.HelpAction;
import editor.actions.ImportAction;
import editor.actions.InfoAction;
import editor.actions.InvoiceAction;
import editor.actions.OpenAction;
import editor.actions.OptionAction;
import editor.actions.PrintAction;
import editor.actions.SaveAction;
import editor.actions.SearchAction;
import editor.actions.ShortcutAction;
import editor.actions.ToolBarAction;
import editor.enums.ClientMenu;
import editor.enums.EditMenu;
import editor.enums.FileMenu;
import editor.enums.HelpMenu;
import editor.enums.MenuEntry;
import editor.enums.OptionMenu;
import editor.gui.components.ClientPanel;
import editor.gui.components.GuiComponent;
import editor.gui.components.InfoPanel;
import editor.gui.components.OptionPanel;
import editor.gui.components.SearchPanel;
import editor.gui.components.ShortcutPanel;

public class Gui{
	
	//	properties
	private int x;
	private int y;
	private int width;
	private int height;
	private int state;
	
	private boolean isFloatable; 
	
	//	gui
	private JFrame frame;
	private JMenuBar menuBar;
	private JToolBar toolBar;
	private JTabbedPane tabbedPane;

	//	map's
	private HashMap<String , GuiComponent> componentMap;
	private HashMap<String , Action> actionMap;
	private HashMap<String , Image> imageMap;

	public Gui(){
		try {
			loadResources();
			init();
			initActions();
			initComponents();
			addListener();
			addComponents();
		}
		catch (NullPointerException | IOException | IllegalArgumentException e) {
			e.printStackTrace();
		}
	}
	
	private void init(){
		frame = new JFrame(App.TITLE + " " + App.VERSION);
		frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
		frame.setMinimumSize(App.DIMENSION);
	}
	
	private void saveProperties(){

		//	window
		
		int state = frame.getExtendedState();
		
		if(state == JFrame.MAXIMIZED_BOTH) {
			App.PREFS.putInt("state", state);
		}
		else{
			Rectangle bounds = frame.getBounds();
			
			App.PREFS.putInt("xPos", bounds.x);
			App.PREFS.putInt("yPos", bounds.y);
			App.PREFS.putInt("width", bounds.width);
			App.PREFS.putInt("height", bounds.height);
			App.PREFS.putInt("state", state);
		}
		
		//	toolbar
		
		App.PREFS.putBoolean("toolbar", toolBar.isFloatable());

	}
	
	private void loadProperties(){
		x		 = App.PREFS.getInt("xPos", 0);
		y		 = App.PREFS.getInt("yPos", 0);
		width	 = App.PREFS.getInt("width", App.WIDTH);
		height	 = App.PREFS.getInt("height", App.HEIGHT);
		state  	 = App.PREFS.getInt("state", JFrame.NORMAL);
		
		isFloatable = App.PREFS.getBoolean("toolbar", true);
	}

	
	private void restoreGui(){
		frame.setBounds(x, y, width, height);
		
		toolBar.setFloatable(isFloatable);
	}

//	RESOURCES
	
	private void loadResources() throws IOException{
		imageMap = new HashMap<>();
		
		imageMap.put("new", ImageIO.read(getClass().getResourceAsStream("/images/newFile_small.png")));
		imageMap.put("open", ImageIO.read(getClass().getResourceAsStream("/images/file_small.png")));
		imageMap.put("close", ImageIO.read(getClass().getResourceAsStream("/images/close_small.png")));
		imageMap.put("save", ImageIO.read(getClass().getResourceAsStream("/images/save_small.png")));
		imageMap.put("search", ImageIO.read(getClass().getResourceAsStream("/images/search_small.png")));
		imageMap.put("client", ImageIO.read(getClass().getResourceAsStream("/images/client_small.png")));
		imageMap.put("option", ImageIO.read(getClass().getResourceAsStream("/images/options_small.png")));
		imageMap.put("help", ImageIO.read(getClass().getResourceAsStream("/images/help_small.png")));
		imageMap.put("toolbar", ImageIO.read(getClass().getResourceAsStream("/images/toolbar_small.png")));
		imageMap.put("locked", ImageIO.read(getClass().getResourceAsStream("/images/toolbar_fixed_small.png")));
		imageMap.put("cut", ImageIO.read(getClass().getResourceAsStream("/images/cut_small.png")));
		imageMap.put("print", ImageIO.read(getClass().getResourceAsStream("/images/print_small.png")));
	}
	
//	ACTIONS

	private void initActions() throws IOException{
		actionMap = new HashMap<>();
		
		actionMap.put("document", new DocumentAction(this, new ImageIcon(imageMap.get("new"))));
		actionMap.put("invoice",  new InvoiceAction(this, null));
		actionMap.put("estimate", new EstimateAction(this, null));
		actionMap.put("open", new OpenAction(this, new ImageIcon(imageMap.get("open"))));
		actionMap.put("close", new CloseAction(this, new ImageIcon(imageMap.get("close"))));
		actionMap.put("print", new PrintAction(this, new ImageIcon(imageMap.get("print"))));
		actionMap.put("save", new SaveAction(this, new ImageIcon(imageMap.get("save"))));
		actionMap.put("import", new ImportAction(this, null));
		actionMap.put("export", new ExportAction(this, null));
		actionMap.put("exit", new ExitAction(this, null));
		actionMap.put("info", new InfoAction(this, null));
		actionMap.put("shortcuts", new ShortcutAction(this, null));
		actionMap.put("search", new SearchAction(this, new ImageIcon(imageMap.get("search"))));
		actionMap.put("client", new ClientAction(this, new ImageIcon(imageMap.get("client"))));
		actionMap.put("options", new OptionAction(this, new ImageIcon(imageMap.get("option"))));
		actionMap.put("help", new HelpAction(this, new ImageIcon(imageMap.get("help"))));
		actionMap.put("toolbar", new ToolBarAction(this, new ImageIcon(imageMap.get("toolbar"))));
		actionMap.put("cut", new CutAction(this, new ImageIcon(imageMap.get("cut"))));
	}

	private void initComponents() throws IOException, NullPointerException, IllegalArgumentException{
		tabbedPane	= createTabbedPane();
		menuBar		= createMenuBar();
		toolBar		= createToolBar();

		componentMap = new HashMap<>();

		componentMap.put("client", new ClientPanel(this));
		componentMap.put("info", new InfoPanel(this));
		componentMap.put("option", new OptionPanel(this));
		componentMap.put("search", new SearchPanel(this));
		componentMap.put("shortcuts", new ShortcutPanel(this));
	}

//	WORKBENCH

	private JTabbedPane createTabbedPane(){
		JTabbedPane pane = new JTabbedPane();

		return pane;
	}

//	JMENUBAR

	private JMenuBar createMenuBar(){
		JMenuBar bar = new JMenuBar();

		JMenu fileMenu	 = new JMenu(MenuEntry.FILE.value());
		JMenu editMenu	 = new JMenu(MenuEntry.EDIT.value());
		JMenu optionMenu = new JMenu(MenuEntry.OPTIONS.value());
		JMenu clientMenu = new JMenu(MenuEntry.CLIENTS.value());
		JMenu helpMenu	 = new JMenu(MenuEntry.HELP.value());

		fileMenu.setMnemonic(KeyEvent.VK_D);
		editMenu.setMnemonic(KeyEvent.VK_B);
		optionMenu.setMnemonic(KeyEvent.VK_E);
		clientMenu.setMnemonic(KeyEvent.VK_K);
		helpMenu.setMnemonic(KeyEvent.VK_H);

		//	FileMenu

		JMenu fileSubMenu	 = new JMenu(FileMenu.NEW.value());
		JMenuItem invoice	 = new JMenuItem(actionMap.get("invoice"));
		JMenuItem estimate	 = new JMenuItem(actionMap.get("estimate"));
		addMenuItems(fileSubMenu, invoice, estimate);

		JMenuItem open		 = new JMenuItem(actionMap.get("open"));
		JMenuItem close		 = new JMenuItem(actionMap.get("close"));
		JMenuItem print		 = new JMenuItem(actionMap.get("print"));
		JMenuItem save 		 = new JMenuItem(actionMap.get("save"));
		JMenuItem importData = new JMenuItem(actionMap.get("import"));
		JMenuItem exportData = new JMenuItem(actionMap.get("export"));
		JMenuItem exit		 = new JMenuItem(actionMap.get("exit"));
		addMenuItems(fileMenu, fileSubMenu, new JSeparator(), open, close, save, new JSeparator());
		addMenuItems(fileMenu, print, new JSeparator(), importData, exportData, new JSeparator(), exit);

		//	EditMenu

		JMenuItem cut	 = new JMenuItem(actionMap.get("cut"));
		JMenuItem copy	 = new JMenuItem(EditMenu.COPY.value());
		JMenuItem paste	 = new JMenuItem(EditMenu.PASTE.value());
		JMenuItem delete = new JMenuItem(EditMenu.DELETE.value());
		JMenuItem search = new JMenuItem(actionMap.get("search"));
		JMenuItem sort	 = new JMenuItem(EditMenu.SORT.value());
		addMenuItems(editMenu, cut, copy, paste, new JSeparator());
		addMenuItems(editMenu, delete, search, sort);

		//	OptionMenu

		JMenu display	= new JMenu(OptionMenu.DISPLAY.value());
		JMenuItem small = new JMenuItem(OptionMenu.SMALL.value());
		JMenuItem mid 	= new JMenuItem(OptionMenu.MID.value());
		JMenuItem big	= new JMenuItem(OptionMenu.BIG.value());
		JMenuItem full 	= new JMenuItem(OptionMenu.FULL.value());
		addMenuItems(display, small, mid, big, full);

		JMenuItem user		 = new JMenuItem(OptionMenu.USER.value());
		JMenuItem options	 = new JMenuItem(actionMap.get("options"));
		addMenuItems(optionMenu, display, new JSeparator(), user, options);

		//	ClientMenu

		JMenuItem clientMaintenance	 = new JMenuItem(actionMap.get("client"));
		JMenuItem saveClient		 = new JMenuItem(ClientMenu.SAVE.value());
		addMenuItems(clientMenu, clientMaintenance, new JSeparator(), saveClient);

		//	HelpMenu

		JMenuItem shortcuts	 = new JMenuItem(actionMap.get("shortcuts"));
		JMenuItem about		 = new JMenuItem(actionMap.get("info"));
		JMenuItem contact 	 = new JMenuItem(HelpMenu.CONTACT.value());
		JMenuItem docu		 = new JMenuItem(HelpMenu.DOCU.value());
		JMenuItem errorlog 	 = new JMenuItem(HelpMenu.ERRORLOG.value());
		JMenuItem update	 = new JMenuItem(HelpMenu.UPDATE.value());
		addMenuItems(helpMenu, shortcuts,new JSeparator(), about, contact);
		addMenuItems(helpMenu, new JSeparator(), docu, errorlog, update);

		bar.add(fileMenu);
		bar.add(editMenu);
		bar.add(optionMenu);
		bar.add(clientMenu);
		bar.add(helpMenu);

		return bar;
	}

	private void addMenuItems(JMenu menu, JComponent...components){
		for (JComponent component : components) {
			menu.add(component);
		}
	}

//	JTOOLBAR

	private JToolBar createToolBar(){
		JToolBar bar = new JToolBar();

		JButton[] btns = new JButton[9];
		for (int i = 0; i < btns.length; i++) {
			btns[i] = new JButton();
			btns[i].setHideActionText(true);
		}

		btns[0].setAction(actionMap.get("document"));
		btns[1].setAction(actionMap.get("open"));
		btns[2].setAction(actionMap.get("close"));
		btns[3].setAction(actionMap.get("search"));
		btns[4].setAction(actionMap.get("save"));
		btns[5].setAction(actionMap.get("client"));
		btns[6].setAction(actionMap.get("print"));
		btns[7].setAction(actionMap.get("options"));
		btns[8].setAction(actionMap.get("help"));
		
		bar.add(btns[0]);
		bar.add(btns[1]);
		bar.add(btns[2]);
		bar.addSeparator();
		bar.add(btns[3]);
		bar.add(btns[4]);
		bar.add(btns[5]);
		bar.addSeparator();
		bar.add(btns[6]);
		bar.add(btns[7]);
		bar.add(btns[8]);

		return bar;
	}
	
	private void addListener(){
		GuiListener listener = new GuiListener();
		frame.addWindowListener(listener);
		frame.addWindowStateListener(listener);
	}

	private void addComponents(){
		frame.setJMenuBar(menuBar);
		frame.add(BorderLayout.PAGE_START, toolBar);
		frame.add(BorderLayout.CENTER, tabbedPane);
	}
	
	public void addTab(JComponent tab){
		tabbedPane.add(tab, tab.getName());
	}
	
	public void removeTab(JComponent tab){
		tabbedPane.remove(tab);
	}

	public void setVisible(){
		frame.setVisible(true);
	}

	public void dispose(){
		frame.dispose();
	}

	public void disposeAll(){
		for(Entry<String, GuiComponent> entry : componentMap.entrySet()){
			entry.getValue().dispose();
		}
		dispose();
		
	}

	public int countChilds()								{return componentMap.size();}
	public JFrame getFrame()								{return frame;}
	public JTabbedPane getTabbedPane()						{return tabbedPane;}
	public JToolBar getToolBar()							{return toolBar;}
	public JMenuBar getMenuBar()							{return menuBar;}
	public JComponent getSelectedTab()						{return (JComponent)tabbedPane.getSelectedComponent();}
	public GuiComponent getComponentByName(String name)		{return componentMap.get(name);}
	public Action getActionByName(String name)				{return actionMap.get(name);}
	public Image getImageByName(String name)				{return imageMap.get(name);}

//	INNER CLASS LISTENER

	class GuiListener implements WindowListener, WindowStateListener{
		
		@Override
		public void windowOpened(WindowEvent e){
			loadProperties();
			if(state == JFrame.MAXIMIZED_BOTH) {
				frame.setExtendedState(state);
			}
			else{
				restoreGui();
			}
		}

		@Override
		public void windowClosing(WindowEvent e){
			disposeAll();
		}

		@Override
		public void windowClosed(WindowEvent e){
			saveProperties();
		}

		@Override
		public void windowIconified(WindowEvent e){}

		@Override
		public void windowDeiconified(WindowEvent e){}

		@Override
		public void windowActivated(WindowEvent e){}

		@Override
		public void windowDeactivated(WindowEvent e){}

		@Override
		public void windowStateChanged(WindowEvent e){
			if(e.getNewState() == JFrame.NORMAL) {
				restoreGui();
			}
		}	
	}
}

Wie würdet ihr die properties der gui und dessen komponenten Speichern so wie hier in einem Preferences Objekt oder sollte jede komponente ihren eigenen state in einem properties oder Preferences Objekt speichern und sich die Gui diese elemente einfach holen und daraus die Gui rekonstruieren?
 

Ruzmanz

Top Contributor
Wie würdet ihr die properties der gui und dessen komponenten Speichern so wie hier in einem Preferences Objekt oder sollte jede komponente ihren eigenen state in einem properties oder Preferences Objekt speichern und sich die Gui diese elemente einfach holen und daraus die Gui rekonstruieren?

Was genau meinst du mit einem "Preferences Objekt"? In Java gibt es Properties (Java Platform SE 7 ) , womit du relativ bequem relevante Einstellungen abspeichern / laden kannst.

Ich würde außerdem nicht so viel mit den Strings arbeiten. Wenn du was umbenennen willst oder musst, dann ist das voll der Aufwand.

Java:
	private HashMap<Class<? extends Action>, Action> actions;

	public Test() {
		actions = new HashMap<>();

		registerAction(new Print());
		registerAction(new PrintFast());
	}

	public void registerAction(Action action) {
		actions.put(action.getClass(), action);
	}
	
	// -> actions.get(PrintFast.class)

Und alles was so eine "schöne Einheit" bildet ist, lässt sich deutlich reduzieren. Bsp. ist es umständlich bei deiner ToolBar ein Element hinzuzufügen bzw. zu löschen. Deutlich wartbarer und weniger fehleranfällig (ungetestet):

Java:
	String[] toolBar = {
			"document", "open", "close",
			"search", "save", "client",
			"print", "options", "help"};
	
	public JToolBar buildToolbar() {
		JToolBar bar = new JToolBar();
		
		btns = new JButton[toolBar.length];
		 
		for(int i = 0; i < btns.length; i++) {
			btns[i] = new JButton();
			btns[i].setHideActionText(true);
			
			bar.add(btns[i]);
			
			if(i%3==2) { // Nach jeweils drei Buttons einen Seperator
				bar.addSeparator();
			}
		}
		
		return bar;
	}
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
Die hardcoded strings hätte ich später durch Konstanten ersetzt,

Zu der Toolbar, so wie du es gepostet hast sieht zwar das hinzufügen wesentlich besser aus, jedoch müsste ich trotzdem die Einzelnen Actions den Buttons zuweisen.

Das mit den Actions schaue ich mir an, allerdings benötigen diese eben eine Referenz auf die gui sowie ein ImageIcon, außer ich halte dies direkt in der Actions selbst, oder gehe tatsächlich dazu über und erstelle mir eigene Buttons welche dann dann Action und Icon als Referenz bekommen.

Zzt. Schreibe ich die bounds und die Einstellung der gui in die registry, sprich also über Preferences, Hierzu halte ich in der Klasse App.Java ein statisches Preferences Objekt, in welches ich beim beenden der Applikation die aktuellen Einstellungen Schreibe.
Beim Start werden diese dann ausgelesen und die gui Rekonstruiert.

Sicher ginge dies auch unter Verwendung von Properties, nur dort muss ich mich jedes mal um selbst in das Parsen kümmern, bei Verwendung von Preferences nicht.

Die Frage ist, halte ich alle Daten zentral oder lege ich für jede Komponente eine Datei an welche deren Letzt Einstellungen enthält.
 
Zuletzt bearbeitet:

kaoZ

Top Contributor
Niemand ein statement dazu ? :noe:

Ausserdem überlege ich die Images bzw. das einlesen dieser direkt in die Actions zu verlagern , da diese ja auch letztendlich das image enthalten,

so kann ich mir auch eine zusätzliche collection und das laden der resourcen in der gui selbst sparen .

PS: das mit den Actions und den Componenten funktioniert übrigens reibungslos über die class. deklaration. :) Guter Tip, danke.
 
Zuletzt bearbeitet:
Ähnliche Java Themen
  Titel Forum Antworten Datum
L JPA keine shared primary key Allgemeine Java-Themen 11
E Java Shared Memory? Allgemeine Java-Themen 6
F Java Native/Shared Library (.so) laden macht Probleme Allgemeine Java-Themen 3
G JNI Shared Object Allgemeine Java-Themen 10
J Zugriff auf Dateien auf einem shared Folder? Allgemeine Java-Themen 3
A Best Practice Wie viele Referenzen machen Sinn? Weniger ist mehr? Allgemeine Java-Themen 1
J Mit Referenzen verkettet Listen. Allgemeine Java-Themen 9
A Referenzen von Bildobjekten löschen Allgemeine Java-Themen 0
K Referenzen finden Allgemeine Java-Themen 6
R Referenzen im Heap anzeigen Allgemeine Java-Themen 3
M Datentypen Referenzen oder seperate IDs vergleichen? Allgemeine Java-Themen 6
D Problem mit Referenzen beim Serialisieren Allgemeine Java-Themen 3
W n:m Beziehung Referenzen löschen Allgemeine Java-Themen 5
A Methoden ohne Referenzen finden Allgemeine Java-Themen 9
E EMF Ecore Modell. Referenzen nach außen. Allgemeine Java-Themen 2
F Referenzen auf Objekte Allgemeine Java-Themen 5
hdi Speichergröße von Objekten & Referenzen Allgemeine Java-Themen 8
S Serialisierung und Referenzen Allgemeine Java-Themen 6
D Referenzen; == Allgemeine Java-Themen 2
D Referenzen weiterreichen vs. statischer Zugriff Allgemeine Java-Themen 3
G Pointer und Referenzen Allgemeine Java-Themen 3
I Referenzen in Datei speichern Allgemeine Java-Themen 2
G (De)serialisierung und Referenzen Allgemeine Java-Themen 5
J Bibliothek gesucht Ana_lysieren von wss. Referenzen Allgemeine Java-Themen 2
A ArrayListe :Doppelte entfernen -> keine Referenzen Allgemeine Java-Themen 26
O Vektor kopieren (Inhalt, nicht Referenzen) Allgemeine Java-Themen 3
M Übergabe von Referenzen Allgemeine Java-Themen 3
H Referenzen statt Objekte für große Speicherstrukturen Allgemeine Java-Themen 19
P Das leidige Thema: Referenzen Allgemeine Java-Themen 2
N int[] referenzen in ein Array packen, brauche Hilfe. Allgemeine Java-Themen 7
A Referenzen / HashCodes nicht konstant? Allgemeine Java-Themen 2
K Probleme mit Referenzen Allgemeine Java-Themen 2

Ähnliche Java Themen

Neue Themen


Oben