/*
* 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.editors.binary.pref;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Font;
import org.eclipse.swt.graphics.FontData;
import org.eclipse.swt.graphics.GC;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.*;
import org.eclipse.swt.widgets.List;
import org.jkiss.dbeaver.core.CoreMessages;
import org.jkiss.dbeaver.core.DBeaverUI;
import org.jkiss.dbeaver.ui.UIUtils;
import org.jkiss.dbeaver.ui.editors.binary.HexEditControl;
import java.util.*;
/**
* Manager of all preferences-editing widgets, with an optional standalone dialog.
*
* @author Jordi
*/
public class HexPreferencesManager {
static final int itemsDisplayed = 9; // Number of font names displayed in list
static final java.util.Set<Integer> scalableSizes = new TreeSet<>(
Arrays.asList(6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 22, 32, 72));
private static final String TEXT_BOLD = CoreMessages.editor_binary_hex_font_style_bold;
private static final String TEXT_BOLD_ITALIC = CoreMessages.editor_binary_hex_font_style_bold_italic;
private static final String TEXT_ITALIC = CoreMessages.editor_binary_hex_font_style_italic;
private static final String TEXT_REGULAR = CoreMessages.editor_binary_hex_font_style_regular;
public static final String SAMPLE_TEXT = CoreMessages.editor_binary_hex_sample_text;
private java.util.List<FontData> fontsListCurrent = null;
private java.util.List<FontData> fontsNonScalable = null;
private java.util.List<FontData> fontsScalable = null;
private GC fontsGc = null;
private java.util.Set<String> fontsRejected = null;
private java.util.Map<String, Set<Integer>> fontsSorted = null;
private FontData sampleFontData = null;
private Composite composite = null;
private Composite parent = null;
private Text text = null;
private Text text1 = null;
private Text text2 = null;
private List list = null;
private List list1 = null;
private List list2 = null;
private Font sampleFont = null;
private Text sampleText = null;
static int fontStyleToInt(String styleString)
{
int style = SWT.NORMAL;
if (TEXT_BOLD.equals(styleString))
style = SWT.BOLD;
else if (TEXT_ITALIC.equals(styleString))
style = SWT.ITALIC;
else if (TEXT_BOLD_ITALIC.equals(styleString))
style = SWT.BOLD | SWT.ITALIC;
return style;
}
static String fontStyleToString(int style)
{
switch (style) {
case SWT.BOLD:
return TEXT_BOLD;
case SWT.ITALIC:
return TEXT_ITALIC;
case SWT.BOLD | SWT.ITALIC:
return TEXT_BOLD_ITALIC;
default:
return TEXT_REGULAR;
}
}
public HexPreferencesManager(FontData aFontData)
{
sampleFontData = aFontData;
fontsSorted = new TreeMap<>();
}
/**
* Creates all internal widgets
*/
void createComposite()
{
composite = new Composite(parent, SWT.NONE);
composite.setLayout(new FillLayout());
Group group = new Group(composite, SWT.NONE);
group.setText(CoreMessages.editor_binary_hex_froup_font_selection);
group.setVisible(true);
GridLayout gridLayout = new GridLayout();
gridLayout.numColumns = 3;
group.setLayout(gridLayout);
Label label = UIUtils.createControlLabel(group, CoreMessages.editor_binary_hex_label_available_fix_width_fonts);
label.setVisible(true);
GridData gridData = new GridData();
gridData.horizontalSpan = 3;
label.setLayoutData(gridData);
UIUtils.createControlLabel(group, CoreMessages.editor_binary_hex_label_name);
UIUtils.createControlLabel(group, CoreMessages.editor_binary_hex_label_style);
UIUtils.createControlLabel(group, CoreMessages.editor_binary_hex_label_size);
text = new Text(group, SWT.SINGLE | SWT.BORDER);
GridData gridData4 = new GridData();
gridData4.horizontalAlignment = org.eclipse.swt.layout.GridData.FILL;
text.setLayoutData(gridData4);
text1 = new Text(group, SWT.BORDER);
GridData gridData5 = new GridData();
gridData5.horizontalAlignment = org.eclipse.swt.layout.GridData.FILL;
text1.setLayoutData(gridData5);
text1.setEnabled(false);
text2 = new Text(group, SWT.BORDER);
GridData gridData6 = new GridData();
gridData6.horizontalAlignment = org.eclipse.swt.layout.GridData.FILL;
GC gc = new GC(group);
int averageCharWidth = gc.getFontMetrics().getAverageCharWidth();
gc.dispose();
gridData6.widthHint = averageCharWidth * 6;
text2.setLayoutData(gridData6);
list = new List(group, SWT.SINGLE | SWT.V_SCROLL | SWT.BORDER);
GridData gridData52 = new GridData();
gridData52.heightHint = itemsDisplayed * list.getItemHeight();
gridData52.widthHint = averageCharWidth * 40;
list.setLayoutData(gridData52);
list.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e)
{
text.setText(list.getSelection()[0]);
updateSizeItemsAndGuessSelected();
updateAndRefreshSample();
}
});
list1 = new List(group, SWT.SINGLE | SWT.BORDER);
GridData gridData21 = new GridData();
gridData21.verticalAlignment = org.eclipse.swt.layout.GridData.FILL;
gridData21.widthHint = averageCharWidth * TEXT_BOLD_ITALIC.length() * 2;
list1.setLayoutData(gridData21);
list1.setItems(new String[]{TEXT_REGULAR, TEXT_BOLD, TEXT_ITALIC, TEXT_BOLD_ITALIC});
list1.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e)
{
text1.setText(list1.getSelection()[0]);
updateAndRefreshSample();
}
});
list2 = new List(group, SWT.SINGLE | SWT.V_SCROLL | SWT.BORDER);
GridData gridData7 = new GridData();
gridData7.widthHint = gridData6.widthHint;
gridData7.heightHint = gridData52.heightHint;
list2.setLayoutData(gridData7);
list2.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e)
{
text2.setText(list2.getSelection()[0]);
updateAndRefreshSample();
}
});
sampleText = new Text(group, SWT.MULTI | SWT.WRAP | SWT.V_SCROLL | SWT.READ_ONLY | SWT.BORDER);
sampleText.setText(SAMPLE_TEXT);
sampleText.setEditable(false);
GridData gridData8 = new GridData();
gridData8.horizontalSpan = 3;
gridData8.widthHint = gridData52.widthHint + gridData21.widthHint + gridData7.widthHint +
gridLayout.horizontalSpacing * 2;
gridData8.heightHint = 50;
gridData8.horizontalAlignment = org.eclipse.swt.layout.GridData.FILL;
sampleText.setLayoutData(gridData8);
sampleText.addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e)
{
if (sampleFont != null && !sampleFont.isDisposed()) {
sampleFont.dispose();
}
}
});
}
/**
* Creates the part containing all preferences-editing widgets, that is, ok and cancel
* buttons are left out so we can call this method from both standalone and plugin.
*
* @param aParent composite where preferences will be drawn
*/
public Composite createPreferencesPart(Composite aParent)
{
parent = aParent;
createComposite();
if (fontsSorted.size() < 1) {
populateFixedCharWidthFonts();
} else {
list.setItems(fontsSorted.keySet().toArray(new String[fontsSorted.keySet().size()]));
refreshWidgets();
}
return composite;
}
/**
* Get the preferred font data
*
* @return a copy of the preferred font data
*/
public FontData getFontData()
{
return new FontData(
sampleFontData.getName(),
sampleFontData.getHeight(),
sampleFontData.getStyle());
}
FontData getNextFontData()
{
if (fontsListCurrent.size() == 0) {
fontsListCurrent = fontsScalable;
}
FontData aData = fontsListCurrent.get(0);
fontsListCurrent.remove(0);
while (fontsRejected.contains(aData.getName()) && fontsScalable.size() > 0) {
if (fontsListCurrent.size() == 0) {
fontsListCurrent = fontsScalable;
}
aData = fontsListCurrent.get(0);
fontsListCurrent.remove(0);
}
return aData;
}
int getSize()
{
int size = 0;
if (!"".equals(text2.getText())) { //$NON-NLS-1$
try {
size = Integer.parseInt(text2.getText());
}
catch (NumberFormatException e) {
// do nothing
} // was not a number, keep it 0
}
// bugfix: HexText's raw array overflows when font is very small and window very big
// very small sizes would compromise responsiveness in large windows, and they are too small
// to see anyway
if (size == 1 || size == 2) size = 3;
return size;
}
void populateFixedCharWidthFonts()
{
fontsNonScalable = new ArrayList<>(Arrays.asList(Display.getCurrent().getFontList(null, false)));
fontsScalable = new ArrayList<>(Arrays.asList(Display.getCurrent().getFontList(null, true)));
if (fontsNonScalable.size() == 0 && fontsScalable.size() == 0) {
fontsNonScalable = null;
fontsScalable = null;
return;
}
fontsListCurrent = fontsNonScalable;
fontsRejected = new HashSet<>();
fontsGc = new GC(parent);
DBeaverUI.asyncExec(new Runnable() {
@Override
public void run() {
populateFixedCharWidthFontsAsync();
}
});
}
void populateFixedCharWidthFontsAsync()
{
FontData fontData = getNextFontData();
if (!fontsRejected.contains(fontData.getName())) {
boolean isScalable = fontsListCurrent == fontsScalable;
int height = 10;
if (!isScalable) height = fontData.getHeight();
Font font = new Font(Display.getCurrent(), fontData.getName(), height, SWT.NORMAL);
fontsGc.setFont(font);
int width = fontsGc.getAdvanceWidth((char) 0x020);
boolean isFixedWidth = true;
for (int j = 0x021; j < 0x0100 && isFixedWidth; ++j) {
if (((char)j) == '.' && j != '.') continue;
if (width != fontsGc.getAdvanceWidth((char) j)) isFixedWidth = false;
}
font.dispose();
if (isFixedWidth) {
if (isScalable) {
fontsSorted.put(fontData.getName(), scalableSizes);
} else {
Set<Integer> heights = fontsSorted.get(fontData.getName());
if (heights == null) {
heights = new TreeSet<>();
fontsSorted.put(fontData.getName(), heights);
}
heights.add(fontData.getHeight());
}
if (!list.isDisposed())
list.setItems(fontsSorted.keySet().toArray(new String[fontsSorted.keySet().size()]));
refreshWidgets();
} else {
fontsRejected.add(fontData.getName());
}
}
if (fontsNonScalable.size() == 0 && fontsScalable.size() == 0) {
if (!parent.isDisposed()) fontsGc.dispose();
fontsGc = null;
fontsNonScalable = fontsScalable = fontsListCurrent = null;
fontsRejected = null;
} else {
DBeaverUI.asyncExec(new Runnable() {
@Override
public void run() {
populateFixedCharWidthFontsAsync();
}
});
}
}
void refreshSample()
{
if (sampleFont != null && !sampleFont.isDisposed()) {
sampleFont.dispose();
}
sampleFont = new Font(Display.getCurrent(), sampleFontData);
sampleText.setFont(sampleFont);
}
void refreshWidgets()
{
if (composite.isDisposed())
return;
if (fontsSorted == null || !fontsSorted.containsKey(sampleFontData.getName())) {
text.setText(CoreMessages.editor_binary_hex_default_font);
} else {
text.setText(sampleFontData.getName());
}
showSelected(list, sampleFontData.getName());
text1.setText(fontStyleToString(sampleFontData.getStyle()));
list1.select(list1.indexOf(fontStyleToString(sampleFontData.getStyle())));
updateSizeItems();
text2.setText(Integer.toString(sampleFontData.getHeight()));
showSelected(list2, Integer.toString(sampleFontData.getHeight()));
refreshSample();
}
/**
* Set preferences to show a font. Use null to show default font.
*
* @param aFontData the font to be shown.
*/
public void setFontData(FontData aFontData)
{
if (aFontData == null)
aFontData = HexEditControl.DEFAULT_FONT_DATA;
sampleFontData = aFontData;
refreshWidgets();
}
static void showSelected(List aList, String item)
{
int selected = aList.indexOf(item);
if (selected >= 0) {
aList.setSelection(selected);
aList.setTopIndex(Math.max(0, selected - itemsDisplayed + 1));
} else {
aList.deselectAll();
aList.setTopIndex(0);
}
}
void updateAndRefreshSample()
{
sampleFontData = new FontData(text.getText(), getSize(), fontStyleToInt(text1.getText()));
refreshSample();
}
void updateSizeItems()
{
Set<Integer> sizes = fontsSorted.get(text.getText());
if (sizes == null) {
list2.removeAll();
return;
}
String[] items = new String[sizes.size()];
int i = 0;
for (Iterator<Integer> j = sizes.iterator(); i < items.length; ++i) items[i] = j.next().toString();
list2.setItems(items);
}
void updateSizeItemsAndGuessSelected()
{
int lastSize = getSize();
updateSizeItems();
int position = 0;
String[] items = list2.getItems();
for (int i = 1; i < items.length; ++i) {
if (lastSize >= Integer.parseInt(items[i]))
position = i;
}
text2.setText(items[position]);
showSelected(list2, items[position]);
}
}