Changeset 11


Ignore:
Timestamp:
Jan 12, 2006, 6:47:30 PM (15 years ago)
Author:
root
Message:

Update 2005-12-04

Location:
trunk
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/gtk/Jamfile

    r7 r11  
    11SubDir TOP gtk ;
    22
    3 GTK_SRC = conf.c main.c prefs.c util.c ;
     3GTK_SRC = conf.c main.c prefs.c util.c gtkcellrenderertorrent.c ;
    44
    55Main          transmission-gtk   : $(GTK_SRC) ;
  • trunk/gtk/main.c

    r10 r11  
    3737
    3838#include "conf.h"
     39#include "gtkcellrenderertorrent.h"
    3940#include "prefs.h"
    4041#include "transmission.h"
    4142#include "util.h"
    4243
    43 #define TRACKER_EXIT_TIMEOUT    30
     44#define TRACKER_EXIT_TIMEOUT    5
    4445
    4546struct cbdata {
     
    4849  GtkListStore *model;
    4950  GtkTreeView *view;
     51  GtkStatusbar *bar;
     52  GtkWidget **buttons;
    5053  guint timer;
    5154};
     
    7881GtkWidget *
    7982makewind_list(struct cbdata *data);
     83void
     84fixbuttons(GtkTreeSelection *sel, gpointer gdata);
     85void
     86dfname(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model,
     87       GtkTreeIter *iter, gpointer gdata);
     88void
     89dfprog(GtkTreeViewColumn *col, GtkCellRenderer *rend, GtkTreeModel *model,
     90       GtkTreeIter *iter, gpointer gdata);
    8091
    8192gboolean
     
    8495listclick(GtkWidget *widget, GdkEventButton *event, gpointer gdata);
    8596gboolean
    86 listpopup(GtkWidget *widget, gpointer userdata);
    87 void
    88 dopopupmenu(GtkWidget *widget, GdkEventButton *event, struct cbdata *data);
     97listpopup(GtkWidget *widget, gpointer gdata);
     98void
     99dopopupmenu(GdkEventButton *event, struct cbdata *data, GtkTreeIter *iter);
    89100void
    90101actionclick(GtkWidget *widget, gpointer gdata);
     
    116127#define LIST_INDEX            "torrent-list-index"
    117128
    118 struct { gint pos; const gchar *name; const gchar *id; enum listact act;
    119   const char *ttext; const char *tpriv; }
     129struct { const gchar *name; const gchar *id; enum listact act; gboolean nomenu;
     130  int avail; const char *ttext; const char *tpriv; }
    120131actionitems[] = {
    121   {0,  "Add",         GTK_STOCK_ADD,          ACT_OPEN,
     132  {"Add",         GTK_STOCK_ADD,          ACT_OPEN,   FALSE,  0,
    122133   "Add a new torrent file", "XXX"},
    123   {1,  "Resume",      GTK_STOCK_MEDIA_PLAY,   ACT_START,
     134  {"Resume",      GTK_STOCK_MEDIA_PLAY,   ACT_START,  FALSE,
     135   (TR_STATUS_STOPPING | TR_STATUS_PAUSE),
    124136   "Resume a torrent that has been paused", "XXX"},
    125   {2,  "Pause",       GTK_STOCK_MEDIA_PAUSE,  ACT_STOP,
     137  {"Pause",       GTK_STOCK_MEDIA_PAUSE,  ACT_STOP,   FALSE,
     138   ~(TR_STATUS_STOPPING | TR_STATUS_PAUSE),
    126139   "Pause a torrent", "XXX"},
    127   {3,  "Remove",      GTK_STOCK_REMOVE,       ACT_DELETE,
     140  {"Remove",      GTK_STOCK_REMOVE,       ACT_DELETE, FALSE, ~0,
    128141   "Remove a torrent from the list", "XXX"},
    129   {4,  "Properties",  GTK_STOCK_PROPERTIES,   ACT_INFO,
     142  {"Properties",  GTK_STOCK_PROPERTIES,   ACT_INFO,   FALSE, ~0,
    130143   "Get additional information for a torrent", "XXX"},
    131   {5,  "Preferences", GTK_STOCK_PREFERENCES,  ACT_PREF,
     144  {"Preferences", GTK_STOCK_PREFERENCES,  ACT_PREF,   TRUE,   0,
    132145   "Open preferences dialog", "XXX"},
    133146};
     
    215228  GtkWidget *vbox = gtk_vbox_new(FALSE, 0);
    216229  GtkWidget *scroll = gtk_scrolled_window_new(NULL, NULL);
     230  GtkWidget *status = gtk_statusbar_new();
    217231  struct cbdata *data = g_new0(struct cbdata, 1);
    218232  GtkWidget *list;
     
    227241  data->model = NULL;
    228242  data->view = NULL;
     243  data->bar = GTK_STATUSBAR(status);
     244  data->buttons = NULL;
    229245
    230246  gtk_box_pack_start(GTK_BOX(vbox), makewind_toolbar(data), FALSE, FALSE, 0);
     
    233249  gtk_widget_size_request(list, &req);
    234250  gtk_container_add(GTK_CONTAINER(scroll), list);
    235   gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 5);
     251  gtk_box_pack_start(GTK_BOX(vbox), scroll, TRUE, TRUE, 0);
     252
     253  gtk_statusbar_push(GTK_STATUSBAR(status), 0, "");
     254  gtk_box_pack_start(GTK_BOX(vbox), status, FALSE, FALSE, 0);
    236255
    237256  gtk_container_add(GTK_CONTAINER(wind), vbox);
     
    345364  gtk_toolbar_set_style(GTK_TOOLBAR(bar), GTK_TOOLBAR_BOTH);
    346365
     366  data->buttons = g_new(GtkWidget*, ALEN(actionitems));
     367
    347368  for(ii = 0; ii < ALEN(actionitems); ii++) {
    348369    item = gtk_tool_button_new_from_stock(actionitems[ii].id);
     370    data->buttons[ii] = GTK_WIDGET(item);
    349371    gtk_tool_button_set_label(GTK_TOOL_BUTTON(item), actionitems[ii].name);
    350372    gtk_tool_item_set_tooltip(GTK_TOOL_ITEM(item), GTK_TOOLBAR(bar)->tooltips,
     
    355377                      GINT_TO_POINTER(FROM_BUTTON));
    356378    g_signal_connect(G_OBJECT(item), "clicked", G_CALLBACK(actionclick), data);
    357     gtk_toolbar_insert(GTK_TOOLBAR(bar), GTK_TOOL_ITEM(item), actionitems[ii].pos);
     379    gtk_toolbar_insert(GTK_TOOLBAR(bar), item, -1);
    358380  }
    359381
     
    361383}
    362384
     385/* XXX check for unused data in model */
    363386enum {MC_NAME, MC_SIZE, MC_STAT, MC_ERR, MC_PROG, MC_DRATE, MC_URATE,
    364387      MC_ETA, MC_PEERS, MC_UPEERS, MC_DPEERS, MC_PIECES, MC_DOWN, MC_UP,
     
    369392  GType types[] = {
    370393    /* info->name, info->totalSize, status,     error,         progress */
    371     G_TYPE_STRING, G_TYPE_UINT64,   G_TYPE_INT, G_TYPE_STRING, G_TYPE_INT,
     394    G_TYPE_STRING, G_TYPE_UINT64,   G_TYPE_INT, G_TYPE_STRING, G_TYPE_FLOAT,
    372395    /* rateDownload, rateUpload,   eta,        peersTotal, peersUploading */
    373396    G_TYPE_FLOAT,    G_TYPE_FLOAT, G_TYPE_INT, G_TYPE_INT, G_TYPE_INT,
     
    378401  GtkListStore *model;
    379402  GtkWidget *view;
    380   /*GtkTreeViewColumn *col;*/
     403  GtkTreeViewColumn *col;
    381404  GtkTreeSelection *sel;
    382   GtkCellRenderer *rend;
    383   GtkCellRenderer *rendprog;
     405  GtkCellRenderer *namerend, *progrend;
    384406
    385407  assert(MC_ROW_COUNT == ALEN(types));
     
    392414  data->view = GTK_TREE_VIEW(view);
    393415
    394   rend = gtk_cell_renderer_text_new();
    395   rendprog = gtk_cell_renderer_progress_new();
    396   g_object_set(rendprog, "text", "", NULL);
    397 
    398   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    399     gtk_tree_view_column_new_with_attributes("Name", rend,
    400                                              "text", MC_NAME, NULL));
    401   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    402     gtk_tree_view_column_new_with_attributes("Size", rend,
    403                                              "text", MC_SIZE, NULL));
    404   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    405     gtk_tree_view_column_new_with_attributes("Status", rend,
    406                                              "text", MC_STAT, NULL));
    407   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    408     gtk_tree_view_column_new_with_attributes("Error", rend,
    409                                              "text", MC_ERR, NULL));
    410   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    411     gtk_tree_view_column_new_with_attributes("Progress", rendprog,
    412                                              "value", MC_PROG, NULL));
    413   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    414     gtk_tree_view_column_new_with_attributes("Download Rate", rend,
    415                                              "text", MC_DRATE, NULL));
    416   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    417     gtk_tree_view_column_new_with_attributes("Upload Rate", rend,
    418                                              "text", MC_URATE, NULL));
    419   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    420     gtk_tree_view_column_new_with_attributes("ETA", rend,
    421                                              "text", MC_ETA, NULL));
    422   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    423     gtk_tree_view_column_new_with_attributes("Peers", rend,
    424                                              "text", MC_PEERS, NULL));
    425   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    426     gtk_tree_view_column_new_with_attributes("Seeders", rend,
    427                                              "text", MC_UPEERS, NULL));
    428   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    429     gtk_tree_view_column_new_with_attributes("Leechers", rend,
    430                                              "text", MC_DPEERS, NULL));
    431   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    432     gtk_tree_view_column_new_with_attributes("Downloaded", rend,
    433                                              "text", MC_DOWN, NULL));
    434   gtk_tree_view_append_column(GTK_TREE_VIEW(view),
    435     gtk_tree_view_column_new_with_attributes("Uploaded", rend,
    436                                              "text", MC_UP, NULL));
     416  namerend = gtk_cell_renderer_text_new();
     417  col = gtk_tree_view_column_new_with_attributes("Name", namerend, NULL);
     418  gtk_tree_view_column_set_cell_data_func(col, namerend, dfname, NULL, NULL);
     419  gtk_tree_view_column_set_expand(col, TRUE);
     420  gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
     421
     422  progrend = gtk_cell_renderer_torrent_new();
     423  g_object_set(progrend, "label", "<big>  fnord    fnord  </big>", NULL);
     424  col = gtk_tree_view_column_new_with_attributes("Progress", progrend, NULL);
     425  gtk_tree_view_column_set_cell_data_func(col, progrend, dfprog, NULL, NULL);
     426  gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
    437427
    438428  gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(view), TRUE);
    439429  sel = gtk_tree_view_get_selection(GTK_TREE_VIEW(view));
    440430  gtk_tree_selection_set_mode(GTK_TREE_SELECTION(sel), GTK_SELECTION_SINGLE);
     431  g_signal_connect(G_OBJECT(sel), "changed", G_CALLBACK(fixbuttons), data);
    441432  g_signal_connect(G_OBJECT(view), "button-press-event",
    442433                   G_CALLBACK(listclick), data);
     
    447438}
    448439
     440/* disable buttons the user shouldn't be able to click on */
     441void
     442fixbuttons(GtkTreeSelection *sel, gpointer gdata) {
     443  struct cbdata *data = gdata;
     444  GtkTreeModel *model;
     445  GtkTreeIter iter;
     446  gboolean selected;
     447  unsigned int ii;
     448  int status;
     449
     450  if(NULL == sel)
     451    sel = gtk_tree_view_get_selection(data->view);
     452  if((selected = gtk_tree_selection_get_selected(sel, &model, &iter)))
     453    gtk_tree_model_get(model, &iter, MC_STAT, &status, -1);
     454
     455  for(ii = 0; ii < ALEN(actionitems); ii++)
     456    if(actionitems[ii].avail)
     457      gtk_widget_set_sensitive(data->buttons[ii],
     458                               (selected && (actionitems[ii].avail & status)));
     459}
     460
     461void
     462dfname(GtkTreeViewColumn *col SHUTUP, GtkCellRenderer *rend,
     463       GtkTreeModel *model, GtkTreeIter *iter, gpointer gdata SHUTUP) {
     464  char *name, *mb, *err, *str, *top, *bottom;
     465  guint64 size;
     466  gfloat prog;
     467  int status, eta, tpeers, upeers, dpeers;
     468
     469  /* XXX should I worry about gtk_tree_model_get failing? */
     470  gtk_tree_model_get(model, iter, MC_NAME, &name, MC_STAT, &status,
     471    MC_SIZE, &size, MC_PROG, &prog, MC_ETA, &eta, MC_PEERS, &tpeers,
     472    MC_UPEERS, &upeers, MC_DPEERS, &dpeers, -1);
     473
     474  if(0 > eta)
     475    eta = 0;
     476  if(0 > tpeers)
     477    tpeers = 0;
     478  if(0 > upeers)
     479    upeers = 0;
     480  if(0 > dpeers)
     481    dpeers = 0;
     482  mb = readablesize(size, 1);
     483  prog *= 100;
     484
     485  if(status & TR_STATUS_CHECK)
     486    top = g_strdup_printf("Checking existing files (%.1f%%)", prog);
     487  else if(status & TR_STATUS_DOWNLOAD)
     488    top = g_strdup_printf("Finishing in %02i:%02i:%02i (%.1f%%)",
     489                           eta / 60 / 60, eta / 60 % 60, eta % 60, prog);
     490  else if(status & TR_STATUS_SEED)
     491    top = g_strdup_printf("Seeding, uploading to %d of %d peer%s",
     492                           dpeers, tpeers, (1 == tpeers ? "" : "s"));
     493  else if(status & TR_STATUS_STOPPING)
     494    top = g_strdup("Stopping...");
     495  else if(status & TR_STATUS_PAUSE)
     496    top = g_strdup_printf("Paused (%.1f%%)", prog);
     497  else {
     498    top = g_strdup("");
     499    assert("XXX unknown status");
     500  }
     501
     502  if(status & TR_TRACKER_ERROR) {
     503    gtk_tree_model_get(model, iter, MC_ERR, &err, -1);
     504    bottom = g_strconcat("Error: ", err, NULL);
     505    g_free(err);
     506  }
     507  else if(status & TR_STATUS_DOWNLOAD)
     508    bottom = g_strdup_printf("Downloading from %i of %i peer%s",
     509                             upeers, tpeers, (1 == tpeers ? "" : "s"));
     510  else
     511    bottom = NULL;
     512
     513  str = g_markup_printf_escaped("<big>%s (%s)</big>\n<small>%s\n%s</small>",
     514                                name, mb, top, (NULL == bottom ? "" : bottom));
     515  g_object_set(rend, "markup", str, NULL);
     516  g_free(name);
     517  g_free(mb);
     518  g_free(str);
     519  g_free(top);
     520  g_free(bottom);
     521}
     522
     523void
     524dfprog(GtkTreeViewColumn *col SHUTUP, GtkCellRenderer *rend,
     525       GtkTreeModel *model, GtkTreeIter *iter, gpointer gdata SHUTUP) {
     526  char *dlstr, *ulstr, *str;
     527  gfloat prog, dl, ul;
     528
     529  /* XXX should I worry about gtk_tree_model_get failing? */
     530  gtk_tree_model_get(model, iter, MC_PROG, &prog,
     531                     MC_DRATE, &dl, MC_URATE, &ul, -1);
     532  if(0.0 > prog)
     533    prog = 0.0;
     534  else if(1.0 < prog)
     535    prog = 1.0;
     536
     537  dlstr = readablesize(dl * 1024.0, 2);
     538  ulstr = readablesize(ul * 1024.0, 2);
     539  str = g_strdup_printf("<small>DL: %s/s\nUL: %s/s</small>", dlstr, ulstr);
     540  g_object_set(rend, "text", str, "value", prog, NULL);
     541  g_free(dlstr);
     542  g_free(ulstr);
     543  g_free(str);
     544}
     545
    449546gboolean
    450547updatemodel(gpointer gdata) {
    451548  struct cbdata *data = gdata;
    452549  tr_stat_t *st;
    453   int ii, max, prog;
     550  int ii, max;
    454551  GtkTreeIter iter;
     552  float up, down;
     553  char *upstr, *downstr, *str;
    455554
    456555  max = tr_torrentStat(data->tr, &st);
     
    459558         gtk_tree_model_get_iter_first(GTK_TREE_MODEL(data->model), &iter)))
    460559      gtk_list_store_append(data->model, &iter);
    461     if(0.0 > (prog = st[ii].progress * 100.0))
    462       prog = 0;
    463     else if(100 < prog)
    464       prog = 100;
    465560    /* XXX find out if setting the same data emits changed signal */
    466     gtk_list_store_set(data->model, &iter, MC_ROW_INDEX, ii,
    467       MC_NAME, st[ii].info.name, MC_SIZE, st[ii].info.totalSize, MC_STAT, st[ii].status,
    468       MC_ERR, st[ii].error, MC_PROG, prog, MC_DRATE, st[ii].rateDownload,
    469       MC_URATE, st[ii].rateUpload, MC_ETA, st[ii].eta, MC_PEERS, st[ii].peersTotal,
     561    gtk_list_store_set(
     562      data->model, &iter, MC_ROW_INDEX, ii,
     563      MC_NAME, st[ii].info.name, MC_SIZE, st[ii].info.totalSize,
     564      MC_STAT, st[ii].status, MC_ERR, st[ii].error, MC_PROG, st[ii].progress,
     565      MC_DRATE, st[ii].rateDownload, MC_URATE, st[ii].rateUpload,
     566      MC_ETA, st[ii].eta, MC_PEERS, st[ii].peersTotal,
    470567      MC_UPEERS, st[ii].peersUploading, MC_DPEERS, st[ii].peersDownloading,
    471568      MC_DOWN, st[ii].downloaded, MC_UP, st[ii].uploaded, -1);
     
    478575      ;
    479576
     577  /* update the status bar */
     578  tr_torrentRates(data->tr, &up, &down);
     579  downstr = readablesize(down * 1024.0, 2);
     580  upstr = readablesize(up * 1024.0, 2);
     581  str = g_strdup_printf("     Total DL: %s/s     Total UL: %s/s", upstr, downstr);
     582  gtk_statusbar_pop(data->bar, 0);
     583  gtk_statusbar_push(data->bar, 0, str);
     584  g_free(str);
     585  g_free(upstr);
     586  g_free(downstr);
     587
     588  /* the status of the selected item may have changed, so update the buttons */
     589  fixbuttons(NULL, data);
     590
    480591  return TRUE;
    481592}
    482593
    483594gboolean
    484 listclick(GtkWidget *widget, GdkEventButton *event, gpointer gdata) {
     595listclick(GtkWidget *widget SHUTUP, GdkEventButton *event, gpointer gdata) {
    485596  struct cbdata *data = gdata;
     597  GtkTreePath *path;
     598  GtkTreeIter iter;
    486599
    487600  if(GDK_BUTTON_PRESS == event->type && 3 == event->button) {
    488     dopopupmenu(widget, event, data);
     601    if(!gtk_tree_view_get_path_at_pos(data->view, event->x, event->y, &path,
     602                                      NULL, NULL, NULL))
     603      dopopupmenu(event, data, NULL);
     604    else {
     605      if(gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path))
     606        dopopupmenu(event, data, &iter);
     607      gtk_tree_path_free(path);
     608    }
    489609    return TRUE;
    490610  }
     
    494614
    495615gboolean
    496 listpopup(GtkWidget *widget, gpointer userdata) {
    497   dopopupmenu(widget, NULL, userdata);
     616listpopup(GtkWidget *widget SHUTUP, gpointer gdata) {
     617  struct cbdata *data = gdata;
     618  GtkTreeSelection *sel = gtk_tree_view_get_selection(data->view);
     619  GtkTreeModel *model;
     620  GtkTreeIter iter;
     621
     622  if(!gtk_tree_selection_get_selected(sel, &model, &iter))
     623    dopopupmenu(NULL, data, NULL);
     624  else {
     625    assert(model == GTK_TREE_MODEL(data->model));
     626    dopopupmenu(NULL, data, &iter);
     627  }
    498628
    499629  return TRUE;
     
    501631
    502632void
    503 dopopupmenu(GtkWidget *widget SHUTUP, GdkEventButton *event,
    504             struct cbdata *data) {
     633dopopupmenu(GdkEventButton *event, struct cbdata *data, GtkTreeIter *iter) {
    505634  GtkWidget *menu = gtk_menu_new();
    506635  GtkWidget *item;
    507   GtkTreePath *path;
    508   GtkTreeIter iter;
    509636  unsigned int ii;
    510   int index;
     637  int index, status;
    511638
    512639  index = -1;
    513   if(NULL != event && gtk_tree_view_get_path_at_pos(
    514        data->view, event->x, event->y, &path, NULL, NULL, NULL)) {
    515     if(gtk_tree_model_get_iter(GTK_TREE_MODEL(data->model), &iter, path))
    516       gtk_tree_model_get(GTK_TREE_MODEL(data->model), &iter, MC_ROW_INDEX, &index, -1);
    517     gtk_tree_path_free(path);
    518   }
     640  if(NULL != iter)
     641    gtk_tree_model_get(GTK_TREE_MODEL(data->model), iter,
     642                       MC_ROW_INDEX, &index, MC_STAT, &status, -1);
    519643
    520644  /* XXX am I leaking references here? */
    521   /* XXX can I cache this in cbdata? */
    522645  for(ii = 0; ii < ALEN(actionitems); ii++) {
     646    if(actionitems[ii].nomenu ||
     647       (actionitems[ii].avail &&
     648        (0 > index || !(actionitems[ii].avail & status))))
     649      continue;
    523650    item = gtk_menu_item_new_with_label(actionitems[ii].name);
    524651    g_object_set_data(G_OBJECT(item), LIST_ACTION,
     
    584711        tr_torrentStart(data->tr, index);
    585712        savetorrents(data->tr, data->wind, -1, NULL);
     713        updatemodel(data);
    586714        break;
    587715      case ACT_STOP:
    588716        tr_torrentStop(data->tr, index);
    589717        savetorrents(data->tr, data->wind, -1, NULL);
     718        updatemodel(data);
    590719        break;
    591720      case ACT_DELETE:
  • trunk/gtk/util.c

    r7 r11  
    5454
    5555  return FALSE;
     56}
     57
     58char *
     59readablesize(guint64 size, int decimals) {
     60  const char *sizes[] = {"B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB"};
     61  unsigned int ii;
     62  double small = size;
     63
     64  for(ii = 0; ii + 1 < ALEN(sizes) && 1024.0 <= small / 1024.0; ii++)
     65    small /= 1024.0;
     66
     67  if(1024.0 <= small) {
     68    small /= 1024.0;
     69    ii++;
     70  }
     71
     72  return g_strdup_printf("%.*f %s", decimals, small, sizes[ii]);
    5673}
    5774
  • trunk/gtk/util.h

    r7 r11  
    4444strbool(const char *str);
    4545
     46char *
     47readablesize(guint64 size, int decimals);
     48
    4649gboolean
    4750mkdir_p(const char *name, mode_t mode);
  • trunk/macosx/Controller.m

    r7 r11  
    851851}
    852852
     853- (BOOL) hasGrowl
     854{
     855    NSFileManager * manager = [NSFileManager defaultManager];
     856    NSString      * helper  = @"/Library/PreferencePanes/Growl.prefPane/"
     857                               "Contents/Resources/GrowlHelperApp.app";
     858
     859    if( [manager fileExistsAtPath: helper] )
     860    {
     861        /* Growl was installed for all users */
     862        return YES;
     863    }
     864    if( [manager fileExistsAtPath: [[NSString stringWithFormat: @"~%@",
     865            helper] stringByExpandingTildeInPath]] )
     866    {
     867        /* Growl was installed for this user only */
     868        return YES;
     869    }
     870
     871    return NO;
     872}
     873
    853874- (void) notifyGrowl: (NSString * ) file
    854875{
     
    856877    NSAppleScript * appleScript;
    857878    NSDictionary * error;
     879
     880    if( ![self hasGrowl] )
     881    {
     882        return;
     883    }
    858884   
    859885    growlScript = [NSString stringWithFormat:
     
    881907    NSAppleScript * appleScript;
    882908    NSDictionary * error;
     909
     910    if( ![self hasGrowl] )
     911    {
     912        return;
     913    }
    883914   
    884915    growlScript = [NSString stringWithFormat:
Note: See TracChangeset for help on using the changeset viewer.