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

util.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
 */

#include <config.h>

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

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

#include <pan/base/argset.h>
#include <pan/base/debug.h>
#include <pan/base/pan-config.h>
#include <pan/base/pan-glib-extensions.h>
#include <pan/base/pan-i18n.h>
#include <pan/base/log.h>

#include <pan/globals.h>
#include <pan/prefs.h>
#include <pan/util.h>

/***
****
****  DIALOG
****
***/

typedef struct
{
      GtkWindow * window;
      GtkMessageType type;
      char * text;
}
IdleDialogStruct;

static int
pan_dialog_idle (gpointer user_data)
{
      GtkWidget * w;
      IdleDialogStruct * info = (IdleDialogStruct*) user_data;

      /* build & show the dialog */
      pan_lock();
      if (!GTK_IS_WINDOW(info->window)) /* make info->window is alive */
            info->window = GTK_WINDOW(Pan.window);
      w = gtk_message_dialog_new (GTK_WINDOW(info->window),
                                  GTK_DIALOG_DESTROY_WITH_PARENT,
                                  info->type,
                            GTK_BUTTONS_CLOSE,
                            "%s", info->text);
      g_signal_connect_swapped (GTK_OBJECT(w), "response",
                                G_CALLBACK (gtk_widget_destroy),
                                GTK_OBJECT(w));
      gtk_widget_show_all (w);
      pan_unlock();

      /* cleanup */
      g_free (info->text);
      g_free (info);
      return 0;
}

static void
pan_dialog (char * i_own_it_str, GtkMessageType type, GtkWindow * window)
{
      IdleDialogStruct * info = g_new (IdleDialogStruct, 1);
      info->text = i_own_it_str;
      info->type = type;
      info->window = window;
      gui_queue_add (pan_dialog_idle, info);
}
void
pan_info_dialog (const char *format, ...)
{
      va_list args;
      char *str = NULL;
      g_return_if_fail (format != NULL);
      va_start (args, format);
      str = g_strdup_vprintf (format, args);
      va_end (args);
      g_warning (str);

      pan_dialog (str, GTK_MESSAGE_INFO, GTK_WINDOW(Pan.window));
}

void
pan_error_dialog_parented (gpointer parent, const char *format, ...)
{
      va_list args;
      char *str = NULL;
      g_return_if_fail (format != NULL);
      va_start (args, format);
      str = g_strdup_vprintf (format, args);
      va_end (args);
      g_warning (str);

      pan_dialog (str, GTK_MESSAGE_ERROR, GTK_WINDOW(parent));
}

void
pan_error_dialog (const char *format, ...)
{
      va_list args;
      char *str = NULL;
      g_return_if_fail (format != NULL);
      va_start (args, format);
      str = g_strdup_vprintf (format, args);
      va_end (args);
      g_warning (str);

      pan_dialog (str, GTK_MESSAGE_ERROR, GTK_WINDOW(Pan.window));
}

/***
****
****  IDLE FUNC
****
***/

/**
 * Thus spoke the GTK FAQ: "Callbacks require a bit of attention.
 * Callbacks from GTK+ (signals) are made within the GTK+ lock. However
 * callbacks from GLib (timeouts, IO callbacks, and idle functions) are
 * made outside of the GTK+ lock. So, within a signal handler you do not
 * need to call gdk_threads_enter(), but within the other types of
 * callbacks, you do."
 *
 * pan_timeout_add() is a wrapper around a glib-level callbacks that
 * ensures pan_lock() works properly to a gdk_threads_enter() lock.
 */

static gboolean main_thread_is_in_glib_callback = FALSE;

