Logo Search packages:      
Sourcecode: pan version File versions  Download package

status-item-view.c

/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
/*
 * Pan - A Newsreader for Gtk+
 * Copyright (C) 2002  Charles Kerr <charles@rebelbase.com>
 *
 * 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; version 2 of the License.
 *
 * 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
 */

/*********************
**********************  Includes
*********************/ 

#include <config.h>

#include <string.h>
#include <stdlib.h>

#include <glib.h>
#include <gdk/gdkkeysyms.h>
#include <gtk/gtk.h>

#include <pan/base/argset.h>
#include <pan/base/debug.h>

#include <pan/status-item-view.h>
#include <pan/util.h>

/*********************
**********************  Defines / Enumerated types
*********************/

/*********************
**********************  Macros
*********************/

/*********************
**********************  Structures / Typedefs
*********************/

typedef struct
{
      guint signal_id_progress_set_percent;
      guint signal_id_status;
      guint signal_id_error;

      GMutex * last_status_mutex;
      GString * last_status;
      StatusItem * item;

      GtkWidget * progress;
}
Impl;

/*********************
**********************  Private Function Prototypes
*********************/

static void status_item_view_unregister (GtkWidget *view);

static void progress_cb      (gpointer, gpointer, gpointer);
static void status_cb        (gpointer, gpointer, gpointer);

/*********************
**********************  Variables
*********************/

/***********
************  Extern
***********/

extern GtkTooltips * ttips;                                                                                      
/***********
************  Public
***********/

/***********
************  Private
***********/

/*********************
**********************  BEGINNING OF SOURCE
*********************/

/************
*************  PRIVATE ROUTINES
************/

/*****
******  Private Utilities
*****/

static char*
string_shorten_nolock (GtkWidget        * w,
                       int                max_width,
                       const char       * original,
                       int                original_len)
{
      gboolean i_turn = TRUE;
      int i, j, len, original_midpoint, width=0;
      const int target_width = max_width - 4 - 30; /* 30 is just a magic safety number */
      GString * tmp;
      PangoLayout * pango_layout;
      const char * cpch_midpoint;

      /* handle cases where the string isn't too long */
      if (!original)
            return NULL;

      pango_layout = gtk_widget_create_pango_layout (w, NULL);
      pango_layout_set_text (pango_layout, original, original_len);
      pango_layout_get_pixel_size (pango_layout, &width, NULL);
      if (width < target_width) {
            g_object_unref (G_OBJECT(pango_layout));
            return g_strndup (original, original_len);
      }

      /* build the clone with elipses in the middle */
      len = g_utf8_strlen (original, original_len);
      original_midpoint = len / 2;
      cpch_midpoint = g_utf8_offset_to_pointer (original, original_midpoint);
            tmp = g_string_sized_new (128);
      g_string_append_len (tmp, original, cpch_midpoint - original);
      g_string_append (tmp, "...");
      g_string_append (tmp, cpch_midpoint);

      /* start trimming out the characters next to the ellipses */
      i = original_midpoint;
      j = original_midpoint + 2; /* "..." */
      for (;;)
      {
            int * pi;
            const char * begin, * end;

            /* check the width */
            pango_layout_set_text (pango_layout, tmp->str, tmp->len);
            pango_layout_get_pixel_size (pango_layout, &width, NULL);
            if (width < target_width)
                  break;

            pi = i_turn ? &i : &j;
            begin = g_utf8_offset_to_pointer (tmp->str, *pi);
            end = i_turn ? g_utf8_prev_char (begin) : g_utf8_next_char (begin);

            *pi -= 1;
            if (*pi<0 || *pi>=len)
                  break;
            g_string_erase (tmp, (i_turn ? end : begin) - tmp->str, abs (end-begin));
            len--;
            i_turn = !i_turn;
      }

      g_object_unref (G_OBJECT(pango_layout));
      return g_string_free (tmp, FALSE);
}

/*****
******  Callback Handlers
*****/

static int
set_percentage_mainthread (gpointer user_data)
{
      ArgSet * argset = (ArgSet*) user_data;
      GtkProgress * progress = GTK_PROGRESS(argset_get (argset, 0));
      const int of_100 = GPOINTER_TO_INT (argset_get (argset, 1));

      /* update the percentage */
      pan_lock ();
      gtk_progress_set_percentage (progress, of_100/100.0);
      pan_unlock ();

      /* cleanup */
      argset_free (argset);
      return 0;
}

static void
progress_cb (gpointer call_object, gpointer call_arg, gpointer user_data)
{
      Impl * impl = (Impl *) user_data;
      const int of_100 = GPOINTER_TO_INT(call_arg);
      if (0<=of_100 && of_100<=100)
            gui_queue_add (set_percentage_mainthread, argset_new2 (impl->progress, GINT_TO_POINTER(of_100)));
}

