/*
* 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.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JTabbedPane;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import com.igormaznitsa.meta.annotation.MustNotContainNull;
import com.igormaznitsa.mindmap.model.logger.Logger;
import com.igormaznitsa.mindmap.model.logger.LoggerFactory;
import com.igormaznitsa.sciareto.Context;
import com.igormaznitsa.sciareto.ui.DialogProviderManager;
import com.igormaznitsa.sciareto.ui.MainFrame;
import com.igormaznitsa.sciareto.ui.UiUtils;
import com.igormaznitsa.sciareto.ui.editors.AbstractEditor;
public class EditorTabPane extends JTabbedPane implements Iterable<TabTitle> {
private static final long serialVersionUID = -8971773653667281550L;
private static final Logger LOGGER = LoggerFactory.getLogger(EditorTabPane.class);
private final Context context;
private boolean enabledNotificationAboutChange;
public EditorTabPane(@Nonnull final Context context) {
super(JTabbedPane.TOP);
this.context = context;
this.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
this.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
processPopup(e);
}
@Override
public void mousePressed(MouseEvent e) {
processPopup(e);
}
@Override
public void mouseClicked(MouseEvent e) {
processPopup(e);
}
private void processPopup(@Nonnull final MouseEvent e) {
if (e.isPopupTrigger()) {
final JPopupMenu menu = makePopupMenu();
if (menu != null) {
menu.show(e.getComponent(), e.getX(), e.getY());
e.consume();
}
}
}
});
this.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(@Nonnull final ChangeEvent e) {
if (enabledNotificationAboutChange) {
((MainFrame) context).processTabChanged(getCurrentTitle());
}
}
});
}
public void setNotifyForTabChanged(final boolean enable) {
this.enabledNotificationAboutChange = enable;
}
public boolean hasEditableAndChangedDocument() {
boolean result = false;
for (final TabTitle t : this) {
if (t != null && t.isChanged()) {
result = true;
break;
}
}
return result;
}
@Nullable
public TabTitle getCurrentTitle() {
final int index = this.getSelectedIndex();
return index < 0 ? null : (TabTitle) this.getTabComponentAt(index);
}
@Nonnull
@MustNotContainNull
public List<TabTitle> findListOfRelatedTabs(@Nonnull final File file) {
final List<TabTitle> result = new ArrayList<>();
for (final TabTitle t : this) {
if (t.belongFolderOrSame(file)) {
result.add(t);
}
}
return result;
}
public boolean replaceFileLink(@Nonnull final File oldFile, @Nonnull final File newFile) {
boolean replaced = false;
int index = 0;
for (final TabTitle title : this) {
if (oldFile.equals(title.getAssociatedFile())) {
title.setAssociatedFile(newFile);
this.setToolTipTextAt(index, title.getToolTipText());
replaced |= true;
}
index++;
}
return replaced;
}
public boolean isEmpty() {
return this.getTabCount() == 0;
}
@Nullable
private JPopupMenu makePopupMenu() {
final EditorTabPane theInstance = this;
final int selected = this.getSelectedIndex();
JPopupMenu result = null;
if (selected >= 0) {
final TabTitle title = (TabTitle) this.getTabComponentAt(selected);
result = new JPopupMenu();
if (title.isChanged()) {
final JMenuItem saveItem = new JMenuItem("Save");
saveItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(@Nonnull final ActionEvent e) {
try {
title.save();
}
catch (IOException ex) {
LOGGER.error("Can't save file", ex); //NOI18N
DialogProviderManager.getInstance().getDialogProvider().msgError("Can't save document, may be it is read-only! See log!");
}
}
});
result.add(saveItem);
}
if (title.getProvider().isSaveable()) {
final JMenuItem saveAsItem = new JMenuItem("Save As..");
saveAsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(@Nonnull final ActionEvent e) {
try {
title.saveAs();
}
catch (IOException ex) {
LOGGER.error("Can't save file", ex); //NOI18N
DialogProviderManager.getInstance().getDialogProvider().msgError("Can't save document, may be it is read-only! See log!");
}
}
});
result.add(saveAsItem);
}
result.add(new JSeparator());
final JMenuItem closeItem = new JMenuItem("Close");
closeItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(@Nonnull final ActionEvent e) {
title.doSafeClose();
}
});
result.add(closeItem);
final JMenuItem closeOthers = new JMenuItem("Close Other");
closeOthers.addActionListener(new ActionListener() {
@Override
public void actionPerformed(@Nonnull ActionEvent e) {
final List<TabTitle> list = new ArrayList<>();
for (final TabTitle t : theInstance) {
if (title != t) {
list.add(t);
}
}
safeCloseTabs(list.toArray(new TabTitle[list.size()]));
}
});
result.add(closeOthers);
final JMenuItem closeAll = new JMenuItem("Close All");
closeAll.addActionListener(new ActionListener() {
@Override
public void actionPerformed(@Nonnull ActionEvent e) {
final List<TabTitle> list = new ArrayList<>();
for (final TabTitle t : theInstance) {
list.add(t);
}
safeCloseTabs(list.toArray(new TabTitle[list.size()]));
}
});
result.add(closeAll);
result.add(new JSeparator());
final JMenuItem showInTree = new JMenuItem("Select in Tree");
showInTree.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
context.focusInTree(title);
}
});
result.add(showInTree);
final JMenuItem openInSystem = new JMenuItem("Open in System");
openInSystem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
final File file = title.getAssociatedFile();
if (file != null && file.exists()) {
UiUtils.openInSystemViewer(file);
}
}
});
result.add(openInSystem);
}
return result;
}
private void safeCloseTabs(@Nonnull @MustNotContainNull final TabTitle... titles) {
boolean foundUnsaved = false;
for (final TabTitle t : titles) {
foundUnsaved |= t.isChanged();
}
if (!foundUnsaved || DialogProviderManager.getInstance().getDialogProvider().msgConfirmOkCancel("Detected unsaved", "Detected unsaved documents! Close anyway?")) {
this.context.closeTab(titles);
}
}
@Nullable
public AbstractEditor getCurrentEditor(){
AbstractEditor result = null;
final int selected = this.getSelectedIndex();
if (selected>=0){
result = ((TabTitle)this.getTabComponentAt(selected)).getProvider().getEditor();
}
return result;
}
public void createTab(@Nonnull final TabProvider panel) {
super.addTab("...", panel.getEditor().getContainerToShow()); //NOI18N
final int count = this.getTabCount() - 1;
final TabTitle tabTitle = panel.getTabTitle();
this.setTabComponentAt(count, tabTitle);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
panel.getMainComponent().requestFocus();
}
});
this.setSelectedIndex(count);
this.setToolTipTextAt(count, tabTitle.getToolTipText());
}
public boolean focusToFile(@Nonnull final File file) {
for (int i = 0; i < this.getTabCount(); i++) {
final TabTitle title = (TabTitle) this.getTabComponentAt(i);
if (file.equals(title.getAssociatedFile())) {
this.setSelectedIndex(i);
((TabTitle) this.getTabComponentAt(i)).getProvider().focusToEditor();
return true;
}
}
return false;
}
public boolean removeTab(@Nonnull final TabTitle title) {
int index = -1;
for (int i = 0; i < this.getTabCount(); i++) {
if (this.getTabComponentAt(i) == title) {
index = i;
break;
}
}
if (index >= 0) {
try {
this.removeTabAt(index);
}
finally {
title.disposeEditor();
}
return true;
}
return false;
}
@Override
@Nonnull
public Iterator<TabTitle> iterator() {
final List<TabTitle> result = new ArrayList<>();
for (int i = 0; i < this.getTabCount(); i++) {
result.add((TabTitle) this.getTabComponentAt(i));
}
return result.iterator();
}
}