/*
Copyright (c) 2004-2005, Miguel Mendez. All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
$Id: qt_ui.C 51 2007-12-18 11:40:51Z mmendez $
Note to self: C++ is an abomination from hell.
*/
#include <qcolor.h>
#include <qgroupbox.h>
#include <qhbox.h>
#include <qvbox.h>
#include <qlabel.h>
#include <qlayout.h>
#include <qlineedit.h>
#include <qlistview.h>
#include <qobject.h>
#include <qmessagebox.h>
#include <qapplication.h>
#include <qmainwindow.h>
#include <qpushbutton.h>
#include <qstatusbar.h>
#include <qtable.h>
#include <qtabwidget.h>
#include <qwidget.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <unistd.h>
#include <time.h>
extern char *tzname[2];
#include "parser.h"
#include "thefish.h"
#include "file.h"
#include "qt_ui_priv.moc"
#include "fish64.xpm"
#define NOT_MODIFIED 0
#define IS_MODIFIED 1
static QListView *knobs_table;
static QListView *strings_table;
static QApplication *thefish;
static QPushButton *SaveButton;
static QMainWindow *mw;
static QStatusBar *my_status_bar;
static int oldsize[2];
static int dirty;
static RC_NODE *my_rc_knobs, *my_rc_strings;
static int my_num_knobs, my_num_strings;
// C compat glue
extern "C" int create_qt_ui(RC_CONF *, int, char **);
static void save_geometry(void);
extern "C" int
create_qt_ui(RC_CONF *my_rc,
int argc, char **argv)
{
int i;
char *homedir;
char temp[FILENAME_MAX];
int fd;
FILE *fp;
QListViewItem *element;
QCheckListItem *foo;
MyDialogs my_dialogs;
TableCallbacks my_tablecallbacks;
thefish = new QApplication( argc, argv);
// It's more convenient to have these
// as global.
my_rc_knobs = my_rc->knobs_ptr;
my_rc_strings = my_rc->string_ptr;
my_num_knobs = my_rc->knobs_size;
my_num_strings = my_rc->string_size;
mw = new QMainWindow;
QVBox *vbox = new QVBox(mw, 0, 0);
QTabWidget main_tab(vbox, 0, 0);
knobs_table = new QListView;
strings_table = new QListView;
strings_table->setAllColumnsShowFocus(TRUE);
knobs_table->addColumn("Variable Name", -1);
strings_table->addColumn("Name", -1);
strings_table->addColumn("Value",-1);
QObject::connect(strings_table, SIGNAL(itemRenamed(QListViewItem*, int, const QString &)),
&my_tablecallbacks, SLOT(StringChanged(QListViewItem*, int, const QString &)));
QObject::connect(knobs_table, SIGNAL(clicked(QListViewItem *)),
&my_tablecallbacks, SLOT(KnobChanged(QListViewItem*)));
QObject::connect(strings_table, SIGNAL(clicked(QListViewItem *)),
&my_tablecallbacks, SLOT(StringClicked(QListViewItem*)));
main_tab.addTab(knobs_table, "Knobs");
main_tab.addTab(strings_table, "Strings");
QHBox *hbuttons = new QHBox(vbox, 0, 0);
SaveButton = new QPushButton("&Save", hbuttons, 0);
QPushButton AddButton("&Add", hbuttons, 0);
QPushButton AboutButton("A&bout", hbuttons, 0);
QPushButton QuitButton("&Quit", hbuttons, 0);
// No save for now...
SaveButton->setEnabled(false);
QObject::connect(&QuitButton, SIGNAL(clicked()), &my_dialogs, SLOT(CheckSaved()));
QObject::connect(&AboutButton, SIGNAL(clicked()), &my_dialogs, SLOT(ShowAbout()));
QObject::connect(&AddButton, SIGNAL(clicked()), &my_dialogs, SLOT(DoAdd()));
QObject::connect(SaveButton, SIGNAL(clicked()), &my_dialogs, SLOT(DoSave()));
// We're now using human readable data, handle the migration
// transparently for the user.
homedir = getenv("HOME");
if (homedir != NULL) {
snprintf(temp, FILENAME_MAX, "%s/%s", homedir, ".thefishrc");
fd = open(temp, O_RDONLY, 0);
if (fd != -1 ) {
i = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
if (i == sizeof(oldsize)) {
read(fd, &oldsize[0], sizeof(oldsize));
close(fd);
} else {
fp = fdopen(fd, "r");
fscanf(fp, "geometry=%i,%i", &oldsize[0], &oldsize[1]);
fclose(fp);
}
} else {
// Set some default values
oldsize[0] = 400;
oldsize[1] = 480;
}
}
// Build the table
knobs_table->setColumnWidthMode (1, QListView::Maximum);
knobs_table->setRootIsDecorated(false);
for (i = my_rc->knobs_size - 1; i >= 0; i--) {
// No user comments yet
(my_rc->knobs_ptr+i)->user_comment = 0;
foo = new QCheckListItem( knobs_table, (my_rc->knobs_ptr+i)->name, QCheckListItem::CheckBox);
if ((my_rc->knobs_ptr+i)->knob_val == KNOB_IS_NO) {
foo->setOn(FALSE);
} else {
foo->setOn(TRUE);
}
}
for (i = my_rc->string_size - 1; i >= 0; i--) {
// No user comments yet
(my_rc->string_ptr+i)->user_comment = 0;
element = new QListViewItem(strings_table,
(my_rc->string_ptr+i)->name,
(my_rc->string_ptr+i)->value);
element->setRenameEnabled(0, FALSE);
element->setRenameEnabled(1, TRUE);
}
// Set the app icon
QPixmap my_icon((const char **) fish64_xpm);
mw->setIcon((const QPixmap ) my_icon);
mw->setCaption("The Fish " THE_FISH_VERSION);
mw->setCentralWidget( vbox );
thefish->setMainWidget(mw);
mw->show();
mw->resize(oldsize[0], oldsize[1]);
dirty = 0;
my_status_bar = mw->statusBar();
my_status_bar->message("Ready");
return thefish->exec();
}
void
MyDialogs::CheckSaved()
{
int retcode;
if (dirty > 0) {
retcode = QMessageBox::warning(0, "The Fish",
"There are unsaved changes. "
"Quit anyway?\n\n",
"Yes",
"No", 0, 0, 1 );
if (retcode == 0) {
save_geometry();
thefish->exit(0);
}
} else {
save_geometry();
thefish->exit(0);
}
}
void
MyDialogs::ShowAbout()
{
QMessageBox::about(0, "About The Fish",
"The Fish "
THE_FISH_VERSION
"\nCopyright (c) 2002 - 2007, "
"Miguel Mendez\n"
"Shark icon (c) 2001-2003, Alan Smith\n"
"E-Mail: <mmendez@energyhq.be>\n"
"http://thefish.berlios.de/\n"
);
}
void
TableCallbacks::StringChanged(QListViewItem * item, int col, const QString & text)
{
QString value;
int i;
value = item->text(0);
#ifdef VERBOSE_CONSOLE
fprintf(stderr, "You've set %s to %s\n", value.ascii(), text.ascii());
#endif
for (i = 0; i <= my_num_strings; i++) {
if (strncmp((my_rc_strings+i)->name, value.ascii(), 255) == 0) {
strncpy((my_rc_strings+i)->value, text.ascii(), 255);
if (strncmp((my_rc_strings+i)->orig, text.ascii(), 255) == 0) {
if (dirty > 0) dirty--;
(my_rc_strings+i)->modified = NOT_MODIFIED;
} else {
dirty++;
(my_rc_strings+i)->modified = IS_MODIFIED;
}
if (dirty == 0) {
SaveButton->setEnabled(FALSE);
} else {
SaveButton->setEnabled(TRUE);
}
break;
}
}
}
void
TableCallbacks::StringClicked(QListViewItem *item)
{
QString value;
int i;
value = item->text(0);
for (i = 0; i <= my_num_strings; i++) {
if (strncmp((my_rc_strings+i)->name, value.ascii(), 255) == 0) {
my_status_bar->message((my_rc_strings+i)->comment);
break;
}
}
}
void
TableCallbacks::KnobChanged(QListViewItem *item)
{
QString value;
QCheckListItem *foo;
int i;
value = item->text(0);
foo = (QCheckListItem *) item;
for (i = 0; i < my_num_knobs; i++) {
if (strncmp(value.ascii(), (my_rc_knobs+i)->name, 255) == 0) {
break;
}
}
if (foo->isOn() && (my_rc_knobs+i)->knob_val == KNOB_IS_NO) {
#ifdef VERBOSE_CONSOLE
printf("Now %s is activated\n", value.ascii());
#endif
(my_rc_knobs+i)->knob_val = KNOB_IS_YES;
if ((my_rc_knobs+i)->knob_orig == KNOB_IS_YES) {
if ((my_rc_knobs+i)->user_added == USER_ADDED_NO) {
(my_rc_knobs+i)->modified = MODIFIED_NO;
if (dirty > 0) dirty--;
}
} else {
(my_rc_knobs+i)->modified = MODIFIED_YES;
dirty++;
}
} else if (foo->isOn()==FALSE && (my_rc_knobs+i)->knob_val == KNOB_IS_YES) {
#ifdef VERBOSE_CONSOLE
printf("Now %s is deactivated\n", value.ascii());
#endif
(my_rc_knobs+i)->knob_val = KNOB_IS_NO;
if ((my_rc_knobs+i)->knob_orig == KNOB_IS_NO) {
if ((my_rc_knobs+i)->user_added == USER_ADDED_NO) {
(my_rc_knobs+i)->modified = MODIFIED_NO;
if (dirty > 0) dirty--;
}
} else {
(my_rc_knobs+i)->modified = MODIFIED_YES;
dirty++;
}
}
my_status_bar->message((my_rc_knobs+i)->comment);
if (dirty == 0) {
SaveButton->setEnabled(FALSE);
} else {
SaveButton->setEnabled(TRUE);
}
}
// Save window geometry prefs
// We use the same format for
// the GTK+ and the Qt interfaces
void
save_geometry(void)
{
int newsize[2];
char *homedir;
char temp[FILENAME_MAX];
int fd;
FILE *fp;
newsize[0] = mw->width();
newsize[1] = mw->height();
if (oldsize[0] != newsize[0] || oldsize[1] != newsize[1]) {
homedir = getenv("HOME");
if (homedir == NULL) return;
snprintf(temp, FILENAME_MAX, "%s/%s", homedir, ".thefishrc");
fd = open(temp, O_WRONLY|O_CREAT|O_TRUNC, 0666);
if (fd == -1) return;
fp = fdopen(fd, "a");
fprintf(fp, "geometry=%i,%i\n", newsize[0], newsize[1]);
fclose(fp);
}
}
// This function saves changes to $FISH_RC or
// /etc/rc.conf
void
MyDialogs::DoSave()
{
int i, not_committed;
char *rc_file;
char path_string[1024];
not_committed = 0;
rc_file = getenv("FISH_RC");
if (dirty > 0) {
i = save_changes(my_rc_knobs, my_num_knobs,
my_rc_strings, my_num_strings);
if (i == -1) {
not_committed = 1;
}
/* Pop up window */
if (not_committed == 1) {
snprintf(path_string, 1023, "Can't open '%s' for writing. Changes not saved.",
rc_file != NULL ? rc_file : RC_FILE);
QMessageBox::critical(0, "The Fish",
path_string);
} else {
QMessageBox::information(0, "The Fish",
"Changes saved.");
}
} else {
QMessageBox::information(0, "The Fish",
"There are no unsaved changes.");
}
if (not_committed == 0) {
dirty = 0;
SaveButton->setEnabled(FALSE);
}
}
void
MyDialogs::DoAdd()
{
int i;
int dupe,invalid;
char c, d;
QString my_name, my_value, my_comment;
QCheckListItem *foo;
QListViewItem *element;
char *new_name, *new_value, *new_comment;
QDialog *my_add_dialog = new QDialog(mw, "my_add_dialog", 0);
QBoxLayout *add_vbox = new QVBoxLayout(my_add_dialog);
QTable *table = new QTable(3, 2, my_add_dialog);
add_vbox->addWidget(table);
table->setText(0, 0, "Name");
table->setText(1, 0, "Value");
table->setText(1, 1, "\"\"");
table->setText(2, 0, "Optional comment");
table->setColumnReadOnly(0, true);
table->adjustColumn(0);
table->setColumnWidth(1, 180);
table->setLeftMargin(0);
table->setTopMargin(0);
QHBox *add_hbuttons = new QHBox(my_add_dialog, 0, 0);
add_vbox->addWidget(add_hbuttons);
QPushButton *AddYesButton = new QPushButton("&OK", add_hbuttons, 0);
QPushButton *AddNoButton = new QPushButton("&Cancel", add_hbuttons, 0);
QObject::connect(AddYesButton, SIGNAL(clicked()), my_add_dialog, SLOT(accept()));
QObject::connect(AddNoButton, SIGNAL(clicked()), my_add_dialog, SLOT(reject()));
add_vbox->setResizeMode(QLayout::Auto);
add_vbox->activate();
my_add_dialog->setCaption("Add a new entry");
if (my_add_dialog->exec() == QDialog::Accepted) {
dupe = 0;
invalid = 0;
my_name = table->text( 0, 1);
my_value = table->text(1, 1);
my_comment = table->text(2, 1);
new_name = (char *) my_name.ascii();
new_value = (char *) my_value.ascii();
new_comment = (char *) my_comment.ascii();
if (new_name == NULL || new_value == NULL) {
QMessageBox::critical(0, "The Fish",
"Name and Value cannot be null.");
delete(my_add_dialog);
return;
}
for (i = 0; i < my_num_knobs; i++) {
if (strncmp(my_name.ascii(), (my_rc_knobs+i)->name, 255) == 0) {
dupe = 1;
break;
}
}
for (i = 0; i < my_num_strings; i++) {
if (strncmp(my_name.ascii(), (my_rc_strings+i)->name, 255) == 0) {
dupe = 1;
break;
}
}
if (dupe == 1) {
QMessageBox::critical(0, "The Fish",
"An entry with that name already exists.");
} else {
c = new_value[0];
d = new_value[strlen(new_value)-1];
if (c != '"' || d != '"') {
QMessageBox::critical(0, "The Fish",
"Value must begin and end with \".");
delete(my_add_dialog);
return;
}
// It's a knob set to YES
if (strncasecmp(new_value, KNOB_YES, 255) == 0) {
(my_rc_knobs+my_num_knobs)->user_comment = 0;
(my_rc_knobs+my_num_knobs)->user_added = USER_ADDED_YES;
(my_rc_knobs+my_num_knobs)->knob_val = KNOB_IS_YES;
(my_rc_knobs+my_num_knobs)->knob_orig = KNOB_IS_YES;
(my_rc_knobs+my_num_knobs)->modified = MODIFIED_YES;
strncpy((my_rc_knobs+my_num_knobs)->name, new_name, 255);
if (new_comment != NULL) {
if (strlen(new_comment) > 1) {
strncpy((my_rc_knobs+my_num_knobs)->comment, new_comment, 255);
(my_rc_knobs+my_num_knobs)->user_comment = 1;
}
}
foo = new QCheckListItem(knobs_table, (my_rc_knobs+my_num_knobs)->name,
QCheckListItem::CheckBox);
foo->setOn(TRUE);
my_num_knobs++;
dirty++;
SaveButton->setEnabled(TRUE);
// It's a knob set to NO
} else if (strncasecmp(new_value, KNOB_NO, 255) == 0) {
(my_rc_knobs+my_num_knobs)->user_comment = 0;
(my_rc_knobs+my_num_knobs)->user_added = USER_ADDED_YES;
(my_rc_knobs+my_num_knobs)->knob_val = KNOB_IS_NO;
(my_rc_knobs+my_num_knobs)->knob_orig = KNOB_IS_NO;
(my_rc_knobs+my_num_knobs)->modified = MODIFIED_YES;
strncpy((my_rc_knobs+my_num_knobs)->name, new_name, 255);
if (new_comment != NULL) {
if (strlen(new_comment) > 1) {
strncpy((my_rc_knobs+my_num_knobs)->comment, new_comment, 255);
(my_rc_knobs+my_num_knobs)->user_comment = 1;
}
}
foo = new QCheckListItem(knobs_table, (my_rc_knobs+my_num_knobs)->name,
QCheckListItem::CheckBox);
foo->setOn(FALSE);
my_num_knobs++;
dirty++;
SaveButton->setEnabled(TRUE);
// It's a string
} else {
(my_rc_strings+my_num_strings)->user_comment = 0;
(my_rc_strings+my_num_strings)->user_added = USER_ADDED_YES;
(my_rc_strings+my_num_strings)->modified = MODIFIED_YES;
strncpy((my_rc_strings+my_num_strings)->name, new_name, 255);
strncpy((my_rc_strings+my_num_strings)->value, new_value, 255);
strncpy((my_rc_strings+my_num_strings)->orig, new_value, 255);
if (new_comment != NULL) {
if (strlen(new_comment) > 1) {
strncpy((my_rc_strings+my_num_strings)->comment, new_comment, 255);
(my_rc_strings+my_num_strings)->user_comment = 1;
}
}
element = new QListViewItem(strings_table,
(my_rc_strings+my_num_strings)->name,
(my_rc_strings+my_num_strings)->value);
element->setRenameEnabled(0, FALSE);
element->setRenameEnabled(1, TRUE);
my_num_strings++;
dirty++;
SaveButton->setEnabled(TRUE);
}
}
}
delete(my_add_dialog);
}
syntax highlighted by Code2HTML, v. 0.9.1