static gint
pan_timeout_wrapper (gpointer data)
{
      ArgSet * argset = (ArgSet*) data;
      GtkFunction func = (GtkFunction) argset_get (argset, 0);
      gpointer user_data = (gpointer) argset_get (argset, 1);
      gint retval;

      /* sanity clause */
      pan_warn_if_fail (g_thread_self() == Pan.main_t);

      /* call the timer func */
      main_thread_is_in_glib_callback = TRUE;
      retval = (*func)(user_data);
      main_thread_is_in_glib_callback = FALSE;

      /* cleanup */
      if (!retval)
            argset_free (argset);
      return retval;
}

guint
pan_timeout_add (guint32 interval, GSourceFunc func, gpointer arg)
{
      return g_timeout_add_full (G_PRIORITY_DEFAULT_IDLE,
                                 interval,
                                 pan_timeout_wrapper,
                                 argset_new2 (func, arg),
                                 (GDestroyNotify)argset_free);
}

/***
****  GUI QUEUE
***/

static unsigned int gui_queue_timer_id = 0;
static GStaticMutex gui_queue_mutex = G_STATIC_MUTEX_INIT;
static GSList * gui_queue_slist = NULL;

typedef struct
{
      GSourceFunc run_func;
      gpointer user_data;
      GDestroyNotify remove_func;
      guint item_id;
}
GuiQueueItem;

static int
gui_queue_timer_cb (gpointer user_data)
{
      GSList * list;

      /* get the work list */
      g_static_mutex_lock (&gui_queue_mutex);
      list = gui_queue_slist;
      gui_queue_slist = NULL;
      g_static_mutex_unlock (&gui_queue_mutex);

      if (list != NULL)
      {
            GSList * l;

            /* gui_queue_slist is LIFO for efficiency of adding nodes;
             * reverse the nodes to make list FIFO */
            list = g_slist_reverse (list);

            /* run the work functions */
            for (l=list; l!=NULL; l=l->next)
            {
                  GuiQueueItem * queue_item = (GuiQueueItem*) l->data;
                  (*queue_item->run_func)(queue_item->user_data);
                  g_free (queue_item);
            }
            g_slist_free (list);
      }

      return 1;
}

void
gui_queue_init (void)
{
      gui_queue_timer_id = pan_timeout_add (200, gui_queue_timer_cb, NULL);
}

void
gui_queue_shutdown (void)
{
      g_source_remove (gui_queue_timer_id);
      gui_queue_timer_id = 0;
}

static gboolean
find_node_from_item_id (gconstpointer item_gpointer, gconstpointer id_key_gpointer)
{
      register const GuiQueueItem * item = (const GuiQueueItem *) item_gpointer;
      register const guint id_key = GPOINTER_TO_UINT (id_key_gpointer);

      /* return 0 when the desired element is found */
      return id_key == item->item_id ? 0 : 1;
}

static void
gui_queue_remove_gpointer (gpointer item_id_gpointer)
{
      gui_queue_remove (GPOINTER_TO_UINT (item_id_gpointer));
}

void
gui_queue_remove (guint item_id)
{
      GSList * l = NULL;

      /* remove the item from the list */
      g_static_mutex_lock (&gui_queue_mutex);
      {
            l = g_slist_find_custom (gui_queue_slist, GUINT_TO_POINTER(item_id), find_node_from_item_id);
            if (l != NULL)
                  gui_queue_slist = g_slist_remove_link (gui_queue_slist, l);
      }
      g_static_mutex_unlock (&gui_queue_mutex);

      /* run the remove function */
      if (l != NULL)
      {
            GuiQueueItem * queue_item = (GuiQueueItem*) l->data;
            if (queue_item->remove_func != NULL)
                  (*queue_item->remove_func)(queue_item->user_data);
            g_free (queue_item);
            g_slist_free_1 (l);
      }
}

guint
gui_queue_add (GSourceFunc run_func, gpointer user_data)
{
      return gui_queue_add_full_to_g_object (run_func, user_data, NULL, NULL);
}

guint
gui_queue_add_full (GSourceFunc run_func, gpointer user_data, GDestroyNotify remove_func)
{
      return gui_queue_add_full_to_g_object (run_func, user_data, remove_func, NULL);
}

