/*************************************************************************** * Copyright (C) 2004 by David Sansome * * david@dave-linux * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program 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 General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * ***************************************************************************/ #if defined( HAVE_CONFIG_H ) # include "config.h" #endif #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 #include #include #include #include #include "kcmgtk.h" #include "mozillaprofile.h" const TQString KcmGtk::GTK_RC_FILE(".gtkrc-2.0-kde-kde4"); const TQString KcmGtk::KDE_RC_DIR(TDEGlobal::dirs()->localtdedir() + "/env/"); const TQString KcmGtk::GTK_TQT_RC_FILE("gtk-qt-engine.rc.sh"); const TQString KcmGtk::GTK3_INI_DIR(TQDir::homeDirPath() + "/.config/gtk-3.0/"); const TQString KcmGtk::GTK3_INI_FILE("settings.ini"); /*typedef KGenericFactory KcmGtkFactory; K_EXPORT_COMPONENT_FACTORY( kcm_gtk, KcmGtkFactory("gtk"))*/ extern "C" { TDE_EXPORT TDECModule *create_kcmgtk( TQWidget * parent, const char * name ) { TDEGlobal::locale()->insertCatalogue( "gtk-qt-engine" ); return new KcmGtk( parent, "kcmgtk" ); } } GtkRcParser::GtkRcParser() : emacs(false) { } void GtkRcParser::parse(TQString fileName) { TQFile file(fileName); file.open(IO_ReadOnly); TQTextStream stream(&file); TQRegExp includeRe("include\\s*\"([^\"]*)\""); TQRegExp fontRe("font_name\\s*=\\s*\"([^\"]*)\""); TQRegExp keyThemeRe("gtk-key-theme-name\\s*=\\s*\"([^\"]*)\""); TQStringList includes; while (1) { TQString line = stream.readLine(); if (line.isNull()) break; if (line.startsWith("#")) continue; line = line.stripWhiteSpace(); if (line.startsWith("include")) { if (includeRe.search(line) == -1) continue; TQString themePath = includeRe.cap(1); if (themePath.endsWith("/gtk-2.0/gtkrc") && !themePath.startsWith("/etc")) style = includeRe.cap(1); } if (line.startsWith("font_name")) { if (fontRe.search(line) == -1) continue; // Assume there will only be one font line font = parseFont(fontRe.cap(1)); } if (line.startsWith("gtk-key-theme-name")) { if (keyThemeRe.search(line) == -1) continue; if (TQString(keyThemeRe.cap(1)).lower() == "emacs") emacs = true; } } file.close(); } TQFont GtkRcParser::parseFont(TQString fontString) { TQFont ret; while (true) { int lastSpacePos = fontString.findRev(' '); if (lastSpacePos == -1) break; TQString lastWord = fontString.right(fontString.length() - lastSpacePos).stripWhiteSpace(); if (lastWord.lower() == "bold") ret.setBold(true); else if (lastWord.lower() == "italic") ret.setItalic(true); else { bool ok; int fontSize = lastWord.toInt(&ok); if (!ok) break; ret.setPointSize(fontSize); } fontString = fontString.left(lastSpacePos); } ret.setFamily(fontString); return ret; } KcmGtk::KcmGtk(TQWidget *parent, const char *name, const TQStringList&) : TDECModule(parent, name), myAboutData(0), emacsDetailsDialog(NULL), searchPathsDialog(NULL) { TDEGlobal::locale()->insertCatalogue("gtk-qt-engine"); config = new TDEConfig("kcmgtkrc"); TQStringList gtkSearchPathsDefault; gtkSearchPathsDefault.append("/usr"); gtkSearchPathsDefault.append("/usr/local"); gtkSearchPathsDefault.append("/opt/gnome"); gtkSearchPathsDefault.append(TQDir::homeDirPath() + "/.local"); gtkSearchPaths = config->readListEntry("gtkSearchPaths", gtkSearchPathsDefault); // Add the widget to our layout TQBoxLayout* l = new TQVBoxLayout(this); widget = new KcmGtkWidget(this); l->addWidget(widget); // Load the icons TDEIconLoader iconLoader; widget->styleIcon->setPixmap(iconLoader.loadIcon("style", TDEIcon::Desktop)); widget->styleIcon3->setPixmap(iconLoader.loadIcon("style", TDEIcon::Desktop)); widget->fontIcon->setPixmap(iconLoader.loadIcon("fonts", TDEIcon::Desktop)); widget->keyboardIcon->setPixmap(iconLoader.loadIcon("input-keyboard", TDEIcon::Desktop)); getInstalledThemes(); load(); // Connect some signals connect(widget->warning2, TQ_SIGNAL(leftClickedURL(const TQString&)), TDEApplication::kApplication(), TQ_SLOT(invokeBrowser(const TQString&))); connect(widget->styleGroup, TQ_SIGNAL(clicked(int)), TQ_SLOT(itemChanged())); connect(widget->warning5, TQ_SIGNAL(leftClickedURL(const TQString&)), TDEApplication::kApplication(), TQ_SLOT(invokeBrowser(const TQString&))); connect(widget->styleGroup3, TQ_SIGNAL(clicked(int)), TQ_SLOT(itemChanged())); connect(widget->fontGroup, TQ_SIGNAL(clicked(int)), TQ_SLOT(itemChanged())); connect(widget->styleBox, TQ_SIGNAL(activated(int)), TQ_SLOT(itemChanged())); connect(widget->styleBox, TQ_SIGNAL(activated(int)), TQ_SLOT(styleChanged())); connect(widget->styleBox3, TQ_SIGNAL(activated(int)), TQ_SLOT(itemChanged())); connect(widget->styleBox3, TQ_SIGNAL(activated(int)), TQ_SLOT(styleChanged())); connect(widget->emacsBox, TQ_SIGNAL(toggled(bool)), TQ_SLOT(itemChanged())); connect(widget->fontChange, TQ_SIGNAL(clicked()), TQ_SLOT(fontChangeClicked())); connect(widget->emacsDetails, TQ_SIGNAL(clicked()), TQ_SLOT(emacsDetailsClicked())); connect(widget->warning3, TQ_SIGNAL(clicked()), TQ_SLOT(searchPathsClicked())); connect(widget->warning6, TQ_SIGNAL(clicked()), TQ_SLOT(searchPathsClicked())); } void KcmGtk::getInstalledThemes() { themes.clear(); for ( TQStringList::Iterator it = gtkSearchPaths.begin(); it != gtkSearchPaths.end(); ++it ) { TQString path = (*it) + "/share/themes/"; TQDir dir(path); TQStringList entryList = dir.entryList(TQDir::Dirs, TQDir::Unsorted); for ( TQStringList::Iterator it2 = entryList.begin(); it2 != entryList.end(); ++it2 ) { if ((*it2).startsWith(".")) continue; if (themes.find(*it2) != themes.end()) continue; if (!TQFile::exists(path + (*it2) + "/gtk-2.0/gtkrc")) continue; themes.insert((*it2), path + (*it2) + "/gtk-2.0/gtkrc"); } } bool installed = false; widget->styleBox->clear(); if(!themes.empty()) { TQStringList otherStyles = themes.keys(); if(otherStyles.find("Qt")!=otherStyles.end()) { otherStyles.remove(otherStyles.find("Qt")); } installed = !otherStyles.empty(); if(installed) { widget->styleBox->insertStringList(otherStyles); } } widget->styleKde->setEnabled(installed); widget->styleBox->setEnabled(installed); widget->styleOther->setEnabled(installed); widget->warning1->setHidden(installed); widget->warning2->setHidden(installed); widget->warning3->setHidden(installed); gtk3Themes.clear(); for ( TQStringList::Iterator it = gtkSearchPaths.begin(); it != gtkSearchPaths.end(); ++it ) { TQString path = (*it) + "/share/themes/"; TQDir dir(path); TQStringList entryList = dir.entryList(TQDir::Dirs, TQDir::Unsorted); for ( TQStringList::Iterator it2 = entryList.begin(); it2 != entryList.end(); ++it2 ) { if ((*it2).startsWith(".")) continue; if (gtk3Themes.find(*it2) != gtk3Themes.end()) continue; if (!TQFile::exists(path + (*it2) + "/gtk-3.0/gtk.css")) continue; gtk3Themes.insert((*it2), path + (*it2) + "/gtk-3.0/gtk.css"); } } bool gtk3installed = !gtk3Themes.empty(); widget->styleBox3->clear(); if(gtk3installed) { TQStringList otherGtk3Styles = gtk3Themes.keys(); if(otherGtk3Styles.find("tdegtk")!=otherGtk3Styles.end()) { otherGtk3Styles.remove(otherGtk3Styles.find("tdegtk")); } if(otherGtk3Styles.find("Default")==otherGtk3Styles.end()) { otherGtk3Styles.prepend("Default"); } widget->styleBox3->insertStringList(otherGtk3Styles); } widget->styleKde3->setEnabled(gtk3installed); widget->styleBox3->setEnabled(gtk3installed); widget->styleOther3->setEnabled(gtk3installed); widget->warning4->setHidden(gtk3installed); widget->warning5->setHidden(gtk3installed); widget->warning6->setHidden(gtk3installed); } void KcmGtk::itemChanged() { // In KDE < 3.3 there is no changed() slot - only a signal. emit changed(true); } void KcmGtk::fontChangeClicked() { if ( TDEFontDialog::getFont( font ) == TDEFontDialog::Accepted ) { updateFontPreview(); widget->fontGroup->setButton(widget->fontGroup->id(widget->fontOther)); itemChanged(); } } void KcmGtk::styleChanged() { widget->styleGroup->setButton(widget->styleGroup->id(widget->styleOther)); itemChanged(); } TQString KcmGtk::env(TQString key) { char* value = getenv(key.latin1()); if (value == NULL) return TQString(); else return TQString(value); } void KcmGtk::updateFontPreview() { widget->fontPreview->setFont(font); widget->fontPreview->setText( i18n("%1 (size %2)").arg(font.family()).arg(TQString::number(font.pointSize()))); widget->fontPreview2->setFont(font); } KcmGtk::~KcmGtk() { delete config; } void KcmGtk::load() { parser.parse(TQDir::homeDirPath() + "/" + GTK_RC_FILE); bool usingQtEngine = false; if (!parser.style.isEmpty()) { for ( TQMapIterator it = themes.begin(); it != themes.end(); ++it ) { if (it.data() != parser.style) continue; if (it.key() == "Qt") usingQtEngine = true; for (int i=0 ; istyleBox->count() ; ++i) { if (widget->styleBox->text(i) == it.key()) { widget->styleBox->setCurrentItem(i); break; } } break; } if (usingQtEngine) { widget->styleGroup->setButton(widget->styleGroup->id(widget->styleKde)); } else { widget->styleGroup->setButton(widget->styleGroup->id(widget->styleOther)); } } font = parser.font; if (TQApplication::font().family() == font.family() && TQApplication::font().pointSize() == font.pointSize() && TQApplication::font().bold() == font.bold() && TQApplication::font().italic() == font.italic()) widget->fontGroup->setButton(widget->fontGroup->id(widget->fontKde)); else widget->fontGroup->setButton(widget->fontGroup->id(widget->fontOther)); widget->emacsBox->setChecked(parser.emacs); updateFontPreview(); bool usingGtk3TQtEngine = false; if (TQFile::exists(GTK3_INI_DIR + GTK3_INI_FILE)) { TQString activeGtk3Style; TDEConfig gtk3Config(GTK3_INI_DIR + GTK3_INI_FILE); gtk3Config.setGroup("Settings"); activeGtk3Style = gtk3Config.readEntry("gtk-theme-name"); if (activeGtk3Style == "tdegtk") { usingGtk3TQtEngine = true; } if (usingGtk3TQtEngine) { widget->styleGroup3->setButton(widget->styleGroup3->id(widget->styleKde3)); } else { widget->styleGroup3->setButton(widget->styleGroup3->id(widget->styleOther3)); for (int i=0; istyleBox3->count(); ++i) { if (widget->styleBox3->text(i) == activeGtk3Style) { widget->styleBox3->setCurrentItem(i); break; } } } } } void KcmGtk::save() { // First write out the gtkrc file TQFile file(TQDir::homeDirPath() + "/" + GTK_RC_FILE); file.open(IO_WriteOnly); TQTextStream stream(&file); stream.setEncoding(TQTextStream::Locale); TQFont selectedFont(widget->fontKde->isChecked() ? TQApplication::font() : font); TQString fontName = selectedFont.family() + " " + TQString(selectedFont.bold() ? "Bold " : "") + TQString(selectedFont.italic() ? "Italic " : "") + TQString::number(selectedFont.pointSize()); TQString themeName = widget->styleKde->isChecked() ? themes["Qt"] : themes[widget->styleBox->currentText()]; TQString themeNameShort = widget->styleKde->isChecked() ? TQString("Qt") : widget->styleBox->currentText(); TQString themeName3 = widget->styleKde3->isChecked() ? gtk3Themes["tdegtk"] : gtk3Themes[widget->styleBox3->currentText()]; TQString themeNameShort3 = widget->styleKde3->isChecked() ? TQString("tdegtk") : widget->styleBox3->currentText(); stream << "# This file was written by TDE\n"; stream << "# You can edit it in the TDE control center, under \"GTK Styles and Fonts\"\n"; stream << "\n"; stream << "include \"" << themeName << "\"\n"; if (TQFile::exists("/etc/gtk-2.0/gtkrc")) stream << "include \"/etc/gtk-2.0/gtkrc\"\n"; stream << "\n"; stream << "style \"user-font\"\n"; stream << "{\n"; stream << "\tfont_name=\"" << fontName << "\"\n"; stream << "}\n"; stream << "widget_class \"*\" style \"user-font\"\n"; stream << "\n"; stream << "gtk-theme-name=\"" << themeNameShort << "\"\n"; stream << "gtk-font-name=\"" << fontName << "\"\n"; stream << "gtk-icon-theme-name=\"tdegtk-icon-theme\"\n"; stream << "gtk-fallback-icon-theme-name=\"tdegtk-fallback-icon-theme\"\n"; stream << "\n"; stream << "include \"~/.gtkrc\"\n"; if (widget->emacsBox->isChecked()) stream << "gtk-key-theme-name=\"Emacs\"\n"; file.close(); // Now we check if that file is actually being loaded. TQDir kdeRcDir; if (!kdeRcDir.exists(KDE_RC_DIR)) { // Make sure KDE's rc dir exists kdeRcDir.mkdir(KDE_RC_DIR); } file.setName(KDE_RC_DIR + "/" + GTK_TQT_RC_FILE); bool envFileDidNotExist = (!file.exists()); file.open(IO_ReadWrite); stream.setDevice(&file); bool found = false; for (;;) { TQString line = stream.readLine(); if (line.isNull()) break; if (line.stripWhiteSpace().startsWith("export GTK2_RC_FILES=$HOME/" + GTK_RC_FILE)) { found = true; break; } } if (!found) { stream << "#!/bin/bash\n\n"; stream << "# Make sure our customised gtkrc file is loaded.\n"; stream << "export GTK2_RC_FILES=$HOME/" + GTK_RC_FILE + "\n"; } file.close(); // Make it executable if (!found) chmod(file.name().utf8(), 0755); // Tell the user to restart KDE if the environment file was created this time if (envFileDidNotExist) TQMessageBox::information(this, "Restart TDE", "Your changes have been saved, but you will have to restart TDE for them to take effect.", TQMessageBox::Ok); // Older versions of the Gtk-Qt theme engine wrote directly into ~/.gtkrc-2.0 // If the user has upgraded, that file needs to be deleted so the old settings // won't override the new ones set now. file.setName(TQDir::homeDirPath() + "/.gtkrc-2.0"); if (file.exists()) { file.open(IO_ReadOnly); TQString firstLine; file.readLine(firstLine, 50); file.close(); if (firstLine == "# This file was written by TDE") file.remove(); } // Simarly, remove the line we added to ~/.bashrc to tell GTK to load ~/.gtkrc-2.0 file.setName(TQDir::homeDirPath() + "/.bashrc"); if (file.exists()) { file.open(IO_ReadOnly); stream.setDevice(&file); TQString fileDataString = stream.read(); file.close(); TQString rcLine = "export GTK2_RC_FILES=$HOME/.gtkrc-2.0"; fileDataString.replace("\n" + rcLine, "\n# (This is no longer needed from version 0.8 of the theme engine)\n# " + rcLine); file.open(IO_WriteOnly); stream.setDevice(&file); stream << fileDataString; file.close(); } TQDir gtk3IniDir; if (!gtk3IniDir.exists(GTK3_INI_DIR)) { // Make sure the GTK INI dir exists gtk3IniDir.mkdir(GTK3_INI_DIR); } TQFile gtk3IniFile(GTK3_INI_DIR + GTK3_INI_FILE); gtk3IniFile.open(IO_WriteOnly); TQTextStream gtk3IniStream(>k3IniFile); gtk3IniStream << "# This file was written by TDE\n"; gtk3IniStream << "# You can edit it in the TDE control center, under \"GTK Styles and Fonts\"\n"; gtk3IniStream << "\n"; gtk3IniStream << "[Settings]\n"; gtk3IniStream << "gtk-theme-name = " << themeNameShort3 << "\n"; gtk3IniStream << "gtk-icon-theme-name = tdegtk-icon-theme\n"; gtk3IniStream << "gtk-fallback-icon-theme = tdegtk-fallback-icon-theme\n"; gtk3IniFile.close(); emit changed(true); } int KcmGtk::buttons() { return TDECModule::Apply; } TQString KcmGtk::quickHelp() const { return ""; } void KcmGtk::emacsDetailsClicked() { if (emacsDetailsDialog == NULL) { emacsDetailsDialog = new EmacsDetails(this); emacsDetailsDialog->list->header()->setStretchEnabled(true, 1); } emacsDetailsDialog->show(); } void KcmGtk::searchPathsClicked() { if (searchPathsDialog == NULL) { searchPathsDialog = new SearchPaths(this); connect(searchPathsDialog->okButton, TQ_SIGNAL(clicked()), TQ_SLOT(searchPathsOk())); connect(searchPathsDialog->pathBox, TQ_SIGNAL(textChanged(const TQString&)), TQ_SLOT(searchPathsTextChanged(const TQString&))); connect(searchPathsDialog->pathList, TQ_SIGNAL(currentChanged(TQListBoxItem*)), TQ_SLOT(searchPathsCurrentChanged(TQListBoxItem*))); connect(searchPathsDialog->addButton, TQ_SIGNAL(clicked()), TQ_SLOT(searchPathsAddClicked())); connect(searchPathsDialog->removeButton, TQ_SIGNAL(clicked()), TQ_SLOT(searchPathsRemoveClicked())); } searchPathsDialog->pathList->clear(); for (TQStringList::Iterator it = gtkSearchPaths.begin(); it != gtkSearchPaths.end(); ++it ) new TQListBoxText(searchPathsDialog->pathList, *it); searchPathsDialog->show(); } void KcmGtk::searchPathsOk() { gtkSearchPaths.clear(); int i=0; TQListBoxItem* item = 0; while ((item = searchPathsDialog->pathList->item(i++))) gtkSearchPaths.append(item->text()); config->writeEntry("gtkSearchPaths", gtkSearchPaths); getInstalledThemes(); } void KcmGtk::searchPathsTextChanged(const TQString& text) { searchPathsDialog->addButton->setDisabled(text.isEmpty()); } void KcmGtk::searchPathsCurrentChanged(TQListBoxItem* item) { searchPathsDialog->removeButton->setEnabled(item != NULL); } void KcmGtk::searchPathsAddClicked() { new TQListBoxText(searchPathsDialog->pathList, searchPathsDialog->pathBox->text()); searchPathsDialog->pathBox->clear(); } void KcmGtk::searchPathsRemoveClicked() { searchPathsDialog->pathList->removeItem(searchPathsDialog->pathList->currentItem()); } #include "kcmgtk.moc"