Hi, I am new to gtk+ programming and I was wondering if I could get some
feedback on my code. I am trying to create an address book program (it doesn't
work) and I've gotten fairly far but now I am kind of lost. Any feedback would
be greatly appreciated.
--
(o_
(o_ (o_ //\ Matthew Hinton -- Linux enthusiast
(/)_ (/)_ V_/_
#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Contact Contact;
struct Contact{
char *first;
char *last;
char *phone;
char *email;
char *address;
char *city;
char *state;
char *grpname;
gboolean delrecord;
Contact *pnext;
Contact *pprev;
};
typedef struct ConTab ConTab;
struct ConTab{
char *groupname;
gboolean deletegroup;
gboolean groupchanged;
Contact *precords;
ConTab *pnext;
};
typedef struct {
GtkWidget *entry_first, *entry_last, *entry_phone, *entry_email;
GtkWidget *entry_address, *entry_city, *entry_state;
GtkWidget *entry_group, *removegroup_entry;
ConTab *contab;
}My_Data;
Contact *create_record_node(){
Contact *pnew;
pnew = (Contact*)malloc(sizeof(Contact));
pnew->first = NULL;
pnew->last = NULL;
pnew->phone = NULL;
pnew->email = NULL;
pnew->address = NULL;
pnew->city = NULL;
pnew->state = NULL;
pnew->grpname = NULL;
pnew->delrecord = FALSE;
pnew->pnext = NULL;
pnew->pprev = NULL;
return pnew;
}
ConTab *create_group_node(){
ConTab *pcon;
pcon = (ConTab *)malloc(sizeof(ConTab));
pcon->groupname = NULL;
pcon->deletegroup = FALSE;
pcon->groupchanged = FALSE;
pcon->precords = NULL;
pcon->pnext = NULL;
return pcon;
}
ConTab *init_groups(ConTab *contab, FILE *datafile){
const int buf_size = 100;
int x, numgrps;
char buf[buf_size];
char *tmpbuf;
ConTab *pcon;
fgets(buf, buf_size, datafile);
numgrps = 0;
tmpbuf = strtok(buf, " ");
while (tmpbuf != NULL){
numgrps++;
tmpbuf = strtok(NULL, " \n");
}
if (numgrps == 0){
printf("\nERROR in data file format!\n");
exit(1);
}
for (x = 0; x < numgrps; x++){
pcon = create_group_node();
if (contab == NULL)
contab = pcon;
else {
pcon->pnext = contab;
contab = pcon;
}
}
rewind(datafile);
fgets(buf, buf_size, datafile);
pcon = contab;
pcon->groupname = strdup(strtok(buf, " "));
for (pcon = pcon->pnext; pcon != NULL; pcon = pcon->pnext){
pcon->groupname = strdup(strtok(NULL, " \n"));
}
return contab;
}
ConTab *init_records(ConTab *contab, FILE *datafile){
const int buf_size = 100;
char buf[buf_size];
char *tmpbuf;
Contact *pnode = NULL;
ConTab *pcon;
fgets(buf, buf_size, datafile);
if (strcmp(buf, "\n") != 0)
printf("ERROR in data file format!\n");
while (!feof(datafile)){
pnode = create_record_node();
fgets(buf, buf_size, datafile);
if (!feof(datafile) && (strcmp(buf, "\n") != 0)){
tmpbuf = strtok(buf, "\\\n");
pcon = contab;
while (strcmp(tmpbuf, pcon->groupname) != 0){
pcon = pcon->pnext;
}
pnode->grpname = strdup(tmpbuf);
fgets(buf, buf_size, datafile);
tmpbuf = strtok(buf, "\\");
pnode->first = strdup(tmpbuf);
tmpbuf = strtok(NULL, "\\\n");
pnode->last = strdup(tmpbuf);
fgets(buf, buf_size, datafile);
tmpbuf = strtok(buf, "\\");
pnode->phone = strdup(tmpbuf);
tmpbuf = strtok(NULL, "\\\n");
pnode->email = strdup(tmpbuf);
fgets(buf, buf_size, datafile);
tmpbuf = strtok(buf, "\n");
pnode->address = strdup(tmpbuf);
fgets(buf, buf_size, datafile);
tmpbuf = strtok(buf, "\\");
pnode->city = strdup(tmpbuf);
tmpbuf = strtok(NULL, "\\\n");
pnode->state = strdup(tmpbuf);
pnode->delrecord = FALSE;
pcon->precords = pnode;
pnode->pprev = pcon->precords;
pnode->pnext = NULL;
} else if (!feof(datafile)){
fgets(buf, buf_size, datafile);
if (!feof(datafile)){
tmpbuf = strtok(buf, "\\\n");
pcon = contab;
while (strcmp(tmpbuf, pcon->groupname) != 0){
pcon = pcon->pnext;
}
pnode->grpname = strdup(tmpbuf);
fgets(buf, buf_size, datafile);
tmpbuf = strtok(buf, "\\");
pnode->first = strdup(tmpbuf);
tmpbuf = strtok(NULL, "\\\n");
pnode->last = strdup(tmpbuf);
fgets(buf, buf_size, datafile);
tmpbuf = strtok(buf, "\\");
pnode->phone = strdup(tmpbuf);
tmpbuf = strtok(NULL, "\\\n");
pnode->email = strdup(tmpbuf);
fgets(buf, buf_size, datafile);
tmpbuf = strtok(buf, "\n");
pnode->address = strdup(tmpbuf);
fgets(buf, buf_size, datafile);
tmpbuf = strtok(buf, "\\");
pnode->city = strdup(tmpbuf);
tmpbuf = strtok(NULL, "\\\n");
pnode->state = strdup(tmpbuf);
pnode->delrecord = FALSE;
pnode->pnext = pcon->precords;
pcon->precords = pnode;
pnode->pprev = pcon->precords;
}
}
}
return contab;
}
/* This is the callback that currently goes with the activate signal
* for the text entry widgets
*/
void enter_callback(My_Data *my_data){
gchar *first, *last, *phone, *email, *address, *city, *state;
first = gtk_entry_get_text(GTK_ENTRY(my_data->entry_first));
printf("first name: %s\n", first);
last = gtk_entry_get_text(GTK_ENTRY(my_data->entry_last));
printf("last name: %s\n", last);
phone = gtk_entry_get_text(GTK_ENTRY(my_data->entry_phone));
printf("phone number: %s\n", phone);
email = gtk_entry_get_text(GTK_ENTRY(my_data->entry_email));
printf("email: %s\n", email);
address = gtk_entry_get_text(GTK_ENTRY(my_data->entry_address));
printf("street address: %s\n", address);
city = gtk_entry_get_text(GTK_ENTRY(my_data->entry_city));
printf("city: %s\n", city);
state = gtk_entry_get_text(GTK_ENTRY(my_data->entry_state));
printf("state: %s\n", state);
}
/* The next two fuctions go with the menu selections */
/* Respond to a button-press by posting a menu passed in as widget.
* Note that the "widget" argument is the menu being posted, NOT
* the button that was pressed.
*/
static gint button_press (GtkWidget *widget, GdkEvent *event){
if (event->type == GDK_BUTTON_PRESS) {
GdkEventButton *bevent = (GdkEventButton *) event;
gtk_menu_popup (GTK_MENU (widget), NULL, NULL, NULL, NULL,
bevent->button, bevent->time);
return TRUE;
}
return FALSE;
}
/* Print a string when a menu item is selected */
static void menuitem_response (gchar *string){
printf ("%s\n", string);
}
/* These are the buttons callback functions
*/
/* i am kind of lost here
*/
void new_group_cb(My_Data *my_data){
gchar *new_group;
ConTab *pcon, *ptemp;
GtkWidget *error_dialog;
GtkWidget *error_label;
GtkWidget *error_hbox;
pcon = create_group_node();
new_group = gtk_entry_get_text(GTK_ENTRY(my_data->entry_group));
pcon->groupname = strdup(new_group);
if (my_data->contab == NULL){
my_data->contab = ptemp;
return;
}
ptemp = my_data->contab;
while (ptemp-> != NULL){
if (strcmp(new_group, ptemp->groupname) == 0){
/* error_dialog = gtk_window_new(GTK_WINDOW_DIALOG);
error_label = gtk_label_new("ERROR -- That group already
exists");
gtk_label_set_justify(GTK_LABEL(error_label),
GTK_JUSTIFY_LEFT);
error_hbox = gtk_hbox_new(FALSE, 0);
gtk_container_add(GTK_WINDOW(error_dialog), error_hbox);
gtk_box_pack_start(GTK_BOX(error_hbox), error_label, TRUE,
TRUE, 0);
gtk_widget_show(error_label);
gtk_widget_show(error_hbox);
gtk_widget_show(error_dialog);
*/
}
ptemp = ptemp->next;
}
ptemp->next = pcon;
return;
}
void remove_group_cb(My_Data *my_data){
gchar *rm_group;
ConTab *pcon, *ptemp;
Contact *prec;
GtkWidget *error_dialog;
GtkWidget *error_label;
GtkWidget *error_hbox;
rm_group = gtk_entry_get_text(GTK_ENTRY(my_data->removegroup_entry));
pcon = my_data->contab;
while (strcmp(pcon->groupname, rm_group) != 0){
pcon = pcon->pnext;
}
if (pcon == NULL){
/* generate error message dialog */
} else {
ptemp = my_data->contab;
while (ptemp != pcon)
ptemp = ptemp->pnext;
ptemp->pnext = pcon->pnext;
prec = pcon->precords;
while (prec != NULL){
free(prec->first);
free(prec->last);
free(prec->phone);
free(prec->email);
free(prec->address);
free(prec->city);
free(prec->state);
free(prec->grpname);
prec = prec->pnext;
}
free(pcon);
}
return;
}
void find_cb(My_Data *my_data){
/* need to add combo box widget to main window in order to implement
* this function
*/
}
void addcontact_cb(My_Data *my_data){
/* need to add combo box widget to main window in order to implement
* this function
*/
}
void removecontact_cb(My_Data *my_data){
/* need to add combo box widget to main window in order to implement
* this function
*/
}
void addgroup_dialog(My_Data *);
void removegroup_dialog(My_Data *);
int main(int argc, char *argv[]) {
GtkWidget *window;
GtkWidget *main_vbox,*vbox_1, *vbox_2;
GtkWidget *hbox_1, *hbox_2, *hbox_3, *hbox_4, *hbox_5;
GtkWidget *hbox_6, *hbox_7, *hbox_8;
GtkWidget *menubar, *file_menu, *file_item, *open_item;
GtkWidget *save_item, *saveas_item, *quit_item;
GtkWidget *help_menu, *help_item, *about_item;
GtkWidget *flabel, *llabel, *plabel, *elabel, *alabel, *clabel, *slabel;
GtkWidget *findbutton, *addgroupbutton, *removegroupbutton;
GtkWidget *addcontactbutton, *removecontactbutton;
My_Data *my_data;
FILE *datafile;
my_data->contab = NULL;
gtk_init(&argc, &argv);
/* setup the main window */
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_signal_connect(GTK_OBJECT(window), "destroy",
GTK_SIGNAL_FUNC(gtk_main_quit), "WM destroy");
gtk_signal_connect (GTK_OBJECT (window), "delete_event",
GTK_SIGNAL_FUNC (gtk_exit), NULL);
gtk_window_set_title(GTK_WINDOW(window), "Matt's Contact Manager");
/* setup the main_vbox */
main_vbox = gtk_vbox_new(FALSE, 1);
gtk_container_border_width(GTK_CONTAINER(main_vbox), 2);
gtk_container_add(GTK_CONTAINER(window), main_vbox);
gtk_box_pack_start(GTK_BOX(main_vbox), vbox_1, FALSE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(main_vbox), vbox_2, FALSE, TRUE, 0);
/* create the menus to go on the menubar */
file_menu = gtk_menu_new(); /* don't need to show menus */
help_menu = gtk_menu_new(); /* don't need to show menus */
/* create the menu items */
open_item = gtk_menu_item_new_with_label("Open");
save_item = gtk_menu_item_new_with_label("Save");
saveas_item = gtk_menu_item_new_with_label("Save As");
quit_item = gtk_menu_item_new_with_label("Quit");
help_item = gtk_menu_item_new_with_label("Help");
about_item = gtk_menu_item_new_with_label("About");
/* add them to the menu */
gtk_menu_append(GTK_MENU(file_menu), open_item);
gtk_menu_append(GTK_MENU(file_menu), save_item);
gtk_menu_append(GTK_MENU(file_menu), saveas_item);
gtk_menu_append(GTK_MENU(file_menu), quit_item);
gtk_menu_append(GTK_MENU(help_menu), help_item);
gtk_menu_append(GTK_MENU(help_menu), about_item);
/* attach the callback functions to the activate signal */
gtk_signal_connect_object(GTK_OBJECT(open_item), "activate",
GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "file.open");
gtk_signal_connect_object(GTK_OBJECT(save_item), "activate",
GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "file.save");
gtk_signal_connect_object(GTK_OBJECT(saveas_item), "activate",
GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "file.saveas");
gtk_signal_connect_object(GTK_OBJECT(help_item), "activate",
GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "help.help");
gtk_signal_connect_object(GTK_OBJECT(about_item), "activate",
GTK_SIGNAL_FUNC(menuitem_response), (gpointer) "help.about");
/* attach the Quit menu item to the exit function */
gtk_signal_connect_object(GTK_OBJECT(quit_item), "activate",
GTK_SIGNAL_FUNC(gtk_main_quit), "WM destroy");
/* create menubar and a menu item for the File and Help entries */
menubar = gtk_menubar_new();
file_item = gtk_menu_item_new_with_label("File");
help_item = gtk_menu_item_new_with_label("Help");
gtk_menu_item_right_justify(help_item);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(file_item), file_menu);
gtk_menu_bar_append(GTK_MENU_BAR(menubar), file_item);
gtk_menu_item_set_submenu(GTK_MENU_ITEM(help_item), help_item);
gtk_menu_bar_append(GTK_MENU_BAR(menubar), help_item);
gtk_box_pack_start(GTK_BOX(vbox_1), menubar, FALSE, TRUE, 0);
/* setup the hbox_1 with the label and text entry widgets
* First Name
*/
hbox_1 = gtk_hbox_new(FALSE, 0);
flabel = gtk_label_new("First Name");
gtk_label_set_justify(GTK_LABEL(flabel), GTK_JUSTIFY_LEFT);
my_data->entry_first = gtk_entry_new_with_max_length(30);
gtk_entry_set_position(my_data->entry_first, 0);
gtk_signal_connect(GTK_OBJECT(my_data->entry_first), "activate",
GTK_SIGNAL_FUNC(enter_callback), my_data);
gtk_box_pack_start(GTK_BOX(hbox_1), flabel, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox_1), my_data->entry_first, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox_1), hbox_1, TRUE, TRUE, 0);
/* setup the hbox_2 with the label and text entry widgets
* Last Name
*/
hbox_2 = gtk_hbox_new(FALSE, 0);
llabel = gtk_label_new("Last Name");
gtk_label_set_justify(GTK_LABEL(llabel), GTK_JUSTIFY_LEFT);
my_data->entry_last = gtk_entry_new_with_max_length(30);
gtk_signal_connect(GTK_OBJECT(my_data->entry_last), "activate",
GTK_SIGNAL_FUNC(enter_callback), my_data);
gtk_box_pack_start(GTK_BOX(hbox_2), llabel, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox_2), my_data->entry_last, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox_1), hbox_2, TRUE, TRUE, 0);
/* setup the hbox_3 with the label and text entry widgets
* Phone Number
*/
hbox_3 = gtk_hbox_new(FALSE, 0);
plabel = gtk_label_new("Phone Number");
gtk_label_set_justify(GTK_LABEL(plabel), GTK_JUSTIFY_LEFT);
my_data->entry_phone = gtk_entry_new_with_max_length(30);
gtk_signal_connect(GTK_OBJECT(my_data->entry_phone), "activate",
GTK_SIGNAL_FUNC(enter_callback), my_data);
gtk_box_pack_start(GTK_BOX(hbox_3), plabel, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox_3), my_data->entry_phone, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox_1), hbox_3, TRUE, TRUE, 0);
/* setup the hbox_4 with the label and text entry widgets
* Email Address
*/
hbox_4 = gtk_hbox_new(FALSE, 0);
elabel = gtk_label_new("Email Address");
gtk_label_set_justify(GTK_LABEL(elabel), GTK_JUSTIFY_LEFT);
my_data->entry_email = gtk_entry_new_with_max_length(30);
gtk_signal_connect(GTK_OBJECT(my_data->entry_email), "activate",
GTK_SIGNAL_FUNC(enter_callback), my_data);
gtk_box_pack_start(GTK_BOX(hbox_4), elabel, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox_4), my_data->entry_email, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox_1), hbox_4, TRUE, TRUE, 0);
/* setup the hbox_5 with the label and text entry widgets
* Street Address
*/
hbox_5 = gtk_hbox_new(FALSE, 0);
alabel = gtk_label_new("Street Address");
gtk_label_set_justify(GTK_LABEL(alabel), GTK_JUSTIFY_LEFT);
my_data->entry_address = gtk_entry_new_with_max_length(30);
gtk_signal_connect(GTK_OBJECT(my_data->entry_address), "activate",
GTK_SIGNAL_FUNC(enter_callback), my_data);
gtk_box_pack_start(GTK_BOX(hbox_5), alabel, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox_5), my_data->entry_address, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox_1), hbox_5, TRUE, TRUE, 0);
/* setup the hbox_6 with the label and text entry widgets
* City
*/
hbox_6 = gtk_hbox_new(FALSE, 0);
clabel = gtk_label_new("City");
gtk_label_set_justify(GTK_LABEL(clabel), GTK_JUSTIFY_LEFT);
my_data->entry_city = gtk_entry_new_with_max_length(30);
gtk_signal_connect(GTK_OBJECT(my_data->entry_city), "activate",
GTK_SIGNAL_FUNC(enter_callback), my_data);
gtk_box_pack_start(GTK_BOX(hbox_6), clabel, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox_6), my_data->entry_city, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox_1), hbox_6, TRUE, TRUE, 0);
/* setup the hbox_7 with the label and text entry widgets
* State
*/
hbox_7 = gtk_hbox_new(FALSE, 0);
slabel = gtk_label_new("State");
gtk_label_set_justify(GTK_LABEL(slabel), GTK_JUSTIFY_LEFT);
my_data->entry_state = gtk_entry_new_with_max_length(3);
gtk_signal_connect(GTK_OBJECT(my_data->entry_state), "activate",
GTK_SIGNAL_FUNC(enter_callback), my_data);
gtk_box_pack_start(GTK_BOX(hbox_7), slabel, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(hbox_7), my_data->entry_state, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox_1), hbox_7, TRUE, TRUE, 0);
/* setup the vbox_2 with the button widgets and attach the buttons
* callback functions to the "clicked" signal
*/
addgroupbutton = gtk_button_new_with_label("Add\n Group");
removegroupbutton = gtk_button_new_with_label("Remove\n Group");
addcontactbutton = gtk_button_new_with_label("Add\n Contact");
removecontactbutton = gtk_button_new_with_label("Remove\n Contact");
findbutton = gtk_button_new_with_label("Find\n Contact");
gtk_signal_connect(GTK_OBJECT(addgroupbutton), "clicked",
GTK_SIGNAL_FUNC(addgroup_dialog), my_data);
gtk_signal_connect(GTK_OBJECT(removegroupbutton), "clicked",
GTK_SIGNAL_FUNC(removegroup_dialog), my_data);
gtk_signal_connect(GTK_OBJECT(addcontactbutton), "clicked",
GTK_SIGNAL_FUNC(addcontact_cb), my_data);
gtk_signal_connect(GTK_OBJECT(removecontactbutton), "clicked",
GTK_SIGNAL_FUNC(removecontact_cb), my_data);
gtk_signal_connect(GTK_OBJECT(findbutton), "clicked",
GTK_SIGNAL_FUNC(find_cb), my_data);
gtk_box_pack_start(GTK_BOX(vbox_2), addgroupbutton, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox_2), removegroupbutton, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox_2), addcontactbutton, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox_2), removecontactbutton, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(vbox_2), findbutton, FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(main_vbox), vbox_2, TRUE, TRUE, 0);
/* Initialize the data structure */
datafile = fopen("data.dat", "a+");
if (datafile == NULL)
fprintf(stderr, "\nError unable to open file data.dat\n");
fseek(datafile, 0, SEEK_SET);
my_data->contab = init_groups(my_data->contab, datafile);
my_data->contab = init_records(my_data->contab, datafile);
fclose(datafile);
gtk_widget_show_all(window);
gtk_main();
return(0);
}
void addgroup_dialog(My_Data *my_data){
GtkWidget *dialog;
GtkWidget *hbox;
dialog = gtk_window_new(GTK_WINDOW_DIALOG);
gtk_window_set_title(GKT_WINDOW(dialog), "Add New Group");
hbox = gtk_hbox_new(FALSE, 1);
gtk_container_border_width(GTK_CONTAINER(hbox), 2);
gtk_container_add(GTK_CONTAINER(dialog), hbox);
my_data->entry_group = gtk_entry_new_with_max_length(30);
gtk_entry_set_position(my_data->entry_group, 0);
gtk_signal_connect(GTK_OBJECT(my_data->entry_group), "activate",
GTK_SIGNAL_FUNC(new_group_cb), my_data);
gtk_box_pack_start(GTK_BOX(hbox), my_data->entry_group, FALSE, TRUE, 0);
gtk_widget_show_all(dialog);
}
void removegroup_dialog(My_Data *my_data){
GtkWidget *dialog;
GtkWidget *hbox;
dialog = gtk_window_new(GTK_WINDOW_DIALOG);
gtk_window_set_title(GKT_WINDOW(dialog), "Remove Group");
hbox = gtk_hbox_new(FALSE, 1);
gtk_container_border_width(GTK_CONTAINER(hbox), 2);
gtk_container_add(GTK_CONTAINER(dialog), hbox);
my_data->removegroup_entry = gtk_entry_new_with_max_length(30);
gtk_entry_set_postion(my_data->removegroup_entry, 0);
gtk_signal_connect(GTK_OBJECT(my_data->removegroup_entry), "activate",
GTK_SIGNAL_FUNC(remove_group_cb), my_data);
gtk_widget_show_all(dialog);
}