Re: Xfce timer plugin
Kemal Eroglu <[email protected]>
| Newsgroups | gmane.comp.desktop.xfce.goodies.devel |
|---|---|
| Message-ID | <[email protected]> |
Hi again, Here's the second version with slight modifications. I'm just sending the two modified files. I added the option for choosing progress bar orientation. I'm still desperately looking for a way to shrink the size of the main container widget. I also added two separators on each side to fill the unused space. Ilgar On Wed, 7 Sep 2005, Kemal Eroglu wrote: > > Hi all, > > Here's my version 0.1 of a timer plugin for XFCE4. It's similar to KTeaTime > for those of you who are faimilar with KDE. You can define a number of > alarms. You can either specify a countdown period or a time of the day for > the alarm. You select the one you want from a popup menu and start the > coundown. When the countdown ends, a Gtk warning window is displayed, or, a > command specified by the user is run. > > Clearly, this first version is waiting for testers and suggestions. Some of > the ideas in my mind are in the TODO file. The only serious issue right now > is the progressbar: This is the main widget in the plugin display; it "fills" > as the countdown approaches to its end. I'm trying to get a thin progressbar > but the panel insists on producing a big, fat one. One other issue is that > you get some warnings when you remove the plugin right after you add it to > the panel. It doesn't crash though. It appears to be related to the freeing > of a gtk_list_store object (pd->list in the code). > > Finally: I'm supposing that this is a plugin which would be added to the > panel only when needed; but one also would like to keep the settings. The XML > system provided in the plugin read/write_config functions loses the settings > when the plugin is removed from the panel. For this reason I'm currently > writing the settings to the file > > ~/.config/xfce4/panel/timer_settings > > It's a file with the format of a GKeyFile. This may not be a very good > solution; and I'm waiting for hints on how to properly achieve the purpose. > > Test and enjoy! > > Ilgar
xfcetimer.h
(text/plain, 2.3 KB)
/* * * Copyright (C) 2005 Kemal Ilgar Eroglu <[email protected]> * * This program 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 of the License, or * (at your option) any later version. * * This program 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 Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define TIMEOUT_TIME 2000 /* Countdown update period in milliseconds */ typedef struct { GtkWidget *eventbox, /* Main container widget in ctrl->base */ *box, /* v/hbox that holds pbar */ *pbar, /* Progress bar */ *tree, /* Treeview */ *buttonadd,*buttonedit,*buttonremove, /* options window buttons */ *menu, *base; /* ctrl->base */ GtkListStore *list; /* The alarms list */ gint count, /* Nubmer of alarms */ selected,timeout_period_in_sec; /* Active countdown period */ guint timeout; /* The timeout ID */ gboolean timer_on; /* TRUE if countdown is in progress */ GtkTooltips *tip; /* Tooltip for panel */ gboolean is_horizontal; /* Progressbar alignment */ GArray *menuarray; /* Array of popup menuitems (alarms). These are used to find the index of the selected menuitem. */ gchar *timeout_command; /* Command when countdown ends */ GTimer *timer; /* Keeps time elapsed */ } plugin_data; typedef struct { GtkSpinButton *timeh,*times,*timem, /* Spinbuttons for h-m-s format */ *time_h,*time_m; /* Spinbuttons for 24h format */ GtkEntry *name,*command; /* Name, and command entries */ GtkRadioButton *rb1; /* Radio button for the h-m-s format */ GtkWindow *window; /* Add/Edit window */ plugin_data *pd; /* Plugin data */ } alarm_data;
xfcetimer.c
(text/plain, 37.6 KB)
/* * * Copyright (C) 2005 Kemal Ilgar Eroglu <[email protected]> * * This program 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 of the License, or * (at your option) any later version. * * This program 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 Library General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #define TIMEOUT_TIME 2000 /* Countdown update period in milliseconds */ #define PBAR_THICKNESS 16 #ifdef HAVE_CONFIG_H #include <config.h> #endif #include <stdio.h> #include <stdlib.h> #include <time.h> #include <string.h> #include <gtk/gtk.h> #include <libxfce4util/libxfce4util.h> #include <libxfcegui4/xfce_iconbutton.h> #include <panel/xfce.h> #include <panel/plugins.h> #include "xfcetimer.h" void make_menu(plugin_data *pd); /** * This is the timeout function that updates the * tooltip, pbar and keeps track of elapsed time **/ static gboolean timeout_function (gpointer data){ plugin_data *pd=(plugin_data *)data; gint elapsed_sec,remaining; gchar tiptext[32]; GtkWidget *dialog; gulong zip; elapsed_sec=(gint)g_timer_elapsed(pd->timer,&zip); /*g_fprintf(stderr,"\nElapsed %d seconds of %d",elapsed_sec,pd- >timeout_period_in_sec);*/ /* If countdown is not over, update tooltip */ if(elapsed_sec < pd->timeout_period_in_sec){ remaining=pd->timeout_period_in_sec-elapsed_sec; if(remaining>=3600) g_snprintf(tiptext,31,"%dh %dm %ds left",remaining/3600, (remaining%3600)/60, remaining%60); else if (remaining>=60) g_snprintf(tiptext,31,"%dm %ds left",remaining/60, remaining%60); else g_snprintf(tiptext,31,"%ds left",remaining); gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR(pd->pbar), ((gdouble)elapsed_sec)/pd-> timeout_period_in_sec); gtk_tooltips_set_tip(pd->tip,pd->base,tiptext,NULL); return TRUE; } /* Countdown is over, stop timer and free resources */ /*g_fprintf(stderr,"\nTimer command is ==> %s...",pd->timeout_command);*/ if(strlen(pd->timeout_command)>0) g_spawn_command_line_async (pd->timeout_command,NULL); else{ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pd->pbar),1); dialog = gtk_message_dialog_new (NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_WARNING, GTK_BUTTONS_CLOSE, "Beeep! :) \nTime is up!"); gtk_dialog_run (GTK_DIALOG (dialog)); gtk_widget_destroy (dialog); } if(pd->timer) g_timer_destroy(pd->timer); pd->timer=NULL; gtk_tooltips_disable(pd->tip); if(pd->timeout_command) g_free(pd->timeout_command); pd->timeout_command=NULL; pd->timeout=0; pd->timer_on=FALSE; /* reset pbar */ gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pd->pbar),0); make_menu(pd); /* This function won't be called again */ return FALSE; } /** * This is the callback function called when a timer * is selected in the popup menu **/ static void timer_selected (GtkWidget* menuitem, GdkEventButton* event, gpointer data){ plugin_data *pd=(plugin_data *)data; gint row_count; row_count=0; /* Find the index of the menuitem selected, save it in pd->selected. Not very elegant, though */ while (GTK_MENU_ITEM(menuitem)!=g_array_index(pd->menuarray,GtkMenuItem*,row_count) ) row_count++; pd->selected=row_count; /*g_fprintf(stderr,"\n Selecten menuitem is %d",row_count);*/ } /** * This is the callback function called when the * start/stop item is selected in the popup menu **/ static void start_stop_selected (GtkWidget* menuitem, GdkEventButton* event, gpointer data){ plugin_data *pd=(plugin_data *)data; GtkTreeIter iter; gboolean valid; GSList *group=NULL; gchar *timerinfo,*tout_command; gchar temp[8]; gint row_count,cur_h,cur_m,cur_s,time; gint timeout_period; gboolean is_cd; GTimeVal timeval; struct tm *current; /* If counting down, we stop the timer and free the resources */ if(pd->timer_on){ /*g_fprintf(stderr,"\nTimer is running, shutting down...");*/ if(pd->timer) g_timer_destroy(pd->timer); if(pd->timeout) g_source_remove(pd->timeout); if(pd->timeout_command) g_free(pd->timeout_command); pd->timer=NULL; pd->timeout_command=NULL; pd->timeout=0; pd->timer_on=FALSE; /* Disable tooltips, reset pbar */ gtk_tooltips_disable(pd->tip); gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pd->pbar),0); /* update menu*/ make_menu(pd); return; } /* If we're here then the timer is off, so we start it */ /*g_fprintf(stderr,"\nStarting timer...");*/ valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(pd->list), &iter); row_count=0; /* Empty timer list-> Nothing to do. pd->selected=0, though. */ if(!valid) return; /* Search the list item with the right index */ while (valid && row_count!=pd->selected){ valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(pd->list), &iter); row_count++; } gtk_tree_model_get (GTK_TREE_MODEL(pd->list), &iter, 2, &timerinfo, 3, &tout_command, 4, &is_cd ,5, &time, -1); /* This will not be freed until the timeout is destroyed */ pd->timeout_command=tout_command; /* If it's a 24h type alarm, we find the difference with current time Here 'time' is in minutes */ if(!is_cd) { g_get_current_time(&timeval); current = localtime((time_t *)&timeval.tv_sec); strftime(temp,7,"%H",current); cur_h=atoi(temp); /*g_fprintf(stderr,"\n Current time: %d : ",cur_h);*/ strftime(temp,7,"%M",current); cur_m=atoi(temp); /*g_fprintf(stderr,"%d : ",cur_m);*/ strftime(temp,7,"%S",current); cur_s=atoi(temp); /*g_fprintf(stderr,"%d \n",cur_s);*/ timeout_period=time*60 - ((60*cur_h + cur_m)*60 + cur_s); if(timeout_period <0) timeout_period+= 24*60*60; } /* Else 'time' already gives the countdown period in seconds */ else timeout_period=time; pd->timeout_period_in_sec=timeout_period; /* start the timer */ pd->timer=g_timer_new(); pd->timer_on=TRUE; /* update stuff */ make_menu(pd); gtk_tooltips_set_tip(pd->tip, GTK_WIDGET(pd->base), timerinfo, NULL); gtk_tooltips_enable(pd->tip); g_free(timerinfo); g_timer_start(pd->timer); pd->timeout = g_timeout_add(TIMEOUT_TIME, timeout_function,pd); } /** * Callback when clicking on pbar. Pops the menu up/down **/ static void pbar_clicked (GtkWidget *pbar, GdkEventButton *event, gpointer data){ plugin_data *pd=(plugin_data *)data; /*g_fprintf(stderr,"\nReceived click on button %d",event->button);*/ if(!pd->menu){ g_fprintf(stderr,"\nNo menu\n"); return; } if(event->button==1) gtk_menu_popup (GTK_MENU(pd->menu),NULL,NULL,NULL,NULL,event->button,event->time); else gtk_menu_popdown(GTK_MENU(pd->menu)); } /** * This function generates the popup menu **/ void make_menu(plugin_data *pd){ GtkTreeIter iter; gboolean valid; GSList *group=NULL; GtkWidget *menuitem; gchar *timername,*timerinfo; gchar itemtext[256]; gint row_count; /* Destroy the existing one */ if(pd->menu) gtk_widget_destroy(pd->menu); if(pd->menuarray) g_array_free(pd->menuarray,TRUE); pd->menu=gtk_menu_new(); pd->menuarray=g_array_new(FALSE,TRUE,sizeof(menuitem)); valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(pd->list), &iter); row_count=0; while (valid){ /* Run through the list, read name and timer period info */ /*g_fprintf(stderr,"\nMaking menuitem %d while selected is %d",row_count,pd-> selected);*/ gtk_tree_model_get(GTK_TREE_MODEL(pd->list),&iter,1,&timername,2,&timerinfo,-1); g_snprintf(itemtext,255,"%s (%s)",timername,timerinfo); menuitem=gtk_radio_menu_item_new_with_label(group,itemtext); gtk_widget_show(menuitem); g_free(timername); g_free(timerinfo); group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (menuitem)); g_signal_connect (G_OBJECT(menuitem),"button_press_event", G_CALLBACK(timer_selected),pd); /* The selected timer is always active */ if(row_count==pd->selected) gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menuitem),TRUE); else if(pd->timer_on) /* others are disabled when timer is already running */ gtk_widget_set_sensitive(GTK_WIDGET(menuitem),FALSE); gtk_menu_shell_append(GTK_MENU_SHELL(pd->menu),menuitem); /*g_fprintf(stderr,"\nAdding menuitem with label : %s",itemtext);*/ /* We add the address of menuitem to the array */ g_array_append_val(pd->menuarray,menuitem); valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(pd->list), &iter); row_count++; } /* Horizontal line (empty item) */ menuitem=gtk_menu_item_new(); gtk_menu_shell_append(GTK_MENU_SHELL(pd->menu),menuitem); gtk_widget_show(menuitem); /* Start/stop menu item */ if(pd->timer_on) menuitem=gtk_menu_item_new_with_label("Stop timer"); else menuitem=gtk_menu_item_new_with_label("Start timer"); gtk_menu_shell_append (GTK_MENU_SHELL(pd->menu),menuitem); g_signal_connect (G_OBJECT(menuitem),"button_press_event", G_CALLBACK(start_stop_selected),pd); gtk_widget_show(menuitem); gtk_widget_show(pd->menu); } /** * Callback to the OK button in the Add window **/ static void ok_add(GtkButton *button, gpointer data){ alarm_data *adata = (alarm_data *)data; GtkTreeIter iter; gint t1,t2,t3,t; gchar timeinfo[16]; /* Add item to the list */ gtk_list_store_append(adata->pd->list,&iter); gtk_list_store_set(GTK_LIST_STORE(adata->pd->list),&iter, 0,adata->pd->count, 1,gtk_entry_get_text(GTK_ENTRY(adata->name)), 3,gtk_entry_get_text(GTK_ENTRY(adata->command)), 4,gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(adata-> rb1)),-1); /* Item count goes up by one */ adata->pd->count=adata->pd->count+1; /* If the h-m-s format was chosen, convert time to seconds, save the choice into the list */ if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(adata->rb1))){ t1=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(adata->timeh)); t2=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(adata->timem)); t3=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(adata->times)); t=t1*3600+t2*60+t3; gtk_list_store_set(GTK_LIST_STORE(adata->pd->list),&iter,5,t,-1); if(t1>0) g_snprintf(timeinfo,15,"%dh %dm %ds",t1,t2,t3); else if(t2>0) g_snprintf(timeinfo,15,"%dm %ds",t2,t3); else g_snprintf(timeinfo,15,"%ds",t3); gtk_list_store_set(GTK_LIST_STORE(adata->pd->list),&iter,2,timeinfo,-1); } else{ /* The 24h format. Save time in minutes */ t1=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(adata->time_h)); t2=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(adata->time_m)); t=t1*60+t2; gtk_list_store_set(GTK_LIST_STORE(adata->pd->list),&iter,5,t,-1); g_snprintf(timeinfo,9,"At %02d:%02d",t1,t2); gtk_list_store_set(GTK_LIST_STORE(adata->pd->list),&iter,2,timeinfo,-1); } /* Update menu */ make_menu(adata->pd); /* Free resources */ gtk_widget_destroy(GTK_WIDGET(adata->window)); g_free(adata); } /** * Callback for cancelling Add and Edit. Just closes the window :). **/ static void cancel_add_edit(GtkButton *button, gpointer data){ alarm_data *adata=(alarm_data *)data; gtk_widget_destroy(GTK_WIDGET(adata->window)); g_free(adata); } /** * Callback for OK button on Edit window. See ok_add for comments. **/ static void ok_edit(GtkButton *button, gpointer data){ alarm_data *adata = (alarm_data *)data; GtkTreeIter iter; gint t1,t2,t3,t; gchar timeinfo[10]; GtkTreeSelection *select; GtkTreeModel *model; select = gtk_tree_view_get_selection (GTK_TREE_VIEW (adata->pd->tree)); if (gtk_tree_selection_get_selected (select, &model, &iter)) { gtk_list_store_set(GTK_LIST_STORE(adata->pd->list),&iter, 1,gtk_entry_get_text(GTK_ENTRY(adata->name)), 3,gtk_entry_get_text(GTK_ENTRY(adata->command)), 4,gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(adata-> rb1)),-1); if(gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(adata->rb1))){ t1=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(adata->timeh)); t2=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(adata->timem)); t3=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(adata->times)); t=t1*3600+t2*60+t3; gtk_list_store_set(GTK_LIST_STORE(adata->pd->list),&iter,5,t,-1); if(t1>0) g_snprintf(timeinfo,15,"%dh %dm %ds",t1,t2,t3); else if(t2>0) g_snprintf(timeinfo,15,"%dm %ds",t2,t3); else g_snprintf(timeinfo,15,"%ds",t3); gtk_list_store_set(GTK_LIST_STORE(adata->pd->list),&iter,2,timeinfo,-1); } else{ t1=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(adata->time_h)); t2=gtk_spin_button_get_value_as_int(GTK_SPIN_BUTTON(adata->time_m)); t=t1*60+t2; gtk_list_store_set(GTK_LIST_STORE(adata->pd->list),&iter,5,t,-1); g_snprintf(timeinfo,9,"At %02d:%02d",t1,t2); gtk_list_store_set(GTK_LIST_STORE(adata->pd->list),&iter,2,timeinfo,-1); } } make_menu(adata->pd); gtk_widget_destroy(GTK_WIDGET(adata->window)); g_free(adata); } /** * Callback to the Add button in options window. * Creates the Add window. **/ static void add_edit_clicked (GtkButton *buttonn, gpointer data){ plugin_data *pd = (plugin_data *)data; GtkWindow *window; GtkLabel *label; GtkEntry *name,*command; GtkSpinButton *timeh,*timem,*times,*time_h,*time_m; GtkRadioButton *rb1,*rb2; GtkWidget *hbox,*vbox,*button; alarm_data *adata=g_new(alarm_data,1); gchar *nc; gboolean is_cd; gint time; GtkTreeIter iter; GtkTreeSelection *select; GtkTreeModel *model; window = (GtkWindow *)gtk_window_new(GTK_WINDOW_TOPLEVEL); adata->window=window; adata->pd=pd; gtk_window_set_modal(GTK_WINDOW(window),TRUE); vbox=gtk_vbox_new(FALSE,0); gtk_container_add(GTK_CONTAINER(window),vbox); /***********/ hbox=gtk_hbox_new(TRUE,0); gtk_box_pack_start(GTK_BOX(vbox),hbox,FALSE,FALSE,0); label = (GtkLabel *)gtk_label_new ("Name"); name = (GtkEntry *) gtk_entry_new_with_max_length(1023); adata->name=name; gtk_box_pack_start(GTK_BOX(hbox),GTK_WIDGET(label),TRUE,TRUE,0); gtk_box_pack_start(GTK_BOX(hbox),GTK_WIDGET(name),TRUE,TRUE,0); /**********/ rb1=(GtkRadioButton *)gtk_radio_button_new_with_label(NULL,"Enter the countdown time"); rb2=(GtkRadioButton *)gtk_radio_button_new_with_label(gtk_radio_button_get_group (rb1),"Enter the time of alarm (24h format)"); adata->rb1=rb1; gtk_box_pack_start(GTK_BOX(vbox),GTK_WIDGET(rb1),TRUE,TRUE,0); hbox=gtk_hbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(vbox),GTK_WIDGET(hbox),TRUE,TRUE,0); timeh = (GtkSpinButton *)gtk_spin_button_new_with_range(0,23,1); gtk_box_pack_start(GTK_BOX(hbox),GTK_WIDGET(timeh),FALSE,FALSE,0); adata->timeh=timeh; label = (GtkLabel *)gtk_label_new ("h"); gtk_box_pack_start(GTK_BOX(hbox),GTK_WIDGET(label),FALSE,FALSE,2); timem = (GtkSpinButton *)gtk_spin_button_new_with_range(0,59,1); gtk_box_pack_start(GTK_BOX(hbox),GTK_WIDGET(timem),FALSE,FALSE,5); adata->timem=timem; label = (GtkLabel *)gtk_label_new ("m"); gtk_box_pack_start(GTK_BOX(hbox),GTK_WIDGET(label),FALSE,FALSE,2); times = (GtkSpinButton *)gtk_spin_button_new_with_range(0,59,1); gtk_box_pack_start(GTK_BOX(hbox),GTK_WIDGET(times),FALSE,FALSE,5); adata->times=times; label = (GtkLabel *)gtk_label_new ("s"); gtk_box_pack_start(GTK_BOX(hbox),GTK_WIDGET(label),FALSE,FALSE,2); label = (GtkLabel *)gtk_label_new ("\nor\n"); gtk_box_pack_start(GTK_BOX(vbox),GTK_WIDGET(label),TRUE,TRUE,0); gtk_box_pack_start(GTK_BOX(vbox),GTK_WIDGET(rb2),TRUE,TRUE,0); hbox=gtk_hbox_new(FALSE,0); gtk_box_pack_start(GTK_BOX(vbox),GTK_WIDGET(hbox),TRUE,TRUE,0); time_h = (GtkSpinButton *)gtk_spin_button_new_with_range(0,23,1); gtk_box_pack_start(GTK_BOX(hbox),GTK_WIDGET(time_h),FALSE,FALSE,0); adata->time_h=time_h; label = (GtkLabel *)gtk_label_new (":"); gtk_box_pack_start(GTK_BOX(hbox),GTK_WIDGET(label),FALSE,FALSE,5); time_m = (GtkSpinButton *)gtk_spin_button_new_with_range(0,59,1); gtk_box_pack_start(GTK_BOX(hbox),GTK_WIDGET(time_m),FALSE,FALSE,5); adata->time_m=time_m; /****************/ label = (GtkLabel *)gtk_label_new ("\nThe command to run"); gtk_box_pack_start(GTK_BOX(vbox),GTK_WIDGET(label),TRUE,TRUE,0); command = (GtkEntry *)gtk_entry_new_with_max_length(1023); adata->command=command; gtk_box_pack_start(GTK_BOX(vbox),GTK_WIDGET(command),TRUE,TRUE,0); /****************/ hbox=gtk_hbox_new(TRUE,0); gtk_box_pack_start(GTK_BOX(vbox),hbox,TRUE,TRUE,0); button=gtk_button_new_from_stock(GTK_STOCK_CANCEL); gtk_box_pack_start(GTK_BOX(hbox),button,TRUE,TRUE,0); g_signal_connect(G_OBJECT(button),"clicked",G_CALLBACK(cancel_add_edit),adata); button=gtk_button_new_from_stock(GTK_STOCK_OK); gtk_box_pack_start(GTK_BOX(hbox),button,TRUE,TRUE,0); if(GTK_WIDGET(buttonn)==pd->buttonadd) g_signal_connect(G_OBJECT(button),"clicked",G_CALLBACK(ok_add),adata); else g_signal_connect(G_OBJECT(button),"clicked",G_CALLBACK(ok_edit),adata); /* If this is the add window, we're done */ if(GTK_WIDGET(buttonn)==pd->buttonadd) { gtk_window_set_title(window,"Add new alarm"); gtk_widget_show_all(GTK_WIDGET(window)); return; } /* Else fill the values in the boxes with the current choices */ select = gtk_tree_view_get_selection (GTK_TREE_VIEW (pd->tree)); /*gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE);*/ if (gtk_tree_selection_get_selected (select, &model, &iter)){ gtk_tree_model_get(model,&iter,1,&nc,-1); gtk_entry_set_text(GTK_ENTRY(name),nc); g_free(nc); gtk_tree_model_get(model,&iter,3,&nc,-1); gtk_entry_set_text(GTK_ENTRY(command),nc); g_free(nc); gtk_tree_model_get(model,&iter,4,&is_cd,-1); gtk_tree_model_get(model,&iter,5,&time,-1); if(is_cd){ gtk_spin_button_set_value(GTK_SPIN_BUTTON(timeh),time/3600); gtk_spin_button_set_value(GTK_SPIN_BUTTON(timem),(time%3600)/60); gtk_spin_button_set_value(GTK_SPIN_BUTTON(times),time%60); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rb1),TRUE); } else{ gtk_spin_button_set_value(GTK_SPIN_BUTTON(time_h),time/60); gtk_spin_button_set_value(GTK_SPIN_BUTTON(time_m),time%60); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(rb2),TRUE); } } gtk_window_set_title(window,"Edit alarm"); gtk_widget_show_all(GTK_WIDGET(window)); } /** * Calllback for the remove button in the options **/ static void remove_clicked(GtkButton *button, gpointer data){ plugin_data *pd = (plugin_data *)data; GtkTreeIter iter,iter_remove; GtkTreeSelection *select; GtkTreePath *path; GtkTreeModel *model; gboolean valid; gint row_count; /* Get the selected row */ select=gtk_tree_view_get_selection(GTK_TREE_VIEW(pd->tree)); valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(pd->list), &iter); row_count=0; while (valid){ /* Re-index the other rows */ /* The selected one is removed, the corresponding menuitem array is also updated */ if(gtk_tree_selection_iter_is_selected(select,&iter)){ /*g_fprintf(stderr,"\n Removing node %d ...\n", row_count);*/ iter_remove=iter; /* Mark to be deleted */ g_array_remove_index(pd->menuarray,row_count); if(pd->selected==row_count) /* Update the index of the selected item */ pd->selected=0; /* If the selected is deleted, new selected one is the first one. The first radiomenuitem gets activated anyway */ else if(pd->selected > row_count) pd->selected=pd->selected-1; /* Those coming after are shifted one behind */ } else{ /* Save new index on the remaning ones */ gtk_list_store_set (pd->list, &iter, 0,row_count, -1); row_count++; } valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(pd->list), &iter); } /* Remove the marked one */ gtk_list_store_remove (pd->list, &iter_remove); /* Update item count and menu */ pd->count=row_count; make_menu(pd); } /** * Adds the progressbar, taking into account the orientation. * pd->pbar is not destroyed, just reparented (saves fraction setting code etc.). **/ static void add_pbar(plugin_data *pd){ gtk_widget_hide(pd->eventbox); /* Always true except at initialization */ if(pd->box){ g_object_ref(G_OBJECT(pd->pbar)); gtk_container_remove(GTK_CONTAINER(pd->box),pd->pbar); gtk_widget_destroy(pd->box); } /* vertical bar -- the default */ if(!pd->is_horizontal){ pd->box=gtk_hbox_new(TRUE,0); gtk_container_add(GTK_CONTAINER(pd->eventbox),pd->box); gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR(pd-> pbar),GTK_PROGRESS_BOTTOM_TO_TOP); gtk_widget_set_size_request(GTK_WIDGET(pd->pbar),PBAR_THICKNESS,0); gtk_box_pack_start(GTK_BOX(pd->box),gtk_vseparator_new(),FALSE,FALSE,0); gtk_box_pack_start(GTK_BOX(pd->box),pd->pbar,FALSE,FALSE,0); gtk_box_pack_start(GTK_BOX(pd->box),gtk_vseparator_new(),FALSE,FALSE,0); } else{ /* horizontal bar */ pd->box=gtk_vbox_new(TRUE,0); gtk_container_add(GTK_CONTAINER(pd->eventbox),pd->box); gtk_progress_bar_set_orientation (GTK_PROGRESS_BAR(pd-> pbar),GTK_PROGRESS_LEFT_TO_RIGHT); gtk_widget_set_size_request(GTK_WIDGET(pd->pbar),0,PBAR_THICKNESS); gtk_box_pack_start(GTK_BOX(pd->box),gtk_hseparator_new(),FALSE,FALSE,0); gtk_box_pack_start(GTK_BOX(pd->box),pd->pbar,FALSE,FALSE,0); gtk_box_pack_start(GTK_BOX(pd->box),gtk_hseparator_new(),FALSE,FALSE,0); } gtk_widget_show_all(pd->eventbox); } /** * Loads the list from a keyfile, then saves them in a list_store **/ static void load_settings(plugin_data *pd) { gchar groupname[8]; gchar *timerstring; gint groupnum,time; gboolean is_cd; GtkTreeIter iter; GError *error=NULL; gchar settings[1024]; GKeyFile *keyfile=g_key_file_new(); /*g_fprintf(stderr,"\n Running read\n");*/ g_snprintf(settings,1023,"%s/.config/xfce4/panel/timer_settings",g_get_home_dir()); /*g_fprintf(stderr,"\n ** %s \n",settings);*/ /*if(g_file_test(settings,G_FILE_TEST_EXISTS))*/ /* somehow this returns false */ if(!g_key_file_load_from_file(keyfile,settings,G_KEY_FILE_NONE,&error)) g_fprintf(stderr,"\n ==> %s\n",error->message); /*else{ g_fprintf(stderr,"\nNokeyfile\n"); return; }*/ groupnum=0; g_sprintf(groupname,"G0"); /*if(!g_key_file_has_group(keyfile,groupname)) g_fprintf(stderr,"\nNo zero group");*/ while(g_key_file_has_group(keyfile,groupname)){ /*g_fprintf(stderr,"\nLoading item %d\n",groupnum);*/ gtk_list_store_append(pd->list,&iter); timerstring=g_key_file_get_value(keyfile,groupname,"timername",NULL); gtk_list_store_set(pd->list,&iter,0,groupnum,1,timerstring,-1); g_free(timerstring); timerstring=g_key_file_get_value(keyfile,groupname,"timercommand",NULL); gtk_list_store_set(pd->list,&iter,3,timerstring,-1); /*g_fprintf(stderr,"\nLoaded timer command ==> %s... with length %d", timerstring,strlen(timerstring));*/ g_free(timerstring); timerstring=g_key_file_get_value(keyfile,groupname,"timerinfo",NULL); gtk_list_store_set(pd->list,&iter,2,timerstring,-1); g_free(timerstring); is_cd=g_key_file_get_boolean(keyfile,groupname,"is_countdown",NULL); time=g_key_file_get_integer(keyfile,groupname,"time",NULL); /* No longer needed, time info is also saved and read now if(is_cd){ if(time<60) g_snprintf(timeinfo,15,"%02ds",time); else if (time < 3600) g_snprintf(timeinfo,15,"%02dm %02ds",time/60,time%60); else g_snprintf(timeinfo,15,"%02dh %d02m %02ds",time/3660,(time%3600)/60,time%60); } else g_snprintf(timeinfo,15,"At %02d:%02d",time/60,time%60); */ gtk_list_store_set(pd->list,&iter,4,is_cd,5,time,-1); groupnum++; g_snprintf(groupname,5,"G%d",groupnum); } pd->count=groupnum; /* Read other options */ if(g_key_file_has_group(keyfile,"others")) pd->is_horizontal= g_key_file_get_boolean(keyfile,"others","is_horizontal",NULL); add_pbar(pd); g_free(keyfile); } /** * Saves the list to a keyfile **/ static void save_settings(plugin_data *pd){ gchar *timername,*timercommand,*timerinfo; gint time; gboolean is_cd; gchar settings[1024]; gchar settingsbak[1024]; gchar line[1024]; GtkTreeIter iter; gboolean valid; gint row_count; gsize size; GIOChannel *io; /*g_fprintf(stderr,"\n Running write\n");*/ g_snprintf(settings,1023,"%s/.config/xfce4/panel/timer_settings",g_get_home_dir()); io = g_io_channel_new_file(settings,"w",NULL); valid = gtk_tree_model_get_iter_first (GTK_TREE_MODEL(pd->list), &iter); row_count=0; while (valid){ gtk_tree_model_get (GTK_TREE_MODEL(pd->list), &iter, 1, &timername, 2, &timerinfo, 3, &timercommand, 4, &is_cd, 5, &time, -1); g_snprintf(line,1023,"[G%d]\n",row_count); g_io_channel_write_chars(io,line,-1,&size,NULL); g_snprintf(line,1023,"timername=%s\n",timername); g_io_channel_write_chars(io,line,-1,&size,NULL); g_snprintf(line,1023,"time=%d\n",time); g_io_channel_write_chars(io,line,-1,&size,NULL); g_snprintf(line,1023,"timercommand=%s\n",timercommand); g_io_channel_write_chars(io,line,-1,&size,NULL); g_snprintf(line,1023,"timerinfo=%s\n",timerinfo); g_io_channel_write_chars(io,line,-1,&size,NULL); if(is_cd) g_io_channel_write_chars(io,"is_countdown=true\n",-1,&size,NULL); else g_io_channel_write_chars(io,"is_countdown=false\n",-1,&size,NULL); g_free(timername); g_free(timercommand); g_free(timerinfo); row_count ++; valid = gtk_tree_model_iter_next (GTK_TREE_MODEL(pd->list), &iter); } /* save the other options */ g_io_channel_write_chars(io,"\n[others]\n",-1,&size,NULL); if(pd->is_horizontal) g_io_channel_write_chars(io,"is_horizontal=true\n",-1,&size,NULL); else g_io_channel_write_chars(io,"is_horizontal=false\n",-1,&size,NULL); g_io_channel_flush(io,NULL); g_io_channel_close(io); g_free(io); } /** * Activates the Edit and Remove buttons when an item in the list is selected **/ static void tree_selected (GtkTreeSelection *select, gpointer data){ plugin_data *pd=(plugin_data *)data; gtk_widget_set_sensitive(pd->buttonedit,TRUE); gtk_widget_set_sensitive(pd->buttonremove,TRUE); } /** * Callback for the horizontal progressbar checkbox **/ static void toggle_horiz (GtkToggleButton *button, gpointer data){ plugin_data *pd=(plugin_data *)data; pd->is_horizontal = gtk_toggle_button_get_active(button); add_pbar(pd); } /** * create_sample_control * * Create a new instance of the plugin. * * @control : #Control parent container * * Returns %TRUE on success, %FALSE on failure. **/ static gboolean create_plugin_control (Control * ctrl) { GtkWidget *base,*menu,*socket,*menuitem,*box,*pbar2; GtkTooltips *tooltip; xmlNodePtr write=NULL,read=NULL; char command[1024]; plugin_data *pd=g_new(plugin_data,1); pd->base=GTK_WIDGET(ctrl->base); pd->count=0; pd->pbar=gtk_progress_bar_new(); pd->list=gtk_list_store_new(6, G_TYPE_INT, /* Column 0: Index */ G_TYPE_STRING, /* Column 1: Name */ G_TYPE_STRING, /* Column 2: Timer period/alarm time */ G_TYPE_STRING, /* Command to run */ G_TYPE_BOOLEAN, /* TRUE= Is countdown, i.e. h-m-s format. FALSE= 24h format */ G_TYPE_INT); /* Timer period in seconds if countdown. Alarm time in minutes if 24h format is used, (i.e. 60 x Hr + Min) */ pd->eventbox=gtk_event_box_new(); pd->box=NULL; pd->timer_on=FALSE; pd->is_horizontal=FALSE; pd->timeout=0; pd->buttonadd=NULL; pd->buttonedit=NULL; pd->buttonremove=NULL; pd->menu=NULL; pd->menuarray=NULL; pd->selected=0; pd->tip=gtk_tooltips_new(); pd->timeout_command=NULL; pd->timer=NULL; /*gtk_box_set_child_packing(GTK_BOX(pd->base->parent),pd-> base,FALSE,FALSE,0,GTK_PACK_START);*/ gtk_tooltips_set_tip(pd->tip, GTK_WIDGET(ctrl->base), "", NULL); gtk_tooltips_disable(pd->tip); g_object_ref(pd->list); load_settings(pd); make_menu(pd); g_signal_connect (G_OBJECT(pd->eventbox), "button_press_event", G_CALLBACK(pbar_clicked), pd); gtk_progress_bar_set_bar_style (GTK_PROGRESS_BAR(pd->pbar), GTK_PROGRESS_CONTINUOUS); gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(pd->pbar),0); add_pbar(pd); /* Trying to get a thin box, but no way */ gtk_widget_set_size_request(pd->eventbox,0,0); gtk_widget_set_size_request(GTK_WIDGET(ctrl->base),-1,-1); gtk_container_add(GTK_CONTAINER(ctrl->base),pd->eventbox); gtk_widget_show_all(GTK_WIDGET(ctrl->base)); ctrl->data = (gpointer) pd; ctrl->with_popup = FALSE; return(TRUE); } /** * sample_read_config * * @control : the #Control to read configuration for * @node : an #xmlNodePtr (part of the panel config file) containing the * configuration. **/ static void plugin_read_config (Control * control, xmlNodePtr node) { } /** * sample_write_config * * @control : the #Control to write configuration for * @node : an #xmlNodePtr (part of the panel config file) containing the * configuration. **/ static void plugin_write_config (Control * control, xmlNodePtr parent) { } /** * sample_attach_callback * * @control : the #Control to attach callbacks to * @signal : the signal name * @callback : callback function * @data : user data * * The plugin is expected to run g_signal_connect() on all widgets that * receive events, at least one. This is used, for example to connect the * right-click menu. **/ static void sample_attach_callback (Control * control, const char *signal, GCallback callback, gpointer data) { } /** * sample_free * * free the memory allocated for a sample #Control * * @control : the #Control to free memory for. **/ static void plugin_free (Control * ctrl) { plugin_data *pd; g_return_if_fail(ctrl != NULL); g_return_if_fail(ctrl-> data != NULL); pd = (plugin_data*) ctrl->data; /* remove timeout */ if (pd->timeout!=0) g_source_remove(pd->timeout); save_settings(pd); if(pd->timer) g_timer_destroy(pd->timer); if(pd->timeout_command) g_free(pd->timeout_command); gtk_object_destroy(GTK_OBJECT(pd->tip)); if(pd->timeout) g_source_remove(pd->timeout); if(pd->menuarray) g_array_free(pd->menuarray,TRUE); /* destroy all widgets */ gtk_widget_destroy(GTK_WIDGET(pd->eventbox)); /* destroy the tooltips */ /*gtk_object_destroy(GTK_OBJECT(pd->tip));*/ if(G_IS_OBJECT(pd->list)) g_free(pd->list); else g_fprintf(stderr,"\npd->list is non-object"); /* free the plugin data structure */ g_free(pd); } /* options dialog */ static void plugin_create_options (Control *ctrl, GtkContainer *con, GtkWidget *done) { plugin_data *pd=(plugin_data *)ctrl->data; GtkWidget *vbox=gtk_vbox_new(FALSE,0); /*outermost box */ GtkWidget *hbox=gtk_hbox_new(FALSE,0); /* holds the treeview and buttons */ GtkWidget *buttonbox,*button,*sw,*tree; GtkTreeSelection *select; GtkCellRenderer *renderer; GtkTreeViewColumn *column; GtkTreeIter iter; gtk_container_add(GTK_CONTAINER(con),vbox); gtk_box_pack_start(GTK_BOX(vbox),hbox,TRUE,TRUE,0); sw = gtk_scrolled_window_new (NULL, NULL); gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw), GTK_SHADOW_ETCHED_IN); gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw), GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC); gtk_box_pack_start(GTK_BOX(hbox),sw,TRUE,TRUE,0); tree=gtk_tree_view_new_with_model(GTK_TREE_MODEL(pd->list)); pd->tree=tree; gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (tree), TRUE); gtk_tree_selection_set_mode (gtk_tree_view_get_selection (GTK_TREE_VIEW (tree)), GTK_SELECTION_SINGLE); renderer = gtk_cell_renderer_text_new (); column = gtk_tree_view_column_new_with_attributes ("Timer\nname", renderer, "text", 1, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); column = gtk_tree_view_column_new_with_attributes ("Countdown period /\nAlarm time", renderer, "text", 2, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); column = gtk_tree_view_column_new_with_attributes ("Alarm command", renderer, "text", 3, NULL); gtk_tree_view_append_column (GTK_TREE_VIEW (tree), column); if(tree) gtk_container_add(GTK_CONTAINER(sw),tree); else g_fprintf(stderr,"\n pd->tree is NULL\n"); gtk_widget_set_size_request(GTK_WIDGET(sw),350,250); select = gtk_tree_view_get_selection (GTK_TREE_VIEW (pd->tree)); gtk_tree_selection_set_mode (select, GTK_SELECTION_SINGLE); g_signal_connect (G_OBJECT (select), "changed", G_CALLBACK(tree_selected), pd); buttonbox=gtk_vbutton_box_new(); gtk_button_box_set_layout(GTK_BUTTON_BOX(buttonbox),GTK_BUTTONBOX_START); gtk_box_pack_start(GTK_BOX(hbox),buttonbox,FALSE,FALSE,0); button = gtk_button_new_from_stock (GTK_STOCK_ADD); pd->buttonadd=button; gtk_box_pack_start(GTK_BOX (buttonbox), button, FALSE, FALSE,0); gtk_widget_set_sensitive(button,TRUE); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK(add_edit_clicked), pd); button = gtk_button_new_from_stock (GTK_STOCK_EDIT); pd->buttonedit=button; gtk_box_pack_start(GTK_BOX (buttonbox), button, FALSE, FALSE,0); gtk_widget_set_sensitive(button,FALSE); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK(add_edit_clicked), pd); button = gtk_button_new_from_stock (GTK_STOCK_REMOVE); pd->buttonremove=button; gtk_box_pack_start(GTK_BOX (buttonbox), button, FALSE, FALSE,0); gtk_widget_set_sensitive(button,FALSE); g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK(remove_clicked), pd); gtk_box_pack_start(GTK_BOX(vbox),gtk_hseparator_new(),TRUE,TRUE,20); button=gtk_check_button_new_with_label("Horizontal progressbar"); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(button),pd->is_horizontal); g_signal_connect(G_OBJECT(button),"toggled",G_CALLBACK(toggle_horiz),pd); gtk_box_pack_start(GTK_BOX(vbox),button,TRUE,TRUE,0); gtk_widget_show_all(GTK_WIDGET(con)); } /** * xfce_control_class_init * * Ideally, this should be the only exported symbol in the plugin, since this * is the only function that is directly accessed from the panel. * * Here you set up the virtual function table for the plugin and specify its * behaviour (e.g. uniqueness). * * @cc : #ControlClass to initialize **/ G_MODULE_EXPORT void xfce_control_class_init (ControlClass * cc) { /*xfce_textdomain (GETTEXT_PACKAGE, LOCALEDIR, "UTF-8");*/ /* Must be present: - name : unique id - caption : display name - create_control : create a new instance - attach_callback : connect a signal (e.g. right-click menu) - free : free allocated resources of Control instance */ cc->name = "xfce4_timerr"; cc->caption = _("XFCE4 Timer"); cc->create_control = (CreateControlFunc) create_plugin_control; cc->attach_callback = sample_attach_callback; cc->free = plugin_free; cc->create_options = plugin_create_options; /* Optional, leave as NULL to get default behaviour - read_config : read configuration from xml - write_config : write configuration to xml - create_options : create widgets for the options dialog - set_size : set size (SMALL, NORMAL, LARGE or HUGE) - set_orientation : set orientation (HORIZONTAL or VERTICAL) - set_theme : set icon theme - about : show an about dialog */ cc->read_config = plugin_read_config; cc->write_config = plugin_write_config; } /* Macro that checks panel API version */ XFCE_PLUGIN_CHECK_INIT