public class SingleParameterFilterListener extends AbstractParameterFilterListener
											implements ActionListener {

	SingleParameterFilter filter;
	double value;
	
	public SingleParameterFilterListener(SingleParameterFilter filter, FunPhotoTimeFrame mainFrame) {
		super(mainFrame);
		this.filter = filter;
	}

	@Override
	protected FilterDialog getDialog(MyInternalFrame frameIn) {
		return (FilterDialog) filter.getDialog(frameIn);
	}

	@Override
	protected void storeValues(FilterDialog dialog) {
		value = ((FilterDialogThatReturnsDouble)dialog).getDouble();
	}

	@Override
	protected Thread createThread(MyInternalFrame frameIn, MyInternalFrame frameOut) {
		return new Thread(new WaitingThread(frameIn, frameOut));
	}
	
	@Override
	protected void createFilterThread(BufferedImage imgIn, MyInternalFrame frameOut) {
		filter.createThread(imgIn, value , frameOut);
	}
	
} public class MultipleParameterFilterListener extends AbstractParameterFilterListener
												implements ActionListener {
	
	MultipleParameterFilter filter;
	int[] values;
	
	public MultipleParameterFilterListener(MultipleParameterFilter filter, FunPhotoTimeFrame mainFrame) {
		super(mainFrame);
		this.filter = filter;
	}

	@Override
	protected FilterDialog getDialog(MyInternalFrame frameIn) {
		return (FilterDialog) filter.getDialog(frameIn);
	}

	@Override
	protected void storeValues(FilterDialog dialog) {
		values = ((FilterDialogThatReturnsInts)dialog).getInts();
	}

	@Override
	protected Thread createThread(MyInternalFrame frameIn, MyInternalFrame frameOut) {
		return new Thread(new WaitingThread(frameIn, frameOut));
	}

	@Override
	protected void createFilterThread(BufferedImage imgIn, MyInternalFrame frameOut) {
		filter.createThread(imgIn, values , frameOut);
	}
	
} public class ParameterlessFilterListener implements ActionListener{
	FunPhotoTimeFrame mainFrame;
	ParameterlessFilter fi;

	public ParameterlessFilterListener(ParameterlessFilter fi, FunPhotoTimeFrame mainFrame) {
		this.mainFrame = mainFrame;
		this.fi = fi;
    }
	
    public void actionPerformed(ActionEvent e) {
    	if (mainFrame.isMemoryCritical())
			return;
		
    	try {
	    	MyInternalFrame frameIn = (MyInternalFrame) mainFrame.desktop.getSelectedFrame();
	    	//naredi nov frame ki zaenkrat vsebuje samo zumirano obdelano sliko brez originala
	    	
    		BufferedImage filteredImage = fi.getFilteredImage(mainFrame.getSelectedBufferedImage());
    		MyInternalFrame frameOut = mainFrame.createZoomedFrame(filteredImage, frameIn);
        	
        	if ( frameIn.getZoom() != 100 ) {
        		// Makes thread that waits for original image to be created.
        		Thread t = new Thread(new WaitingThread(frameIn, frameOut, fi));
                t.start();
            }
	    } catch (OutOfMemoryError g) {
	    	mainFrame.outOfMemoryMessage();
		}
    }
    
    private static class WaitingThread implements Runnable {
    	MyInternalFrame frameIn;
    	MyInternalFrame frameOut;
    	ParameterlessFilter fi;
	    public WaitingThread(MyInternalFrame frameIn, MyInternalFrame frameOut,
				ParameterlessFilter fi) {
			this.frameIn = frameIn;
			this.frameOut = frameOut;
			this.fi = fi;
		}

		public void run() {
			BufferedImage imgIn = Utility.waitForOriginalImage(frameIn);
    		// Makes thread that will filter the original image.
        	fi.createThread(imgIn, frameOut);
	    }
    }
    
} public class LoadListener implements ActionListener {
	
	FunPhotoTimeFrame frame;
	public LoadListener(FunPhotoTimeFrame frame) {
		this.frame = frame;
	}
	
    public void actionPerformed(ActionEvent e) {
    	try {
	    	JFileChooser chooser = new JFileChooser();
	    	
	    	ImagePreviewPanel preview = new ImagePreviewPanel();
	    	chooser.setAccessory(preview);
	    	chooser.addPropertyChangeListener(preview);
	    	
	    	chooser.setMultiSelectionEnabled(true);
	    	// adds file filters
	    	for (ExtensionFileFilter filter : ExtensionFileFilter.all) {
				chooser.addChoosableFileFilter(filter);
			}
	    	if (frame.lastPathLoad != null) {
	    		chooser.setCurrentDirectory(frame.lastPathLoad);
	    	}
	    	int returnVal = chooser.showOpenDialog(frame);
	    	if (returnVal == JFileChooser.APPROVE_OPTION) {
	    		try {
	    			for (File fIn : chooser.getSelectedFiles()) {
	    				// TODO memory footprint check
	    				BufferedImage imgIn = ImageIO.read(fIn);
						frame.createFrame(imgIn, fIn.getName());
	    			}
				} catch (IOException f) {}
				frame.lastPathLoad = chooser.getSelectedFile();
		    }
    	} catch (OutOfMemoryError g) {
    		frame.outOfMemoryMessage();
    	}
	}

} public class OperationDialogListener implements ActionListener {
	FunPhotoTimeFrame mainFrame;
	Operation op;
	
	public OperationDialogListener(Operation op, FunPhotoTimeFrame mainFrame) {
		this.mainFrame = mainFrame;
		this.op = op;
	}
	
	public void actionPerformed(ActionEvent e) {
		// TODO Zoom problem
		if (mainFrame.isMemoryCritical())
			return;
		
		try {
			OperationDialog dialog = (OperationDialog) op.getDialog(mainFrame.desktop);
	    	if (!dialog.wasCanceled()) {
	    		int firstZoom = dialog.getFirstZoom();
	    		int secondZoom = dialog.getSecondZoom();
	    		// If frames have different zoom, or one of them is actual size.
	    		if ( (firstZoom != secondZoom) || (firstZoom == 100) || (secondZoom == 100) ) {
	    			MyInternalFrame frame = 
	        				mainFrame.createFrame(dialog.getProcessedOrigImage(), dialog.getName());
	        		frame.setZoom(firstZoom);
	    		}
	    		else {
	    			MyInternalFrame frame = 
	        				mainFrame.createZoomedFrame(dialog.getProcessedImage(), dialog.getFirstFrame());
	    			// TODO waiting thread
	    			Thread t = new Thread(new WaitingThread(frame, dialog));
	                t.start();
	    		}
	        }
		} catch (OutOfMemoryError g) {
	    	mainFrame.outOfMemoryMessage();
		}
    }
	
    private static class WaitingThread implements Runnable {
    	MyInternalFrame frameOut;
    	OperationDialog dialog;
	    public WaitingThread(MyInternalFrame frameOut, OperationDialog dialog) {
			this.frameOut = frameOut;
			this.dialog = dialog;
		}

		public void run() {
			frameOut.setOriginalImg(dialog.getProcessedOrigImage());
	    }
    }
	
} public class SaveListener implements ActionListener {
	
	FunPhotoTimeFrame frame;
	public SaveListener(FunPhotoTimeFrame frame) {
		this.frame = frame;
	}
	
	private void saveFile(String formatName, File outputfile) {
		try {
        	ImageIO.write(frame.getSelectedOriginalBufferedImage(), formatName, outputfile);
        	frame.lastPathSave = outputfile;
    	} catch (IOException f) {
    		JOptionPane.showMessageDialog(null, "SAVE ERROR!", "Error", JOptionPane.ERROR_MESSAGE);
    	}
	}
	
    public void actionPerformed(ActionEvent e) {
    	if ( frame.desktop.getSelectedFrame() == null ) 
    		return;

		JFileChooser chooser = new JFileChooser() {
			private static final long serialVersionUID = 291238218189760173L;

			@Override
			public void approveSelection() {

		    	String formatName = "";
		    	String errMessage = "";
		    	File outputfile = this.getSelectedFile();
		    	final String givenName = outputfile.getName();
		    	
		    	// Filename Test:
		    	// No filter chosen
		    	if (this.getFileFilter().getDescription() == "All Files") {
		    		ExtensionFileFilter fileFilter = ExtensionFileFilter.getFilter(givenName);
		    		// No extension given - ERR
		    		if ( givenName.indexOf(".") == -1 ) {
		    			errMessage = "No filename extension or file filter selected. Image was not saved.";
		        	}
		    		// No file filter matches the extension - ERR 
		    		else if (fileFilter == null) {
		    			errMessage = "Unknown filename extension. Image was not saved.";
		    		}
		    		// Valid extension - OK
		    		else {
		    			formatName = fileFilter.getDescription();
		    		}
		    	}
		    	// Filter selected
		    	else {
		        	ExtensionFileFilter selectedFilter = (ExtensionFileFilter) this.getFileFilter();
		        	// Filter and extension match - OK
		        	if ( selectedFilter.accept(givenName) ) {
		        		formatName = selectedFilter.getDescription();
		        	}
		        	// No extension given - OK
		        	else if ( givenName.indexOf(".") == -1 ) {
		        		String newPathName = outputfile.getPath().concat("."+selectedFilter.getDescription() );
		        		outputfile = new File(newPathName);
		        		formatName = selectedFilter.getDescription();
		        	}
		        	// Extension does not match selected filter - ERR
		        	else {
		        		errMessage = "Filename extension and file filter did not match. Image was not saved.";
		        	}
		    	}
				
		    	// If it passed the test above -> Filename was valid and passed trough filter.
				if (formatName != "") {
					//********************************************//
					// File already exists check:
					if (outputfile.exists() && getDialogType() == SAVE_DIALOG) {
						int result = JOptionPane.showConfirmDialog(this,
								"The file exists, overwrite?", "Existing file",
								JOptionPane.YES_NO_CANCEL_OPTION);
						switch (result) {
							case JOptionPane.YES_OPTION:
								
								// SAVE (overwrite existing file)
								saveFile(formatName, outputfile);
								
								super.approveSelection();
								return;
							case JOptionPane.NO_OPTION:
								return;
							case JOptionPane.CLOSED_OPTION:
								return;
							case JOptionPane.CANCEL_OPTION:
								cancelSelection();
								return;
						}
					}
					// SAVE (new file)
					saveFile(formatName, outputfile);
			    	
					super.approveSelection();
					return;
				}
				// If it didn't pass the test from above -> Filename wasn't valid or it didn't pass trough filter.
				else {
					JOptionPane.showMessageDialog(null, errMessage, "Error", JOptionPane.ERROR_MESSAGE);
					return;
				}
			}
		};
		
    	ImagePreviewPanel preview = new ImagePreviewPanel();
    	chooser.setAccessory(preview);
    	chooser.addPropertyChangeListener(preview);
		
		String fileName = ((MyInternalFrame)frame.desktop.getSelectedFrame()).getFileName();
		// odstrani koncnico imenu
		fileName = removeExtension(fileName);
		if (frame.lastPathSave != null) {
			File path = frame.lastPathSave.getParentFile();
			chooser.setSelectedFile(new File(path, fileName));
		} else if (frame.lastPathLoad != null) {
			File path = frame.lastPathLoad.getParentFile();
			chooser.setSelectedFile(new File(path, fileName));
		} else {
			chooser.setSelectedFile(new File(fileName));
		}
		chooser.setDialogTitle("Save As");

		for (ExtensionFileFilter filter : ExtensionFileFilter.all) {
			chooser.addChoosableFileFilter(filter);
		}
		chooser.setFileFilter(ExtensionFileFilter.png);
		
		// OPEN DIALOG
    	chooser.showSaveDialog(frame);
    }
    
    private static String removeExtension(String fileName) {
    	int indexOfDot = fileName.indexOf(".");
		if (indexOfDot != -1)
			fileName = (String) fileName.subSequence(0, indexOfDot);
		return fileName;
    }
    
} public class SpatialFilterWithouthDialogListener implements ActionListener{
	FunPhotoTimeFrame mainFrame;
	ParameterlessFilter fi;

	public SpatialFilterWithouthDialogListener(ParameterlessFilter fi, FunPhotoTimeFrame mainFrame) {
		this.mainFrame = mainFrame;
		this.fi = fi;
    }
	
    public void actionPerformed(ActionEvent e) {
    	if (mainFrame.isMemoryCritical())
			return;
		
    	try { 
	    	MyInternalFrame frameIn = (MyInternalFrame) mainFrame.desktop.getSelectedFrame();
	    	BufferedImage origImgIn = Utility.waitForOriginalImage(frameIn);
		    
	    	BufferedImage origImgOut = fi.getFilteredImage(origImgIn);
	    	int zoom = frameIn.getZoom();
	    	
	    	if ( frameIn.getZoom() == 100 ) {
	    		mainFrame.createZoomedFrame(origImgOut, frameIn);
	    	}
	    	else {
	    		BufferedImage zoomedImgOut = Zoom.out(origImgOut, zoom);
	    		MyInternalFrame frameOut = mainFrame.createZoomedFrame(zoomedImgOut, frameIn);
	    		frameOut.setOriginalImg(origImgOut);
	    	}
    	} catch (OutOfMemoryError g) {
	    	mainFrame.outOfMemoryMessage();
		}
    }
    
} public class TileListener implements ActionListener {
	
	FunPhotoTimeFrame mainFrame;
	public TileListener(FunPhotoTimeFrame mainFrame) {
		this.mainFrame = mainFrame;
	}

	public void actionPerformed(ActionEvent arg0) {
		HorVerCombo maxCombo = getMaxCombo(mainFrame);
		tileFrames(mainFrame, maxCombo.combo, maxCombo.zoom);
	}
	
	////////////////////////

	/*
	 * GET BEST HORIZONTAL/VERTICAL COMBINATION
	 */
	public static HorVerCombo getMaxCombo(FunPhotoTimeFrame mainFrame) {
		MyInternalFrame[] frames = mainFrame.getAllFrames();
		Dimension desktopSize = mainFrame.desktop.getSize();
		Point[] combos = getAllHorVerCombos(frames.length);
		HorVerCombo maxCombo = new HorVerCombo(null, 0, 100);

		for (Point combo : combos) {
			Dimension cellSize = getCellSize(combo, desktopSize); 
			int commonZoom = getCommonMaxZoom(frames, cellSize);
			int comboSurface = getSurfaceSum(frames, commonZoom);
			
			if (comboSurface > maxCombo.surface) {
				maxCombo = new HorVerCombo(combo, comboSurface, commonZoom);
			}
		}
		return maxCombo;
	}
	
	////////////////////////

	/*
	 * GET ALL HORIZONTAL/VERTICAL COMBINATIONS
	 * 
	 * Returns all combinations of columns/rows, that could potentialy maximize
	 * sum of surfacess. For noOfFrames = 9 it returns: (1,9), (2,5), (3,3), (5,2), (9,1)  
	 */
	public static Point[] getAllHorVerCombos(int noOfFrames) {
		List<Point> combos = new ArrayList<Point>();
		int lastY = 0;
		for (int x = 1; x <= noOfFrames; x++) {
			int y = noOfFrames / x;
			int reminder = noOfFrames % x;
			if (reminder > 0)
				y += 1;
			if (lastY == y)
				continue;
			lastY = y;
			combos.add(new Point(x, y));
		}
		return combos.toArray(new Point[0]);
	}

	/////////////////////
	
	/*
	 * GET COMMON MAXIMUM ZOOM
	 * 
	 * Returns the zoom that maximizes the sum of surfaces of internal frames.
	 * Zoom is restricted to values of Zoom.STEPS array. 
	 */
	public static int getCommonMaxZoom(MyInternalFrame[] frames, Dimension cellSize) {
		for (int zoom : Zoom.STEPS) {
			if (allFitInCell(frames, cellSize, zoom)) 
				return zoom;
		}
		return 0;
	}
	
	public static boolean allFitInCell(MyInternalFrame[] frames, Dimension cellSize, int zoom) {
		for (MyInternalFrame frame : frames) {
			if (!fitsInCell(frame, cellSize, zoom)) 
				return false;
		}
		return true;
	}

	public static boolean fitsInCell(MyInternalFrame frame, Dimension cellSize, int zoom) {
		Dimension imageSize = frame.getOriginalImageSize();
		imageSize.setSize(
			imageSize.width * (zoom / 100.0), 
			imageSize.height * (zoom / 100.0)
		);
		
		if (imageSize.width + MyInternalFrame.BORDER_WIDTH > cellSize.width)
			return false;
		if (imageSize.height + MyInternalFrame.BORDER_HEIGHT > cellSize.height)
			return false;
		
		return true;
	}
	
	/////////////////////

	/*
	 * GET SUM OF SURFACES
	 */
	public static int getSurfaceSum(MyInternalFrame[] frames, int zoom) {
		int sum = 0;
		for (MyInternalFrame frame : frames) {
			sum += getSurface(frame, zoom);
		}
		return sum;
	}
	
	public static int getSurface(MyInternalFrame frame, int zoom) {
		return (int) ((frame.getOriginalImageMemoryFootprint() / 3) 
				/ Utility.getSurfaceAreaFactorForZoom(zoom));
	}

	////////////////////////
	////////////////////////

	/*
	 * TILE FRAMES
	 */
	private static void tileFrames(FunPhotoTimeFrame mainFrame, Point combo, int zoom) {
		Dimension desktopSize = mainFrame.desktop.getSize();
		int slotNo = 0;
		for (MyInternalFrame frame : mainFrame.getAllFrames()) {
			frame.setZoom(zoom);
			Point slot = getSlot(combo.x, slotNo);
			Point location = getLocation(desktopSize, frame.getSize(), combo, slot);
			frame.setLocation(location);
			slotNo++;
		}
	}

	// Slots start from (0,0).
	private static Point getSlot(int width, int n) {
		return new Point(n % width, n / width);
	}

	private static Point getLocation(Dimension desktopSize, Dimension frameSize, Point combo,	Point slot) {
		Dimension cellSize = getCellSize(combo, desktopSize);
		Point upperLeftCornerOfCell = getUpperLeftCornerOfCell(cellSize, slot);
		Point locationWithinCell = getLocationWithinCell(frameSize, cellSize);		
		return new Point(
			upperLeftCornerOfCell.x + locationWithinCell.x,
			upperLeftCornerOfCell.y + locationWithinCell.y
		);
	}

	private static Point getUpperLeftCornerOfCell(Dimension cellSize, Point slot) {
		return new Point(
			slot.x * cellSize.width,
			slot.y * cellSize.height
		);
	}

	private static Point getLocationWithinCell(Dimension frameSize,
			Dimension cellSize) {
		return new Point(
			(cellSize.width - frameSize.width) / 2,
			(cellSize.height - frameSize.height) / 2
		);
	}

	////////////////////////
	////////////////////////
	
	/*
	 * COMMON
	 */
	
	public static Dimension getCellSize(Point combo, Dimension desktopSize) {
		return new Dimension( 
			desktopSize.width / combo.x, 
			desktopSize.height / combo.y 
		);
	}

} public class ZoomListener implements ActionListener {
	FunPhotoTimeFrame mainFrame;
	ZoomOperation zo;
	
	public ZoomListener(ZoomOperation zo, FunPhotoTimeFrame mainFrame) {
		this.mainFrame = mainFrame;
		this.zo = zo;
	}

	public void actionPerformed(ActionEvent e) {
		MyInternalFrame selectedFrame = (MyInternalFrame) mainFrame.desktop.getSelectedFrame();
		long footprint = (long) (selectedFrame.getOriginalImageMemoryFootprint());
		if (zo != ZoomOperation.ZOOM_ACTUAL) {
			footprint = (long) (footprint / Utility.getSurfaceAreaFactorForZoom(zo.newZoom(selectedFrame)));
		}
		if (!mainFrame.isThereEnoughMemoryFor(footprint)) {
			mainFrame.lowOnMemoryWarning();
			return;
		}
		
		try {
			MyInternalFrame frame = (MyInternalFrame) mainFrame.desktop.getSelectedFrame();
			zo.apply(frame);
		} catch (OutOfMemoryError g) {
			mainFrame.outOfMemoryMessage();
		}
	}
} public class AboutDialog extends JFrame {

	private static final long serialVersionUID = 4236082773760097536L;
	static final int WIDTH = 240;
	static final int HEIGHT = 105;
	
	protected JPanel p;
	protected JDialog dlg;
	protected JOptionPane op;

    public AboutDialog() throws URISyntaxException {
    	/* Buton to website
    	final URI uri = new URI("http://www.yahoo.com");
        class OpenUrlAction implements ActionListener {
          @Override public void actionPerformed(ActionEvent e) {
            open(uri);
          }
        }
        
        JButton button = new JButton();
        button.setText("<HTML><FONT color=\"#000099\"><U>homepage</U></FONT></HTML>");
        button.setHorizontalAlignment(SwingConstants.LEFT);
        button.setBorderPainted(false);
        button.setOpaque(false);
        button.setBackground(Color.WHITE);
        button.setToolTipText(uri.toString());
        button.addActionListener(new OpenUrlAction());
    	*/
    	//////////////
        
		p = new JPanel();
    	p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
    	op = new JOptionPane(p,
    		JOptionPane.PLAIN_MESSAGE,
    		JOptionPane.CLOSED_OPTION);
    	dlg = op.createDialog(this, "About");
    	JLabel lbl;

    	// Name
    	lbl = new JLabel("Fun Photo Time", SwingConstants.CENTER); 
    	lbl.setAlignmentX(Component.CENTER_ALIGNMENT);
    	Font newLabelFont =new Font(lbl.getFont().getName(), Font.BOLD, lbl.getFont().getSize()+3);
    	lbl.setFont(newLabelFont);
    	lbl.setHorizontalAlignment(JLabel.CENTER);
    	p.add(lbl);
    	// Icon
    	URL url = getClass().getResource(Conf.ICON_FILENAME_M);
    	ImageIcon icon = new ImageIcon(url);
	    JLabel label = new JLabel(icon);
	    label.setAlignmentX(Component.CENTER_ALIGNMENT);
    	p.add(label);
    	// Other info
    	addLabelToPanel(" ", p);
    	addLabelToPanel("Version: " + Conf.VERSION, p);
    	addLabelToPanel(" ", p);
    	addLabelToPanel("Author: Jure Šorn", p);
    	addLabelToPanel("<sornjure@hotmail.com>", p);
    	addLabelToPanel(" ", p);
    	//p.add(button);

    	
    	dlg.setSize(WIDTH, HEIGHT);
    	Point location = Utility.centerTheCenter(FunPhotoTime.frame.getFrameCenter(), WIDTH, HEIGHT);
    	dlg.setLocation(location);
    	dlg.pack();
    	dlg.setAlwaysOnTop(true);
    	dlg.setVisible(true);
    	dlg.dispose();
    	
	}
    
    private static void addLabelToPanel(String txt, JPanel pnl) {
    	JLabel lbl = new JLabel(txt);
    	lbl.setAlignmentX(Component.CENTER_ALIGNMENT);
    	pnl.add(lbl);
    }
	  
    /*
	private static void open(URI uri) {
	    if (Desktop.isDesktopSupported()) {
	    	try {
	    		Desktop.getDesktop().browse(uri);
	    	} catch (IOException e) {
	    	}
	    }
	}
	*/
    
} public enum MultipleParameterFilter {
	HISTOGRAM_STRETCHING,
	COLOR_BALANCE;

	public FilterDialogThatReturnsInts getDialog(MyInternalFrame frame) {
		switch (this) {
	    	case HISTOGRAM_STRETCHING:
	    		double[] histogram = Utility.getHistogram(frame.getImg());
            	BufferedImage histogramImg = Utility.getHistogramImage(histogram);
	    		return new HistogramStretchingDialog(frame, histogramImg);
	    	case COLOR_BALANCE:
	    		return new ColorsDialog(frame);
	    	default:
	    		throw new ExceptionInInitializerError() {
					private static final long serialVersionUID = -2115640049630620261L;
	    		};
		}
    }
	
	public void createThread(BufferedImage imgIn, int[] values, MyInternalFrame frameOut ) {
		switch (this) {
	    	case HISTOGRAM_STRETCHING:
	    		new HistogramStretchingThread2(imgIn, values, frameOut);
	    		break;
	    	case COLOR_BALANCE:
	    		new ColorsThread2(imgIn, values, frameOut);
	    		break;
	    	default:
	    		throw new ExceptionInInitializerError() {
					private static final long serialVersionUID = -2877266507139641775L;
	    		};
		}
	}
	
} public enum Operation {
	ADDITION,
	SUBTRACTION,
	MULTIPLICATION,
	DIVISION,
	AND,
	OR,
	XOR;

	public OperationDialog getDialog(JDesktopPane desktop) {
		switch (this) {
	    	case ADDITION:
	    		return new AdditionDialog(desktop);
	    	case SUBTRACTION:
	    		return new SubtractionDialog(desktop);
	    	case MULTIPLICATION:
	    		return new MultiplicationDialog(desktop);
	    	case DIVISION:
	    		return new DivisionDialog(desktop);
	    	case AND:
	    		return new AndDialog(desktop);
	    	case OR:
	    		return new OrDialog(desktop);
	    	case XOR:
	    		return new XorDialog(desktop);
	    	default:
	    		throw new ExceptionInInitializerError() {
					private static final long serialVersionUID = -3603518262645278157L;
	    		};
		}
    }

} public enum ParameterlessFilter {
	NEGATIV,
	GREYSCALE,
	BLUR,
	RELIEF,
	SHARPEN,
	EDGE,
	MEDIAN,
	HISTOGRAM_EQ,
	SMART_BIN;
	
	public BufferedImage getFilteredImage(BufferedImage image) {
		switch (this) {
	    	case NEGATIV:
	    		return Filters.negativ(image);  
	    	case GREYSCALE:
	    		return Filters.greyScale1(image);
	    	case BLUR:
	    		return SpatialFilters.blur(image);
	    	case RELIEF:
	    		return SpatialFilters.relief(image);
	    	case SHARPEN:
	    		return SpatialFilters.sharpen(image);
	    	case EDGE:
	    		return SpatialFilters.edgeDetection(image);
	    	case MEDIAN:
	    		return SpatialFilters.median(image);
	    	case HISTOGRAM_EQ:
	    		return Filters.histogramEqualization(image);
	    	case SMART_BIN:
	    		return Filters.smartBinarize(image);
	    	default:
	    		throw new ExceptionInInitializerError() {
					private static final long serialVersionUID = 7199345466977708588L;
	    		};
		}
    }
	
	public void createThread(BufferedImage imgIn, MyInternalFrame frameOut ) {
		switch (this) {
	    	case NEGATIV:
	    		new NegativeThread2(imgIn, frameOut);
	    		break;
	    	case GREYSCALE:
	    		new Greyscale1Thread2(imgIn, frameOut);
	    		break;
	    	/* BLUR, RELIEF, SHARPEN, EDGE and MEDIAN dont have threads, because zoomed image
	    	 * has to be created from originalImage and not directly from zoomed one.*/
	    	case HISTOGRAM_EQ:
				double[] histogram = Utility.getHistogram(imgIn);
				int[] mappingArray = Filters.calculateMappingArrayForEqualization(histogram);
				new HistogramEqualizationThread2(imgIn, frameOut, mappingArray);
	    		break;
	    	case SMART_BIN:
            	BufferedImage img = SpatialFilters.blur(imgIn);
            	double[] his = Utility.getHistogram(img);
            	int sedlo = Filters.findSaddle(his);
            	new ThresholdingThread2(imgIn, sedlo, frameOut);
            	break;
	    	default:
	    		throw new ExceptionInInitializerError() {
					private static final long serialVersionUID = 821403174133489165L;
	    		};
		}
	}
	
} public enum SingleParameterFilter {
	CONTRAST,
	GAMMA,
	SATURATION,
	BRIGHTNESS, 
	THRESHOLDING,
	BIT_PLANE;

	public FilterDialogThatReturnsDouble getDialog(MyInternalFrame frame) {
		switch (this) {
	    	case CONTRAST:
	    		return new ContrastDialog(frame);
	    	case GAMMA:
	    		return new GammaDialog(frame);
	    	case SATURATION:
	    		return new SaturationDialog(frame);
	    	case BRIGHTNESS:
	    		return new Brightness1Dialog(frame);
	    	case THRESHOLDING:
	    		return new ThresholdingDialog(frame);
	    	case BIT_PLANE:
	    		return new BitPlaneDialog(frame);
	    	default:
	    		throw new ExceptionInInitializerError() {
					private static final long serialVersionUID = -2115640049630620261L;
	    		};
		}
    }
	
	public void createThread(BufferedImage imgIn, double value, MyInternalFrame frameOut ) {
		switch (this) {
	    	case CONTRAST:
	    		new ContrastThread2(imgIn, value, frameOut);
	    		break;
	    	case GAMMA:
	    		new GammaThread2(imgIn, value, frameOut);
	    		break;
	    	case SATURATION:
	    		new SaturationThread2(imgIn, value, frameOut);
	    		break;
	    	case BRIGHTNESS:
	    		new Brightness1Thread2(imgIn, value, frameOut);
	    		break;
	    	case THRESHOLDING:
	    		new ThresholdingThread2(imgIn, value, frameOut);
	    		break;
	    	case BIT_PLANE:
	    		new BitPlaneThread2(imgIn, value, frameOut);
	    		break;
	    	default:
	    		throw new ExceptionInInitializerError() {
					private static final long serialVersionUID = -2877266507139641775L;
	    		};
		}
	}
	
} public enum ZoomOperation {
	ZOOM_OUT,
	ZOOM_IN,
	ZOOM_6,
	ZOOM_12,
	ZOOM_25,
	ZOOM_50,
	ZOOM_66,
	ZOOM_ACTUAL;
	
	public int newZoom(MyInternalFrame frame) {
		switch (this) {
	    	case ZOOM_OUT:
	    		int newZoomOut = frame.getZoom() + 10;
	    		if (newZoomOut >= 100)
	    			return 100;
	    		else 
	    			return newZoomOut;
	    	case ZOOM_IN:
	    		int newZoomIn = frame.getZoom() - 10;
	    		if (newZoomIn < 1)
	    			return 1;
	    		else 
	    			return newZoomIn;
	    	case ZOOM_6:
	    		return 6;
	    	case ZOOM_12:
	    		return 12;
	    	case ZOOM_25:
	    		return 25;
	    	case ZOOM_50:
	    		return 50;
	    	case ZOOM_66:
	    		return 66;
	    	case ZOOM_ACTUAL:
	    		return 100;
	    	default:
	    		throw new ExceptionInInitializerError() {
					private static final long serialVersionUID = 4033579193896323312L;
	    		};
		}
	}

	public void apply(MyInternalFrame frame) {
		switch (this) {
	    	case ZOOM_OUT:
	    		frame.zoomOut(10);
	    		break;
	    	case ZOOM_IN:
	    		frame.zoomIn(10);
	    		break;
	    	case ZOOM_6:
	    		frame.setZoom(6);
	    		break;
	    	case ZOOM_12:
	    		frame.setZoom(12);
	    		break;
	    	case ZOOM_25:
	    		frame.setZoom(25);
	    		break;
	    	case ZOOM_50:
	    		frame.setZoom(50);
	    		break;
	    	case ZOOM_66:
	    		frame.setZoom(66);
	    		break;
	    	case ZOOM_ACTUAL:
	    		frame.actualSize();
	    		break;
	    	default:
	    		throw new ExceptionInInitializerError() {
					private static final long serialVersionUID = 4033579193896323312L;
	    		};
		}
    }
} public class ViewMenuUtil {
	private static final String MENU_NAME = "Window";

	public static void createItem(JFrame mainFrame, JInternalFrame internalFrame) {
		JMenu menu = getViewMenu(mainFrame);
		if (menu == null) return;
		ViewMenuItem menuItem = new ViewMenuItem(internalFrame.getTitle(), internalFrame);
		menuItem.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				ViewMenuItem menuItem = (ViewMenuItem) e.getSource();
				try {
					menuItem.internalFrame.setSelected(true);
				} catch (PropertyVetoException e1) {
					e1.printStackTrace();
				}
			}
		});
		menu.add(menuItem);
		refreshItems(mainFrame);
	}

	public static void removeItem(JFrame mainFrame, JInternalFrame internalFrame) {
		JMenu menu = getViewMenu(mainFrame);
		if (menu == null) return;
		for (MenuElement menuItem : menu.getSubElements()) {
			JPopupMenu popUp = (JPopupMenu) menuItem;
			for (MenuElement subElement : popUp.getSubElements()) {
				if (!(subElement instanceof ViewMenuItem) /*MyMenuItem*/)
					continue;
				ViewMenuItem viewMenuItem = (ViewMenuItem) subElement;
				if (viewMenuItem.compareTo(internalFrame) == 0) {
					popUp.remove(viewMenuItem);
					refreshItems(mainFrame);
					return;
				}
			}
		}
	}
	
	public static void refreshItems(JFrame mainFrame) {
		boolean first = true;
		boolean next = false;
		ViewMenuItem viewMenuItem = null;
		ViewMenuItem lastMenuItem = null;
		JMenu menu = getViewMenu(mainFrame);
		if (menu == null) return;
		for (MenuElement menuItem : menu.getSubElements()) {
			JPopupMenu popUp = (JPopupMenu) menuItem;
			for (MenuElement subElement : popUp.getSubElements()) {
				if (subElement instanceof MyMenuItem)
					continue;
				lastMenuItem = viewMenuItem;
				viewMenuItem = (ViewMenuItem) subElement;
				viewMenuItem.setAccelerator(null);
				
				if (next) {
					viewMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, 0));
					next = false;
				}
				
				JDesktopPane desktop = ((FunPhotoTimeFrame) mainFrame).desktop; 
				if (viewMenuItem.compareTo(desktop.getSelectedFrame()) == 0) {
					//viewMenuItem.setText("> "+viewMenuItem.getText());
					if (!first)
						lastMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_TAB, ActionEvent.SHIFT_MASK));
					next = true;
				}
				
				if (first) {
					viewMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_HOME, 0));
					first = false;
				} 
			}
		}
		if (!first) {
			viewMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_END, 0));
		}
	}
	
	/*
	 * COMMON
	 */
	
	private static JMenu getViewMenu(JFrame mainFrame) {
		JMenuBar menuBar = mainFrame.getJMenuBar();
		MenuElement[] menuElements = menuBar.getSubElements();
		for (MenuElement menuElement : menuElements) {
			if (menuElement instanceof JMenu) {
				JMenu menu = (JMenu) menuElement;
				String name = menu.getText();
				if (name.equalsIgnoreCase(MENU_NAME)) {
					return menu;				
				}
			}
		}
		return null;
	}
	
}
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.net.URISyntaxException;
import javax.swing.JOptionPane;
import si.gto76.funphototime.actionlisteners.SingleParameterFilterListener;
import si.gto76.funphototime.actionlisteners.MultipleParameterFilterListener;
import si.gto76.funphototime.actionlisteners.ParameterlessFilterListener;
import si.gto76.funphototime.actionlisteners.LoadListener;
import si.gto76.funphototime.actionlisteners.OperationDialogListener;
import si.gto76.funphototime.actionlisteners.SaveListener;
import si.gto76.funphototime.actionlisteners.SpatialFilterWithouthDialogListener;
import si.gto76.funphototime.actionlisteners.TileListener;
import si.gto76.funphototime.actionlisteners.ZoomListener;
import si.gto76.funphototime.dialogs.AboutDialog;
import si.gto76.funphototime.enums.MultipleParameterFilter;
import si.gto76.funphototime.enums.Operation;
import si.gto76.funphototime.enums.ParameterlessFilter;
import si.gto76.funphototime.enums.SingleParameterFilter;
import si.gto76.funphototime.enums.ZoomOperation;
import si.gto76.funphototime.mymenu.ViewMenuUtil;
package si.gto76.funphototime;



