diff options
Diffstat (limited to 'bundles/org.eclipse.swt/Eclipse SWT')
34 files changed, 1543 insertions, 510 deletions
diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/TextLayout.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/TextLayout.java index 2d004432bc..23c3df501b 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/TextLayout.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/graphics/TextLayout.java @@ -248,7 +248,7 @@ void computeRuns() { if (style.strikeout) { attrStr.addAttribute(OS.NSStrikethroughStyleAttributeName, NSNumber.numberWithInt(OS.NSUnderlineStyleSingle), range); Color strikeColor = style.strikeoutColor; - if (strikeColor != null) { + if (strikeColor != null && !strikeColor.isDisposed()) { NSColor color = NSColor.colorWithDeviceRed(strikeColor.handle[0], strikeColor.handle[1], strikeColor.handle[2], 1); attrStr.addAttribute(OS.NSStrikethroughColorAttributeName, color, range); } @@ -277,7 +277,7 @@ void computeRuns() { if (underlineStyle != 0) { attrStr.addAttribute(OS.NSUnderlineStyleAttributeName, NSNumber.numberWithInt(underlineStyle), range); Color underlineColor = style.underlineColor; - if (underlineColor != null) { + if (underlineColor != null && !underlineColor.isDisposed()) { NSColor color = NSColor.colorWithDeviceRed(underlineColor.handle[0], underlineColor.handle[1], underlineColor.handle[2], 1); attrStr.addAttribute(OS.NSUnderlineColorAttributeName, color, range); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Button.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Button.java index 02cc5bdf67..370c88d76c 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Button.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Button.java @@ -173,7 +173,8 @@ NSSize cellSizeForBounds (long id, long sel, NSRect cellFrame) { NSAttributedString attribStr = createString(text, null, foreground, style, true, true, true); NSRect rect = attribStr.boundingRectWithSize(wrapSize, OS.NSStringDrawingUsesLineFragmentOrigin); attribStr.release(); - double trimHeight = size.height - titleRect.height; + // Avoid trim height to be set to a negative value + double trimHeight = Math.max(size.height - titleRect.height, 0); size.height = rect.height; if (image != null && ((style & (SWT.CHECK|SWT.RADIO)) !=0)) { NSSize imageSize = image.handle.size(); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java index 7f2b241843..d2ef22338b 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Display.java @@ -860,8 +860,9 @@ static private void configureSystemOptions () { * macOS 11 always enables it regardless of sdk. The option is force * enabled here in case SWT runs with java/launcher linked with older sdk. */ - if (!OS.isBigSurOrLater ()) + if (!OS.isBigSurOrLater ()) { configureSystemOption ("NSViewAllowsRootLayerBacking", true); + } /* * Starting with macOS 11, layer backing is always enabled. That's fine. @@ -874,8 +875,36 @@ static private void configureSystemOptions () { * things a lot slower. The workaround is to disable the "automatic" image * format. */ - if (OS.isBigSurOrLater ()) + if (OS.isBigSurOrLater ()) { configureSystemOption ("NSViewUsesAutomaticLayerBackingStores", false); + } + + /* + * Bug 578171: There is new code in macOS 12 that remembers which + * Shell was active before menu popup was shown and tries to + * re-activate after menu popup is closed. Unfortunately there is a + * bug in this code: if window list changes, it activates a wrong + * Shell. + * + * This is a bug on its own, but worse yet, this causes a JVM crash + * because activating a new Shell causes menu bar to reset its + * internal data, which is unexpected to the macOS's menu tracking + * loop. + * + * Both bugs are bugs of macOS itself. The workaround is to disable + * the new macOS 12 behavior. + * + * The condition should be for (macOS >= 12), but it's not possible + * to reliably distinguish 11 from 12, see comment for OS.VERSION. + * That's fine: older macOS don't know this setting and will not + * check for it anyway. + */ + if (OS.isBigSurOrLater ()) { + // The name of the option is misleading. What it really means + // is whether '-[NSMenuWindowManagerWindow _setVisible:]' shall + // save/restore current key window or not. + configureSystemOption ("NSMenuWindowManagerWindowShouldSetVisible", true); + } } /** @@ -1028,13 +1057,13 @@ void createMainMenu () { appleMenu.initWithTitle(emptyStr); OS.objc_msgSend(application.id, OS.sel_registerName("setAppleMenu:"), appleMenu.id); - title = NSString.stringWith(SWT.getMessage("About") + " " + appName); + title = NSString.stringWith(SWT.getMessage("SWT_About") + " " + appName); menuItem = appleMenu.addItemWithTitle(title, OS.sel_orderFrontStandardAboutPanel_, emptyStr); menuItem.setTarget(applicationDelegate); appleMenu.addItem(NSMenuItem.separatorItem()); - title = NSString.stringWith(SWT.getMessage("Preferences...")); + title = NSString.stringWith(SWT.getMessage("SWT_Preferences")); menuItem = appleMenu.addItemWithTitle(title, 0, NSString.stringWith(",")); /* @@ -1045,7 +1074,7 @@ void createMainMenu () { appleMenu.addItem(NSMenuItem.separatorItem()); - title = NSString.stringWith(SWT.getMessage("Services")); + title = NSString.stringWith(SWT.getMessage("SWT_Services")); menuItem = appleMenu.addItemWithTitle(title, 0, emptyStr); NSMenu servicesMenu = (NSMenu)new NSMenu().alloc(); servicesMenu.initWithTitle(emptyStr); @@ -1055,22 +1084,22 @@ void createMainMenu () { appleMenu.addItem(NSMenuItem.separatorItem()); - title = NSString.stringWith(SWT.getMessage("Hide") + " " + appName); + title = NSString.stringWith(SWT.getMessage("SWT_Hide") + " " + appName); menuItem = appleMenu.addItemWithTitle(title, OS.sel_hide_, NSString.stringWith("h")); menuItem.setTarget(applicationDelegate); - title = NSString.stringWith(SWT.getMessage("Hide Others")); + title = NSString.stringWith(SWT.getMessage("SWT_HideOthers")); menuItem = appleMenu.addItemWithTitle(title, OS.sel_hideOtherApplications_, NSString.stringWith("h")); menuItem.setKeyEquivalentModifierMask(OS.NSCommandKeyMask | OS.NSAlternateKeyMask); menuItem.setTarget(applicationDelegate); - title = NSString.stringWith(SWT.getMessage("Show All")); + title = NSString.stringWith(SWT.getMessage("SWT_ShowAll")); menuItem = appleMenu.addItemWithTitle(title, OS.sel_unhideAllApplications_, emptyStr); menuItem.setTarget(applicationDelegate); appleMenu.addItem(NSMenuItem.separatorItem()); - title = NSString.stringWith(SWT.getMessage("Quit") + " " + appName); + title = NSString.stringWith(SWT.getMessage("SWT_Quit") + " " + appName); menuItem = appleMenu.addItemWithTitle(title, OS.sel_applicationShouldTerminate_, NSString.stringWith("q")); menuItem.setTarget(applicationDelegate); @@ -5654,28 +5683,24 @@ void applicationWillFinishLaunching (long id, long sel, long notification) { /* To find the nib look for each of these paths, in order, until one is found: * /System/Library/..../Resources/<display name>.lproj/DefaultApp.nib * /System/Library/..../Resources/<language>.lproj/DefaultApp.nib - * /System/Library/..../Resources/<user's default language>.lproj/DefaultApp.nib - * /System/Library/..../Resources/English.lproj/DefaultApp.nib. - * /System/Library/..../Resources/en.lproj/DefaultApp.nib. + * /System/Library/..../Resources/Base.lproj/DefaultApp.nib + * + * If nib file is not found, use the fallback method createMainMenu() to create menu with localized strings. */ NSString path; NSDictionary dict = NSDictionary.dictionaryWithObject(applicationDelegate, NSString.stringWith("NSOwner")); - NSBundle bundle = NSBundle.bundleWithIdentifier(NSString.stringWith("com.apple.JavaVM")); + NSBundle bundle = NSBundle.bundleWithPath(NSString.stringWith("/System/Library/Frameworks/JavaVM.framework/")); if (bundle != null) { path = bundle.pathForResource(NSString.stringWith("DefaultApp"), NSString.stringWith("nib"), null, languageDisplayName); if (path == null) path = bundle.pathForResource(NSString.stringWith("DefaultApp"), NSString.stringWith("nib"), null, NSString.stringWith(languageISOValue)); - if (path == null) path = bundle.pathForResource(NSString.stringWith("DefaultApp"), NSString.stringWith("nib")); - if (!loaded) loaded = path != null && NSBundle.loadNibFile(path, dict, 0); - if (!loaded) { - path = bundle.pathForResource(NSString.stringWith("DefaultApp"), NSString.stringWith("nib"), null, NSString.stringWith("English")); - if (path == null) path = bundle.pathForResource(NSString.stringWith("DefaultApp"), NSString.stringWith("nib"), null, NSString.stringWith("en")); - loaded = path != null && NSBundle.loadNibFile(path, dict, 0); + if (path == null && languageISOValue.equals("en")) { + path = bundle.pathForResource(NSString.stringWith("DefaultApp"), NSString.stringWith("nib")); } + if (!loaded) loaded = path != null && NSBundle.loadNibFile(path, dict, 0); } - if (!loaded) { - path = NSString.stringWith(System.getProperty("java.home") + "/../Resources/English.lproj/DefaultApp.nib"); - loaded = path != null && NSBundle.loadNibFile(path, dict, 0); - } + /* + * Create the main menu ourselves if Default.nib was not loaded or was not found for the specific language + */ if (!loaded) { createMainMenu(); } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java index 965fb9d96e..c49f1354fe 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Table.java @@ -3636,39 +3636,66 @@ boolean tableView_writeRowsWithIndexes_toPasteboard(long id, long sel, long arg0 return sendMouseEvent(NSApplication.sharedApplication().currentEvent(), SWT.DragDetect, true); } +void handleClickSelected() { + /* + * When there are multiple selected items and one of them is clicked + * without modifiers, macOS supports two cases: + * 1) For single click, all other items are deselected + * 2) For double-click, selection stays as is, allowing to create + * double-click event with multiple items. + * In order to distinguish between the two, macOS delays (1) by + * [NSEvent doubleClickInterval] in order to see if it's case (2). + * This causes SWT.Selection to occur after SWT.MouseUp. + * + * Bug 289483: For consistent cross-platform behavior, we want + * SWT.Selection to occur before SWT.MouseUp. The workaround is to + * implement (1) in SWT code and ignore the delayed selection event. + */ + + int clickedRow = selectedRowIndex; + selectedRowIndex = -1; + + if (clickedRow == -1) return; + if (dragDetected) return; + + // Deselect all items except the clicked one + NSTableView widget = (NSTableView)view; + NSIndexSet selectedRows = widget.selectedRowIndexes (); + int count = (int)selectedRows.count(); + long [] indexBuffer = new long [count]; + selectedRows.getIndexes(indexBuffer, count, 0); + for (int i = 0; i < count; i++) { + if (indexBuffer[i] == clickedRow) continue; + ignoreSelect = true; + widget.deselectRow (indexBuffer[i]); + ignoreSelect = false; + } + + // Bug 456602: It's possible that item is removed between mouse + // down (where 'selectedRowIndex' was cached) and mouse up (current + // code). In such case, all other items are still deselected, because + // 1) without workaround, selection should have happened in mouse down, + // where item still existed + // 2) clicking empty space deselects all items on macOS + // If item is deleted, then pending selection is canceled by macOS, so + // there's no need to ignore the next selection event. + if (clickedRow >= itemCount) return; + + // Emulate SWT.Selection + Event event = new Event (); + event.item = _getItem(clickedRow); + sendSelectionEvent (SWT.Selection, event, false); + + // Ignore real SWT.Selection that will arrive later + ignoreSelect = true; +} + @Override boolean sendMouseEvent(NSEvent nsEvent, int type, boolean send) { if (type == SWT.DragDetect) { dragDetected = true; } else if (type == SWT.MouseUp) { - /* - * This code path handles the case of an unmodified click on an already-selected row. - * To keep the order of events correct, deselect the other selected items and send the - * selection event before MouseUp is sent. Ignore the next selection event. - */ - if (selectedRowIndex != -1) { - if (dragDetected) { - selectedRowIndex = -1; - } else { - NSTableView widget = (NSTableView)view; - NSIndexSet selectedRows = widget.selectedRowIndexes (); - int count = (int)selectedRows.count(); - long [] indexBuffer = new long [count]; - selectedRows.getIndexes(indexBuffer, count, 0); - for (int i = 0; i < count; i++) { - if (indexBuffer[i] == selectedRowIndex) continue; - ignoreSelect = true; - widget.deselectRow (indexBuffer[i]); - ignoreSelect = false; - } - - Event event = new Event (); - event.item = _getItem ((int)selectedRowIndex); - selectedRowIndex = -1; - sendSelectionEvent (SWT.Selection, event, false); - ignoreSelect = true; - } - } + handleClickSelected(); dragDetected = false; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java index da9954aa6a..1c2de57fa6 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Tree.java @@ -2805,11 +2805,7 @@ boolean sendMouseEvent(NSEvent nsEvent, int type, boolean send) { if (type == SWT.DragDetect) { dragDetected = true; } else if (type == SWT.MouseUp) { - /* - * This code path handles the case of an unmodified click on an already-selected row. - * To keep the order of events correct, deselect the other selected items and send the - * selection event before MouseUp is sent. Ignore the next selection event. - */ + // See code comment in Table.handleClickSelected() if (selectedRowIndex != -1) { if (dragDetected) { selectedRowIndex = -1; @@ -2828,6 +2824,8 @@ boolean sendMouseEvent(NSEvent nsEvent, int type, boolean send) { Event event = new Event (); id itemID = widget.itemAtRow (selectedRowIndex); + // (itemID = null) means that item was removed after + // 'selectedRowIndex' was cached if (itemID != null) { Widget item = display.getWidget (itemID.id); if (item != null && item instanceof TreeItem) { @@ -3074,7 +3072,7 @@ public void setHeaderVisible (boolean show) { /** * Sets the number of root-level items contained in the receiver. * <p> - * The fastest way to insert many items is documented in {@link TreeItem#TreeItem(org.eclipse.swt.widgets.Tree,int,int)} + * The fastest way to insert many items is documented in {@link TreeItem#TreeItem(Tree,int,int)} * and {@link TreeItem#setItemCount} * * @param count the number of items diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TreeItem.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TreeItem.java index 1fdf19e060..529082d051 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TreeItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/TreeItem.java @@ -67,8 +67,8 @@ public class TreeItem extends Item { * Constructs <code>TreeItem</code> and <em>inserts</em> it into <code>Tree</code>. * Item is inserted as last direct child of the tree. * <p> - * The fastest way to insert many items is documented in {@link org.eclipse.swt.widgets.TreeItem#TreeItem(Tree,int,int)} - * and {@link org.eclipse.swt.widgets.TreeItem#setItemCount} + * The fastest way to insert many items is documented in {@link TreeItem#TreeItem(Tree,int,int)} + * and {@link TreeItem#setItemCount} * * @param parent a tree control which will be the parent of the new instance (cannot be null) * @param style no styles are currently supported, pass SWT.NONE @@ -126,8 +126,8 @@ public TreeItem (Tree parent, int style, int index) { * Constructs <code>TreeItem</code> and <em>inserts</em> it into <code>Tree</code>. * Item is inserted as last direct child of the specified <code>TreeItem</code>. * <p> - * The fastest way to insert many items is documented in {@link org.eclipse.swt.widgets.TreeItem#TreeItem(Tree,int,int)} - * and {@link org.eclipse.swt.widgets.TreeItem#setItemCount} + * The fastest way to insert many items is documented in {@link TreeItem#TreeItem(Tree,int,int)} + * and {@link TreeItem#setItemCount} * * @param parentItem a tree control which will be the parent of the new instance (cannot be null) * @param style no styles are currently supported, pass SWT.NONE @@ -152,8 +152,8 @@ public TreeItem (TreeItem parentItem, int style) { * Constructs <code>TreeItem</code> and <em>inserts</em> it into <code>Tree</code>. * Item is inserted as <code>index</code> direct child of the specified <code>TreeItem</code>. * <p> - * The fastest way to insert many items is documented in {@link org.eclipse.swt.widgets.TreeItem#TreeItem(Tree,int,int)} - * and {@link org.eclipse.swt.widgets.TreeItem#setItemCount} + * The fastest way to insert many items is documented in {@link TreeItem#TreeItem(Tree,int,int)} + * and {@link TreeItem#setItemCount} * * @param parentItem a tree control which will be the parent of the new instance (cannot be null) * @param style no styles are currently supported, pass SWT.NONE diff --git a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java index d9f38fd976..d20f9ca7c4 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/cocoa/org/eclipse/swt/widgets/Widget.java @@ -105,7 +105,7 @@ public abstract class Widget { static final int DEFAULT_HEIGHT = 64; Widget () { - /* Do nothing */ + notifyCreationTracker(); } /** @@ -143,6 +143,7 @@ public Widget (Widget parent, int style) { this.style = style; display = parent.display; reskinWidget (); + notifyCreationTracker(); } long accessibleHandle() { @@ -1376,6 +1377,7 @@ void release (boolean destroy) { } } } + notifyDisposalTracker(); } void releaseChildren (boolean destroy) { @@ -2190,4 +2192,16 @@ boolean writeSelectionToPasteboard(long id, long sel, long pasteboard, long type return false; } +void notifyCreationTracker() { + if (WidgetSpy.isEnabled) { + WidgetSpy.getInstance().widgetCreated(this); + } +} + +void notifyDisposalTracker() { + if (WidgetSpy.isEnabled) { + WidgetSpy.getInstance().widgetDisposed(this); + } +} + } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/library/make_common.mak b/bundles/org.eclipse.swt/Eclipse SWT/common/library/make_common.mak index a2a919082b..429f0e5b6b 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/library/make_common.mak +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/library/make_common.mak @@ -1,5 +1,5 @@ #******************************************************************************* -# Copyright (c) 2000, 2021 IBM Corporation and others. +# Copyright (c) 2000, 2022 IBM Corporation and others. # # This program and the accompanying materials # are made available under the terms of the Eclipse Public License 2.0 @@ -13,7 +13,7 @@ #******************************************************************************* maj_ver=4 -min_ver=950 -rev=3 -comma_ver=4,950,3,0 +min_ver=952 +rev=6 +comma_ver=4,952,6,0 cef_ver=3071
\ No newline at end of file diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/SWT.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/SWT.java index 5dadc75cac..97837153a1 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/SWT.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/SWT.java @@ -2275,6 +2275,7 @@ public class SWT { * <p><b>Used By:</b></p> * <ul> * <li><code>FormAttachment</code> in a <code>FormLayout</code></li> + * <li><code>BoderData</code> in a <code>BoderLayout</code></li> * </ul> */ public static final int TOP = UP; @@ -2299,6 +2300,7 @@ public class SWT { * <ul> * <li><code>FormAttachment</code> in a <code>FormLayout</code></li> * <li><code>TabFolder</code></li> + * <li><code>BoderData</code> in a <code>BoderLayout</code></li> * </ul> */ public static final int BOTTOM = DOWN; @@ -2329,6 +2331,10 @@ public class SWT { * This constant can also be used to representing the left keyboard * location during a key event. * </p> + * <p><b>Used By:</b></p> + * <ul> + * <li><code>BoderData</code> in a <code>BoderLayout</code></li> + * </ul> */ public static final int LEFT = LEAD; @@ -2358,6 +2364,10 @@ public class SWT { * This constant can also be used to representing the right keyboard * location during a key event. * </p> + * <p><b>Used By:</b></p> + * <ul> + * <li><code>BoderData</code> in a <code>BoderLayout</code></li> + * </ul> */ public static final int RIGHT = TRAIL; @@ -2369,6 +2379,7 @@ public class SWT { * <li><code>Label</code></li> * <li><code>TableColumn</code></li> * <li><code>FormAttachment</code> in a <code>FormLayout</code></li> + * <li><code>BoderData</code> in a <code>BoderLayout</code></li> * </ul> */ public static final int CENTER = 1 << 24; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SWTMessages.properties b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SWTMessages.properties index da86f4f1e8..1c9a2c67aa 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SWTMessages.properties +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/SWTMessages.properties @@ -126,3 +126,10 @@ SWT_ValidTo=Valid To SWT_ValidFromTo=Valid from: {0} to: {1} SWT_Subject=Subject SWT_SerialNumber=Serial Number +SWT_About=About +SWT_Preferences=Preferences... +SWT_Services=Services +SWT_Hide=Hide +SWT_HideOthers=Hide Others +SWT_ShowAll=Show All +SWT_Quit=Quit diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/WidgetSpy.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/WidgetSpy.java new file mode 100644 index 0000000000..0acb6c3479 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/internal/WidgetSpy.java @@ -0,0 +1,154 @@ +/******************************************************************************* + * Copyright (c) 2022 Simeon Andreev and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Simeon Andreev - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.internal; + +import java.util.*; +import java.util.List; + +import org.eclipse.swt.widgets.*; + +/** + * Helper class to allow widget creation and disposal monitoring + */ +public class WidgetSpy { + + /** + * Flag to prevent {@link Widget} from entering this class during debugging, + * if tracking of creation and disposal is not enabled. + */ + public static boolean isEnabled; + + private static final WidgetSpy instance = new WidgetSpy(); + + private WidgetTracker widgetTracker; + + private WidgetSpy() { + // singleton + } + + public static WidgetSpy getInstance() { + return instance; + } + + /** + * Enables tracking of {@link Widget} object creation and disposal. + * + * WARNING: the tracker will be called from the UI thread. Do not block + * it and do not throw any exceptions. + * + * @param tracker notified when a widget is created or disposed. Use + * {@code null} to disable tracking. The tracker will be + * notified of widgets created and disposed after setting the tracker. + */ + public void setWidgetTracker(WidgetTracker tracker) { + isEnabled = tracker != null; + widgetTracker = tracker; + } + + public void widgetCreated(Widget widget) { + if (widgetTracker != null) { + widgetTracker.widgetCreated(widget); + } + } + + public void widgetDisposed(Widget widget) { + if (widgetTracker != null) { + widgetTracker.widgetDisposed(widget); + } + } + + /** + * Custom callback to register widget creation / disposal + */ + public static interface WidgetTracker { + default void widgetCreated(Widget widget) {} + + default void widgetDisposed(Widget widget) {} + } + + /** + * Default implementation simply collects all created and not disposed widgets + */ + public static class NonDisposedWidgetTracker implements WidgetTracker { + + private final Map<Widget, Error> nonDisposedWidgets = new LinkedHashMap<>(); + private final Set<Class<? extends Widget> > trackedTypes = new HashSet<>(); + + @Override + public void widgetCreated(Widget widget) { + boolean isTracked = isTracked(widget); + if (isTracked) { + Error creationException = new Error("Created widget of type: " + widget.getClass().getSimpleName()); + nonDisposedWidgets.put(widget, creationException); + } + } + + @Override + public void widgetDisposed(Widget widget) { + boolean isTracked = isTracked(widget); + if (isTracked) { + nonDisposedWidgets.remove(widget); + } + } + + public Map<Widget, Error> getNonDisposedWidgets() { + return Collections.unmodifiableMap(nonDisposedWidgets); + } + + public void startTracking() { + clearNonDisposedWidgets(); + WidgetSpy.getInstance().setWidgetTracker(this); + } + + private void clearNonDisposedWidgets() { + nonDisposedWidgets.clear(); + } + + public void stopTracking() { + WidgetSpy.getInstance().setWidgetTracker(null); + } + + public void setTrackingEnabled(boolean enabled) { + if (enabled) { + startTracking(); + } else { + stopTracking(); + } + } + + public void setTrackedTypes(List<Class<? extends Widget>> types) { + trackedTypes.clear(); + trackedTypes.addAll(types); + } + + private boolean isTracked(Widget widget) { + boolean isTrackingAllTypes = trackedTypes.isEmpty(); + if (isTrackingAllTypes) { + return true; + } + if (widget != null) { + Class<? extends Widget> widgetType = widget.getClass(); + if (trackedTypes.contains(widgetType)) { + return true; + } + for (Class<? extends Widget> filteredType : trackedTypes) { + if (filteredType.isAssignableFrom(widgetType)) { + return true; + } + } + } + return false; + } + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/layout/BorderData.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/layout/BorderData.java new file mode 100644 index 0000000000..bd4dfcb663 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/layout/BorderData.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2022 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.layout; + +import static org.eclipse.swt.SWT.*; + +import java.util.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.widgets.*; + +/** + * Controls the several aspects of a {@link BorderLayout}. + * + * @since 3.119 + */ +public final class BorderData { + + private final Map<Control, Point> cachedSize = new IdentityHashMap<>(1); + + public int hHint = SWT.DEFAULT; + public int wHint = SWT.DEFAULT; + public int region = SWT.CENTER; + + /** + * creates a {@link BorderData} with default options + */ + public BorderData() { + } + + /** + * creates a {@link BorderData} initialized with the given region, valid values + * are {@link SWT#TOP}, {@link SWT#CENTER}, {@link SWT#LEFT}, {@link SWT#RIGHT}, + * {@link SWT#BOTTOM} + * + * @param region the region valid values are {@link SWT#TOP}, + * {@link SWT#CENTER}, {@link SWT#LEFT}, {@link SWT#RIGHT}, + * {@link SWT#BOTTOM} + */ + public BorderData(int region) { + this.region = region; + } + + /** + * creates a {@link BorderData} initialized with the given region and width and + * height hints + * + * @param region the region valid values are {@link SWT#TOP}, + * {@link SWT#CENTER}, {@link SWT#LEFT}, {@link SWT#RIGHT}, + * {@link SWT#BOTTOM} + * @param widthHint the default hint for the width + * @param heightHint he default hint for the height + */ + public BorderData(int region, int widthHint, int heightHint) { + this.region = region; + this.wHint = widthHint; + this.hHint = heightHint; + } + + Point getSize(Control control) { + return cachedSize.computeIfAbsent(control, c -> c.computeSize(wHint, hHint, true)); + } + + void flushCache(Control control) { + cachedSize.remove(control); + } + + @Override + public String toString() { + return "BorderData [region=" + getRegionString(region) + ", hHint=" + hHint + ", wHint=" + wHint + "]"; + } + + static String getRegionString(int region) { + switch (region) { + case SWT.TOP: + return "SWT.TOP"; + case SWT.RIGHT: + return "SWT.RIGHT"; + case SWT.BOTTOM: + return "SWT.BOTTOM"; + case SWT.LEFT: + return "SWT.LEFT"; + case SWT.CENTER: + return "SWT.CENTER"; + default: + return "SWT.NONE"; + } + } + + /** + * + * @return the region of this BorderData or {@link SWT#NONE} if it is out of + * range + */ + int getRegion() { + switch (region) { + case TOP: + case BOTTOM: + case CENTER: + case RIGHT: + case LEFT: + return region; + case SWT.NONE: + default: + return SWT.NONE; + } + } +}
\ No newline at end of file diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/layout/BorderLayout.java b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/layout/BorderLayout.java new file mode 100644 index 0000000000..4f6b176971 --- /dev/null +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/org/eclipse/swt/layout/BorderLayout.java @@ -0,0 +1,420 @@ +/******************************************************************************* + * Copyright (c) 2022 Christoph Läubrich and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Christoph Läubrich - initial API and implementation + *******************************************************************************/ +package org.eclipse.swt.layout; + +import static org.eclipse.swt.SWT.*; + +import java.util.*; +import java.util.AbstractMap.*; +import java.util.List; +import java.util.Map.*; +import java.util.function.*; +import java.util.stream.*; +import java.util.stream.IntStream.*; + +import org.eclipse.swt.*; +import org.eclipse.swt.graphics.*; +import org.eclipse.swt.widgets.*; + +/** + * {@link BorderLayout} places controls in five regions + * + * <pre> + * +--------------------------------+ + * | NORTH / TOP | + * +---+------------------------+---+ + * | | | | + * | W | | E | + * | E | | A | + * | S | | S | + * | T | | T | + * | / | | / | + * | L | CENTER | R | + * | E | | I | + * | F | | G | + * | T | | H | + * | | | T | + * | | | | + * +---+------------------------+---+ + * | SOUTH / BOTTOM | + * +--------------------------------+ + * </pre> + * + * The controls at the NORTH/SOUTH borders get their preferred heights, the + * controls at the EAST/WEST get their preferred widths and the center region + * grow/shrink according to the remaining space. If more than one control is + * placed inside a region the controls are equally distributed across their axis + * where the grow (CENTER controlled by the {@link BorderLayout#type} value) + * + * @since 3.119 + */ +public class BorderLayout extends Layout { + + private static final ToIntFunction<Point> WIDTH = p -> p.x; + private static final ToIntFunction<Point> HEIGHT = p -> p.y; + + /** + * type specifies how controls will be positioned within the center region. + * + * The default value is {@link SWT#HORIZONTAL}. + * + * Possible values are: + * <ul> + * <li>{@link SWT#HORIZONTAL}: Position the controls horizontally from left to + * right</li> + * <li>{@link SWT#VERTICAL}: Position the controls vertically from top to + * bottom</li> + * </ul> + */ + public int type = SWT.HORIZONTAL; + /** + * marginWidth specifies the number of points of horizontal margin that will be + * placed along the left and right edges of the layout. + * + * The default value is 0. + * + */ + public int marginWidth = 0; + /** + * marginHeight specifies the number of points of vertical margin that will be + * placed along the top and bottom edges of the layout. + * + * The default value is 0. + * + */ + public int marginHeight = 0; + /** + * spacing specifies the number of points between the edge of one region and its + * neighboring regions. + * + * The default value is 0. + * + */ + public int spacing = 0; + /** + * controlSpacing specifies the number of points between the edge of one control + * and its neighboring control inside a region. + * + * The default value is 0. + * + */ + public int controlSpacing = 0; + /** + * If the width of the {@link SWT#LEFT} and {@link SWT#RIGHT} region exceeds the + * available space this factor is used to distribute the size to the controls, + * valid values range between [0 ... 1] + * + * The default value is 0.5 (equal distribution of available space) + */ + public double widthDistributionFactor = 0.5; + /** + * If the height of the {@link SWT#TOP} and {@link SWT#BOTTOM} region exceeds the + * available space this factor is used to distribute the size to the controls, + * valid values range between [0 ... 1] + * + * The default value is 0.5 (equal distribution of available space) + * + */ + public double heightDistributionFactor = 0.5; + + @Override + protected Point computeSize(Composite composite, int wHint, int hHint, boolean flushCache) { + if (hHint > SWT.DEFAULT && wHint > SWT.DEFAULT) { + return new Point(wHint, hHint); + } + Stream<Entry<Control, BorderData>> children = Arrays.stream(composite.getChildren())// + .map(control-> borderDataControl(control, flushCache)); + Map<Integer, List<Entry<Control, BorderData>>> regionMap = children + .collect(Collectors.groupingBy(BorderLayout::region)); + int width; + if (wHint <= SWT.DEFAULT) { + Builder widthBuilder = IntStream.builder(); + int northWidth = getTotal(WIDTH, TOP, regionMap); + int southWidth = getTotal(WIDTH, BOTTOM, regionMap); + int centerWidth; + if (type == SWT.HORIZONTAL) { + centerWidth = getTotal(WIDTH, CENTER, regionMap); + } else { + centerWidth = getMax(WIDTH, CENTER, regionMap); + } + int westWidth = getMax(WIDTH, LEFT, regionMap); + int eastWidth = getMax(WIDTH, RIGHT, regionMap); + int middleWidth = westWidth + centerWidth + eastWidth; + if (centerWidth > 0) { + if (westWidth > 0) { + middleWidth += spacing; + } + if (eastWidth > 0) { + middleWidth += spacing; + } + } else if (westWidth > 0 && eastWidth > 0) { + middleWidth += spacing; + } + widthBuilder.add(middleWidth); + widthBuilder.add(northWidth); + widthBuilder.add(southWidth); + width = widthBuilder.build().max().orElse(0) + 2 * marginWidth; + } else { + width = wHint; + } + int height; + if (hHint <= SWT.DEFAULT) { + Builder heightBuilder = IntStream.builder(); + int northHeight = getMax(HEIGHT, TOP, regionMap); + int southHeight = getMax(HEIGHT, BOTTOM, regionMap); + int westHeight = getTotal(HEIGHT, LEFT, regionMap); + int eastHeight = getTotal(HEIGHT, RIGHT, regionMap); + int centerHeight; + if (type == SWT.HORIZONTAL) { + centerHeight = getMax(HEIGHT, CENTER, regionMap); + } else { + centerHeight = getTotal(HEIGHT, CENTER, regionMap); + } + if (centerHeight > 0) { + if (northHeight > 0) { + centerHeight += spacing; + } + if (southHeight > 0) { + centerHeight += spacing; + } + } + if (westHeight > 0) { + if (northHeight > 0) { + westHeight += spacing; + } + if (southHeight > 0) { + westHeight += spacing; + } + } + if (eastHeight > 0) { + if (northHeight > 0) { + eastHeight += spacing; + } + if (southHeight > 0) { + eastHeight += spacing; + } + } + int sum = northHeight + southHeight; + heightBuilder.add(westHeight + sum); + heightBuilder.add(centerHeight + sum); + heightBuilder.add(eastHeight + sum); + height = heightBuilder.build().max().orElse(0) + 2 * marginHeight; + } else { + height = hHint; + } + return new Point(width, height); + } + + /** + * Calculates the total W/H according to the extractor + * + * @param extractor either {@link #WIDTH} or {@link #HEIGHT} + * @param region the region to compute + * @param regionMap the map of regions + * @return the total W/H including the {@link #controlSpacing} + */ + private int getTotal(ToIntFunction<Point> extractor, int region, + Map<Integer, List<Entry<Control, BorderData>>> regionMap) { + List<Entry<Control, BorderData>> list = regionMap.getOrDefault(region, Collections.emptyList()); + if (list.isEmpty()) { + return 0; + } + return list.stream().mapToInt(entry -> extractor.applyAsInt(entry.getValue().getSize(entry.getKey()))).sum() + + ((list.size() - 1) * controlSpacing); + } + + private static int getMax(ToIntFunction<Point> extractor, int region, + Map<Integer, List<Entry<Control, BorderData>>> regionMap) { + List<Entry<Control, BorderData>> list = regionMap.getOrDefault(region, Collections.emptyList()); + return getMax(extractor, list); + } + + private static int getMax(ToIntFunction<Point> extractor, List<Entry<Control, BorderData>> list) { + return list.stream().mapToInt(entry -> extractor.applyAsInt(entry.getValue().getSize(entry.getKey()))).max() + .orElse(0); + } + + @Override + protected void layout(Composite composite, boolean flushCache) { + Rectangle clientArea = composite.getClientArea(); + int clientX = clientArea.x + marginWidth; + int clientY = clientArea.y + marginHeight; + int clientWidth = clientArea.width - 2 * marginWidth; + int clientHeight = clientArea.height - 2 * marginHeight; + Stream<Entry<Control, BorderData>> children = Arrays.stream(composite.getChildren())// + .map(control-> borderDataControl(control, flushCache)); + Map<Integer, List<Entry<Control, BorderData>>> regionMap = children + .collect(Collectors.groupingBy(BorderLayout::region)); + regionMap.getOrDefault(SWT.NONE, Collections.emptyList()) + .forEach(entry -> entry.getKey().setBounds(clientX, clientY, 0, 0)); + List<Entry<Control, BorderData>> northList = regionMap.getOrDefault(TOP, Collections.emptyList()); + List<Entry<Control, BorderData>> southList = regionMap.getOrDefault(BOTTOM, Collections.emptyList()); + List<Entry<Control, BorderData>> westList = regionMap.getOrDefault(LEFT, Collections.emptyList()); + List<Entry<Control, BorderData>> eastList = regionMap.getOrDefault(RIGHT, Collections.emptyList()); + List<Entry<Control, BorderData>> centerList = regionMap.getOrDefault(CENTER, Collections.emptyList()); + int northControlCount = northList.size(); + int northControlHeight = getMax(HEIGHT, northList); + int southControlCount = southList.size(); + int southControlHeight = getMax(HEIGHT, southList); + if (northControlHeight + southControlHeight > clientHeight) { + int distributionSize = (int) (clientHeight * heightDistributionFactor); + if (northControlHeight > distributionSize) { + northControlHeight = distributionSize; + } + southControlHeight = clientHeight - northControlHeight; + } + int centerControlHeight = clientHeight - northControlHeight - southControlHeight; + int westControlCount = westList.size(); + int westControlWidth = getMax(WIDTH, westList); + int eastControlCount = eastList.size(); + int eastControlWidth = getMax(WIDTH, eastList); + if (westControlWidth + eastControlWidth > clientWidth) { + int distributionSize = (int) (clientWidth * widthDistributionFactor); + if (westControlWidth > distributionSize) { + westControlWidth = distributionSize; + } + eastControlWidth = clientWidth - westControlWidth; + } + int centerControlWidth = clientWidth - westControlWidth - eastControlWidth; + int centerControlCount = centerList.size(); + // Full width and preferred height for NORTH and SOUTH if possible + if (northControlCount > 0) { + int controlWidth = (clientWidth - (northControlCount - 1) * controlSpacing) / northControlCount; + int x = clientX; + int y = clientY; + for (Entry<Control, BorderData> entry : northList) { + entry.getKey().setBounds(x, y, controlWidth, northControlHeight); + x += controlWidth + controlSpacing; + } + } + if (southControlCount > 0) { + int controlWidth = (clientWidth - (southControlCount - 1) * controlSpacing) / southControlCount; + int x = clientX; + int y = clientY + centerControlHeight + northControlHeight; + for (Entry<Control, BorderData> entry : southList) { + entry.getKey().setBounds(x, y, controlWidth, southControlHeight); + x += controlWidth + controlSpacing; + } + } + // remaining height for WEST and EAST, preferred width for WEST and EAST if + // possible ... + if (westControlCount > 0) { + int x = clientX; + int y = clientY + northControlHeight; + int h = clientHeight - northControlHeight - southControlHeight; + if (northControlCount > 0) { + y += spacing; + h -= spacing; + } + if (southControlCount > 0) { + h -= spacing; + } + int controlHeight = (h - (westControlCount - 1) * controlSpacing) / westControlCount; + for (Entry<Control, BorderData> entry : westList) { + entry.getKey().setBounds(x, y, westControlWidth, controlHeight); + y += controlHeight + controlSpacing; + } + } + if (eastControlCount > 0) { + int x = clientX + centerControlWidth + westControlWidth; + int y = clientY + northControlHeight; + int h = clientHeight - northControlHeight - southControlHeight; + if (northControlCount > 0) { + y += spacing; + h -= spacing; + } + if (southControlCount > 0) { + h -= spacing; + } + int controlHeight = (h - (eastControlCount - 1) * controlSpacing) / eastControlCount; + for (Entry<Control, BorderData> entry : eastList) { + entry.getKey().setBounds(x, y, eastControlWidth, controlHeight); + y += controlHeight + controlSpacing; + } + } + // remaining height and width for CENTER + if (centerControlCount > 0) { + int x = clientX + westControlWidth; + int y = clientY + northControlHeight; + int h = centerControlHeight; + int w = centerControlWidth; + if (westControlCount > 0) { + x += spacing; + w -= spacing; + } + if (eastControlCount > 0) { + w -= spacing; + } + if (northControlCount > 0) { + y += spacing; + h -= spacing; + } + if (southControlCount > 0) { + h -= spacing; + } + int controlHeight; + int controlWidth; + if (type == SWT.HORIZONTAL) { + controlHeight = h; + controlWidth = (w - (centerControlCount - 1) * controlSpacing) / centerControlCount; + } else { + controlWidth = w; + controlHeight = (h - (centerControlCount - 1) * controlSpacing) / centerControlCount; + } + for (Entry<Control, BorderData> entry : centerList) { + entry.getKey().setBounds(x, y, controlWidth, controlHeight); + if (type == SWT.HORIZONTAL) { + x += controlWidth + controlSpacing; + } else { + y += controlHeight + controlSpacing; + } + } + } + } + + private static <C extends Control> Entry<C, BorderData> borderDataControl(C control, boolean flushCache) { + Object layoutData = control.getLayoutData(); + if (layoutData instanceof BorderData) { + BorderData borderData = (BorderData) layoutData; + if (flushCache) { + borderData.flushCache(control); + } + return new SimpleEntry<>(control, borderData); + } else { + return new SimpleEntry<>(control, null); + } + } + + private static int region(Entry<Control, BorderData> entry) { + BorderData borderData = entry.getValue(); + if (borderData == null) { + //we assume all controls without explicit data to be placed in the center area + return SWT.CENTER; + } + return borderData.getRegion(); + } + + @Override + public String toString() { + return "BorderLayout [" // + + "type=" + ((type == SWT.HORIZONTAL) ? "SWT.HORIZONTAL" : "SWT.VERTICAL") // + + ", marginWidth=" + marginWidth // + + ", marginHeight=" + marginHeight // + + ", spacing=" + spacing // + + ", controlSpacing=" + controlSpacing // + + ", widthDistributionFactor=" + widthDistributionFactor// + + ", heightDistributionFactor=" + heightDistributionFactor // + + "]"; + } +} diff --git a/bundles/org.eclipse.swt/Eclipse SWT/common/version.txt b/bundles/org.eclipse.swt/Eclipse SWT/common/version.txt index f423d893b9..ccc5290de9 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/common/version.txt +++ b/bundles/org.eclipse.swt/Eclipse SWT/common/version.txt @@ -1 +1 @@ -version 4.950
\ No newline at end of file +version 4.952
\ No newline at end of file diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java index ec5d50b551..7a0f7aee60 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/graphics/ImageLoader.java @@ -17,11 +17,13 @@ package org.eclipse.swt.graphics; import java.io.*; import java.util.*; +import java.util.List; import org.eclipse.swt.*; import org.eclipse.swt.internal.*; import org.eclipse.swt.internal.gtk.*; import org.eclipse.swt.internal.image.*; +import org.eclipse.swt.widgets.*; /** * Instances of this class are used to load images from, @@ -176,16 +178,12 @@ boolean isInterlacedPNG(byte [] imageAsByteArray) { } ImageData [] getImageDataArrayFromStream(InputStream stream) { - byte[] buffer = new byte[2048]; long loader = GDK.gdk_pixbuf_loader_new(); - int length; List<ImageData> imgDataList = new ArrayList<>(); try { // 1) Load InputStream into byte array ByteArrayOutputStream baos = new ByteArrayOutputStream(); - while ((length = stream.read(buffer)) > -1) { - baos.write(buffer, 0, length); - } + stream.transferTo(baos); baos.flush(); byte[] data_buffer = baos.toByteArray(); if (data_buffer.length == 0) SWT.error(SWT.ERROR_UNSUPPORTED_FORMAT); // empty stream @@ -193,7 +191,20 @@ ImageData [] getImageDataArrayFromStream(InputStream stream) { // 2) Copy byte array to C memory, write to GdkPixbufLoader long buffer_ptr = OS.g_malloc(data_buffer.length); C.memmove(buffer_ptr, data_buffer, data_buffer.length); - GDK.gdk_pixbuf_loader_write(loader, buffer_ptr, data_buffer.length, null); + long [] error = new long [1]; + GDK.gdk_pixbuf_loader_write(loader, buffer_ptr, data_buffer.length, error); + if(error[0] != 0) { + /* Bug 576484 + * It is safe just to assume if this fails it is most likely an IO error + * since unsupported format is checked before, and invalid image right after. + * Still, check if it belongs to the G_FILE_ERROR domain and IO error code + */ + if(OS.g_error_matches(error[0], OS.g_file_error_quark(), OS.G_FILE_ERROR_IO)){ + SWT.error(SWT.ERROR_IO, null, Display.extractFreeGError(error[0])); + } else { + OS.g_error_free(error[0]); + } + } GDK.gdk_pixbuf_loader_close(loader, null); // 3) Get GdkPixbufAnimation from loader diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/GDBus.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/GDBus.java index 8bda62b217..78def99d4a 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/GDBus.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/internal/GDBus.java @@ -335,7 +335,7 @@ public class GDBus { * * @param gVariant a pointer to the native GVariant */ - private static Object[] convertGVariantToJava(long gVariant) { + public static Object[] convertGVariantToJava(long gVariant) { Object retVal = convertGVariantToJavaHelper(gVariant); if (retVal instanceof Object[]) { return (Object[]) retVal; @@ -405,7 +405,7 @@ public class GDBus { * * @return pointer GVariant * */ - private static long convertJavaToGVariant(Object javaObject) throws SWTException { + public static long convertJavaToGVariant(Object javaObject) throws SWTException { if (javaObject == null) { return 0; } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java index 07609de651..10a2f92441 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Button.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2020 IBM Corporation and others. + * Copyright (c) 2000, 2022 IBM Corporation and others. * * This program and the accompanying materials * are made available under the terms of the Eclipse Public License 2.0 @@ -304,7 +304,7 @@ void createHandle (int index) { switch (style & bits) { case SWT.ARROW: - byte arrowType [] = GTK.GTK_NAMED_ICON_GO_UP; + byte[] arrowType = GTK.GTK_NAMED_ICON_GO_UP; if ((style & SWT.UP) != 0) arrowType = GTK.GTK_NAMED_ICON_GO_UP; if ((style & SWT.DOWN) != 0) arrowType = GTK.GTK_NAMED_ICON_GO_DOWN; if ((style & SWT.LEFT) != 0) arrowType = GTK.GTK_NAMED_ICON_GO_PREVIOUS; @@ -844,7 +844,7 @@ void _setAlignment (int alignment) { style &= ~(SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT); style |= alignment & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT); boolean isRTL = (style & SWT.RIGHT_TO_LEFT) != 0; - byte arrowType [] = GTK.GTK_NAMED_ICON_GO_UP; + byte[] arrowType = GTK.GTK_NAMED_ICON_GO_UP; switch (alignment) { case SWT.UP: arrowType = GTK.GTK_NAMED_ICON_GO_UP; break; case SWT.DOWN: arrowType = GTK.GTK_NAMED_ICON_GO_DOWN; break; @@ -1187,7 +1187,7 @@ void setOrientation (boolean create) { if (labelHandle != 0) GTK.gtk_widget_set_direction (labelHandle, dir); if (imageHandle != 0) GTK.gtk_widget_set_direction (imageHandle, dir); if (arrowHandle != 0) { - byte arrowType [] = (style & SWT.RIGHT_TO_LEFT) != 0 ? GTK.GTK_NAMED_ICON_GO_NEXT : GTK.GTK_NAMED_ICON_GO_PREVIOUS; + byte[] arrowType = (style & SWT.RIGHT_TO_LEFT) != 0 ? GTK.GTK_NAMED_ICON_GO_NEXT : GTK.GTK_NAMED_ICON_GO_PREVIOUS; switch (style & (SWT.LEFT | SWT.RIGHT)) { case SWT.LEFT: GTK3.gtk_image_set_from_icon_name (arrowHandle, arrowType, GTK.GTK_ICON_SIZE_MENU); break; case SWT.RIGHT: GTK3.gtk_image_set_from_icon_name (arrowHandle, arrowType, GTK.GTK_ICON_SIZE_MENU); break; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java index ffdbf489ac..d3bb60ab59 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Control.java @@ -57,6 +57,8 @@ public abstract class Control extends Widget implements Drawable { static final boolean DISABLE_EMOJI = Boolean.getBoolean("SWT_GTK_INPUT_HINT_NO_EMOJI"); long fixedHandle; + long firstFixedHandle = 0; + long keyController; long redrawWindow, enableWindow, provider; int drawCount, backgroundAlpha = 255; long dragGesture, zoomGesture, rotateGesture, panGesture; @@ -432,7 +434,7 @@ void hookEvents () { private void hookKeyboardAndFocusSignals(long focusHandle) { if (GTK.GTK4) { - long keyController = GTK4.gtk_event_controller_key_new(); + keyController = GTK4.gtk_event_controller_key_new(); GTK4.gtk_widget_add_controller(focusHandle, keyController); GTK.gtk_event_controller_set_propagation_phase(keyController, GTK.GTK_PHASE_CAPTURE); OS.g_signal_connect(keyController, OS.key_pressed, display.keyPressReleaseProc, KEY_PRESSED); @@ -3926,6 +3928,25 @@ void gtk4_focus_enter_event(long controller, long event) { } @Override +void gtk4_focus_window_event(long handle, long event) { + super.gtk4_focus_window_event(handle, event); + + if(firstFixedHandle == 0) { + long child = handle; + //3rd child of shell will be SWTFixed + for(int i = 0; i<3; i++) { + child = GTK4.gtk_widget_get_first_child(child); + } + firstFixedHandle = child != 0 ? child:0; + } + + if(firstFixedHandle !=0 && GTK.gtk_widget_has_focus(firstFixedHandle)) { + if(event == SWT.FocusIn)sendFocusEvent(SWT.FocusIn); + else sendFocusEvent(SWT.FocusOut); + } +} + +@Override long gtk_focus_out_event (long widget, long event) { // widget could be disposed at this point if (handle != 0) { diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java index 4dfc47fb1f..4eaa9e6cff 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Display.java @@ -132,13 +132,13 @@ public class Display extends Device { Callback eventCallback; long eventProc, windowProc2, windowProc3, windowProc4, windowProc5, windowProc6; long changeValueProc; - long snapshotDrawProc, keyPressReleaseProc, focusProc, enterMotionProc, leaveProc, + long snapshotDrawProc, keyPressReleaseProc, focusProc, windowActiveProc, enterMotionProc, leaveProc, scrollProc, resizeProc, activateProc, gesturePressReleaseProc; long notifyProc; long computeSizeProc; Callback windowCallback2, windowCallback3, windowCallback4, windowCallback5, windowCallback6; Callback changeValue; - Callback snapshotDraw, keyPressReleaseCallback, focusCallback, enterMotionCallback, computeSizeCallback, + Callback snapshotDraw, keyPressReleaseCallback, focusCallback, windowActiveCallback, enterMotionCallback, computeSizeCallback, scrollCallback, leaveCallback, resizeCallback, activateCallback, gesturePressReleaseCallback; Callback notifyCallback; EventTable eventTable, filterTable; @@ -222,19 +222,6 @@ public class Display extends Device { SessionManagerListener sessionManagerListener; Runnable [] disposeList; - /* - * DBus objects to be freed upong Display release. Only public for use in - * other areas of SWT (i.e. WebKit). See bug 540060. - */ - /** @noreference */ - public java.util.List<Long> dBusServers = new ArrayList<>(); - /** @noreference */ - public java.util.List<Long> dBusAuthObservers = new ArrayList<>(); - /** @noreference */ - public java.util.List<Long> dBusGUIDS = new ArrayList<>(); - /** @noreference */ - public java.util.List<Long> dBusConnections = new ArrayList<>(); - /* Deferred Layout list */ Composite[] layoutDeferred; int layoutDeferredCount; @@ -3573,6 +3560,9 @@ void initializeCallbacks () { focusCallback = new Callback(this, "focusProc", void.class, new Type[] {long.class, long.class}); //$NON-NLS-1$ focusProc = focusCallback.getAddress(); + windowActiveCallback = new Callback(this, "windowActiveProc", void.class, new Type[] {long.class, long.class}); //$NON-NLS-1$ + windowActiveProc = windowActiveCallback.getAddress(); + enterMotionCallback = new Callback(this, "enterMotionProc", void.class, new Type[] { long.class, double.class, double.class, long.class}); //$NON-NLS-1$ enterMotionProc = enterMotionCallback.getAddress (); @@ -3850,44 +3840,6 @@ void initializeSessionManager() { } /** - * Some parts of SWT (like WebKit) use GDBus for IPC. Some of these objects - * cannot be disposed of in their own classes due to design challenges. - * In these instances we release them along with this Display. This ensures - * no Browser will be using them at disposal time. - */ -void releaseDBusServices() { - releaseSessionManager(); - for (long connection : dBusConnections) { - if (OS.g_dbus_connection_is_closed(connection)) continue; - long [] error = new long [1]; - boolean closed = OS.g_dbus_connection_close_sync(connection, 0, error); - if (error[0] != 0) { - String msg = extractFreeGError(error[0]); - System.err.println("SWT Display: error closing connection: " + msg); - } - if (closed) { - // Free this as we added a reference to it - OS.g_object_unref(connection); - } - } - for (long server : dBusServers) { - OS.g_dbus_server_stop(server); - OS.g_object_unref(server); - } - for (long authObserver : dBusAuthObservers) { - OS.g_object_unref(authObserver); - } - for (long guid : dBusGUIDS) { - OS.g_free(guid); - } - dBusConnections.clear(); - dBusServers.clear(); - dBusAuthObservers.clear(); - dBusGUIDS.clear(); - dBusServers = dBusAuthObservers = dBusGUIDS = dBusConnections = null; -} - -/** * Helper method to extract GError messages. Only call if the pointer is valid (i.e. non-zero). * * @param errorPtr pointer to the GError @@ -4670,7 +4622,6 @@ protected void release () { synchronizer.releaseSynchronizer (); synchronizer = null; - releaseDBusServices (); releaseSessionManager (); releaseDisplay (); super.release (); @@ -4701,6 +4652,10 @@ void releaseDisplay () { focusCallback = null; focusProc = 0; + windowActiveCallback.dispose(); + windowActiveCallback = null; + windowActiveProc = 0; + enterMotionCallback.dispose(); enterMotionCallback = null; enterMotionProc = 0; @@ -6130,13 +6085,18 @@ boolean scrollProc(long controller, double dx, double dy, long user_data) { return false; } -void focusProc(long controller, long user_data) { +void focusProc(long controller, long user_data) {; long handle = GTK.gtk_event_controller_get_widget(controller); Widget widget = getWidget(handle); if (widget != null) widget.focusProc(controller, user_data); } +void windowActiveProc(long handle, long user_data) {; + Widget widget = getWidget(handle); + if (widget != null) widget.windowActiveProc(handle, user_data); +} + boolean keyPressReleaseProc(long controller, int keyval, int keycode, int state, long user_data) { long handle = GTK.gtk_event_controller_get_widget(controller); Widget widget = getWidget(handle); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java index 23db4e2023..224734ad0e 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Shell.java @@ -947,6 +947,7 @@ void hookEvents () { } if (GTK.GTK4) { OS.g_signal_connect_closure (shellHandle, OS.close_request, display.getClosure (CLOSE_REQUEST), false); + OS.g_signal_connect(shellHandle, OS.notify_is_active, display.windowActiveProc, FOCUS_IN); long keyController = GTK4.gtk_event_controller_key_new(); GTK4.gtk_widget_add_controller(shellHandle, keyController); GTK.gtk_event_controller_set_propagation_phase(keyController, GTK.GTK_PHASE_TARGET); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java index 1e0ec9eca1..cc67a0eef6 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Table.java @@ -2961,6 +2961,32 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr } } } + + GdkRectangle rendererRect = new GdkRectangle (); + GdkRectangle columnRect = new GdkRectangle (); + int y_offset; + { + /* + * SWT creates multiple renderers (kind of sub-columns) per column. + * For example: one for checkbox, one for image, one for text. + * 'background_area' argument in this function is area of currently + * painted renderer. However, for SWT.EraseItem and SWT.PaintItem, + * SWT wants entire column's area along with the event. There's api + * 'gtk_tree_view_get_background_area()' but it calculates item's + * rect in control, which will have wrong Y if item is rendered + * separately (for example, for drag image). + * The workaround is to take X range from api and Y range from argument. + */ + OS.memmove (rendererRect, background_area, GdkRectangle.sizeof); + + long path = GTK.gtk_tree_model_get_path (modelHandle, iter); + GTK.gtk_tree_view_get_background_area (handle, path, columnHandle, columnRect); + GTK.gtk_tree_path_free (path); + + y_offset = columnRect.y - rendererRect.y; + columnRect.y -= y_offset; + } + if (item != null) { if (GTK.GTK_IS_CELL_RENDERER_TOGGLE (cell) || (columnIndex != 0 || (style & SWT.CHECK) == 0)) { drawFlags = (int)flags; @@ -2980,10 +3006,7 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr if ((flags & GTK.GTK_CELL_RENDERER_FOCUSED) != 0) drawState |= SWT.FOCUSED; } - GdkRectangle rect = new GdkRectangle (); - long path = GTK.gtk_tree_model_get_path (modelHandle, iter); - GTK.gtk_tree_view_get_background_area (handle, path, columnHandle, rect); - GTK.gtk_tree_path_free (path); + Rectangle rect = columnRect.toRectangle (); if ((drawState & SWT.SELECTED) == 0) { if ((state & PARENT_BACKGROUND) != 0 || backgroundImage != null) { Control control = findBackgroundControl (); @@ -3029,23 +3052,36 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr if (cr != 0) { GdkRectangle r = new GdkRectangle(); GDK.gdk_cairo_get_clip_rectangle(cr, r); - Rectangle rect2 = DPIUtil.autoScaleDown(new Rectangle(rect.x, r.y, r.width, r.height)); + Rectangle rect2 = DPIUtil.autoScaleDown(rect); // Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075 gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height); } else { - Rectangle rect2 = DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height)); + Rectangle rect2 = DPIUtil.autoScaleDown(rect); // Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075 gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height); } + + // SWT.PaintItem/SWT.EraseItem often expect that event.y matches + // what 'event.item.getBounds()' returns. The workaround is to + // adjust coordinate system temporarily. Event event = new Event (); - event.item = item; - event.index = columnIndex; - event.gc = gc; - event.detail = drawState; - Rectangle eventRect = new Rectangle (rect.x, rect.y, rect.width, rect.height); - event.setBounds (DPIUtil.autoScaleDown (eventRect)); - sendEvent (SWT.EraseItem, event); + try { + Rectangle eventRect = new Rectangle (rect.x, rect.y, rect.width, rect.height); + + eventRect.y += y_offset; + Cairo.cairo_translate (cr, 0, -y_offset); + + event.item = item; + event.index = columnIndex; + event.gc = gc; + event.detail = drawState; + event.setBounds (DPIUtil.autoScaleDown (eventRect)); + sendEvent (SWT.EraseItem, event); + } finally { + Cairo.cairo_translate (cr, 0, y_offset); + } + drawForegroundRGBA = null; drawState = event.doit ? event.detail : 0; drawFlags &= ~(GTK.GTK_CELL_RENDERER_FOCUSED | GTK.GTK_CELL_RENDERER_SELECTED); @@ -3074,9 +3110,7 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr if ((drawState & SWT.BACKGROUND) != 0 && (drawState & SWT.SELECTED) == 0) { GC gc = getGC(cr); gc.setBackground (item.getBackground (columnIndex)); - GdkRectangle rect = new GdkRectangle (); - OS.memmove (rect, background_area, GdkRectangle.sizeof); - gc.fillRectangle(DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height))); + gc.fillRectangle (DPIUtil.autoScaleDown (rendererRect.toRectangle ())); gc.dispose (); } if ((drawState & SWT.FOREGROUND) != 0 || GTK.GTK_IS_CELL_RENDERER_TOGGLE (cell)) { @@ -3102,10 +3136,7 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr if (GTK.GTK_IS_CELL_RENDERER_TEXT (cell)) { if (hooks (SWT.PaintItem)) { if (wasSelected) drawState |= SWT.SELECTED; - GdkRectangle rect = new GdkRectangle (); - long path = GTK.gtk_tree_model_get_path (modelHandle, iter); - GTK.gtk_tree_view_get_background_area (handle, path, columnHandle, rect); - GTK.gtk_tree_path_free (path); + Rectangle rect = columnRect.toRectangle (); ignoreSize = true; int [] contentX = new int [1], contentWidth = new int [1]; gtk_cell_renderer_get_preferred_size (cell, handle, contentWidth, null); @@ -3149,18 +3180,30 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr gc.setFont (item.getFont (columnIndex)); if ((style & SWT.MIRRORED) != 0) rect.x = getClientWidth () - rect.width - rect.x; - Rectangle rect2 = DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height)); + Rectangle rect2 = DPIUtil.autoScaleDown(rect); // Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075 gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height); + // SWT.PaintItem/SWT.EraseItem often expect that event.y matches + // what 'event.item.getBounds()' returns. The workaround is to + // adjust coordinate system temporarily. Event event = new Event (); - event.item = item; - event.index = columnIndex; - event.gc = gc; - Rectangle eventRect = new Rectangle (rect.x + contentX [0], rect.y, contentWidth [0], rect.height); - event.setBounds (DPIUtil.autoScaleDown (eventRect)); - event.detail = drawState; - sendEvent (SWT.PaintItem, event); + try { + Rectangle eventRect = new Rectangle (rect.x + contentX [0], rect.y, contentWidth [0], rect.height); + + eventRect.y += y_offset; + Cairo.cairo_translate (cr, 0, -y_offset); + + event.item = item; + event.index = columnIndex; + event.gc = gc; + event.detail = drawState; + event.setBounds (DPIUtil.autoScaleDown (eventRect)); + sendEvent (SWT.PaintItem, event); + } finally { + Cairo.cairo_translate (cr, 0, y_offset); + } + gc.dispose(); } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolItem.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolItem.java index 723b591b09..bdf0b5fa2a 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/ToolItem.java @@ -59,6 +59,15 @@ public class ToolItem extends Item { boolean mapHooked; boolean enabled = true; + /** + * The image that is currently used by the tool item. + * Either the image set by client code via {@link #setImage(Image)} + * or {@link #setDisabledImage(Image)}, depending on button state. + * Or if the button is disabled but no disabled image is specified, + * a grayed out version of the "normal" image. + */ + Image currentImage; + /** * Constructs a new instance of this class given its parent * (which must be a <code>ToolBar</code>) and a style value @@ -657,6 +666,7 @@ long gtk_create_menu_proxy (long widget) { return 1; } + Image image = currentImage; if (image != null) { ImageList imageList = parent.imageList; if (imageList != null) { @@ -1261,6 +1271,7 @@ private void disposeDefault() { void _setImage (Image image) { if ((style & SWT.SEPARATOR) != 0) return; + currentImage = image; if (image != null) { ImageList imageList = parent.imageList; if (imageList == null) imageList = parent.imageList = new ImageList (); @@ -1293,15 +1304,7 @@ void _setImage (Image image) { * required to reset the proxy menu. Otherwise, the * old menuItem appears in the overflow menu. */ - if ((style & SWT.DROP_DOWN) != 0) { - if (GTK.GTK4) { - /* TODO: GTK4 have to implement our own overflow menu */ - } else { - proxyMenuItem = 0; - proxyMenuItem = GTK3.gtk_tool_item_retrieve_proxy_menu_item (handle); - OS.g_signal_connect(proxyMenuItem, OS.activate, ToolBar.menuItemSelectedFunc.getAddress(), handle); - } - } + recreateMenuProxy(); parent.relayout (); } @@ -1411,15 +1414,7 @@ public void setText (String string) { * required to reset the proxy menu. Otherwise, the * old menuItem appears in the overflow menu. */ - if ((style & SWT.DROP_DOWN) != 0) { - if (GTK.GTK4) { - /* TODO: GTK4 have to implement our own overflow menu */ - } else { - proxyMenuItem = 0; - proxyMenuItem = GTK3.gtk_tool_item_retrieve_proxy_menu_item (handle); - OS.g_signal_connect(proxyMenuItem, OS.activate, ToolBar.menuItemSelectedFunc.getAddress(), handle); - } - } + recreateMenuProxy(); parent.relayout (); } @@ -1474,15 +1469,7 @@ public void setToolTipText(String string) { * Otherwise, the old menuItem appears in the overflow * menu as a blank item. */ - if ((style & SWT.DROP_DOWN) != 0) { - if (GTK.GTK4) { - /* TODO: GTK4 have to implement our own overflow menu */ - } else { - proxyMenuItem = 0; - proxyMenuItem = GTK3.gtk_tool_item_retrieve_proxy_menu_item (handle); - OS.g_signal_connect(proxyMenuItem, OS.activate, ToolBar.menuItemSelectedFunc.getAddress(), handle); - } - } + recreateMenuProxy(); } /** @@ -1570,4 +1557,15 @@ long dpiChanged(long object, long arg0) { return 0; } + +private void recreateMenuProxy() { + if ((style & SWT.DROP_DOWN) != 0 || proxyMenuItem != 0) { + if (GTK.GTK4) { + /* TODO: GTK4 have to implement our own overflow menu */ + } else { + proxyMenuItem = 0; + proxyMenuItem = GTK3.gtk_tool_item_retrieve_proxy_menu_item (handle); + } + } +} }
\ No newline at end of file diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java index 77e6bdfd26..cab5bf8567 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Tree.java @@ -3168,6 +3168,32 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr } } } + + GdkRectangle rendererRect = new GdkRectangle (); + GdkRectangle columnRect = new GdkRectangle (); + int y_offset; + { + /* + * SWT creates multiple renderers (kind of sub-columns) per column. + * For example: one for checkbox, one for image, one for text. + * 'background_area' argument in this function is area of currently + * painted renderer. However, for SWT.EraseItem and SWT.PaintItem, + * SWT wants entire column's area along with the event. There's api + * 'gtk_tree_view_get_background_area()' but it calculates item's + * rect in control, which will have wrong Y if item is rendered + * separately (for example, for drag image). + * The workaround is to take X range from api and Y range from argument. + */ + OS.memmove (rendererRect, background_area, GdkRectangle.sizeof); + + long path = GTK.gtk_tree_model_get_path (modelHandle, iter); + GTK.gtk_tree_view_get_background_area (handle, path, columnHandle, columnRect); + GTK.gtk_tree_path_free (path); + + y_offset = columnRect.y - rendererRect.y; + columnRect.y -= y_offset; + } + if (item != null) { if (GTK.GTK_IS_CELL_RENDERER_TOGGLE (cell) || ( columnIndex != 0 || (style & SWT.CHECK) == 0)) { drawFlags = (int)flags; @@ -3187,10 +3213,7 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr if ((flags & GTK.GTK_CELL_RENDERER_FOCUSED) != 0) drawState |= SWT.FOCUSED; } - GdkRectangle rect = new GdkRectangle (); - long path = GTK.gtk_tree_model_get_path (modelHandle, iter); - GTK.gtk_tree_view_get_background_area (handle, path, columnHandle, rect); - GTK.gtk_tree_path_free (path); + Rectangle rect = columnRect.toRectangle (); // Use the x and width information from the Cairo context. See bug 535124. if (cr != 0) { GdkRectangle r2 = new GdkRectangle (); @@ -3243,21 +3266,34 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr if (cr != 0) { // Use the original rectangle, not the Cairo clipping for the y, width, and height values. // See bug 535124. - Rectangle rect2 = DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height)); + Rectangle rect2 = DPIUtil.autoScaleDown(rect); gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height); } else { - Rectangle rect2 = DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height)); + Rectangle rect2 = DPIUtil.autoScaleDown(rect); // Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075 gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height); } + + // SWT.PaintItem/SWT.EraseItem often expect that event.y matches + // what 'event.item.getBounds()' returns. The workaround is to + // adjust coordinate system temporarily. Event event = new Event (); - event.item = item; - event.index = columnIndex; - event.gc = gc; - Rectangle eventRect = new Rectangle (rect.x, rect.y, rect.width, rect.height); - event.setBounds (DPIUtil.autoScaleDown (eventRect)); - event.detail = drawState; - sendEvent (SWT.EraseItem, event); + try { + Rectangle eventRect = new Rectangle (rect.x, rect.y, rect.width, rect.height); + + eventRect.y += y_offset; + Cairo.cairo_translate (cr, 0, -y_offset); + + event.item = item; + event.index = columnIndex; + event.gc = gc; + event.detail = drawState; + event.setBounds (DPIUtil.autoScaleDown (eventRect)); + sendEvent (SWT.EraseItem, event); + } finally { + Cairo.cairo_translate (cr, 0, y_offset); + } + drawForegroundRGBA = null; drawState = event.doit ? event.detail : 0; drawFlags &= ~(GTK.GTK_CELL_RENDERER_FOCUSED | GTK.GTK_CELL_RENDERER_SELECTED); @@ -3275,12 +3311,9 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr } } if ((drawState & SWT.BACKGROUND) != 0 && (drawState & SWT.SELECTED) == 0) { - GC gc = getGC(cr); gc.setBackground (item.getBackground (columnIndex)); - GdkRectangle rect = new GdkRectangle (); - OS.memmove (rect, background_area, GdkRectangle.sizeof); - gc.fillRectangle(DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height))); + gc.fillRectangle (DPIUtil.autoScaleDown (rendererRect.toRectangle ())); gc.dispose (); } if ((drawState & SWT.FOREGROUND) != 0 || GTK.GTK_IS_CELL_RENDERER_TOGGLE (cell)) { @@ -3306,10 +3339,7 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr if (GTK.GTK_IS_CELL_RENDERER_TEXT (cell)) { if (hooks (SWT.PaintItem)) { if (wasSelected) drawState |= SWT.SELECTED; - GdkRectangle rect = new GdkRectangle (); - long path = GTK.gtk_tree_model_get_path (modelHandle, iter); - GTK.gtk_tree_view_get_background_area (handle, path, columnHandle, rect); - GTK.gtk_tree_path_free (path); + Rectangle rect = columnRect.toRectangle (); ignoreSize = true; int [] contentX = new int [1], contentWidth = new int [1]; gtk_cell_renderer_get_preferred_size (cell, handle, contentWidth, null); @@ -3339,7 +3369,7 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr /* indent */ GdkRectangle rect3 = new GdkRectangle (); GTK.gtk_widget_realize (handle); - path = GTK.gtk_tree_model_get_path (modelHandle, iter); + long path = GTK.gtk_tree_model_get_path (modelHandle, iter); GTK.gtk_tree_view_get_cell_area (handle, path, columnHandle, rect3); GTK.gtk_tree_path_free (path); contentX[0] += rect3.x; @@ -3362,18 +3392,30 @@ void rendererRender (long cell, long cr, long snapshot, long widget, long backgr rect.x = getClientWidth () - rect.width - rect.x; } - Rectangle rect2 = DPIUtil.autoScaleDown(new Rectangle(rect.x, rect.y, rect.width, rect.height)); + Rectangle rect2 = DPIUtil.autoScaleDown(rect); // Caveat: rect2 is necessary because GC#setClipping(Rectangle) got broken by bug 446075 gc.setClipping(rect2.x, rect2.y, rect2.width, rect2.height); + // SWT.PaintItem/SWT.EraseItem often expect that event.y matches + // what 'event.item.getBounds()' returns. The workaround is to + // adjust coordinate system temporarily. Event event = new Event (); - event.item = item; - event.index = columnIndex; - event.gc = gc; - Rectangle eventRect = new Rectangle (rect.x + contentX [0], rect.y, contentWidth [0], rect.height); - event.setBounds (DPIUtil.autoScaleDown (eventRect)); - event.detail = drawState; - sendEvent(SWT.PaintItem, event); + try { + Rectangle eventRect = new Rectangle (rect.x + contentX [0], rect.y, contentWidth [0], rect.height); + + eventRect.y += y_offset; + Cairo.cairo_translate (cr, 0, -y_offset); + + event.item = item; + event.index = columnIndex; + event.gc = gc; + event.detail = drawState; + event.setBounds (DPIUtil.autoScaleDown (eventRect)); + sendEvent (SWT.PaintItem, event); + } finally { + Cairo.cairo_translate (cr, 0, y_offset); + } + gc.dispose(); } } @@ -3500,7 +3542,7 @@ void setItemCount (long parentIter, int count) { /** * Sets the number of root-level items contained in the receiver. * <p> - * The fastest way to insert many items is documented in {@link TreeItem#TreeItem(org.eclipse.swt.widgets.Tree,int,int)} + * The fastest way to insert many items is documented in {@link TreeItem#TreeItem(Tree,int,int)} * and {@link TreeItem#setItemCount} * * @param count the number of items diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/TreeItem.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/TreeItem.java index c7545f6658..d4f94ac010 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/TreeItem.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/TreeItem.java @@ -51,8 +51,8 @@ public class TreeItem extends Item { * Constructs <code>TreeItem</code> and <em>inserts</em> it into <code>Tree</code>. * Item is inserted as last direct child of the tree. * <p> - * The fastest way to insert many items is documented in {@link org.eclipse.swt.widgets.TreeItem#TreeItem(Tree,int,int)} - * and {@link org.eclipse.swt.widgets.TreeItem#setItemCount} + * The fastest way to insert many items is documented in {@link TreeItem#TreeItem(Tree,int,int)} + * and {@link TreeItem#setItemCount} * * @param parent a tree control which will be the parent of the new instance (cannot be null) * @param style no styles are currently supported, pass SWT.NONE @@ -110,8 +110,8 @@ public TreeItem (Tree parent, int style, int index) { * Constructs <code>TreeItem</code> and <em>inserts</em> it into <code>Tree</code>. * Item is inserted as last direct child of the specified <code>TreeItem</code>. * <p> - * The fastest way to insert many items is documented in {@link org.eclipse.swt.widgets.TreeItem#TreeItem(Tree,int,int)} - * and {@link org.eclipse.swt.widgets.TreeItem#setItemCount} + * The fastest way to insert many items is documented in {@link TreeItem#TreeItem(Tree,int,int)} + * and {@link TreeItem#setItemCount} * * @param parentItem a tree control which will be the parent of the new instance (cannot be null) * @param style no styles are currently supported, pass SWT.NONE @@ -136,8 +136,8 @@ public TreeItem (TreeItem parentItem, int style) { * Constructs <code>TreeItem</code> and <em>inserts</em> it into <code>Tree</code>. * Item is inserted as <code>index</code> direct child of the specified <code>TreeItem</code>. * <p> - * The fastest way to insert many items is documented in {@link org.eclipse.swt.widgets.TreeItem#TreeItem(Tree,int,int)} - * and {@link org.eclipse.swt.widgets.TreeItem#setItemCount} + * The fastest way to insert many items is documented in {@link TreeItem#TreeItem(Tree,int,int)} + * and {@link TreeItem#setItemCount} * * @param parentItem a tree control which will be the parent of the new instance (cannot be null) * @param style no styles are currently supported, pass SWT.NONE diff --git a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java index 0c301d5b1e..3aa2083ff4 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/gtk/org/eclipse/swt/widgets/Widget.java @@ -255,7 +255,9 @@ public abstract class Widget { /** * Prevents uninitialized instances from being created outside the package. */ -Widget () {} +Widget () { + notifyCreationTracker(); +} /** * Constructs a new instance of this class given its parent @@ -292,6 +294,7 @@ public Widget (Widget parent, int style) { this.style = style; display = parent.display; reskinWidget (); + notifyCreationTracker(); } void _addListener (int eventType, Listener listener) { @@ -793,6 +796,14 @@ void gtk4_key_release_event(long controller, int keyval, int keycode, int state, void gtk4_focus_enter_event(long controller, long event) {} /** + * @param handle the handle of the window that caused the event + * @param event the type of event, should be FocusIn or FocusOut + */ +void gtk4_focus_window_event(long handle, long event) { + gtk_focus_in_event (handle, event); +} + +/** * @param controller the corresponding controller responsible for capturing the event * @param event the GdkEvent captured */ @@ -1345,6 +1356,7 @@ void release (boolean destroy) { releaseHandle (); } } + notifyDisposalTracker(); } } @@ -1583,7 +1595,8 @@ char [] sendIMKeyEvent (int type, long event, char [] chars) { int index = 0, count = 0, state = 0; long ptr = 0; if (event == 0) { - ptr = GTK3.gtk_get_current_event (); + long controller = Control.getControl(this.handle).keyController; + ptr = GTK.GTK4 ? GTK4.gtk_event_controller_get_current_event(controller):GTK3.gtk_get_current_event (); if (ptr != 0) { int eventType = GDK.gdk_event_get_event_type(ptr); eventType = Control.fixGdkEventTypeValues(eventType); @@ -1603,9 +1616,13 @@ char [] sendIMKeyEvent (int type, long event, char [] chars) { break; } } else { - int [] buffer = new int [1]; - GTK3.gtk_get_current_event_state (buffer); - state = buffer [0]; + if(GTK.GTK4) { + state = GTK4.gtk_event_controller_get_current_event_state(controller); + } else { + int [] buffer = new int [1]; + GTK3.gtk_get_current_event_state (buffer); + state = buffer [0]; + } } } else { ptr = event; @@ -1627,13 +1644,13 @@ char [] sendIMKeyEvent (int type, long event, char [] chars) { * the key by returning null. */ if (isDisposed ()) { - if (ptr != 0 && ptr != event) gdk_event_free (ptr); + if (ptr != 0 && ptr != event && !GTK.GTK4) gdk_event_free (ptr); return null; } if (javaEvent.doit) chars [count++] = chars [index]; index++; } - if (ptr != 0 && ptr != event) gdk_event_free (ptr); + if (ptr != 0 && ptr != event && !GTK.GTK4) gdk_event_free (ptr); if (count == 0) return null; if (index != count) { char [] result = new char [count]; @@ -2295,6 +2312,12 @@ void focusProc(long controller, long user_data) { } } +void windowActiveProc(long handle, long user_data) { + long eventType = GTK.gtk_window_is_active(handle) ? SWT.FocusIn:SWT.FocusOut; + + gtk4_focus_window_event(handle, eventType); +} + boolean keyPressReleaseProc(long controller, int keyval, int keycode, int state, long user_data) { long event = GTK4.gtk_event_controller_get_current_event(controller); @@ -2520,4 +2543,17 @@ void gtk_widget_size_allocate (long widget, GtkAllocation allocation, int baseli GTK3.gtk_widget_size_allocate(widget, allocation); } } + +void notifyCreationTracker() { + if (WidgetSpy.isEnabled) { + WidgetSpy.getInstance().widgetCreated(this); + } +} + +void notifyDisposalTracker() { + if (WidgetSpy.isEnabled) { + WidgetSpy.getInstance().widgetDisposed(this); + } +} + } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java index a89ddf86f9..b79fe461f5 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/GC.java @@ -1220,12 +1220,14 @@ void drawBitmap(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, data.hNullBitmap = 0; } } - if (srcImage.alpha != -1 || srcImage.alphaData != null) { - drawBitmapAlpha(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight); + boolean isDib = bm.bmBits != 0; + int depth = bm.bmPlanes * bm.bmBitsPixel; + if (isDib && depth == 32) { + drawBitmapAlpha(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple); } else if (srcImage.transparentPixel != -1) { drawBitmapTransparent(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight); } else { - drawBitmap(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight); + drawBitmapColor(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple); } if (mustRestore) { long hOldBitmap = OS.SelectObject(memGC.handle, srcImage.handle); @@ -1233,35 +1235,15 @@ void drawBitmap(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, } } -void drawBitmapAlpha(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple, BITMAP bm, int imgWidth, int imgHeight) { - /* Simple cases */ - if (srcImage.alpha == 0) return; - if (srcImage.alpha == 255) { - drawBitmap(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple, bm, imgWidth, imgHeight); - return; - } - +void drawBitmapAlpha(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple) { boolean alphaBlendSupport = true; boolean isPrinter = OS.GetDeviceCaps(handle, OS.TECHNOLOGY) == OS.DT_RASPRINTER; + int sourceAlpha = -1; if (isPrinter) { int caps = OS.GetDeviceCaps(handle, OS.SHADEBLENDCAPS); if (caps != 0) { - if (srcImage.alpha != -1) { - alphaBlendSupport = (caps & OS.SB_CONST_ALPHA) != 0; - } else { - alphaBlendSupport = (caps & OS.SB_PIXEL_ALPHA) != 0; - } - } - } - if (alphaBlendSupport) { - BLENDFUNCTION blend = new BLENDFUNCTION(); - blend.BlendOp = OS.AC_SRC_OVER; - long srcHdc = OS.CreateCompatibleDC(handle); - long oldSrcBitmap = OS.SelectObject(srcHdc, srcImage.handle); - if (srcImage.alpha != -1) { - blend.SourceConstantAlpha = (byte)srcImage.alpha; - OS.AlphaBlend(handle, destX, destY, destWidth, destHeight, srcHdc, srcX, srcY, srcWidth, srcHeight, blend); - } else { + long srcHdc = OS.CreateCompatibleDC(handle); + long oldSrcBitmap = OS.SelectObject(srcHdc, srcImage.handle); long memDib = Image.createDIB(srcWidth, srcHeight, 32); if (memDib == 0) SWT.error(SWT.ERROR_NO_HANDLES); long memHdc = OS.CreateCompatibleDC(handle); @@ -1271,34 +1253,41 @@ void drawBitmapAlpha(Image srcImage, int srcX, int srcY, int srcWidth, int srcHe OS.BitBlt(memHdc, 0, 0, srcWidth, srcHeight, srcHdc, srcX, srcY, OS.SRCCOPY); byte[] srcData = new byte[dibBM.bmWidthBytes * dibBM.bmHeight]; OS.MoveMemory(srcData, dibBM.bmBits, srcData.length); - final int apinc = imgWidth - srcWidth; - int ap = srcY * imgWidth + srcX, sp = 0; - byte[] alphaData = srcImage.alphaData; - for (int y = 0; y < srcHeight; ++y) { - for (int x = 0; x < srcWidth; ++x) { - int alpha = alphaData[ap++] & 0xff; - int r = ((srcData[sp + 0] & 0xFF) * alpha) + 128; - r = (r + (r >> 8)) >> 8; - int g = ((srcData[sp + 1] & 0xFF) * alpha) + 128; - g = (g + (g >> 8)) >> 8; - int b = ((srcData[sp + 2] & 0xFF) * alpha) + 128; - b = (b + (b >> 8)) >> 8; - srcData[sp+0] = (byte)r; - srcData[sp+1] = (byte)g; - srcData[sp+2] = (byte)b; - srcData[sp+3] = (byte)alpha; - sp += 4; + int size = srcData.length; + sourceAlpha = srcData[3] & 0xFF; + for (int sp = 7; sp < size; sp += 4) { + int currentAlpha = srcData[sp] & 0xFF; + if (sourceAlpha != currentAlpha) { + sourceAlpha = -1; + break; } - ap += apinc; } - OS.MoveMemory(dibBM.bmBits, srcData, srcData.length); - blend.SourceConstantAlpha = (byte)0xff; - blend.AlphaFormat = OS.AC_SRC_ALPHA; - OS.AlphaBlend(handle, destX, destY, destWidth, destHeight, memHdc, 0, 0, srcWidth, srcHeight, blend); OS.SelectObject(memHdc, oldMemBitmap); OS.DeleteDC(memHdc); OS.DeleteObject(memDib); + OS.SelectObject(srcHdc, oldSrcBitmap); + OS.DeleteDC(srcHdc); + if (sourceAlpha != -1) { + if (sourceAlpha == 0) return; + if (sourceAlpha == 255) { + drawBitmapColor(srcImage, srcX, srcY, srcWidth, srcHeight, destX, destY, destWidth, destHeight, simple); + return; + } + alphaBlendSupport = (caps & OS.SB_CONST_ALPHA) != 0; + } + else { + alphaBlendSupport = (caps & OS.SB_PIXEL_ALPHA) != 0; + } } + } + if (alphaBlendSupport) { + BLENDFUNCTION blend = new BLENDFUNCTION(); + blend.BlendOp = OS.AC_SRC_OVER; + long srcHdc = OS.CreateCompatibleDC(handle); + long oldSrcBitmap = OS.SelectObject(srcHdc, srcImage.handle); + blend.SourceConstantAlpha = (byte)sourceAlpha; + blend.AlphaFormat = OS.AC_SRC_ALPHA; + OS.AlphaBlend(handle, destX, destY, destWidth, destHeight, srcHdc, 0, 0, srcWidth, srcHeight, blend); OS.SelectObject(srcHdc, oldSrcBitmap); OS.DeleteDC(srcHdc); return; @@ -1348,26 +1337,6 @@ void drawBitmapAlpha(Image srcImage, int srcX, int srcY, int srcWidth, int srcHe byte[] srcData = new byte[sizeInBytes]; OS.MoveMemory(srcData, dibBM.bmBits, sizeInBytes); - /* Merge the alpha channel in place */ - int alpha = srcImage.alpha; - final boolean hasAlphaChannel = (srcImage.alpha == -1); - if (hasAlphaChannel) { - final int apinc = imgWidth - srcWidth; - final int spinc = dibBM.bmWidthBytes - srcWidth * 4; - int ap = srcY * imgWidth + srcX, sp = 3; - byte[] alphaData = srcImage.alphaData; - for (int y = 0; y < srcHeight; ++y) { - for (int x = 0; x < srcWidth; ++x) { - srcData[sp] = alphaData[ap++]; - sp += 4; - } - ap += apinc; - sp += spinc; - } - } - - /* Scale the foreground pixels with alpha */ - OS.MoveMemory(dibBM.bmBits, srcData, sizeInBytes); /* * When drawing to a printer, StretchBlt does not correctly stretch if * the source and destination HDCs are the same. The workaround is to @@ -1403,10 +1372,10 @@ void drawBitmapAlpha(Image srcImage, int srcX, int srcY, int srcWidth, int srcHe int dp = 0; for (int y = 0; y < destHeight; ++y) { for (int x = 0; x < destWidth; ++x) { - if (hasAlphaChannel) alpha = srcData[dp + 3] & 0xff; - destData[dp] += ((srcData[dp] & 0xff) - (destData[dp] & 0xff)) * alpha / 255; - destData[dp + 1] += ((srcData[dp + 1] & 0xff) - (destData[dp + 1] & 0xff)) * alpha / 255; - destData[dp + 2] += ((srcData[dp + 2] & 0xff) - (destData[dp + 2] & 0xff)) * alpha / 255; + int alpha = srcData[dp + 3] & 0xFF; + destData[dp ] += (srcData[dp ] & 0xFF) - (destData[dp ] & 0xFF) * alpha / 255; + destData[dp + 1] += (srcData[dp + 1] & 0xFF) - (destData[dp + 1] & 0xFF) * alpha / 255; + destData[dp + 2] += (srcData[dp + 2] & 0xFF) - (destData[dp + 2] & 0xFF) * alpha / 255; dp += 4; } dp += dpinc; @@ -1642,7 +1611,7 @@ void drawBitmapTransparent(Image srcImage, int srcX, int srcY, int srcWidth, int OS.DeleteDC(srcHdc); } -void drawBitmap(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple, BITMAP bm, int imgWidth, int imgHeight) { +void drawBitmapColor(Image srcImage, int srcX, int srcY, int srcWidth, int srcHeight, int destX, int destY, int destWidth, int destHeight, boolean simple) { long srcHdc = OS.CreateCompatibleDC(handle); long oldSrcBitmap = OS.SelectObject(srcHdc, srcImage.handle); int dwRop = OS.GetROP2(handle) == OS.R2_XORPEN ? OS.SRCINVERT : OS.SRCCOPY; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java index c0766ccab7..1b38c70d27 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/Image.java @@ -112,16 +112,6 @@ public final class Image extends Resource implements Drawable { GC memGC; /** - * the alpha data for the image - */ - byte[] alphaData; - - /** - * the global alpha value to be used for every pixel - */ - int alpha = -1; - - /** * ImageFileNameProvider to provide file names at various Zoom levels */ private ImageFileNameProvider imageFileNameProvider; @@ -283,11 +273,6 @@ public Image(Device device, Image srcImage, int flag) { device.internal_dispose_GC(hDC, null); transparentPixel = srcImage.transparentPixel; - alpha = srcImage.alpha; - if (srcImage.alphaData != null) { - alphaData = new byte[srcImage.alphaData.length]; - System.arraycopy(srcImage.alphaData, 0, alphaData, 0, alphaData.length); - } break; case SWT.ICON: handle = OS.CopyImage(srcImage.handle, OS.IMAGE_ICON, rect.width, rect.height, 0); @@ -977,9 +962,12 @@ void initNative(String filename) { long [] createGdipImage() { switch (type) { case SWT.BITMAP: { - if (alpha != -1 || alphaData != null || transparentPixel != -1) { - BITMAP bm = new BITMAP(); - OS.GetObject(handle, BITMAP.sizeof, bm); + BITMAP bm = new BITMAP(); + OS.GetObject(handle, BITMAP.sizeof, bm); + int depth = bm.bmPlanes * bm.bmBitsPixel; + boolean isDib = bm.bmBits != 0; + boolean hasAlpha = isDib && depth == 32; + if (hasAlpha || transparentPixel != -1) { int imgWidth = bm.bmWidth; int imgHeight = bm.bmHeight; long hDC = device.internal_new_GC(null); @@ -993,8 +981,14 @@ long [] createGdipImage() { OS.GetObject(memDib, BITMAP.sizeof, dibBM); int sizeInBytes = dibBM.bmWidthBytes * dibBM.bmHeight; OS.BitBlt(memHdc, 0, 0, imgWidth, imgHeight, srcHdc, 0, 0, OS.SRCCOPY); + long hHeap = OS.GetProcessHeap(); + long pixels = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, sizeInBytes); + if (pixels == 0) SWT.error(SWT.ERROR_NO_HANDLES); byte red = 0, green = 0, blue = 0; - if (transparentPixel != -1) { + if (hasAlpha) { + OS.MoveMemory(pixels, bm.bmBits, sizeInBytes); + } + else { if (bm.bmBitsPixel <= 8) { byte[] color = new byte[4]; OS.GetDIBColorTable(srcHdc, transparentPixel, 1, color); @@ -1029,30 +1023,8 @@ long [] createGdipImage() { break; } } - } - OS.SelectObject(srcHdc, oldSrcBitmap); - OS.SelectObject(memHdc, oldMemBitmap); - OS.DeleteObject(srcHdc); - OS.DeleteObject(memHdc); - byte[] srcData = new byte[sizeInBytes]; - OS.MoveMemory(srcData, dibBM.bmBits, sizeInBytes); - OS.DeleteObject(memDib); - device.internal_dispose_GC(hDC, null); - if (alpha != -1) { - for (int y = 0, dp = 0; y < imgHeight; ++y) { - for (int x = 0; x < imgWidth; ++x) { - srcData[dp + 3] = (byte)alpha; - dp += 4; - } - } - } else if (alphaData != null) { - for (int y = 0, dp = 0, ap = 0; y < imgHeight; ++y) { - for (int x = 0; x < imgWidth; ++x) { - srcData[dp + 3] = alphaData[ap++]; - dp += 4; - } - } - } else if (transparentPixel != -1) { + byte[] srcData = new byte[sizeInBytes]; + OS.MoveMemory(srcData, dibBM.bmBits, sizeInBytes); for (int y = 0, dp = 0; y < imgHeight; ++y) { for (int x = 0; x < imgWidth; ++x) { if (srcData[dp] == blue && srcData[dp + 1] == green && srcData[dp + 2] == red) { @@ -1063,12 +1035,15 @@ long [] createGdipImage() { dp += 4; } } + OS.MoveMemory(pixels, srcData, sizeInBytes); } - long hHeap = OS.GetProcessHeap(); - long pixels = OS.HeapAlloc(hHeap, OS.HEAP_ZERO_MEMORY, srcData.length); - if (pixels == 0) SWT.error(SWT.ERROR_NO_HANDLES); - OS.MoveMemory(pixels, srcData, sizeInBytes); - return new long []{Gdip.Bitmap_new(imgWidth, imgHeight, dibBM.bmWidthBytes, Gdip.PixelFormat32bppARGB, pixels), pixels}; + OS.SelectObject(srcHdc, oldSrcBitmap); + OS.SelectObject(memHdc, oldMemBitmap); + OS.DeleteObject(srcHdc); + OS.DeleteObject(memHdc); + OS.DeleteObject(memDib); + int pixelFormat = hasAlpha ? Gdip.PixelFormat32bppPARGB : Gdip.PixelFormat32bppARGB; + return new long []{Gdip.Bitmap_new(imgWidth, imgHeight, dibBM.bmWidthBytes, pixelFormat, pixels), pixels}; } return new long []{Gdip.Bitmap_new(handle, 0), 0}; } @@ -1633,10 +1608,32 @@ public ImageData getImageDataAtCurrentZoom() { /* Construct and return the ImageData */ ImageData imageData = new ImageData(width, height, depth, palette, 4, data); imageData.transparentPixel = this.transparentPixel; - imageData.alpha = alpha; - if (alpha == -1 && alphaData != null) { - imageData.alphaData = new byte[alphaData.length]; - System.arraycopy(alphaData, 0, imageData.alphaData, 0, alphaData.length); + if (isDib && depth == 32) { + byte straightData[] = new byte[imageSize]; + byte alphaData[] = new byte[width * height]; + boolean validAlpha = true; + for (int ap = 0, dp = 0; validAlpha && ap < alphaData.length; ap++, dp += 4) { + int b = data[dp ] & 0xFF; + int g = data[dp + 1] & 0xFF; + int r = data[dp + 2] & 0xFF; + int a = data[dp + 3] & 0xFF; + alphaData[ap] = (byte) a; + validAlpha = validAlpha && b <= a && g <= a && r <= a; + if (a != 0) { + straightData[dp ] = (byte) (((b * 0xFF) + a / 2) / a); + straightData[dp + 1] = (byte) (((g * 0xFF) + a / 2) / a); + straightData[dp + 2] = (byte) (((r * 0xFF) + a / 2) / a); + } + } + if (validAlpha) { + imageData.data = straightData; + imageData.alphaData = alphaData; + } + else { + for (int dp = 3; dp < imageSize; dp += 4) { + data[dp] = (byte) 0xFF; + } + } } return imageData; } @@ -1684,6 +1681,7 @@ void init(int width, int height) { int planes = OS.GetDeviceCaps(hDC, OS.PLANES); int depth = bits * planes; if (depth < 16) depth = 16; + if (depth > 24) depth = 24; handle = createDIB(width, height, depth); } if (handle != 0) { @@ -1729,6 +1727,9 @@ static long [] init(Device device, Image image, ImageData i) { img.alphaData = i.alphaData; i = img; } + + boolean hasAlpha = i.alpha != -1 || i.alphaData != null; + /* * Windows supports 16-bit mask of (0x7C00, 0x3E0, 0x1F), * 24-bit mask of (0xFF0000, 0xFF00, 0xFF) and 32-bit mask @@ -1745,51 +1746,56 @@ static long [] init(Device device, Image image, ImageData i) { int newOrder = ImageData.MSB_FIRST; PaletteData newPalette = null; - switch (i.depth) { - case 8: - /* - * Bug 566545. Usually each color mask selects a different part of the pixel - * value to encode the according color. In this common case it is rather trivial - * to convert an 8-bit direct color image to the Windows supported 16-bit image. - * However there is no enforcement for the color masks to be disjunct. For - * example an 8-bit image where all color masks select the same 8-bit of pixel - * value (mask = 0xFF and shift = 0 for all colors) results in a very efficient - * 8-bit gray-scale image without the need of defining a color table. - * - * That's why we need to calculate the actual required depth if all colors are - * stored non-overlapping which might require 24-bit instead of the usual - * expected 16-bit. - */ - int minDepth = ImageData.getChannelWidth(redMask, palette.redShift) - + ImageData.getChannelWidth(greenMask, palette.greenShift) - + ImageData.getChannelWidth(blueMask, palette.blueShift); - if (minDepth <= 16) { - newDepth = 16; + if (hasAlpha) { + newDepth = 32; + newPalette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); + } + else { + switch (i.depth) { + case 8: + /* + * Bug 566545. Usually each color mask selects a different part of the pixel + * value to encode the according color. In this common case it is rather trivial + * to convert an 8-bit direct color image to the Windows supported 16-bit image. + * However there is no enforcement for the color masks to be disjunct. For + * example an 8-bit image where all color masks select the same 8-bit of pixel + * value (mask = 0xFF and shift = 0 for all colors) results in a very efficient + * 8-bit gray-scale image without the need of defining a color table. + * + * That's why we need to calculate the actual required depth if all colors are + * stored non-overlapping which might require 24-bit instead of the usual + * expected 16-bit. + */ + int minDepth = ImageData.getChannelWidth(redMask, palette.redShift) + + ImageData.getChannelWidth(greenMask, palette.greenShift) + + ImageData.getChannelWidth(blueMask, palette.blueShift); + if (minDepth <= 16) { + newDepth = 16; + newOrder = ImageData.LSB_FIRST; + newPalette = new PaletteData(0x7C00, 0x3E0, 0x1F); + } else { + newDepth = 24; + newPalette = new PaletteData(0xFF, 0xFF00, 0xFF0000); + } + break; + case 16: newOrder = ImageData.LSB_FIRST; - newPalette = new PaletteData(0x7C00, 0x3E0, 0x1F); - } else { + if (!(redMask == 0x7C00 && greenMask == 0x3E0 && blueMask == 0x1F)) { + newPalette = new PaletteData(0x7C00, 0x3E0, 0x1F); + } + break; + case 24: + if (!(redMask == 0xFF && greenMask == 0xFF00 && blueMask == 0xFF0000)) { + newPalette = new PaletteData(0xFF, 0xFF00, 0xFF0000); + } + break; + case 32: newDepth = 24; newPalette = new PaletteData(0xFF, 0xFF00, 0xFF0000); - } - break; - case 16: - newOrder = ImageData.LSB_FIRST; - if (!(redMask == 0x7C00 && greenMask == 0x3E0 && blueMask == 0x1F)) { - newPalette = new PaletteData(0x7C00, 0x3E0, 0x1F); - } - break; - case 24: - if (!(redMask == 0xFF && greenMask == 0xFF00 && blueMask == 0xFF0000)) { - newPalette = new PaletteData(0xFF, 0xFF00, 0xFF0000); - } - break; - case 32: - if (!(redMask == 0xFF00 && greenMask == 0xFF0000 && blueMask == 0xFF000000)) { - newPalette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); - } - break; - default: - SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); + break; + default: + SWT.error(SWT.ERROR_UNSUPPORTED_DEPTH); + } } if (newPalette != null) { ImageData img = new ImageData(i.width, i.height, newDepth, newPalette); @@ -1808,6 +1814,73 @@ static long [] init(Device device, Image image, ImageData i) { i = img; } } + else if (hasAlpha) { + int newDepth = 32; + PaletteData newPalette = new PaletteData(0xFF00, 0xFF0000, 0xFF000000); + int newOrder = ImageData.MSB_FIRST; + RGB[] rgbs = i.palette.getRGBs(); + int length = rgbs.length; + byte[] srcReds = new byte[length]; + byte[] srcGreens = new byte[length]; + byte[] srcBlues = new byte[length]; + for (int j = 0; j < rgbs.length; j++) { + RGB rgb = rgbs[j]; + if (rgb == null) continue; + srcReds[j] = (byte)rgb.red; + srcGreens[j] = (byte)rgb.green; + srcBlues[j] = (byte)rgb.blue; + } + ImageData img = new ImageData(i.width, i.height, newDepth, newPalette); + ImageData.blit(ImageData.BLIT_SRC, + i.data, i.depth, i.bytesPerLine, i.getByteOrder(), 0, 0, i.width, i.height, srcReds, srcGreens, srcBlues, + ImageData.ALPHA_OPAQUE, null, 0, 0, 0, + img.data, img.depth, img.bytesPerLine, newOrder, 0, 0, img.width, img.height, newPalette.redMask, newPalette.greenMask, newPalette.blueMask, + false, false); + + if (i.transparentPixel != -1) { + img.transparentPixel = newPalette.getPixel(i.palette.getRGB(i.transparentPixel)); + } + img.maskPad = i.maskPad; + img.maskData = i.maskData; + img.alpha = i.alpha; + img.alphaData = i.alphaData; + i = img; + } + if (i.alpha != -1) { + int alpha = i.alpha & 0xFF; + byte[] data = i.data; + for (int dp = 0; dp < i.data.length; dp += 4) { + /* pre-multiplied alpha */ + int r = ((data[dp ] & 0xFF) * alpha) + 128; + r = (r + (r >> 8)) >> 8; + int g = ((data[dp + 1] & 0xFF) * alpha) + 128; + g = (g + (g >> 8)) >> 8; + int b = ((data[dp + 2] & 0xFF) * alpha) + 128; + b = (b + (b >> 8)) >> 8; + data[dp ] = (byte) b; + data[dp + 1] = (byte) g; + data[dp + 2] = (byte) r; + data[dp + 3] = (byte) alpha; + } + } + else if (i.alphaData != null) { + byte[] data = i.data; + for (int ap = 0, dp = 0; dp < i.data.length; ap++, dp += 4) { + /* pre-multiplied alpha */ + int a = i.alphaData[ap] & 0xFF; + int r = ((data[dp ] & 0xFF) * a) + 128; + r = (r + (r >> 8)) >> 8; + int g = ((data[dp + 1] & 0xFF) * a) + 128; + g = (g + (g >> 8)) >> 8; + int b = ((data[dp + 2] & 0xFF) * a) + 128; + b = (b + (b >> 8)) >> 8; + data[dp ] = (byte) r; + data[dp + 1] = (byte) g; + data[dp + 2] = (byte) b; + data[dp + 3] = (byte) a; + } + } + /* Construct bitmap info header by hand */ RGB[] rgbs = i.palette.getRGBs(); BITMAPINFOHEADER bmiHeader = new BITMAPINFOHEADER(); @@ -1894,14 +1967,6 @@ static long [] init(Device device, Image image, ImageData i) { image.handle = hDib; image.type = SWT.BITMAP; image.transparentPixel = i.transparentPixel; - if (image.transparentPixel == -1) { - image.alpha = i.alpha; - if (i.alpha == -1 && i.alphaData != null) { - int length = i.alphaData.length; - image.alphaData = new byte[length]; - System.arraycopy(i.alphaData, 0, image.alphaData, 0, length); - } - } } } return result; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java index 4f8ae61709..1c71538df3 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/graphics/TextLayout.java @@ -2871,10 +2871,12 @@ StyleItem[] merge (long items, int itemCount) { } } + boolean mayNeedSplit = true; int styleLimit = translateOffset(styles[styleIndex + 1].start); if (styleLimit <= itemLimit) { int runLen = styleLimit - start; if (runLen < MAX_RUN_LENGTH) { + mayNeedSplit = false; styleIndex++; start = styleLimit; if (start < itemLimit && 0 < start && start < end) { @@ -2888,7 +2890,7 @@ StyleItem[] merge (long items, int itemCount) { } } int runLen = itemLimit - start; - if (runLen > MAX_RUN_LENGTH) { + if (mayNeedSplit && runLen > MAX_RUN_LENGTH) { start += splitLongRun(item); } else if (itemLimit <= styleLimit) { itemIndex = nextItemIndex; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java index ff2a39ff6c..34ccb923d2 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/internal/ImageList.java @@ -151,10 +151,16 @@ long copyWithAlpha (long hBitmap, int background, byte[] alphaData, int destWidt /* Merge the alpha channel in place */ if (alphaData != null) { int spinc = dibBM.bmWidthBytes - srcWidth * 4; - int ap = 0, sp = 3; + int ap = 0, sp = 0; for (int y = 0; y < srcHeight; ++y) { for (int x = 0; x < srcWidth; ++x) { - srcData [sp] = alphaData [ap++]; + int a = alphaData [ap++] & 0xFF; + if (a != 0) { + srcData [sp ] = (byte)((((srcData [sp ] & 0xFF) * 0xFF) + a / 2) / a); + srcData [sp + 1] = (byte)((((srcData [sp + 1] & 0xFF) * 0xFF) + a / 2) / a); + srcData [sp + 2] = (byte)((((srcData [sp + 2] & 0xFF) * 0xFF) + a / 2) / a); + } + srcData [sp + 3] = (byte)a; sp += 4; } sp += spinc; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java index 82516a201f..592eaedc40 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Button.java @@ -1433,6 +1433,71 @@ LRESULT wmNotifyChild (NMHDR hdr, long wParam, long lParam) { return super.wmNotifyChild (hdr, wParam, lParam); } +static int getThemeStateId(int style, boolean pressed, boolean enabled) { + int direction = style & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT); + + /* + * Feature in Windows. DrawThemeBackground() does not mirror the drawing. + * The fix is switch left to right and right to left. + */ + if ((style & SWT.MIRRORED) != 0) { + if (direction == SWT.LEFT) { + direction = SWT.RIGHT; + } else if (direction == SWT.RIGHT) { + direction = SWT.LEFT; + } + } + + /* + * On Win11, scrollbars no longer show arrows by default. + * Arrows only show up when hot/disabled/pushed. + * The workaround is to use hot image in place of default. + */ + boolean hot = false; + if (OS.WIN32_BUILD >= OS.WIN32_BUILD_WIN11_21H2) { + if (!pressed && enabled) { + hot = true; + } + } + + if (hot) { + switch (direction) { + case SWT.UP: return OS.ABS_UPHOT; + case SWT.DOWN: return OS.ABS_DOWNHOT; + case SWT.LEFT: return OS.ABS_LEFTHOT; + case SWT.RIGHT: return OS.ABS_RIGHTHOT; + } + } + + if (pressed) { + switch (direction) { + case SWT.UP: return OS.ABS_UPPRESSED; + case SWT.DOWN: return OS.ABS_DOWNPRESSED; + case SWT.LEFT: return OS.ABS_LEFTPRESSED; + case SWT.RIGHT: return OS.ABS_RIGHTPRESSED; + } + } + + if (!enabled) { + switch (direction) { + case SWT.UP: return OS.ABS_UPDISABLED; + case SWT.DOWN: return OS.ABS_DOWNDISABLED; + case SWT.LEFT: return OS.ABS_LEFTDISABLED; + case SWT.RIGHT: return OS.ABS_RIGHTDISABLED; + } + } + + switch (direction) { + case SWT.UP: return OS.ABS_UPNORMAL; + case SWT.DOWN: return OS.ABS_DOWNNORMAL; + case SWT.LEFT: return OS.ABS_LEFTNORMAL; + case SWT.RIGHT: return OS.ABS_RIGHTNORMAL; + } + + // Have some sane value if all else fails + return OS.ABS_LEFTNORMAL; +} + @Override LRESULT wmDrawChild (long wParam, long lParam) { if ((style & SWT.ARROW) == 0) return super.wmDrawChild (wParam, lParam); @@ -1441,29 +1506,9 @@ LRESULT wmDrawChild (long wParam, long lParam) { RECT rect = new RECT (); OS.SetRect (rect, struct.left, struct.top, struct.right, struct.bottom); if (OS.IsAppThemed ()) { - int iStateId = OS.ABS_LEFTNORMAL; - switch (style & (SWT.UP | SWT.DOWN | SWT.LEFT | SWT.RIGHT)) { - case SWT.UP: iStateId = OS.ABS_UPNORMAL; break; - case SWT.DOWN: iStateId = OS.ABS_DOWNNORMAL; break; - case SWT.LEFT: iStateId = OS.ABS_LEFTNORMAL; break; - case SWT.RIGHT: iStateId = OS.ABS_RIGHTNORMAL; break; - } - /* - * Feature in Windows. DrawThemeBackground() does not mirror the drawing. - * The fix is switch left to right and right to left. - */ - if ((style & SWT.MIRRORED) != 0) { - if ((style & (SWT.LEFT | SWT.RIGHT)) != 0) { - iStateId = iStateId == OS.ABS_RIGHTNORMAL ? OS.ABS_LEFTNORMAL : OS.ABS_RIGHTNORMAL; - } - } - /* - * NOTE: The normal, hot, pressed and disabled state is - * computed relying on the fact that the increment between - * the direction states is invariant (always separated by 4). - */ - if (!getEnabled ()) iStateId += OS.ABS_UPDISABLED - OS.ABS_UPNORMAL; - if ((struct.itemState & OS.ODS_SELECTED) != 0) iStateId += OS.ABS_UPPRESSED - OS.ABS_UPNORMAL; + boolean pressed = ((struct.itemState & OS.ODS_SELECTED) != 0); + boolean enabled = getEnabled (); + int iStateId = getThemeStateId(style, pressed, enabled); OS.DrawThemeBackground (display.hScrollBarTheme (), struct.hDC, OS.SBP_ARROWBTN, iStateId, rect, null); } else { int uState = OS.DFCS_SCROLLLEFT; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Caret.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Caret.java index bc0f470331..5b5342b940 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Caret.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Caret.java @@ -38,6 +38,9 @@ import org.eclipse.swt.internal.win32.*; * @noextend This class is not intended to be subclassed by clients. */ public class Caret extends Widget { + /** The Caret last updated on the OS-level */ + private static Caret currentCaret; + Canvas parent; int x, y, width, height; boolean moved, resized; @@ -285,6 +288,7 @@ void killFocus () { void move () { moved = false; + setCurrentCaret(this); if (!OS.SetCaretPos (x, y)) return; resizeIME (); } @@ -332,6 +336,9 @@ void releaseParent () { @Override void releaseWidget () { super.releaseWidget (); + if (isCurrentCaret()) { + setCurrentCaret(null); + } parent = null; image = null; font = null; @@ -390,7 +397,7 @@ public void setBounds (int x, int y, int width, int height) { void setBoundsInPixels (int x, int y, int width, int height) { boolean samePosition = this.x == x && this.y == y; boolean sameExtent = this.width == width && this.height == height; - if (samePosition && sameExtent) return; + if (samePosition && sameExtent && isCurrentCaret()) return; this.x = x; this.y = y; this.width = width; @@ -530,12 +537,20 @@ public void setLocation (int x, int y) { } void setLocationInPixels (int x, int y) { - if (this.x == x && this.y == y) return; + if (this.x == x && this.y == y && isCurrentCaret()) return; this.x = x; this.y = y; moved = true; if (isVisible && hasFocus ()) move (); } +private boolean isCurrentCaret() { + return Caret.currentCaret == this; +} + +private void setCurrentCaret(Caret caret) { + Caret.currentCaret = caret; +} + /** * Sets the receiver's location to the point specified by * the argument which is relative to the receiver's @@ -572,7 +587,7 @@ public void setSize (int width, int height) { } void setSizeInPixels (int width, int height) { - if (this.width == width && this.height == height) return; + if (this.width == width && this.height == height && isCurrentCaret()) return; this.width = width; this.height = height; resized = true; if (isVisible && hasFocus ()) resize (); diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java index 151be728f1..2e71ec17df 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Display.java @@ -991,39 +991,6 @@ static long create32bitDIB (Image image) { dp += 4; } } - } else if (alpha != -1) { - for (int y = 0, dp = 0; y < imgHeight; ++y) { - for (int x = 0; x < imgWidth; ++x) { - int r = ((srcData[dp + 0] & 0xFF) * alpha) + 128; - r = (r + (r >> 8)) >> 8; - int g = ((srcData[dp + 1] & 0xFF) * alpha) + 128; - g = (g + (g >> 8)) >> 8; - int b = ((srcData[dp + 2] & 0xFF) * alpha) + 128; - b = (b + (b >> 8)) >> 8; - srcData[dp+0] = (byte)r; - srcData[dp+1] = (byte)g; - srcData[dp+2] = (byte)b; - srcData[dp+3] = (byte)alpha; - dp += 4; - } - } - } else if (alphaData != null) { - for (int y = 0, dp = 0, ap = 0; y < imgHeight; ++y) { - for (int x = 0; x < imgWidth; ++x) { - int a = alphaData[ap++] & 0xFF; - int r = ((srcData[dp + 0] & 0xFF) * a) + 128; - r = (r + (r >> 8)) >> 8; - int g = ((srcData[dp + 1] & 0xFF) * a) + 128; - g = (g + (g >> 8)) >> 8; - int b = ((srcData[dp + 2] & 0xFF) * a) + 128; - b = (b + (b >> 8)) >> 8; - srcData[dp+0] = (byte)r; - srcData[dp+1] = (byte)g; - srcData[dp+2] = (byte)b; - srcData[dp+3] = (byte)a; - dp += 4; - } - } } else if (transparentPixel != -1) { for (int y = 0, dp = 0; y < imgHeight; ++y) { for (int x = 0; x < imgWidth; ++x) { @@ -1035,7 +1002,7 @@ static long create32bitDIB (Image image) { dp += 4; } } - } else { + } else if (alpha == -1 && alphaData == null) { for (int y = 0, dp = 0; y < imgHeight; ++y) { for (int x = 0; x < imgWidth; ++x) { srcData [dp + 3] = (byte)0xFF; @@ -1117,6 +1084,11 @@ static long create32bitDIB (long hBitmap, int alpha, byte [] alphaData, int tran if (alpha != -1) { for (int y = 0, dp = 0; y < imgHeight; ++y) { for (int x = 0; x < imgWidth; ++x) { + if (alpha != 0) { + srcData [dp ] = (byte)((((srcData[dp ] & 0xFF) * 0xFF) + alpha / 2) / alpha); + srcData [dp + 1] = (byte)((((srcData[dp + 1] & 0xFF) * 0xFF) + alpha / 2) / alpha); + srcData [dp + 2] = (byte)((((srcData[dp + 2] & 0xFF) * 0xFF) + alpha / 2) / alpha); + } srcData [dp + 3] = (byte)alpha; dp += 4; } @@ -1124,7 +1096,13 @@ static long create32bitDIB (long hBitmap, int alpha, byte [] alphaData, int tran } else if (alphaData != null) { for (int y = 0, dp = 0, ap = 0; y < imgHeight; ++y) { for (int x = 0; x < imgWidth; ++x) { - srcData [dp + 3] = alphaData [ap++]; + int a = alphaData [ap++] & 0xFF; + if (a != 0) { + srcData [dp ] = (byte)((((srcData[dp ] & 0xFF) * 0xFF) + a / 2) / a); + srcData [dp + 1] = (byte)((((srcData[dp + 1] & 0xFF) * 0xFF) + a / 2) / a); + srcData [dp + 2] = (byte)((((srcData[dp + 2] & 0xFF) * 0xFF) + a / 2) / a); + } + srcData [dp + 3] = (byte)a; dp += 4; } } diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableColumn.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableColumn.java index 823a620c74..097ab46efa 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableColumn.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/TableColumn.java @@ -324,6 +324,75 @@ int getWidthInPixels () { } /** + * WINAPI doesn't provide any means to request column's optimal size. + * There is only an API to resize to optimal size. The workaround is to + * 1) disable redraw + * 2) resize to optimal + * 3) query new column size + * 4) set old column size + * 5) enable redraw + * This preserves old column size. As a consequence, no painting is + * needed after enabling redraw. + */ +private int calcAutoWidth(int index, boolean withHeader) { + long hwnd = parent.handle; + + // WM_SETREDRAW has a side effect of forcing Control to be visible. + // On the other hand, if control is invisible, 'WM_SETREDRAW' is not needed. + int style = OS.GetWindowLong (hwnd, OS.GWL_STYLE); + boolean isTableVisible = ((style & OS.WS_VISIBLE) != 0); + boolean isTableDrawing = parent.getDrawing (); + boolean needsDisableRedraw = isTableVisible && isTableDrawing; + + try { + if (needsDisableRedraw) { + // WM_SETREDRAW is used directly, because 'Control.setRedraw()' + // also repaints, which is to be avoided in this function. + OS.SendMessage (hwnd, OS.WM_SETREDRAW, 0, 0); + } + + int oldWidth = (int)OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0); + + /* + * Feature in Windows. When LVSCW_AUTOSIZE_USEHEADER is used + * with LVM_SETCOLUMNWIDTH to resize the last column, the last + * column is expanded to fill the client area. The fix is to + * resize the table to be small, set the column width and then + * restore the table to its original size. + * + * Note: temporarily setting LVS_EX_COLUMNSNAPPOINTS may be a + * less intrusive workaround. + */ + RECT rect = null; + boolean fixWidth = index == parent.getColumnCount () - 1; + if (fixWidth) { + rect = new RECT (); + OS.GetWindowRect (hwnd, rect); + OS.UpdateWindow (hwnd); + int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER; + OS.SetWindowPos (hwnd, 0, 0, 0, 0, rect.bottom - rect.top, flags); + } + + int resizeType = withHeader ? OS.LVSCW_AUTOSIZE_USEHEADER : OS.LVSCW_AUTOSIZE; + OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, resizeType); + + if (fixWidth) { + int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOZORDER; + OS.SetWindowPos (hwnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top, flags); + } + + int newWidth = (int)OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0); + OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, oldWidth); + + return newWidth; + } finally { + if (needsDisableRedraw) { + OS.SendMessage (hwnd, OS.WM_SETREDRAW, 1, 0); + } + } +} + +/** * Causes the receiver to be resized to its preferred size. * For a composite, this involves computing the preferred size * from its layout, if there is one. @@ -381,10 +450,8 @@ public void pack () { } if (newFont != 0) OS.SelectObject (hDC, oldFont); OS.ReleaseDC (hwnd, hDC); - OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, columnWidth); } else { - OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, OS.LVSCW_AUTOSIZE); - columnWidth = (int)OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0); + columnWidth = calcAutoWidth (index, false); if (index == 0) { /* * Bug in Windows. When LVM_SETCOLUMNWIDTH is used with LVSCW_AUTOSIZE @@ -412,38 +479,19 @@ public void pack () { } if (headerWidth > columnWidth) { if (!hasHeaderImage) { - /* - * Feature in Windows. When LVSCW_AUTOSIZE_USEHEADER is used - * with LVM_SETCOLUMNWIDTH to resize the last column, the last - * column is expanded to fill the client area. The fix is to - * resize the table to be small, set the column width and then - * restore the table to its original size. - */ - RECT rect = null; - boolean fixWidth = index == parent.getColumnCount () - 1; - if (fixWidth) { - rect = new RECT (); - OS.GetWindowRect (hwnd, rect); - OS.UpdateWindow (hwnd); - int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOREDRAW | OS.SWP_NOZORDER; - OS.SetWindowPos (hwnd, 0, 0, 0, 0, rect.bottom - rect.top, flags); - } - OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, OS.LVSCW_AUTOSIZE_USEHEADER); - if (fixWidth) { - int flags = OS.SWP_NOACTIVATE | OS.SWP_NOMOVE | OS.SWP_NOZORDER; - OS.SetWindowPos (hwnd, 0, 0, 0, rect.right - rect.left, rect.bottom - rect.top, flags); - } + // The code has been there for years and it's no longer clear why + // not just use 'headerWidth' here. Maybe because SWT's size + // calculation is imperfect and WINAPI will do it better? + columnWidth = calcAutoWidth (index, true); } else { - OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, headerWidth); - } - } else { - if (index == 0) { - OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, columnWidth); + columnWidth = headerWidth; } } + + OS.SendMessage (hwnd, OS.LVM_SETCOLUMNWIDTH, index, columnWidth); + parent.ignoreColumnResize = false; - int newWidth = (int)OS.SendMessage (hwnd, OS.LVM_GETCOLUMNWIDTH, index, 0); - if (oldWidth != newWidth) { + if (oldWidth != columnWidth) { updateToolTip (index); sendEvent (SWT.Resize); if (isDisposed ()) return; diff --git a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java index 3cb9a97aaf..488cf58683 100644 --- a/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java +++ b/bundles/org.eclipse.swt/Eclipse SWT/win32/org/eclipse/swt/widgets/Widget.java @@ -125,6 +125,7 @@ public abstract class Widget { * Prevents uninitialized instances from being created outside the package. */ Widget () { + notifyCreationTracker(); } /** @@ -162,6 +163,7 @@ public Widget (Widget parent, int style) { this.style = style; display = parent.display; reskinWidget (); + notifyCreationTracker(); } void _addListener (int eventType, Listener listener) { @@ -829,6 +831,7 @@ void release (boolean destroy) { releaseHandle (); } } + notifyDisposalTracker(); } } @@ -2487,4 +2490,17 @@ LRESULT wmXButtonUp (long hwnd, long wParam, long lParam) { } return result; } + +void notifyCreationTracker() { + if (WidgetSpy.isEnabled) { + WidgetSpy.getInstance().widgetCreated(this); + } +} + +void notifyDisposalTracker() { + if (WidgetSpy.isEnabled) { + WidgetSpy.getInstance().widgetDisposed(this); + } +} + } |