<commit_before>/*
 * Copyright ©1998-2020 by <NAME>. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, version 2.0. If a copy of the MPL was not distributed with
 * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This Source Code Form is "Incompatible With Secondary Licenses", as
 * defined by the Mozilla Public License, version 2.0.
 */

package com.trollworks.gcs.ui.widget.dock;

import com.trollworks.gcs.menu.file.CloseHandler;
import com.trollworks.gcs.menu.file.Saveable;
import com.trollworks.gcs.ui.Colors;
import com.trollworks.gcs.ui.UIUtilities;
import com.trollworks.gcs.ui.border.EmptyBorder;
import com.trollworks.gcs.ui.image.Images;
import com.trollworks.gcs.ui.layout.PrecisionLayout;
import com.trollworks.gcs.ui.layout.PrecisionLayoutData;
import com.trollworks.gcs.ui.widget.DataModifiedListener;
import com.trollworks.gcs.ui.widget.IconButton;
import com.trollworks.gcs.utility.I18n;
import com.trollworks.gcs.utility.text.NumericComparator;
import com.trollworks.gcs.utility.text.Text;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Path2D;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

/** Provides a tab that contains the {@link Dockable}'s icon, title, and close button, if any. */
public class DockTab extends JPanel implements ContainerListener, MouseListener, DragGestureListener, DataModifiedListener, Comparable<DockTab> {
    private Dockable mDockable;
    private JLabel   mTitle;

