/* * Copyright (c) 1998-2017 by Richard A. Wilkes. 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.template; import com.trollworks.gcs.advantage.Advantage; import com.trollworks.gcs.advantage.AdvantageOutline; import com.trollworks.gcs.character.GURPSCharacter; import com.trollworks.gcs.equipment.Equipment; import com.trollworks.gcs.equipment.EquipmentOutline; import com.trollworks.gcs.notes.Note; import com.trollworks.gcs.notes.NoteOutline; import com.trollworks.gcs.preferences.SheetPreferences; import com.trollworks.gcs.skill.Skill; import com.trollworks.gcs.skill.SkillOutline; import com.trollworks.gcs.spell.Spell; import com.trollworks.gcs.spell.SpellOutline; import com.trollworks.gcs.widgets.outline.ListRow; import com.trollworks.toolkit.annotation.Localize; import com.trollworks.toolkit.io.Log; import com.trollworks.toolkit.ui.UIUtilities; import com.trollworks.toolkit.ui.border.EmptyBorder; import com.trollworks.toolkit.ui.layout.ColumnLayout; import com.trollworks.toolkit.ui.scale.Scale; import com.trollworks.toolkit.ui.scale.ScaleRoot; import com.trollworks.toolkit.ui.widget.outline.Outline; import com.trollworks.toolkit.ui.widget.outline.OutlineHeader; import com.trollworks.toolkit.ui.widget.outline.OutlineSyncer; import com.trollworks.toolkit.ui.widget.outline.Row; import com.trollworks.toolkit.ui.widget.outline.RowSelection; import com.trollworks.toolkit.utility.Localization; import com.trollworks.toolkit.utility.notification.BatchNotifierTarget; import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Rectangle; import java.awt.dnd.DnDConstants; import java.awt.dnd.DropTarget; import java.awt.dnd.DropTargetDragEvent; import java.awt.dnd.DropTargetDropEvent; import java.awt.dnd.DropTargetEvent; import java.awt.dnd.DropTargetListener; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.ArrayList; import javax.swing.JPanel; import javax.swing.Scrollable; import javax.swing.SwingConstants; /** The template sheet. */ public class TemplateSheet extends JPanel implements Scrollable, BatchNotifierTarget, DropTargetListener, ActionListener, ScaleRoot { @Localize("Advantages, Disadvantages & Quirks") @Localize(locale = "de", value = "Vorteile, Nachteile & Marotten") @Localize(locale = "ru", value = "Преимущества, недостатки и причуды") @Localize(locale = "es", value = "Ventajas, Desventajas y Singularidades") private static String ADVANTAGES; @Localize("Skills") @Localize(locale = "de", value = "Fertigkeiten") @Localize(locale = "ru", value = "Умения") @Localize(locale = "es", value = "Habilidades") private static String SKILLS; @Localize("Spells") @Localize(locale = "de", value = "Zauber") @Localize(locale = "ru", value = "Заклинания") @Localize(locale = "es", value = "Sortilegios") private static String SPELLS; @Localize("Equipment") @Localize(locale = "de", value = "Ausrüstung") @Localize(locale = "ru", value = "Снаряжение") @Localize(locale = "es", value = "Equipo") private static String EQUIPMENT; @Localize("Notes") @Localize(locale = "de", value = "Notizen") @Localize(locale = "ru", value = "Заметка") @Localize(locale = "es", value = "Notas") private static String NOTES; static { Localization.initialize(); } private static final EmptyBorder NORMAL_BORDER = new EmptyBorder(5); private Scale mScale; private Template mTemplate; private boolean mBatchMode; private AdvantageOutline mAdvantageOutline; private SkillOutline mSkillOutline; private SpellOutline mSpellOutline; private EquipmentOutline mEquipmentOutline; private NoteOutline mNoteOutline; /** Used to determine whether an edit cell is pending. */ protected boolean mStartEditingPending; /** Used to determine whether a resize action is pending. */ protected boolean mSizePending; /** * Creates a new {@link TemplateSheet}. * * @param template The template to display the data for. */ public TemplateSheet(Template template) { super(new ColumnLayout(1, 0, 5)); setOpaque(true); setBackground(Color.WHITE); setBorder(NORMAL_BORDER); mScale = SheetPreferences.getInitialUIScale().getScale(); mTemplate = template; // Make sure our primary outlines exist mAdvantageOutline = new AdvantageOutline(mTemplate); mSkillOutline = new SkillOutline(mTemplate); mSpellOutline = new SpellOutline(mTemplate); mEquipmentOutline = new EquipmentOutline(mTemplate); mNoteOutline = new NoteOutline(mTemplate); add(new TemplateOutlinePanel(mAdvantageOutline, ADVANTAGES)); add(new TemplateOutlinePanel(mSkillOutline, SKILLS)); add(new TemplateOutlinePanel(mSpellOutline, SPELLS)); add(new TemplateOutlinePanel(mEquipmentOutline, EQUIPMENT)); add(new TemplateOutlinePanel(mNoteOutline, NOTES)); mAdvantageOutline.addActionListener(this); mSkillOutline.addActionListener(this); mSpellOutline.addActionListener(this); mEquipmentOutline.addActionListener(this); mNoteOutline.addActionListener(this); // Ensure everything is laid out and register for notification revalidate(); mTemplate.addTarget(this, Template.TEMPLATE_PREFIX, GURPSCharacter.CHARACTER_PREFIX); setDropTarget(new DropTarget(this, this)); runAdjustSize(); } @Override public Scale getScale() { return mScale; } @Override public void setScale(Scale scale) { if (mScale.getScale() != scale.getScale()) { mScale = scale; revalidate(); repaint(); } } @Override public void actionPerformed(ActionEvent event) { String command = event.getActionCommand(); if (Outline.CMD_POTENTIAL_CONTENT_SIZE_CHANGE.equals(command)) { adjustSize(); } } private void adjustSize() { if (!mSizePending) { mSizePending = true; EventQueue.invokeLater(new Runnable() { @Override public void run() { runAdjustSize(); } }); } } void runAdjustSize() { Scale scale = Scale.get(this); mSizePending = false; Dimension size = getLayout().preferredLayoutSize(TemplateSheet.this); size.width = scale.scale(8 * 72); int min = scale.scale(4 * 72); if (size.height < min) { size.height = min; } UIUtilities.setOnlySize(TemplateSheet.this, size); setSize(size); } /** * Prepares the specified outline for embedding in the sheet. * * @param outline The outline to prepare. */ public static void prepOutline(Outline outline) { OutlineHeader header = outline.getHeaderPanel(); outline.setDynamicRowHeight(true); outline.setAllowColumnDrag(false); outline.setAllowColumnResize(false); outline.setAllowColumnContextMenu(false); header.setIgnoreResizeOK(true); header.setBackground(Color.black); header.setTopDividerColor(Color.black); } /** @return The outline containing the Advantages, Disadvantages & Quirks. */ public AdvantageOutline getAdvantageOutline() { return mAdvantageOutline; } /** @return The outline containing the skills. */ public SkillOutline getSkillOutline() { return mSkillOutline; } /** @return The outline containing the spells. */ public SpellOutline getSpellOutline() { return mSpellOutline; } /** @return The outline containing the equipment. */ public EquipmentOutline getEquipmentOutline() { return mEquipmentOutline; } /** @return The outline containing the notes equipment. */ public NoteOutline getNoteOutline() { return mNoteOutline; } @Override public void enterBatchMode() { mBatchMode = true; } @Override public void leaveBatchMode() { mBatchMode = false; validate(); } @Override public void handleNotification(Object producer, String type, Object data) { if (type.startsWith(Advantage.PREFIX)) { OutlineSyncer.add(mAdvantageOutline); } else if (type.startsWith(Skill.PREFIX)) { OutlineSyncer.add(mSkillOutline); } else if (type.startsWith(Spell.PREFIX)) { OutlineSyncer.add(mSpellOutline); } else if (type.startsWith(Equipment.PREFIX)) { OutlineSyncer.add(mEquipmentOutline); } else if (type.startsWith(Note.PREFIX)) { OutlineSyncer.add(mNoteOutline); } if (!mBatchMode) { validate(); } } @Override public int getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { return orientation == SwingConstants.VERTICAL ? visibleRect.height : visibleRect.width; } @Override public Dimension getPreferredScrollableViewportSize() { return getPreferredSize(); } @Override public int getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { return 10; } @Override public boolean getScrollableTracksViewportHeight() { return false; } @Override public boolean getScrollableTracksViewportWidth() { return false; } private boolean mDragWasAcceptable; private ArrayList<Row> mDragRows; @Override public void dragEnter(DropTargetDragEvent dtde) { mDragWasAcceptable = false; try { if (dtde.isDataFlavorSupported(RowSelection.DATA_FLAVOR)) { Row[] rows = (Row[]) dtde.getTransferable().getTransferData(RowSelection.DATA_FLAVOR); if (rows != null && rows.length > 0) { mDragRows = new ArrayList<>(rows.length); for (Row element : rows) { if (element instanceof ListRow) { mDragRows.add(element); } } if (!mDragRows.isEmpty()) { mDragWasAcceptable = true; dtde.acceptDrag(DnDConstants.ACTION_MOVE); } } } } catch (Exception exception) { Log.error(exception); } if (!mDragWasAcceptable) { dtde.rejectDrag(); } } @Override public void dragOver(DropTargetDragEvent dtde) { if (mDragWasAcceptable) { dtde.acceptDrag(DnDConstants.ACTION_MOVE); } else { dtde.rejectDrag(); } } @Override public void dropActionChanged(DropTargetDragEvent dtde) { if (mDragWasAcceptable) { dtde.acceptDrag(DnDConstants.ACTION_MOVE); } else { dtde.rejectDrag(); } } @Override public void drop(DropTargetDropEvent dtde) { dtde.acceptDrop(dtde.getDropAction()); UIUtilities.getAncestorOfType(this, TemplateDockable.class).addRows(mDragRows); mDragRows = null; dtde.dropComplete(true); } @Override public void dragExit(DropTargetEvent dte) { mDragRows = null; } @Override public int getNotificationPriority() { return 0; } }