guint
gui_queue_add_full_to_g_object (GSourceFunc run_func, gpointer user_data, GDestroyNotify remove_func, GObject * object)
{
      guint retval;
      GuiQueueItem * item;

      /* create the new queue item */     
      item = g_new (GuiQueueItem, 1);
      item->run_func = run_func;
      item->user_data = user_data;
      item->remove_func = remove_func;

      /* add it to the queue */
      g_static_mutex_lock (&gui_queue_mutex);
      {
            static guint item_id = 0;
            retval = item->item_id = item_id++;
            gui_queue_slist = g_slist_prepend (gui_queue_slist, item);

            /* are we attaching it to a GObject? */
            if (object!=NULL && G_IS_OBJECT(object))
            {
                  char str [64];
                  g_snprintf (str, sizeof(str), "gui_queue_item_%u", retval); /* make sure the GObject's data key is unique */
                  g_object_set_data_full (object, str, GUINT_TO_POINTER(retval), gui_queue_remove_gpointer);
            }
      }
      g_static_mutex_unlock (&gui_queue_mutex);

      return retval;
}



/***
****
****  LOCKING
****
***/

static const GThread * has_lock_thr = NULL;

void
pan_lock_from (const gchar * file, const gchar * func, int line)
{
      static const gchar * last_file = NULL;
      static const gchar * last_func = NULL;
      static int last_line = -1;
      const GThread * thr = g_thread_self ();

      /**
       * If pan_lock() is called from the main thread while it has a GUI lock
       * (typically from a gtk signals, like a button press signal etc.)
       * then we don't need to lock.
       *
       * However if pan_lock() is called from a worker thread, or the main
       * thread inside a glib idle function (via pan_timeout_add())
       * then we _do_ need to obtain a gtk lock.
       */
      if (thr==Pan.main_t && !main_thread_is_in_glib_callback)
      {
            debug4 (DEBUG_LOCK,"mainthread %p attempted unnecessary lock from %s:%d (%s)", thr, file, line, func);
      }
      else if (thr == has_lock_thr)
      {
            g_error ("thread %p attempted double lock!\nfirst lock was in %s:%d (%s),\nnow trying for another one from %s:%d (%s)",
                  thr,
                  last_file, last_line, last_func,
                  file, line, func);
      }
      else
      {
                  /* if (thr==Pan.main_t && main_thread_is_in_glib_callback)
                  odebug3 ("idle func %s:%d (%s) getting a pan lock", file, line, func);*/

            gdk_threads_enter();
            last_file = file;
            last_func = func;
            last_line = line;
            has_lock_thr = thr;
            debug3 (DEBUG_LOCK,"thread %p entered gdk_threads from %s %d", thr, file, line);
      }
}

void
pan_unlock_from (const gchar* file, const gchar * func, int line)
{
      const GThread* thr = g_thread_self ();

      if (thr==Pan.main_t && !has_lock_thr)
      {
            debug4 (DEBUG_LOCK,"mainthread %p attempted unnecessary unlock from %s:%d (%s)", thr, file, line, func);
      }
      else if (has_lock_thr != thr)
      {
            g_error ("thread %p attempted to remove a lock it didn't have from %s:%d (%s)", thr, file, line, func);
      }
      else
      {
            has_lock_thr = NULL;
            gdk_threads_leave();
            debug4 (DEBUG_LOCK,"thread %p left gdk_threads from %s:%d (%s)", g_thread_self(), file, line, func);
      }
}

/***
****
****  UNSORTED
****
***/

