/*
 * GTimer
 *
 * Copyright:
 *	(C) 1999 Craig Knudsen, cknudsen@cknudsen.com
 *	See accompanying file "COPYING".
 * 
 *	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 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
 *
 * Description:
 *	Helps you keep track of time spent on different tasks.
 *
 * Author:
 *	Craig Knudsen, cknudsen@cknudsen.com, http://www.cknudsen.com
 *
 * Home Page:
 *	http://www.cknudsen.com/gtimer/
 *
 * History:
 *	27-Feb-2006	Added "Last Year" report option.  (Bruno Gravato)
 *	27-Feb-2006	Fix a crash in daily reports when annotations are
 *			included.  (Russ Allbery)
 *	15-Jul-2005	Allow the start of the week to be configured.
 *			(Russ Allbery)
 *	15-Jul-2005	Honor the user's locale for weekday names.
 *	17-Apr-2005	Added configurability of the browser. (Russ Allbery)
 *	02-Jan-2004	Honor the user's locale for date display.  (Colin
 *			Phipps)
 *	09-Mar-2003	Avoid a temporary file when printing by using popen.
 *			Safely create the tmeporary file for HTML reports.
 *			Use tmpfile for formatting the reports.  (CPhipps
 *			and chewie@debian.org)
 *	03-Mar-2003	Added support for rounding.
 *	28-Feb-2003	Include project name or "none" in reports
 *	09-Mar-2000	Updated calls to create_confirm_window()
 *	25-Mar-1999	Added use of gtk_window_set_transient_for() for
 *			GTK 1.1 or later.
 *	18-Mar-1999	Internationalization
 *	02-Feb-1999	gtk 1.1 support
 *	04-Jan-1999	Eek...  Fixed bug for "last month" reports when
 *			run in Jan.
 *	10-May-1998	Removed ifdef for GTK versions before 1.0.0
 *	05-Apr-1998	Added word wrap for report display
 *	04-Apr-1998	Added print
 *	28-Mar-1998	Finished adding support for HTML.
 *			Tell Netscape where to look for the file.
 *	26-Mar-1998	Added O_TRUNC option when saving to a file.
 *	26-Mar-1998	Added preliminary support for HTML output.
 *			Must save to a file and open with Netscape.
 *	22-Mar-1998	Added config_midnight_offset as an parameter to
 *			TaskGetAnnotationEntries.  Entries after midnight
 *			were showing up on the day after the hours were
 *			recorded.
 *	20-Mar-1998	Replaced label widget with a text widget.
 *	18-Mar-1998	Added calls to gtk_window_set_wmclass so the windows
 *			behave better for window managers.
 *	18-Mar-1998	Fixed bug where all weekdays show up as "Mon" in
 *			reports.
 *	18-Mar-1998	Fixed bug where for loop went past array boundaries
 *			and called free on invalid address.
 *	17-Mar-1998	Changed font in report window to be courier so
 *			that text would line up correctly.
 *	17-Mar-1998	Added option of including annotations and make
 *			including hours worked optional.
 *	16-Mar-1998	Removed reference to sys_errlist[] for
 *			compatibility with glibc.
 *	13-Mar-1998	Use config_midnight_offset when figuring out what the
 *			current date is.
 *	13-Mar-1998	Include seconds in reports
 *	12-Mar-1998	Remove toggle buttons for report type.  Now set
 *			by calling routine.
 *	10-Mar-1998	Added save to file
 *	10-Mar-1998	Added additional report options:
 *			  allow hours for each day to be optional
 *			  include summary for day
 *			  include summary for week
 *			  include summary for month
 *			  include days with no hours
 *	10-Mar-1998	Commented out all tooltips since the API is
 *			different for GTK-0.99.3 and GTK-0.99.5.
 *	08-Mar-1998	Created
 *
 ****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <time.h>
#include <memory.h>
#include <ctype.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include <gtk/gtk.h>

#include "project.h"
#include "task.h"
#include "gtimer.h"
#include "config.h"
// PV:
#include "custom-list.h"


#ifdef GTIMER_MEMDEBUG
#include "memdebug/memdebug.h"
#endif

#define NO_TOOLTIPS	1

/* CSS stylesheet */
#define CSS_STYLE "\
<style>\n\
body {\n\
  background: #ffffff;\n\
}\n\
th {\n\
  background: #c0c0c0;\n\
}\n\
td {\n\
  background: #ffffff;\n\
}\n\
</style>\n"

#define ONE_DAY	(3600*24)
static int month_days[] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static int lmonth_days[] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };

extern TaskData **visible_tasks;
extern int num_visible_tasks;
extern int config_midnight_offset;
extern int config_start_of_week;
extern GtkWidget *main_window;
extern GdkPixmap *appicon2;
extern GdkPixmap *appicon2_mask;

/* Report types */
#define REPORT_RANGE_TODAY              0
#define REPORT_RANGE_THIS_WEEK          1
#define REPORT_RANGE_LAST_WEEK          2
#define REPORT_RANGE_THIS_AND_LAST_WEEK 3
#define REPORT_RANGE_LAST_TWO_WEEKS     4
#define REPORT_RANGE_THIS_MONTH         5
#define REPORT_RANGE_LAST_MONTH         6
#define REPORT_RANGE_THIS_YEAR          7
#define REPORT_RANGE_LAST_YEAR          8
static char *time_options[] = { gettext_noop("Today"),
                                gettext_noop("This Week"),
                                gettext_noop("Last Week"),
                                gettext_noop("This Week & Last Week"),
                                gettext_noop("Last Two Weeks"),
                                gettext_noop("This Month"),
                                gettext_noop("Last Month"),
                                gettext_noop("This Year"),
                                gettext_noop("Last Year"),
				NULL };

/* Output types */
#define REPORT_OUTPUT_TEXT		0
#define REPORT_OUTPUT_HTML		1
/* LaTeX next? */
static char *output_options[] = { gettext_noop("Text"),
                                  gettext_noop("HTML"),
				  NULL };

/* Data to include */
#define REPORT_DATA_HOURS		0
#define REPORT_DATA_ANNOTATIONS		1
#define REPORT_DATA_BOTH		2
static char *data_options[] = { gettext_noop("Hours worked"),
                                gettext_noop( "Annotations"),
                                gettext_noop("Hours & Annotations"),
				NULL };

/* Rounding options */
#define REPORT_ROUND_NONE		0
#define REPORT_ROUND_MINUTE		60
#define REPORT_ROUND_5_MINUTES		300
#define REPORT_ROUND_10_MINUTES		600
#define REPORT_ROUND_15_MINUTES		900
#define REPORT_ROUND_30_MINUTES		1800
#define REPORT_ROUND_HOUR		3600
static char *round_options[] = { gettext_noop("None"),
                                 gettext_noop("Minute"),
                                 gettext_noop("5 Minutes"),
                                gettext_noop("10 Minutes"),
                                gettext_noop("15 minutes"),
                                gettext_noop("30 Minutes"),
                                gettext_noop("Hour"),
				NULL };
static int round_values[]= { REPORT_ROUND_NONE,
  REPORT_ROUND_MINUTE, REPORT_ROUND_5_MINUTES,
  REPORT_ROUND_10_MINUTES, REPORT_ROUND_15_MINUTES,
  REPORT_ROUND_30_MINUTES, REPORT_ROUND_HOUR, -1 };
  

typedef struct {
  TaskData *taskdata;
  time_t weekly_total;
  time_t monthly_total;
  time_t yearly_total;
  time_t total;
  char *todays_annotations;
  char *weekly_annotations;
  char *monthly_annotations;
  char *yearly_annotations;
  char *total_annotations;
  char week_start[20];
} ReportTaskData;

typedef struct {
  GtkWidget *window;
  GtkWidget *time_menu;
  GtkWidget *time_menu_items[9];
  GtkWidget *output_menu;
  GtkWidget *output_menu_items[2];
  GtkWidget *data_menu;
  GtkWidget *data_menu_items[3];
  GtkWidget *round_menu;
  GtkWidget *round_menu_items[8];
//  GtkWidget *task_list;    // PV: -
  GtkTreeView *task_list;    // PV: +
  TaskData **tasks;
  int num_tasks;
//  GtkWidget **list_items;  // PV: -
  GtkTreePath **list_items;  // PV: +
  report_type type;
  int include_hours;
  int include_annotations;
} ReportData;

