/*
* DBeaver - Universal Database Manager
* Copyright (C) 2010-2017 Serge Rider (serge@jkiss.org)
*
* 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 org.jkiss.dbeaver.ui.controls.folders;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.*;
import org.eclipse.swt.graphics.Rectangle;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Sash;
import org.jkiss.code.NotNull;
import org.jkiss.code.Nullable;
import org.jkiss.dbeaver.ui.UIUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Folders composite.
* Styles:
* SWT.LEFT, SWT.RIGHT - tabs orientation
*/
public class TabbedFolderComposite extends Composite implements ITabbedFolderContainer {
public static final int MIN_PANE_HEIGHT = 60;
@NotNull
private final Composite compositePane;
@Nullable
private TabbedFolderInfo[] folders;
private final Map<TabbedFolderInfo, Composite> contentsMap = new HashMap<>();
private List<ITabbedFolderListener> listeners = new ArrayList<>();
private FolderPane[] folderPanes;
private FolderPane lastActiveFolder = null;
private class FolderPane {
TabbedFolderInfo[] folders;
TabbedFolderList folderList;
Composite editorPane;
@Nullable
private Control curContent;
@Nullable
private ITabbedFolder curFolder;
public FolderPane(Composite parent, boolean last) {
this.folderList = new TabbedFolderList(parent, !last);
GridData gd = new GridData(GridData.FILL_VERTICAL);
if (!last) {
gd.verticalSpan = 2;
}
//gd.heightHint = 100;
this.folderList.setLayoutData(gd);
editorPane = UIUtils.createPlaceholder(parent, 1);
gd = new GridData(GridData.FILL_BOTH);
gd.heightHint = MIN_PANE_HEIGHT;
editorPane.setLayoutData(gd);
if (!last) {
final Sash sash = new Sash(parent, SWT.NONE);
gd = new GridData(GridData.FILL_HORIZONTAL);
gd.heightHint = TabbedFolderList.SECTION_DIV_HEIGHT;
sash.setLayoutData(gd);
sash.addPaintListener(new PaintListener() {
@Override
public void paintControl(PaintEvent e) {
e.gc.setBackground(folderList.widgetBackground);
e.gc.setForeground(folderList.widgetForeground);
e.gc.fillRectangle(0, 1, e.width, e.height - 2);
e.gc.setForeground(folderList.widgetNormalShadow);
e.gc.drawLine(0, 0, e.width - 1, 0);
e.gc.drawLine(0, e.height - 1, e.width - 1, e.height - 1);
}
});
sash.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
// Resize folders
Rectangle sashBounds = sash.getBounds();
int shift = e.y - sashBounds.y;
if (shift > 0 && shift > getNextFolderPane(FolderPane.this).editorPane.getBounds().height - MIN_PANE_HEIGHT) {
e.doit = false;
return;
}
if (shift < 0 && Math.abs(shift) > editorPane.getBounds().height - MIN_PANE_HEIGHT) {
e.doit = false;
return;
}
if (Math.abs(shift) > 0) {
TabbedFolderComposite.this.setRedraw(false);
try {
shiftPane(FolderPane.this, shift);
} finally {
TabbedFolderComposite.this.setRedraw(true);
}
}
}
});
}
folderList.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
onFolderSwitch(folderList.getElementAt(folderList.getSelectionIndex()).getInfo());
}
});
}
public void setFolders(TabbedFolderInfo[] folders) {
this.folders = folders;
this.folderList.setFolders(this.folders);
}
private void onFolderSwitch(TabbedFolderInfo folder) {
Composite newContent = contentsMap.get(folder);
ITabbedFolder newFolder = folder.getContents();
if (newContent == null) {
newContent = new Composite(editorPane, SWT.NONE);
newContent.setLayoutData(new GridData(GridData.FILL_BOTH));
newContent.setLayout(new FillLayout());
newFolder.createControl(newContent);
contentsMap.put(folder, newContent);
}
// Notify part about hide/show
if (curContent != null && curFolder != null) {
curFolder.aboutToBeHidden();
}
newFolder.aboutToBeShown();
// Make actual hide/show
if (curContent != null && curFolder != null) {
curContent.setVisible(false);
((GridData)curContent.getLayoutData()).exclude = true;
}
((GridData)newContent.getLayoutData()).exclude = false;
newContent.setVisible(true);
// Layout and notify listeners
curContent = newContent;
curFolder = newFolder;
editorPane.layout();
for (ITabbedFolderListener listener : listeners) {
listener.folderSelected(folder.getId());
}
}
}
private void shiftPane(FolderPane curPane, int shift) {
// Set current height to heightHint
for (FolderPane pane : folderPanes) {
Rectangle bounds = pane.editorPane.getBounds();
GridData gd = (GridData) pane.editorPane.getLayoutData();
gd.heightHint = bounds.height;
}
FolderPane nextPane = getNextFolderPane(curPane);
((GridData) curPane.editorPane.getLayoutData()).heightHint += shift;
((GridData) nextPane.editorPane.getLayoutData()).heightHint -= shift;
compositePane.layout();
/*
if (shift < 0) {
// Decrease self size and increase next pane's
//nextPane.editorPane.
} else {
// Increase self size and decrease next pane's
}
*/
}
private FolderPane getNextFolderPane(FolderPane pane) {
for (int i = 0; i < folderPanes.length - 1; i++) {
if (pane == folderPanes[i]) {
return folderPanes[i + 1];
}
}
return null;
}
public TabbedFolderComposite(Composite parent, int style) {
super(parent, style);
GridLayout gl = new GridLayout(2, false);
gl.horizontalSpacing = 0;
gl.verticalSpacing = 0;
gl.marginHeight = 0;
gl.marginWidth = 0;
setLayout(gl);
compositePane = new Composite(this, SWT.NONE);
gl = new GridLayout(2, false);
gl.horizontalSpacing = 0;
gl.verticalSpacing = 0;
gl.marginHeight = 0;
gl.marginWidth = 0;
compositePane.setLayout(gl);
compositePane.setLayoutData(new GridData(GridData.FILL_BOTH));
addTraverseListener(new TraverseListener() {
@Override
public void keyTraversed(TraverseEvent e) {
FolderPane pane = getActiveFolderPane();
if (pane != null) {
pane.folderList.handleTraverse(e);
}
}
});
addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
for (TabbedFolderInfo folderDescription : contentsMap.keySet()) {
folderDescription.getContents().dispose();
}
}
});
}
public void setFolders(@NotNull final TabbedFolderInfo[] folders) {
this.folders = folders;
List<List<TabbedFolderInfo>> groups = new ArrayList<>();
List<TabbedFolderInfo> curGroup = null;
for (TabbedFolderInfo folder : folders) {
if (folder.isEmbeddable()) {
groups.add(curGroup = new ArrayList<>());
curGroup.add(folder);
curGroup = null;
} else {
if (curGroup == null) {
groups.add(curGroup = new ArrayList<>());
}
curGroup.add(folder);
}
}
folderPanes = new FolderPane[groups.size()];
for (int i = 0; i < groups.size(); i++) {
List<TabbedFolderInfo> group = groups.get(i);
FolderPane folderPane = new FolderPane(compositePane, i >= groups.size() - 1);
folderPane.setFolders(group.toArray(new TabbedFolderInfo[group.size()]));
folderPanes[i] = folderPane;
}
// Make all sub folders the same size
int maxWidth = 0;
for (FolderPane folderPane : folderPanes) {
int width = folderPane.folderList.computeSize(-1, -1, false).x;
if (width > maxWidth) {
maxWidth = width;
}
}
for (FolderPane folderPane : folderPanes) {
((GridData)folderPane.folderList.getLayoutData()).widthHint = maxWidth;
}
// Re-layout
compositePane.layout();
}
@Nullable
public TabbedFolderInfo[] getFolders() {
return folders;
}
@Override
public ITabbedFolder getActiveFolder() {
FolderPane pane = getActiveFolderPane();
if (pane != null) {
return getActiveFolder(pane);
}
return null;
}
public FolderPane getActiveFolderPane() {
if (folderPanes.length == 1) {
return folderPanes[0];
}
Control focusControl = getDisplay().getFocusControl();
for (FolderPane folderPane : folderPanes) {
if (UIUtils.isParent(folderPane.editorPane, focusControl)) {
lastActiveFolder = folderPane;
return folderPane;
}
}
if (lastActiveFolder != null) {
return lastActiveFolder;
}
return null;
}
private ITabbedFolder getActiveFolder(FolderPane folderPane) {
TabbedFolderList folderList = folderPane.folderList;
int selectionIndex = folderList.getSelectionIndex();
if (selectionIndex < 0) {
// If no folder was activated - do it now
selectionIndex = 0;
folderList.select(selectionIndex);
}
return folderList.getElementAt(selectionIndex).getInfo().getContents();
}
@Override
public void switchFolder(@Nullable String folderId) {
for (FolderPane folderPane : folderPanes) {
for (int i = 0; i < folderPane.folderList.getNumberOfElements(); i++) {
if (folderId == null || folderPane.folderList.getElementAt(i).getInfo().getId().equals(folderId)) {
folderPane.folderList.select(i);
lastActiveFolder = folderPane;
break;
}
}
}
}
@Override
public void addFolderListener(ITabbedFolderListener listener) {
listeners.add(listener);
}
@Override
public void removeFolderListener(ITabbedFolderListener listener) {
listeners.remove(listener);
}
}