char *
get_date_display_string (time_t date, const char * fmt)
{
      char buf[512];
      char *freeme;
      struct tm tm_date;
      const char *cpch;

      /* sanity clause */
      g_return_val_if_fail (is_nonempty_string(fmt), NULL);

      /* get the date string */
      pan_localtime_r (&date, &tm_date);

      /* try to put the date in the buffer */
      if (!strftime (buf, sizeof buf, fmt, &tm_date))
            *buf = '\0';

      cpch = pan_utf8ize (buf, -1, &freeme);
      replace_gstr (&freeme, g_strdup (cpch));

      return freeme;
}

static gboolean
try_to_execute_url (const char * template, const char * url)
{
      int i;
      int argc = 0;
      char * freeme;
      char ** argv = NULL;
      gboolean ok = TRUE;

      /* sanity clause */
      g_return_val_if_fail (is_nonempty_string(template), FALSE);
      g_return_val_if_fail (is_nonempty_string(url), FALSE);

      /* make sure the backslashes are escaped */
      freeme = pan_substitute (template, "\\", "\\\\");
      template = freeme;
      debug1 (DEBUG_TRACE, "about to parse the command [%s]", template);

      /* parse the command line */
      if (ok) {
            GError * err = NULL;
            g_shell_parse_argv (template, &argc, &argv, &err);
            if (err != NULL) {
                  log_add_va (LOG_ERROR, _("Error parsing \"web browser\" command line: %s"), err->message);
                  log_add_va (LOG_ERROR, _("The command line was: %s"), template);
                  g_error_free (err);
                  ok = FALSE;
            }
      }

      /* substitute in the URL _after_ parsing the command line:
         path slashes and backslashes in the URL confuse g_shell_parse_argv. */
      for (i=0; i<argc; ++i)
            if (strstr (argv[i], "%s") != NULL) /* filename */
                  replace_gstr (&argv[i], pan_substitute (argv[i], "%s", url));

      /* spawn off the external editor */
      if (ok) {
            GError * err = NULL;
            g_spawn_async (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, &err);
            if (err != NULL) {
                  log_add_va (LOG_ERROR, _("Error starting external browser: %s"), err->message);
                  g_error_free (err);
                  ok = FALSE;
            }
      }

      /* cleanup */
      g_free (freeme);
      g_strfreev (argv);
      return ok;
}


/**
 * http://www.catb.org/~esr/BROWSER/
 *
 * The value of BROWSER may consist of a colon-separated series of browser
 * command parts.  These should be tried in order until one succeeds. Each
 * command part may optionally contain the string "%s"; if it does, the URL
 * to be viewed is substituted there. If a command part does not contain %s,
 * the browser is to be launched as if the URL had been supplied as its first
 * argument. The string %% must be substituted as a single %.
 *
 * Pan issues: on Windows, ':' is typically embedded in the pathname, so it's
 * a poor choice for browser command delimiters there.  On Windows, use ';'
 * instead.
 */

#ifdef G_OS_WIN32
#define BROWSER_DELIMITER ';'
#else
#define BROWSER_DELIMITER ':'
#endif

void
pan_url_show (const char * url)
{
      const char * browser_string;
      const char * march;
      GString * gstr;

      /* sanity clause */
      g_return_if_fail (is_nonempty_string(url));

      /* if the user has a browser defined, use it; "BROWSER" is fallback #1; "mozilla %s" is fallback #2 */
      browser_string = external_web_browser;
      if (!is_nonempty_string (browser_string))
            browser_string = g_getenv ("BROWSER");
      if (!is_nonempty_string (browser_string))
            browser_string = "mozilla %s";
      debug1 (DEBUG_TRACE, "browser_string is [%s]", browser_string);

      /* walk through the string, trying different browser commands until one works */
      march = browser_string;
      gstr = g_string_new (NULL);
      while (get_next_token_g_str (march, BROWSER_DELIMITER, &march, gstr))
      {
            debug1 (DEBUG_TRACE, "parsed [%s] out of browser string", gstr->str);

            if (strstr (gstr->str, "%s") == NULL)
                  g_string_append (gstr, " %s");
            
            if (try_to_execute_url (gstr->str, url))
                  break;
      }

      g_string_free (gstr, TRUE);
}


