1 /*
2  * <one line to give the library's name and an idea of what it does.>
3  * Copyright (C) 2013  <copyright holder> <email>
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20 
21 #include "maindialog.h"
22 #include "ui_maindialog.h"
23 #include <QDebug>
24 #include <QDir>
25 #include <QFile>
26 #include <QFileInfo>
27 #include <QDBusInterface>
28 #include <QDBusConnection>
29 #include <QDialogButtonBox>
30 #include <QColorDialog>
31 #include <QMessageBox>
32 
33 // dbus interface of compton
34 #define COMPTON_SERVICE_PREFIX    "com.github.chjj.compton."
35 #define COMPTON_PATH       "/"
36 #define COMPTON_INTERFACE  "com.github.chjj.compton"
37 
38 
MainDialog(QString userConfigFile)39 MainDialog::MainDialog(QString userConfigFile) {
40   ui = new Ui::MainDialog;
41   ui->setupUi(this);
42 
43   if(userConfigFile.isEmpty()) {
44     userConfigFile_ = QFile::decodeName(qgetenv("XDG_CONFIG_HOME"));
45     if(userConfigFile_.isEmpty()) {
46       userConfigFile_ = QDir::homePath();
47       userConfigFile_ += QLatin1String("/.config");
48     }
49     // QDir configDir = QDir(userConfigFile);
50     // if(!configDir.exists())
51     userConfigFile_ += QLatin1String("/compton.conf");
52   }
53   else
54     userConfigFile_ = userConfigFile;
55 
56   config_init(&config_);
57   if(config_read_file(&config_, userConfigFile_.toLocal8Bit().constData()) == CONFIG_FALSE) {
58     // loading user config file failed
59     // try our default example
60     qDebug() << "load fail, try " << COMPTON_CONF_DATA_DIR << "/compton.conf.example";
61     config_read_file(&config_, COMPTON_CONF_DATA_DIR "/compton.conf.example");
62   }
63 
64   // set up signal handlers and initial values of the controls
65   connect(ui->buttonBox, SIGNAL(clicked(QAbstractButton*)), SLOT(onDialogButtonClicked(QAbstractButton*)));
66   connect(ui->aboutButton, SIGNAL(clicked(bool)), SLOT(onAboutButtonClicked()));
67   connect(ui->shadow_color, SIGNAL(clicked(bool)), SLOT(onColorButtonClicked()));
68   double color;
69   shadowColor_.setRedF(config_lookup_float(&config_, "shadow-red", &color) == CONFIG_TRUE ?  color : 0.0);
70   shadowColor_.setGreenF(config_lookup_float(&config_, "shadow-green", &color) == CONFIG_TRUE ? color : 0.0);
71   shadowColor_.setBlueF(config_lookup_float(&config_, "shadow-blue", &color) == CONFIG_TRUE ? color : 0.0);
72   updateShadowColorButton();
73 
74   // objectNames are kept the same as config file key names.
75   const QList<QWidget*> widgets = findChildren<QWidget*>();
76   QList<QWidget*>::const_iterator i;
77   for(i = widgets.constBegin(); i != widgets.constEnd(); ++i) {
78     QWidget *child = *i;
79     if(!child->isWidgetType() || child->objectName().isEmpty())
80       continue;
81     // objectName uses _ while config file keys uses - as separator.
82     QByteArray keyName = child->objectName().replace(QLatin1Char('_'), QLatin1Char('-')).toLatin1(); // generate config key from objectName.
83     if(child->inherits("QCheckBox")) {
84       int val = -1;
85       if(config_lookup_bool(&config_, keyName.constData(), &val) == CONFIG_TRUE)
86         static_cast<QCheckBox*>(child)->setChecked((bool)val);
87       connect(child, SIGNAL(toggled(bool)), SLOT(onButtonToggled(bool)));
88     }
89     else if(child->inherits("QDoubleSpinBox")) {
90       double val;
91       if(config_lookup_float(&config_, keyName.constData(), &val) == CONFIG_TRUE)
92         static_cast<QDoubleSpinBox*>(child)->setValue(val);
93       connect(child, SIGNAL(valueChanged(double)), SLOT(onSpinValueChanged(double)));
94     }
95     else if(child->inherits("QSpinBox")) {
96       int val;
97       if(config_lookup_int(&config_, keyName.constData(), &val) == CONFIG_TRUE)
98         static_cast<QSpinBox*>(child)->setValue(val);
99       connect(child, SIGNAL(valueChanged(int)), SLOT(onSpinValueChanged(int)));
100     }
101     else if(child->inherits("QRadioButton")) {
102       if(child->parent()->inherits("QGroupBox")) {
103         QByteArray groupKeyName = child->parent()->objectName().replace(QLatin1Char('_'), QLatin1Char('-')).toLatin1();
104         if(keyName.startsWith(groupKeyName)) {
105           const char *val;
106           if(config_lookup_string(&config_, groupKeyName.constData(), &val) == CONFIG_TRUE)
107               static_cast<QRadioButton*>(child)->setChecked(keyName == groupKeyName.append('-').append(val));
108           connect(child, SIGNAL(toggled(bool)), SLOT(onRadioGroupToggled(bool)));
109           continue;
110         }
111       }
112       int val = -1;
113       if(config_lookup_bool(&config_, keyName.constData(), &val) == CONFIG_TRUE)
114         static_cast<QRadioButton*>(child)->setChecked((bool)val);
115       connect(child, SIGNAL(toggled(bool)), SLOT(onButtonToggled(bool)));
116     }
117   }
118 }
119 
~MainDialog()120 MainDialog::~MainDialog() {
121   config_destroy(&config_);
122     delete ui;
123 }
124 
onButtonToggled(bool checked)125 void MainDialog::onButtonToggled(bool checked) {
126   qDebug() << "toggled: " << sender()->objectName();
127   // generate config key from objectName.
128   QByteArray keyName = sender()->objectName().replace(QLatin1Char('_'), QLatin1Char('-')).toLatin1();
129   configSetBool(keyName.constData(), checked);
130   // saveConfig();
131 }
132 
onSpinValueChanged(double d)133 void MainDialog::onSpinValueChanged(double d) {
134   qDebug() << "changed: " << sender()->objectName() << ": " << d;
135   // generate config key from objectName.
136   QByteArray keyName = sender()->objectName().replace(QLatin1Char('_'), QLatin1Char('-')).toLatin1();
137   configSetFloat(keyName.constData(), d);
138   // saveConfig();
139 }
140 
onSpinValueChanged(int i)141 void MainDialog::onSpinValueChanged(int i) {
142   qDebug() << "changed: " << sender()->objectName() << ": " << i;
143   // generate config key from objectName.
144   QByteArray keyName = sender()->objectName().replace(QLatin1Char('_'), QLatin1Char('-')).toLatin1();
145   configSetInt(keyName.constData(), i);
146   // saveConfig();
147 }
148 
onRadioGroupToggled(bool checked)149 void MainDialog::onRadioGroupToggled(bool checked) {
150   if (checked) {
151     qDebug() << "toggled: " << sender()->objectName();
152     // generate config key from objectName.
153     QByteArray keyName = sender()->parent()->objectName().replace(QLatin1Char('_'), QLatin1Char('-')).toLatin1();
154     QByteArray val = sender()->objectName().right(sender()->objectName().size() - (keyName.size() + 1)).replace(QLatin1Char('_'), QLatin1Char('-')).toLatin1();
155     configSetString(keyName.constData(), val.constData());
156     // saveConfig();
157   }
158 }
159 
saveConfig()160 void MainDialog::saveConfig() {
161   // ensure the existance of user config dir
162   QString configDir = QFileInfo(userConfigFile_).dir().path();
163   QDir().mkpath(configDir);
164   qDebug() << userConfigFile_;
165   // save the config file
166   config_write_file(&config_, userConfigFile_.toLocal8Bit().constData());
167 
168   // ask compton to reload the config
169   QString displayName = QString::fromLocal8Bit(qgetenv("DISPLAY"));
170   for(int i = 0; i < displayName.length(); ++i) {
171     if(!displayName[i].isNumber()) // replace non-numeric chars with _
172       displayName[i] = QLatin1Char('_');
173   }
174   QString comptonServiceName = QStringLiteral(COMPTON_SERVICE_PREFIX) + displayName;
175   QDBusInterface iface(comptonServiceName, QStringLiteral(COMPTON_PATH), QStringLiteral(COMPTON_INTERFACE));
176   if(iface.isValid()) {
177     iface.call(QStringLiteral("reset"));
178     // raise ourself to the top again (we'll loosing focus after reloading compton)
179     activateWindow();
180   }
181   // FIXME: dbus interface of compton is not always available and reset() creates
182   // much flickers. Maybe we should use internal dbus method set_opts().
183   // Or, we can patch compton to do what we want.
184 }
185 
done(int res)186 void MainDialog::done(int res) {
187   QDialog::done(res);
188 }
189 
onDialogButtonClicked(QAbstractButton * button)190 void MainDialog::onDialogButtonClicked(QAbstractButton* button) {
191   if(ui->buttonBox->buttonRole(button) == QDialogButtonBox::ApplyRole) {
192     saveConfig();
193   }
194 }
195 
onColorButtonClicked()196 void MainDialog::onColorButtonClicked() {
197   QColorDialog dlg(shadowColor_);
198   dlg.setOption(QColorDialog::ShowAlphaChannel, false);
199   if(dlg.exec() == QDialog::Accepted) {
200     shadowColor_ = dlg.selectedColor();
201     updateShadowColorButton();
202     configSetFloat("shadow-red", shadowColor_.redF());
203     configSetFloat("shadow-green", shadowColor_.greenF());
204     configSetFloat("shadow-blue", shadowColor_.blueF());
205   }
206 }
207 
onAboutButtonClicked()208 void MainDialog::onAboutButtonClicked() {
209   QMessageBox::about(this, tr("About ComptonConf"),
210                      tr("ComptonConf - configuration tool for compton\n\nCopyright (C) 2013\nAuthor: Hong Jen Yee (PCMan) <pcman.tw@gmail.com>"));
211 }
212 
updateShadowColorButton()213 void MainDialog::updateShadowColorButton() {
214   QString qss = QStringLiteral("QPushButton {"
215   "background-color:%1;"
216   "}").arg(shadowColor_.name());
217   ui->shadow_color->setStyleSheet(qss);
218 }
219 
configSetInt(const char * key,int val)220 void MainDialog::configSetInt(const char* key, int val) {
221   config_setting_t* setting = config_lookup(&config_, key);
222   if(!setting) { // setting not found
223     // add a new setting for it
224     config_setting_t* root = config_root_setting(&config_);
225     setting = config_setting_add(root, key, CONFIG_TYPE_INT);
226   }
227   config_setting_set_int(setting, val);
228 }
229 
configSetFloat(const char * key,double val)230 void MainDialog::configSetFloat(const char* key, double val) {
231   config_setting_t* setting = config_lookup(&config_, key);
232   if(!setting) { // setting not found
233     // add a new setting for it
234     config_setting_t* root = config_root_setting(&config_);
235     setting = config_setting_add(root, key, CONFIG_TYPE_FLOAT);
236   }
237   config_setting_set_float(setting, val);
238 }
239 
configSetBool(const char * key,bool val)240 void MainDialog::configSetBool(const char* key, bool val) {
241   config_setting_t* setting = config_lookup(&config_, key);
242   if(!setting) { // setting not found
243     // add a new setting for it
244     config_setting_t* root = config_root_setting(&config_);
245     setting = config_setting_add(root, key, CONFIG_TYPE_BOOL);
246   }
247   config_setting_set_bool(setting, val);
248 }
249 
configSetString(const char * key,const char * val)250 void MainDialog::configSetString(const char *key, const char *val)
251 {
252   config_setting_t* setting = config_lookup(&config_, key);
253   if(!setting) { // setting not found
254     // add a new setting for it
255     config_setting_t* root = config_root_setting(&config_);
256     setting = config_setting_add(root, key, CONFIG_TYPE_STRING);
257   }
258   config_setting_set_string(setting, val);
259 }
260