static int
status_cb_mainthread (gpointer data)
{
      Impl * impl;

      pan_lock ();
      impl = (Impl*) data;

      if (impl!=NULL && impl->item!=NULL)
      {
            GtkProgress * prog = GTK_PROGRESS(impl->progress);
            char * s;

            g_mutex_lock (impl->last_status_mutex);
            s = string_shorten_nolock (impl->progress,
                                       impl->progress->allocation.width,
                                       impl->last_status->str,
                                       impl->last_status->len);
            g_mutex_unlock (impl->last_status_mutex);

            /* update progressbar w/o a resize */
            replace_gstr (&prog->format, s);
            GTK_PROGRESS_GET_CLASS (GTK_OBJECT(prog))->update (prog);
      }
      pan_unlock ();

      return 0;
}

static void
status_cb (gpointer call_object, gpointer call_arg, gpointer user_data)
{
      Impl * impl = (Impl*) user_data;

      g_mutex_lock (impl->last_status_mutex);
      g_string_assign (impl->last_status, (const char*) call_arg);
      g_mutex_unlock (impl->last_status_mutex);

      gui_queue_add (status_cb_mainthread, impl);
}

/*****
******  Life Cycle
*****/

static void
status_item_view_unregister (GtkWidget * w)
{
      Impl * impl;

      /* sanity clause */
      g_return_if_fail (GTK_IS_WIDGET(w));
      impl = (Impl*) g_object_get_data (G_OBJECT(w), "impl");
      g_return_if_fail (impl != NULL);

      if (impl->item != NULL)
      {
            /* stop listening to the item... */
            pan_callback_remove (impl->item->progress, progress_cb, impl);
            pan_callback_remove (impl->item->status, status_cb, impl);
            pan_callback_remove (impl->item->error, status_cb, impl);

            /* unref the item... */
            pan_object_unref(PAN_OBJECT(impl->item));
            impl->item = NULL;
      }
}

static void
destroy_cb (GtkWidget* w)
{
      Impl * impl;

      status_item_view_unregister (w);

      impl = (Impl*) g_object_get_data (G_OBJECT(w), "impl");
      if (impl != NULL)
      {
            if (impl->last_status != NULL)
            {
                  g_string_truncate (impl->last_status, 0);
                  g_string_free (impl->last_status, TRUE);
                  impl->last_status = NULL;
            }

            if (impl->last_status_mutex != NULL)
            {
                  g_mutex_free (impl->last_status_mutex);
                  impl->last_status_mutex = NULL;
            }

            g_free (impl);
            g_object_set_data (G_OBJECT(w), "impl", NULL);
      }
}

/************
*************  PUBLIC ROUTINES
************/

/*****
******
*****/

GtkWidget*
status_item_view_new (void)
{
      GtkWidget * w;
      GtkWidget * progress;
      Impl * impl;

      w = gtk_event_box_new ();

      progress = gtk_progress_bar_new ();
      gtk_progress_set_format_string (GTK_PROGRESS(progress), "");
      gtk_progress_set_show_text (GTK_PROGRESS(progress), TRUE);
      gtk_container_add (GTK_CONTAINER(w), progress);
      gtk_widget_show (progress);

      g_signal_connect (GTK_OBJECT(w), "destroy", G_CALLBACK(destroy_cb), NULL);

      impl = g_new0 (Impl, 1);
      impl->last_status = g_string_new (NULL);
      impl->last_status_mutex = g_mutex_new ();
      impl->progress = progress;
      g_object_set_data (G_OBJECT(w), "impl", impl);
      return w;
}

void
status_item_view_set_item_nolock (GtkWidget * w, StatusItem* item)
{
      Impl * impl;
      debug_enter ("status_item_view_set_item_nolock");

      /* sanity clause */
      g_return_if_fail (GTK_IS_WIDGET(w));
      impl = (Impl*) g_object_get_data (G_OBJECT(w), "impl");
      g_return_if_fail (impl != NULL);

      if (item != impl->item)
      {
            GtkProgress * prog;
            status_item_view_unregister (w);

            /* update last_status; update progressbar w/o a resize */
            gui_queue_add (set_percentage_mainthread, argset_new2 (impl->progress, NULL));
            prog = GTK_PROGRESS(impl->progress);
            g_mutex_lock (impl->last_status_mutex);
            g_string_truncate (impl->last_status, 0);
            g_mutex_unlock (impl->last_status_mutex);
            replace_gstr (&prog->format, g_strdup(""));
            GTK_PROGRESS_GET_CLASS (GTK_OBJECT(prog))->update (prog);
            gtk_tooltips_set_tip (ttips, w, NULL, NULL);

            if (item != NULL)
            {
                  char * description = status_item_describe (item);
                  pan_object_ref (PAN_OBJECT(item));
                  pan_callback_add (item->progress, progress_cb, impl);
                  pan_callback_add (item->status, status_cb, impl);
                  pan_callback_add (item->error, status_cb, impl);
                  gtk_tooltips_set_tip (ttips, w, description, NULL);
                  g_free (description);
            }

            impl->item = item;

            /* show the last status text */
            if (item !=NULL)
            {
                  char * pch = item->status_text;
                  if (is_nonempty_string(pch))
                        status_cb (NULL, pch, impl);
            }
      }

      debug_exit ("status_item_view_set_item_nolock");
}

Generated by  Doxygen 1.6.0   Back to index