/***
****  Menu Building
***/

static char*
menu_translate(const char* path, gpointer data)
{
      return gettext (path);
}

GtkWidget*
menubar_create (GtkWidget            * window,
                GtkItemFactoryEntry  * entries,
                guint                  entries_qty,
                const char           * path,
                gpointer               data)
{
      GtkItemFactory *factory;
      GtkAccelGroup *accel_group;

      accel_group = gtk_accel_group_new ();
      gtk_window_add_accel_group (GTK_WINDOW (window), accel_group);
      g_object_unref (accel_group);

      factory = gtk_item_factory_new(GTK_TYPE_MENU_BAR, path, accel_group);
      gtk_item_factory_set_translate_func(factory, menu_translate, NULL, NULL);
      gtk_item_factory_create_items(factory, entries_qty, entries, data);

      return gtk_item_factory_get_widget (factory, path);
}

GtkWidget*
menu_create_items (GtkItemFactoryEntry   * entries,
                   guint                   entries_qty,
                   const char            * path,
                   GtkItemFactory       ** factory,
                   gpointer                data)
{
      *factory = gtk_item_factory_new(GTK_TYPE_MENU, path, NULL);
      gtk_item_factory_set_translate_func(*factory, menu_translate, NULL, NULL);
      gtk_item_factory_create_items(*factory, entries_qty, entries, data);

      return gtk_item_factory_get_widget(*factory, path);
}

void
menu_set_sensitive (GtkItemFactory   * ifactory,
                    const char       * path,
                    gboolean           sensitive)
{
      GtkWidget *widget;

      g_return_if_fail(ifactory != NULL);

      widget = gtk_item_factory_get_item (ifactory, path);
      if (widget == NULL)
            g_error ("couldn't find menu item \"%s\"", path);

      gtk_widget_set_sensitive (widget, sensitive);
}

void
menu_set_checked (GtkItemFactory   * ifactory,
                  const char       * path,
                  gboolean           active)
{
      GtkCheckMenuItem * w;

      g_return_if_fail (ifactory != NULL);

      w = GTK_CHECK_MENU_ITEM (gtk_item_factory_get_item (ifactory, path));
      if (w == NULL)
            g_error ("couldn't find menu item \"%s\"", path);
      else if (!!w->active != !!active)
            gtk_check_menu_item_set_active (w, active);
}

void
pan_gtk_entry_set_text (GtkWidget            * w,
                        const char           * text)
{
      char * pch;

      g_return_if_fail (GTK_IS_ENTRY(w));
      pch = pan_header_to_utf8 (text, -1, NULL);
      gtk_entry_set_text (GTK_ENTRY(w), pch ? pch : "");
      g_free (pch);
}

void
pan_widget_set_font (GtkWidget * w, const char * font_name)
{
      PangoFontDescription * font_desc;

      g_return_if_fail (GTK_IS_WIDGET(w));
      g_return_if_fail (is_nonempty_string(font_name));

      font_desc = pango_font_description_from_string (font_name);
      gtk_widget_modify_font (w, font_desc);
      pango_font_description_free (font_desc);
}

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

void
gui_save_column_widths (GtkWidget    * clist,
                        const char   * type)
{
      if (GTK_IS_CLIST (clist))
      {
            int i;
            int cols = GTK_CLIST (clist)->columns;
            GtkCList * list = GTK_CLIST (clist);
            char buf[1024];
            
            for (i=0; i<cols; i++)
            {
                  g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_column_%d", type, i);
                  pan_config_set_int (buf, (int)list->column[i].width);
            }
      }
}

