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); }