typedef struct {
  GtkWidget *window;
  char *text;
  GtkWidget *filesel;
  GtkWidget *printwin;
  GtkWidget *printentry;
} DisplayReportData;






/*
** Append an annotation to the given pointer after we realloc some
** space for it.  optionally include the date and time and indent
** all lines to make it look nice.
*/
static void concat_annotation ( ptr, annotation, include_date,
  include_time, indentation, newline )
char **ptr;
TaskAnnotation *annotation;
int include_date, include_time;
char *indentation;
char *newline;
{
  struct tm *tm;
  char date_time_str[30];
  char padding[30], *p;
  char *anntext, *newtext;
  int first;

  strcpy ( date_time_str, indentation );
  tm = localtime ( &annotation->text_time );
  if ( include_date )
    sprintf ( date_time_str + strlen ( date_time_str ),
      "%02d/%02d/%02d ", tm->tm_mon + 1,
      tm->tm_mday, tm->tm_year % 100 );

  if ( include_time )
    sprintf ( date_time_str + strlen ( date_time_str ),
      "%02d:%02d ", tm->tm_hour, tm->tm_min );

  strcpy ( padding, date_time_str );
  for ( p = padding; *p != '\0'; p++ )
    *p = ' ';

  /* make a copy of the annotation text because strtok() stomps on it */
  anntext = (char *) malloc ( strlen ( annotation->text ) + 1 );
  strcpy ( anntext, annotation->text );

  if ( *ptr ) {
    newtext = (char *) malloc ( strlen ( *ptr ) + 1 );
    strcpy ( newtext, *ptr );
  } else {
    newtext = (char *) malloc ( 1 );
    newtext[0] = '\0';
  }
  first = 1;
  p = strtok ( anntext, "\n" );
  while ( p ) {
    newtext = (char *) realloc ( newtext,
      strlen ( newtext ) + strlen ( p ) + strlen ( padding ) + 2 +
      strlen ( newline ) );
    if ( first ) {
      first = 0;
      strcat ( newtext, date_time_str );
    } else {
      strcat ( newtext, padding );
    }
    strcat ( newtext, p );
    strcat ( newtext, newline );
    p = strtok ( NULL, "\n" );
  }
  free ( anntext );

  if ( *ptr )
    free ( *ptr );
  *ptr = newtext;
}



static void display_text_results_ok_callback ( widget, data )
GtkWidget *widget;
gpointer data;
{
  DisplayReportData *drd = (DisplayReportData *)data;
  gtk_grab_remove ( drd->window );
  gtk_widget_destroy ( drd->window );
  if ( drd->filesel ) {
    gtk_grab_remove ( drd->filesel );
    gtk_widget_destroy ( drd->filesel );
  }
  if ( drd->printwin ) {
    gtk_grab_remove ( drd->printwin );
    gtk_widget_destroy ( drd->printwin );
  }
  free ( drd->text );
  free ( drd );
}

static void display_text_results_save_cancel_callback ( widget, data )
GtkWidget *widget;
gpointer data;
{
  DisplayReportData *drd = (DisplayReportData *)data;
  gtk_grab_remove ( drd->filesel );
  gtk_widget_destroy ( drd->filesel );
  drd->filesel = NULL;
}

static void display_text_results_save_ok_callback ( widget, data )
GtkWidget *widget;
gpointer data;
{
  DisplayReportData *drd = (DisplayReportData *)data;
  char *file;
  int fd;
  char *msg;

// PV: better: gtk_file_chooser_dialog  see
// http://library.gnome.org/devel/gtk/stable/GtkFileChooserDialog.html

// PV: warning: assignment discards qualifiers from pointer target type
  file = gtk_file_selection_get_filename (
    GTK_FILE_SELECTION ( drd->filesel ) );
  fd = open ( file, O_WRONLY | O_CREAT | O_TRUNC, 0644 );
  if ( fd <= 0 ) {
    msg = (char *) malloc ( strlen ( file ) + 100 );
    sprintf ( msg, "%s %d %s\n%s", gettext ("Error"),
      errno, gettext("writing to file"), file );
    create_confirm_window ( CONFIRM_ERROR,
      gettext("Error"), msg, gettext("Ok"), NULL, NULL,
      NULL, NULL, NULL, NULL );
    free ( msg );
  } else {
    write ( fd, drd->text, strlen ( drd->text ) );
    close ( fd );
    gtk_grab_remove ( drd->filesel );
    gtk_widget_destroy ( drd->filesel );
    drd->filesel = NULL;
  }
}

static void display_text_results_save_callback ( widget, data )
GtkWidget *widget;
gpointer data;
{
  DisplayReportData *drd = (DisplayReportData *)data;
  char msg[100];

  if ( drd->filesel )
    return;

  sprintf ( msg, "GTimer: %s", gettext("Save Report") );
  drd->filesel = gtk_file_selection_new ( msg );

  gtk_signal_connect (GTK_OBJECT (GTK_FILE_SELECTION(drd->filesel)->ok_button),
    "clicked", GTK_SIGNAL_FUNC (display_text_results_save_ok_callback), drd);
  gtk_signal_connect (
    GTK_OBJECT (GTK_FILE_SELECTION(drd->filesel)->cancel_button),
    "clicked", GTK_SIGNAL_FUNC (display_text_results_save_cancel_callback), drd);

  gtk_widget_show ( drd->filesel );
}



static void print_ok_callback ( widget, data )
GtkWidget *widget;
gpointer data;
{
  DisplayReportData *drd = (DisplayReportData *)data;
  FILE *fp = NULL;
  char cmd[500], *ptr, *msg;

#if PV_DEBUG
  printf("OK(print)\n");
  printf("\n****************\n%s", drd->text);
#endif

// PV: warning: assignment discards qualifiers from pointer target type
  ptr = gtk_entry_get_text ( GTK_ENTRY(drd->printentry) );
  if ( ! ptr || ! strlen ( ptr ) ) {
    create_confirm_window ( CONFIRM_ERROR,
      gettext("Error"),
      gettext("You must enter a print command"),
      gettext("Ok"), NULL, NULL,
      NULL, NULL, NULL, NULL );
    return;
  }

  fp = popen(ptr,"w");
  if ( !fp ) {
    msg = (char *) malloc ( 120 );
    sprintf ( msg, "%s %d %s\n%s", gettext("Error"),
        errno, gettext("Error printing"), strerror(errno) );
    create_confirm_window ( CONFIRM_ERROR, gettext("Error"), msg,
        gettext("Ok"), NULL, NULL,
        NULL, NULL, NULL, NULL );
    free ( msg );
  } else {
    fwrite ( drd->text, strlen ( drd->text ), sizeof(char), fp );
    fclose ( fp );
    if ( ferror (fp) ) {
      create_confirm_window ( CONFIRM_ERROR,
        gettext("Error"), gettext("Error printing"),
        gettext("Ok"), NULL, NULL,
        NULL, NULL, NULL, NULL );
    } else {
      configSetAttribute ( CONFIG_PRINT, ptr );
      gtk_grab_remove ( drd->printwin );
      gtk_widget_destroy ( drd->printwin );
      drd->printwin = NULL;
    }
  }
}


static void print_cancel_callback ( widget, data )
GtkWidget *widget;
gpointer data;
{
  DisplayReportData *drd = (DisplayReportData *)data;

  gtk_grab_remove ( drd->printwin );
  gtk_widget_destroy ( drd->printwin );
  drd->printwin = NULL;
}