void
gui_save_column_widths_tree_view (GtkWidget    * tree_view,
                          const char   * type)
{
      GList * columns;
      char buf[1024];   
      int i;
      int length;

      debug_enter ("gui_save_column_widths_tree_view");

      columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(tree_view));
      length = g_list_length(columns);

      for (i = 0; i < length; i++) {
            g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_column_%d", type, i);
            pan_config_set_int (buf, (int)gtk_tree_view_column_get_width(gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view), i)));
      }

      /* cleanup */
      g_list_free(columns);

      debug_exit ("gui_save_column_widths_tree_view");      
}


 
void
gui_save_window_size (GtkWidget     * widget,
                      const char    * key)
{
      char buf[1024];
      int x, y, w, h;
      GtkWindow * window;
      GdkWindow *toplevel;
      GdkWindowState state = 0;

      /* sanity clause */
      g_return_if_fail (GTK_IS_WINDOW(widget));
      g_return_if_fail (widget->window!=NULL);
      g_return_if_fail (is_nonempty_string(key));

      window = GTK_WINDOW(widget);
      toplevel = widget->window;
      state = toplevel ? gdk_window_get_state (toplevel) : 0;

      if (state & GDK_WINDOW_STATE_MAXIMIZED)
      {
            g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_max", key);
            pan_config_set_bool (buf, TRUE);
      }
      else
      {
            gtk_window_get_position (window, &x, &y);
            gtk_window_get_size (window, &w, &h);

            g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_max", key);
            pan_config_set_bool (buf, FALSE);
            g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_x", key);
            pan_config_set_int (buf, x);
            g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_y", key);
            pan_config_set_int (buf, y);
            g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_width", key);
            pan_config_set_int (buf, w);
            g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_height", key);
            pan_config_set_int (buf, h);
      }
}

void
gui_restore_column_widths (GtkWidget     * clist,
                           const char   * type)
{
      if (GTK_IS_CLIST (clist))
      {
            int i;
            GtkCList * list = GTK_CLIST (clist);
            const int cols = list->columns;
            int * widths = g_newa (int, cols);
            char buf[1024];
            debug_enter ("gui_restore_column_widths");

            /* get width from config... */
            for (i=0; i!=cols; ++i) {
                  g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_column_%d", type, i);
                  widths[i] = pan_config_get_int (buf, 0);
            }

            /* set ui.. */
            pan_lock();
            for (i=0; i!=cols; ++i)
                  if (widths[i] > 0)
                        gtk_clist_set_column_width (list, i, widths[i]);
            pan_unlock();

            /* cleanup */
            debug_exit ("gui_restore_column_widths");
      }
}

void
gui_restore_column_widths_tree_view (GtkWidget     * tree_view,
                             const char   * type)
{
      GList * columns;
      char buf[1024];   
      int * widths;
      int i;
      int length;
            
      debug_enter ("gui_restore_column_widths_tree_view");
      
      columns = gtk_tree_view_get_columns(GTK_TREE_VIEW(tree_view));
      length = g_list_length(columns);
      widths = g_newa (int, length);
      
      /* get width from config... */
      for (i = 0; i != length; ++i) {
            g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_column_%d", type, i);
            widths[i] = pan_config_get_int (buf, 0);
      }

      /* set ui.. */
      pan_lock();
      for (i = 0; i != length; ++i) {
            if (widths[i] != 0)
                  gtk_tree_view_column_set_fixed_width(gtk_tree_view_get_column(GTK_TREE_VIEW(tree_view), i), widths[i]);
      }
      
      pan_unlock();
      
      /* cleanup */
      g_list_free(columns);
      
      debug_exit ("gui_restore_column_widths_tree_view");
}



