/* This file is part of GNUnet GNUnet 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, or (at your option) any later version. GNUnet 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 GNUnet; see the file COPYING. If not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /** * @file src/applications/afs/gtkui/helper.c * @brief This file contains some GUI helper functions * @author Igor Wronsky */ #include "platform.h" #include "helper.h" #include #ifndef MINGW #include #endif #include "main.h" #define HELPER_DEBUG NO GtkWidget * infoWindow = NULL; static GtkWidget * infoText = NULL; /* the main thread */ static PTHREAD_T mainThread; static SaveCall ** psc; static unsigned int pscCount; static Mutex sclock; static int saveCallsUp; /** * Call a callback function from the mainloop/main thread ("SaveCall"). * Since GTK doesn't work with multi-threaded applications under Windows, * all GTK operations have to be done in the main thread */ void gtkSaveCall(GtkFunction func, void *args) { SaveCall call; int i; call.args = args; call.func = func; MUTEX_LOCK(&sclock); if ( (saveCallsUp == NO) || (! PTHREAD_SELF_TEST(&mainThread)) ) { call.sem = SEMAPHORE_NEW(0); GROW(psc, pscCount, pscCount+1); psc[pscCount-1] = &call; MUTEX_UNLOCK(&sclock); gtk_idle_add(func, &call); SEMAPHORE_DOWN(call.sem); /* remove from psc list */ MUTEX_LOCK(&sclock); for (i=0;ifunc(psc[i]); MUTEX_UNLOCK(&sclock); gnunet_util_sleep(50 * cronMILLIS); /* sleep here is somewhat important, first of all, after completion we need to give the semaphore-mechanism time to remove the save-call from the list to avoid running it twice; also, this function might be called in a tight loop (see search.c), so we should give the other threads some time to run. */ return YES; } void gtkDoneSaveCalls() { int i; saveCallsUp = NO; PTHREAD_REL_SELF(&mainThread); MUTEX_LOCK(&sclock); for (i=0;ifunc(psc[i]); i = pscCount; MUTEX_UNLOCK(&sclock); /* wait until all PSC-jobs have left the gtkSaveCall method before destroying the mutex! */ while (i != 0) { gnunet_util_sleep(50 * cronMILLIS); MUTEX_LOCK(&sclock); i = pscCount; MUTEX_UNLOCK(&sclock); } MUTEX_DESTROY(&sclock); } /** * Called from a "SaveCall"-function to indicate that it is done */ void gtkSaveCallDone(Semaphore *sem) { if (sem) SEMAPHORE_UP(sem); } /** * Destroy a widget. Called from threads other than the main thread */ gint doDestroyWidget(SaveCall *call) { gtk_widget_destroy((GtkWidget *) call->args); gtkSaveCallDone(call->sem); return FALSE; } /** * Callback for handling "delete_event": close the window */ gint deleteEvent(GtkWidget * widget, GdkEvent * event, gpointer data) { #if DEBUG_HELPER LOG(LOG_DEBUG, "In '%s'.\n", __FUNCTION__); #endif return FALSE; } /** * A callback to destroy any widget given as second argument */ void destroyWidget(GtkWidget * dummy, GtkWidget * widget) { #if DEBUG_HELPER LOG(LOG_DEBUG, "In '%s' of %p.\n", __FUNCTION__, widget); #endif gtk_widget_destroy(widget); } /** * Callback function for guiMessage() */ gint doGuiMessage(SaveCall *call) { GtkWidget * window; GtkWidget * label; GtkWidget * box; GtkWidget * button; window = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_container_set_border_width(GTK_CONTAINER(window), 10); gtk_window_set_title(GTK_WINDOW(window), _("Notification")); gtk_signal_connect(GTK_OBJECT(window), "delete_event", GTK_SIGNAL_FUNC(deleteEvent), NULL); box = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER(window), box); label = gtk_label_new((gchar *) call->args); free((gchar *) call->args); /* allocated in g_strdup_vprintf */ gtk_box_pack_start(GTK_BOX(box), label, FALSE, FALSE, 0); button = gtk_button_new_with_label(_("Ok")); gtk_signal_connect(GTK_OBJECT (button), "clicked", GTK_SIGNAL_FUNC(destroyWidget), window); gtk_box_pack_start(GTK_BOX(box),button,FALSE,FALSE,0); gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_MOUSE); gtk_widget_show_all(window); gtk_widget_grab_focus(button); gtkSaveCallDone(call->sem); return FALSE; } /** * Displays an informative message to the user in a fresh window */ void guiMessage(const char * format, ...) { va_list args; gchar *note; va_start(args, format); note = g_strdup_vprintf(format, args); va_end(args); gtkSaveCall((GtkFunction) doGuiMessage, note); } static void hideWindow(GtkWidget * widget, gpointer data) { if(widget) gtk_widget_hide(widget); } /** * Callback for infoMessage() */ gint doInfoMessage(SaveCall *call) { GtkTextIter iter; GtkTextBuffer * buffer; if(!infoWindow) { GtkWidget * box1; GtkWidget * button; GtkWidget * scrolled_window; infoWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL); gtk_signal_connect(GTK_OBJECT(infoWindow), "delete_event", GTK_SIGNAL_FUNC(deleteEvent), NULL); gtk_window_set_title(GTK_WINDOW(infoWindow), _("Messages")); gtk_widget_set_usize(GTK_WIDGET(infoWindow), 780, 300); box1 = gtk_vbox_new(FALSE, 0); gtk_container_add(GTK_CONTAINER (infoWindow), box1); gtk_widget_show(box1); /* create a scrollable window */ scrolled_window = gtk_scrolled_window_new(NULL, NULL); gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window), GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS); gtk_box_pack_start(GTK_BOX(box1), scrolled_window, TRUE, TRUE, 0); gtk_widget_show(scrolled_window); /* create a text widget */ infoText = gtk_text_view_new(); gtk_text_view_set_editable(GTK_TEXT_VIEW (infoText), FALSE); gtk_container_add(GTK_CONTAINER(scrolled_window), infoText); gtk_widget_show(infoText); gtk_widget_realize(infoText); /* finish with a close button */ button = gtk_button_new_with_label(_("Close")); gtk_box_pack_start(GTK_BOX (box1), button, FALSE, FALSE, 0); gtk_signal_connect_object(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(hideWindow), GTK_OBJECT(infoWindow)); gtk_signal_connect_object(GTK_OBJECT(infoWindow), "delete_event", GTK_SIGNAL_FUNC(hideWindow), GTK_OBJECT(infoWindow)); gtk_signal_connect_object(GTK_OBJECT(infoWindow), "destroy", GTK_SIGNAL_FUNC(hideWindow), GTK_OBJECT(infoWindow)); gtk_widget_show(button); } if(((InfoMessage *) call->args)->doPopup==YES) gtk_widget_show(infoWindow); /* append the text */ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW (infoText)); gtk_text_buffer_get_iter_at_offset(buffer, &iter, -1); gtk_text_buffer_insert(buffer, &iter, ((InfoMessage *) call->args)->note, -1); gtkSaveCallDone(call->sem); return FALSE; } /** * Appends a message to the info window * * @param doPopup do we open the window, YES or NO * */ void infoMessage(int doPopup, const char * format, ...) { va_list args; InfoMessage info; va_start(args, format); info.note = g_strdup_vprintf(format, args); va_end(args); info.doPopup = doPopup; gtkSaveCall((GtkFunction) doInfoMessage, &info); g_free(info.note); } static gint saveAddLogEntry(SaveCall * call) { static GtkWidget * s = NULL; static int once = 1; static guint id; if (once) { once = 0; s = glade_xml_get_widget(mainXML, "statusbar"); id = gtk_statusbar_get_context_id(GTK_STATUSBAR(s), "LOG"); } else gtk_statusbar_pop(GTK_STATUSBAR(s), id); gtk_statusbar_push(GTK_STATUSBAR(s), id, (const char*) call->args); gtkSaveCallDone(call->sem); return FALSE; } /** * Appends a log entry to the info window * * @param txt the log entry * */ void addLogEntry(const char *txt) { infoMessage(NO, txt); gtkSaveCall((GtkFunction)saveAddLogEntry, (void*) txt); } GtkNotebook * notebook = NULL; gint doAddToNotebook(SaveCall *call) { GtkWidget * label = gtk_label_new(((AddNotebook *) call->args)->labelName); gtk_notebook_append_page(notebook, ((AddNotebook *) call->args)->frame, label); gtk_widget_show(((AddNotebook *) call->args)->frame); gtkSaveCallDone(call->sem); return FALSE; } void addToNotebook(const char * labelName, GtkWidget * frame) { AddNotebook note; note.labelName = labelName; note.frame = frame; /* add a new notebook for the search results */ gtkSaveCall((GtkFunction) doAddToNotebook, ¬e); } /** * A function for numeric comparisons of strings */ gint numericComp(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2) { double value1; double value2; GtkCListRow * row1 = (GtkCListRow *) ptr1; GtkCListRow * row2 = (GtkCListRow *) ptr2; value1 = atof(GTK_CELL_TEXT(row1->cell[clist->sort_column])->text); value2 = atof(GTK_CELL_TEXT(row2->cell[clist->sort_column])->text); if(value1>value2) return(-1); else if(value1==value2) return(0); else return(1); } /** * A function for case-insensitive text comparisons */ gint alphaComp(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2) { char * text1; char * text2; GtkCListRow * row1 = (GtkCListRow *) ptr1; GtkCListRow * row2 = (GtkCListRow *) ptr2; text1 = GTK_CELL_TEXT(row1->cell[clist->sort_column])->text; text2 = GTK_CELL_TEXT(row2->cell[clist->sort_column])->text; return (strcasecmp(text1,text2)); } /** * A function for percentage comparisons */ gint percentComp(GtkCList *clist, gconstpointer ptr1, gconstpointer ptr2) { char * tmp1; char * tmp2; double value1; double value2; GtkCListRow * row1 = (GtkCListRow *) ptr1; GtkCListRow * row2 = (GtkCListRow *) ptr2; tmp1 = GTK_CELL_TEXT(row1->cell[clist->sort_column])->text; tmp2 = GTK_CELL_TEXT(row2->cell[clist->sort_column])->text; /* Hack for DONE strings :) */ if(strstr(tmp1,"%") == 0) { if(strstr(tmp2,"%") == 0) return 0; /* Both "DONE" */ else return -1; /* A done, B not */ } if(strstr(tmp2,"%")==0) return 1; /* B done, A not */ /* Both have %, must remove */ tmp1 = STRDUP(GTK_CELL_TEXT(row1->cell[clist->sort_column])->text); tmp2 = STRDUP(GTK_CELL_TEXT(row2->cell[clist->sort_column])->text); tmp1[strlen(tmp1)-1]=0; tmp2[strlen(tmp2)-1]=0; value1 = atof(tmp1); value2 = atof(tmp2); FREE(tmp1); FREE(tmp2); if(value1>value2) return(-1); else if(value1==value2) return(0); else return(1); } /* end of helper.c */