static void display_text_results_print_callback ( widget, data )
GtkWidget *widget;
gpointer data;
{
  DisplayReportData *drd = (DisplayReportData *)data;
  char *ptr, msg[100];
  GtkWidget *hbox, *label, *button;

  if ( drd->printwin )
    return;

  drd->printwin = gtk_dialog_new ();
  gtk_window_set_wmclass ( GTK_WINDOW ( drd->printwin ), "GTimer", "gtimer" );
  sprintf ( msg, "GTimer: %s", gettext("Print Report") );
  gtk_window_set_title (GTK_WINDOW (drd->printwin), msg );
  gtk_window_position ( GTK_WINDOW(drd->printwin), GTK_WIN_POS_MOUSE );
  gtk_grab_add ( drd->printwin );
  gtk_widget_realize ( drd->printwin );
  gdk_window_set_icon ( GTK_WIDGET ( drd->printwin )->window,
    NULL, appicon2, appicon2_mask );

  gtk_window_set_transient_for ( GTK_WINDOW ( drd->printwin ),
    GTK_WINDOW ( drd->window ) );

  hbox = gtk_hbox_new ( TRUE, 5 );
  gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG (drd->printwin)->vbox ),
    hbox, FALSE, FALSE, 5 );

  sprintf ( msg, "%s: ", gettext("Print Command") );
  label = gtk_label_new ( msg );
  gtk_box_pack_start ( GTK_BOX ( hbox ), label, FALSE, FALSE, 5 );
  gtk_widget_show ( label );

  drd->printentry = gtk_entry_new ();
  gtk_box_pack_start ( GTK_BOX ( hbox ), drd->printentry, FALSE, FALSE, 5 );
  if ( configGetAttribute ( CONFIG_PRINT, &ptr ) == 0 )
    gtk_entry_set_text ( GTK_ENTRY( drd->printentry ), ptr );
  gtk_widget_show ( drd->printentry );

  gtk_widget_show ( hbox );

  button = gtk_button_new_with_label ( gettext("Ok") );
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (drd->printwin)->action_area),
    button, TRUE, TRUE, 5);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
    GTK_SIGNAL_FUNC (print_ok_callback), drd);
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_widget_grab_default (button);
  gtk_widget_show (button);

  button = gtk_button_new_with_label ( gettext("Cancel") );
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (drd->printwin)->action_area),
    button, TRUE, TRUE, 5);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
    GTK_SIGNAL_FUNC (print_cancel_callback), drd);
  gtk_widget_show (button);

  gtk_widget_show ( drd->printwin );
}


/* PV: static void display_text_results ( text )
*     rewritten during migration to GTK2
*     Thanks to Vijay Kumar B. and his tutorial
*     Multiline Text Editing Widget
*/

void
find (GtkTextView *text_view, const gchar *text, GtkTextIter *iter)
{
  GtkTextIter mstart, mend;
  gboolean found;
  GtkTextBuffer *buffer;
  GtkTextMark *last_pos;

  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
  found = gtk_text_iter_forward_search (iter, text, 0, &mstart, &mend, NULL);

  if (found)
    {
      gtk_text_buffer_select_range (buffer, &mstart, &mend);
      last_pos = gtk_text_buffer_create_mark (buffer, "last_pos", 
                                              &mend, FALSE);
      gtk_text_view_scroll_mark_onscreen (text_view, last_pos);
    }
}



typedef struct _App {
  GtkWidget *text_view;
  GtkWidget *search_entry;
} App;

App app;

void
win_destroy (void)
{
  gtk_main_quit();
}

void
next_button_clicked (GtkWidget *next_button, App *app)
{
  const gchar *text;
  GtkTextBuffer *buffer;
  GtkTextMark *last_pos;
  GtkTextIter iter;

  text = gtk_entry_get_text (GTK_ENTRY (app->search_entry));
  
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (app->text_view));

  last_pos = gtk_text_buffer_get_mark (buffer, "last_pos");
  if (last_pos == NULL)
    return;

  gtk_text_buffer_get_iter_at_mark (buffer, &iter, last_pos);
  find (GTK_TEXT_VIEW (app->text_view), text, &iter);
}

void
search_button_clicked (GtkWidget *search_button, App *app)
{
  const gchar *text;
  GtkTextBuffer *buffer;
  GtkTextIter iter;

  text = gtk_entry_get_text (GTK_ENTRY (app->search_entry));
  buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (app->text_view));
  gtk_text_buffer_get_start_iter (buffer, &iter);

  find (GTK_TEXT_VIEW (app->text_view), text, &iter);
}


void static display_text_results ( char *text )
{
  GtkWidget *vbox;
  GtkWidget *hbox;
  GtkWidget *search_button;
  GtkWidget *next_button;
  GtkWidget *swindow;

  // PV: additional variables and original objects
  GtkWidget		*hbox2;
  GtkWidget		*save_button, *print_button, *ok_button;
  GtkTextBuffer		*txbuf;
  size_t		txlen;
  DisplayReportData	*drd;
  char			msg[100];

#if PV_DEBUG
  g_message("Display_text_results: start");
#endif


  // At first copy original code
  drd = (DisplayReportData *) malloc ( sizeof ( DisplayReportData ) );
  memset ( drd, '\0', sizeof ( DisplayReportData ) );
  drd->text = text;

#if PV_DEBUG
  g_message("Display_text_results: checkpoint 1");
#endif

  // ... and now create window using GTK2 logic

  drd->window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
//  g_signal_connect (G_OBJECT (drd->window), "destroy", win_destroy, NULL);

  // Set window attributes:
  sprintf ( msg, "GTimer: %s", gettext ("Report") );
  gtk_window_set_title (GTK_WINDOW (drd->window), msg );
  gtk_window_set_wmclass ( GTK_WINDOW ( drd->window ), "GTimer", "gtimer" );
// PV: set default size 450x300
  gtk_window_set_default_size (GTK_WINDOW(drd->window), 450, 500);
  gtk_widget_realize ( drd->window );
  gdk_window_set_icon ( GTK_WIDGET ( drd->window )->window,
    NULL, appicon2, appicon2_mask );
  // set font?
  //  if ( font == NULL ) {
  //    font = gdk_font_load ( "-adobe-courier-medium-r-*-*-12-*-*-*-*-*-*-*" );
  //    if ( font == NULL )
  //      font = gdk_font_load ( "fixed" );
  //  }
  
#if PV_DEBUG
  g_message("Display_text_results: checkpoint 2");
#endif

  vbox = gtk_vbox_new (FALSE, 2);
  gtk_container_add (GTK_CONTAINER (drd->window), vbox);

  hbox = gtk_hbox_new (FALSE, 2);
  gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, FALSE, 0);
  
  app.search_entry = gtk_entry_new ();
  gtk_box_pack_start (GTK_BOX (hbox), app.search_entry, TRUE, TRUE, 0);

#if PV_DEBUG
  g_message("Display_text_results: checkpoint 3");
#endif


  search_button = gtk_button_new_with_label ("Search");  
  gtk_box_pack_start (GTK_BOX (hbox), search_button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (search_button), "clicked", 
                    G_CALLBACK (search_button_clicked), &app);

  next_button = gtk_button_new_with_label ("Next");
  gtk_box_pack_start (GTK_BOX (hbox), next_button, FALSE, FALSE, 0);
  g_signal_connect (G_OBJECT (next_button), "clicked",
                    G_CALLBACK (next_button_clicked), &app);

  swindow = gtk_scrolled_window_new (NULL, NULL);
  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
                                  GTK_POLICY_AUTOMATIC,
                                  GTK_POLICY_AUTOMATIC);
  gtk_box_pack_start (GTK_BOX (vbox), swindow, TRUE, TRUE, 0);

#if PV_DEBUG
  g_message("Display_text_results: checkpoint 4");
#endif


// PV: add: set text
  txbuf = gtk_text_buffer_new(NULL);
  txlen = strlen ( text );
  gtk_text_buffer_set_text ( GTK_TEXT_BUFFER ( txbuf ), text, txlen );
// PV: end of set text

#if PV_DEBUG
  g_message("Display_text_results: checkpoint 5");
#endif

  app.text_view = gtk_text_view_new_with_buffer (txbuf);
  gtk_text_view_set_editable(GTK_TEXT_VIEW(app.text_view),0);
  gtk_container_add (GTK_CONTAINER (swindow), app.text_view);

  hbox2 = gtk_hbox_new (FALSE, 2);
  gtk_box_pack_start (GTK_BOX (vbox), hbox2, FALSE, FALSE, 0);

#if PV_DEBUG
  g_message("Display_text_results: checkpoint 6");