gboolean
gui_restore_window_size (GtkWidget     * widget,
                         const char    * key)
{
      int x, y, w, h;
      char buf[1024];
      int screen_w = gdk_screen_width ();
      int screen_h = gdk_screen_height ();
      const int fuzz = 10;
      gboolean retval = FALSE;

      /* sanity clause */
      g_return_val_if_fail (GTK_IS_WIDGET(widget), FALSE);
      g_return_val_if_fail (is_nonempty_string(key), FALSE);

      /* get the dimensions */
      g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_x", key);
      x = pan_config_get_int (buf, -1);
      g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_y", key);
      y = pan_config_get_int (buf, -1);
      g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_width", key);
      w = pan_config_get_int (buf, -1);
      g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_height", key);
      h = pan_config_get_int (buf, -1);

      /* move & resize */
      if ((0<=x && x<screen_w-fuzz) && (0<=y && y<screen_h-fuzz))
            gtk_window_move (GTK_WINDOW(widget), x, y);
      if (w>0 && h>0) {
            gtk_window_set_default_size (GTK_WINDOW(widget), w, h);
            retval = TRUE;
      }

      if (widget == Pan.window)
      {
            gboolean maximize;
            g_snprintf (buf, sizeof(buf), "/Pan/Geometry/%s_max", key);
            maximize = pan_config_get_bool (buf, FALSE);

            if (maximize)
                  gtk_window_maximize (GTK_WINDOW(widget));
            else
                  gtk_window_unmaximize (GTK_WINDOW(widget));
      }

      return retval;
}

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

void
launch_external_editor (const char    * edit_command,
                        const char    * filename,
                        int             line_number,
                        GSourceFunc     finished_callback,
                        gpointer        finished_user_data)
{
      int i;
      int argc = 0;
      char ** argv = NULL;
      gboolean ok = TRUE;

      /* sanity checks */
      g_return_if_fail (filename != NULL);

      /* parse the command line */
      if (ok) {
            GError * err = NULL;
            g_shell_parse_argv (edit_command, &argc, &argv, &err);
            if (err != NULL) {
                  log_add_va (LOG_ERROR, _("Error parsing \"external editor\" command line: %s"), err->message);
                  log_add_va (LOG_ERROR, _("The command line was: %s"), edit_command);
                  g_error_free (err);
                  ok = FALSE;
            }
      }

      /* make sure the filename is substitued _after_ the command-line parser;
       * those path slashes and backslashes make Windows very sad */
      for (i=0; i<argc; ++i) {
            if (strstr (argv[i], "%t")) /* filename */
                  replace_gstr (&argv[i], pan_substitute (argv[i], "%t", filename));
            if (strstr (argv[i], "%n")) { /* line number */
                  char buf[32];
                  g_snprintf (buf, sizeof(buf), "%d", line_number);
                  replace_gstr (&argv[i], pan_substitute (argv[i], "%n", buf));
            }
      }

      /* spawn off the external editor */
      if (ok) {
            GError * err = NULL;
            g_spawn_sync (NULL, argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL, NULL, &err);
            if (err != NULL) {
                  log_add_va (LOG_ERROR, _("Error starting external scorefile editor: %s"), err->message);
                  g_error_free (err);
                  ok = FALSE;
            }
      }

      /* cleanup */
      g_strfreev (argv);
      gui_queue_add (finished_callback, finished_user_data);
}

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

GtkWidget*
pan_gtk_image_new_from_inline_text (const guint8 * inline_text, GtkIconSize icon_size)
{
      GdkPixbuf * pixbuf;
      GtkIconSet * icon_set;
      GtkWidget * image;

      pixbuf = gdk_pixbuf_new_from_inline (-1, inline_text, FALSE, NULL);
      icon_set = gtk_icon_set_new_from_pixbuf (pixbuf);
      g_object_unref (G_OBJECT(pixbuf));
      image = gtk_image_new_from_icon_set (icon_set, icon_size);
      gtk_icon_set_unref (icon_set);

      return image;
}

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

GtkWidget*
pan_hig_workarea_create (void)
{
      GtkWidget * t = gtk_table_new (4, 100, FALSE);
      gtk_table_set_row_spacings (GTK_TABLE(t), 6);
      gtk_container_set_border_width (GTK_CONTAINER(t), 12);
      return t;
}

