package com.devdaily.justwrite.controller;
import java.awt.Color;
import java.awt.Component;
import java.awt.FileDialog;
import java.awt.Font;
import java.awt.FontMetrics;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import javax.swing.JColorChooser;
import javax.swing.JComponent;
import javax.swing.JOptionPane;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.text.StyledDocument;
import com.devdaily.justwrite.JustWrite;
import com.devdaily.justwrite.view.FileUtils;
import com.devdaily.justwrite.view.JustWriteMainFrame;
/**
* Copyright 2010, Alvin Alexander, http://devdaily.com.
* This software is distributed under the terms of the
* GNU General Public License.
*
* This file is part of an application named JustWrite.
*
* JustWrite is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* JustWrite is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with JustWrite. If not, see <http://www.gnu.org/licenses/>.
*
*/
public class MainFrameController
{
public static final Color DEFAULT_FONT_COLOR = Color.green;
public static final Color DEFAULT_BACKGROUND_COLOR = Color.black;
public static final Color DEFAULT_SELECTED_TEXT_COLOR = Color.darkGray;
private JustWrite justWriteMain;
private JustWriteMainFrame mainFrame;
private JTextPane editingArea;
private OpenRecentFileController openRecentFileController;
private ColorChooserController colorChooserController;
private String currentFilename;
// preferences
private String lastDirectoryAccessed;
private Color currentBackgroundColor;
private Color currentFontColor;
private int lastTextareaWidth;
private int lastSpaceAboveTextarea;
private int lastSpaceBelowTextarea;
private Font lastFont;
public MainFrameController(JustWrite justWriteMain)
{
this.justWriteMain = justWriteMain;
// TODO get these from the user preferences
}
public void setMainFrame(JustWriteMainFrame mainFrame)
{
// TODO doing things like this is a kludge, but i need that mainFrame reference,
// and it's a bit of a catch-22.
this.mainFrame = mainFrame;
editingArea = mainFrame.getEditorPane();
openRecentFileController = new OpenRecentFileController(this, mainFrame);
colorChooserController = new ColorChooserController(this, mainFrame);
setCurrentForegroundColor(DEFAULT_FONT_COLOR);
setCurrentBackgroundColor(DEFAULT_BACKGROUND_COLOR);
// TODO this should probably be done with some bitmapping or xor function i don't currently
// know about
editingArea.setSelectionColor(DEFAULT_SELECTED_TEXT_COLOR);
}
public void doOpenFileAction()
{
if (!okToAbandon())
{
return;
}
// show open file dialog; user selects file, or cancels
FileDialog fileDialog = new FileDialog(mainFrame, "Select a File to Open", FileDialog.LOAD);
if (lastDirectoryAccessed!=null && !lastDirectoryAccessed.trim().equals(""))
{
fileDialog.setDirectory(lastDirectoryAccessed);
}
fileDialog.setModal(true);
fileDialog.setVisible(true);
if (fileDialog.getFile() == null) return;
// doesn't need a File.separator on Mac
lastDirectoryAccessed = fileDialog.getDirectory();
currentFilename = fileDialog.getDirectory() + fileDialog.getFile();
// if user did not cancel, get the file
loadFileContentsIntoEditorAndUpdateEverything(currentFilename);
}
private void loadFileContentsIntoEditorAndUpdateEverything(final String filename)
{
// TODO make sure i'm not about to try to read binary data
// TODO do this with a SwingWorker or SwingUtilities ???
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
try
{
String fileContents;
try
{
// 1 - reade the file contents
fileContents = FileUtils.readFileAsString(filename);
// 2 - set the content in the mainframe editor
mainFrame.setEditorText(fileContents);
// 3 - update everything
currentFilename = filename;
openRecentFileController.addFilenameToRecentFilelist(currentFilename);
mainFrame.setDirty(false);
mainFrame.setFocusInEditingArea();
}
catch (IOException e)
{
// TODO improve this; add a title, show message in scrolling text area
JOptionPane.showMessageDialog(mainFrame, e.getMessage());
}
}
catch (Exception exception)
{
// do nothing
}
}
});
}
/**
* Called when the user indicates they want to save the current file.
* We may already have a filename, and we may not.
*/
public void doSaveFileAction()
{
// no filename? do save-as action
if (currentFilename == null)
{
doSaveFileAsAction();
mainFrame.setFocusInEditingArea();
return;
}
// just write the contents to the current filename
try
{
FileUtils.writeFile(currentFilename, mainFrame.getEditorText());
mainFrame.setDirty(false);
File f = new File(currentFilename);
// TODO set the lastDirectoryAccessed here
lastDirectoryAccessed = f.getCanonicalPath();
mainFrame.setFocusInEditingArea();
}
catch (IOException e)
{
// TODO improve this; add a title, put text in a scrolling textarea
// "had a problem trying to save your file"
JOptionPane.showMessageDialog(mainFrame, e.getMessage());
}
}
/**
* The user wants to do a Save-As action, or we need to do a Save-As
* action because they want to save their content to a file, and we
* don't have a filename.
*/
public void doSaveFileAsAction()
{
saveAsFile();
mainFrame.setFocusInEditingArea();
}
/**
* Save the editor content to the current filename.
* If we don't have a filename, the saveAsFile method will be called.
*/
boolean saveEditorContentToCurrentFilename()
{
// handle the case where we don't have a file name yet.
if (currentFilename == null)
{
return saveAsFile();
}
try
{
// save the text in the editing area to the current filename
writeFileAndUpdateEverything(currentFilename, mainFrame.getEditorText());
return true;
}
catch (IOException e)
{
showFileSaveErrorOccurredDialog(currentFilename, e);
}
return false;
}
/**
* Write the given text to the desired filename, and then do whatever needs to be
* done to keep the editor in sync with this change.
*/
private void writeFileAndUpdateEverything(String filename, String text) throws IOException
{
FileUtils.writeFile(currentFilename, mainFrame.getEditorText());
// do the maintenance work
mainFrame.setDirty(false);
lastDirectoryAccessed = new File(filename).getCanonicalPath();
mainFrame.setFocusInEditingArea();
}
/**
* A convenience method, created so we get consistent error messages.
*/
private void showFileSaveErrorOccurredDialog(String filename, Exception e)
{
String message = "An error happened trying to save '" + filename + "'. Message follows.\n" + e.getMessage();
JOptionPane.showMessageDialog(mainFrame, message);
}
/**
* Do the File Save-As thing. Return true if the editor content was saved to a file.
*/
boolean saveAsFile()
{
FileDialog fileDialog = new FileDialog(mainFrame);
fileDialog.setMode(FileDialog.SAVE);
fileDialog.setTitle("Save Current Editor to File");
if (lastDirectoryAccessed!=null && !lastDirectoryAccessed.trim().equals(""))
{
fileDialog.setDirectory(lastDirectoryAccessed);
}
fileDialog.setVisible(true);
// after the dialog is used ...
if (fileDialog.getDirectory() != null && fileDialog.getFile() != null)
{
// we got a filename; go ahead and save the editor content to this file
currentFilename = fileDialog.getDirectory() + fileDialog.getFile();
try
{
// save the text in the editing area to the current filename
writeFileAndUpdateEverything(currentFilename, mainFrame.getEditorText());
openRecentFileController.addFilenameToRecentFilelist(currentFilename);
return true;
}
catch (IOException e)
{
showFileSaveErrorOccurredDialog(currentFilename, e);
mainFrame.setFocusInEditingArea();
return false;
}
}
else
{
return false;
}
}
boolean okToAbandon()
{
// if the current content is not dirty, it's okay to abandon it
if (!mainFrame.isDirty())
{
return true;
}
// otherwise, we need to ask if it's okay to abandon it
int value = JOptionPane.showConfirmDialog(mainFrame,
"Save changes to current editor contents?",
"Save Changes?",
JOptionPane.YES_NO_CANCEL_OPTION) ;
mainFrame.getEditorPane().requestFocusInWindow();
switch (value)
{
case JOptionPane.YES_OPTION:
return saveEditorContentToCurrentFilename();
case JOptionPane.NO_OPTION:
// 'no' means abandon edits; return true without saving
return true;
case JOptionPane.CANCEL_OPTION:
return false;
default:
// cancel
return false;
}
}
public String getCurrentFilename()
{
return currentFilename;
}
/**
* this is the start of a color chooser; really need a color-chooser
* dialog that lets the user choose the different colors i make available.
*/
public void doChooseColorAction()
{
colorChooserController.showColorChooserDialog(currentFontColor, currentBackgroundColor, true);
if (colorChooserController.fontColorWasChanged())
{
setCurrentForegroundColor(colorChooserController.getNewFontColor());
editingArea.setForeground(currentFontColor);
}
if (colorChooserController.backgroundColorWasChanged())
{
setCurrentBackgroundColor(colorChooserController.getNewBackgroundColor());
editingArea.setBackground(currentBackgroundColor);
}
if (colorChooserController.fontColorWasChanged() || colorChooserController.backgroundColorWasChanged())
{
mainFrame.getEditorPane().updateUI();
}
mainFrame.setFocusInEditingArea();
}
private void setCurrentForegroundColor(Color c)
{
this.currentFontColor = c;
// TODO update preferences, or tell someone else to
}
private void setCurrentBackgroundColor(Color c)
{
this.currentBackgroundColor = c;
// TODO update preferences, or tell someone else to
}
/**
* Reduce the size of the font in the editor area.
*/
public void doSmallerFontSizeAction()
{
// get the current font
Font f = editingArea.getFont();
// create a new, smaller font from the current font
Font f2 = new Font(f.getFontName(), f.getStyle(), f.getSize()-1);
// set the new font in the editing area
editingArea.setFont(f2);
}
public void doLargerFontSizeAction()
{
// get the current font
Font f = editingArea.getFont();
// create a new, smaller font from the current font
Font f2 = new Font(f.getFontName(), f.getStyle(), f.getSize()+1);
// set the new font in the editing area
editingArea.setFont(f2);
}
// //////////// handle the "close window" process /////////////
public void doCloseCurrentFileAction()
{
// if the current editing area is not dirty, just set the text to blank,
// and make sure the currentFilename is null or blank
if (!mainFrame.isDirty())
{
clearEditorContentsAndUpdateEverything();
return;
}
// current editor contents are dirty; prompt user to save them
if (okToAbandon())
{
clearEditorContentsAndUpdateEverything();
}
}
void clearEditorContentsAndUpdateEverything()
{
currentFilename = null;
mainFrame.getEditorPane().setText("");
mainFrame.setDirty(false);
mainFrame.setFocusInEditingArea();
}
public void doOpenRecentFileAction()
{
openRecentFileController.doDisplayDialogAction();
String filename = openRecentFileController.getSelectedFilename();
// System.err.println("filename = " + filename);
if (filename == null)
{
mainFrame.setFocusInEditingArea();
return;
}
if (okToAbandon())
{
// System.err.println("About to do 'load' action");
loadFileContentsIntoEditorAndUpdateEverything(filename);
}
}
}