#endif
  
  save_button = gtk_button_new_with_label ("Save");
  gtk_box_pack_start (GTK_BOX (hbox2), save_button, TRUE, FALSE, 0);
  g_signal_connect (G_OBJECT (save_button), "clicked",
                    G_CALLBACK (display_text_results_save_callback), drd);
   
  print_button = gtk_button_new_with_label ("Print");
  gtk_box_pack_start (GTK_BOX (hbox2), print_button, TRUE, FALSE, 0);
  g_signal_connect (G_OBJECT (print_button), "clicked",
                    G_CALLBACK (display_text_results_print_callback), drd);

  ok_button = gtk_button_new_with_label ("Ok");
  gtk_box_pack_start (GTK_BOX (hbox2), ok_button, TRUE, FALSE, 0);
  g_signal_connect (G_OBJECT (ok_button), "clicked",
                    G_CALLBACK (display_text_results_ok_callback), drd);

#if PV_DEBUG
  g_message("Display_text_results: checkpoint 7");
#endif


  gtk_widget_show_all (drd->window);

#if PV_DEBUG
  g_message("Display_text_results: end");
#endif


}




static time_t do_round ( time_in, round_incr )
time_t time_in;
int round_incr;
{
  int t, n, r;

  if ( round_incr == 0 )
    return ( time_in );

  t = (int) time_in;
  r = t % round_incr;
  t -= r;
  if ( r > ( round_incr / 2 ) )
    t += round_incr;

  return ( (time_t) t );
}



static time_t summarize_day ( fp, seltasks, num_seltasks, then, type,
  format, include_hours, include_annotations, round_incr, is_last )