public class ActionListeners {
	public static void set(final FunPhotoTimeFrame frame, Menu meni) {
		
        /*
         * FILE
         */
        //FILE OPEN
        meni.menuFileOpen.addActionListener (
    		new LoadListener(frame)
        ); 
        //FILE SAVE
        meni.menuFileSaveas.addActionListener (
        	new SaveListener(frame)
        );
        //FILE EXIT
        meni.menuFileExit.addActionListener
        (
            new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    FunPhotoTime.onWindowClose(); 
                }
            }
        ); 
        
        /*
         * EDIT
         */
        // UNDO CLOSE WINDOW
        meni.menuEditUndoCloseWindow.addActionListener(
		new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				if (frame.lastClosedIntrnalFrame != null) {
					frame.internalFrameInit(frame.lastClosedIntrnalFrame);
					frame.lastClosedIntrnalFrame = null;
				}
			}
		});
        
        /*
         * ZOOM
         */
        //ZOOM OUT
        meni.menuZoomOut.addActionListener (
       		new ZoomListener(ZoomOperation.ZOOM_OUT, frame)
        );
        //ZOOM IN
        meni.menuZoomIn.addActionListener (
           	new ZoomListener(ZoomOperation.ZOOM_IN, frame)
        );
        //ZOOM 6%
        meni.menuZoomMagnificationSix.addActionListener (
           	new ZoomListener(ZoomOperation.ZOOM_6, frame)
        );
        //ZOOM 12%
        meni.menuZoomMagnificationTwelve.addActionListener (
        	new ZoomListener(ZoomOperation.ZOOM_12, frame)
        );
        //ZOOM 25%
        meni.menuZoomMagnificationTwentyfive.addActionListener (
           	new ZoomListener(ZoomOperation.ZOOM_25, frame)
        );
        //ZOOM 50%
        meni.menuZoomMagnificationFifty.addActionListener (
           	new ZoomListener(ZoomOperation.ZOOM_50, frame)
        );
        //ZOOM 66%
        meni.menuZoomMagnificationSixtysix.addActionListener (
           	new ZoomListener(ZoomOperation.ZOOM_66, frame)
        );
        //ZOOM 100%
        meni.menuZoomMagnificationHoundred.addActionListener (
       		new ZoomListener(ZoomOperation.ZOOM_ACTUAL, frame)
        );
        //ZOOM ACTUAL SIZE
        meni.menuZoomActualsize.addActionListener (
       		new ZoomListener(ZoomOperation.ZOOM_ACTUAL, frame)
        );

        
        /*
         * FILTERS WITHOUTH PARAMETERS
         */
        //FILTER NEGATIV
        meni.menuFiltersNegativ.addActionListener (
        	new ParameterlessFilterListener(ParameterlessFilter.NEGATIV, frame)