/* The TdeGtk Theming Engine for Gtk+. * Copyright (C) 2012 Timothy Pearson * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, * MA 02110-1301, USA. * */ // NOTE // GTK contains a number of bugs that will interfere with your overall visual experience // Those that I have reported can be accessed with this link: // https://bugzilla.gnome.org/buglist.cgi?type0-0-4=substring&emailcc1=1&emailreporter1=1&emaillongdesc1=1&field0-0-0=product&type0-0-1=substring&field0-0-1=component&field0-0-4=longdesc&emailtype1=substring&type0-0-3=substring&query_format=advanced&emailassigned_to1=1&field0-0-3=status_whiteboard&emailqa_contact1=1&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&bug_status=NEEDINFO&bug_status=RESOLVED&bug_status=VERIFIED&field0-0-2=short_desc&email1=kb9vqf%40pearsoncomputing.net&type0-0-0=substring&value0-0-0=gtk%2B&type0-0-2=substring // FIXME // This style should use the TDE open/save dialogs instead of the stock Gnome GtkFileChooserDialog // This is not currently possible in GTK // See upstream GTK bug #687173 #define TDEGTK_THEME_THEME_DIRECTORY_NAME "tdegtk" #define TDEGTK_THEME_CONTROL_FILE_NAME "gtk.css" #define RC_CACHE_VERSION TQString("1") #define DEBUG_PRINT(x) if (gtk3TQtDebug) printf(x"\n\r"); #define DEBUG_PRINT_ARGS(x,y) if (gtk3TQtDebug) printf(x"\n\r",y); bool gtk3TQtEnable = false; int gtk3TQtDebug = false; bool tde_showIconsOnPushButtons = false; void initTDESettings(); void writeGtkThemeControlFile(int forceRecreate); int m_scrollBarSubLineWidth = -1; bool m_scrollbarBack1 = false; bool m_scrollbarForward1 = false; bool m_scrollbarBack2 = false; bool m_scrollbarForward2 = false; extern "C" { #include #include #include "tdegtk-engine.h" G_MODULE_EXPORT void theme_init(GTypeModule *module); G_MODULE_EXPORT void theme_exit(void); G_MODULE_EXPORT GtkThemingEngine* create_engine(void); G_MODULE_EXPORT void theme_init(GTypeModule *module) { tdegtk_engine_register_types(module); } G_MODULE_EXPORT void theme_exit(void) { // } } #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "tdegtk-widgetlookup.h" extern WidgetLookup m_widgetLookup; typedef std::pair SizePair; typedef std::vector SizeMap; SizeMap icon_sizes; G_MODULE_EXPORT GtkThemingEngine* create_engine(void) { gtk3TQtEnable = true; gtk3TQtDebug = (getenv("GTK3_TQT_ENGINE_DEBUG") != NULL) ? 1 : 0; DEBUG_PRINT("Creating KApplication") int argc = 1; char** argv; // Supply it with fake data to keep KApplication happy argv = (char**) malloc(sizeof(char*)); argv[0] = (char*) malloc(sizeof(char) * 21); strncpy(argv[0], "gtk3-tqt-application", 21); KAboutData aboutData("gtk3-tqt-engine", I18N_NOOP("gtk3-tqt-engine"), "v0.1", "GTK3 TQt theme engine", KAboutData::License_GPL, "(c) 2012, Timothy Pearson", "", 0 /* TODO: Website */, "kb9vqf@pearsoncomputing.net"); KCmdLineArgs::init(argc, const_cast(argv), &aboutData); KApplication::disableAutoDcopRegistration(); new KApplication(gdk_x11_get_default_xdisplay(), true, 0, 0, true); initTDESettings(); // initialize icon sizes icon_sizes.push_back(std::make_pair("panel-menu", 16)); icon_sizes.push_back(std::make_pair("panel", 32)); icon_sizes.push_back(std::make_pair("gtk-small-toolbar", IconSize(KIcon::Toolbar))); icon_sizes.push_back(std::make_pair("gtk-large-toolbar", IconSize(KIcon::MainToolbar))); icon_sizes.push_back(std::make_pair("gtk-dnd", IconSize(KIcon::Small))); icon_sizes.push_back(std::make_pair("gtk-button", IconSize(KIcon::Small))); icon_sizes.push_back(std::make_pair("gtk-menu", IconSize(KIcon::Small))); icon_sizes.push_back(std::make_pair("gtk-dialog", IconSize(KIcon::Small))); icon_sizes.push_back(std::make_pair("", IconSize(KIcon::Small))); if (gtk3TQtDebug) { writeGtkThemeControlFile(true); } // Initialize hooks m_widgetLookup.initializeHooks(); return GTK_THEMING_ENGINE (g_object_new (TDEGTK_TYPE_ENGINE, "name", "tdegtk", NULL)); } // ========================================================================================================= // // TQt3/GTK Theme Control Functions // // ========================================================================================================= TQStringList tdeSearchPaths; TQString iconTheme; TQStringList iconThemeDirs; TQColor alternateBackgroundColour; int showIconsOnButtons; int toolbarStyle; TQString tdeConfigValue(const TQString& section, const TQString& name, const TQString& def) { KConfig currentConfig; currentConfig.setGroup(section); return currentConfig.readEntry(name, def); } bool tdeBoolConfigValue(const TQString& section, const TQString& name, bool def) { KConfig currentConfig; currentConfig.setGroup(section); return currentConfig.readBoolEntry(name, def); } TQString tdeFindDir(const TQString& suffix, const TQString& file1, const TQString& file2) { for ( TQStringList::Iterator it = tdeSearchPaths.begin(); it != tdeSearchPaths.end(); ++it ) { if ((TQFile::exists((*it) + suffix + file1)) || (TQFile::exists((*it) + suffix + file2))) return (*it) + suffix; } return TQString(); } TQString runCommand(const TQString& command) { FILE* p = popen(command.latin1(), "r"); if ((p == NULL) || (p < 0)) return TQString(); TQString ret; while (!feof(p)) { char buffer[256]; int n = fread(buffer, 1, 255, p); buffer[n] = '\0'; ret += buffer; } pclose(p); return ret.stripWhiteSpace(); } void initTDESettings() { tdeSearchPaths.clear(); TQString tdeHome = getenv("TDEHOME"); TQString tdeDirs = getenv("TDEDIRS"); TQString tdeDir = getenv("TDEDIR"); if (!tdeHome.isEmpty()) { tdeSearchPaths.append(tdeHome); } tdeSearchPaths.append(runCommand("tde-config --localprefix")); if (!tdeDirs.isEmpty()) { tdeSearchPaths += TQStringList::split(':', tdeDirs); } if (!tdeDir.isEmpty()) { tdeSearchPaths.append(tdeDir); } tdeSearchPaths.append(runCommand("tde-config --prefix")); iconTheme = tdeConfigValue("Icons", "Theme", "crystalsvg"); tde_showIconsOnPushButtons = tdeBoolConfigValue("KDE", "ShowIconsOnPushButtons", false); TQStringList back = TQStringList::split(',', tdeConfigValue("General", "alternateBackground", "238,246,255")); alternateBackgroundColour.setRgb(back[0].toInt(), back[1].toInt(), back[2].toInt()); showIconsOnButtons = (tdeConfigValue("KDE", "ShowIconsOnPushButtons", "true").lower() == "true"); TQString tmp = tdeConfigValue("Toolbar style", "IconText", "true").lower(); if (tmp == "icononly") { toolbarStyle = 0; } else if (tmp == "icontextright") { toolbarStyle = 3; } else if (tmp == "textonly") { toolbarStyle = 1; } else if (tmp == "icontextbottom") { toolbarStyle = 2; } else { // Should never happen, but just in case we fallback to TDE's default "icononly" toolbarStyle = 0; } } void gtk3_tqt_load_resource_string(const char* css_string) { GtkCssProvider *provider; provider = gtk_css_provider_new(); gtk_css_provider_load_from_data(provider, css_string, -1, NULL); GdkDisplay *display = gdk_display_get_default(); GdkScreen *screen = gdk_display_get_default_screen(display); gtk_style_context_add_provider_for_screen(screen, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); g_object_unref(provider); DEBUG_PRINT_ARGS("gtk3_tqt_load_resource_string(%s)", css_string) } void gtk3_tqt_reload_theme_definition_file(const char* filename) { GtkCssProvider *provider; GError *error; provider = gtk_css_provider_new(); GdkDisplay *display = gdk_display_get_default(); GdkScreen *screen = gdk_display_get_default_screen(display); gtk_style_context_add_provider_for_screen(screen, GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); g_object_unref(provider); if (!gtk_css_provider_load_from_path(provider, filename, &error)) { g_printerr("%s: error parsing %s: %s\n", G_STRFUNC, filename, error->message); g_clear_error(&error); } } TQString tdeFontToGTKFontString(TQFont font) { TQString fontDescriptor = "font: "; fontDescriptor.append(font.family()); if (font.bold()) { fontDescriptor.append(" bold"); } if (font.italic()) { fontDescriptor.append(" italic"); } if (font.pointSizeFloat() != -1) { fontDescriptor.append(TQString(" %1").arg(font.pointSizeFloat())); } else { fontDescriptor.append(TQString(" %1px").arg(font.pixelSize())); } return fontDescriptor; } // Thanks Martin Dvorak of metatheme TQString parse_rc_string(const TQString& defs, const TQString& pattern, bool widgetClass = true) { static int dynamic_counter = 0; ++dynamic_counter; return pattern + " {\n\t" + defs + ";\n}\n\n"; } GtkIconSet* generateIconSet(const std::string& gtkIconName, const std::string& tdeIconName, TQStringList pathList) { if (tdeIconName == "NONE") { return 0L; } bool empty = true; // create iconSet GtkIconSet* iconSet = gtk_icon_set_new(); // loop over iconSizes for (SizeMap::const_iterator sizeIter = icon_sizes.begin(); sizeIter != icon_sizes.end(); ++sizeIter) { // generate full icon name std::ostringstream iconFileStream; iconFileStream << sizeIter->second << "x" << sizeIter->second << "/" << tdeIconName; // loop over provided path to see if at least one icon is found for (TQStringList::const_iterator pathIter = pathList.begin(); pathIter != pathList.end(); ++pathIter) { std::string filename((*pathIter + '/' + iconFileStream.str()).latin1()); if (!std::ifstream(filename.c_str())) { continue; } empty = false; GtkIconSource* iconSource(gtk_icon_source_new()); // set name gtk_icon_source_set_filename(iconSource, filename.c_str()); // set direction and state wildcarded gtk_icon_source_set_direction_wildcarded(iconSource, TRUE); gtk_icon_source_set_state_wildcarded(iconSource, TRUE); // set size if (sizeIter->first.empty()) { gtk_icon_source_set_size_wildcarded(iconSource, TRUE); } else { GtkIconSize size = gtk_icon_size_from_name(sizeIter->first.c_str()); if (size != GTK_ICON_SIZE_INVALID) { gtk_icon_source_set_size_wildcarded(iconSource, FALSE); gtk_icon_source_set_size(iconSource, size); } } // add source to iconSet gtk_icon_set_add_source(iconSet, iconSource); break; } } // if nothing found, return; if (empty) { gtk_icon_set_unref(iconSet); return 0L; } else { return iconSet; } } void doIconMapping(const char* gtkName, const char * tdeName, GtkIconFactory* factory, TQStringList tdeIconThemeDirs) { GtkIconSet* iconSet = generateIconSet(gtkName, tdeName, tdeIconThemeDirs); if (iconSet) { gtk_icon_factory_add(factory, gtkName, iconSet); gtk_icon_set_unref(iconSet); } } TQString colorString(TQColor color) { TQString ret = "rgb("; ret += TQString::number(color.red()) + ", "; ret += TQString::number(color.green()) + ", "; ret += TQString::number(color.blue()) + ")"; return ret; } void setColour(TQString name, TQString state, TQColor color, TQString widgetTypes = TQString("*")) { state = ":" + state; if (state == ":normal") { state = ""; } gtk3_tqt_load_resource_string(parse_rc_string(name + ": " + colorString(color), widgetTypes + state).latin1()); } static TQStringList iconInheritsDirs(const TQString& icondir) { TQFile index; index.setName(icondir + "index.theme"); if( !index.open( IO_ReadOnly )) { index.setName(icondir + "index.desktop"); if( !index.open( IO_ReadOnly )) return TQStringList(); } char buf[1024]; TQRegExp reg( "^\\s*Inherits=([^\\n]*)" ); for(;;) { if( index.readLine(buf, 1023) <= 0 ) { break; } if( reg.search(buf, 0) >= 0 ) { return TQStringList::split(",", reg.cap(1)); } } return TQStringList(); } void addIconThemeDir(const TQString& theme) { // Try to find this theme's directory TQString icondir = tdeFindDir("/share/icons/" + theme + "/", "index.theme", "index.desktop"); if(icondir.isEmpty()) { return; } if (iconThemeDirs.contains(icondir)) { return; } // Add this theme to the list iconThemeDirs.append(icondir); // Do it again for any parent themes TQStringList parents = iconInheritsDirs(icondir); for ( TQStringList::Iterator it=parents.begin() ; it!=parents.end(); ++it) { addIconThemeDir((*it).stripWhiteSpace()); } } void writeGtkThemeControlFile(int forceRecreate) { if (!gtk3TQtEnable) { return; } DEBUG_PRINT("writeGtkThemeControlFile()") // Set colors // Normal setColour("color", "normal", tqApp->palette().active().text()); setColour("background-color", "normal", tqApp->palette().active().background()); // setColour("text", "normal", tqApp->palette().active().text()); // setColour("base", "normal", tqApp->palette().active().base()); // Active (on) setColour("color", "active", tqApp->palette().active().text()); setColour("background-color", "active", tqApp->palette().active().background()); // setColour("text", "active", tqApp->palette().active().text()); // setColour("base", "active", tqApp->palette().active().base()); // Mouseover setColour("color", "prelight", tqApp->palette().active().text()); // menu items - change? setColour("background-color", "prelight", tqApp->palette().active().highlight()); // setColour("text", "prelight", tqApp->palette().active().text()); // setColour("base", "prelight", tqApp->palette().active().base()); // Selected setColour("color", "selected", tqApp->palette().active().highlightedText()); setColour("background-color", "selected", tqApp->palette().active().highlight()); // setColour("text", "selected", tqApp->palette().active().highlightedText()); // setColour("base", "selected", tqApp->palette().active().highlight()); // Disabled setColour("color", "insensitive", tqApp->palette().disabled().text()); setColour("background-color", "insensitive", tqApp->palette().disabled().background()); // setColour("text", "insensitive", tqApp->palette().disabled().text()); // setColour("base", "insensitive", tqApp->palette().disabled().background()); TQString iconSettingsString; iconSettingsString.append(TQString("gtk-menu=%1,%2").arg(IconSize(KIcon::Small)).arg(IconSize(KIcon::Small))); iconSettingsString.append(TQString(":gtk-button=%1,%2").arg(IconSize(KIcon::Small)).arg(IconSize(KIcon::Small))); iconSettingsString.append(TQString(":gtk-small-toolbar=%1,%2").arg(IconSize(KIcon::Toolbar)).arg(IconSize(KIcon::Toolbar))); iconSettingsString.append(TQString(":gtk-large-toolbar=%1,%2").arg(IconSize(KIcon::MainToolbar)).arg(IconSize(KIcon::MainToolbar))); iconSettingsString.append(TQString(":gtk-dnd=%1,%2").arg(IconSize(KIcon::Small)).arg(IconSize(KIcon::Small))); iconSettingsString.append(TQString(":gtk-dialog=%1,%2").arg(IconSize(KIcon::Small)).arg(IconSize(KIcon::Small))); // Set up global application settings GtkSettings* gtksettings = gtk_settings_get_default(); GtkSettingsValue svalue; if (gtksettings) { g_object_set(gtksettings, "gtk-alternative-button-order", TRUE, NULL); g_object_set(gtksettings, "gtk-button-images", tde_showIconsOnPushButtons, NULL); g_object_set(gtksettings, "gtk-menu-popup-delay", tqApp->style().styleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay), NULL); g_object_set(gtksettings, "gtk-icon-sizes", iconSettingsString.ascii(), NULL); // Handle toolbar text display if (toolbarStyle == 3) { // This is broken by GTK bug #68700 g_object_set(gtksettings, "gtk-toolbar-style", GTK_TOOLBAR_BOTH_HORIZ, NULL); } else if (toolbarStyle == 2) { g_object_set(gtksettings, "gtk-toolbar-style", GTK_TOOLBAR_BOTH, NULL); } else if (toolbarStyle == 1) { g_object_set(gtksettings, "gtk-toolbar-style", GTK_TOOLBAR_TEXT, NULL); } else { g_object_set(gtksettings, "gtk-toolbar-style", GTK_TOOLBAR_ICONS, NULL); } } // This function takes quite a long time to execute, and is run at the start of every app. // In order to speed it up, we can store the results in a file, along with the name of icon // theme and style. This file can then be regenerated when the icon theme or style change. TQDir curDir; TQString themeFilePath = TQDir::homeDirPath(); curDir.setPath(themeFilePath); if (!curDir.exists()) { curDir.mkdir(themeFilePath); } themeFilePath = themeFilePath + "/.themes/"; curDir.setPath(themeFilePath); if (!curDir.exists()) { curDir.mkdir(themeFilePath); } themeFilePath = themeFilePath + TQString(TDEGTK_THEME_THEME_DIRECTORY_NAME) + "/"; curDir.setPath(themeFilePath); if (!curDir.exists()) { curDir.mkdir(themeFilePath); } themeFilePath = themeFilePath + "gtk-3.0/"; curDir.setPath(themeFilePath); if (!curDir.exists()) { curDir.mkdir(themeFilePath); } themeFilePath = themeFilePath + TDEGTK_THEME_CONTROL_FILE_NAME; TQFile themeFile(themeFilePath); TQTextStream stream; if (!forceRecreate && themeFile.exists()) { themeFile.open(IO_ReadOnly); stream.setDevice(TQT_TQIODEVICE(&themeFile)); if (stream.readLine() == "/* " + iconTheme + ", " + tqApp->style().name() + ", " + RC_CACHE_VERSION + " */") { // This cache matches the current icon theme and style gtk3_tqt_reload_theme_definition_file(themeFilePath.latin1()); return; } stream.unsetDevice(); themeFile.close(); } themeFile.open(IO_WriteOnly | IO_Truncate); stream.setDevice(TQT_TQIODEVICE(&themeFile)); stream << "/* " << iconTheme << ", " << tqApp->style().name() << ", " << RC_CACHE_VERSION << " */\n\n"; stream << "/* This file was generated by the Gtk TQt Theme Engine */\n"; stream << "/* It will be recreated when you change your TDE icon theme or widget style */\n\n"; stream << "\n"; stream << "* {\n\tengine:tdegtk;\n}\n"; stream << "\n"; TQScrollBar sbar(NULL); sbar.setOrientation(TQt::Horizontal); sbar.setValue(1); sbar.resize(200,25); // The following code determines how many buttons are on a scrollbar // It works by looking at each pixel of the scrollbar's area not taken up by the groove, // and asking the style which subcontrol is at that location. TQRect rect = tqApp->style().querySubControlMetrics(TQStyle::CC_ScrollBar, &sbar, TQStyle::SC_ScrollBarGroove); TQStyle::SubControl sc = TQStyle::SC_None; for (TQPoint pos(0,7) ; pos.x()style().querySubControl(TQStyle::CC_ScrollBar, &sbar, pos); if (sc != sc2) { if (sc2 == TQStyle::SC_ScrollBarAddLine) m_scrollbarForward1 = true; if (sc2 == TQStyle::SC_ScrollBarSubLine) m_scrollbarBack1 = true; sc = sc2; } } sc = TQStyle::SC_None; for (TQPoint pos(rect.x()+rect.width(),7) ; pos.x()<200 ; pos.setX(pos.x()+1)) { TQStyle::SubControl sc2 = tqApp->style().querySubControl(TQStyle::CC_ScrollBar, &sbar, pos); if (sc != sc2) { if (sc2 == TQStyle::SC_ScrollBarAddLine) m_scrollbarForward2 = true; if (sc2 == TQStyle::SC_ScrollBarSubLine) m_scrollbarBack2 = true; sc = sc2; } } // FIXME // The items in a GTK3 combobox cannot be styled, either to draw them in a custom manner or to change their height // A bug report should be filed on this issue... bool combobox_appears_as_list = (!(tqApp->style().styleHint(TQStyle::SH_ComboBox_Popup) || tqApp->style().styleHint(TQStyle::SH_GUIStyle) == TQt::MotifStyle)); stream << parse_rc_string(TQString("-GtkComboBox-appears-as-list: %1px").arg(combobox_appears_as_list), "*"); // FIXME // Work around problems detailed in upstream GTK bug #687677 stream << parse_rc_string(TQString("-GtkComboBox-shadow-type: in"), "*"); stream << parse_rc_string("-GtkComboBox-arrow-size: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_ArrowSize)), "*"); stream << parse_rc_string("-GtkComboBox-arrow-scaling: 0", "*"); stream << parse_rc_string(TQString("-GtkScrollbar-has-backward-stepper: ") + (m_scrollbarBack1 ? "1" : "0"), "*"); stream << parse_rc_string(TQString("-GtkScrollbar-has-forward-stepper: ") + (m_scrollbarForward2 ? "1" : "0"), "*"); stream << parse_rc_string(TQString("-GtkScrollbar-has-secondary-forward-stepper: ") + (m_scrollbarForward1 ? "1" : "0"), "*"); stream << parse_rc_string(TQString("-GtkScrollbar-has-secondary-backward-stepper: ") + (m_scrollbarBack2 ? "1" : "0"), "*"); m_scrollBarSubLineWidth = tqApp->style().querySubControlMetrics(TQStyle::CC_ScrollBar, &sbar, TQStyle::SC_ScrollBarSubLine).width(); stream << parse_rc_string("-GtkRange-stepper-size: " + TQString::number(m_scrollBarSubLineWidth), "GtkScrollbar"); stream << parse_rc_string("-GtkRange-slider-width: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_ScrollBarExtent)), "GtkScrollbar"); stream << parse_rc_string("-GtkRange-trough-border: 0", "GtkScrollbar"); stream << parse_rc_string("-GtkRange-trough-under-steppers: 1", "GtkScrollbar"); stream << parse_rc_string("-GtkScrollbar-min-slider-length: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_ScrollBarSliderMin)), "*"); // FIXME // This is currently set to the scrollbar stepper button size, as TQt3 does not have the concept of a stepper for a TQSlider stream << parse_rc_string("-GtkRange-stepper-size: " + TQString::number(m_scrollBarSubLineWidth), "GtkScale"); TQSlider slider(NULL); // To keep BlueCurve happy stream << parse_rc_string("-GtkRange-slider-width: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_SliderThickness, &slider)), "GtkScale"); stream << parse_rc_string("-GtkScale-slider-length: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_SliderLength, &slider)), "*"); stream << parse_rc_string("-GtkButton-child-displacement-x: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_ButtonShiftHorizontal)), "*"); stream << parse_rc_string("-GtkButton-child-displacement-y: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_ButtonShiftVertical)), "*"); stream << parse_rc_string("-GtkRange-arrow-displacement-x: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_ButtonShiftHorizontal)), "*"); stream << parse_rc_string("-GtkRange-arrow-displacement-y: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_ButtonShiftVertical)), "*"); stream << parse_rc_string("-GtkRange-arrow-displacement-x: " + TQString::number(0), "GtkScrollbar"); stream << parse_rc_string("-GtkRange-arrow-displacement-y: " + TQString::number(0), "GtkScrollbar"); stream << parse_rc_string("-GtkButton-default-border: 0 0 0 0", "*"); stream << parse_rc_string("-GtkButton-default-outside-border: 0 0 0 0", "*"); #ifdef USE_NATIVE_GTK_BUTTON_DRAWING stream << parse_rc_string("-GtkButton-inner-border: 2 2 2 2", "*"); #else if (tde_showIconsOnPushButtons) { stream << parse_rc_string("-GtkButton-inner-border: 10 10 2 2", "*"); // Allow space for the icon on either side of the text } else { stream << parse_rc_string("-GtkButton-inner-border: 2 2 2 2", "*"); } #endif stream << parse_rc_string("-GtkButtonBox-child_min_height: 0", "*"); stream << parse_rc_string("-GtkButtonBox-child_internal_pad_x: 0", "*"); stream << parse_rc_string("-GtkButtonBox-child_internal_pad_y: 0", "*"); stream << parse_rc_string("-GtkRange-arrow-scaling: 1.0", "GtkScrollbar"); stream << parse_rc_string("-xthickness: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_DefaultFrameWidth)), "*.GtkMenu"); stream << parse_rc_string("-ythickness: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_DefaultFrameWidth)), "*.GtkMenu"); stream << parse_rc_string("-xthickness: 5", "*.GtkMenu.Gtk*MenuItem"); stream << parse_rc_string("-xthickness: 3", "*.GtkNotebook"); stream << parse_rc_string("-ythickness: 3", "*.GtkNotebook"); stream << parse_rc_string("-ythickness: 1", "*.GtkButton"); stream << parse_rc_string("-color: " + colorString(TQColor(0,0,0)), "gtk-tooltips.GtkLabel", false); stream << parse_rc_string("-xthickness: 1", "*.GtkButton.*"); stream << parse_rc_string("-ythickness: 1", "*.GtkButton.*"); // stream << parse_rc_string("GtkTreeView::allow-rules = 0", "*"); // stream << parse_rc_string("GtkTreeView::tree-line-width = 1", "*"); // stream << parse_rc_string("GtkTreeView::vertical-separator = 30", "*"); // //stream << parse_rc_string("GtkTreeView::odd-row-color = { 0.0, 0.0, 0.0 }", "*"); stream << parse_rc_string("-GtkButton-inner-border: 0 0 0 0", "*GtkToolbar*GtkButton*"); stream << parse_rc_string("-GtkButton-inner-border: 0 0 0 0", "*GtkToolbar*GtkToggleButton*"); stream << parse_rc_string("-GtkButton-inner-border: 0 0 0 0", "*GtkNotebook*GtkButton*"); stream << parse_rc_string("-GtkButton-inner-border: 0 0 0 0", "*GtkNotebook*GtkToggleButton*"); // TQt calls them tab boxes, GTK calls them notebooks (!??!?) Either way they are a pain... //stream << parse_rc_string("-GtkNotebook-tab-overlap: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_TabBarTabOverlap)), "*"); stream << parse_rc_string("-GtkNotebook-tab-overlap: 0", "*"); // Overlap is handled in the style engine itself stream << parse_rc_string("-GtkNotebook-tab-curvature: 0", "*"); stream << parse_rc_string("-GtkNotebook-initial-gap: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_TabBarTabOverlap)), "*"); // This sets both the radio button and check box sizes stream << parse_rc_string("-GtkCheckButton-indicator-size: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_IndicatorHeight)), "*"); stream << parse_rc_string("-GtkCheckMenuItem-indicator-size: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_IndicatorHeight)), "*"); // FIXME // GtkCellRendererToggle indicator-size must be set as follows, however GTK3 does not provide any means to change the default size! // See upstream GTK bug #687076 //stream << parse_rc_string("-GtkCellRendererToggle-indicator-size: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_IndicatorHeight)), "*"); // FIXME // GtkSpinButton widgets use ugly unthemed horizontal buttons // When GTK provides an option to switch to vertical buttons, implement theming support for them // See upstream GTK bug #687172 // FIXME // There must be a way to set the selected tab offset (shift) // If not, it may be neccessary to override the GTK text drawing routines for notebooks to insert the proper shift //stream << parse_rc_string("-????: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_TabBarTabShiftHorizontal)), "*"); // Expand widget padding so as to keep the prelighting out of the active widget area // FIXME // Where do these 3px values actually come from? stream << parse_rc_string("padding: " + TQString::number(3) + "px " + TQString::number(3) + "px", "GtkButton"); stream << parse_rc_string("padding: " + TQString::number(3) + "px " + TQString::number(3) + "px", "GtkNotebook"); stream << parse_rc_string("padding: " + TQString::number(3) + "px " + TQString::number(3) + "px", "GtkEntry"); TQRect probeRect; TQRect comboBoxArrowRect; TQRect comboBoxEditFieldRect; { probeRect = TQRect(0, 0, 100, 100); TQStringList objectTypes; objectTypes.append(TQCOMBOBOX_OBJECT_NAME_STRING); TQPalette objectPalette = tqApp->palette(objectTypes); TQStyleControlElementData ceData; TQStyle::ControlElementFlags elementFlags = TQStyle::CEF_None; ceData.widgetObjectTypes = objectTypes; ceData.orientation = TQt::Horizontal; ceData.rect = probeRect; elementFlags = elementFlags | TQStyle::CEF_IsEditable; comboBoxArrowRect = tqApp->style().querySubControlMetrics(TQStyle::CC_ComboBox, ceData, elementFlags, TQStyle::SC_ComboBoxArrow); comboBoxEditFieldRect = tqApp->style().querySubControlMetrics(TQStyle::CC_ComboBox, ceData, elementFlags, TQStyle::SC_ComboBoxEditField); } // The padding is specified in a highly unusual format: // top_width, right_width, bottom_width, left_width // Compare that to the more typical format of: // left, top, right, bottom stream << parse_rc_string("padding: " + TQString::number(comboBoxEditFieldRect.y()-probeRect.y()) + "px " + TQString::number(probeRect.right()-comboBoxEditFieldRect.right()-comboBoxArrowRect.width()) + "px " + TQString::number(probeRect.bottom()-comboBoxEditFieldRect.bottom()) + "px " + TQString::number(comboBoxEditFieldRect.x()-probeRect.x()) + "px", "GtkComboBox>GtkEntry"); // Set header sizes stream << parse_rc_string("padding: " + TQString::number(0) + "px " + TQString::number(0) + "px", "GtkTreeView>*>GtkButton"); stream << parse_rc_string("border-width: " + TQString::number(0) + "px " + TQString::number(0) + "px", "GtkTreeView>*>GtkButton"); stream << parse_rc_string("padding: " + TQString::number(0) + "px " + TQString::number(0) + "px", "GtkList>*>GtkButton"); stream << parse_rc_string("border-width: " + TQString::number(0) + "px " + TQString::number(0) + "px", "GtkList>*>GtkButton"); // Ensure group boxes are not cut off by their labels // FIXME // Where do these 2px values actually come from? stream << parse_rc_string("padding: " + TQString::number(2) + "px " + TQString::number(2) + "px", "GtkFrame"); // Separators appear to be hardcoded to 1px high in TQt3 // Anything else will cause a silent drawing failure! stream << parse_rc_string("-GtkWidget-separator-height: " + TQString::number(1), "*"); stream << parse_rc_string("-GtkWidget-separator-width: " + TQString::number(1), "*"); stream << parse_rc_string("-GtkWidget-wide-separators: 1", "*"); // Set treeview cell size stream << parse_rc_string("-GtkTreeView-vertical-separator: 0", "*"); // FIXME // GTK3 does not currently support drawing GtkTextView backgrounds through the style engine // Therefore, the best we can currently do is to set the background to the correct TQt3 color, which is obviously suboptimal as the TQt3 background drawing methods are never called! // Even the recommended CSS style properties do not appear to work! // See upstream GTK bug #687363 { TQStringList objectTypes; objectTypes.append(TQTEXTEDIT_OBJECT_NAME_STRING); TQPalette objectPalette = tqApp->palette(objectTypes); // stream << parse_rc_string("background-color: " + colorString(objectPalette.color(TQPalette::Active, TQColorGroup::Base)), "GtkTextView"); // stream << parse_rc_string("background-color: " + colorString(objectPalette.color(TQPalette::Disabled, TQColorGroup::Base)), "GtkTextView:insensitive"); setColour("color", "normal", objectPalette.color(TQPalette::Active, TQColorGroup::Foreground), "GtkTextView"); setColour("color", "insensitive", objectPalette.color(TQPalette::Disabled, TQColorGroup::Foreground), "GtkTextView"); setColour("background-color", "normal", objectPalette.color(TQPalette::Active, TQColorGroup::Base), "GtkTextView"); setColour("background-color", "insensitive", objectPalette.color(TQPalette::Disabled, TQColorGroup::Base), "GtkTextView"); } // Handle menu metrics int tdeStandardMenuItemHeight; int tdeSeparatorMenuItemHeight; int tdeMenuFrameWidth; { TQStringList objectTypes; objectTypes.append(TQPOPUPMENU_OBJECT_NAME_STRING); TQPalette objectPalette = tqApp->palette(objectTypes); TQStyleControlElementData ceData; TQStyle::ControlElementFlags elementFlags = TQStyle::CEF_None; ceData.widgetObjectTypes = objectTypes; ceData.orientation = TQt::Horizontal; TQMenuItem tqt3MenuItem; tqt3MenuItem.setSeparator(false); tdeStandardMenuItemHeight = TQPopupMenu::menuItemHeight(&tqt3MenuItem, tqApp->fontMetrics()); tqt3MenuItem.setSeparator(true); tdeSeparatorMenuItemHeight = TQPopupMenu::menuItemHeight(&tqt3MenuItem, tqApp->fontMetrics()); TQSize sz; ceData.rect = TQRect(0, 0, 0, tdeStandardMenuItemHeight); sz = tqApp->style().sizeFromContents(TQStyle::CT_PopupMenuItem, ceData, elementFlags, TQSize(0, tdeStandardMenuItemHeight), TQStyleOption(&tqt3MenuItem)); sz = sz.expandedTo(TQSize(0, sz.height())); tdeStandardMenuItemHeight = sz.height(); ceData.rect = TQRect(0, 0, 0, tdeSeparatorMenuItemHeight); sz = tqApp->style().sizeFromContents(TQStyle::CT_PopupMenuItem, ceData, elementFlags, TQSize(0, tdeSeparatorMenuItemHeight), TQStyleOption(&tqt3MenuItem)); sz = sz.expandedTo(TQSize(0, sz.height())); tdeSeparatorMenuItemHeight = sz.height(); tdeMenuFrameWidth = tqApp->style().pixelMetric(TQStyle::PM_DefaultFrameWidth, ceData, elementFlags); } stream << parse_rc_string("-GtkWidget-separator-height: " + TQString::number(tdeSeparatorMenuItemHeight), "GtkMenuItem"); stream << parse_rc_string("-GtkWidget-separator-width: " + TQString::number(tdeSeparatorMenuItemHeight), "GtkMenuItem"); stream << parse_rc_string("-GtkWidget-wide-separators: 1", "GtkMenuItem"); stream << parse_rc_string("padding: " + TQString::number(tdeMenuFrameWidth) + "px " + TQString::number(tdeMenuFrameWidth) + "px " + TQString::number(tdeMenuFrameWidth) + "px " + TQString::number(tdeMenuFrameWidth) + "px", "GtkMenu"); // FIXME // GTK does not seem to support inserting actual space between the items in a menu bar; instead it insists on expanding the size of the menu items themselves (GtkMenuBar>GtkMenuItem padding:) // This is rather jarring visually; if a way is found to work around this GTK bug the menu bar item spacing should be fixed ASAP! // stream << parse_rc_string("padding: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_)), "GtkMenuBar"); stream << parse_rc_string("padding: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_MenuBarFrameWidth)) + "px " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_MenuBarItemSpacing)*0.75) + "px", "GtkMenuBar>GtkMenuItem"); stream << parse_rc_string("padding: " + TQString::number((tdeStandardMenuItemHeight-tqApp->style().pixelMetric(TQStyle::PM_IndicatorHeight))/2) + "px " + TQString::number(0) + "px", "GtkMenu>GtkMenuItem"); stream << parse_rc_string("padding: " + TQString::number(0) + "px " + TQString::number(0) + "px", "GtkMenu>GtkMenuSpacerItem"); // stream << parse_rc_string("padding: " + TQString::number(tqApp->style().pixelMetric(TQStyle::PM_)), "GtkMenu>GtkSeparatorMenuItem"); // Set up fonts TQFont generalFont = KGlobalSettings::generalFont(); TQFont toolBarFont = KGlobalSettings::toolBarFont(); TQFont menuFont = KGlobalSettings::menuFont(); // TQFont windowTitleFont = KGlobalSettings::windowTitleFont(); // TQFont taskbarFont = KGlobalSettings::taskbarFont(); stream << parse_rc_string(tdeFontToGTKFontString(generalFont), "*"); stream << parse_rc_string(tdeFontToGTKFontString(toolBarFont), "*GtkToolbar*GtkButton*"); stream << parse_rc_string(tdeFontToGTKFontString(menuFont), "*.GtkMenu"); // Set up icons // Build the list of icon theme directories. // This function is recursive - it gets the directories of all the inherited themes as well addIconThemeDir(iconTheme); if (iconThemeDirs.isEmpty()) { themeFile.close(); if (!forceRecreate) { gtk3_tqt_reload_theme_definition_file(themeFilePath.latin1()); } return; } GtkIconFactory* factory; factory = gtk_icon_factory_new(); doIconMapping("gtk-about", "actions/about_kde.png", factory, iconThemeDirs); doIconMapping("gtk-add", "actions/add.png", factory, iconThemeDirs); doIconMapping("gtk-apply", "actions/apply.png", factory, iconThemeDirs); doIconMapping("gtk-bold", "actions/text_bold.png", factory, iconThemeDirs); doIconMapping("gtk-cancel", "actions/button_cancel.png", factory, iconThemeDirs); doIconMapping("gtk-cdrom", "devices/cdrom_unmount.png", factory, iconThemeDirs); doIconMapping("gtk-clear", "actions/editclear.png", factory, iconThemeDirs); doIconMapping("gtk-close", "actions/fileclose.png", factory, iconThemeDirs); doIconMapping("gtk-color-picker", "actions/colorpicker.png", factory, iconThemeDirs); doIconMapping("gtk-copy", "actions/editcopy.png", factory, iconThemeDirs); doIconMapping("gtk-convert", "actions/gtk-convert.png", factory, iconThemeDirs); doIconMapping("gtk-connect", "actions/connect_creating.png", factory, iconThemeDirs); doIconMapping("gtk-cut", "actions/editcut.png", factory, iconThemeDirs); doIconMapping("gtk-delete", "actions/editdelete.png", factory, iconThemeDirs); doIconMapping("gtk-dialog-authentication", "status/gtk-dialog-authentication", factory, iconThemeDirs); doIconMapping("gtk-dialog-error", "actions/messagebox_critical.png", factory, iconThemeDirs); doIconMapping("gtk-dialog-info", "actions/messagebox_info.png", factory, iconThemeDirs); doIconMapping("gtk-dialog-question", "actions/help.png", factory, iconThemeDirs); doIconMapping("gtk-dialog-warning", "actions/messagebox_warning.png", factory, iconThemeDirs); doIconMapping("gtk-directory", "filesystems/folder.png", factory, iconThemeDirs); doIconMapping("gtk-disconnect", "actions/connect_no.png", factory, iconThemeDirs); doIconMapping("gtk-dnd", "mimetypes/empty.png", factory, iconThemeDirs); doIconMapping("gtk-dnd-multiple", "mimetypes/kmultiple.png", factory, iconThemeDirs); doIconMapping("gtk-edit", "actions/edit.png", factory, iconThemeDirs); //2.6 doIconMapping("gtk-execute", "actions/exec.png", factory, iconThemeDirs); doIconMapping("gtk-file", "mimetypes/empty.png", factory, iconThemeDirs); doIconMapping("gtk-find", "actions/find.png", factory, iconThemeDirs); doIconMapping("gtk-find-and-replace", "actions/find.png", factory, iconThemeDirs); // Is there a TDE "find and replace" icon? FIXME doIconMapping("gtk-floppy", "devices/3floppy_unmount.png", factory, iconThemeDirs); doIconMapping("gtk-fullscreen", "actions/window-fullscreen.png", factory, iconThemeDirs); doIconMapping("gtk-goto-bottom", "actions/bottom.png", factory, iconThemeDirs); doIconMapping("gtk-goto-first", "actions/start.png", factory, iconThemeDirs); doIconMapping("gtk-goto-last", "actions/finish.png", factory, iconThemeDirs); doIconMapping("gtk-goto-top", "actions/top.png", factory, iconThemeDirs); doIconMapping("gtk-go-back", "actions/back.png", factory, iconThemeDirs); doIconMapping("gtk-go-down", "actions/down.png", factory, iconThemeDirs); doIconMapping("gtk-go-forward", "actions/forward.png", factory, iconThemeDirs); doIconMapping("gtk-go-up", "actions/up.png", factory, iconThemeDirs); doIconMapping("gtk-harddisk", "devices/hdd_unmount.png", factory, iconThemeDirs); doIconMapping("gtk-help", "apps/khelpcenter.png", factory, iconThemeDirs); doIconMapping("gtk-home", "filesystems/folder_home.png", factory, iconThemeDirs); doIconMapping("gtk-indent", "actions/indent.png", factory, iconThemeDirs); doIconMapping("gtk-index", "actions/contents.png", factory, iconThemeDirs); doIconMapping("gtk-info", "actions/messagebox_info.png", factory, iconThemeDirs); doIconMapping("gtk-italic", "actions/text_italic.png", factory, iconThemeDirs); doIconMapping("gtk-jump-to", "actions/goto.png", factory, iconThemeDirs); doIconMapping("gtk-justify-center", "actions/text_center.png", factory, iconThemeDirs); doIconMapping("gtk-justify-fill", "actions/text_block.png", factory, iconThemeDirs); doIconMapping("gtk-justify-left", "actions/text_left.png", factory, iconThemeDirs); doIconMapping("gtk-justify-right", "actions/text_right.png", factory, iconThemeDirs); doIconMapping("gtk-leave-fullscreen", "actions/window-nofullscreen.png", factory, iconThemeDirs); doIconMapping("gtk-media-forward", "player-fwd.png", factory, iconThemeDirs); doIconMapping("gtk-media-next", "actions/player-end.png", factory, iconThemeDirs); doIconMapping("gtk-media-pause", "actions/player-pause.png", factory, iconThemeDirs); doIconMapping("gtk-media-previous", "actions/player-start.png", factory, iconThemeDirs); doIconMapping("gtk-media-record", "actions/gtk-media-record.png", factory, iconThemeDirs); // FIXME doIconMapping("gtk-media-rewind", "actions/player-rew.png", factory, iconThemeDirs); doIconMapping("gtk-media-stop", "actions/player-stop.png", factory, iconThemeDirs); doIconMapping("gtk-missing-image", "mimetypes/unknown.png", factory, iconThemeDirs); doIconMapping("gtk-network", "filesystems/network.png", factory, iconThemeDirs); doIconMapping("gtk-new", "actions/filenew.png", factory, iconThemeDirs); doIconMapping("gtk-no", "actions/gtk-no.png", factory, iconThemeDirs); doIconMapping("gtk-ok", "actions/button_ok.png", factory, iconThemeDirs); doIconMapping("gtk-open", "actions/fileopen.png", factory, iconThemeDirs); //doIconMapping("gtk-orientation-landscape", factory, iconThemeDirs); // FIXME //doIconMapping("gtk-orientation-portrait", factory, iconThemeDirs); // FIXME //doIconMapping("gtk-orientation-reverse-landscape", factory, iconThemeDirs); // FIXME //doIconMapping("gtk-orientation-reverse-portrait"", factory, iconThemeDirs); // FIXME doIconMapping("gtk-paste", "actions/editpaste.png", factory, iconThemeDirs); doIconMapping("gtk-preferences", "actions/configure.png", factory, iconThemeDirs); doIconMapping("gtk-print", "actions/fileprint.png", factory, iconThemeDirs); doIconMapping("gtk-print-preview", "actions/filequickprint.png", factory, iconThemeDirs); doIconMapping("gtk-properties", "actions/configure.png", factory, iconThemeDirs); doIconMapping("gtk-quit", "actions/exit.png", factory, iconThemeDirs); doIconMapping("gtk-redo", "actions/redo.png", factory, iconThemeDirs); doIconMapping("gtk-refresh", "actions/reload.png", factory, iconThemeDirs); doIconMapping("gtk-remove", "actions/remove.png", factory, iconThemeDirs); doIconMapping("gtk-revert-to-saved", "actions/revert.png", factory, iconThemeDirs); doIconMapping("gtk-save", "actions/filesave.png", factory, iconThemeDirs); doIconMapping("gtk-save-as", "actions/filesaveas.png", factory, iconThemeDirs); doIconMapping("gtk-select-all", "actions/gtk-select-all.png", factory, iconThemeDirs); // FIXME doIconMapping("gtk-select-color", "actions/colorize.png", factory, iconThemeDirs); doIconMapping("gtk-select-font", "mimetypes/font.png", factory, iconThemeDirs); //doIconMapping("gtk-sort-ascending", "??", factory, iconThemeDirs); // FIXME //doIconMapping("gtk-sort-descending", "??", factory, iconThemeDirs); // FIXME doIconMapping("gtk-spell-check", "actions/spellcheck.png", factory, iconThemeDirs); doIconMapping("gtk-stop", "actions/stop.png", factory, iconThemeDirs); doIconMapping("gtk-strikethrough", "actions/text_strike.png", factory, iconThemeDirs); doIconMapping("gtk-undelete", "actions/gtk-undelete.png", factory, iconThemeDirs); // FIXME doIconMapping("gtk-underline", "actions/text_under.png", factory, iconThemeDirs); doIconMapping("gtk-undo", "actions/undo.png", factory, iconThemeDirs); doIconMapping("gtk-unindent", "actions/unindent.png", factory, iconThemeDirs); doIconMapping("gtk-yes", "actions/button_ok.png", factory, iconThemeDirs); // Verify mapping here doIconMapping("gtk-zoom-100", "actions/viewmag1.png", factory, iconThemeDirs); doIconMapping("gtk-zoom-fit", "actions/viewmagfit.png", factory, iconThemeDirs); doIconMapping("gtk-zoom-in", "actions/viewmag+.png", factory, iconThemeDirs); doIconMapping("gtk-zoom-out", "actions/viewmag-.png", factory, iconThemeDirs); // Other icons that really should have Trinity equivalents in tdelibs doIconMapping("list-add", "actions/add.png", factory, iconThemeDirs); doIconMapping("list-remove", "actions/remove.png", factory, iconThemeDirs); doIconMapping("list-add-symbolic", "actions/add.png", factory, iconThemeDirs); doIconMapping("list-remove-symbolic", "actions/remove.png", factory, iconThemeDirs); gtk_icon_factory_add_default(factory); themeFile.close(); if (!gtk3TQtDebug) { gtk3_tqt_reload_theme_definition_file(themeFilePath.latin1()); } } // =========================================================================================================