FILE *fp;
ReportTaskData **seltasks;
int num_seltasks;
time_t then;
report_type type;
int format;
int include_hours;
int include_annotations;
int round_incr;
int is_last;
{
  struct tm *tm;
  int found = 0;
  int loop, loop2;
  int mon, mday, year, wday;
  TaskTimeEntry *entry;
  TaskAnnotation **anns;
  int num_anns = 0;
  int h, m, s, rounded;
  time_t ret = 0;
  char indentation[30];
  int ncols = 0;
  char *newline = "\n";
  char daystring[20];

  switch ( format ) {
    case REPORT_OUTPUT_TEXT:
      newline = "\n";
      break;
    case REPORT_OUTPUT_HTML:
      newline = "<br>\n";
      break;
  }

  if ( include_hours )
    ncols++;
  if ( include_annotations )
    ncols++;

  tm = localtime ( &then );
  mon = tm->tm_mon + 1;
  mday = tm->tm_mday;
  year = tm->tm_year + 1900;
  wday = tm->tm_wday;

  if ( include_hours )
    strcpy ( indentation, "            " );
  else
    strcpy ( indentation, "  " );

  strftime ( daystring, sizeof(daystring), "%x %a", tm );

  for ( loop = 0; loop < num_seltasks; loop++ ) {
    if ( ( seltasks[loop]->week_start[0] == '\0' ) ||
      ( tm->tm_wday == config_start_of_week ) )
      strcpy ( seltasks[loop]->week_start, daystring );
    if ( include_hours )
      entry = taskGetTimeEntry ( seltasks[loop]->taskdata->task,
        year, mon, mday );
    else
      entry = NULL;
    if ( include_annotations ) {
      anns = TaskGetAnnotationEntries ( seltasks[loop]->taskdata->task,
        year, mon, mday, config_midnight_offset, &num_anns );
      for ( loop2 = 0; loop2 < num_anns; loop2++ ) {
        if ( type == REPORT_TYPE_DAILY )
          concat_annotation ( &seltasks[loop]->todays_annotations,
            anns[loop2], 0, 0, indentation, newline );
        else if ( type == REPORT_TYPE_WEEKLY )
          concat_annotation ( &seltasks[loop]->weekly_annotations,
            anns[loop2], 0, 0, indentation, newline );
        else if ( type == REPORT_TYPE_MONTHLY )
          concat_annotation ( &seltasks[loop]->monthly_annotations,
            anns[loop2], 0, 0, indentation, newline );
        else if ( type == REPORT_TYPE_YEARLY )
          concat_annotation ( &seltasks[loop]->yearly_annotations,
            anns[loop2], 0, 0, indentation, newline );
        else if ( type == REPORT_TYPE_TOTAL )
          concat_annotation ( &seltasks[loop]->total_annotations,
            anns[loop2], 0, 0, indentation, newline );
      }
      if ( anns )
        free ( anns );
    }
    rounded = 0;
    if ( ( entry && entry->seconds ) || seltasks[loop]->todays_annotations ) {
      if ( entry && entry->seconds ) {
        rounded = do_round ( entry->seconds, round_incr );
        h = rounded / 3600;
        m = ( rounded - h * 3600 ) / 60;
        s = rounded % 60;
      }
      if ( type == REPORT_TYPE_DAILY ) {
        if ( ! found ) {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "\n%s\n", daystring );
              fprintf ( fp, "-------------------------------------\n" );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<tr><th colspan=\"%d\">",
                ncols + 1 );
              fprintf ( fp, "\n%s\n", daystring );
              fprintf ( fp, "</th></tr>\n" );
              break;
          }
          found = 1;
        }
        if ( format == REPORT_OUTPUT_HTML )
          fprintf ( fp, "<tr>" );
        if ( include_hours ) {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "%3d:%02d:%02d - [%s] %s\n", h, m, s,
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp,
                "<td align=\"right\" valign=\"top\">%d:%02d:%02d</td>",
                h, m, s );
              fprintf ( fp, "<td valign=\"top\">[%s] %s</td>",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
          }
        } else {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "[%s] %s\n",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<td valign=\"top\">[%s] %s</td>",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
          }
        }
      }
      if ( rounded ) {
        ret += rounded;
        /* update totals */
        seltasks[loop]->weekly_total += rounded;
        seltasks[loop]->monthly_total += rounded;
        seltasks[loop]->yearly_total += rounded;
        seltasks[loop]->total += rounded;
      }
      if ( seltasks[loop]->todays_annotations ) {
        switch ( format ) {
          case REPORT_OUTPUT_TEXT:
            fprintf ( fp, "%s", seltasks[loop]->todays_annotations );
            break;
          case REPORT_OUTPUT_HTML:
            fprintf ( fp, "<td>%s</td>", seltasks[loop]->todays_annotations );
            break;
        }
        free ( seltasks[loop]->todays_annotations );
        seltasks[loop]->todays_annotations = NULL;
      }
      if ( type == REPORT_TYPE_DAILY && format == REPORT_OUTPUT_HTML )
        fprintf ( fp, "</tr>\n" );
    }
  }

  if ( type == REPORT_TYPE_DAILY && ret ) {
    h = ret / 3600;
    m = ( ret - h * 3600 ) / 60;
    s = ret % 60;
    switch ( format ) {
      case REPORT_OUTPUT_TEXT:
        fprintf ( fp, "---------\n" );
        fprintf ( fp, "%3d:%02d:%02d - %s\n", h, m, s, gettext("Total") );
        break;
      case REPORT_OUTPUT_HTML:
        fprintf ( fp, "<tr><td align=\"right\"><b>%d:%02d:%02d</b></td>",
          h, m, s );
        fprintf ( fp, "<td><b>%s</b></td></tr>\n", gettext("Total") );
        break;
    }
  }

  if ( ( type == REPORT_TYPE_WEEKLY ) && 
    ( wday == ( ( config_start_of_week + 6 ) % 7 ) || is_last ) ) {
    found = 0;
    for ( loop = 0; loop < num_seltasks; loop++ ) {
      if ( seltasks[loop]->weekly_total ||
        seltasks[loop]->weekly_annotations ) {
        if ( ! found ) {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "\n%s %s %s %s\n", gettext("Week"),
                seltasks[loop]->week_start, gettext("to"), daystring );
              fprintf ( fp, "-------------------------------------\n" );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<tr><th colspan=\"%d\">", ncols + 1 );
              fprintf ( fp, "%s %s %s %s", gettext("Week"),
                seltasks[loop]->week_start, gettext("to"), daystring );
              fprintf ( fp, "</th></tr>\n" );
              break;
          }
          found = 1;
        }
        if ( include_hours ) {
          h = seltasks[loop]->weekly_total / 3600;
          m = ( seltasks[loop]->weekly_total - h * 3600 ) / 60;
          s = seltasks[loop]->weekly_total % 60;
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "%3d:%02d:%02d - [%s] %s\n", h, m, s,
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp,
                "<tr><td valign=\"top\" align=\"right\">%d:%02d:%02d</td>",
                h, m, s );
              fprintf ( fp, "<td valign=\"top\">[%s] %s</td>",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
          }
          seltasks[loop]->weekly_total = 0;
        } else {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "[%s] %s\n",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<td valign=\"top\">[%s] %s</td>",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
          }
        }
        if ( seltasks[loop]->weekly_annotations ) {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "%s", seltasks[loop]->weekly_annotations );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<td>%s</td>", seltasks[loop]->weekly_annotations );
              break;
          }
          free ( seltasks[loop]->weekly_annotations );
          seltasks[loop]->weekly_annotations = NULL;
        }
        if ( format == REPORT_OUTPUT_HTML )
          fprintf ( fp, "</tr>\n" );
      }
    }
  }

  if ( ( type == REPORT_TYPE_MONTHLY ) &&
    ( is_last ||
    ( ( ( tm->tm_year % 4 == 0 ) &&
      ( lmonth_days[tm->tm_mon] == tm->tm_mday ) ) ||
    ( ( tm->tm_year % 4 != 0 ) &&
      ( month_days[tm->tm_mon] == tm->tm_mday ) ) ) ) ) {
    found = 0;
    for ( loop = 0; loop < num_seltasks; loop++ ) {
      if ( seltasks[loop]->monthly_total ||
        seltasks[loop]->monthly_annotations ) {
        if ( ! found ) {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "\n%s %02d/%d\n", gettext("Month"), mon, year );
              fprintf ( fp, "-------------------------------------\n" );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<tr><th colspan=\"%d\">%s %02d/%d</th></tr>",
                ncols + 1, gettext("Month"), mon, year );
              break;
          }
          found = 1;
        }
        if ( include_hours ) {
          h = seltasks[loop]->monthly_total / 3600;
          m = ( seltasks[loop]->monthly_total - h * 3600 ) / 60;
          s = seltasks[loop]->monthly_total % 60;
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "%3d:%02d:%02d - [%s] %s\n", h, m, s,
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<td valign=\"top\">%d:%02d:%02d</td>",
                h, m, s );
              fprintf ( fp, "<td valign=\"top\">[%s] %s</td>",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
          }
          seltasks[loop]->monthly_total = 0;
        }
        else {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "[%s] %s\n",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<td valign=\"top\">[%s] %s</td>",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
          }
        }
        if ( seltasks[loop]->monthly_annotations ) {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "%s", seltasks[loop]->monthly_annotations );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<td>%s</td>",
                seltasks[loop]->monthly_annotations );
              break;
          }
          free ( seltasks[loop]->monthly_annotations );
          seltasks[loop]->monthly_annotations = NULL;
        }
        if ( format == REPORT_OUTPUT_HTML )
          fprintf ( fp, "</tr>\n" );
      }
    }
  }

  if ( ( type == REPORT_TYPE_YEARLY ) &&
    ( is_last || ( tm->tm_mon == 12 && tm->tm_mday == 31 ) ) ) {
    found = 0;
    for ( loop = 0; loop < num_seltasks; loop++ ) {
      if ( seltasks[loop]->yearly_total ||
        seltasks[loop]->yearly_annotations ) {
        if ( ! found ) {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "\n%s %d\n", gettext("Year"), year );
              fprintf ( fp, "-------------------------------------\n" );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<tr><th colspan=\"%d\">%s %d</th></tr>\n",
                ncols + 1, gettext("Year"), year );
              break;
          }
          found = 1;
        }
        if ( include_hours ) {
          h = seltasks[loop]->yearly_total / 3600;
          m = ( seltasks[loop]->yearly_total - h * 3600 ) / 60;
          s = seltasks[loop]->yearly_total % 60;
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "%3d:%02d:%02d - [%s] %s\n", h, m, s,
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<td valign=\"top\">%d:%02d:%02d</td>",
                h, m, s );
              fprintf ( fp, "<td valign=\"top\">[%s] %s</td>",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
          }
          seltasks[loop]->yearly_total = 0;
        }
        else {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "[%s] %s\n",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<td valign=\"top\">[%s] %s</td>",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
          }
        }
        if ( seltasks[loop]->yearly_annotations ) {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "%s", seltasks[loop]->yearly_annotations );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<td>%s</td>",
                seltasks[loop]->yearly_annotations );
              break;
          }
          free ( seltasks[loop]->yearly_annotations );
          seltasks[loop]->yearly_annotations = NULL;
        }
        if ( format == REPORT_OUTPUT_HTML )
          fprintf ( fp, "</tr>\n" );
      }
    }
  }

  if ( ( type == REPORT_TYPE_TOTAL ) && is_last ) {
    found = 0;
    for ( loop = 0; loop < num_seltasks; loop++ ) {
      if ( seltasks[loop]->yearly_total ) {
        if ( ! found ) {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "\n%s\n", gettext("Totals") );
              fprintf ( fp, "-------------------------------------\n" );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<tr><th COLSPAN=\"%d\">%s</th></tr>\n",
                ncols + 1, gettext("Totals") );
              break;
          }
          found = 1;
        }
        if ( include_hours ) {
          h = seltasks[loop]->total / 3600;
          m = ( seltasks[loop]->total - h * 3600 ) / 60;
          s = seltasks[loop]->total % 60;
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "%3d:%02d:%02d - [%s] %s\n", h, m, s,
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<td valign=\"top\">%d:%02d:%02d</td>", h, m, s );
              fprintf ( fp, "<td valign=\"top\">[%s] %s</td>",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
          }
          seltasks[loop]->total = 0;
        }
        else {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "[%s] %s\n",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<td valign=\"top\">[%s] %s</td>",
                seltasks[loop]->taskdata->task->project_id < 0 ?
                "none" : seltasks[loop]->taskdata->project_name,
                seltasks[loop]->taskdata->task->name );
              break;
          }
        }
        if ( seltasks[loop]->total_annotations ) {
          switch ( format ) {
            case REPORT_OUTPUT_TEXT:
              fprintf ( fp, "%s", seltasks[loop]->total_annotations );
              break;
            case REPORT_OUTPUT_HTML:
              fprintf ( fp, "<td>%s</td>",
                seltasks[loop]->total_annotations );
              break;
          }
          free ( seltasks[loop]->total_annotations );
          seltasks[loop]->total_annotations = NULL;
        }
        if ( format == REPORT_OUTPUT_HTML )
          fprintf ( fp, "</tr>\n" );
      }
    }
  }

  if ( is_last ) {
    for ( loop = 0; loop < num_seltasks; loop++ ) {
      if ( seltasks[loop]->todays_annotations )
        free ( seltasks[loop]->todays_annotations );
      if ( seltasks[loop]->weekly_annotations )
        free ( seltasks[loop]->weekly_annotations );
      if ( seltasks[loop]->monthly_annotations )
        free ( seltasks[loop]->monthly_annotations );
      if ( seltasks[loop]->yearly_annotations )
        free ( seltasks[loop]->yearly_annotations );
      if ( seltasks[loop]->total_annotations )
        free ( seltasks[loop]->total_annotations );
      seltasks[loop]->todays_annotations = NULL;
      seltasks[loop]->weekly_annotations = NULL;
      seltasks[loop]->monthly_annotations = NULL;
      seltasks[loop]->yearly_annotations = NULL;
      seltasks[loop]->total_annotations = NULL;
    }
  }

  return ( ret );
}