    /**
     * Creates a new {@link DockTab} for the specified {@link Dockable}.
     *
     * @param dockable The {@link Dockable} to work with.
     */
    public DockTab(Dockable dockable) {
        super(new PrecisionLayout().setMargins(2, 4, 2, 4).setMiddleVerticalAlignment());
        mDockable = dockable;
        setOpaque(false);
        setBorder(new EmptyBorder(2, 1, 0, 1));
        addContainerListener(this);
        mTitle = new JLabel(getFullTitle(), dockable.getTitleIcon(), SwingConstants.LEFT);
        IconButton closeButton = new IconButton(Images.DOCK_CLOSE, I18n.Text("Close"), this::attemptClose);
        add(mTitle, new PrecisionLayoutData().setGrabHorizontalSpace(true).setHeightHint(Math.max(mTitle.getPreferredSize().height, closeButton.getPreferredSize().height)));
        if (dockable instanceof CloseHandler) {
            add(closeButton, new PrecisionLayoutData().setEndHorizontalAlignment());
        }
        if (dockable instanceof Saveable) {
            ((Saveable) dockable).addDataModifiedListener(this);
        }
        addMouseListener(this);
        setToolTipText(Text.wrapPlainTextForToolTip(dockable.getTitleTooltip()));
        DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this);
    }

    /**
     * @return {@code true} if this {@link DockTab} is the current one for the {@link
     *         DockContainer}.
     */
    public boolean isCurrent() {
        DockContainer dc = getDockContainer();
        return dc != null && dc.getCurrentDockable() == mDockable;
    }

    /** @return The {@link Dockable} this tab represents. */
    public Dockable getDockable() {
        return mDockable;
    }

    /** @return The icon used by the tab. */
    public Icon getIcon() {
        return mDockable.getTitleIcon();
    }

    /** @return The full title used by the tab. */
    public String getFullTitle() {
        StringBuilder buffer = new StringBuilder();
        if (mDockable instanceof Saveable) {
            if (((Saveable) mDockable).isModified()) {
                buffer.append('*');
            }
        }
        buffer.append(mDockable.getTitle());
        return buffer.toString();
    }

    /** Update the title and icon from the {@link Dockable}. */
    public void updateTitle() {
        mTitle.setText(getFullTitle());
        mTitle.setIcon(mDockable.getTitleIcon());
        mTitle.revalidate();
    }

    @Override
    protected void paintComponent(Graphics g) {
        Insets        insets = getInsets();
        Path2D.Double path   = new Path2D.Double();
        int           bottom = getHeight();
        path.moveTo(0, bottom);
        path.lineTo(0, 6);
        path.curveTo(0, 6, 0, 1, 6, 1);
        int width = getWidth();
        path.lineTo(width - 7, 1);
        path.curveTo(width - 7, 1, width - 1, 1, width - 1, 7);
        path.lineTo(width - 1, bottom);
        DockContainer dc   = getDockContainer();
        Color         base = DockColors.BACKGROUND;
        if (dc != null) {
            if (dc.getCurrentDockable() == mDockable) {
                base = dc.isActive() ? DockColors.ACTIVE_TAB_BACKGROUND : DockColors.CURRENT_TAB_BACKGROUND;
            }
        }
        Graphics2D gc = (Graphics2D) g;
        gc.setPaint(new GradientPaint(new Point(insets.left, insets.top), base, new Point(insets.left, getHeight() - (insets.top + insets.bottom)), Colors.adjustBrightness(base, -0.1f)));<commit_msg>Fix for #266: update tooltip when tab name changes<commit_after>/*
 * Copyright ©1998-2020 by <NAME>. All rights reserved.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, version 2.0. If a copy of the MPL was not distributed with
 * this file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This Source Code Form is "Incompatible With Secondary Licenses", as
 * defined by the Mozilla Public License, version 2.0.
 */

package com.trollworks.gcs.ui.widget.dock;

import com.trollworks.gcs.menu.file.CloseHandler;
import com.trollworks.gcs.menu.file.Saveable;
import com.trollworks.gcs.ui.Colors;
import com.trollworks.gcs.ui.UIUtilities;
import com.trollworks.gcs.ui.border.EmptyBorder;
import com.trollworks.gcs.ui.image.Images;
import com.trollworks.gcs.ui.layout.PrecisionLayout;
import com.trollworks.gcs.ui.layout.PrecisionLayoutData;
import com.trollworks.gcs.ui.widget.DataModifiedListener;
import com.trollworks.gcs.ui.widget.IconButton;
import com.trollworks.gcs.utility.I18n;
import com.trollworks.gcs.utility.text.NumericComparator;
import com.trollworks.gcs.utility.text.Text;

import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.LayoutManager;
import java.awt.Point;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.event.ContainerEvent;
import java.awt.event.ContainerListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Path2D;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;

/** Provides a tab that contains the {@link Dockable}'s icon, title, and close button, if any. */
public class DockTab extends JPanel implements ContainerListener, MouseListener, DragGestureListener, DataModifiedListener, Comparable<DockTab> {
    private Dockable mDockable;
    private JLabel   mTitle;

    /**
     * Creates a new {@link DockTab} for the specified {@link Dockable}.
     *
     * @param dockable The {@link Dockable} to work with.
     */
    public DockTab(Dockable dockable) {
        super(new PrecisionLayout().setMargins(2, 4, 2, 4).setMiddleVerticalAlignment());
        mDockable = dockable;
        setOpaque(false);
        setBorder(new EmptyBorder(2, 1, 0, 1));
        addContainerListener(this);
        mTitle = new JLabel(getFullTitle(), dockable.getTitleIcon(), SwingConstants.LEFT);
        IconButton closeButton = new IconButton(Images.DOCK_CLOSE, I18n.Text("Close"), this::attemptClose);
        add(mTitle, new PrecisionLayoutData().setGrabHorizontalSpace(true).setHeightHint(Math.max(mTitle.getPreferredSize().height, closeButton.getPreferredSize().height)));
        if (dockable instanceof CloseHandler) {
            add(closeButton, new PrecisionLayoutData().setEndHorizontalAlignment());
        }
        if (dockable instanceof Saveable) {
            ((Saveable) dockable).addDataModifiedListener(this);
        }
        addMouseListener(this);
        setToolTipText(Text.wrapPlainTextForToolTip(dockable.getTitleTooltip()));
        DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this);
    }

    /**
     * @return {@code true} if this {@link DockTab} is the current one for the {@link
     *         DockContainer}.
     */
    public boolean isCurrent() {
        DockContainer dc = getDockContainer();
        return dc != null && dc.getCurrentDockable() == mDockable;
    }

    /** @return The {@link Dockable} this tab represents. */
    public Dockable getDockable() {
        return mDockable;
    }

    /** @return The icon used by the tab. */
    public Icon getIcon() {
        return mDockable.getTitleIcon();
    }

    /** @return The full title used by the tab. */
    public String getFullTitle() {
        StringBuilder buffer = new StringBuilder();
        if (mDockable instanceof Saveable) {
            if (((Saveable) mDockable).isModified()) {
                buffer.append('*');
            }
        }
        buffer.append(mDockable.getTitle());
        return buffer.toString();
    }

    /** Update the title and icon from the {@link Dockable}. */
    public void updateTitle() {
        mTitle.setText(getFullTitle());
        mTitle.setIcon(mDockable.getTitleIcon());
        setToolTipText(Text.wrapPlainTextForToolTip(mDockable.getTitleTooltip()));
        mTitle.revalidate();
    }

    @Override
    protected void paintComponent(Graphics g) {
        Insets        insets = getInsets();
        Path2D.Double path   = new Path2D.Double();
        int           bottom = getHeight();
        path.moveTo(0, bottom);
        path.lineTo(0, 6);
        path.curveTo(0, 6, 0, 1, 6, 1);
        int width = getWidth();
        path.lineTo(width - 7, 1);
        path.curveTo(width - 7, 1, width - 1, 1, width - 1, 7);
        path.lineTo(width - 1, bottom);
        DockContainer dc   = getDockContainer();
        Color         base = DockColors.BACKGROUND;
        if (dc != null) {
            if (dc.getCurrentDockable() == mDockable) {
                base = dc.isActive() ? DockColors.ACTIVE_TAB_BACKGROUND : DockColors.CURRENT_TAB_BACKGROUND;
            }
        }
        Graphics2D gc = (Graphics2D) g;
        gc.setPaint(new GradientPaint(new Point(insets.left, insets.top), base, new Point(insets.left, getHeight() - (insets.top + insets.bottom)), Colors.adjustBrightness(base, -0.1f)));
{'fraction_non_alphanumeric': 0.11618893095366865, 'fraction_numerical': 0.009644853073733549, 'mean_word_length': 3.1773343373493974, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6000966', 'n_tokens_mistral': 3524, 'n_tokens_neox': 3380, 'n_words': 796}