First commit
|
@ -0,0 +1,16 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="src" path="External/Images"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER">
|
||||
<attributes>
|
||||
<attribute name="module" value="true"/>
|
||||
</attributes>
|
||||
</classpathentry>
|
||||
<classpathentry kind="lib" path="External/JARs/jsch-0.1.55.jar"/>
|
||||
<classpathentry kind="lib" path="External/JARs/gson-2.9.1.jar"/>
|
||||
<classpathentry kind="lib" path="External/JARs/jgoodies-forms-1.8.0.jar"/>
|
||||
<classpathentry kind="lib" path="External/JARs/jgoodies-common-1.8.1.jar"/>
|
||||
<classpathentry kind="lib" path="External/JARs/commons-io-2.13.0.jar"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>Guify</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
|
@ -0,0 +1,2 @@
|
|||
eclipse.preferences.version=1
|
||||
encoding/<project>=UTF-8
|
|
@ -0,0 +1,15 @@
|
|||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=17
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=17
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
|
||||
org.eclipse.jdt.core.compiler.release=enabled
|
||||
org.eclipse.jdt.core.compiler.source=17
|
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 4.1 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 4.9 KiB |
After Width: | Height: | Size: 5.4 KiB |
After Width: | Height: | Size: 3.5 KiB |
After Width: | Height: | Size: 8.3 KiB |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 979 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 4.8 KiB |
|
@ -0,0 +1,19 @@
|
|||
/back_icon.png
|
||||
/code/
|
||||
/controllers/
|
||||
/copy_icon.png
|
||||
/cut_icon.png
|
||||
/delete_icon.png
|
||||
/download_icon.png
|
||||
/file_icon.png
|
||||
/folder_icon.png
|
||||
/go_to_icon.png
|
||||
/paste_icon.png
|
||||
/plus_icon.png
|
||||
/question_mark.png
|
||||
/queue_icon.png
|
||||
/rename_icon.png
|
||||
/save_icon.png
|
||||
/search_icon.png
|
||||
/upload_icon.png
|
||||
/views/
|
|
@ -0,0 +1,21 @@
|
|||
package code;
|
||||
|
||||
import java.awt.Color;
|
||||
|
||||
public class Constants {
|
||||
public static final String APP_NAME = "Guify";
|
||||
public static final String VERSION = "1.0";
|
||||
public static final int VERSION_PROGRESSIVE = 0;
|
||||
|
||||
public static class Constants_FSOperations{
|
||||
public static final int NONE = 0;
|
||||
public static final int CUT = 1;
|
||||
public static final int COPY = 2;
|
||||
}
|
||||
|
||||
public static class GuifyColors{
|
||||
public static final Color BLUE = new Color(3, 169, 244);
|
||||
public static final Color GRAY = new Color(240, 240, 240);
|
||||
public static final Color GRAY_HOVER = new Color(220, 220, 220);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
package code.GuiAbstractions.Implementations;
|
||||
|
||||
import code.GuiAbstractions.Interfaces.IFrameFactory;
|
||||
import views.*;
|
||||
|
||||
/**
|
||||
* Frame factory. Factory for JFrame (Java Swing).
|
||||
*/
|
||||
public class JFrameFactory implements IFrameFactory {
|
||||
|
||||
public static Object createJFrame(int frameType) throws Exception {
|
||||
|
||||
switch(frameType) {
|
||||
|
||||
case LOGIN:
|
||||
throw new Exception("Frame Login requires additional parameters. "
|
||||
+ "Call createJFrame(int frameType, Object additionalParameters) instead");
|
||||
|
||||
case DESKTOP:
|
||||
throw new Exception("Frame Desktop requires additional parameters. "
|
||||
+ "Call createJFrame(int frameType, Object additionalParameters) instead");
|
||||
|
||||
case QUEUE:
|
||||
return new Queue();
|
||||
|
||||
case NOTEPAD:
|
||||
throw new Exception("Frame Notepad requires additional parameters. "
|
||||
+ "Call createJFrame(int frameType, Object additionalParameters) instead");
|
||||
|
||||
case FIND_AND_REPLACE:
|
||||
throw new Exception("Frame FindAndReplace requires additional parameters. "
|
||||
+ "Call createJFrame(int frameType, Object additionalParameters) instead");
|
||||
|
||||
default:
|
||||
throw new Exception("Invalid frame name");
|
||||
}
|
||||
}
|
||||
|
||||
public static Object createJFrame(int frameType, Object controller) throws Exception{
|
||||
|
||||
if( frameType != NOTEPAD
|
||||
&& frameType != FIND_AND_REPLACE
|
||||
&& frameType != LOGIN
|
||||
&& frameType != DESKTOP) {
|
||||
System.err.println("additionalParams ignored for this frame");
|
||||
return createJFrame(frameType);
|
||||
}
|
||||
|
||||
switch(frameType) {
|
||||
|
||||
case LOGIN:
|
||||
return new Login(controller);
|
||||
|
||||
case DESKTOP:
|
||||
return new Desktop(controller);
|
||||
|
||||
case NOTEPAD:
|
||||
return new Notepad(controller);
|
||||
|
||||
case FIND_AND_REPLACE:
|
||||
return new FindAndReplace(controller);
|
||||
|
||||
default:
|
||||
throw new Exception("Invalid frame name");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package code.GuiAbstractions.Implementations;
|
||||
|
||||
import javax.swing.JTextArea;
|
||||
import code.GuiAbstractions.Interfaces.IGenericTextArea;
|
||||
|
||||
/**
|
||||
*
|
||||
* A class implementing an interface for a generic text
|
||||
* area. It is currently using JTextArea (Java Swing)
|
||||
*
|
||||
*/
|
||||
public class JGenericTextArea implements IGenericTextArea {
|
||||
|
||||
private JTextArea textArea;
|
||||
|
||||
public JGenericTextArea(JTextArea textArea) {
|
||||
this.textArea = textArea;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void selectText(int start, int end) {
|
||||
if(textArea == null)
|
||||
return;
|
||||
|
||||
textArea.requestFocus(); // enforce focus or it will not be selected
|
||||
textArea.select(start, end);
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
if(textArea == null) {
|
||||
throw new NullPointerException("TextArea is null");
|
||||
}
|
||||
|
||||
return textArea.getText();
|
||||
}
|
||||
|
||||
public void setText(String text) {
|
||||
if(textArea == null) {
|
||||
throw new NullPointerException("TextArea is null");
|
||||
}
|
||||
|
||||
textArea.setText(text);
|
||||
}
|
||||
|
||||
public void replaceRange(String s, int start, int end) {
|
||||
if(textArea == null) {
|
||||
throw new NullPointerException("TextArea is null");
|
||||
}
|
||||
|
||||
textArea.replaceRange(s, start, end);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCaretPosition(int position) {
|
||||
if(textArea == null) {
|
||||
throw new NullPointerException("TextArea is null");
|
||||
}
|
||||
|
||||
textArea.setCaretPosition(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getCaretPosition() {
|
||||
if(textArea == null) {
|
||||
throw new NullPointerException("TextArea is null");
|
||||
}
|
||||
|
||||
return textArea.getCaretPosition();
|
||||
}
|
||||
|
||||
public int getSelectionStart(){
|
||||
if(textArea == null) {
|
||||
throw new NullPointerException("TextArea is null");
|
||||
}
|
||||
return textArea.getSelectionStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasHighlightedText() {
|
||||
if(textArea == null) {
|
||||
throw new NullPointerException("TextArea is null");
|
||||
}
|
||||
return textArea.getSelectionStart() != textArea.getCaretPosition();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package code.GuiAbstractions.Interfaces;
|
||||
|
||||
/**
|
||||
* All the possible frames
|
||||
*/
|
||||
public interface IFrameFactory {
|
||||
public static final int LOGIN = 0;
|
||||
public static final int DESKTOP = 1;
|
||||
public static final int NOTEPAD = 2;
|
||||
public static final int FIND_AND_REPLACE = 3;
|
||||
public static final int QUEUE = 4;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package code.GuiAbstractions.Interfaces;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Interface for a generic TextArea.
|
||||
* It is used to create an abstraction of a
|
||||
* TextArea, without using view-specific objects
|
||||
* (such as JTextArea).
|
||||
*
|
||||
* This increases modularity, flexibility and
|
||||
* creates a separation of concerns.
|
||||
*
|
||||
* In case of change of the GUI library you
|
||||
* do not need to change neither the Controllers nor
|
||||
* these interfaces, but only the implementations.
|
||||
*
|
||||
*/
|
||||
public interface IGenericTextArea {
|
||||
void selectText(int start, int end);
|
||||
String getText();
|
||||
void setText(String text);
|
||||
void replaceRange(String s, int start, int end);
|
||||
void setCaretPosition(int position);
|
||||
int getCaretPosition();
|
||||
int getSelectionStart();
|
||||
boolean hasHighlightedText();
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package code;
|
||||
|
||||
import com.jcraft.jsch.SftpProgressMonitor;
|
||||
|
||||
// Documentation: https://epaul.github.io/jsch-documentation/javadoc/com/jcraft/jsch/SftpProgressMonitor.html
|
||||
public class GuifySftpProgressMonitor implements SftpProgressMonitor {
|
||||
|
||||
TransferProgress transferProgress = null;
|
||||
|
||||
@Override
|
||||
public boolean count(long bytes) {
|
||||
|
||||
if(transferProgress != null) {
|
||||
transferProgress.setTransferredBytes(transferProgress.getTransferredBytes() + bytes);
|
||||
transferProgress.setTransferStatus(TransferProgress.UPDATING);
|
||||
QueueEventManager.getInstance().notify(transferProgress);
|
||||
}
|
||||
|
||||
// true if the transfer should go on
|
||||
// false if the transfer should be cancelled
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void end() {
|
||||
if(transferProgress != null) {
|
||||
transferProgress.setTransferStatus(TransferProgress.END);
|
||||
QueueEventManager.getInstance().notify(transferProgress);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(int op, String src, String dest, long maxBytes) {
|
||||
transferProgress = new TransferProgress();
|
||||
transferProgress.setOperation(op);
|
||||
transferProgress.setSource(src);
|
||||
transferProgress.setDestination(dest);
|
||||
transferProgress.setTotalBytes(maxBytes);
|
||||
transferProgress.setTransferredBytes(0);
|
||||
transferProgress.setTransferStatus(TransferProgress.INIT);
|
||||
QueueEventManager.getInstance().notify(transferProgress);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package code;
|
||||
|
||||
import java.nio.file.Path;
|
||||
|
||||
public class Helper {
|
||||
|
||||
/**
|
||||
* Combine directories with POSIX-style forward slash
|
||||
*/
|
||||
public static String combinePath(String s1, String s2) {
|
||||
StringBuilder result = new StringBuilder(s1);
|
||||
if(!s1.endsWith("/")) {
|
||||
result.append('/');
|
||||
}
|
||||
result.append(s2);
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String getParentPath(String path) {
|
||||
if(path.equals("/")) {
|
||||
return "/";
|
||||
}
|
||||
else if(path.equals("~")) {
|
||||
return Path.of(SshEngine.executeCommand("pwd")).getParent().toString().replace('\\', '/');
|
||||
}
|
||||
else {
|
||||
return Path.of(path).getParent().toString().replace('\\', '/');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package code;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
|
||||
/**
|
||||
*
|
||||
* Interface describing a DirectoryNodeButton,
|
||||
* independently of how a concrete DirectoryNodeButton
|
||||
* will be (currently it is concretely a JButton)
|
||||
*
|
||||
*/
|
||||
public interface IDirectoryNodeButton {
|
||||
public void setNode(LsEntry node);
|
||||
public LsEntry getNode();
|
||||
public void setSelected(boolean selected);
|
||||
public boolean getSelected();
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package code;
|
||||
|
||||
import javax.swing.JButton;
|
||||
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
|
||||
/**
|
||||
*
|
||||
* A JButton representing a Directory Node.
|
||||
* Useful when a Directory Node is needed
|
||||
* to be drew on screen.
|
||||
*
|
||||
*/
|
||||
public class JDirectoryNodeButton extends JButton implements IDirectoryNodeButton {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
public ChannelSftp.LsEntry node = null;
|
||||
private boolean isSelected = false;
|
||||
|
||||
public JDirectoryNodeButton() {
|
||||
super();
|
||||
}
|
||||
|
||||
public JDirectoryNodeButton(ChannelSftp.LsEntry node) {
|
||||
super();
|
||||
setNode(node);
|
||||
}
|
||||
|
||||
public void setNode(ChannelSftp.LsEntry node) {
|
||||
this.node = node;
|
||||
}
|
||||
|
||||
public void setSelected(boolean selected) {
|
||||
this.isSelected = selected;
|
||||
}
|
||||
|
||||
public boolean getSelected() {
|
||||
return this.isSelected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LsEntry getNode() {
|
||||
return this.node;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package code;
|
||||
|
||||
import java.awt.EventQueue;
|
||||
|
||||
import controllers.LoginController;
|
||||
public class Main {
|
||||
|
||||
/**
|
||||
* Guify's entry point
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
EventQueue.invokeLater(new Runnable() {
|
||||
public void run() {
|
||||
try {
|
||||
new LoginController().showFrame(true);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
package code;
|
||||
|
||||
import java.util.Observable;
|
||||
import java.util.concurrent.*;
|
||||
@SuppressWarnings("deprecation") // Observer is okay here
|
||||
public class QueueEventManager extends Observable {
|
||||
|
||||
private static QueueEventManager instance;
|
||||
|
||||
// Cannot instantiate from outside
|
||||
private QueueEventManager() {}
|
||||
|
||||
// We need this object in order to retrieve old transfers which are not being transferred
|
||||
// TODO Prove mathematical correctness
|
||||
// See documentation
|
||||
ConcurrentLinkedQueue<TransferProgress> queue = new ConcurrentLinkedQueue<TransferProgress>();
|
||||
|
||||
public static synchronized QueueEventManager getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new QueueEventManager();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void notify(TransferProgress arg) {
|
||||
updateQueue(arg);
|
||||
setChanged();
|
||||
notifyObservers(arg);
|
||||
}
|
||||
|
||||
private void updateQueue(TransferProgress transferProgressObj) {
|
||||
if(transferProgressObj.getTransferStatus() == TransferProgress.INIT) {
|
||||
queue.add(transferProgressObj);
|
||||
}
|
||||
else if(transferProgressObj.getTransferStatus() == TransferProgress.END) {
|
||||
queue.remove();
|
||||
}
|
||||
}
|
||||
|
||||
public TransferProgress[] getQueue() {
|
||||
return queue.toArray(new TransferProgress[queue.size()]);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,533 @@
|
|||
package code;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.Vector;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import com.jcraft.jsch.*;
|
||||
import com.jcraft.jsch.ChannelSftp.LsEntry;
|
||||
import controllers.LoginController;
|
||||
import java.nio.file.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* Underlying SSH engine for
|
||||
* the application
|
||||
*
|
||||
*/
|
||||
public class SshEngine {
|
||||
|
||||
private static Session session = null;
|
||||
|
||||
/*
|
||||
* ========== BEGIN SSH Utilities ==========
|
||||
*/
|
||||
|
||||
public static boolean connetion() {
|
||||
return createSession();
|
||||
}
|
||||
|
||||
private static boolean createSession() {
|
||||
Properties config = new Properties();
|
||||
config.put("StrictHostKeyChecking", "no");
|
||||
JSch jsch = new JSch();
|
||||
|
||||
try {
|
||||
session = jsch.getSession(LoginController.LoginCredentials.username, LoginController.LoginCredentials.host,
|
||||
LoginController.LoginCredentials.port);
|
||||
session.setPassword(LoginController.LoginCredentials.password);
|
||||
session.setConfig(config);
|
||||
session.setTimeout(5000); // 5 seconds
|
||||
session.connect();
|
||||
System.out.println("Connected to remote server");
|
||||
} catch (JSchException e) {
|
||||
session = null;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void checkValidityOrCreateSession() throws JSchException {
|
||||
if(session == null || !session.isConnected()) {
|
||||
if(!createSession()) {
|
||||
throw new JSchException("Failed to create a session in Jsch");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void disconnectSession() {
|
||||
if(session != null) {
|
||||
session.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ========== END SSH Utilities ==========
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* ========== BEGIN SFTP get() and put() methods ==========
|
||||
*/
|
||||
|
||||
/**
|
||||
* Downloads a file from remote host to local machine.
|
||||
* Executed asynchronously.
|
||||
*/
|
||||
public static void downloadFile(String source, String dest) {
|
||||
// We execute the lengthy and time-consuming operation on a different
|
||||
// thread instead of the Event Dispatch Thread.
|
||||
// We use SwingWorker so any GUI changes requested by this thread will
|
||||
// be correctly addressed to the Event Dispatch Thread
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
ChannelSftp channelSftp = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
channelSftp.get(source, dest, new GuifySftpProgressMonitor());
|
||||
System.out.println("File " + source + " downloaded in " + dest);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads a directory recursively.
|
||||
* Executed asynchronously.
|
||||
*/
|
||||
public static void downloadDirectoryRecursively(String source, String dest) {
|
||||
// We execute the lengthy and time-consuming operation on a different
|
||||
// thread instead of the Event Dispatch Thread.
|
||||
// We use SwingWorker so any GUI changes requested by this thread will
|
||||
// be correctly addressed to the Event Dispatch Thread
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
ChannelSftp channelSftp = null;
|
||||
try {
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
downloadDirectoryRecursively_aux(channelSftp, source, dest);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Private utility
|
||||
* @param channel_aux An auxiliary SFTP channel
|
||||
* @param remoteDirectory
|
||||
* @param localDirectory
|
||||
* @throws SftpException
|
||||
*/
|
||||
private static void downloadDirectoryRecursively_aux(ChannelSftp channel_aux, String remoteDirectory, String localDirectory) throws SftpException {
|
||||
channel_aux.cd(remoteDirectory);
|
||||
String newLocalDir = Helper.combinePath(localDirectory, Paths.get(remoteDirectory).getFileName().toString());
|
||||
new java.io.File(newLocalDir).mkdirs();
|
||||
@SuppressWarnings("unchecked")
|
||||
Vector<LsEntry> entries = channel_aux.ls("*");
|
||||
|
||||
for (ChannelSftp.LsEntry entry : entries) {
|
||||
if (!entry.getAttrs().isDir()) {
|
||||
// File - download it
|
||||
// Creates a thread for each file. If there are a lot of files
|
||||
// it may be resource-draining. Consider using a ThreadPool
|
||||
downloadFile(Helper.combinePath(remoteDirectory, entry.getFilename()), Helper.combinePath(newLocalDir, entry.getFilename()));
|
||||
} else if (!".".equals(entry.getFilename()) && !"..".equals(entry.getFilename())) {
|
||||
// Directory - download recursively
|
||||
String newRemoteDir = Helper.combinePath(remoteDirectory, entry.getFilename());
|
||||
downloadDirectoryRecursively_aux(channel_aux, newRemoteDir, newLocalDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads a file from the local machine to the remote host.
|
||||
* Executed asynchronously.
|
||||
*/
|
||||
public static void uploadFile(File fileToUpload, String remoteDirectory) throws SftpException {
|
||||
// We execute the lengthy and time-consuming operation on a different
|
||||
// thread instead of the Event Dispatch Thread.
|
||||
// We use SwingWorker so any GUI changes requested by this thread will
|
||||
// be correctly addressed to the Event Dispatch Thread
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
ChannelSftp channelSftp = null;
|
||||
String remotePath = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
remotePath = Helper.combinePath(remoteDirectory, fileToUpload.getName());
|
||||
channelSftp.put(fileToUpload.getAbsolutePath(), remotePath, new GuifySftpProgressMonitor());
|
||||
System.out.println("File: " + fileToUpload.getAbsolutePath() + " uploaded to remote path: " + remotePath);
|
||||
}
|
||||
catch(SftpException sftpex) {
|
||||
// TODO maybe no permissions
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads directory recursively.
|
||||
* Executed asynchronously.
|
||||
* @param directory Full path of the local directory to upload
|
||||
* @param remoteDirectory Full path of the remote directory which the local
|
||||
* directory will be uploaded in
|
||||
*/
|
||||
public static void uploadDirectoriesRecursively(File directory, String remoteDirectory) throws SftpException {
|
||||
// We execute the lengthy and time-consuming operation on a different
|
||||
// thread instead of the Event Dispatch Thread.
|
||||
// We use SwingWorker so any GUI changes requested by this thread will
|
||||
// be correctly addressed to the Event Dispatch Thread
|
||||
new Thread(new Runnable() {
|
||||
public void run() {
|
||||
ChannelSftp channelSftp = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
uploadDirectoriesRecursively_aux(channelSftp, directory, remoteDirectory);
|
||||
}
|
||||
catch(SftpException sftpex) {
|
||||
//TODO maybe no permissions
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Private utility
|
||||
* @param channel_aux
|
||||
* @param localPath
|
||||
* @param remoteDirectory
|
||||
* @throws SftpException
|
||||
*/
|
||||
private static void uploadDirectoriesRecursively_aux(ChannelSftp channel_aux, File localPath, String remoteDirectory) throws SftpException {
|
||||
if(localPath != null) {
|
||||
String subDirectoryPath = Helper.combinePath(remoteDirectory, localPath.getName());
|
||||
channel_aux.mkdir(subDirectoryPath);
|
||||
|
||||
File[] files = localPath.listFiles();
|
||||
if (files != null) {
|
||||
for (File file : files) {
|
||||
if (file.isFile()) {
|
||||
// Creates a thread for each file. If there are a lot of files
|
||||
// it may be resource-draining. Consider using a ThreadPool
|
||||
channel_aux.put(file.getAbsolutePath(), Helper.combinePath(subDirectoryPath, file.getName()), new GuifySftpProgressMonitor());
|
||||
System.out.println("File: " + file.getAbsolutePath() + " uploaded to remote path: " + Helper.combinePath(subDirectoryPath, file.getName()));
|
||||
} else if (file.isDirectory()) {
|
||||
uploadDirectoriesRecursively_aux(channel_aux, file, subDirectoryPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ========== END SFTP get() and put() methods ==========
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* ========== BEGIN File System operations ==========
|
||||
*/
|
||||
|
||||
public static void mkdir(String path) throws SftpException {
|
||||
ChannelSftp channelSftp = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
channelSftp.mkdir(path);
|
||||
}
|
||||
catch(SftpException sftpex) {
|
||||
throw sftpex;
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static String readFile(String filePath) {
|
||||
ChannelSftp channel = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channel = (ChannelSftp) session.openChannel("sftp");
|
||||
channel.connect();
|
||||
InputStream in = channel.get(filePath);
|
||||
return IOUtils.toString(in, StandardCharsets.UTF_8);
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
} finally {
|
||||
if (channel != null)
|
||||
channel.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeFile(String content, String pathToFile) {
|
||||
ChannelSftp channelSftp = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
channelSftp.put(new ByteArrayInputStream(content.getBytes()), pathToFile);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static void rename(String oldPath, String newPath) throws SftpException {
|
||||
ChannelSftp channelSftp = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
channelSftp.rename(oldPath, newPath);
|
||||
}
|
||||
catch(SftpException sftpex) {
|
||||
throw sftpex;
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an empty file in the specified remote file path
|
||||
* @throws SftpException
|
||||
*/
|
||||
public static void touch(String remoteFilePath) throws SftpException {
|
||||
ChannelSftp channelSftp = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
channelSftp.put(new ByteArrayInputStream(new byte[0]), remoteFilePath);
|
||||
}
|
||||
catch(SftpException sftpex) {
|
||||
throw sftpex;
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static void rm(String remoteFilePath) throws SftpException {
|
||||
ChannelSftp channelSftp = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
channelSftp.rm(remoteFilePath);
|
||||
}
|
||||
catch(SftpException sftpex) {
|
||||
throw sftpex;
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static void rm(List<String> remoteFilePaths) throws SftpException {
|
||||
ChannelSftp channelSftp = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
for(String remoteFilePath : remoteFilePaths) {
|
||||
rm(remoteFilePath, channelSftp);
|
||||
}
|
||||
}
|
||||
catch(SftpException sftpex) {
|
||||
throw sftpex;
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private static void rm(String remoteFilePath, ChannelSftp channelSftp) throws SftpException, JSchException {
|
||||
channelSftp.rm(remoteFilePath);
|
||||
}
|
||||
|
||||
public static void rmdir(String remoteFilePath) throws SftpException {
|
||||
ChannelSftp channelSftp = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
channelSftp.rmdir(remoteFilePath);
|
||||
}
|
||||
catch(SftpException sftpex) {
|
||||
throw sftpex;
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public static void rmdir(List<String> remotePaths) throws SftpException {
|
||||
ChannelSftp channelSftp = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
for(String remotePath : remotePaths) {
|
||||
rmdir(remotePath, channelSftp);
|
||||
}
|
||||
}
|
||||
catch(SftpException sftpex) {
|
||||
throw sftpex;
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
private static void rmdir(String remotePath, ChannelSftp channelSftp) throws SftpException, JSchException {
|
||||
channelSftp.rmdir(remotePath);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static Vector<ChannelSftp.LsEntry> ls(String path) throws SftpException{
|
||||
ChannelSftp channelSftp = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channelSftp = (ChannelSftp) session.openChannel("sftp");
|
||||
channelSftp.connect();
|
||||
Vector<ChannelSftp.LsEntry> entries = channelSftp.ls(path);
|
||||
// remove hidden directories (TODO create a setting for that)
|
||||
entries.removeIf(entry -> entry.getFilename().equals(".") || entry.getFilename().equals("..") || entry.getFilename().startsWith("."));
|
||||
return entries;
|
||||
}
|
||||
catch(SftpException sftpex) {
|
||||
throw sftpex;
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
} finally {
|
||||
if (channelSftp != null)
|
||||
channelSftp.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ========== END File System operations ==========
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* ========== BEGIN Other ==========
|
||||
*/
|
||||
|
||||
public static String executeCommand(String command) {
|
||||
System.out.println("> " + command);
|
||||
Channel channel = null;
|
||||
InputStream in = null;
|
||||
try {
|
||||
checkValidityOrCreateSession();
|
||||
channel = session.openChannel("exec");
|
||||
((ChannelExec) channel).setCommand(command);
|
||||
channel.connect();
|
||||
in = channel.getInputStream();
|
||||
String returnText = new String(in.readAllBytes(), StandardCharsets.UTF_8);
|
||||
in.close();
|
||||
// Remove possible \r\n at the end of the string
|
||||
returnText = returnText.replaceAll("[\r\n]+$", "").trim();
|
||||
System.out.println("< " + returnText);
|
||||
return returnText;
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} catch (JSchException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (channel != null) {
|
||||
channel.disconnect();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* ========== END Other ==========
|
||||
*/
|
||||
|
||||
|
||||
}
|
||||
/*
|
||||
* When executing a command on a remote server using SSH with Jsch in Java, we
|
||||
* typically want to capture the output of the command as it is executed on the
|
||||
* remote server. Therefore, we need to obtain an input stream from the remote
|
||||
* server that will allow us to read the output of the command.
|
||||
*
|
||||
* The channel.getInputStream() method returns an input stream that is connected
|
||||
* to the standard output of the remote command being executed. This means that
|
||||
* any output produced by the command will be sent to the input stream, which we
|
||||
* can then read in our Java program to obtain the output.
|
||||
*
|
||||
* On the other hand, the channel.getOutputStream() method returns an output
|
||||
* stream that is connected to the standard input of the remote command being
|
||||
* executed. This means that any input provided to the output stream will be
|
||||
* sent to the remote command as its standard input. While this may be useful in
|
||||
* some cases, it is not typically what we want when executing a command on a
|
||||
* remote server and capturing its output.
|
||||
*
|
||||
* So to sum up, we use channel.getInputStream() to obtain an input stream that
|
||||
* we can use to read the output of the command being executed on the remote
|
||||
* server.
|
||||
*/
|
|
@ -0,0 +1,59 @@
|
|||
package code;
|
||||
|
||||
/**
|
||||
*
|
||||
* An object representing the transfer progress of a
|
||||
* file between the server and the host machine.
|
||||
*
|
||||
*/
|
||||
public class TransferProgress {
|
||||
|
||||
// Transfer statuses
|
||||
public static final int INIT = 0;
|
||||
public static final int UPDATING = 1;
|
||||
public static final int END = 2;
|
||||
|
||||
private String source;
|
||||
private String destination;
|
||||
private long totalBytes;
|
||||
private long transferredBytes;
|
||||
private int operation;
|
||||
private int transferStatus;
|
||||
|
||||
public String getSource() {
|
||||
return source;
|
||||
}
|
||||
public void setSource(String source) {
|
||||
this.source = source;
|
||||
}
|
||||
public String getDestination() {
|
||||
return destination;
|
||||
}
|
||||
public void setDestination(String destination) {
|
||||
this.destination = destination;
|
||||
}
|
||||
public long getTotalBytes() {
|
||||
return totalBytes;
|
||||
}
|
||||
public void setTotalBytes(long totalBytes) {
|
||||
this.totalBytes = totalBytes;
|
||||
}
|
||||
public long getTransferredBytes() {
|
||||
return transferredBytes;
|
||||
}
|
||||
public void setTransferredBytes(long transferredBytes) {
|
||||
this.transferredBytes = transferredBytes;
|
||||
}
|
||||
public int getOperation() {
|
||||
return operation;
|
||||
}
|
||||
public void setOperation(int operation) {
|
||||
this.operation = operation;
|
||||
}
|
||||
public int getTransferStatus() {
|
||||
return transferStatus;
|
||||
}
|
||||
public void setTransferStatus(int transferStatus) {
|
||||
this.transferStatus = transferStatus;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
package code;
|
||||
|
||||
/**
|
||||
*
|
||||
* A class representing a
|
||||
* "tree -J" result
|
||||
*
|
||||
*/
|
||||
public class TreeNode {
|
||||
public String type;
|
||||
public String name;
|
||||
public TreeNode[] contents;
|
||||
public String error;
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
package code;
|
||||
|
||||
import java.awt.*;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
/**
|
||||
* FlowLayout subclass that fully supports wrapping of components.
|
||||
*/
|
||||
public class WrapLayout extends FlowLayout
|
||||
{
|
||||
/**
|
||||
*
|
||||
*/
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
* Constructs a new <code>WrapLayout</code> with a left
|
||||
* alignment and a default 5-unit horizontal and vertical gap.
|
||||
*/
|
||||
public WrapLayout()
|
||||
{
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a new <code>FlowLayout</code> with the specified
|
||||
* alignment and a default 5-unit horizontal and vertical gap.
|
||||
* The value of the alignment argument must be one of
|
||||
* <code>WrapLayout</code>, <code>WrapLayout</code>,
|
||||
* or <code>WrapLayout</code>.
|
||||
* @param align the alignment value
|
||||
*/
|
||||
public WrapLayout(int align)
|
||||
{
|
||||
super(align);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new flow layout manager with the indicated alignment
|
||||
* and the indicated horizontal and vertical gaps.
|
||||
* <p>
|
||||
* The value of the alignment argument must be one of
|
||||
* <code>WrapLayout</code>, <code>WrapLayout</code>,
|
||||
* or <code>WrapLayout</code>.
|
||||
* @param align the alignment value
|
||||
* @param hgap the horizontal gap between components
|
||||
* @param vgap the vertical gap between components
|
||||
*/
|
||||
public WrapLayout(int align, int hgap, int vgap)
|
||||
{
|
||||
super(align, hgap, vgap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the preferred dimensions for this layout given the
|
||||
* <i>visible</i> components in the specified target container.
|
||||
* @param target the component which needs to be laid out
|
||||
* @return the preferred dimensions to lay out the
|
||||
* subcomponents of the specified container
|
||||
*/
|
||||
@Override
|
||||
public Dimension preferredLayoutSize(Container target)
|
||||
{
|
||||
return layoutSize(target, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum dimensions needed to layout the <i>visible</i>
|
||||
* components contained in the specified target container.
|
||||
* @param target the component which needs to be laid out
|
||||
* @return the minimum dimensions to lay out the
|
||||
* subcomponents of the specified container
|
||||
*/
|
||||
@Override
|
||||
public Dimension minimumLayoutSize(Container target)
|
||||
{
|
||||
Dimension minimum = layoutSize(target, false);
|
||||
minimum.width -= (getHgap() + 1);
|
||||
return minimum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum or preferred dimension needed to layout the target
|
||||
* container.
|
||||
*
|
||||
* @param target target to get layout size for
|
||||
* @param preferred should preferred size be calculated
|
||||
* @return the dimension to layout the target container
|
||||
*/
|
||||
private Dimension layoutSize(Container target, boolean preferred)
|
||||
{
|
||||
synchronized (target.getTreeLock())
|
||||
{
|
||||
// Each row must fit with the width allocated to the containter.
|
||||
// When the container width = 0, the preferred width of the container
|
||||
// has not yet been calculated so lets ask for the maximum.
|
||||
|
||||
int targetWidth = target.getSize().width;
|
||||
Container container = target;
|
||||
|
||||
while (container.getSize().width == 0 && container.getParent() != null)
|
||||
{
|
||||
container = container.getParent();
|
||||
}
|
||||
|
||||
targetWidth = container.getSize().width;
|
||||
|
||||
if (targetWidth == 0)
|
||||
targetWidth = Integer.MAX_VALUE;
|
||||
|
||||
int hgap = getHgap();
|
||||
int vgap = getVgap();
|
||||
Insets insets = target.getInsets();
|
||||
int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2);
|
||||
int maxWidth = targetWidth - horizontalInsetsAndGap;
|
||||
|
||||
// Fit components into the allowed width
|
||||
|
||||
Dimension dim = new Dimension(0, 0);
|
||||
int rowWidth = 0;
|
||||
int rowHeight = 0;
|
||||
|
||||
int nmembers = target.getComponentCount();
|
||||
|
||||
for (int i = 0; i < nmembers; i++)
|
||||
{
|
||||
Component m = target.getComponent(i);
|
||||
|
||||
if (m.isVisible())
|
||||
{
|
||||
Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize();
|
||||
|
||||
// Can't add the component to current row. Start a new row.
|
||||
|
||||
if (rowWidth + d.width > maxWidth)
|
||||
{
|
||||
addRow(dim, rowWidth, rowHeight);
|
||||
rowWidth = 0;
|
||||
rowHeight = 0;
|
||||
}
|
||||
|
||||
// Add a horizontal gap for all components after the first
|
||||
|
||||
if (rowWidth != 0)
|
||||
{
|
||||
rowWidth += hgap;
|
||||
}
|
||||
|
||||
rowWidth += d.width;
|
||||
rowHeight = Math.max(rowHeight, d.height);
|
||||
}
|
||||
}
|
||||
|
||||
addRow(dim, rowWidth, rowHeight);
|
||||
|
||||
dim.width += horizontalInsetsAndGap;
|
||||
dim.height += insets.top + insets.bottom + vgap * 2;
|
||||
|
||||
// When using a scroll pane or the DecoratedLookAndFeel we need to
|
||||
// make sure the preferred size is less than the size of the
|
||||
// target containter so shrinking the container size works
|
||||
// correctly. Removing the horizontal gap is an easy way to do this.
|
||||
|
||||
Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target);
|
||||
|
||||
if (scrollPane != null && target.isValid())
|
||||
{
|
||||
dim.width -= (hgap + 1);
|
||||
}
|
||||
|
||||
return dim;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* A new row has been completed. Use the dimensions of this row
|
||||
* to update the preferred size for the container.
|
||||
*
|
||||
* @param dim update the width and height when appropriate
|
||||
* @param rowWidth the width of the row to add
|
||||
* @param rowHeight the height of the row to add
|
||||
*/
|
||||
private void addRow(Dimension dim, int rowWidth, int rowHeight)
|
||||
{
|
||||
dim.width = Math.max(dim.width, rowWidth);
|
||||
|
||||
if (dim.height > 0)
|
||||
{
|
||||
dim.height += getVgap();
|
||||
}
|
||||
|
||||
dim.height += rowHeight;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,437 @@
|
|||
package controllers;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.jcraft.jsch.ChannelSftp;
|
||||
import com.jcraft.jsch.SftpException;
|
||||
|
||||
import code.Constants;
|
||||
import code.Constants.Constants_FSOperations;
|
||||
import code.GuiAbstractions.Implementations.JFrameFactory;
|
||||
import views.interfaces.IDesktopFrame;
|
||||
import code.TreeNode;
|
||||
import code.Helper;
|
||||
import code.IDirectoryNodeButton;
|
||||
import code.SshEngine;
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
public class DesktopController {
|
||||
|
||||
/*
|
||||
* ========== BEGIN Attributes ==========
|
||||
*/
|
||||
|
||||
private IDesktopFrame frame;
|
||||
private String currentWorkingDirectory = "~";
|
||||
private String lastSafeDirectory = null;
|
||||
private List<IDirectoryNodeButton> selectedNodes = new ArrayList<IDirectoryNodeButton>();
|
||||
public CutCopyPasteController cutCopyPasteController = new CutCopyPasteController();
|
||||
|
||||
/*
|
||||
* ========== END Attributes ==========
|
||||
*/
|
||||
|
||||
/*
|
||||
* ========== BEGIN Constructors ==========
|
||||
*/
|
||||
|
||||
public DesktopController() {
|
||||
try {
|
||||
frame = (IDesktopFrame) JFrameFactory.createJFrame(JFrameFactory.DESKTOP, this);
|
||||
frame.drawComponentsForDirectory("~");
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ========== END Constructors ==========
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* ========== BEGIN Getters and Setters ==========
|
||||
*/
|
||||
|
||||
public String getCurrentWorkingDirectory() {
|
||||
return currentWorkingDirectory;
|
||||
}
|
||||
|
||||
public void setCurrentWorkingDirectory(String directory) {
|
||||
if(directory.equals("~")) {
|
||||
currentWorkingDirectory = SshEngine.executeCommand("pwd");
|
||||
}
|
||||
else {
|
||||
currentWorkingDirectory = directory.trim();
|
||||
}
|
||||
}
|
||||
|
||||
public String getLastSafeDirectory() {
|
||||
return lastSafeDirectory;
|
||||
}
|
||||
|
||||
public void setLastSafeDirectory(String directory) {
|
||||
|
||||
if(directory == null) {
|
||||
lastSafeDirectory = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if(directory.equals("~")) {
|
||||
lastSafeDirectory = SshEngine.executeCommand("pwd");
|
||||
}
|
||||
else {
|
||||
lastSafeDirectory = directory.trim();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ========== END Getters and Setters ==========
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* ========== BEGIN Create desktop helper methods ==========
|
||||
*/
|
||||
|
||||
public TreeNode getTree() {
|
||||
final int maxDepth = 3;
|
||||
StringBuilder command = new StringBuilder("tree -Ji -L ");
|
||||
command.append(maxDepth);
|
||||
command.append(' ');
|
||||
command.append(currentWorkingDirectory);
|
||||
String jsonTree = SshEngine.executeCommand(command.toString());
|
||||
TreeNode[] tree = null;
|
||||
|
||||
try {
|
||||
// Might throw Invalid JSON exception because of incorrect
|
||||
// JSON output returned from tree:
|
||||
// https://gitlab.com/OldManProgrammer/unix-tree/-/issues/11
|
||||
// Fixed in tree 2.1.1 https://gitlab.com/OldManProgrammer/unix-tree/-/commit/84fa3ddff51b30835a0f9c4a9e4c9225970f9aff
|
||||
//
|
||||
// For this reason, we temporarily explicitly avoid it to happen
|
||||
jsonTree = jsonTree.replace("}{\"error\"", "},{\"error\"");
|
||||
tree = new Gson().fromJson(jsonTree, TreeNode[].class);
|
||||
return tree[0];
|
||||
}
|
||||
catch(Exception ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Vector<ChannelSftp.LsEntry> getDirectoryElements() throws SftpException {
|
||||
return SshEngine.ls(currentWorkingDirectory);
|
||||
}
|
||||
|
||||
/*
|
||||
* ========== END Create desktop helper methods ==========
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* ========== BEGIN Download and Upload section ==========
|
||||
*/
|
||||
|
||||
/**
|
||||
* Downloads a file from the remote server to the local machine
|
||||
* @param sourcePath Remote file's full path
|
||||
* @param destinationPath Local file's full path
|
||||
*/
|
||||
public void downloadFile(String sourcePath, String destinationPath) {
|
||||
SshEngine.downloadFile(sourcePath, destinationPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uploads files and folders to the remote server
|
||||
* @param selectedNodes
|
||||
* @throws SftpException
|
||||
*/
|
||||
public void uploadToRemoteServer(File[] selectedNodes) throws SftpException {
|
||||
if (selectedNodes.length > 0) {
|
||||
List<File> selectedFiles = new ArrayList<File>();
|
||||
List<File> selectedDirectories = new ArrayList<File>();
|
||||
for (java.io.File file : selectedNodes) {
|
||||
if(file.isFile()) {
|
||||
selectedFiles.add(file);
|
||||
}
|
||||
else if(file.isDirectory()) {
|
||||
selectedDirectories.add(file);
|
||||
}
|
||||
}
|
||||
|
||||
for(File file : selectedFiles) {
|
||||
SshEngine.uploadFile(file, this.getCurrentWorkingDirectory());
|
||||
}
|
||||
|
||||
|
||||
if(selectedDirectories.size() > 0) {
|
||||
for(File directory : selectedDirectories) {
|
||||
SshEngine.uploadDirectoriesRecursively(directory, this.getCurrentWorkingDirectory());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ========== END Download and Upload section ==========
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* ========== BEGIN Selected Nodes section ==========
|
||||
*/
|
||||
|
||||
public void addSelectedNode(IDirectoryNodeButton node) {
|
||||
selectedNodes.add(node);
|
||||
node.setSelected(true);
|
||||
}
|
||||
|
||||
public void removeSelectedNode(IDirectoryNodeButton node) {
|
||||
selectedNodes.remove(node);
|
||||
node.setSelected(false);
|
||||
}
|
||||
|
||||
public void clearSelectedNodes() {
|
||||
if(selectedNodes != null) {
|
||||
Iterator<IDirectoryNodeButton> iterator = selectedNodes.iterator();
|
||||
while (iterator.hasNext()) {
|
||||
IDirectoryNodeButton node = iterator.next();
|
||||
iterator.remove();
|
||||
node.setSelected(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<IDirectoryNodeButton> getSelectedNodes() {
|
||||
return selectedNodes;
|
||||
}
|
||||
|
||||
public int countSelectedNodes() {
|
||||
return selectedNodes.size();
|
||||
}
|
||||
|
||||
public void deleteSelectedNodes() throws SftpException {
|
||||
|
||||
List<String> filesToDelete = new ArrayList<String>();
|
||||
List<String> directoriesToDelete = new ArrayList<String>();
|
||||
|
||||
for(IDirectoryNodeButton node : selectedNodes) {
|
||||
if(node.getNode().getAttrs().isDir()) {
|
||||
directoriesToDelete.add(Helper.combinePath(getCurrentWorkingDirectory(), node.getNode().getFilename()).replace("\"", "\\\""));
|
||||
}
|
||||
else {
|
||||
filesToDelete.add(Helper.combinePath(getCurrentWorkingDirectory(), node.getNode().getFilename()).replace("\"", "\\\""));
|
||||
}
|
||||
}
|
||||
|
||||
SshEngine.rm(filesToDelete);
|
||||
SshEngine.rmdir(directoriesToDelete);
|
||||
|
||||
clearSelectedNodes();
|
||||
}
|
||||
|
||||
public void downloadSelectedNodes(String destinationPath) {
|
||||
List<String> directories = new ArrayList<String>();
|
||||
List<String> files = new ArrayList<String>();
|
||||
String tmp;
|
||||
for(IDirectoryNodeButton node : selectedNodes) {
|
||||
tmp = Helper.combinePath(getCurrentWorkingDirectory(), node.getNode().getFilename());
|
||||
if(node.getNode().getAttrs().isDir()) {
|
||||
directories.add(tmp);
|
||||
}
|
||||
else {
|
||||
files.add(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
for(String dir : directories) {
|
||||
SshEngine.downloadDirectoryRecursively(dir, destinationPath);
|
||||
}
|
||||
|
||||
for(String file : files) {
|
||||
SshEngine.downloadFile(file, destinationPath);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ========== END Selected Nodes section ==========
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* ========== BEGIN CutCopyPasteController controller ==========
|
||||
*/
|
||||
|
||||
public class CutCopyPasteController{
|
||||
private List<String> sources = new ArrayList<String>();
|
||||
private int selectedOperation = Constants.Constants_FSOperations.NONE;
|
||||
|
||||
public void startCopying(List<IDirectoryNodeButton> selectedNodes, String currentPath) {
|
||||
String fullPath = null;
|
||||
for(IDirectoryNodeButton nodeBtn : selectedNodes) {
|
||||
fullPath = Helper.combinePath(currentPath, nodeBtn.getNode().getFilename());
|
||||
sources.add(fullPath);
|
||||
}
|
||||
selectedOperation = Constants.Constants_FSOperations.COPY;
|
||||
}
|
||||
|
||||
public void startCuttying(List<IDirectoryNodeButton> selectedNodes, String currentPath) {
|
||||
String fullPath = null;
|
||||
for(IDirectoryNodeButton nodeBtn : selectedNodes) {
|
||||
fullPath = Helper.combinePath(currentPath, nodeBtn.getNode().getFilename());
|
||||
sources.add(fullPath);
|
||||
}
|
||||
selectedOperation = Constants.Constants_FSOperations.CUT;
|
||||
}
|
||||
|
||||
public void paste(String destination) {
|
||||
StringBuilder command = null;
|
||||
|
||||
// no source
|
||||
if(sources.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// cannot write on destination
|
||||
// we keep using isWriteable as
|
||||
// the executeCommand() does not fire
|
||||
// an exception in case of fail
|
||||
if(!isWriteable(destination)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// copy
|
||||
if(selectedOperation == Constants.Constants_FSOperations.COPY) {
|
||||
command = new StringBuilder("cp -r");
|
||||
|
||||
}
|
||||
|
||||
// cut
|
||||
else if(selectedOperation == Constants.Constants_FSOperations.CUT) {
|
||||
command = new StringBuilder("mv");
|
||||
}
|
||||
|
||||
// invalid command
|
||||
else {
|
||||
return;
|
||||
}
|
||||
|
||||
// execute
|
||||
for(String path : sources) {
|
||||
command.append(' ');
|
||||
command.append('"');
|
||||
command.append(path.replace("\"", "\\\""));
|
||||
command.append('"');
|
||||
}
|
||||
command.append(' ');
|
||||
command.append('"');
|
||||
command.append(destination.replace("\"", "\\\""));
|
||||
command.append('"');
|
||||
SshEngine.executeCommand(command.toString());
|
||||
selectedOperation = Constants_FSOperations.NONE;
|
||||
}
|
||||
|
||||
public int getSelectedOperation() {
|
||||
return selectedOperation;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ========== END CutCopyPasteController controller ==========
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* ========== BEGIN File System Operations ==========
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new folder
|
||||
* @param newFolderPath Folder's path
|
||||
* @throws SftpException
|
||||
*/
|
||||
public void mkdir(String newFolderPath) throws SftpException {
|
||||
SshEngine.mkdir(newFolderPath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a file in the remote file path
|
||||
* @param remoteFilePath remote file path
|
||||
* @throws SftpException
|
||||
*/
|
||||
public void touch(String remoteFilePath) throws SftpException {
|
||||
SshEngine.touch(remoteFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renames a file
|
||||
* @param oldNamePath Path of the old name
|
||||
* @param newNamePath Path of the new name
|
||||
* @throws SftpException
|
||||
*/
|
||||
public void rename(String oldNamePath, String newNamePath) throws SftpException {
|
||||
SshEngine.rename(oldNamePath, newNamePath);
|
||||
}
|
||||
|
||||
/*
|
||||
* ========== END File System Operations ==========
|
||||
*/
|
||||
|
||||
/*
|
||||
* ========== BEGIN Other ==========
|
||||
*/
|
||||
|
||||
/**
|
||||
* Given a remote file path, opens a graphical notepad for it
|
||||
* @param filePath remote file path to display in the notepad
|
||||
*/
|
||||
public void openNotepadForFile(String filePath) {
|
||||
new NotepadController(filePath).show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disposes resources which need to be freed before exiting
|
||||
* the application
|
||||
*/
|
||||
public void disposeResources() {
|
||||
SshEngine.disconnectSession();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method is deprecated.
|
||||
* Catch SftpException
|
||||
* and look for "Permission denied" instead. This prevents
|
||||
* unnecessary overhead
|
||||
*/
|
||||
public boolean isReadable(String path) {
|
||||
StringBuilder command = new StringBuilder();
|
||||
command.append("[ -r \"");
|
||||
command.append(path.equals("~") ? SshEngine.executeCommand("pwd").replace("\"", "\\\"") : path.replace("\"", "\\\""));
|
||||
command.append("\" ] && echo 1 || echo 0"); // short circuiting
|
||||
return SshEngine.executeCommand(command.toString()).trim().equals("1");
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method is deprecated.
|
||||
* Catch SftpException
|
||||
* and look for "Permission denied" instead
|
||||
*/
|
||||
public boolean isWriteable(String path) {
|
||||
StringBuilder command = new StringBuilder();
|
||||
command.append("[ -w \"");
|
||||
command.append(path.equals("~") ? SshEngine.executeCommand("pwd").replace("\"", "\\\"") : path.replace("\"", "\\\""));
|
||||
command.append("\" ] && echo 1 || echo 0"); // short circuiting
|
||||
return SshEngine.executeCommand(command.toString()).trim().equals("1");
|
||||
}
|
||||
|
||||
public void showFrame(boolean show) {
|
||||
frame.setVisible(show);
|
||||
}
|
||||
|
||||
/*
|
||||
* ========== END Other ==========
|
||||
*/
|
||||
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
package controllers;
|
||||
|
||||
import code.GuiAbstractions.Implementations.JFrameFactory;
|
||||
import code.GuiAbstractions.Interfaces.*;
|
||||
import views.interfaces.IFindAndReplaceFrame;
|
||||
import views.interfaces.INotepadFrame;
|
||||
|
||||
public class FindAndReplaceController {
|
||||
|
||||
private IGenericTextArea textArea;
|
||||
private IFindAndReplaceFrame frame;
|
||||
|
||||
public FindAndReplaceController(IGenericTextArea textArea) {
|
||||
this.textArea = textArea;
|
||||
try {
|
||||
frame = (IFindAndReplaceFrame) JFrameFactory.createJFrame(IFrameFactory.FIND_AND_REPLACE, this);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Show frame centered to parent
|
||||
*/
|
||||
public void showAtTheCenterOfFrame(INotepadFrame notepadFrame) {
|
||||
int childX = notepadFrame.getX() + (notepadFrame.getWidth() - frame.getWidth()) / 2;
|
||||
int childY = notepadFrame.getY() + (notepadFrame.getHeight() - frame.getHeight()) / 2;
|
||||
frame.setLocation(childX, childY);
|
||||
frame.setVisible(true);
|
||||
}
|
||||
|
||||
public int findNext(String searchText) {
|
||||
String text = textArea.getText();
|
||||
int currentIndex = textArea.getCaretPosition();
|
||||
int nextIndex = text.indexOf(searchText, currentIndex);
|
||||
|
||||
if (nextIndex != -1) {
|
||||
textArea.selectText(nextIndex, nextIndex + searchText.length());
|
||||
return nextIndex;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int findPrevious(String searchText) {
|
||||
String text = textArea.getText();
|
||||
int cutAt;
|
||||
|
||||
if(textArea.hasHighlightedText()) {
|
||||
cutAt = textArea.getSelectionStart();
|
||||
}
|
||||
else {
|
||||
cutAt = textArea.getCaretPosition();
|
||||
}
|
||||
String firstPart = text.substring(0, cutAt);
|
||||
int previousIndex = firstPart.lastIndexOf(searchText, firstPart.length() - 1);
|
||||
if (previousIndex != -1) {
|
||||
textArea.selectText(previousIndex, previousIndex + searchText.length());
|
||||
return previousIndex;
|
||||
}
|
||||
else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public int replaceNext(String toReplace, String replaceWith) {
|
||||
int index = findNext(toReplace);
|
||||
|
||||
if(index != -1) {
|
||||
textArea.replaceRange(replaceWith, index, index + toReplace.length());
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
public void replaceAll(String searchText, String replacement) {
|
||||
String text = textArea.getText();
|
||||
text = text.replaceAll(searchText, replacement);
|
||||
textArea.setText(text);
|
||||
}
|
||||
|
||||
public void disposeMyFrame() {
|
||||
if(frame != null) {
|
||||
frame.dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package controllers;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import code.SshEngine;
|
||||
import code.GuiAbstractions.Implementations.JFrameFactory;
|
||||
import code.GuiAbstractions.Interfaces.IFrameFactory;
|
||||
import views.interfaces.ILoginFrame;
|
||||
|
||||
public class LoginController {
|
||||
|
||||
private ILoginFrame frame;
|
||||
|
||||
public LoginController() {
|
||||
try {
|
||||
frame = (ILoginFrame) JFrameFactory.createJFrame(IFrameFactory.LOGIN, this);
|
||||
}
|
||||
catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean Login(String host, String username, String password, String port) throws IllegalArgumentException {
|
||||
LoginCredentials.host = host;
|
||||
LoginCredentials.username = username;
|
||||
LoginCredentials.password = password;
|
||||
LoginCredentials.port = Integer.parseInt(port);
|
||||
|
||||
if (SshEngine.connetion()) {
|
||||
frame.setVisible(false);
|
||||
new DesktopController().showFrame(true);;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public void ValidateInput(String host, String username, String password, String port) throws IllegalArgumentException {
|
||||
|
||||
// Host Validation. Consider its necessity.
|
||||
try {
|
||||
InetAddress.getByName(host);
|
||||
} catch (UnknownHostException ex) {
|
||||
throw new IllegalArgumentException("Host could not be found", ex);
|
||||
}
|
||||
|
||||
// Port Validation
|
||||
try {
|
||||
Integer.parseInt(port);
|
||||
}
|
||||
catch(NumberFormatException ex) {
|
||||
throw new IllegalArgumentException("Invalid port number", ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void showFrame(boolean show) {
|
||||
frame.setVisible(show);
|
||||
}
|
||||
|
||||
public static class LoginCredentials{
|
||||
public static String host;
|
||||
public static String username;
|
||||
public static String password;
|
||||
public static int port;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package controllers;
|
||||
|
||||
import code.SshEngine;
|
||||
import code.GuiAbstractions.Implementations.JFrameFactory;
|
||||
import code.GuiAbstractions.Interfaces.IFrameFactory;
|
||||
import code.GuiAbstractions.Interfaces.IGenericTextArea;
|
||||
import views.interfaces.INotepadFrame;
|
||||
public class NotepadController {
|
||||
|
||||
private String filePath = null;
|
||||
private INotepadFrame notepadFrame = null;
|
||||
private FindAndReplaceController myFindAndReplaceController;
|
||||
private boolean unsaved = false;
|
||||
|
||||
public boolean isUnsaved() {
|
||||
return unsaved;
|
||||
}
|
||||
|
||||
public void setUnsaved(boolean unsaved) {
|
||||
this.unsaved = unsaved;
|
||||
}
|
||||
|
||||
public String getFilePath() {
|
||||
return this.filePath;
|
||||
}
|
||||
|
||||
public NotepadController(String filePath) {
|
||||
this.filePath = filePath;
|
||||
String contentToDisplay = SshEngine.readFile(filePath);
|
||||
try {
|
||||
notepadFrame = (INotepadFrame) JFrameFactory.createJFrame(IFrameFactory.NOTEPAD, this);
|
||||
notepadFrame.displayContent(contentToDisplay);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void show() {
|
||||
notepadFrame.setVisible(true);
|
||||
}
|
||||
|
||||
public void writeOnFile(String text) {
|
||||
SshEngine.writeFile(text, this.filePath);
|
||||
}
|
||||
|
||||
public void showFindAndReplace(IGenericTextArea textArea) {
|
||||
myFindAndReplaceController = new FindAndReplaceController(textArea);
|
||||
myFindAndReplaceController.showAtTheCenterOfFrame(notepadFrame);
|
||||
}
|
||||
|
||||
public void disposeFindAndReplaceFrame() {
|
||||
if(myFindAndReplaceController != null)
|
||||
myFindAndReplaceController.disposeMyFrame();
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return filePath + (unsaved ? " - UNSAVED" : "");
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
package controllers;
|
||||
|
||||
import java.util.Observable;
|
||||
import java.util.Observer;
|
||||
import code.QueueEventManager;
|
||||
import code.TransferProgress;
|
||||
import code.GuiAbstractions.Implementations.JFrameFactory;
|
||||
import code.GuiAbstractions.Interfaces.IFrameFactory;
|
||||
import views.interfaces.IQueueFrame;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import javax.swing.SwingUtilities;
|
||||
|
||||
import com.jcraft.jsch.SftpProgressMonitor;
|
||||
|
||||
@SuppressWarnings("deprecation") // Observer/Observable objects are okay here
|
||||
public class QueueController implements Observer {
|
||||
|
||||
private IQueueFrame frame;
|
||||
// A HashMap containing the Transfer Progress entry and the index of said entry
|
||||
// in the table.
|
||||
// We need a ConcurrentHashMap instead of a simple HashMap because it
|
||||
// is accessed by multiple threads. In particular the threads executing update()
|
||||
// and the Event Dispatch Thread.
|
||||
// Alternatively, the HashMap could've been managed by the view itself without the need
|
||||
// to concern over threading/concurrent problems.
|
||||
// TODO Make the HashMap to be handled by the view itself (EDT thread) rather than concurrent threads
|
||||
// to enhance readability and understanding
|
||||
private ConcurrentHashMap<TransferProgress, Integer> indexAssociationMap = new ConcurrentHashMap<>();
|
||||
private final static int HASHMAP_DUMMY_VALUE = -1;
|
||||
|
||||
// Executed by the EDT
|
||||
public QueueController() {
|
||||
try {
|
||||
frame = (IQueueFrame) JFrameFactory.createJFrame(IFrameFactory.QUEUE);
|
||||
}
|
||||
catch (Exception e) {}
|
||||
|
||||
// Register observer of the changes
|
||||
QueueEventManager.getInstance().addObserver(this);
|
||||
|
||||
// Get previous enqueued elements. Do not place before addObserver(this) or some
|
||||
// transfers could go lost
|
||||
TransferProgress[] queued = QueueEventManager.getInstance().getQueue();
|
||||
for(TransferProgress transferProgress : queued) {
|
||||
// It is possible that while iterating on this for, the element
|
||||
// has already been inserted into the indexAssociationMap from
|
||||
// another thread executing update(), hence, we check if the key is contained
|
||||
// already.
|
||||
if (indexAssociationMap.putIfAbsent(transferProgress, HASHMAP_DUMMY_VALUE) == null) {
|
||||
int percentage = (int) Math.floor( ((transferProgress.getTransferredBytes() * 100) / transferProgress.getTotalBytes()) );
|
||||
int rowIndex = frame.addRow(transferProgress.getSource(), transferProgress.getDestination(), transferProgress.getOperation() == SftpProgressMonitor.GET? "Download" : "Upload", percentage);
|
||||
indexAssociationMap.put(transferProgress, rowIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Updated by QueueEventManager. Can run simultaneously
|
||||
// on multiple threads.
|
||||
@Override
|
||||
public void update(Observable o, Object arg) {
|
||||
TransferProgress transferProgressObj = (TransferProgress)arg;
|
||||
if(transferProgressObj.getTransferStatus() == TransferProgress.INIT) {
|
||||
// Since the Runnable in SwingUtilities.invokeLater contained in this if
|
||||
// might run *after* a subsequent execution of
|
||||
// update() having a TransferProgress whose status is UPDATING, said subsequent update()
|
||||
// must know that this specific transferProgressObj was already inserted in the HashMap,
|
||||
// otherwise the if (indexAssociationMap.putIfAbsent(transferProgressObj, HASHMAP_DUMMY_VALUE) == null)
|
||||
// (contained in the if checking whether the status is UPDATING)
|
||||
// would return true and add it again to the table, because indexAssociationMap.put(transferProgressObj, rowIndex);
|
||||
// has not been completed yet, being in the Runnable.
|
||||
//
|
||||
// We call putIfAbsent() instead of just put() because it is possible that this transferProgressObj is also
|
||||
// in the initial for present in Queue() if this transfer was initiated in a time x where
|
||||
// t1 < x < t2, where t1 is the time of completion of QueueEventManager.getInstance().addObserver(this);
|
||||
// and t2 is the time of completion of QueueEventManager.getInstance().getQueue();
|
||||
//
|
||||
// To sum it up:
|
||||
// 1) update() receives transferProgressObj whose status is "INIT". Puts "DUMMY_VALUE"
|
||||
// and schedules the EDT for a table insertion;
|
||||
// 2) update() receives the same transferProgressObj with the status "UPDATING".
|
||||
// It successfully sees that indexAssociationMap contains this specific transferProgressObj,
|
||||
// whose value is DUMMY_VALUE, so it does not schedule an insert, but rather an update. While
|
||||
// updating it will see that the value is not valid (if(rowIndex != HASHMAP_DUMMY_VALUE)) and will
|
||||
// not perform an update as well.
|
||||
// 3) The EDT runs and puts the correct index in the HashMap.
|
||||
//
|
||||
// Remember that if an update() receives a transferProgress whose status is INIT, that update() will always run
|
||||
// before an update() on the same transferProgress whose status is UPDATING, as the thread handling
|
||||
// each transferProgress is one and one only.
|
||||
if(indexAssociationMap.putIfAbsent(transferProgressObj, HASHMAP_DUMMY_VALUE) == null) {
|
||||
// We need SwingUtilities.invokeLater because we
|
||||
// are not on the Event Dispatch Thread (the thread
|
||||
// responsible for GUI management), but rather on the
|
||||
// thread created in SshEngine
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int rowIndex = frame.addRow(transferProgressObj.getSource(), transferProgressObj.getDestination(), transferProgressObj.getOperation() == SftpProgressMonitor.GET? "Download" : "Upload", 0);
|
||||
indexAssociationMap.put(transferProgressObj, rowIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
else if(transferProgressObj.getTransferStatus() == TransferProgress.UPDATING) {
|
||||
int percentage;
|
||||
// Avoid division by zero
|
||||
if(transferProgressObj.getTotalBytes() == 0) {
|
||||
// The percentage is 100% if ∀ byte in the file, byte was transferred.
|
||||
// If there are no bytes in the file, this logic proposition holds true (vacuous truth)
|
||||
percentage = 100;
|
||||
}
|
||||
else {
|
||||
percentage = (int) Math.floor( ((transferProgressObj.getTransferredBytes() * 100) / transferProgressObj.getTotalBytes()) );
|
||||
}
|
||||
// It is possible to receive TransferProgress.UPDATING without receiving
|
||||
// a TransferProgress.INIT (when this controller gets created when the transferring
|
||||
// was already occurring) and before "for(TransferProgress transferProgress : queued)"
|
||||
// gets executed on this element
|
||||
if (indexAssociationMap.putIfAbsent(transferProgressObj, HASHMAP_DUMMY_VALUE) == null) {
|
||||
// We need SwingUtilities.invokeLater because we
|
||||
// are not on the Event Dispatch Thread (the thread
|
||||
// responsible for GUI management), but rather on the
|
||||
// thread created in SshEngine
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int rowIndex = frame.addRow(transferProgressObj.getSource(), transferProgressObj.getDestination(), transferProgressObj.getOperation() == SftpProgressMonitor.GET? "Download" : "Upload", percentage);
|
||||
indexAssociationMap.put(transferProgressObj, rowIndex);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
int rowIndex = indexAssociationMap.get(transferProgressObj);
|
||||
// It is possible that rowIndex is a DUMMY_VALUE if the insertion
|
||||
// into the table has been scheduled with SwingUtilities.invokeLater
|
||||
// but has not ran yet
|
||||
if(rowIndex != HASHMAP_DUMMY_VALUE) {
|
||||
// We need SwingUtilities.invokeLater because we
|
||||
// are not on the Event Dispatch Thread (the thread
|
||||
// responsible for GUI management), but rather on the
|
||||
// thread created in SshEngine
|
||||
SwingUtilities.invokeLater(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
frame.updateRow(rowIndex, percentage);
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
// TODO If the file is small enough, in all the (few)
|
||||
// updates, rowIndex could always be HASHMAP_DUMMY_VALUE
|
||||
// because when SwingUtilities tries to insert the row the first time,
|
||||
// the EDT task will not execute soon enough before the termination
|
||||
// of the transfer, so we will see that the percentage it's fixed
|
||||
// to a specific value.
|
||||
}
|
||||
}
|
||||
}
|
||||
else if(transferProgressObj.getTransferStatus() == TransferProgress.END) {
|
||||
// We choose not to remove the element from the table once it has finished
|
||||
}
|
||||
}
|
||||
|
||||
public void showFrame(boolean visible) {
|
||||
frame.setVisible(true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
package views;
|
||||
|
||||
import java.awt.event.*;
|
||||
import javax.swing.*;
|
||||
|
||||
import controllers.FindAndReplaceController;
|
||||
import views.interfaces.IFindAndReplaceFrame;
|
||||
|
||||
// This JFrame has been forked from the project "Simple-Java-Text-Editor" you
|
||||
// can find at its link: https://github.com/pH-7/Simple-Java-Text-Editor.
|
||||
// Its license can be found here https://github.com/pH-7/Simple-Java-Text-Editor/blob/master/license.txt (Apache License 2.0)
|
||||
|
||||
public class FindAndReplace extends JFrame implements IFindAndReplaceFrame {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public FindAndReplace(Object controller) {
|
||||
JLabel lab1 = new JLabel("Find:");
|
||||
JLabel lab2 = new JLabel("Replace:");
|
||||
JTextField findTextField = new JTextField(30);
|
||||
JTextField replaceTextField = new JTextField(30);
|
||||
JButton findNextBtn = new JButton("Find Next");
|
||||
findNextBtn.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e){
|
||||
((FindAndReplaceController) controller).findNext(findTextField.getText());
|
||||
}
|
||||
});
|
||||
|
||||
JButton findPreviousBtn = new JButton("Find Previous");
|
||||
findPreviousBtn.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e){
|
||||
((FindAndReplaceController) controller).findPrevious(findTextField.getText());
|
||||
}
|
||||
});
|
||||
|
||||
JButton replaceNextBtn = new JButton("Replace Next");
|
||||
replaceNextBtn.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e){
|
||||
((FindAndReplaceController) controller).replaceNext(findTextField.getText(), replaceTextField.getText());
|
||||
}
|
||||
});
|
||||
|
||||
JButton replaceAllBtn = new JButton("Replace All");
|
||||
replaceAllBtn.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e){
|
||||
((FindAndReplaceController) controller).replaceAll(findTextField.getText(), replaceTextField.getText());
|
||||
}
|
||||
});
|
||||
|
||||
JButton cancel = new JButton("Cancel");
|
||||
cancel.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e){
|
||||
dispose();
|
||||
}
|
||||
});
|
||||
|
||||
setLayout(null);
|
||||
|
||||
// Set the width and height of the label
|
||||
int labWidth = 80;
|
||||
int labHeight = 20;
|
||||
|
||||
// Adding labels
|
||||
lab1.setBounds(10,10, labWidth, labHeight);
|
||||
add(lab1);
|
||||
findTextField.setBounds(10+labWidth, 10, 120, 20);
|
||||
add(findTextField);
|
||||
lab2.setBounds(10, 10+labHeight+10, labWidth, labHeight);
|
||||
add(lab2);
|
||||
replaceTextField.setBounds(10+labWidth, 10+labHeight+10, 120, 20);
|
||||
add(replaceTextField);
|
||||
|
||||
// Adding buttons
|
||||
findNextBtn.setBounds(225, 6, 115, 20);
|
||||
add(findNextBtn);
|
||||
|
||||
findPreviousBtn.setBounds(225, 28, 115, 20);
|
||||
add(findPreviousBtn);
|
||||
|
||||
replaceNextBtn.setBounds(225, 50, 115, 20);
|
||||
add(replaceNextBtn);
|
||||
|
||||
replaceAllBtn.setBounds(225, 72, 115, 20);
|
||||
add(replaceAllBtn);
|
||||
|
||||
cancel.setBounds(225, 94, 115, 20);
|
||||
add(cancel);
|
||||
|
||||
// Set the width and height of the window
|
||||
int width = 360;
|
||||
int height = 160;
|
||||
|
||||
// Set size window
|
||||
setSize(width, height);
|
||||
setResizable(false);
|
||||
setTitle("Find/Replace");
|
||||
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
setAlwaysOnTop(true);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,124 @@
|
|||
package views;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
|
||||
import code.Constants;
|
||||
import code.Constants.GuifyColors;
|
||||
import controllers.LoginController;
|
||||
import views.interfaces.ILoginFrame;
|
||||
|
||||
import javax.swing.JPasswordField;
|
||||
import javax.swing.JLabel;
|
||||
import javax.swing.JOptionPane;
|
||||
import javax.swing.JTextField;
|
||||
import javax.swing.JButton;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.Color;
|
||||
|
||||
public class Login extends JFrame implements ILoginFrame {
|
||||
|
||||
private static final long serialVersionUID = 1;
|
||||
private LoginController controller;
|
||||
private JPanel contentPane;
|
||||
private JPasswordField passwordField;
|
||||
private JTextField usernameField;
|
||||
private JTextField hostField;
|
||||
private JTextField portField;
|
||||
|
||||
public Login(Object controller) {
|
||||
this.controller = (LoginController) controller;
|
||||
|
||||
setTitle(Constants.APP_NAME);
|
||||
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
|
||||
setBounds(100, 100, 312, 400);
|
||||
contentPane = new JPanel();
|
||||
contentPane.setForeground(Color.WHITE);
|
||||
contentPane.setBackground(Color.WHITE);
|
||||
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
|
||||
|
||||
setContentPane(contentPane);
|
||||
contentPane.setLayout(null);
|
||||
|
||||
passwordField = new JPasswordField();
|
||||
passwordField.setToolTipText("SSH Password");
|
||||
passwordField.setBounds(10, 159, 139, 20);
|
||||
contentPane.add(passwordField);
|
||||
|
||||
JLabel lblNewLabel = new JLabel("Username");
|
||||
lblNewLabel.setBounds(10, 78, 139, 14);
|
||||
contentPane.add(lblNewLabel);
|
||||
|
||||
JLabel lblNewLabel_1 = new JLabel("Password");
|
||||
lblNewLabel_1.setBounds(10, 134, 139, 14);
|
||||
contentPane.add(lblNewLabel_1);
|
||||
|
||||
usernameField = new JTextField();
|
||||
usernameField.setToolTipText("SSH Username");
|
||||
usernameField.setBounds(10, 103, 139, 20);
|
||||
contentPane.add(usernameField);
|
||||
usernameField.setColumns(10);
|
||||
|
||||
JButton btnConnect = new JButton("Connect");
|
||||
btnConnect.setForeground(Color.WHITE);
|
||||
btnConnect.setBackground(GuifyColors.BLUE);
|
||||
btnConnect.addActionListener(new ActionListener() {
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
btnConnect_OnClick();
|
||||
}
|
||||
});
|
||||
btnConnect.setBounds(75, 297, 139, 30);
|
||||
contentPane.add(btnConnect);
|
||||
|
||||
JLabel lblNewLabel_2 = new JLabel("Host");
|
||||
lblNewLabel_2.setBounds(10, 22, 139, 14);
|
||||
contentPane.add(lblNewLabel_2);
|
||||
|
||||
hostField = new JTextField();
|
||||
hostField.setToolTipText("SSH Host");
|
||||
hostField.setBounds(10, 47, 139, 20);
|
||||
contentPane.add(hostField);
|
||||
hostField.setColumns(10);
|
||||
|
||||
JLabel lblNewLabel_3 = new JLabel("Port");
|
||||
lblNewLabel_3.setBounds(10, 190, 139, 14);
|
||||
contentPane.add(lblNewLabel_3);
|
||||
|
||||
portField = new JTextField();
|
||||
portField.setText("22");
|
||||
portField.setToolTipText("SSH Port");
|
||||
portField.setBounds(10, 215, 86, 20);
|
||||
contentPane.add(portField);
|
||||
portField.setColumns(10);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Events
|
||||
*/
|
||||
private void btnConnect_OnClick() {
|
||||
|
||||
String host = hostField.getText();
|
||||
String username = usernameField.getText();
|
||||
String password = String.valueOf(passwordField.getPassword());
|
||||
String port = portField.getText();
|
||||
|
||||
// Perform validation
|
||||
try {
|
||||
controller.ValidateInput(host, username, password, port);
|
||||
}
|
||||
catch(IllegalArgumentException ex) {
|
||||
JOptionPane.showMessageDialog(new JFrame(), ex.getMessage(), "Attention required", JOptionPane.ERROR_MESSAGE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Perform login
|
||||
if(!controller.Login(host, username, password, port)) {
|
||||
JOptionPane.showMessageDialog(new JFrame(), "SSH Login failed", "SSH Login failed", JOptionPane.ERROR_MESSAGE);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
package views;
|
||||
|
||||
import java.awt.Color;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.Box;
|
||||
import javax.swing.ImageIcon;
|
||||
import javax.swing.JButton;
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JTextArea;
|
||||
import javax.swing.JToolBar;
|
||||
import javax.swing.border.EmptyBorder;
|
||||
import javax.swing.event.DocumentEvent;
|
||||
import javax.swing.event.DocumentListener;
|
||||
|
||||
import code.GuiAbstractions.Implementations.JGenericTextArea;
|
||||
import controllers.NotepadController;
|
||||
import views.interfaces.INotepadFrame;
|
||||
|
||||
import java.awt.Font;
|
||||
import java.awt.Image;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.WindowAdapter;
|
||||
import java.awt.event.WindowEvent;
|
||||
import java.io.IOException;
|
||||
import javax.swing.JScrollPane;
|
||||
public class Notepad extends JFrame implements INotepadFrame {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private NotepadController controller;
|
||||
/**
|
||||
* Create the application.
|
||||
*/
|
||||
|
||||
JTextArea textArea;
|
||||
|
||||
public Notepad(Object controller) {
|
||||
this.controller = (NotepadController) controller;
|
||||
setTitle(this.controller.getFilePath());
|
||||
setBounds(100, 100, 800, 600);
|
||||
setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
getContentPane().setLayout(null);
|
||||
|
||||
JScrollPane scrollPane = new JScrollPane();
|
||||
scrollPane.setBounds(10, 38, 764, 512);
|
||||
getContentPane().add(scrollPane);
|
||||
|
||||
textArea = new JTextArea();
|
||||
scrollPane.setViewportView(textArea);
|
||||
textArea.setTabSize(4);
|
||||
textArea.setFont(new Font("Monospaced", Font.PLAIN, 14));
|
||||
textArea.setCaretPosition(0);
|
||||
textArea.getDocument().addDocumentListener(new DocumentListener() {
|
||||
@Override
|
||||
public void insertUpdate(DocumentEvent e) {
|
||||
// Called when text is inserted into the document
|
||||
handleTextChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeUpdate(DocumentEvent e) {
|
||||
// Called when text is removed from the document
|
||||
handleTextChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void changedUpdate(DocumentEvent e) {
|
||||
// Called when attributes of the document change
|
||||
// NO OP
|
||||
}
|
||||
|
||||
private void handleTextChange() {
|
||||
if(!((NotepadController)controller).isUnsaved()) {
|
||||
((NotepadController)controller).setUnsaved(true);
|
||||
setTitle(((NotepadController)controller).getTitle());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
JToolBar toolBar = new JToolBar();
|
||||
toolBar.setFloatable(false);
|
||||
toolBar.setBounds(10, 0, 614, 37);
|
||||
toolBar.setBackground(new Color(240, 240, 240));
|
||||
|
||||
JButton saveBtn = new JButton();
|
||||
saveBtn.setBorderPainted(false);
|
||||
saveBtn.setBorder(new EmptyBorder(0, 0, 0, 0)); // Set empty border
|
||||
saveBtn.setToolTipText("Save");
|
||||
try {
|
||||
saveBtn.setIcon(new ImageIcon(ImageIO.read(getClass().getClassLoader().getResource("save_icon.png")).getScaledInstance(25, 25, Image.SCALE_SMOOTH)));
|
||||
saveBtn.setBackground(new Color(240, 240, 240));
|
||||
saveBtn.setBorderPainted(false);
|
||||
saveBtn.setEnabled(true);
|
||||
}catch(IOException ex) {
|
||||
saveBtn.setText("Save");
|
||||
}
|
||||
saveBtn.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
((NotepadController)controller).writeOnFile(textArea.getText());
|
||||
((NotepadController)controller).setUnsaved(false);
|
||||
setTitle(((NotepadController)controller).getTitle());
|
||||
}
|
||||
|
||||
// Hover on
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
if(saveBtn.isEnabled())
|
||||
saveBtn.setBackground(new Color(220, 220, 220));
|
||||
}
|
||||
|
||||
// Hover off
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
saveBtn.setBackground(new Color(240, 240, 240));
|
||||
}
|
||||
});
|
||||
|
||||
JButton searchBtn = new JButton();
|
||||
searchBtn.setBorderPainted(false);
|
||||
searchBtn.setBorder(new EmptyBorder(0, 0, 0, 0)); // Set empty border);
|
||||
searchBtn.setToolTipText("Serch/Replace");
|
||||
try {
|
||||
searchBtn.setIcon(new ImageIcon(ImageIO.read(getClass().getClassLoader().getResource("search_icon.png")).getScaledInstance(25, 25, Image.SCALE_SMOOTH)));
|
||||
searchBtn.setBackground(new Color(240, 240, 240));
|
||||
searchBtn.setBorderPainted(false);
|
||||
searchBtn.setEnabled(true);
|
||||
}catch(IOException ex) {
|
||||
searchBtn.setText("Serch/Replace");
|
||||
}
|
||||
searchBtn.addMouseListener(new MouseAdapter() {
|
||||
@Override
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
((NotepadController)controller).showFindAndReplace(new JGenericTextArea(textArea));
|
||||
}
|
||||
|
||||
// Hover on
|
||||
@Override
|
||||
public void mouseEntered(MouseEvent e) {
|
||||
if(searchBtn.isEnabled())
|
||||
searchBtn.setBackground(new Color(220, 220, 220));
|
||||
}
|
||||
|
||||
// Hover off
|
||||
@Override
|
||||
public void mouseExited(MouseEvent e) {
|
||||
searchBtn.setBackground(new Color(240, 240, 240));
|
||||
}
|
||||
});
|
||||
|
||||
toolBar.add(saveBtn);
|
||||
toolBar.add(Box.createHorizontalStrut(15));
|
||||
toolBar.add(searchBtn);
|
||||
|
||||
getContentPane().add(toolBar);
|
||||
|
||||
/**
|
||||
* Close "Find and Replace" if this window
|
||||
* gets closed
|
||||
*/
|
||||
addWindowListener(new WindowAdapter() {
|
||||
@Override
|
||||
public void windowClosing(WindowEvent e) {
|
||||
((NotepadController)controller).disposeFindAndReplaceFrame();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void displayContent(String content) {
|
||||
textArea.setText(content);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package views;
|
||||
|
||||
import javax.swing.JFrame;
|
||||
import javax.swing.JProgressBar;
|
||||
import java.awt.BorderLayout;
|
||||
import java.awt.Color;
|
||||
import java.awt.Component;
|
||||
import javax.swing.JTable;
|
||||
import javax.swing.JScrollPane;
|
||||
import javax.swing.table.DefaultTableCellRenderer;
|
||||
import javax.swing.table.DefaultTableModel;
|
||||
import javax.swing.table.TableColumn;
|
||||
|
||||
import code.Constants;
|
||||
import views.interfaces.IQueueFrame;
|
||||
public class Queue extends JFrame implements IQueueFrame {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
/**
|
||||
*
|
||||
* Custom cell renderer in order to be able to display
|
||||
* a progress bar in the JTable
|
||||
*
|
||||
*/
|
||||
public static class ProgressBarTableCellRenderer extends DefaultTableCellRenderer {
|
||||
private static final long serialVersionUID = 1L;
|
||||
private JProgressBar progressBar;
|
||||
|
||||
public ProgressBarTableCellRenderer() {
|
||||
super();
|
||||
progressBar = new JProgressBar();
|
||||
progressBar.setStringPainted(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
|
||||
boolean hasFocus, int row, int column) {
|
||||
if (value instanceof Integer) {
|
||||
int progressValue = (Integer) value;
|
||||
progressBar.setValue(progressValue);
|
||||
progressBar.setString(progressValue + "%");
|
||||
}
|
||||
|
||||
progressBar.setStringPainted(true);
|
||||
progressBar.setForeground(Constants.GuifyColors.BLUE);
|
||||
progressBar.setBackground(Color.WHITE);
|
||||
|
||||
return progressBar;
|
||||
}
|
||||
}
|
||||
|
||||
public DefaultTableModel tableModel;
|
||||
|
||||
public Queue() {
|
||||
setTitle("Queue");
|
||||
String[] columnNames = {"Source", "Destination", "Operation", "Percentage"};
|
||||
tableModel = new DefaultTableModel(columnNames, 0);
|
||||
JTable table = new JTable(tableModel);
|
||||
table.setEnabled(false); // Prevents user editing
|
||||
// Show percentage by using a custom cell renderer
|
||||
TableColumn percentageColumn = table.getColumnModel().getColumn(3);
|
||||
percentageColumn.setCellRenderer(new ProgressBarTableCellRenderer());
|
||||
JScrollPane scrollPane = new JScrollPane(table);
|
||||
this.getContentPane().add(scrollPane, BorderLayout.CENTER);
|
||||
this.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
|
||||
this.pack();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a row in the JTable
|
||||
* @return The index of the inserted row
|
||||
*/
|
||||
public int addRow(String source, String destination, String operation, int percentage) {
|
||||
tableModel.addRow(new Object[]{source, destination, operation, percentage});
|
||||
return tableModel.getRowCount() - 1;
|
||||
}
|
||||
|
||||
public void updateRow(int rowIndex, int percentage) {
|
||||
if(rowIndex < tableModel.getRowCount()) {
|
||||
tableModel.setValueAt(percentage, rowIndex, 3);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package views.interfaces;
|
||||
|
||||
public interface IDesktopFrame {
|
||||
void drawComponentsForDirectory(String directory);
|
||||
void setVisible(boolean visible);
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package views.interfaces;
|
||||
|
||||
public interface IFindAndReplaceFrame {
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
void setLocation(int x, int y);
|
||||
void setVisible(boolean visible);
|
||||
void dispose();
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
package views.interfaces;
|
||||
|
||||
public interface ILoginFrame {
|
||||
void setVisible(boolean visible);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package views.interfaces;
|
||||
|
||||
public interface INotepadFrame {
|
||||
void setVisible(boolean visible);
|
||||
void displayContent(String content);
|
||||
int getX();
|
||||
int getY();
|
||||
int getWidth();
|
||||
int getHeight();
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package views.interfaces;
|
||||
|
||||
public interface IQueueFrame {
|
||||
public void setVisible(boolean visible);
|
||||
int addRow(String source, String destination, String operation, int percentage);
|
||||
void updateRow(int rowIndex, int percentage);
|
||||
}
|
After Width: | Height: | Size: 302 KiB |
|
@ -0,0 +1,23 @@
|
|||
# Guify
|
||||
Guify creates a Graphical User Interface for SSH.
|
||||
|
||||
Works on any machine able to execute Java code, but is able to target only Linux (or more in general POSIX-compliant) systems.
|
||||
|
||||
## Features:
|
||||
- Navigation in the File System;
|
||||
- Common file operations (cut, copy, rename, delete, paste, create);
|
||||
- File and folder transfer (to/from);
|
||||
- Integrated file editor.
|
||||
|
||||
## Alternatives
|
||||
If you want to achieve similar results you can:
|
||||
* Use [WinSCP](https://winscp.net/eng/index.php) (Windows only);
|
||||
* Use a file manager like Nautilus, Konqueror, PCManFM (Linux examples)
|
||||
* Configure an FTP server and accessing it through FileZilla;
|
||||
* Mount a remote partition locally with the SMB protocol;
|
||||
* Route X11 output to your local machine over SSH;
|
||||
* ...
|
||||
|
||||
## Screenshots
|
||||
<img src="/Images/Image.jpg" alt="Homescreen">
|
||||
|