static void display_html_results ( text )
char *text;
{
  char *path;
  int fd = -1;
  char tempfile[L_tmpnam];
  char *command;

  if (tmpnam(tempfile))
    fd = open ( tempfile, O_WRONLY | O_CREAT | O_EXCL, 0600 );
  if (fd == -1) {
    create_confirm_window ( CONFIRM_ERROR,
        gettext("Error"),
        gettext("Error opening temporary file"),
        gettext("Ok"), NULL, NULL,
        NULL, NULL, NULL, NULL );
    return;
  }
  write ( fd, text, strlen ( text ) );
  close ( fd );
  free ( text );

  if ( configGetAttribute ( CONFIG_BROWSER, &path ) < 0 )
    path = "mozilla";

  command = (char *) malloc ( strlen ( path ) + 100 + L_tmpnam );
  if ( strstr ( path, "%s" ) )
    sprintf ( command, path, tempfile );
  else
    sprintf ( command, "%s %s", path, tempfile );
  if ( system ( command ) != 0 ) {
    create_confirm_window ( CONFIRM_ERROR,
      gettext("Error"), gettext("Error communicating with browser."),
      gettext("Ok"), NULL, NULL,
      NULL, NULL, NULL, NULL );
  }

  free ( command );
}



static void ok_callback ( widget, data )
GtkWidget *widget;
gpointer data;
{
  ReportData *rd = (ReportData *) data;
  GList *selected, *item;
#if PV_DEBUG
  volatile int loop;
#else
  int loop;
#endif
  ReportTaskData **seltasks;
  int num_selected = 0;
  int range = REPORT_RANGE_TODAY;
  int format = REPORT_OUTPUT_TEXT;
  int round_incr = REPORT_ROUND_NONE;
  FILE *fp;
  time_t now, time_start, time_end, time_loop, total;
  struct tm *tm;
  struct stat buf;
  char *text = NULL;
  int ret, ncols, h, m, s;
  size_t len;
  // PV:
  GtkTreeSelection *select;
#if PV_DEBUG
  volatile const char *s1;
  volatile const char *s2;
#else
  const char *s1, *s2;
#endif

//  selected = GTK_LIST ( rd->task_list ) ->selection;

#if PV_DEBUG
  g_message("Report start");
#endif


  select = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->task_list));
  selected = gtk_tree_selection_get_selected_rows(GTK_TREE_SELECTION(select), NULL);
  // selected contains list of paths

  if ( ! selected ) {
    create_confirm_window ( CONFIRM_ERROR,
      gettext("Error"),
      gettext("You have not selected any tasks"),
      gettext("Ok"), NULL, NULL,
      NULL, NULL, NULL,
      NULL );
    return;
  }

  /* include hours / annotations*/
  for ( loop = 0; data_options[loop]; loop++ ) {
    if ( GTK_OPTION_MENU ( rd->data_menu ) -> menu_item ==
      rd->data_menu_items[loop] ) {
      switch ( loop ) {
        case REPORT_DATA_HOURS:
          rd->include_hours = 1;
          rd->include_annotations = 0;
          break;
        case REPORT_DATA_ANNOTATIONS:
          rd->include_hours = 0;
          rd->include_annotations = 1;
          break;
        case REPORT_DATA_BOTH:
          rd->include_hours = 1;
          rd->include_annotations = 1;
          break;
      }
    }
  }

  if ( ! rd->include_hours && ! rd->include_annotations ) {
    create_confirm_window ( CONFIRM_ERROR,
      gettext("Error"),
      gettext("You must select either \"include hours\"\nor \"include annotations\"."),
      gettext("Ok"), NULL, NULL,
      NULL, NULL, NULL,
      NULL );
    return;
  }

#if PV_DEBUG
  g_message("Report CP1 -- Tasks: %d \n", rd->num_tasks);
#endif

  /* which tasks were selected... */
  seltasks = (ReportTaskData **) malloc
    ( sizeof ( ReportTaskData * ) * rd->num_tasks );
  for ( loop = 0; loop < rd->num_tasks; loop++ )
    seltasks[loop] = NULL;
  for ( item = selected; item != NULL; item = item->next ) {
    s1 = gtk_tree_path_to_string(item->data);
    for ( loop = 0; loop < rd->num_tasks; loop++ ) {
       s2 = gtk_tree_path_to_string(rd->list_items[loop]);
        if ( ! strcmp(s1, s2) ) {
        seltasks[num_selected] = (ReportTaskData *) malloc
          ( sizeof ( ReportTaskData ) );
        memset ( seltasks[num_selected], '\0', sizeof ( ReportTaskData ) );
        seltasks[num_selected++]->taskdata = rd->tasks[loop];
      }
    }
  }

#if PV_DEBUG
  g_message("Report checkpoint 2");
#endif


  /* which time range ? */
  for ( loop = 0; time_options[loop]; loop++ ) {
    if ( GTK_OPTION_MENU ( rd->time_menu ) -> menu_item ==
      rd->time_menu_items[loop] ) {
      range = loop;
    }
  }

#if PV_DEBUG
  g_message("Report checkpoint 3");
#endif
  /* which output format */
  for ( loop = 0; output_options[loop]; loop++ ) {
    if ( GTK_OPTION_MENU ( rd->output_menu ) -> menu_item ==
      rd->output_menu_items[loop] ) {
      format = loop;
    }
  }

#if PV_DEBUG
  g_message("Report checkpoint 4");
#endif
  /* which rounding ? */
  for ( loop = 0; round_options[loop]; loop++ ) {
    if ( GTK_OPTION_MENU ( rd->round_menu ) -> menu_item ==
      rd->round_menu_items[loop] ) {
      round_incr = round_values[loop];
    }
  }

#if PV_DEBUG
  g_message("Report checkpoint 5");
#endif

  gtk_grab_remove ( rd->window );
  gtk_widget_destroy ( rd->window );

  /* Generate the report... */
  fp = tmpfile();
  if ( ! fp ) {
    create_confirm_window ( CONFIRM_ERROR,
      gettext("Error"),
      gettext("Error opening temporary file"),
      gettext("Ok"), NULL, NULL,
      NULL, NULL, NULL,
      NULL );
    for ( loop = 0; loop < num_visible_tasks; loop++ )
      free ( seltasks[loop] );
    free ( seltasks );
    free ( rd->tasks );
    free ( rd->list_items );
    free ( rd );
    return;
  }
  time ( &now );
  now -= config_midnight_offset;
  tm = localtime ( &now );
  switch ( range ) {
    case REPORT_RANGE_TODAY:
      time_start = time_end = now;
      break;
    case REPORT_RANGE_LAST_WEEK:
      time_start = now - ONE_DAY * tm->tm_wday - ONE_DAY * 7;
      time_end = now - ONE_DAY * tm->tm_wday - ONE_DAY;
      break;
    case REPORT_RANGE_THIS_AND_LAST_WEEK:
      time_start = now - ONE_DAY * tm->tm_wday - ONE_DAY * 7;
      time_end = time_start + ONE_DAY * 14;
      break;
    case REPORT_RANGE_LAST_TWO_WEEKS:
      time_start = now - ONE_DAY * tm->tm_wday - ONE_DAY * 14;
      time_end = now - ONE_DAY * tm->tm_wday - ONE_DAY;
      break;
    case REPORT_RANGE_THIS_MONTH:
      time_start = now - ( ONE_DAY * ( tm->tm_mday - 1 ) );
      time_end = now;
      break;
    case REPORT_RANGE_LAST_MONTH:
      if ( tm->tm_mon == 0 )
        time_start = now - ( ONE_DAY * ( tm->tm_mday - 1 ) ) - ONE_DAY * 31;
      else if ( tm->tm_year % 4 == 0 )
        time_start = now - ( ONE_DAY * ( tm->tm_mday - 1 ) ) -
          ONE_DAY * lmonth_days[tm->tm_mon-1];
      else
        time_start = now - ( ONE_DAY * ( tm->tm_mday - 1 ) ) -
          ONE_DAY * month_days[tm->tm_mon-1];
      time_end = now - ONE_DAY * tm->tm_mday;
      break;
    case REPORT_RANGE_THIS_YEAR:
      time_start = now - ONE_DAY * tm->tm_yday;
      time_end = now;
      break;
    case REPORT_RANGE_LAST_YEAR:
      if ( (tm->tm_year - 1) % 4 == 0 )
        time_start = now - ONE_DAY * ( tm->tm_yday + 366 );
      else
        time_start = now - ONE_DAY * ( tm->tm_yday + 365 );
      time_end = now - ONE_DAY * ( tm->tm_yday + 1 );
      break;
    default:
    case REPORT_RANGE_THIS_WEEK:
      time_start = now - ONE_DAY * tm->tm_wday;
      time_end = now;
      break;
  }

  if ( format == REPORT_OUTPUT_HTML ) {
    fprintf ( fp, "<html><head><title>GTimer %s</title></head>\n",
      gettext("Report") );
    fprintf ( fp, "%s", CSS_STYLE );
    fprintf ( fp, "<body>\n" );
    fprintf ( fp, "<table>\n" );
    ncols = 1;
    if ( rd->include_hours )
      ncols++;
    if ( rd->include_annotations )
      ncols++;
  }

