/*
* Copyright 2016 Igor Maznitsa.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.igormaznitsa.sciareto.ui.tabs;
import java.awt.Cursor;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.ToolTipManager;
import javax.swing.UIManager;
import org.apache.commons.lang.StringEscapeUtils;
import com.igormaznitsa.meta.common.interfaces.Disposable;
import com.igormaznitsa.mindmap.model.logger.Logger;
import com.igormaznitsa.mindmap.model.logger.LoggerFactory;
import com.igormaznitsa.mindmap.model.nio.Paths;
import com.igormaznitsa.mindmap.swing.panel.utils.Utils;
import com.igormaznitsa.sciareto.Context;
import com.igormaznitsa.sciareto.ui.DialogProviderManager;
import com.igormaznitsa.sciareto.ui.UiUtils;
import com.igormaznitsa.sciareto.ui.editors.EditorContentType;
import com.igormaznitsa.sciareto.ui.tree.NodeProject;
public final class TabTitle extends JPanel {
private static final long serialVersionUID = -6534083975320248288L;
private final JLabel titleLabel;
private final JButton closeButton;
private volatile File associatedFile;
private volatile boolean changed;
private final Context context;
private final TabProvider parent;
private boolean visited;
private static final Icon NIMBUS_CLOSE_ICON = new ImageIcon(UiUtils.loadIcon("nimbusCloseFrame.png")); //NOI18N
private static final Logger LOGGER = LoggerFactory.getLogger(TabTitle.class);
public TabTitle(@Nonnull final Context context, @Nonnull final TabProvider parent, @Nullable final File associatedFile) {
super(new GridBagLayout());
this.parent = parent;
this.context = context;
this.associatedFile = associatedFile;
this.changed = this.associatedFile == null;
this.setOpaque(false);
final GridBagConstraints constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.BOTH;
constraints.weightx = 1000.0d;
final TabTitle theInstance = this;
this.titleLabel = new JLabel() {
private static final long serialVersionUID = 8689945842487138781L;
@Override
protected void processKeyEvent(@Nonnull final KeyEvent e) {
theInstance.getParent().dispatchEvent(e);
}
@Override
public String getToolTipText() {
return theInstance.getToolTipText();
}
@Override
public boolean isFocusable() {
return false;
}
};
this.add(this.titleLabel, constraints);
final Icon uiCloseIcon = UIManager.getIcon("InternalFrameTitlePane.closeIcon"); //NOI18N
this.closeButton = new JButton(uiCloseIcon == null ? NIMBUS_CLOSE_ICON : uiCloseIcon) {
private static final long serialVersionUID = -8005282815756047979L;
@Override
public String getToolTipText() {
return theInstance.getToolTipText();
}
@Override
public boolean isFocusable() {
return false;
}
};
this.closeButton.setToolTipText("Close tab");
this.closeButton.setBorder(null);
this.closeButton.setContentAreaFilled(false);
this.closeButton.setMargin(new Insets(0, 0, 0, 0));
this.closeButton.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
this.closeButton.setOpaque(false);
this.closeButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(@Nonnull final ActionEvent e) {
doSafeClose();
}
});
constraints.fill = GridBagConstraints.BOTH;
constraints.weightx = 0.0d;
constraints.insets = new Insets(2, 8, 2, 0);
this.add(this.closeButton, constraints);
updateView();
ToolTipManager.sharedInstance().registerComponent(closeButton);
ToolTipManager.sharedInstance().registerComponent(this.titleLabel);
ToolTipManager.sharedInstance().registerComponent(this);
}
public void visited() {
this.visited = true;
}
public boolean isVisited() {
return this.visited;
}
@Override
public boolean contains(int x, int y) {
return this.closeButton.getBounds().contains(x, y);
}
public boolean save() throws IOException {
boolean result = false;
if (this.parent.saveDocument()) {
result = true;
this.changed = false;
updateView();
}
return result;
}
public boolean saveAs() throws IOException {
boolean result = false;
if (this.parent.saveDocumentAs()) {
result = true;
final NodeProject project = this.context.findProjectForFile(this.associatedFile);
if (project != null) {
project.getGroup().refreshProjectFolder(project);
this.context.focusInTree(this);
}
}
return result;
}
@Override
public boolean isFocusable() {
return false;
}
@Override
@Nullable
public String getToolTipText() {
return this.associatedFile == null ? null : this.associatedFile.getAbsolutePath();
}
public boolean belongFolderOrSame(@Nonnull final File folder) {
boolean result = false;
if (this.associatedFile != null) {
return folder.equals(this.associatedFile) || Paths.toPath(this.associatedFile).startsWith(Paths.toPath(folder));
}
return result;
}
@Nonnull
public TabProvider getProvider() {
return this.parent;
}
public void doSafeClose() {
final boolean close = !this.changed || DialogProviderManager.getInstance().getDialogProvider().msgConfirmOkCancel("Non saved file", "Close unsaved document '" + makeName() + "\'?");
if (close) {
this.context.closeTab(this);
}
}
@Nullable
public File getAssociatedFile() {
return this.associatedFile;
}
public void setAssociatedFile(@Nullable final File file) {
this.associatedFile = file;
updateView();
}
public void setChanged(final boolean flag) {
this.changed = flag;
updateView();
}
public boolean isChanged() {
return this.changed;
}
public void disposeEditor() {
final JComponent compo = this.parent.getMainComponent();
if (compo instanceof Disposable) {
((Disposable) compo).dispose();
}
}
public boolean reload(final boolean askUserConfirmationIfChanged) {
boolean reloaded = false;
if (askUserConfirmationIfChanged && isChanged() && !DialogProviderManager.getInstance().getDialogProvider().msgConfirmYesNo("File changed", String.format("File '%s' is changed, reload?", (this.associatedFile == null ? "..." : this.associatedFile.getName())))) {
return reloaded;
}
final File file = getAssociatedFile();
if (file.isFile()){
try{
this.parent.loadContent(file);
reloaded = true;
}catch(IOException ex){
LOGGER.error("Can't reload file :"+file, ex); //NOI18N
}
}
return reloaded;
}
@Nonnull
public EditorContentType getType() {
return this.parent.getEditor().getEditorContentType();
}
@Nonnull
private String makeName() {
final File file = this.associatedFile;
return file == null ? "Untitled" : file.getName(); //NOI18N
}
private void updateView() {
Utils.safeSwingCall(new Runnable() {
@Override
public void run() {
titleLabel.setText("<html>" + (changed ? "<b>*<u>" : "") + StringEscapeUtils.escapeHtml(makeName()) + (changed ? "</u></b>" : "") + "</html>"); //NOI18N
revalidate();
}
});
}
}