mirror of
https://github.com/transmission/transmission
synced 2025-01-30 19:03:04 +00:00
Update 2005-12-04
This commit is contained in:
parent
4ac92bd6d0
commit
6fb3416215
7 changed files with 634 additions and 85 deletions
|
@ -1,6 +1,6 @@
|
|||
SubDir TOP gtk ;
|
||||
|
||||
GTK_SRC = conf.c main.c prefs.c util.c ;
|
||||
GTK_SRC = conf.c main.c prefs.c util.c gtkcellrenderertorrent.c ;
|
||||
|
||||
Main transmission-gtk : $(GTK_SRC) ;
|
||||
LinkLibraries transmission-gtk : libtransmission.a ;
|
||||
|
|
300
gtk/gtkcellrenderertorrent.c
Normal file
300
gtk/gtkcellrenderertorrent.c
Normal file
|
@ -0,0 +1,300 @@
|
|||
/* gtkcellrenderertorrent.c
|
||||
* Copyright (C) 2002 Naba Kumar <kh_naba@users.sourceforge.net>
|
||||
* heavily modified by Jörgen Scheibengruber <mfcn@gmx.de>
|
||||
* heavily modified by Marco Pesenti Gritti <marco@gnome.org>
|
||||
* heavily modified by Josh Elsasser <josh@elsasser.org> for transmission
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
/*
|
||||
* Modified by the GTK+ Team and others 1997-2004. See the AUTHORS
|
||||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include "gtkcellrenderertorrent.h"
|
||||
#include "util.h"
|
||||
|
||||
enum { PROP_0, PROP_VALUE, PROP_TEXT, PROP_LABEL };
|
||||
|
||||
struct _GtkCellRendererTorrentPrivate {
|
||||
gfloat value;
|
||||
gchar *text;
|
||||
PangoAttrList *text_attrs;
|
||||
gchar *label;
|
||||
PangoAttrList *label_attrs;
|
||||
GtkStyle *style;
|
||||
};
|
||||
|
||||
static void
|
||||
finalize(GObject *object);
|
||||
static void
|
||||
get_property(GObject *obj, guint id, GValue *value, GParamSpec *spec);
|
||||
static void
|
||||
set_property(GObject *obj, guint id, const GValue *value, GParamSpec *spec);
|
||||
static void
|
||||
get_size(GtkCellRenderer *cell, GtkWidget *widget, GdkRectangle *area,
|
||||
gint *xoff, gint *yoff, gint *width, gint *height);
|
||||
static void
|
||||
render(GtkCellRenderer *cell, GdkWindow *window, GtkWidget *widget,
|
||||
GdkRectangle *bg, GdkRectangle *area, GdkRectangle *exp, guint flags);
|
||||
|
||||
|
||||
G_DEFINE_TYPE(GtkCellRendererTorrent, gtk_cell_renderer_torrent, GTK_TYPE_CELL_RENDERER);
|
||||
|
||||
static void
|
||||
gtk_cell_renderer_torrent_class_init (GtkCellRendererTorrentClass *klass) {
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
GtkCellRendererClass *cell_class = GTK_CELL_RENDERER_CLASS (klass);
|
||||
|
||||
object_class->finalize = finalize;
|
||||
object_class->get_property = get_property;
|
||||
object_class->set_property = set_property;
|
||||
|
||||
cell_class->get_size = get_size;
|
||||
cell_class->render = render;
|
||||
|
||||
g_object_class_install_property(
|
||||
object_class, PROP_VALUE,
|
||||
g_param_spec_float("value", "Value", "Value of the torrent bar",
|
||||
0.0, 1.0, 0.0, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property(
|
||||
object_class, PROP_TEXT,
|
||||
g_param_spec_string ("text", "Text", "Text under the torrent bar",
|
||||
/* XXX should I have NULL or "" here, and is initial strdup needed? */
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_object_class_install_property(
|
||||
object_class, PROP_LABEL,
|
||||
g_param_spec_string ("label", "Label", "Text on the torrent bar",
|
||||
NULL, G_PARAM_READWRITE));
|
||||
|
||||
g_type_class_add_private (object_class,
|
||||
sizeof (GtkCellRendererTorrentPrivate));
|
||||
}
|
||||
|
||||
static void
|
||||
gtk_cell_renderer_torrent_init(GtkCellRendererTorrent *tcell) {
|
||||
tcell->priv = G_TYPE_INSTANCE_GET_PRIVATE(
|
||||
tcell, GTK_TYPE_CELL_RENDERER_TORRENT, GtkCellRendererTorrentPrivate);
|
||||
tcell->priv->value = 0.0;
|
||||
tcell->priv->text = g_strdup("");
|
||||
tcell->priv->text_attrs = NULL;
|
||||
tcell->priv->label = g_strdup("");
|
||||
tcell->priv->text_attrs = NULL;
|
||||
tcell->priv->style = NULL;
|
||||
}
|
||||
|
||||
GtkCellRenderer*
|
||||
gtk_cell_renderer_torrent_new(void) {
|
||||
return g_object_new (GTK_TYPE_CELL_RENDERER_TORRENT, NULL);
|
||||
}
|
||||
|
||||
static void
|
||||
finalize(GObject *object) {
|
||||
GtkCellRendererTorrent *tcell = GTK_CELL_RENDERER_TORRENT(object);
|
||||
|
||||
g_free(tcell->priv->text);
|
||||
g_free(tcell->priv->label);
|
||||
|
||||
if(NULL != tcell->priv->text_attrs)
|
||||
pango_attr_list_unref(tcell->priv->text_attrs);
|
||||
if(NULL != tcell->priv->label_attrs)
|
||||
pango_attr_list_unref(tcell->priv->label_attrs);
|
||||
if(NULL != tcell->priv->style) {
|
||||
gtk_style_detach(tcell->priv->style);
|
||||
gtk_style_unref(tcell->priv->style);
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (gtk_cell_renderer_torrent_parent_class)->finalize(object);
|
||||
}
|
||||
|
||||
static void
|
||||
get_property(GObject *object, guint id, GValue *value, GParamSpec *pspec) {
|
||||
GtkCellRendererTorrent *tcell = GTK_CELL_RENDERER_TORRENT (object);
|
||||
|
||||
switch (id) {
|
||||
case PROP_VALUE:
|
||||
g_value_set_float (value, tcell->priv->value);
|
||||
break;
|
||||
case PROP_TEXT:
|
||||
g_value_set_string (value, tcell->priv->text);
|
||||
break;
|
||||
case PROP_LABEL:
|
||||
g_value_set_string (value, tcell->priv->label);
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, id, pspec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_property(GObject *obj, guint id, const GValue *value, GParamSpec *spec) {
|
||||
GtkCellRendererTorrent *tcell = GTK_CELL_RENDERER_TORRENT(obj);
|
||||
gchar **prop = NULL;
|
||||
PangoAttrList **attrs = NULL;
|
||||
/*GError *err = NULL;*/
|
||||
const gchar *markup;
|
||||
|
||||
switch(id) {
|
||||
case PROP_VALUE:
|
||||
tcell->priv->value = g_value_get_float(value);
|
||||
break;
|
||||
|
||||
case PROP_TEXT:
|
||||
prop = &tcell->priv->text;
|
||||
attrs = &tcell->priv->text_attrs;
|
||||
/* fallthrough */
|
||||
|
||||
case PROP_LABEL:
|
||||
if(PROP_LABEL == id) {
|
||||
prop = &tcell->priv->label;
|
||||
attrs = &tcell->priv->label_attrs;
|
||||
}
|
||||
|
||||
if(NULL == (markup = g_value_get_string(value)))
|
||||
markup = "";
|
||||
|
||||
g_free(*prop);
|
||||
if(NULL != *attrs)
|
||||
pango_attr_list_unref(*attrs);
|
||||
|
||||
*prop = g_strdup(markup);
|
||||
|
||||
/*
|
||||
if(pango_parse_markup(markup, -1, 0, attrs, prop, NULL, &err))
|
||||
break;
|
||||
|
||||
g_warning ("Failed to parse markup: %s", err->message);
|
||||
g_error_free(err);
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, id, spec);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
get_size(GtkCellRenderer *cell, GtkWidget *widget, GdkRectangle *area,
|
||||
gint *xoff, gint *yoff, gint *width, gint *height) {
|
||||
GtkCellRendererTorrent *tcell = GTK_CELL_RENDERER_TORRENT(cell);
|
||||
/* XXX do I have to unref the context? */
|
||||
PangoLayout *layout = pango_layout_new(gtk_widget_get_pango_context(widget));
|
||||
PangoRectangle rect;
|
||||
gint h = cell->ypad * 2;
|
||||
gint w1, w2;
|
||||
|
||||
pango_layout_set_markup(layout, tcell->priv->label, -1);
|
||||
pango_layout_get_pixel_extents(layout, NULL, &rect);
|
||||
w1 = rect.width;
|
||||
h += rect.height;
|
||||
|
||||
pango_layout_set_markup(layout, tcell->priv->text, -1);
|
||||
pango_layout_get_pixel_extents(layout, NULL, &rect);
|
||||
w2 = rect.width;
|
||||
h += rect.height;
|
||||
|
||||
if(NULL != xoff)
|
||||
*xoff = 0;
|
||||
if(NULL != yoff)
|
||||
*yoff = (area->height - h) / 2;
|
||||
if(NULL != width)
|
||||
*width = MAX(w1, w2) + cell->xpad * 2;
|
||||
if(NULL != height)
|
||||
*height = h;
|
||||
|
||||
g_object_unref(layout);
|
||||
}
|
||||
|
||||
#define RECTARGS(rect) (rect).x, (rect).y, (rect).width, (rect).height
|
||||
|
||||
static void
|
||||
render(GtkCellRenderer *cell, GdkWindow *window, GtkWidget *widget,
|
||||
GdkRectangle *bg SHUTUP, GdkRectangle *area, GdkRectangle *exp SHUTUP,
|
||||
guint flags) {
|
||||
GtkCellRendererTorrent *tcell = GTK_CELL_RENDERER_TORRENT(cell);
|
||||
PangoContext *ctx = gtk_widget_get_pango_context(widget);
|
||||
PangoLayout *llayout, *tlayout;
|
||||
PangoRectangle lrect, trect;
|
||||
GdkRectangle bar, complete, text;
|
||||
gboolean rtl;
|
||||
GtkStyle *style;
|
||||
|
||||
/* try to use the style for GtkProgressBar */
|
||||
if(NULL == tcell->priv->style)
|
||||
if(NULL != (tcell->priv->style = gtk_rc_get_style_by_paths(
|
||||
gtk_widget_get_settings(widget), NULL, NULL,
|
||||
gtk_progress_bar_get_type())))
|
||||
tcell->priv->style = gtk_style_attach(gtk_style_ref(tcell->priv->style),
|
||||
window);
|
||||
style = (NULL == tcell->priv->style ? widget->style : tcell->priv->style);
|
||||
|
||||
rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
|
||||
|
||||
/* get the text layouts */
|
||||
llayout = pango_layout_new(ctx);
|
||||
/* XXX cache parsed markup? */
|
||||
pango_layout_set_markup(llayout, tcell->priv->label, -1);
|
||||
pango_layout_get_pixel_extents(llayout, NULL, &lrect);
|
||||
tlayout = pango_layout_new(ctx);
|
||||
pango_layout_set_markup(tlayout, tcell->priv->text, -1);
|
||||
pango_layout_get_pixel_extents (tlayout, NULL, &trect);
|
||||
|
||||
/* set up the dimensions for the bar */
|
||||
bar.x = area->x + cell->xpad;
|
||||
bar.y = area->y + cell->ypad +
|
||||
(area->height - lrect.height - trect.height) / 2;
|
||||
bar.width = area->width - cell->xpad * 2;
|
||||
bar.height = lrect.height;
|
||||
|
||||
/* set up the dimensions for the complete portion of the bar */
|
||||
complete.x = bar.x + style->xthickness;
|
||||
complete.y = bar.y + style->ythickness;
|
||||
complete.width = (bar.width - style->xthickness * 2) * tcell->priv->value;
|
||||
complete.height = bar.height - style->ythickness * 2;
|
||||
if(rtl)
|
||||
complete.x += bar.width - complete.width;
|
||||
|
||||
/* set up the dimensions for the text under the bar */
|
||||
text.x = bar.x;
|
||||
text.y = bar.y + bar.height;
|
||||
text.width = bar.width;
|
||||
text.height = area->height - bar.height;
|
||||
|
||||
/* draw the background of the bar */
|
||||
if(complete.width < bar.width)
|
||||
gtk_paint_box(style, window, GTK_STATE_NORMAL, GTK_SHADOW_IN,
|
||||
NULL, widget, "trough", RECTARGS(bar));
|
||||
|
||||
/* draw the complete portion of the bar */
|
||||
if(0 < complete.width)
|
||||
gtk_paint_box(style, window, GTK_STATE_PRELIGHT, GTK_SHADOW_OUT,
|
||||
NULL, widget, "bar", RECTARGS(complete));
|
||||
|
||||
/* draw the text under the bar */
|
||||
gtk_paint_layout(style, window, (GTK_CELL_RENDERER_SELECTED & flags ?
|
||||
GTK_STATE_SELECTED : GTK_STATE_NORMAL), FALSE, &text,
|
||||
widget, "cellrenderertext", text.x, text.y, tlayout);
|
||||
|
||||
/* free memory */
|
||||
g_object_unref(llayout);
|
||||
g_object_unref(tlayout);
|
||||
}
|
69
gtk/gtkcellrenderertorrent.h
Normal file
69
gtk/gtkcellrenderertorrent.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
/* gtkcellrenderertorrent.h
|
||||
* Copyright (C) 2002 Naba Kumar <kh_naba@users.sourceforge.net>
|
||||
* modified by Jörgen Scheibengruber <mfcn@gmx.de>
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public
|
||||
* License as published by the Free Software Foundation; either
|
||||
* version 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with this library; if not, write to the
|
||||
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
|
||||
* Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
/*
|
||||
* Modified by the GTK+ Team and others 1997-2004. See the AUTHORS
|
||||
* file for a list of people on the GTK+ Team. See the ChangeLog
|
||||
* files for a list of changes. These files are distributed with
|
||||
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
|
||||
*/
|
||||
|
||||
#ifndef __GTK_CELL_RENDERER_TORRENT_H__
|
||||
#define __GTK_CELL_RENDERER_TORRENT_H__
|
||||
|
||||
#include <gtk/gtkcellrenderer.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define GTK_TYPE_CELL_RENDERER_TORRENT (gtk_cell_renderer_torrent_get_type ())
|
||||
#define GTK_CELL_RENDERER_TORRENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_CELL_RENDERER_TORRENT, GtkCellRendererTorrent))
|
||||
#define GTK_CELL_RENDERER_TORRENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_CELL_RENDERER_TORRENT, GtkCellRendererTorrentClass))
|
||||
#define GTK_IS_CELL_RENDERER_TORRENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_CELL_RENDERER_TORRENT))
|
||||
#define GTK_IS_CELL_RENDERER_TORRENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_CELL_RENDERER_TORRENT))
|
||||
#define GTK_CELL_RENDERER_TORRENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_CELL_RENDERER_TORRENT, GtkCellRendererTorrentClass))
|
||||
|
||||
typedef struct _GtkCellRendererTorrent GtkCellRendererTorrent;
|
||||
typedef struct _GtkCellRendererTorrentClass GtkCellRendererTorrentClass;
|
||||
typedef struct _GtkCellRendererTorrentPrivate GtkCellRendererTorrentPrivate;
|
||||
|
||||
struct _GtkCellRendererTorrent
|
||||
{
|
||||
GtkCellRenderer parent_instance;
|
||||
|
||||
/*< private >*/
|
||||
GtkCellRendererTorrentPrivate *priv;
|
||||
};
|
||||
|
||||
struct _GtkCellRendererTorrentClass
|
||||
{
|
||||
GtkCellRendererClass parent_class;
|
||||
|
||||
/* Padding for future expansion */
|
||||
void (*_gtk_reserved1) (void);
|
||||
void (*_gtk_reserved2) (void);
|
||||
void (*_gtk_reserved3) (void);
|
||||
void (*_gtk_reserved4) (void);
|
||||
};
|
||||
|
||||
GType gtk_cell_renderer_torrent_get_type (void) G_GNUC_CONST;
|
||||
GtkCellRenderer* gtk_cell_renderer_torrent_new (void);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __GTK_CELL_RENDERER_TORRENT_H__ */
|
297
gtk/main.c
297
gtk/main.c
|
@ -36,17 +36,20 @@
|
|||
#include <gtk/gtk.h>
|
||||
|
||||
#include "conf.h"
|
||||
#include "gtkcellrenderertorrent.h"
|
||||
#include "prefs.h"
|
||||
#include "transmission.h"
|
||||
#include "util.h"
|
||||
|
||||
#define TRACKER_EXIT_TIMEOUT 30
|
||||
#define TRACKER_EXIT_TIMEOUT 5
|
||||
|
||||
struct cbdata {
|
||||
tr_handle_t *tr;
|
||||
GtkWindow *wind;
|
||||
GtkListStore *model;
|
||||
GtkTreeView *view;
|
||||
GtkStatusbar *bar;
|
||||
GtkWidget **buttons;
|
||||
guint timer;
|
||||
};
|
||||
|
||||
|
@ -77,15 +80,23 @@ GtkWidget *
|
|||
makewind_toolbar(struct cbdata *data);
|
||||
GtkWidget *
|
||||
makewind_list(struct cbdata *data);
|
||||
void
|
||||
fixbuttons(GtkTreeSelection *sel, gpointer gdata);
|
||||
void
|
||||
dfname(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model,
|
||||
GtkTreeIter *iter, gpointer gdata);
|
||||
void
|
||||
dfprog(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model,
|
||||
GtkTreeIter *iter, gpointer gdata);
|
||||
|
||||
gboolean
|
||||
updatemodel(gpointer gdata);
|
||||
gboolean
|
||||
listclick(GtkWidget *widget, GdkEventButton *event, gpointer gdata);
|
||||
gboolean
|
||||
listpopup(GtkWidget *widget, gpointer userdata);
|
||||
listpopup(GtkWidget *widget, gpointer gdata);
|
||||
void
|
||||
dopopupmenu(GtkWidget *widget, GdkEventButton *event, struct cbdata *data);
|
||||
dopopupmenu(GdkEventButton *event, struct cbdata *data, GtkTreeIter *iter);
|
||||
void
|
||||
actionclick(GtkWidget *widget, gpointer gdata);
|
||||
|
||||
|
@ -115,20 +126,22 @@ enum listfrom { FROM_BUTTON, FROM_POPUP };
|
|||
|
||||
#define LIST_INDEX "torrent-list-index"
|
||||
|
||||
struct { gint pos; const gchar *name; const gchar *id; enum listact act;
|
||||
const char *ttext; const char *tpriv; }
|
||||
struct { const gchar *name; const gchar *id; enum listact act; gboolean nomenu;
|
||||
int avail; const char *ttext; const char *tpriv; }
|
||||
actionitems[] = {
|
||||
{0, "Add", GTK_STOCK_ADD, ACT_OPEN,
|
||||
{"Add", GTK_STOCK_ADD, ACT_OPEN, FALSE, 0,
|
||||
"Add a new torrent file", "XXX"},
|
||||
{1, "Resume", GTK_STOCK_MEDIA_PLAY, ACT_START,
|
||||
{"Resume", GTK_STOCK_MEDIA_PLAY, ACT_START, FALSE,
|
||||
(TR_STATUS_STOPPING | TR_STATUS_PAUSE),
|
||||
"Resume a torrent that has been paused", "XXX"},
|
||||
{2, "Pause", GTK_STOCK_MEDIA_PAUSE, ACT_STOP,
|
||||
{"Pause", GTK_STOCK_MEDIA_PAUSE, ACT_STOP, FALSE,
|
||||
~(TR_STATUS_STOPPING | TR_STATUS_PAUSE),
|
||||
"Pause a torrent", "XXX"},
|
||||
{3, "Remove", GTK_STOCK_REMOVE, ACT_DELETE,
|
||||
{"Remove", GTK_STOCK_REMOVE, ACT_DELETE, FALSE, ~0,
|
||||
"Remove a torrent from the list", "XXX"},
|
||||
{4, "Properties", GTK_STOCK_PROPERTIES, ACT_INFO,
|
||||
{"Properties", GTK_STOCK_PROPERTIES, ACT_INFO, FALSE, ~0,
|
||||
"Get additional information for a torrent", "XXX"},
|
||||
{5, "Preferences", GTK_STOCK_PREFERENCES, ACT_PREF,
|
||||
{"Preferences", GTK_STOCK_PREFERENCES, ACT_PREF, TRUE, 0,
|
||||
"Open preferences dialog", "XXX"},
|
||||
};
|
||||
|
||||
|
@ -214,6 +227,7 @@ void
|
|||
makewind(GtkWidget *wind, tr_handle_t *tr, GList *saved) {
|
||||
GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
|
||||
GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
|
||||
GtkWidget *status = gtk_statusbar_new();
|
||||
struct cbdata *data = g_new0(struct cbdata, 1);
|
||||
GtkWidget *list;
|
||||
GtkRequisition req;
|
||||
|
@ -226,13 +240,18 @@ makewind(GtkWidget *wind, tr_handle_t *tr, GList *saved) {
|
|||
/* filled in by makewind_list */
|
||||
data->model = NULL;
|
||||
data->view = NULL;
|
||||
data->bar = GTK_STATUSBAR(status);
|
||||
data->buttons = NULL;
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(vbox), makewind_toolbar(data), FALSE, FALSE, 0);
|
||||
|
||||
list = makewind_list(data);
|
||||
gtk_widget_size_request(list, &req);
|
||||
gtk_container_add(GTK_CONTAINER(scroll), list);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
|
||||
|
||||
gtk_statusbar_push(GTK_STATUSBAR(status), 0, "");
|
||||
gtk_box_pack_start(GTK_BOX(vbox), status, FALSE, FALSE, 0);
|
||||
|
||||
gtk_container_add(GTK_CONTAINER(wind), vbox);
|
||||
gtk_window_set_default_size(GTK_WINDOW(wind), req.width, req.height);
|
||||
|
@ -344,8 +363,11 @@ makewind_toolbar(struct cbdata *data) {
|
|||
gtk_toolbar_set_tooltips(GTK_TOOLBAR(bar), TRUE);
|
||||
gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH);
|
||||
|
||||
data->buttons = g_new(GtkWidget*, ALEN(actionitems));
|
||||
|
||||
for(ii = 0; ii < ALEN(actionitems); ii++) {
|
||||
item = gtk_tool_button_new_from_stock(actionitems[ii].id);
|
||||
data->buttons[ii] = GTK_WIDGET(item);
|
||||
gtk_tool_button_set_label(GTK_TOOL_BUTTON(item), actionitems[ii].name);
|
||||
gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(item), GTK_TOOLBAR(bar)->tooltips,
|
||||
actionitems[ii].ttext, actionitems[ii].tpriv);
|
||||
|
@ -354,12 +376,13 @@ makewind_toolbar(struct cbdata *data) {
|
|||
g_object_set_data(G_OBJECT(item), LIST_ACTION_FROM,
|
||||
GINT_TO_POINTER(FROM_BUTTON));
|
||||
g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(actionclick), data);
|
||||
gtk_toolbar_insert(GTK_TOOLBAR(bar), GTK_TOOL_ITEM(item), actionitems[ii].pos);
|
||||
gtk_toolbar_insert(GTK_TOOLBAR(bar), item, -1);
|
||||
}
|
||||
|
||||
return bar;
|
||||
}
|
||||
|
||||
/* XXX check for unused data in model */
|
||||
enum {MC_NAME, MC_SIZE, MC_STAT, MC_ERR, MC_PROG, MC_DRATE, MC_URATE,
|
||||
MC_ETA, MC_PEERS, MC_UPEERS, MC_DPEERS, MC_PIECES, MC_DOWN, MC_UP,
|
||||
MC_ROW_INDEX, MC_ROW_COUNT};
|
||||
|
@ -368,7 +391,7 @@ GtkWidget *
|
|||
makewind_list(struct cbdata *data) {
|
||||
GType types[] = {
|
||||
/* info->name, info->totalSize, status, error, progress */
|
||||
G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT,
|
||||
G_TYPE_STRING, G_TYPE_UINT64, G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT,
|
||||
/* rateDownload, rateUpload, eta, peersTotal, peersUploading */
|
||||
G_TYPE_FLOAT, G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
|
||||
/* peersDownloading, pieces, downloaded, uploaded */
|
||||
|
@ -377,10 +400,9 @@ makewind_list(struct cbdata *data) {
|
|||
G_TYPE_INT};
|
||||
GtkListStore *model;
|
||||
GtkWidget *view;
|
||||
/*GtkTreeViewColumn *col;*/
|
||||
GtkTreeViewColumn *col;
|
||||
GtkTreeSelection *sel;
|
||||
GtkCellRenderer *rend;
|
||||
GtkCellRenderer *rendprog;
|
||||
GtkCellRenderer *namerend, *progrend;
|
||||
|
||||
assert(MC_ROW_COUNT == ALEN(types));
|
||||
|
||||
|
@ -391,53 +413,22 @@ makewind_list(struct cbdata *data) {
|
|||
data->model = model;
|
||||
data->view = GTK_TREE_VIEW(view);
|
||||
|
||||
rend = gtk_cell_renderer_text_new();
|
||||
rendprog = gtk_cell_renderer_progress_new();
|
||||
g_object_set(rendprog, "text", "", NULL);
|
||||
namerend = gtk_cell_renderer_text_new();
|
||||
col = gtk_tree_view_column_new_with_attributes("Name", namerend, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func(col, namerend, dfname, NULL, NULL);
|
||||
gtk_tree_view_column_set_expand(col, TRUE);
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
|
||||
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("Name", rend,
|
||||
"text", MC_NAME, NULL));
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("Size", rend,
|
||||
"text", MC_SIZE, NULL));
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("Status", rend,
|
||||
"text", MC_STAT, NULL));
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("Error", rend,
|
||||
"text", MC_ERR, NULL));
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("Progress", rendprog,
|
||||
"value", MC_PROG, NULL));
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("Download Rate", rend,
|
||||
"text", MC_DRATE, NULL));
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("Upload Rate", rend,
|
||||
"text", MC_URATE, NULL));
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("ETA", rend,
|
||||
"text", MC_ETA, NULL));
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("Peers", rend,
|
||||
"text", MC_PEERS, NULL));
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("Seeders", rend,
|
||||
"text", MC_UPEERS, NULL));
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("Leechers", rend,
|
||||
"text", MC_DPEERS, NULL));
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("Downloaded", rend,
|
||||
"text", MC_DOWN, NULL));
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view),
|
||||
gtk_tree_view_column_new_with_attributes("Uploaded", rend,
|
||||
"text", MC_UP, NULL));
|
||||
progrend = gtk_cell_renderer_torrent_new();
|
||||
g_object_set(progrend, "label", "<big> fnord fnord </big>", NULL);
|
||||
col = gtk_tree_view_column_new_with_attributes("Progress", progrend, NULL);
|
||||
gtk_tree_view_column_set_cell_data_func(col, progrend, dfprog, NULL, NULL);
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
|
||||
|
||||
gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
|
||||
sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
|
||||
gtk_tree_selection_set_mode(GTK_TREE_SELECTION(sel), GTK_SELECTION_SINGLE);
|
||||
g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(fixbuttons), data);
|
||||
g_signal_connect(G_OBJECT(view), "button-press-event",
|
||||
G_CALLBACK(listclick), data);
|
||||
g_signal_connect(G_OBJECT(view), "popup-menu", G_CALLBACK(listpopup), data);
|
||||
|
@ -446,27 +437,133 @@ makewind_list(struct cbdata *data) {
|
|||
return view;
|
||||
}
|
||||
|
||||
/* disable buttons the user shouldn't be able to click on */
|
||||
void
|
||||
fixbuttons(GtkTreeSelection *sel, gpointer gdata) {
|
||||
struct cbdata *data = gdata;
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
gboolean selected;
|
||||
unsigned int ii;
|
||||
int status;
|
||||
|
||||
if(NULL == sel)
|
||||
sel = gtk_tree_view_get_selection(data->view);
|
||||
if((selected = gtk_tree_selection_get_selected(sel, &model, &iter)))
|
||||
gtk_tree_model_get(model, &iter, MC_STAT, &status, -1);
|
||||
|
||||
for(ii = 0; ii < ALEN(actionitems); ii++)
|
||||
if(actionitems[ii].avail)
|
||||
gtk_widget_set_sensitive(data->buttons[ii],
|
||||
(selected && (actionitems[ii].avail & status)));
|
||||
}
|
||||
|
||||
void
|
||||
dfname(GtkTreeViewColumn *col SHUTUP, GtkCellRenderer *rend,
|
||||
GtkTreeModel *model, GtkTreeIter *iter, gpointer gdata SHUTUP) {
|
||||
char *name, *mb, *err, *str, *top, *bottom;
|
||||
guint64 size;
|
||||
gfloat prog;
|
||||
int status, eta, tpeers, upeers, dpeers;
|
||||
|
||||
/* XXX should I worry about gtk_tree_model_get failing? */
|
||||
gtk_tree_model_get(model, iter, MC_NAME, &name, MC_STAT, &status,
|
||||
MC_SIZE, &size, MC_PROG, &prog, MC_ETA, &eta, MC_PEERS, &tpeers,
|
||||
MC_UPEERS, &upeers, MC_DPEERS, &dpeers, -1);
|
||||
|
||||
if(0 > eta)
|
||||
eta = 0;
|
||||
if(0 > tpeers)
|
||||
tpeers = 0;
|
||||
if(0 > upeers)
|
||||
upeers = 0;
|
||||
if(0 > dpeers)
|
||||
dpeers = 0;
|
||||
mb = readablesize(size, 1);
|
||||
prog *= 100;
|
||||
|
||||
if(status & TR_STATUS_CHECK)
|
||||
top = g_strdup_printf("Checking existing files (%.1f%%)", prog);
|
||||
else if(status & TR_STATUS_DOWNLOAD)
|
||||
top = g_strdup_printf("Finishing in %02i:%02i:%02i (%.1f%%)",
|
||||
eta / 60 / 60, eta / 60 % 60, eta % 60, prog);
|
||||
else if(status & TR_STATUS_SEED)
|
||||
top = g_strdup_printf("Seeding, uploading to %d of %d peer%s",
|
||||
dpeers, tpeers, (1 == tpeers ? "" : "s"));
|
||||
else if(status & TR_STATUS_STOPPING)
|
||||
top = g_strdup("Stopping...");
|
||||
else if(status & TR_STATUS_PAUSE)
|
||||
top = g_strdup_printf("Paused (%.1f%%)", prog);
|
||||
else {
|
||||
top = g_strdup("");
|
||||
assert("XXX unknown status");
|
||||
}
|
||||
|
||||
if(status & TR_TRACKER_ERROR) {
|
||||
gtk_tree_model_get(model, iter, MC_ERR, &err, -1);
|
||||
bottom = g_strconcat("Error: ", err, NULL);
|
||||
g_free(err);
|
||||
}
|
||||
else if(status & TR_STATUS_DOWNLOAD)
|
||||
bottom = g_strdup_printf("Downloading from %i of %i peer%s",
|
||||
upeers, tpeers, (1 == tpeers ? "" : "s"));
|
||||
else
|
||||
bottom = NULL;
|
||||
|
||||
str = g_markup_printf_escaped("<big>%s (%s)</big>\n<small>%s\n%s</small>",
|
||||
name, mb, top, (NULL == bottom ? "" : bottom));
|
||||
g_object_set(rend, "markup", str, NULL);
|
||||
g_free(name);
|
||||
g_free(mb);
|
||||
g_free(str);
|
||||
g_free(top);
|
||||
g_free(bottom);
|
||||
}
|
||||
|
||||
void
|
||||
dfprog(GtkTreeViewColumn *col SHUTUP, GtkCellRenderer *rend,
|
||||
GtkTreeModel *model, GtkTreeIter *iter, gpointer gdata SHUTUP) {
|
||||
char *dlstr, *ulstr, *str;
|
||||
gfloat prog, dl, ul;
|
||||
|
||||
/* XXX should I worry about gtk_tree_model_get failing? */
|
||||
gtk_tree_model_get(model, iter, MC_PROG, &prog,
|
||||
MC_DRATE, &dl, MC_URATE, &ul, -1);
|
||||
if(0.0 > prog)
|
||||
prog = 0.0;
|
||||
else if(1.0 < prog)
|
||||
prog = 1.0;
|
||||
|
||||
dlstr = readablesize(dl * 1024.0, 2);
|
||||
ulstr = readablesize(ul * 1024.0, 2);
|
||||
str = g_strdup_printf("<small>DL: %s/s\nUL: %s/s</small>", dlstr, ulstr);
|
||||
g_object_set(rend, "text", str, "value", prog, NULL);
|
||||
g_free(dlstr);
|
||||
g_free(ulstr);
|
||||
g_free(str);
|
||||
}
|
||||
|
||||
gboolean
|
||||
updatemodel(gpointer gdata) {
|
||||
struct cbdata *data = gdata;
|
||||
tr_stat_t *st;
|
||||
int ii, max, prog;
|
||||
int ii, max;
|
||||
GtkTreeIter iter;
|
||||
float up, down;
|
||||
char *upstr, *downstr, *str;
|
||||
|
||||
max = tr_torrentStat(data->tr, &st);
|
||||
for(ii = 0; ii < max; ii++) {
|
||||
if(!(ii ? gtk_tree_model_iter_next(GTK_TREE_MODEL(data->model), &iter) :
|
||||
gtk_tree_model_get_iter_first(GTK_TREE_MODEL(data->model), &iter)))
|
||||
gtk_list_store_append(data->model, &iter);
|
||||
if(0.0 > (prog = st[ii].progress * 100.0))
|
||||
prog = 0;
|
||||
else if(100 < prog)
|
||||
prog = 100;
|
||||
/* XXX find out if setting the same data emits changed signal */
|
||||
gtk_list_store_set(data->model, &iter, MC_ROW_INDEX, ii,
|
||||
MC_NAME, st[ii].info.name, MC_SIZE, st[ii].info.totalSize, MC_STAT, st[ii].status,
|
||||
MC_ERR, st[ii].error, MC_PROG, prog, MC_DRATE, st[ii].rateDownload,
|
||||
MC_URATE, st[ii].rateUpload, MC_ETA, st[ii].eta, MC_PEERS, st[ii].peersTotal,
|
||||
gtk_list_store_set(
|
||||
data->model, &iter, MC_ROW_INDEX, ii,
|
||||
MC_NAME, st[ii].info.name, MC_SIZE, st[ii].info.totalSize,
|
||||
MC_STAT, st[ii].status, MC_ERR, st[ii].error, MC_PROG, st[ii].progress,
|
||||
MC_DRATE, st[ii].rateDownload, MC_URATE, st[ii].rateUpload,
|
||||
MC_ETA, st[ii].eta, MC_PEERS, st[ii].peersTotal,
|
||||
MC_UPEERS, st[ii].peersUploading, MC_DPEERS, st[ii].peersDownloading,
|
||||
MC_DOWN, st[ii].downloaded, MC_UP, st[ii].uploaded, -1);
|
||||
}
|
||||
|
@ -477,15 +574,38 @@ updatemodel(gpointer gdata) {
|
|||
while(gtk_list_store_remove(data->model, &iter))
|
||||
;
|
||||
|
||||
/* update the status bar */
|
||||
tr_torrentRates(data->tr, &up, &down);
|
||||
downstr = readablesize(down * 1024.0, 2);
|
||||
upstr = readablesize(up * 1024.0, 2);
|
||||
str = g_strdup_printf(" Total DL: %s/s Total UL: %s/s", upstr, downstr);
|
||||
gtk_statusbar_pop(data->bar, 0);
|
||||
gtk_statusbar_push(data->bar, 0, str);
|
||||
g_free(str);
|
||||
g_free(upstr);
|
||||
g_free(downstr);
|
||||
|
||||
/* the status of the selected item may have changed, so update the buttons */
|
||||
fixbuttons(NULL, data);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
gboolean
|
||||
listclick(GtkWidget *widget, GdkEventButton *event, gpointer gdata) {
|
||||
listclick(GtkWidget *widget SHUTUP, GdkEventButton *event, gpointer gdata) {
|
||||
struct cbdata *data = gdata;
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if(GDK_BUTTON_PRESS == event->type && 3 == event->button) {
|
||||
dopopupmenu(widget, event, data);
|
||||
if(!gtk_tree_view_get_path_at_pos(data->view, event->x, event->y, &path,
|
||||
NULL, NULL, NULL))
|
||||
dopopupmenu(event, data, NULL);
|
||||
else {
|
||||
if(gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path))
|
||||
dopopupmenu(event, data, &iter);
|
||||
gtk_tree_path_free(path);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
|
@ -493,33 +613,40 @@ listclick(GtkWidget *widget, GdkEventButton *event, gpointer gdata) {
|
|||
}
|
||||
|
||||
gboolean
|
||||
listpopup(GtkWidget *widget, gpointer userdata) {
|
||||
dopopupmenu(widget, NULL, userdata);
|
||||
listpopup(GtkWidget *widget SHUTUP, gpointer gdata) {
|
||||
struct cbdata *data = gdata;
|
||||
GtkTreeSelection *sel = gtk_tree_view_get_selection(data->view);
|
||||
GtkTreeModel *model;
|
||||
GtkTreeIter iter;
|
||||
|
||||
if(!gtk_tree_selection_get_selected(sel, &model, &iter))
|
||||
dopopupmenu(NULL, data, NULL);
|
||||
else {
|
||||
assert(model == GTK_TREE_MODEL(data->model));
|
||||
dopopupmenu(NULL, data, &iter);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
dopopupmenu(GtkWidget *widget SHUTUP, GdkEventButton *event,
|
||||
struct cbdata *data) {
|
||||
dopopupmenu(GdkEventButton *event, struct cbdata *data, GtkTreeIter *iter) {
|
||||
GtkWidget *menu = gtk_menu_new();
|
||||
GtkWidget *item;
|
||||
GtkTreePath *path;
|
||||
GtkTreeIter iter;
|
||||
unsigned int ii;
|
||||
int index;
|
||||
int index, status;
|
||||
|
||||
index = -1;
|
||||
if(NULL != event && gtk_tree_view_get_path_at_pos(
|
||||
data->view, event->x, event->y, &path, NULL, NULL, NULL)) {
|
||||
if(gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path))
|
||||
gtk_tree_model_get(GTK_TREE_MODEL(data->model), &iter, MC_ROW_INDEX, &index, -1);
|
||||
gtk_tree_path_free(path);
|
||||
}
|
||||
if(NULL != iter)
|
||||
gtk_tree_model_get(GTK_TREE_MODEL(data->model), iter,
|
||||
MC_ROW_INDEX, &index, MC_STAT, &status, -1);
|
||||
|
||||
/* XXX am I leaking references here? */
|
||||
/* XXX can I cache this in cbdata? */
|
||||
for(ii = 0; ii < ALEN(actionitems); ii++) {
|
||||
if(actionitems[ii].nomenu ||
|
||||
(actionitems[ii].avail &&
|
||||
(0 > index || !(actionitems[ii].avail & status))))
|
||||
continue;
|
||||
item = gtk_menu_item_new_with_label(actionitems[ii].name);
|
||||
g_object_set_data(G_OBJECT(item), LIST_ACTION,
|
||||
GINT_TO_POINTER(actionitems[ii].act));
|
||||
|
@ -583,10 +710,12 @@ actionclick(GtkWidget *widget, gpointer gdata) {
|
|||
case ACT_START:
|
||||
tr_torrentStart(data->tr, index);
|
||||
savetorrents(data->tr, data->wind, -1, NULL);
|
||||
updatemodel(data);
|
||||
break;
|
||||
case ACT_STOP:
|
||||
tr_torrentStop(data->tr, index);
|
||||
savetorrents(data->tr, data->wind, -1, NULL);
|
||||
updatemodel(data);
|
||||
break;
|
||||
case ACT_DELETE:
|
||||
/* XXX need to be able to stat just one torrent */
|
||||
|
|
17
gtk/util.c
17
gtk/util.c
|
@ -55,6 +55,23 @@ strbool(const char *str) {
|
|||
return FALSE;
|
||||
}
|
||||
|
||||
char *
|
||||
readablesize(guint64 size, int decimals) {
|
||||
const char *sizes[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
|
||||
unsigned int ii;
|
||||
double small = size;
|
||||
|
||||
for(ii = 0; ii + 1 < ALEN(sizes) && 1024.0 <= small / 1024.0; ii++)
|
||||
small /= 1024.0;
|
||||
|
||||
if(1024.0 <= small) {
|
||||
small /= 1024.0;
|
||||
ii++;
|
||||
}
|
||||
|
||||
return g_strdup_printf("%.*f %s", decimals, small, sizes[ii]);
|
||||
}
|
||||
|
||||
gboolean
|
||||
mkdir_p(const char *name, mode_t mode) {
|
||||
struct stat sb;
|
||||
|
|
|
@ -43,6 +43,9 @@
|
|||
gboolean
|
||||
strbool(const char *str);
|
||||
|
||||
char *
|
||||
readablesize(guint64 size, int decimals);
|
||||
|
||||
gboolean
|
||||
mkdir_p(const char *name, mode_t mode);
|
||||
|
||||
|
|
|
@ -850,11 +850,37 @@ static void sleepCallBack( void * controller, io_service_t y,
|
|||
URLWithString:@"http://transmission.m0k.org/forum/"]];
|
||||
}
|
||||
|
||||
- (BOOL) hasGrowl
|
||||
{
|
||||
NSFileManager * manager = [NSFileManager defaultManager];
|
||||
NSString * helper = @"/Library/PreferencePanes/Growl.prefPane/"
|
||||
"Contents/Resources/GrowlHelperApp.app";
|
||||
|
||||
if( [manager fileExistsAtPath: helper] )
|
||||
{
|
||||
/* Growl was installed for all users */
|
||||
return YES;
|
||||
}
|
||||
if( [manager fileExistsAtPath: [[NSString stringWithFormat: @"~%@",
|
||||
helper] stringByExpandingTildeInPath]] )
|
||||
{
|
||||
/* Growl was installed for this user only */
|
||||
return YES;
|
||||
}
|
||||
|
||||
return NO;
|
||||
}
|
||||
|
||||
- (void) notifyGrowl: (NSString * ) file
|
||||
{
|
||||
NSString * growlScript;
|
||||
NSAppleScript * appleScript;
|
||||
NSDictionary * error;
|
||||
|
||||
if( ![self hasGrowl] )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
growlScript = [NSString stringWithFormat:
|
||||
@"tell application \"System Events\"\n"
|
||||
|
@ -880,6 +906,11 @@ static void sleepCallBack( void * controller, io_service_t y,
|
|||
NSString * growlScript;
|
||||
NSAppleScript * appleScript;
|
||||
NSDictionary * error;
|
||||
|
||||
if( ![self hasGrowl] )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
growlScript = [NSString stringWithFormat:
|
||||
@"tell application \"System Events\"\n"
|
||||
|
|
Loading…
Reference in a new issue