#if PV_DEBUG
  g_message("Report checkpoint \"total\"");
#endif


  total = 0;
  for ( time_loop = time_start; time_loop <= time_end; time_loop += ONE_DAY ) {
    total += summarize_day ( fp, seltasks, num_selected, time_loop, rd->type,
      format, rd->include_hours, rd->include_annotations, round_incr,
      ( time_loop == time_end ) );
  }

  h = total / 3600;
  m = ( total - h * 3600 ) / 60;
  s = total % 60;

  switch ( format ) {
    case REPORT_OUTPUT_TEXT:
      fprintf ( fp, "\n%s\n-------------------------------------\n",
        gettext("Grand Total") );
      fprintf ( fp, "%3d:%02d:%02d\n", h, m, s );
      break;
    case REPORT_OUTPUT_HTML:
      fprintf ( fp, "<tr><th colspan=\"%d\">%s</th></tr>",
        1 + ncols, gettext("Grand Total") );
      fprintf ( fp, "<tr><td align=\"right\"><b>%d:%02d:%02d</b></td></tr>",
        h, m, s );
      fprintf ( fp, "</table><p><hr><font size=\"-1\">%s ",
        gettext("Generated by") );
      fprintf ( fp, "<a href=\"%s\">GTimer v%s (%s)</a>.\n",
        GTIMER_URL, GTIMER_VERSION, GTIMER_VERSION_DATE );
      fprintf ( fp, "</body></html>\n" );
      break;
  }

#if PV_DEBUG
  g_message("Report checkpoint (total printed)");
#endif


  len = ftell(fp);
  rewind ( fp );
  text = (char *) calloc ( len + 1, sizeof(char) );
  fread ( text, len, sizeof(char), fp );
  fclose ( fp );
  switch ( format ) {
    case REPORT_OUTPUT_TEXT:
      display_text_results ( text );
      break;
    case REPORT_OUTPUT_HTML:
      display_html_results ( text );
      break;
  }
#if PV_DEBUG
  g_message("Report checkpoint 99%");
#endif

  /* Free resources */
  for ( loop = 0; loop < num_selected; loop++ )
    free ( seltasks[loop] );
  free ( seltasks );
  free ( rd->tasks );
  free ( rd->list_items );
  free ( rd );
#if PV_DEBUG
  g_message("Report end");
#endif

}


static void cancel_callback ( widget, data )
GtkWidget *widget;
gpointer data;
{
  ReportData *rd = (ReportData *) data;
  gtk_grab_remove ( rd->window );
  gtk_widget_destroy ( rd->window );
  free ( rd->tasks );
  free ( rd->list_items );
  free ( rd );
}



static void select_all_callback ( widget, data )
GtkWidget *widget;
gpointer data;
{
  ReportData *rd = (ReportData *) data;
  int loop;

  gtk_tree_selection_select_all (
     gtk_tree_view_get_selection ( GTK_TREE_VIEW (rd->task_list) ) ) ;
//  for ( loop = 0; loop < rd->num_tasks; loop++ )
//    gtk_list_select_item ( GTK_LIST ( rd->task_list ), loop );

}



static void select_none_callback ( widget, data )
GtkWidget *widget;
gpointer data;
{
  ReportData *rd = (ReportData *) data;
  int loop;

  gtk_tree_selection_unselect_all (
     gtk_tree_view_get_selection ( GTK_TREE_VIEW (rd->task_list) ) ) ;
//  for ( loop = 0; loop < rd->num_tasks; loop++ )
//    gtk_list_unselect_item ( GTK_LIST ( rd->task_list ), loop );

}




static GtkWidget *create_time_menu ( rd )
ReportData *rd;
{
  GtkWidget *menu;
  int loop;

  menu = gtk_menu_new ();

  for ( loop = 0; time_options[loop]; loop++ ) {
    rd->time_menu_items[loop] =
      gtk_menu_item_new_with_label ( gettext(time_options[loop]) );
    gtk_menu_append ( GTK_MENU ( menu ), rd->time_menu_items[loop] );
    gtk_widget_show ( rd->time_menu_items[loop] );
  }
  return ( menu );
}




static GtkWidget *create_output_menu ( rd )
ReportData *rd;
{
  GtkWidget *menu;
  int loop;

  menu = gtk_menu_new ();

  for ( loop = 0; output_options[loop]; loop++ ) {
    rd->output_menu_items[loop] =
      gtk_menu_item_new_with_label ( gettext(output_options[loop]) );
    gtk_menu_append ( GTK_MENU ( menu ), rd->output_menu_items[loop] );
    gtk_widget_show ( rd->output_menu_items[loop] );
  }
  return ( menu );
}




static GtkWidget *create_data_menu ( rd )
ReportData *rd;
{
  GtkWidget *menu;
  int loop;

  menu = gtk_menu_new ();

  for ( loop = 0; data_options[loop]; loop++ ) {
    rd->data_menu_items[loop] =
      gtk_menu_item_new_with_label ( gettext(data_options[loop]) );
    gtk_menu_append ( GTK_MENU ( menu ), rd->data_menu_items[loop] );
    gtk_widget_show ( rd->data_menu_items[loop] );
  }
  return ( menu );
}


static GtkWidget *create_round_menu ( rd )
ReportData *rd;
{
  GtkWidget *menu;
  int loop;

  menu = gtk_menu_new ();

  for ( loop = 0; round_options[loop]; loop++ ) {
    rd->round_menu_items[loop] =
      gtk_menu_item_new_with_label ( gettext(round_options[loop]) );
    gtk_menu_append ( GTK_MENU ( menu ), rd->round_menu_items[loop] );
    gtk_widget_show ( rd->round_menu_items[loop] );
  }
  return ( menu );
}



/*
** Create the report setup window.
** It's an add if taskdata is NULL.
*/
GtkWidget *create_report_window ( type )
report_type type;
{
  GtkWidget *report_window;
  GtkWidget *table;
  /*GtkTooltips *tooltips;*/
  GtkWidget *label, *button, *time_menu, *output_menu,
    *data_menu, *round_menu, *scrolled;
  ReportData *rd;
  GList *items = NULL;
  int loop;
  char msg[100], temp[512];
  // PV:
  CustomList *customlist;
  //  GtkWidget *select;
  GtkCellRenderer *renderer;
  GtkTreeViewColumn *col;
//  GtkTreePath *path;
  GtkTreeSelection *select;
  gchar *xtaskname;

#if PV_DEBUG
  g_message("Report window: start");
#endif

  customlist = custom_list_new();

  rd = (ReportData *) malloc ( sizeof ( ReportData ) );
#if PV_DEBUG
  g_message("rd: %d", (int) rd);
#endif

  memset ( rd, '\0', sizeof ( ReportData ) );
  rd->type = type;
  rd->window = report_window = gtk_dialog_new ();
  gtk_window_set_wmclass ( GTK_WINDOW ( rd->window ), "GTimer", "gtimer" );
  sprintf ( msg, "GTimer: %s", gettext("Report") );
  gtk_window_set_title (GTK_WINDOW (report_window), msg );
  gtk_window_position ( GTK_WINDOW(report_window), GTK_WIN_POS_MOUSE );
  gtk_grab_add ( report_window );
  gtk_widget_realize ( report_window );
  gdk_window_set_icon ( GTK_WIDGET ( rd->window )->window,
    NULL, appicon2, appicon2_mask );

#if PV_DEBUG
  g_message("Report window: CP 1");
#endif


  table = gtk_table_new ( 4, 2, FALSE );
  gtk_table_set_row_spacings (GTK_TABLE (table), 4);
  gtk_table_set_col_spacings (GTK_TABLE (table), 8);
  gtk_container_border_width (GTK_CONTAINER (table), 6);
  gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG (report_window)->vbox ),
    table, TRUE, TRUE, 2 );

  label = gtk_label_new ( "Time Range: " );
  gtk_table_attach_defaults ( GTK_TABLE (table), label, 0, 1, 0, 1 );
  gtk_widget_show ( label );

  rd->time_menu = gtk_option_menu_new ();
  time_menu = create_time_menu ( rd );
  gtk_option_menu_set_menu ( GTK_OPTION_MENU ( rd->time_menu ), time_menu );
  gtk_option_menu_set_history ( GTK_OPTION_MENU ( rd->time_menu ),
    REPORT_RANGE_THIS_MONTH );
  gtk_table_attach_defaults ( GTK_TABLE (table), rd->time_menu, 1, 2, 0, 1 );
  gtk_widget_show ( rd->time_menu );


