Fixed color struct, so now color queries match what C returned
This commit is contained in:
parent
2ca5cdf622
commit
baaaa72079
|
@ -541,7 +541,7 @@ class Gtk {
|
||||||
* appearance. (GTK+ actually keeps a cache of previously created styles, so a new style may not be created.)
|
* appearance. (GTK+ actually keeps a cache of previously created styles, so a new style may not be created.)
|
||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
Pointer gtk_rc_get_style(Pointer widget) {
|
GtkStyle gtk_rc_get_style(Pointer widget) {
|
||||||
return Gtk2.gtk_rc_get_style(widget);
|
return Gtk2.gtk_rc_get_style(widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -325,7 +325,7 @@ class Gtk2 {
|
||||||
* appearance. (GTK+ actually keeps a cache of previously created styles, so a new style may not be created.)
|
* appearance. (GTK+ actually keeps a cache of previously created styles, so a new style may not be created.)
|
||||||
*/
|
*/
|
||||||
public static native
|
public static native
|
||||||
Pointer gtk_rc_get_style(Pointer widget);
|
GtkStyle gtk_rc_get_style(Pointer widget);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Looks up color_name in the style’s logical color mappings, filling in color and returning TRUE if found, otherwise returning
|
* Looks up color_name in the style’s logical color mappings, filling in color and returning TRUE if found, otherwise returning
|
||||||
|
@ -365,14 +365,28 @@ class Gtk2 {
|
||||||
* by two pango_extents_to_pixels() calls, rounding ink_rect and logical_rect such that the rounded rectangles fully contain the
|
* by two pango_extents_to_pixels() calls, rounding ink_rect and logical_rect such that the rounded rectangles fully contain the
|
||||||
* unrounded one (that is, passes them as first argument to pango_extents_to_pixels()).
|
* unrounded one (that is, passes them as first argument to pango_extents_to_pixels()).
|
||||||
*
|
*
|
||||||
*
|
|
||||||
* @param layout a PangoLayout
|
* @param layout a PangoLayout
|
||||||
* @param ink_rect rectangle used to store the extents of the layout as drawn or NULL to indicate that the result is not needed.
|
* @param ink_rect rectangle used to store the extents of the layout as drawn or NULL to indicate that the result is not needed.
|
||||||
* @param logical_rect rectangle used to store the logical extents of the layout or NULL to indicate that the result is not needed.
|
* @param logical_rect rectangle used to store the logical extents of the layout or NULL to indicate that the result is not needed.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
public static native
|
public static native
|
||||||
void pango_layout_get_pixel_extents (Pointer layout, Pointer ink_rect, Pointer logical_rect);
|
void pango_layout_get_pixel_extents(Pointer layout, Pointer ink_rect, Pointer logical_rect);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates the GDK (windowing system) resources associated with a widget. For example, widget->window will be created when a widget
|
||||||
|
* is realized. Normally realization happens implicitly; if you show a widget and all its parent containers, then the widget will
|
||||||
|
* be realized and mapped automatically.
|
||||||
|
*
|
||||||
|
* Realizing a widget requires all the widget’s parent widgets to be realized; calling gtk_widget_realize() realizes the widget’s
|
||||||
|
* parents in addition to widget itself. If a widget is not yet inside a toplevel window when you realize it, bad things will happen.
|
||||||
|
*
|
||||||
|
* This function is primarily used in widget implementations, and isn’t very useful otherwise. Many times when you think you might
|
||||||
|
* need it, a better approach is to connect to a signal that will be called after the widget is realized automatically, such as
|
||||||
|
* “draw”. Or simply g_signal_connect() to the “realize” signal.
|
||||||
|
*/
|
||||||
|
public static native
|
||||||
|
void gtk_widget_realize(Pointer widget);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a toplevel container widget that is used to retrieve snapshots of widgets without showing them on the screen.
|
* Creates a toplevel container widget that is used to retrieve snapshots of widgets without showing them on the screen.
|
||||||
|
|
|
@ -36,120 +36,6 @@ class Gtk3 {
|
||||||
public static native
|
public static native
|
||||||
int gtk_get_micro_version();
|
int gtk_get_micro_version();
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a theme from the usual theme paths
|
|
||||||
*
|
|
||||||
* @param name A theme name
|
|
||||||
* @param variant variant to load, for example, "dark", or NULL for the default.
|
|
||||||
*
|
|
||||||
* @return a GtkCssProvider with the theme loaded. This memory is owned by GTK+, and you must not free it.
|
|
||||||
*
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
public static native
|
|
||||||
Pointer gtk_css_provider_get_named(String name, String variant);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the provider containing the style settings used as a fallback for all widgets.
|
|
||||||
*
|
|
||||||
* @return a GtkCssProvider with the theme loaded. This memory is owned by GTK+, and you must not free it.
|
|
||||||
*
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
public static native
|
|
||||||
Pointer gtk_css_provider_get_default();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Converts the provider into a string representation in CSS format.
|
|
||||||
* <p>
|
|
||||||
* Using gtk_css_provider_load_from_data() with the return value from this function on a new provider created with
|
|
||||||
* gtk_css_provider_new() will basically create a duplicate of this provider .
|
|
||||||
*
|
|
||||||
* @since 3.2 (released in 2011)
|
|
||||||
*/
|
|
||||||
public static native
|
|
||||||
String gtk_css_provider_to_string(Pointer provider);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the foreground color for a given state.
|
|
||||||
*
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
public static native
|
|
||||||
void gtk_style_context_get_color(Pointer context, int stateFlags, Pointer color);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the state used for style matching.
|
|
||||||
*
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
public static native
|
|
||||||
int gtk_style_context_get_state(Pointer context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Looks up and resolves a color name in the context color map.
|
|
||||||
*
|
|
||||||
* @since 3.0 (but not in the documentation...)
|
|
||||||
*/
|
|
||||||
public static native
|
|
||||||
boolean gtk_style_context_lookup_color(Pointer widget, String name, Pointer color);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the style context associated to widget . The returned object is guaranteed to be the same for the lifetime of widget .
|
|
||||||
*
|
|
||||||
* @since 3.0 (but not in the documentation...)
|
|
||||||
*/
|
|
||||||
public static native
|
|
||||||
Pointer gtk_widget_get_style_context(Pointer widget);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves the context state, so temporary modifications done through gtk_style_context_add_class(), gtk_style_context_remove_class(),
|
|
||||||
* gtk_style_context_set_state(), etc. can quickly be reverted in one go through gtk_style_context_restore().
|
|
||||||
* <p>
|
|
||||||
* The matching call to gtk_style_context_restore() must be done before GTK returns to the main loop.
|
|
||||||
*
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
public static native
|
|
||||||
void gtk_style_context_save(Pointer context);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores context state to a previous stage. See gtk_style_context_save().
|
|
||||||
*
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
public static native
|
|
||||||
void gtk_style_context_restore(Pointer context);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a style class to context , so posterior calls to gtk_style_context_get() or any of the gtk_render_*() functions will make
|
|
||||||
* use of this new class for styling.
|
|
||||||
*
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
public static native
|
|
||||||
void gtk_style_context_add_class(Pointer context, String className);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the padding for a given state as a GtkBorder. See gtk_style_context_get() and GTK_STYLE_PROPERTY_PADDING for details.
|
|
||||||
*
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
public static native
|
|
||||||
void gtk_style_context_get_padding(Pointer context, int state, Pointer border);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the border for a given state as a GtkBorder.
|
|
||||||
* <p>
|
|
||||||
* See gtk_style_context_get_property() and GTK_STYLE_PROPERTY_BORDER_WIDTH for details.
|
|
||||||
*
|
|
||||||
* @since 3.0
|
|
||||||
*/
|
|
||||||
public static native
|
|
||||||
void gtk_style_context_get_border(Pointer context, int state, Pointer border);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the internal scale factor that maps from window coordinates to the actual device pixels. On traditional systems this is 1,
|
* Returns the internal scale factor that maps from window coordinates to the actual device pixels. On traditional systems this is 1,
|
||||||
* but on very high density outputs this can be a higher value (often 2).
|
* but on very high density outputs this can be a higher value (often 2).
|
||||||
|
|
|
@ -15,15 +15,11 @@
|
||||||
*/
|
*/
|
||||||
package dorkbox.systemTray.jna.linux;
|
package dorkbox.systemTray.jna.linux;
|
||||||
|
|
||||||
import static dorkbox.systemTray.util.CssParser.injectAdditionalCss;
|
|
||||||
import static dorkbox.systemTray.util.CssParser.removeComments;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
import java.awt.Color;
|
||||||
import java.awt.Rectangle;
|
import java.awt.Rectangle;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintStream;
|
import java.io.PrintStream;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -34,16 +30,9 @@ import com.sun.jna.Pointer;
|
||||||
import com.sun.jna.ptr.PointerByReference;
|
import com.sun.jna.ptr.PointerByReference;
|
||||||
|
|
||||||
import dorkbox.systemTray.SystemTray;
|
import dorkbox.systemTray.SystemTray;
|
||||||
import dorkbox.systemTray.Tray;
|
|
||||||
import dorkbox.systemTray.jna.linux.structs.GdkColor;
|
|
||||||
import dorkbox.systemTray.jna.linux.structs.GdkRGBAColor;
|
|
||||||
import dorkbox.systemTray.jna.linux.structs.GtkRequisition;
|
import dorkbox.systemTray.jna.linux.structs.GtkRequisition;
|
||||||
import dorkbox.systemTray.jna.linux.structs.GtkStyle;
|
import dorkbox.systemTray.jna.linux.structs.GtkStyle;
|
||||||
import dorkbox.systemTray.jna.linux.structs.PangoRectangle;
|
import dorkbox.systemTray.jna.linux.structs.PangoRectangle;
|
||||||
import dorkbox.systemTray.util.CssParser;
|
|
||||||
import dorkbox.systemTray.util.CssParser.Css;
|
|
||||||
import dorkbox.systemTray.util.CssParser.CssNode;
|
|
||||||
import dorkbox.systemTray.util.CssParser.Entry;
|
|
||||||
import dorkbox.util.FileUtil;
|
import dorkbox.util.FileUtil;
|
||||||
import dorkbox.util.MathUtil;
|
import dorkbox.util.MathUtil;
|
||||||
import dorkbox.util.OS;
|
import dorkbox.util.OS;
|
||||||
|
@ -63,25 +52,26 @@ import dorkbox.util.process.ShellProcessBuilder;
|
||||||
@SuppressWarnings({"deprecation", "WeakerAccess"})
|
@SuppressWarnings({"deprecation", "WeakerAccess"})
|
||||||
public
|
public
|
||||||
class GtkTheme {
|
class GtkTheme {
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = true;
|
||||||
private static final boolean DEBUG_SHOW_CSS = false;
|
|
||||||
private static final boolean DEBUG_VERBOSE = false;
|
|
||||||
|
|
||||||
// CSS nodes that we care about, in oder of preference from left to right.
|
|
||||||
private static final
|
|
||||||
String[] cssNodes = new String[] {".menuitem", ".entry", "*"};
|
|
||||||
|
|
||||||
public static
|
public static
|
||||||
Rectangle getPixelTextHeight(String text) {
|
Rectangle getPixelTextHeight(String text) {
|
||||||
// have to use pango to get the size of text (for the checkmark size)
|
// the following method requires an offscreen widget to get the size of text (for the checkmark size) via pango
|
||||||
Pointer offscreen = Gtk.gtk_offscreen_window_new();
|
// don't forget to destroy everything!
|
||||||
|
Pointer menu = null;
|
||||||
|
Pointer item = null;
|
||||||
|
|
||||||
// we use the size of "X" as the checkmark
|
try {
|
||||||
Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic(text);
|
menu = Gtk.gtk_menu_new();
|
||||||
|
item = Gtk.gtk_image_menu_item_new_with_mnemonic(text);
|
||||||
|
|
||||||
Gtk.gtk_container_add(offscreen, item);
|
Gtk.gtk_container_add(menu, item);
|
||||||
|
|
||||||
// get the text widget (GtkAccelLabel) from inside the GtkMenuItem
|
Gtk2.gtk_widget_realize(menu);
|
||||||
|
Gtk2.gtk_widget_realize(item);
|
||||||
|
Gtk2.gtk_widget_show_all(menu);
|
||||||
|
|
||||||
|
// get the text widget (GtkAccelLabel/GtkLabel) from inside the GtkMenuItem
|
||||||
Pointer textLabel = Gtk.gtk_bin_get_child(item);
|
Pointer textLabel = Gtk.gtk_bin_get_child(item);
|
||||||
Pointer pangoLayout = Gtk.gtk_label_get_layout(textLabel);
|
Pointer pangoLayout = Gtk.gtk_label_get_layout(textLabel);
|
||||||
|
|
||||||
|
@ -91,40 +81,11 @@ class GtkTheme {
|
||||||
Gtk.pango_layout_get_pixel_extents(pangoLayout, ink.getPointer(), null);
|
Gtk.pango_layout_get_pixel_extents(pangoLayout, ink.getPointer(), null);
|
||||||
ink.read();
|
ink.read();
|
||||||
|
|
||||||
Rectangle size = new Rectangle(ink.width, ink.height);
|
return new Rectangle(ink.width, ink.height);
|
||||||
|
} finally {
|
||||||
Gtk.gtk_widget_destroy(item);
|
Gtk.gtk_widget_destroy(item);
|
||||||
Gtk.gtk_widget_destroy(offscreen);
|
Gtk.gtk_widget_destroy(menu);
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static
|
|
||||||
Rectangle getLogicalTextHeight(String text) {
|
|
||||||
// have to use pango to get the size of text (for the checkmark size)
|
|
||||||
Pointer offscreen = Gtk.gtk_offscreen_window_new();
|
|
||||||
|
|
||||||
// we use the size of "X" as the checkmark
|
|
||||||
Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic(text);
|
|
||||||
|
|
||||||
Gtk.gtk_container_add(offscreen, item);
|
|
||||||
|
|
||||||
// get the text widget (GtkAccelLabel) from inside the GtkMenuItem
|
|
||||||
Pointer textLabel = Gtk.gtk_bin_get_child(item);
|
|
||||||
Pointer pangoLayout = Gtk.gtk_label_get_layout(textLabel);
|
|
||||||
|
|
||||||
// logical pixel size (ascent + descent)
|
|
||||||
PangoRectangle logical = new PangoRectangle();
|
|
||||||
|
|
||||||
Gtk.pango_layout_get_pixel_extents(pangoLayout, null, logical.getPointer());
|
|
||||||
logical.read();
|
|
||||||
|
|
||||||
Rectangle size = new Rectangle(logical.width, logical.height);
|
|
||||||
|
|
||||||
Gtk.gtk_widget_destroy(item);
|
|
||||||
Gtk.gtk_widget_destroy(offscreen);
|
|
||||||
|
|
||||||
return size;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -172,7 +133,7 @@ class GtkTheme {
|
||||||
* - GtkStatusIndicator: ??
|
* - GtkStatusIndicator: ??
|
||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
int getIndicatorSize(final Class<? extends Tray> trayType) {
|
int getIndicatorSize() {
|
||||||
// Linux is similar enough, that it just uses this method
|
// Linux is similar enough, that it just uses this method
|
||||||
// https://wiki.archlinux.org/index.php/HiDPI
|
// https://wiki.archlinux.org/index.php/HiDPI
|
||||||
|
|
||||||
|
@ -181,6 +142,8 @@ class GtkTheme {
|
||||||
|
|
||||||
final AtomicReference<Double> screenScale = new AtomicReference<Double>();
|
final AtomicReference<Double> screenScale = new AtomicReference<Double>();
|
||||||
final AtomicInteger screenDPI = new AtomicInteger();
|
final AtomicInteger screenDPI = new AtomicInteger();
|
||||||
|
screenScale.set(0D);
|
||||||
|
screenDPI.set(0);
|
||||||
|
|
||||||
GtkEventDispatch.dispatchAndWait(new Runnable() {
|
GtkEventDispatch.dispatchAndWait(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
|
@ -196,7 +159,8 @@ class GtkTheme {
|
||||||
if (Gtk.isGtk3) {
|
if (Gtk.isGtk3) {
|
||||||
Pointer window = Gtk.gdk_get_default_root_window();
|
Pointer window = Gtk.gdk_get_default_root_window();
|
||||||
if (window != null) {
|
if (window != null) {
|
||||||
screenScale.set((double) Gtk3.gdk_window_get_scale_factor(window));
|
double scale = Gtk3.gdk_window_get_scale_factor(window);
|
||||||
|
screenScale.set(scale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -267,8 +231,7 @@ class GtkTheme {
|
||||||
|
|
||||||
|
|
||||||
OSUtil.DesktopEnv.Env env = OSUtil.DesktopEnv.get();
|
OSUtil.DesktopEnv.Env env = OSUtil.DesktopEnv.get();
|
||||||
// sometimes the scaling-factor is set
|
// sometimes the scaling-factor is set. If we have gsettings, great! otherwise try KDE
|
||||||
if (env == OSUtil.DesktopEnv.Env.Gnome) {
|
|
||||||
try {
|
try {
|
||||||
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8196);
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(8196);
|
||||||
PrintStream outputStream = new PrintStream(byteArrayOutputStream);
|
PrintStream outputStream = new PrintStream(byteArrayOutputStream);
|
||||||
|
@ -307,8 +270,8 @@ class GtkTheme {
|
||||||
}
|
}
|
||||||
} catch (Throwable ignore) {
|
} catch (Throwable ignore) {
|
||||||
}
|
}
|
||||||
}
|
|
||||||
else if (OSUtil.DesktopEnv.isKDE()) {
|
if (OSUtil.DesktopEnv.isKDE()) {
|
||||||
// check the custom KDE override file
|
// check the custom KDE override file
|
||||||
try {
|
try {
|
||||||
File customSettings = new File("/usr/bin/startkde-custom");
|
File customSettings = new File("/usr/bin/startkde-custom");
|
||||||
|
@ -364,7 +327,7 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac
|
||||||
if (mainFile.canRead()) {
|
if (mainFile.canRead()) {
|
||||||
List<String> lines = FileUtil.readLines(mainFile);
|
List<String> lines = FileUtil.readLines(mainFile);
|
||||||
boolean found = false;
|
boolean found = false;
|
||||||
int index = 0;
|
int index;
|
||||||
for (final String line : lines) {
|
for (final String line : lines) {
|
||||||
if (line.contains("<entry name=\"iconSize\" type=\"Int\">")) {
|
if (line.contains("<entry name=\"iconSize\" type=\"Int\">")) {
|
||||||
found = true;
|
found = true;
|
||||||
|
@ -416,7 +379,7 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (OSUtil.Linux.isUbuntu() && env == OSUtil.DesktopEnv.Env.Unity) {
|
if (OSUtil.Linux.isUbuntu() && env == OSUtil.DesktopEnv.Env.Unity || env == OSUtil.DesktopEnv.Env.Unity7) {
|
||||||
// if we measure on ubuntu unity using a screen shot (using swing, so....) , the max size was 24, HOWEVER this goes from
|
// if we measure on ubuntu unity using a screen shot (using swing, so....) , the max size was 24, HOWEVER this goes from
|
||||||
// the top->bottom of the indicator bar -- and since it was swing, it uses a different rendering method and it (honestly)
|
// the top->bottom of the indicator bar -- and since it was swing, it uses a different rendering method and it (honestly)
|
||||||
// looks weird, because there is no padding for the icon. The official AppIndicator size is hardcoded...
|
// looks weird, because there is no padding for the icon. The official AppIndicator size is hardcoded...
|
||||||
|
@ -511,145 +474,42 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac
|
||||||
*/
|
*/
|
||||||
public static
|
public static
|
||||||
Color getTextColor() {
|
Color getTextColor() {
|
||||||
// NOTE: when getting CSS, we redirect STDERR to null (via GTK), so that we won't spam the console if there are parse errors.
|
|
||||||
// this is a horrid hack, but the only way to work around the errors we have no control over. The parse errors, if bad enough
|
|
||||||
// just mean that we are unable to get the CSS as we want.
|
|
||||||
|
|
||||||
// these methods are from most accurate (but limited in support) to compatible across Linux OSes.. Strangely enough, GTK makes
|
|
||||||
// this information insanely difficult to get.
|
|
||||||
final AtomicReference<Color> color = new AtomicReference<Color>(null);
|
final AtomicReference<Color> color = new AtomicReference<Color>(null);
|
||||||
GtkEventDispatch.dispatchAndWait(new Runnable() {
|
GtkEventDispatch.dispatchAndWait(new Runnable() {
|
||||||
@SuppressWarnings("UnusedAssignment")
|
@SuppressWarnings("UnusedAssignment")
|
||||||
@Override
|
@Override
|
||||||
public
|
public
|
||||||
void run() {
|
void run() {
|
||||||
Color c;
|
Color c = null;
|
||||||
|
|
||||||
|
// the following method requires an offscreen widget to get the style information from.
|
||||||
|
// don't forget to destroy everything!
|
||||||
|
Pointer menu = null;
|
||||||
|
Pointer item = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
menu = Gtk.gtk_menu_new();
|
||||||
|
item = Gtk.gtk_image_menu_item_new_with_mnemonic("a");
|
||||||
|
|
||||||
|
Gtk.gtk_container_add(menu, item);
|
||||||
|
|
||||||
|
Gtk2.gtk_widget_realize(menu);
|
||||||
|
Gtk2.gtk_widget_realize(item);
|
||||||
|
Gtk2.gtk_widget_show_all(menu);
|
||||||
|
|
||||||
|
|
||||||
|
GtkStyle style = Gtk.gtk_rc_get_style(item);
|
||||||
|
style.read();
|
||||||
|
|
||||||
|
// this is the same color chromium uses (fg)
|
||||||
|
// https://chromium.googlesource.com/chromium/src/+/b3ca230ddd7d1238ee96ed26ea23e369f10dd655/chrome/browser/ui/libgtk2ui/gtk2_ui.cc#873
|
||||||
|
c = style.fg[Gtk.State.NORMAL].getColor();
|
||||||
|
|
||||||
// see if we can get the info via CSS properties (> GTK+ 3.2 uses an API, GTK2 gets it from disk).
|
|
||||||
// This is often the BEST way to get information, since GTK **DOES NOT** make it easy to get widget information BEFORE
|
|
||||||
// the widget is realized -- which in our case, we must do.
|
|
||||||
c = getColorFromCss();
|
|
||||||
if (c != null) {
|
|
||||||
if (DEBUG) {
|
|
||||||
System.err.println("Got from CSS");
|
|
||||||
}
|
|
||||||
color.set(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// try to get via the color scheme.
|
|
||||||
c = getFromColorScheme();
|
|
||||||
if (c != null) {
|
|
||||||
if (DEBUG) {
|
|
||||||
System.err.println("Got from color scheme");
|
|
||||||
}
|
|
||||||
color.set(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// if we get here, this means that there was NO "gtk-color-scheme" value in the theme file.
|
|
||||||
// This usually happens when the theme does not have @fg_color (for example), but instead has each color explicitly
|
|
||||||
// defined for every widget instance in the theme file. Old/bizzare themes tend to do it this way...
|
|
||||||
if (Gtk.isGtk2) {
|
|
||||||
c = getFromGtk2ThemeText();
|
|
||||||
if (c != null) {
|
|
||||||
if (DEBUG) {
|
|
||||||
System.err.println("Got from gtk2 color theme file");
|
|
||||||
}
|
|
||||||
color.set(c);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// the following methods all require an offscreen widget to get the style information from.
|
|
||||||
|
|
||||||
// create an off-screen widget (don't forget to destroy everything!)
|
|
||||||
Pointer offscreen = Gtk.gtk_offscreen_window_new();
|
|
||||||
Pointer item = Gtk.gtk_image_menu_item_new_with_mnemonic("a");
|
|
||||||
|
|
||||||
Gtk.gtk_container_add(offscreen, item);
|
|
||||||
Gtk.gtk_widget_show_all(item);
|
|
||||||
|
|
||||||
// Try to get via RC style... Sometimes this works (sometimes it does not...)
|
|
||||||
{
|
|
||||||
Pointer style = Gtk.gtk_rc_get_style(item);
|
|
||||||
|
|
||||||
GdkColor gdkColor = new GdkColor();
|
|
||||||
boolean success = false;
|
|
||||||
|
|
||||||
success = Gtk.gtk_style_lookup_color(style, "menu_fg_color", gdkColor.getPointer());
|
|
||||||
if (!success) {
|
|
||||||
success = Gtk.gtk_style_lookup_color(style, "text_color", gdkColor.getPointer());
|
|
||||||
if (success) {
|
|
||||||
System.err.println("a");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!success) {
|
|
||||||
success = Gtk.gtk_style_lookup_color(style, "theme_text_color", gdkColor.getPointer());
|
|
||||||
if (success) {
|
|
||||||
System.err.println("a");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (success) {
|
|
||||||
c = gdkColor.getColor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c != null) {
|
|
||||||
if (DEBUG) {
|
|
||||||
System.err.println("Got from gtk offscreen gtk_style_lookup_color");
|
|
||||||
}
|
|
||||||
color.set(c);
|
color.set(c);
|
||||||
|
} finally {
|
||||||
Gtk.gtk_widget_destroy(item);
|
Gtk.gtk_widget_destroy(item);
|
||||||
return;
|
Gtk.gtk_widget_destroy(menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (Gtk.isGtk3) {
|
|
||||||
Pointer context = Gtk3.gtk_widget_get_style_context(item);
|
|
||||||
|
|
||||||
GdkRGBAColor gdkColor = new GdkRGBAColor();
|
|
||||||
boolean success = false;
|
|
||||||
|
|
||||||
success = Gtk3.gtk_style_context_lookup_color(context, "fg_color", gdkColor.getPointer());
|
|
||||||
if (!success) {
|
|
||||||
success = Gtk3.gtk_style_context_lookup_color(context, "text_color", gdkColor.getPointer());
|
|
||||||
}
|
|
||||||
if (!success) {
|
|
||||||
success = Gtk3.gtk_style_context_lookup_color(context, "menu_fg_color", gdkColor.getPointer());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!success) {
|
|
||||||
success = Gtk3.gtk_style_context_lookup_color(context, "color", gdkColor.getPointer());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
c = gdkColor.getColor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c != null) {
|
|
||||||
color.set(c);
|
|
||||||
if (DEBUG) {
|
|
||||||
System.err.println("Got from gtk3 offscreen gtk_widget_get_style_context");
|
|
||||||
}
|
|
||||||
Gtk.gtk_widget_destroy(item);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// this doesn't always work...
|
|
||||||
GtkStyle.ByReference style = Gtk.gtk_widget_get_style(item);
|
|
||||||
color.set(style.text[Gtk.State.NORMAL].getColor());
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
System.err.println("Got from gtk gtk_widget_get_style");
|
|
||||||
}
|
|
||||||
|
|
||||||
Gtk.gtk_widget_destroy(item);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -669,604 +529,4 @@ My ratio is 1.47674, that means I have no scaling at all when there is a 1.5 fac
|
||||||
// who knows WHAT the color is supposed to be. This is just a "best guess" default value.
|
// who knows WHAT the color is supposed to be. This is just a "best guess" default value.
|
||||||
return Color.BLACK;
|
return Color.BLACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* get the color we are interested in via raw CSS parsing. This is specifically to get the color of the text of the
|
|
||||||
* appindicator/gtk-status-icon menu.
|
|
||||||
* <p>
|
|
||||||
* > GTK+ 3.2 uses an API, GTK2 gets it from disk
|
|
||||||
*
|
|
||||||
* @return the color string, parsed from CSS,
|
|
||||||
*/
|
|
||||||
private static
|
|
||||||
Color getColorFromCss() {
|
|
||||||
Css css = getCss();
|
|
||||||
if (css != null) {
|
|
||||||
if (DEBUG_SHOW_CSS) {
|
|
||||||
System.err.println(css);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
// collect a list of all of the sections that have what we are interested in.
|
|
||||||
List<CssNode> sections = CssParser.getSections(css, cssNodes, null);
|
|
||||||
List<Entry> colorStrings = CssParser.getAttributeFromSections(sections, "color", true);
|
|
||||||
|
|
||||||
String colorString = CssParser.selectMostRelevantAttribute(cssNodes, colorStrings);
|
|
||||||
|
|
||||||
if (colorString != null) {
|
|
||||||
if (colorString.startsWith("@")) {
|
|
||||||
// it's a color definition
|
|
||||||
String colorSubString = css.getColorDefinition(colorString.substring(1));
|
|
||||||
return parseColor(colorSubString);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return parseColor(colorString);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the CSS for the current theme or null. It is important that this is called AFTER GTK has been initialized.
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
Css getCss() {
|
|
||||||
String css;
|
|
||||||
if (Gtk.isLoaded && Gtk.isGtk3) {
|
|
||||||
final AtomicReference<String> css_ = new AtomicReference<String>(null);
|
|
||||||
|
|
||||||
GtkEventDispatch.dispatchAndWait(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
String themeName = getThemeName();
|
|
||||||
|
|
||||||
if (themeName != null) {
|
|
||||||
Pointer value = Gtk3.gtk_css_provider_get_named(themeName, null);
|
|
||||||
if (value != null) {
|
|
||||||
// we have the css provider!
|
|
||||||
|
|
||||||
// NOTE: This can output warnings if the theme doesn't parse correctly by GTK, so we suppress them
|
|
||||||
Glib.GLogFunc orig = Glib.g_log_set_default_handler(Glib.nullLogFunc, null);
|
|
||||||
|
|
||||||
css_.set(Gtk3.gtk_css_provider_to_string(value));
|
|
||||||
|
|
||||||
Glib.g_log_set_default_handler(orig, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Pointer value = Gtk3.gtk_css_provider_get_default();
|
|
||||||
if (value != null) {
|
|
||||||
// we have the css provider!
|
|
||||||
|
|
||||||
// NOTE: This can output warnings if the theme doesn't parse correctly by GTK, so we suppress them
|
|
||||||
Glib.GLogFunc orig = Glib.g_log_set_default_handler(Glib.nullLogFunc, null);
|
|
||||||
|
|
||||||
css_.set(Gtk3.gtk_css_provider_to_string(value));
|
|
||||||
|
|
||||||
Glib.g_log_set_default_handler(orig, null);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// will be either the string, or null.
|
|
||||||
css = css_.get();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// GTK2 has to get the GTK3 theme text a different way (parsing it from disk). SOMETIMES, the app must be GTK2, even though
|
|
||||||
// the system is GTK3. This works around the API restriction if we are an APP in GTK2 mode. This is not done ALL the time,
|
|
||||||
// because this is not as accurate as using the GTK3 API.
|
|
||||||
// This can also be a requirement if GTK is not loaded
|
|
||||||
css = getGtk3ThemeCssViaFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
return CssParser.parse(css);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* this works for GtkStatusIcon menus.
|
|
||||||
*
|
|
||||||
* @return the menu_fg/fg/text color from gtk-color-scheme or null
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
Color getFromColorScheme() {
|
|
||||||
Pointer screen = Gtk.gdk_screen_get_default();
|
|
||||||
Pointer settings = null;
|
|
||||||
|
|
||||||
if (screen != null) {
|
|
||||||
settings = Gtk.gtk_settings_get_for_screen(screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings != null) {
|
|
||||||
// see if we can get the info we want the EASY way (likely only when GTK+ 2 is used, but can be < GTK+ 3.2)...
|
|
||||||
|
|
||||||
// been deprecated since version 3.8
|
|
||||||
PointerByReference pointer = new PointerByReference();
|
|
||||||
Gobject.g_object_get(settings, "gtk-color-scheme", pointer.getPointer(), null);
|
|
||||||
|
|
||||||
|
|
||||||
// A palette of named colors for use in themes. The format of the string is
|
|
||||||
// name1: color1
|
|
||||||
// name2: color2
|
|
||||||
//
|
|
||||||
// Color names must be acceptable as identifiers in the gtkrc syntax, and color specifications must be in the format
|
|
||||||
// accepted by gdk_color_parse().
|
|
||||||
//
|
|
||||||
// Note that due to the way the color tables from different sources are merged, color specifications will be converted
|
|
||||||
// to hexadecimal form when getting this property.
|
|
||||||
//
|
|
||||||
// Starting with GTK+ 2.12, the entries can alternatively be separated by ';' instead of newlines:
|
|
||||||
// name1: color1; name2: color2; ...
|
|
||||||
//
|
|
||||||
// GtkSettings:gtk-color-scheme has been deprecated since version 3.8 and should not be used in newly-written code.
|
|
||||||
// Color scheme support was dropped and is no longer supported. You can still set this property, but it will be ignored.
|
|
||||||
|
|
||||||
|
|
||||||
Pointer value = pointer.getValue();
|
|
||||||
if (value != null) {
|
|
||||||
String s = value.getString(0);
|
|
||||||
if (!s.isEmpty()) {
|
|
||||||
if (DEBUG) {
|
|
||||||
System.out.println("\t string: " + s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: these are the values on my system when forcing GTK+ 2 (XUbuntu 16.04) with GtkStatusIcon and Aidwata theme
|
|
||||||
// bg_color_dark: #686868686868
|
|
||||||
// fg_color: #3c3c3c3c3c3c
|
|
||||||
// fm_color: #f7f7f7f7f7f7
|
|
||||||
// selected_fg_color: #ffffffffffff
|
|
||||||
// panel_bg: #686868686868
|
|
||||||
// text_color: #212121212121
|
|
||||||
// text_color_dark: #ffffffffffff
|
|
||||||
// tooltip_bg_color: #000000000000
|
|
||||||
// link_color: #2d2d7171b8b8
|
|
||||||
// tooltip_fg_color: #e1e1e1e1e1e1
|
|
||||||
// base_color: #fcfcfcfcfcfc
|
|
||||||
// bg_color: #cececececece
|
|
||||||
// selected_bg_color: #39398e8ee7e7
|
|
||||||
|
|
||||||
// list of colors, in order of importance, that we want to parse.
|
|
||||||
String colors[] = new String[] {"menu_fg_color", "fg_color", "text_color"};
|
|
||||||
|
|
||||||
for (String colorName : colors) {
|
|
||||||
int i = 0;
|
|
||||||
while (i != -1) {
|
|
||||||
i = s.indexOf(colorName, i);
|
|
||||||
if (i >= 0) {
|
|
||||||
try {
|
|
||||||
// the color will ALWAYS be in hex notation
|
|
||||||
|
|
||||||
// it is also possible to be separated by ; instead of newline
|
|
||||||
int endIndex = s.indexOf(';', i);
|
|
||||||
if (endIndex == -1) {
|
|
||||||
endIndex = s.indexOf('\n', i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s.charAt(i - 1) == '_') {
|
|
||||||
i = endIndex;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int startIndex = s.indexOf('#', i);
|
|
||||||
String colorString = s.substring(startIndex, endIndex)
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
if (DEBUG_VERBOSE) {
|
|
||||||
System.out.println("Color string: " + colorString);
|
|
||||||
}
|
|
||||||
return parseColor(colorString);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks in the following locations for the current GTK3 theme.
|
|
||||||
* <p>
|
|
||||||
* /usr/share/themes
|
|
||||||
* /opt/gnome/share/themes
|
|
||||||
*/
|
|
||||||
private static
|
|
||||||
String getGtk3ThemeCssViaFile() {
|
|
||||||
File themeDirectory = getThemeDirectory(true);
|
|
||||||
|
|
||||||
if (themeDirectory == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
File gtkFile = new File(themeDirectory, "gtk.css");
|
|
||||||
try {
|
|
||||||
StringBuilder stringBuilder = new StringBuilder((int) (gtkFile.length()));
|
|
||||||
FileUtil.read(gtkFile, stringBuilder);
|
|
||||||
|
|
||||||
removeComments(stringBuilder);
|
|
||||||
|
|
||||||
// only comments in the file
|
|
||||||
if (stringBuilder.length() < 2) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
injectAdditionalCss(themeDirectory, stringBuilder);
|
|
||||||
|
|
||||||
return stringBuilder.toString();
|
|
||||||
} catch (IOException e) {
|
|
||||||
// cant read the file or something else.
|
|
||||||
if (SystemTray.DEBUG) {
|
|
||||||
SystemTray.logger.error("Error getting RAW GTK3 theme file.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the discovered fg[NORMAL] or text[NORMAL] color for this theme or null
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
Color getFromGtk2ThemeText() {
|
|
||||||
String gtk2ThemeText = getGtk2ThemeText();
|
|
||||||
|
|
||||||
if (gtk2ThemeText != null) {
|
|
||||||
String[] colorText = new String[] {"fg[NORMAL]", "text[NORMAL]"};
|
|
||||||
for (String text : colorText) {
|
|
||||||
int i = 0;
|
|
||||||
while (i != -1) {
|
|
||||||
i = gtk2ThemeText.indexOf(text, i);
|
|
||||||
if (i != -1) {
|
|
||||||
if (i > 0 && gtk2ThemeText.charAt(i - 1) != '_') {
|
|
||||||
i += text.length();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
int j = gtk2ThemeText.indexOf("=", i);
|
|
||||||
if (j != -1) {
|
|
||||||
int lineEnd = gtk2ThemeText.indexOf('\n', j);
|
|
||||||
|
|
||||||
if (lineEnd != -1) {
|
|
||||||
String colorName = gtk2ThemeText.substring(j + 1, lineEnd)
|
|
||||||
.trim();
|
|
||||||
|
|
||||||
colorName = colorName.replaceAll("\"", "");
|
|
||||||
return parseColor(colorName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks in the following locations for the current GTK2 theme.
|
|
||||||
* <p>
|
|
||||||
* /usr/share/themes
|
|
||||||
* /opt/gnome/share/themes
|
|
||||||
*/
|
|
||||||
private static
|
|
||||||
String getGtk2ThemeText() {
|
|
||||||
File themeDirectory = getThemeDirectory(false);
|
|
||||||
|
|
||||||
if (themeDirectory == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ie: /usr/share/themes/Numix/gtk-2.0/gtkrc
|
|
||||||
File gtkFile = new File(themeDirectory, "gtkrc");
|
|
||||||
|
|
||||||
try {
|
|
||||||
StringBuilder stringBuilder = new StringBuilder((int) (gtkFile.length()));
|
|
||||||
FileUtil.read(gtkFile, stringBuilder);
|
|
||||||
|
|
||||||
removeComments(stringBuilder);
|
|
||||||
|
|
||||||
// only comments in the file
|
|
||||||
if (stringBuilder.length() < 2) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return stringBuilder.toString();
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
// cant read the file or something else.
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Figures out what the directory is for the specified type of GTK theme files (css/gtkrc/etc)
|
|
||||||
*
|
|
||||||
* @param gtk3 true if you want to look for the GTK3 theme dir, false if you want the GTK2 theme dir
|
|
||||||
*
|
|
||||||
* @return the directory or null if it cannot be found
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
File getThemeDirectory(boolean gtk3) {
|
|
||||||
String themeName = getThemeName();
|
|
||||||
|
|
||||||
if (themeName == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
String gtkType;
|
|
||||||
if (gtk3) {
|
|
||||||
gtkType = "gtk-3.0";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
gtkType = "gtk-2.0";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
String[] dirs = new String[] {"/usr/share/themes", "/opt/gnome/share/themes"};
|
|
||||||
|
|
||||||
// ie: /usr/share/themes
|
|
||||||
for (String dirName : dirs) {
|
|
||||||
File themesDir = new File(dirName);
|
|
||||||
|
|
||||||
File[] themeDirs = themesDir.listFiles();
|
|
||||||
if (themeDirs != null) {
|
|
||||||
// ie: /usr/share/themes/Numix
|
|
||||||
for (File themeDir : themeDirs) {
|
|
||||||
File[] files1 = themeDir.listFiles();
|
|
||||||
if (files1 != null) {
|
|
||||||
boolean isCorrectTheme;
|
|
||||||
|
|
||||||
File themeIndexFile = new File(themeDir, "index.theme");
|
|
||||||
try {
|
|
||||||
List<String> read = FileUtil.read(themeIndexFile, false);
|
|
||||||
for (String s : read) {
|
|
||||||
if (s.startsWith("GtkTheme=")) {
|
|
||||||
String calculatedThemeName = s.substring("GtkTheme=".length());
|
|
||||||
|
|
||||||
isCorrectTheme = calculatedThemeName.equals(themeName);
|
|
||||||
|
|
||||||
if (isCorrectTheme) {
|
|
||||||
// ie: /usr/share/themes/Numix/gtk-3.0/gtk.css
|
|
||||||
// the DARK variant is only used by some apps. The dark variant is NOT SYSTEM-WIDE!
|
|
||||||
return new File(themeDir, gtkType);
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (IOException ignored) {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parses out the color from a color:
|
|
||||||
* <p>
|
|
||||||
* - the word "transparent"
|
|
||||||
* - hex 12 digit #ffffaaaaffff
|
|
||||||
* - hex 6 digit #ffaaff
|
|
||||||
* - hex 3 digit #faf
|
|
||||||
* - rgb(r, g, b) rgb(33, 33, 33)
|
|
||||||
* - rgb(r, g, b) rgb(.6, .3, .3)
|
|
||||||
* - rgb(r%, g%, b%) rgb(10%, 20%, 30%)
|
|
||||||
* - rgba(r, g, b, a) rgb(33, 33, 33, 0.53)
|
|
||||||
* - rgba(r, g, b, a) rgb(.33, .33, .33, 0.53)
|
|
||||||
* - rgba(r, g, b, a) rgb(10%, 20%, 30%, 0.53)
|
|
||||||
* <p>
|
|
||||||
* Notes:
|
|
||||||
* - rgb(), when an int, is between 0-255
|
|
||||||
* - rgb(), when a float, is between 0.0-1.0
|
|
||||||
* - rgb(), when a percent, is between 0-100
|
|
||||||
* - alpha is always a float
|
|
||||||
*
|
|
||||||
* @return the parsed color
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("Duplicates")
|
|
||||||
private static
|
|
||||||
Color parseColor(String colorString) {
|
|
||||||
if (colorString == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
int red = 0;
|
|
||||||
int green = 0;
|
|
||||||
int blue = 0;
|
|
||||||
int alpha = 255;
|
|
||||||
|
|
||||||
if (colorString.startsWith("#")) {
|
|
||||||
colorString = colorString.substring(1);
|
|
||||||
|
|
||||||
if (colorString.length() > 11) {
|
|
||||||
red = Integer.parseInt(colorString.substring(0, 4), 16);
|
|
||||||
green = Integer.parseInt(colorString.substring(4, 8), 16);
|
|
||||||
blue = Integer.parseInt(colorString.substring(8), 16);
|
|
||||||
|
|
||||||
// Have to convert to positive int (value between 0 and 65535, these are 16 bits per pixel) that is from 0-255
|
|
||||||
red = red & 0x0000FFFF;
|
|
||||||
green = green & 0x0000FFFF;
|
|
||||||
blue = blue & 0x0000FFFF;
|
|
||||||
|
|
||||||
red = (red >> 8) & 0xFF;
|
|
||||||
green = (green >> 8) & 0xFF;
|
|
||||||
blue = (blue >> 8) & 0xFF;
|
|
||||||
}
|
|
||||||
else if (colorString.length() > 5) {
|
|
||||||
red = Integer.parseInt(colorString.substring(0, 2), 16);
|
|
||||||
green = Integer.parseInt(colorString.substring(2, 4), 16);
|
|
||||||
blue = Integer.parseInt(colorString.substring(4), 16);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
red = Integer.parseInt(colorString.substring(0, 1), 16);
|
|
||||||
green = Integer.parseInt(colorString.substring(1, 2), 16);
|
|
||||||
blue = Integer.parseInt(colorString.substring(2), 16);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (colorString.startsWith("rgba")) {
|
|
||||||
colorString = colorString.substring(colorString.indexOf('(') + 1, colorString.indexOf(')'));
|
|
||||||
String[] split = colorString.split(",");
|
|
||||||
|
|
||||||
String trim1 = split[0].trim();
|
|
||||||
String trim2 = split[1].trim();
|
|
||||||
String trim3 = split[2].trim();
|
|
||||||
String trim4 = split[3].trim();
|
|
||||||
|
|
||||||
if (colorString.contains("%")) {
|
|
||||||
trim1 = trim1.replace("%", "");
|
|
||||||
trim2 = trim2.replace("%", "");
|
|
||||||
trim3 = trim3.replace("%", "");
|
|
||||||
|
|
||||||
red = Integer.parseInt(trim1) * 255;
|
|
||||||
green = Integer.parseInt(trim2) * 255;
|
|
||||||
blue = Integer.parseInt(trim3) * 255;
|
|
||||||
}
|
|
||||||
else if (colorString.contains(".")) {
|
|
||||||
red = (int) (Float.parseFloat(trim1) * 255);
|
|
||||||
green = (int) (Float.parseFloat(trim2) * 255);
|
|
||||||
blue = (int) (Float.parseFloat(trim3) * 255);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
red = Integer.parseInt(trim1);
|
|
||||||
green = Integer.parseInt(trim2);
|
|
||||||
blue = Integer.parseInt(trim3);
|
|
||||||
}
|
|
||||||
|
|
||||||
float alphaF = Float.parseFloat(trim4);
|
|
||||||
alpha = (int) (alphaF * 255);
|
|
||||||
}
|
|
||||||
else if (colorString.startsWith("rgb")) {
|
|
||||||
colorString = colorString.substring(colorString.indexOf('(') + 1, colorString.indexOf(')'));
|
|
||||||
String[] split = colorString.split(",");
|
|
||||||
|
|
||||||
String trim1 = split[0].trim();
|
|
||||||
String trim2 = split[1].trim();
|
|
||||||
String trim3 = split[2].trim();
|
|
||||||
|
|
||||||
if (colorString.contains("%")) {
|
|
||||||
trim1 = trim1.replace("%", "");
|
|
||||||
trim2 = trim2.replace("%", "");
|
|
||||||
trim3 = trim3.replace("%", "");
|
|
||||||
|
|
||||||
red = Integer.parseInt(trim1) * 255;
|
|
||||||
green = Integer.parseInt(trim2) * 255;
|
|
||||||
blue = Integer.parseInt(trim3) * 255;
|
|
||||||
}
|
|
||||||
else if (colorString.contains(".")) {
|
|
||||||
red = (int) (Float.parseFloat(trim1) * 255);
|
|
||||||
green = (int) (Float.parseFloat(trim2) * 255);
|
|
||||||
blue = (int) (Float.parseFloat(trim3) * 255);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
red = Integer.parseInt(trim1);
|
|
||||||
green = Integer.parseInt(trim2);
|
|
||||||
blue = Integer.parseInt(trim3);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (colorString.contains("transparent")) {
|
|
||||||
alpha = 0;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
int index = colorString.indexOf(";");
|
|
||||||
if (index > 0) {
|
|
||||||
colorString = colorString.substring(0, index);
|
|
||||||
}
|
|
||||||
colorString = colorString.replaceAll("\"", "");
|
|
||||||
colorString = colorString.replaceAll("'", "");
|
|
||||||
|
|
||||||
// maybe it's just a "color" description, such as "red"?
|
|
||||||
try {
|
|
||||||
return Color.decode(colorString);
|
|
||||||
} catch (Exception e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Color(red, green, blue, alpha);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://wiki.archlinux.org/index.php/GTK%2B
|
|
||||||
* <p>
|
|
||||||
* gets the name of the currently loaded theme
|
|
||||||
* GTK+ 2:
|
|
||||||
* ~/.gtkrc-2.0
|
|
||||||
* gtk-icon-theme-name = "Adwaita"
|
|
||||||
* gtk-theme-name = "Adwaita"
|
|
||||||
* gtk-font-name = "DejaVu Sans 11"
|
|
||||||
* <p>
|
|
||||||
* <p>
|
|
||||||
* GTK+ 3:
|
|
||||||
* $XDG_CONFIG_HOME/gtk-3.0/settings.ini
|
|
||||||
* [Settings]
|
|
||||||
* gtk-icon-theme-name = Adwaita
|
|
||||||
* gtk-theme-name = Adwaita
|
|
||||||
* gtk-font-name = DejaVu Sans 11
|
|
||||||
* <p>
|
|
||||||
* <p>
|
|
||||||
* Note: The icon theme name is the name defined in the theme's index file, not the name of its directory.
|
|
||||||
* <p>
|
|
||||||
* directories:
|
|
||||||
* /usr/share/themes
|
|
||||||
* /opt/gnome/share/themes
|
|
||||||
* <p>
|
|
||||||
* GTK+ 2 user specific: ~/.gtkrc-2.0
|
|
||||||
* GTK+ 2 system wide: /etc/gtk-2.0/gtkrc
|
|
||||||
* <p>
|
|
||||||
* GTK+ 3 user specific: $XDG_CONFIG_HOME/gtk-3.0/settings.ini, or $HOME/.config/gtk-3.0/settings.ini if $XDG_CONFIG_HOME is not set
|
|
||||||
* GTK+ 3 system wide: /etc/gtk-3.0/settings.ini
|
|
||||||
*
|
|
||||||
* @return the theme name, or null if it cannot find it.
|
|
||||||
*/
|
|
||||||
public static
|
|
||||||
String getThemeName() {
|
|
||||||
final AtomicReference<String> themeName = new AtomicReference<String>(null);
|
|
||||||
|
|
||||||
GtkEventDispatch.dispatchAndWait(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public
|
|
||||||
void run() {
|
|
||||||
Pointer screen = Gtk.gdk_screen_get_default();
|
|
||||||
Pointer settings = null;
|
|
||||||
|
|
||||||
if (screen != null) {
|
|
||||||
settings = Gtk.gtk_settings_get_for_screen(screen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings != null) {
|
|
||||||
PointerByReference pointer = new PointerByReference();
|
|
||||||
Gobject.g_object_get(settings, "gtk-theme-name", pointer.getPointer(), null);
|
|
||||||
|
|
||||||
Pointer value = pointer.getValue();
|
|
||||||
if (value != null) {
|
|
||||||
themeName.set(value.getString(0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (DEBUG) {
|
|
||||||
System.err.println("Theme name: " + themeName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// will be either the string, or null.
|
|
||||||
return themeName.get();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,48 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2015 dorkbox, llc
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.systemTray.jna.linux.structs;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.sun.jna.Structure;
|
|
||||||
|
|
||||||
import dorkbox.util.Keep;
|
|
||||||
|
|
||||||
@Keep
|
|
||||||
public
|
|
||||||
class GParamSpecStruct extends Structure {
|
|
||||||
public
|
|
||||||
class ByValue extends GParamSpecStruct implements Structure.ByValue {}
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
class ByReference extends GParamSpecStruct implements Structure.ByReference {}
|
|
||||||
|
|
||||||
|
|
||||||
public GTypeInstanceStruct g_type_instance;
|
|
||||||
|
|
||||||
public String name; /* interned string */
|
|
||||||
// Pointer flags;
|
|
||||||
// double value_type;
|
|
||||||
// double owner_type; /* class or interface using this property */
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
List<String> getFieldOrder() {
|
|
||||||
return Arrays.asList("g_type_instance", "name");
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -42,7 +42,7 @@ class GdkColor extends Structure {
|
||||||
public short blue;
|
public short blue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert to positive int (value between 0 and 65535, these are 16 bits per pixel) that is from 0-255
|
* Convert from positive int (value between 0 and 65535, these are 16 bits per pixel) to values from 0-255
|
||||||
*/
|
*/
|
||||||
private static int convert(int inputColor) {
|
private static int convert(int inputColor) {
|
||||||
return (inputColor & 0x0000FFFF >> 8) & 0xFF;
|
return (inputColor & 0x0000FFFF >> 8) & 0xFF;
|
||||||
|
|
|
@ -1,68 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2017 dorkbox, llc
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
package dorkbox.systemTray.jna.linux.structs;
|
|
||||||
|
|
||||||
import java.awt.Color;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import com.sun.jna.Structure;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* https://developer.gnome.org/gdk3/stable/gdk3-RGBA-Colors.html#GdkRGBA
|
|
||||||
*/
|
|
||||||
public
|
|
||||||
class GdkRGBAColor extends Structure {
|
|
||||||
|
|
||||||
// these are from 0.0 to 1.0 inclusive
|
|
||||||
public double red;
|
|
||||||
public double green;
|
|
||||||
public double blue;
|
|
||||||
public double alpha;
|
|
||||||
|
|
||||||
public float red() {
|
|
||||||
return (float) red;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float green() {
|
|
||||||
return (float) green;
|
|
||||||
}
|
|
||||||
|
|
||||||
public float blue() {
|
|
||||||
return (float) blue;
|
|
||||||
}
|
|
||||||
|
|
||||||
public
|
|
||||||
Color getColor() {
|
|
||||||
read(); // have to read the struct members first!
|
|
||||||
return new Color(red(), green(), blue());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected
|
|
||||||
List<String> getFieldOrder() {
|
|
||||||
return Arrays.asList("red", "green", "blue", "alpha");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
class ByValue extends GdkRGBAColor implements Structure.ByValue {}
|
|
||||||
|
|
||||||
|
|
||||||
public
|
|
||||||
class ByReference extends GdkRGBAColor implements Structure.ByReference {}
|
|
||||||
}
|
|
|
@ -46,18 +46,22 @@ class GtkStyle extends Structure {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public static
|
public static
|
||||||
class ByReference extends GtkStyle implements Structure.ByReference {}
|
class ByReference extends GtkStyle implements Structure.ByReference {
|
||||||
|
}
|
||||||
|
|
||||||
public
|
public
|
||||||
class ByValue extends GtkStyle implements Structure.ByValue {}
|
class ByValue extends GtkStyle implements Structure.ByValue {
|
||||||
|
}
|
||||||
|
|
||||||
// only list public fields
|
// required, even though it's "private" in the corresponding C code. OTHERWISE the memory offsets are INCORRECT.
|
||||||
|
public GObjectStruct parent_instance;
|
||||||
|
|
||||||
/** fg: foreground for drawing GtkLabel */
|
/** fg: foreground for drawing GtkLabel */
|
||||||
public GdkColor fg[] = new GdkColor[5];
|
public GdkColor fg[] = new GdkColor[5];
|
||||||
|
|
||||||
/** bg: the usual background color, gray by default */
|
/** bg: the usual background color, gray by default */
|
||||||
public GdkColor bg[] = new GdkColor[5];
|
public GdkColor bg[] = new GdkColor[5];
|
||||||
|
|
||||||
public GdkColor light[] = new GdkColor[5];
|
public GdkColor light[] = new GdkColor[5];
|
||||||
public GdkColor dark[] = new GdkColor[5];
|
public GdkColor dark[] = new GdkColor[5];
|
||||||
public GdkColor mid[] = new GdkColor[5];
|
public GdkColor mid[] = new GdkColor[5];
|
||||||
|
@ -69,7 +73,10 @@ class GtkStyle extends Structure {
|
||||||
|
|
||||||
/** base: background when using text, colored white in the default theme. */
|
/** base: background when using text, colored white in the default theme. */
|
||||||
public GdkColor base[] = new GdkColor[5];
|
public GdkColor base[] = new GdkColor[5];
|
||||||
public GdkColor text_aa[] = new GdkColor[5]; /* Halfway between text/base */
|
|
||||||
|
/** Halfway between text/base */
|
||||||
|
public GdkColor text_aa[] = new GdkColor[5];
|
||||||
|
|
||||||
public GdkColor black;
|
public GdkColor black;
|
||||||
public GdkColor white;
|
public GdkColor white;
|
||||||
public Pointer /*PangoFontDescription*/ font_desc;
|
public Pointer /*PangoFontDescription*/ font_desc;
|
||||||
|
@ -77,10 +84,21 @@ class GtkStyle extends Structure {
|
||||||
public int ythickness;
|
public int ythickness;
|
||||||
public Pointer /*cairo_pattern_t*/ background[] = new Pointer[5];
|
public Pointer /*cairo_pattern_t*/ background[] = new Pointer[5];
|
||||||
|
|
||||||
|
public
|
||||||
|
void debug(final int gtkState) {
|
||||||
|
System.err.println("base " + base[gtkState].getColor());
|
||||||
|
System.err.println("text " + text[gtkState].getColor());
|
||||||
|
System.err.println("text_aa " + text_aa[gtkState].getColor());
|
||||||
|
System.err.println("bg " + bg[gtkState].getColor());
|
||||||
|
System.err.println("fg " + fg[gtkState].getColor());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected
|
protected
|
||||||
List<String> getFieldOrder() {
|
List<String> getFieldOrder() {
|
||||||
return Arrays.asList("fg",
|
return Arrays.asList("parent_instance",
|
||||||
|
"fg",
|
||||||
"bg",
|
"bg",
|
||||||
"light",
|
"light",
|
||||||
"dark",
|
"dark",
|
||||||
|
|
|
@ -3,11 +3,11 @@
|
||||||
#include <libappindicator/app-indicator.h>
|
#include <libappindicator/app-indicator.h>
|
||||||
|
|
||||||
|
|
||||||
// gcc example.c `pkg-config --cflags --libs gtk+-2.0 appindicator-0.1` -I/usr/include/libappindicator-0.1/ -o example
|
// gcc example.c `pkg-config --cflags --libs gtk+-2.0 appindicator-0.1` -I/usr/include/libappindicator-0.1/ -o example && ./example
|
||||||
|
|
||||||
// apt libgtk-3-dev install libappindicator3-dev
|
// apt libgtk-3-dev install libappindicator3-dev
|
||||||
// NOTE: there will be warnings, but the file will build and run. NOTE: this will not run as root on ubuntu (no dbus connection back to the normal user)
|
// NOTE: there will be warnings, but the file will build and run. NOTE: this will not run as root on ubuntu (no dbus connection back to the normal user)
|
||||||
// gcc example.c `pkg-config --cflags --libs gtk+-3.0 appindicator3-0.1` -I/usr/include/libappindicator3-0.1/ -o example
|
// gcc example.c `pkg-config --cflags --libs gtk+-3.0 appindicator3-0.1` -I/usr/include/libappindicator3-0.1/ -o example && ./example
|
||||||
|
|
||||||
|
|
||||||
static void activate_action (GtkAction *action);
|
static void activate_action (GtkAction *action);
|
||||||
|
@ -278,6 +278,15 @@ int main (int argc, char **argv)
|
||||||
|
|
||||||
|
|
||||||
menuItem1 = gtk_image_menu_item_new_with_label("menu1");
|
menuItem1 = gtk_image_menu_item_new_with_label("menu1");
|
||||||
|
|
||||||
|
// double check color info
|
||||||
|
GtkStyle *style;
|
||||||
|
style = gtk_rc_get_style(gtk_image_menu_item_new_with_mnemonic("xxx"));
|
||||||
|
|
||||||
|
GdkColor color = style->fg[GTK_STATE_NORMAL];
|
||||||
|
|
||||||
|
fprintf(stderr, "COLOR %s\n", gdk_color_to_string(&color));
|
||||||
|
|
||||||
// g_signal_connect(menuItem1, "button_press_event", G_CALLBACK (gtkCallback), NULL);
|
// g_signal_connect(menuItem1, "button_press_event", G_CALLBACK (gtkCallback), NULL);
|
||||||
gtk_menu_shell_insert(GTK_MENU_SHELL(indicator_menu), menuItem1, 0);
|
gtk_menu_shell_insert(GTK_MENU_SHELL(indicator_menu), menuItem1, 0);
|
||||||
gtk_widget_show(menuItem1);
|
gtk_widget_show(menuItem1);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user