void
pan_hig_workarea_add_section_divider  (GtkWidget   * table,
                                       int         * row)
{
      GtkWidget * w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f);
      gtk_widget_set_usize (w, 0u, 6u);
      gtk_table_attach (GTK_TABLE(table), w, 0, 4, *row, *row+1, 0, 0, 0, 0);
      ++*row;
}

void
pan_hig_workarea_add_section_title    (GtkWidget   * table,
                                       int         * row,
                                       const char  * section_title)
{
      char buf[512];
      GtkWidget * l;

      g_snprintf (buf, sizeof(buf), "<b>%s</b>", section_title);
      l = gtk_label_new (buf);
      gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
      gtk_label_set_use_markup (GTK_LABEL(l), TRUE);
      gtk_table_attach (GTK_TABLE(table), l, 0, 4, *row, *row+1, GTK_EXPAND|GTK_SHRINK|GTK_FILL, 0, 0, 0);
      ++*row;
}

void
pan_hig_workarea_add_section_spacer  (GtkWidget   * table,
                                      int           row,
                                      int           items_in_section)
{
      GtkWidget * w;

      /* spacer to move the fields a little to the right of the name header */
      w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f);
      gtk_widget_set_usize (w, 18u, 0u);
      gtk_table_attach (GTK_TABLE(table), w, 0, 1, row, row+items_in_section, 0, 0, 0, 0);

      /* spacer between the controls and their labels */
      w = gtk_alignment_new (0.0f, 0.0f, 0.0f, 0.0f);
      gtk_widget_set_usize (w, 12u, 0u);
      gtk_table_attach (GTK_TABLE(table), w, 2, 3, row, row+items_in_section, 0, 0, 0, 0);
}

GtkWidget *
pan_hig_workarea_add_wide_checkbutton   (GtkWidget   * table,
                                         int         * row,
                                         const char  * mnemonic_string,
                                         gboolean      is_active)
{
      GtkWidget * w = gtk_check_button_new_with_mnemonic (mnemonic_string);
      gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON(w), is_active);
      gtk_table_attach_defaults (GTK_TABLE(table), w, 1, 4, *row, *row+1);

      ++*row;

      return w;
}


GtkWidget*
pan_hig_workarea_add_label   (GtkWidget   * table,
                              int           row,
                              const char  * mnemonic_string)
{
      GtkWidget * l;

      l = gtk_label_new_with_mnemonic (mnemonic_string);
      gtk_misc_set_alignment (GTK_MISC(l), 0.0f, 0.5f);
      gtk_label_set_use_markup (GTK_LABEL(l), TRUE);
      gtk_table_attach (GTK_TABLE(table), l, 1, 2, row, row+1, GTK_FILL, 0, 0, 0);

      return l;
}

                                                                                                                                                             
void
pan_hig_workarea_add_control (GtkWidget   * table,
                              int           row,
                              GtkWidget   * control)
{
      gtk_table_attach_defaults (GTK_TABLE(table), control, 3, 4, row, row+1);
}

                                                                                                                                                             
GtkWidget*
pan_hig_workarea_add_row  (GtkWidget   * table,
                           int         * row,
                           const char  * mnemonic_string,
                           GtkWidget   * control,
                           GtkWidget   * mnemonic_or_null_if_control_is_mnemonic)
{
      GtkWidget * l;
      GtkWidget * mnemonic;

      l = pan_hig_workarea_add_label (table, *row, mnemonic_string);
      pan_hig_workarea_add_control (table, *row, control);

      if (mnemonic_or_null_if_control_is_mnemonic == NULL)
            mnemonic = control;
      else
            mnemonic = mnemonic_or_null_if_control_is_mnemonic;

      gtk_label_set_mnemonic_widget (GTK_LABEL(l), mnemonic);

      ++*row;

      return l;
}


Generated by  Doxygen 1.6.0   Back to index