#if PV_DEBUG
  g_message("Report window: CP 2");
#endif

  label = gtk_label_new ( gettext("Format: ") );
  gtk_table_attach_defaults ( GTK_TABLE (table), label, 0, 1, 1, 2 );
  gtk_widget_show ( label );

  rd->output_menu = gtk_option_menu_new ();
  output_menu = create_output_menu ( rd );
  gtk_option_menu_set_menu ( GTK_OPTION_MENU ( rd->output_menu ), output_menu );
  gtk_option_menu_set_history ( GTK_OPTION_MENU ( rd->output_menu ),
    REPORT_OUTPUT_TEXT );
  gtk_table_attach_defaults ( GTK_TABLE (table), rd->output_menu, 1, 2, 1, 2 );
  gtk_widget_show ( rd->output_menu );

  label = gtk_label_new ( gettext("Data: ") );
  gtk_table_attach_defaults ( GTK_TABLE (table), label, 0, 1, 2, 3 );
  gtk_widget_show ( label );

  rd->data_menu = gtk_option_menu_new ();
  data_menu = create_data_menu ( rd );
  gtk_option_menu_set_menu ( GTK_OPTION_MENU ( rd->data_menu ), data_menu );
  gtk_option_menu_set_history ( GTK_OPTION_MENU ( rd->data_menu ),
    REPORT_OUTPUT_TEXT );
  gtk_table_attach_defaults ( GTK_TABLE (table), rd->data_menu, 1, 2, 2, 3 );
  gtk_widget_show ( rd->data_menu );

  label = gtk_label_new ( gettext("Rounding: ") );
  gtk_table_attach_defaults ( GTK_TABLE (table), label, 0, 1, 3, 4 );
  gtk_widget_show ( label );

  rd->round_menu = gtk_option_menu_new ();
  round_menu = create_round_menu ( rd );
  gtk_option_menu_set_menu ( GTK_OPTION_MENU ( rd->round_menu ), round_menu );
  gtk_option_menu_set_history ( GTK_OPTION_MENU ( rd->round_menu ),
    REPORT_OUTPUT_TEXT );
  gtk_table_attach_defaults ( GTK_TABLE (table), rd->round_menu, 1, 2, 3, 4 );
  gtk_widget_show ( rd->round_menu );

  gtk_widget_show ( table );


#if PV_DEBUG
  g_message("Report window: CP 3");
#endif


  label = gtk_label_new ( gettext("Tasks to include:") );
  gtk_label_set_justify ( GTK_LABEL ( label ), GTK_JUSTIFY_LEFT );
  gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG (report_window)->vbox ),
    label, FALSE, FALSE, 2 );
  gtk_widget_show ( label );

  /* list of tasks */
// PV: GTK_LIST --> GTK_TREE_VIEW
  scrolled = gtk_scrolled_window_new ( NULL, NULL );
  gtk_widget_set_usize ( scrolled, 300, 200 );
  gtk_scrolled_window_set_policy ( GTK_SCROLLED_WINDOW (scrolled),
    GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS );
  gtk_box_pack_start ( GTK_BOX ( GTK_DIALOG (report_window)->vbox ),
    scrolled, TRUE, TRUE, 2 );

// PV: -  rd->task_list = gtk_list_new ();
  rd->tasks = (TaskData **) malloc ( sizeof ( TaskData * ) *
    num_visible_tasks );
  rd->list_items = (GtkTreePath **) malloc ( sizeof ( GtkTreePath * ) *
    num_visible_tasks );
  rd->num_tasks = num_visible_tasks;

  for ( loop = 0; loop < num_visible_tasks; loop++ ) {
    if ( visible_tasks[loop]->task->project_id < 0 )
      snprintf ( temp, sizeof ( temp ), "%s",
         visible_tasks[loop]->task->name );
    else
      snprintf ( temp, sizeof ( temp ),
        "[%s] %s", visible_tasks[loop]->project_name,
        visible_tasks[loop]->task->name );
    items = g_list_append ( items, rd->list_items[loop] );
    rd->tasks[loop] = visible_tasks[loop];
    xtaskname = g_strdup_printf ("%s", temp);
    rd->list_items[loop] = custom_list_append_record (customlist, xtaskname);
  }


#if PV_DEBUG
  g_message("Report window: CP 4");
#endif

  rd->task_list = gtk_tree_view_new_with_model(GTK_TREE_MODEL(customlist));
  g_object_unref(customlist); /* destroy store automatically with view */

  renderer = gtk_cell_renderer_text_new();
  col = gtk_tree_view_column_new();

  gtk_tree_view_column_pack_start (col, renderer, TRUE);
  gtk_tree_view_column_add_attribute (col, renderer, "text", CUSTOM_LIST_COL_NAME);
  gtk_tree_view_column_set_title (col, gettext("[Project] Task"));
  gtk_tree_view_append_column(GTK_TREE_VIEW(rd->task_list),col);
  select = gtk_tree_view_get_selection(GTK_TREE_VIEW(rd->task_list));
  gtk_tree_selection_set_mode(GTK_TREE_SELECTION(select), GTK_SELECTION_MULTIPLE);

  gtk_scrolled_window_add_with_viewport ( GTK_SCROLLED_WINDOW ( scrolled ),
    rd->task_list );

// PV: select all
  gtk_tree_selection_select_all(GTK_TREE_SELECTION(select));

  gtk_widget_show ( scrolled );
  gtk_widget_show ( rd->task_list );
  
  /* add command buttons */
  /*tooltips = gtk_tooltips_new ();*/

  gtk_box_set_homogeneous ( GTK_BOX ( GTK_DIALOG (report_window)->action_area ),
    1 );
  button = gtk_button_new_with_label ( gettext("Ok") );
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (report_window)->action_area),
    button, TRUE, FALSE, 2);
  gtk_box_set_spacing ( GTK_BOX (GTK_DIALOG (report_window)->action_area),
    2 );
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
    GTK_SIGNAL_FUNC (ok_callback), rd);
  GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
  gtk_widget_grab_default (button);
  gtk_widget_show (button);
  /*gtk_tooltips_set_tips (tooltips, button, "Generate a report" );*/

  button = gtk_button_new_with_label ( gettext("Cancel") );
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (report_window)->action_area),
    button, TRUE, FALSE, 2);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
    GTK_SIGNAL_FUNC (cancel_callback), rd);
  gtk_widget_show (button);
  /*gtk_tooltips_set_tips (tooltips, button,
    "Cancel without generating a report" );*/

  button = gtk_button_new_with_label ( gettext("Select all") );
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (report_window)->action_area),
    button, TRUE, FALSE, 2);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
    GTK_SIGNAL_FUNC (select_all_callback), rd);
  gtk_widget_show (button);
  /*gtk_tooltips_set_tips (tooltips, button, "Select all tasks" );*/

  button = gtk_button_new_with_label ( gettext("Select none") );
  gtk_box_pack_start (GTK_BOX (GTK_DIALOG (report_window)->action_area),
    button, TRUE, FALSE, 2);
  gtk_signal_connect (GTK_OBJECT (button), "clicked",
    GTK_SIGNAL_FUNC (select_none_callback), rd);
  gtk_widget_show (button);
  /*gtk_tooltips_set_tips (tooltips, button, "Unselect all tasks" );*/

  gtk_widget_show_all (report_window);


#if PV_DEBUG
  g_message("Report window: CP 100");
#endif


  return ( report_window );
}




