/* * Copyright 2000-2014 JetBrains s.r.o. * * 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.intellij.ui.tabs.impl; import com.intellij.ide.ui.UISettings; import com.intellij.openapi.Disposable; import com.intellij.openapi.actionSystem.ActionManager; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.registry.Registry; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.IdeFocusManager; import com.intellij.ui.tabs.JBTabsPosition; import com.intellij.ui.tabs.TabInfo; import com.intellij.ui.tabs.TabsUtil; import com.intellij.ui.tabs.impl.singleRow.ScrollableSingleRowLayout; import com.intellij.ui.tabs.impl.singleRow.SingleRowLayout; import com.intellij.ui.tabs.impl.table.TableLayout; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.awt.*; import java.util.Collections; import java.util.Comparator; import java.util.List; /** * @author pegov */ public class JBEditorTabs extends JBTabsImpl { public static final String TABS_ALPHABETICAL_KEY = "tabs.alphabetical"; static final String TABS_SHORTEN_TITLE_IF_NEED = "tabs.shorten.title.if.need"; private JBEditorTabsPainter myDarkPainter = new DarculaEditorTabsPainter(); private JBEditorTabsPainter myDefaultPainter = new DefaultEditorTabsPainter(); public JBEditorTabs(@Nullable Project project, @NotNull ActionManager actionManager, IdeFocusManager focusManager, @NotNull Disposable parent) { super(project, actionManager, focusManager, parent); } @Override protected SingleRowLayout createSingleRowLayout() { if (ApplicationManager.getApplication().isInternal() || Registry.is("editor.use.scrollable.tabs")) { return new ScrollableSingleRowLayout(this); } return super.createSingleRowLayout(); } @Override protected TabLabel createTabLabel(TabInfo info) { TabLabel label = super.createTabLabel(info); label.putClientProperty(TABS_SHORTEN_TITLE_IF_NEED, Boolean.TRUE); return label; } @Override public boolean isEditorTabs() { return true; } @Override protected void paintFirstGhost(Graphics2D g2d) { } @Override protected void paintLastGhost(Graphics2D g2d) { } @Override public boolean isGhostsAlwaysVisible() { return false; } @Override public boolean useSmallLabels() { return UISettings.getInstance().USE_SMALL_LABELS_ON_TABS; } @Override public boolean useBoldLabels() { return SystemInfo.isMac && Registry.is("ide.mac.boldEditorTabs"); } @Override public boolean hasUnderline() { return true; } protected void doPaintInactive(Graphics2D g2d, boolean leftGhostExists, TabLabel label, Rectangle effectiveBounds, boolean rightGhostExists, int row, int column) { Insets insets = getTabsBorder().getEffectiveBorder(); int _x = effectiveBounds.x + insets.left; int _y = effectiveBounds.y + insets.top; int _width = effectiveBounds.width - insets.left - insets.right + (getTabsPosition() == JBTabsPosition.right ? 1 : 0); int _height = effectiveBounds.height - insets.top - insets.bottom; if ((!isSingleRow() /* for multiline */) || (isSingleRow() && isHorizontalTabs())) { if (isSingleRow() && getPosition() == JBTabsPosition.bottom) { _y += TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT; } else { if (isSingleRow()) { _height -= TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT; } else { TabInfo info = label.getInfo(); if (((TableLayout)getEffectiveLayout()).isLastRow(info)) { _height -= TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT; } } } } final boolean vertical = getTabsPosition() == JBTabsPosition.left || getTabsPosition() == JBTabsPosition.right; final Color tabColor = label.getInfo().getTabColor(); getPainter().doPaintInactive(g2d, effectiveBounds, _x, _y, _width, _height, tabColor, row, column, vertical); } JBEditorTabsPainter getPainter() { return UIUtil.isUnderDarcula() ? myDarkPainter : myDefaultPainter; } public boolean isAlphabeticalMode() { return Registry.is(TABS_ALPHABETICAL_KEY); } public static void setAlphabeticalMode(boolean on) { Registry.get(TABS_ALPHABETICAL_KEY).setValue(on); } @Override protected void doPaintBackground(Graphics2D g2d, Rectangle clip) { List visibleInfos = getVisibleInfos(); if (isAlphabeticalMode()) { Collections.sort(visibleInfos, new Comparator() { @Override public int compare(TabInfo o1, TabInfo o2) { return StringUtil.naturalCompare(o1.getText(), o2.getText()); } }); } final boolean vertical = getTabsPosition() == JBTabsPosition.left || getTabsPosition() == JBTabsPosition.right; Insets insets = getTabsBorder().getEffectiveBorder(); int maxOffset = 0; int maxLength = 0; for (int i = visibleInfos.size() - 1; i >= 0; i--) { TabInfo visibleInfo = visibleInfos.get(i); TabLabel tabLabel = myInfo2Label.get(visibleInfo); Rectangle r = tabLabel.getBounds(); if (r.width == 0 || r.height == 0) continue; maxOffset = vertical ? r.y + r.height : r.x + r.width; maxLength = vertical ? r.width : r.height; break; } maxOffset++; Rectangle r2 = getBounds(); Rectangle rectangle; if (vertical) { rectangle = new Rectangle(insets.left, maxOffset, getWidth(), r2.height - maxOffset - insets.top - insets.bottom); } else { int y = r2.y + insets.top; int height = maxLength - insets.top - insets.bottom; if (getTabsPosition() == JBTabsPosition.bottom) { y = r2.height - height - insets.top + TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT; } else { height -= TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT; } rectangle = new Rectangle(maxOffset, y, r2.width - maxOffset - insets.left - insets.right, height); } getPainter().doPaintBackground(g2d, clip, vertical, rectangle); } protected void paintSelectionAndBorder(Graphics2D g2d) { if (getSelectedInfo() == null) return; TabLabel label = getSelectedLabel(); Rectangle r = label.getBounds(); ShapeInfo selectedShape = _computeSelectedLabelShape(); Insets insets = getTabsBorder().getEffectiveBorder(); Color tabColor = label.getInfo().getTabColor(); final boolean isHorizontalTabs = isHorizontalTabs(); getPainter().paintSelectionAndBorder(g2d, r, selectedShape, insets, tabColor, isHorizontalTabs); } @Override public Color getBackground() { return getPainter().getBackgroundColor(); } public Color getForeground() { return UIUtil.getLabelForeground(); } protected ShapeInfo _computeSelectedLabelShape() { final ShapeInfo shape = new ShapeInfo(); shape.path = getEffectiveLayout().createShapeTransform(getSize()); shape.insets = shape.path.transformInsets(getLayoutInsets()); shape.labelPath = shape.path.createTransform(getSelectedLabel().getBounds()); shape.labelBottomY = shape.labelPath.getMaxY() - shape.labelPath.deltaY(TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT - 1); shape.labelTopY = shape.labelPath.getY() + (getPosition() == JBTabsPosition.top || getPosition() == JBTabsPosition.bottom ? shape.labelPath.deltaY(1) : 0) ; shape.labelLeftX = shape.labelPath.getX() + (getPosition() == JBTabsPosition.top || getPosition() == JBTabsPosition.bottom ? 0 : shape.labelPath.deltaX( 1)); shape.labelRightX = shape.labelPath.getMaxX() - shape.labelPath.deltaX(1); int leftX = shape.insets.left + (getPosition() == JBTabsPosition.top || getPosition() == JBTabsPosition.bottom ? 0 : shape.labelPath.deltaX(1)); shape.path.moveTo(leftX, shape.labelBottomY); shape.path.lineTo(shape.labelLeftX, shape.labelBottomY); shape.path.lineTo(shape.labelLeftX, shape.labelTopY); shape.path.lineTo(shape.labelRightX, shape.labelTopY); shape.path.lineTo(shape.labelRightX, shape.labelBottomY); int lastX = shape.path.getWidth() - shape.path.deltaX(shape.insets.right); shape.path.lineTo(lastX, shape.labelBottomY); shape.path.lineTo(lastX, shape.labelBottomY + shape.labelPath.deltaY(TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT - 1)); shape.path.lineTo(leftX, shape.labelBottomY + shape.labelPath.deltaY(TabsUtil.ACTIVE_TAB_UNDERLINE_HEIGHT - 1)); shape.path.closePath(); shape.fillPath = shape.path.copy(); return shape; } }