source: util.c @ 385fda9

release-1.7release-1.8release-1.9
Last change on this file since 385fda9 was 385fda9, checked in by David Benjamin <davidben@mit.edu>, 11 years ago
A fairly hacky job of porting sepbar to owl_window We now use the new framework, but the sepbar isn't being marked dirty at the appropriate times. It'll want to be redone later to take advantage of the new signals. The 'in' parameter was also dropped. It was being unused. At best it provided a strange bug when resizing with appendtosepbar set. Also revert the attempt at cleaning up the cursor. We still have a hack, but using owl_window. The cursor probably needs integration with the context stack or some such.
  • Property mode set to 100644
File size: 19.8 KB
Line 
1#include "owl.h"
2#include <stdlib.h>
3#include <string.h>
4#include <unistd.h>
5#include <ctype.h>
6#include <pwd.h>
7#include <sys/stat.h>
8#include <sys/types.h>
9
10#include <glib-object.h>
11
12void sepbar_dirty(void)
13{
14  owl_window_dirty(owl_global_get_curs_sepwin(&g));
15}
16
17void sepbar_redraw(owl_window *w, WINDOW *sepwin, void *user_data)
18{
19  const owl_messagelist *ml;
20  const owl_view *v;
21  int x, y, i;
22  const char *foo, *appendtosepbar;
23
24  ml=owl_global_get_msglist(&g);
25  v=owl_global_get_current_view(&g);
26
27  werase(sepwin);
28  wattron(sepwin, A_REVERSE);
29  if (owl_global_is_fancylines(&g)) {
30    whline(sepwin, ACS_HLINE, owl_global_get_cols(&g));
31  } else {
32    whline(sepwin, '-', owl_global_get_cols(&g));
33  }
34
35  if (owl_global_is_sepbar_disable(&g)) {
36    getyx(sepwin, y, x);
37    wmove(sepwin, y, owl_global_get_cols(&g)-1);
38    return;
39  }
40
41  wmove(sepwin, 0, 2);
42
43  if (owl_messagelist_get_size(ml) == 0)
44    waddstr(sepwin, " (-/-) ");
45  else
46    wprintw(sepwin, " (%i/%i/%i) ", owl_global_get_curmsg(&g) + 1,
47            owl_view_get_size(v),
48            owl_messagelist_get_size(ml));
49
50  foo=owl_view_get_filtname(v);
51  if (strcmp(foo, owl_global_get_view_home(&g)))
52      wattroff(sepwin, A_REVERSE);
53  wprintw(sepwin, " %s ", owl_view_get_filtname(v));
54  if (strcmp(foo, owl_global_get_view_home(&g)))
55      wattron(sepwin, A_REVERSE);
56
57  if (owl_mainwin_is_curmsg_truncated(owl_global_get_mainwin(&g))) {
58    getyx(sepwin, y, x);
59    wmove(sepwin, y, x+2);
60    wattron(sepwin, A_BOLD);
61    waddstr(sepwin, " <truncated> ");
62    wattroff(sepwin, A_BOLD);
63  }
64
65  i=owl_mainwin_get_last_msg(owl_global_get_mainwin(&g));
66  if ((i != -1) &&
67      (i < owl_view_get_size(v)-1)) {
68    getyx(sepwin, y, x);
69    wmove(sepwin, y, x+2);
70    wattron(sepwin, A_BOLD);
71    waddstr(sepwin, " <more> ");
72    wattroff(sepwin, A_BOLD);
73  }
74
75  if (owl_global_get_rightshift(&g)>0) {
76    getyx(sepwin, y, x);
77    wmove(sepwin, y, x+2);
78    wprintw(sepwin, " right: %i ", owl_global_get_rightshift(&g));
79  }
80
81  if (owl_global_is_zaway(&g) || owl_global_is_aaway(&g)) {
82    getyx(sepwin, y, x);
83    wmove(sepwin, y, x+2);
84    wattron(sepwin, A_BOLD);
85    wattroff(sepwin, A_REVERSE);
86    if (owl_global_is_zaway(&g) && owl_global_is_aaway(&g)) {
87      waddstr(sepwin, " AWAY ");
88    } else if (owl_global_is_zaway(&g)) {
89      waddstr(sepwin, " Z-AWAY ");
90    } else if (owl_global_is_aaway(&g)) {
91      waddstr(sepwin, " A-AWAY ");
92    }
93    wattron(sepwin, A_REVERSE);
94    wattroff(sepwin, A_BOLD);
95  }
96
97  if (owl_global_get_curmsg_vert_offset(&g)) {
98    getyx(sepwin, y, x);
99    wmove(sepwin, y, x+2);
100    wattron(sepwin, A_BOLD);
101    wattroff(sepwin, A_REVERSE);
102    waddstr(sepwin, " SCROLL ");
103    wattron(sepwin, A_REVERSE);
104    wattroff(sepwin, A_BOLD);
105  }
106
107  appendtosepbar = owl_global_get_appendtosepbar(&g);
108  if (appendtosepbar && *appendtosepbar) {
109    getyx(sepwin, y, x);
110    wmove(sepwin, y, x+2);
111    waddstr(sepwin, " ");
112    waddstr(sepwin, owl_global_get_appendtosepbar(&g));
113    waddstr(sepwin, " ");
114  }
115
116  getyx(sepwin, y, x);
117  wmove(sepwin, y, owl_global_get_cols(&g)-1);
118   
119  wattroff(sepwin, A_BOLD);
120  wattroff(sepwin, A_REVERSE);
121}
122
123char **atokenize(const char *buffer, const char *sep, int *i)
124{
125  /* each element of return must be freed by user */
126  char **args;
127  char *workbuff, *foo;
128  int done=0, first=1, count=0;
129
130  workbuff = owl_strdup(buffer);
131
132  args=NULL;
133  while (!done) {
134    if (first) {
135      first=0;
136      foo=strtok(workbuff, sep);
137    } else {
138      foo=strtok(NULL, sep);
139    }
140    if (foo==NULL) {
141      done=1;
142    } else {
143      args=owl_realloc(args, sizeof(char *) * (count+1));
144      args[count] = owl_strdup(foo);
145      count++;
146    }
147  }
148  *i=count;
149  owl_free(workbuff);
150  return(args);
151}
152
153const char *skiptokens(const char *buff, int n) {
154  /* skips n tokens and returns where that would be. */
155  char quote = 0;
156  while (*buff && n>0) {
157      while (*buff == ' ') buff++;
158      while (*buff && (quote || *buff != ' ')) {
159        if(quote) {
160          if(*buff == quote) quote = 0;
161        } else if(*buff == '"' || *buff == '\'') {
162          quote = *buff;
163        }
164        buff++;
165      }
166      while (*buff == ' ') buff++;
167      n--;
168  }
169  return buff;
170}
171
172/* Return a "nice" version of the path.  Tilde expansion is done, and
173 * duplicate slashes are removed.  Caller must free the return.
174 */
175char *owl_util_makepath(const char *in)
176{
177  int i, j, x;
178  char *out, user[MAXPATHLEN];
179  struct passwd *pw;
180
181  out=owl_malloc(MAXPATHLEN+1);
182  out[0]='\0';
183  j=strlen(in);
184  x=0;
185  for (i=0; i<j; i++) {
186    if (in[i]=='~') {
187      if ( (i==(j-1)) ||          /* last character */
188           (in[i+1]=='/') ) {     /* ~/ */
189        /* use my homedir */
190        pw=getpwuid(getuid());
191        if (!pw) {
192          out[x]=in[i];
193        } else {
194          out[x]='\0';
195          strcat(out, pw->pw_dir);
196          x+=strlen(pw->pw_dir);
197        }
198      } else {
199        /* another user homedir */
200        int a, b;
201        b=0;
202        for (a=i+1; i<j; a++) {
203          if (in[a]==' ' || in[a]=='/') {
204            break;
205          } else {
206            user[b]=in[a];
207            i++;
208            b++;
209          }
210        }
211        user[b]='\0';
212        pw=getpwnam(user);
213        if (!pw) {
214          out[x]=in[i];
215        } else {
216          out[x]='\0';
217          strcat(out, pw->pw_dir);
218          x+=strlen(pw->pw_dir);
219        }
220      }
221    } else if (in[i]=='/') {
222      /* check for a double / */
223      if (i<(j-1) && (in[i+1]=='/')) {
224        /* do nothing */
225      } else {
226        out[x]=in[i];
227        x++;
228      }
229    } else {
230      out[x]=in[i];
231      x++;
232    }
233  }
234  out[x]='\0';
235  return(out);
236}
237
238void atokenize_delete(char **tok, int nels)
239{
240  int i;
241  for (i=0; i<nels; i++) {
242    owl_free(tok[i]);
243  }
244  owl_free(tok);
245}
246
247
248void owl_parse_delete(char **argv, int argc)
249{
250  int i;
251
252  if (!argv) return;
253 
254  for (i=0; i<argc; i++) {
255    if (argv[i]) owl_free(argv[i]);
256  }
257  owl_free(argv);
258}
259
260char **owl_parseline(const char *line, int *argc)
261{
262  /* break a command line up into argv, argc.  The caller must free
263     the returned values.  If there is an error argc will be set to
264     -1, argv will be NULL and the caller does not need to free
265     anything */
266
267  char **argv;
268  int i, len, between=1;
269  char *curarg;
270  char quote;
271
272  argv=owl_malloc(sizeof(char *));
273  len=strlen(line);
274  curarg=owl_malloc(len+10);
275  strcpy(curarg, "");
276  quote='\0';
277  *argc=0;
278  for (i=0; i<len+1; i++) {
279    /* find the first real character */
280    if (between) {
281      if (line[i]==' ' || line[i]=='\t' || line[i]=='\0') {
282        continue;
283      } else {
284        between=0;
285        i--;
286        continue;
287      }
288    }
289
290    /* deal with a quote character */
291    if (line[i]=='"' || line[i]=="'"[0]) {
292      /* if this type of quote is open, close it */
293      if (quote==line[i]) {
294        quote='\0';
295        continue;
296      }
297
298      /* if no quoting is open then open with this */
299      if (quote=='\0') {
300        quote=line[i];
301        continue;
302      }
303
304      /* if another type of quote is open then treat this as a literal */
305      curarg[strlen(curarg)+1]='\0';
306      curarg[strlen(curarg)]=line[i];
307      continue;
308    }
309
310    /* if it's not a space or end of command, then use it */
311    if (line[i]!=' ' && line[i]!='\t' && line[i]!='\n' && line[i]!='\0') {
312      curarg[strlen(curarg)+1]='\0';
313      curarg[strlen(curarg)]=line[i];
314      continue;
315    }
316
317    /* otherwise, if we're not in quotes, add the whole argument */
318    if (quote=='\0') {
319      /* add the argument */
320      argv=owl_realloc(argv, sizeof(char *)*((*argc)+1));
321      argv[*argc] = owl_strdup(curarg);
322      *argc=*argc+1;
323      strcpy(curarg, "");
324      between=1;
325      continue;
326    }
327
328    /* if it is a space and we're in quotes, then use it */
329    curarg[strlen(curarg)+1]='\0';
330    curarg[strlen(curarg)]=line[i];
331  }
332
333  owl_free(curarg);
334
335  /* check for unbalanced quotes */
336  if (quote!='\0') {
337    owl_parse_delete(argv, *argc);
338    *argc=-1;
339    return(NULL);
340  }
341
342  return(argv);
343}
344
345/* caller must free the return */
346char *owl_util_minutes_to_timestr(int in)
347{
348  int days, hours;
349  long run;
350  char *out;
351
352  run=in;
353
354  days=run/1440;
355  run-=days*1440;
356  hours=run/60;
357  run-=hours*60;
358
359  if (days>0) {
360    out=owl_sprintf("%i d %2.2i:%2.2li", days, hours, run);
361  } else {
362    out=owl_sprintf("    %2.2i:%2.2li", hours, run);
363  }
364  return(out);
365}
366
367/* hooks for doing memory allocation et. al. in owl */
368
369void *owl_malloc(size_t size)
370{
371  return(g_malloc(size));
372}
373
374void owl_free(void *ptr)
375{
376  g_free(ptr);
377}
378
379char *owl_strdup(const char *s1)
380{
381  return(g_strdup(s1));
382}
383
384void *owl_realloc(void *ptr, size_t size)
385{
386  return(g_realloc(ptr, size));
387}
388
389/* allocates memory and returns the string or null.
390 * caller must free the string.
391 */
392char *owl_sprintf(const char *fmt, ...)
393{
394  va_list ap;
395  char *ret = NULL;
396  va_start(ap, fmt);
397  ret = g_strdup_vprintf(fmt, ap);
398  va_end(ap);
399  return ret;
400}
401
402/* These are in order of their value in owl.h */
403static const struct {
404  int number;
405  const char *name;
406} color_map[] = {
407  {OWL_COLOR_INVALID, "invalid"},
408  {OWL_COLOR_DEFAULT, "default"},
409  {OWL_COLOR_BLACK, "black"},
410  {OWL_COLOR_RED, "red"},
411  {OWL_COLOR_GREEN, "green"},
412  {OWL_COLOR_YELLOW,"yellow"},
413  {OWL_COLOR_BLUE, "blue"},
414  {OWL_COLOR_MAGENTA, "magenta"},
415  {OWL_COLOR_CYAN, "cyan"},
416  {OWL_COLOR_WHITE, "white"},
417};
418
419/* Return the owl color associated with the named color.  Return -1
420 * if the named color is not available
421 */
422int owl_util_string_to_color(const char *color)
423{
424  int c, i;
425  char *p;
426
427  for (i = 0; i < (sizeof(color_map)/sizeof(color_map[0])); i++)
428    if (strcasecmp(color, color_map[i].name) == 0)
429      return color_map[i].number;
430
431  c = strtol(color, &p, 10);
432  if (p != color && c >= -1 && c < COLORS) {
433    return(c);
434  }
435  return(OWL_COLOR_INVALID);
436}
437
438/* Return a string name of the given owl color */
439const char *owl_util_color_to_string(int color)
440{
441  if (color >= OWL_COLOR_INVALID && color <= OWL_COLOR_WHITE)
442    return color_map[color - OWL_COLOR_INVALID].name;
443  return("Unknown color");
444}
445
446/* Get the default tty name.  Caller must free the return */
447char *owl_util_get_default_tty(void)
448{
449  const char *tmp;
450  char *out;
451
452  if (getenv("DISPLAY")) {
453    out=owl_strdup(getenv("DISPLAY"));
454  } else if ((tmp=ttyname(fileno(stdout)))!=NULL) {
455    out=owl_strdup(tmp);
456    if (!strncmp(out, "/dev/", 5)) {
457      owl_free(out);
458      out=owl_strdup(tmp+5);
459    }
460  } else {
461    out=owl_strdup("unknown");
462  }
463  return(out);
464}
465
466/* strip leading and trailing new lines.  Caller must free the
467 * return.
468 */
469char *owl_util_stripnewlines(const char *in)
470{
471 
472  char  *tmp, *ptr1, *ptr2, *out;
473
474  ptr1=tmp=owl_strdup(in);
475  while (ptr1[0]=='\n') {
476    ptr1++;
477  }
478  ptr2=ptr1+strlen(ptr1)-1;
479  while (ptr2>ptr1 && ptr2[0]=='\n') {
480    ptr2[0]='\0';
481    ptr2--;
482  }
483
484  out=owl_strdup(ptr1);
485  owl_free(tmp);
486  return(out);
487}
488
489/* Delete all lines matching "line" from the named file.  If no such
490 * line is found the file is left intact.  If backup==1 then leave a
491 * backup file containing the original contents.  The match is
492 * case-insensitive.
493 *
494 * Returns the number of lines removed
495 */
496int owl_util_file_deleteline(const char *filename, const char *line, int backup)
497{
498  char *backupfile, *newfile, *buf = NULL;
499  FILE *old, *new;
500  struct stat st;
501  int numremoved = 0;
502
503  if ((old = fopen(filename, "r")) == NULL) {
504    owl_function_error("Cannot open %s (for reading): %s",
505                       filename, strerror(errno));
506    return 0;
507  }
508
509  if (fstat(fileno(old), &st) != 0) {
510    owl_function_error("Cannot stat %s: %s", filename, strerror(errno));
511    return 0;
512  }
513
514  newfile = owl_sprintf("%s.new", filename);
515  if ((new = fopen(newfile, "w")) == NULL) {
516    owl_function_error("Cannot open %s (for writing): %s",
517                       filename, strerror(errno));
518    free(newfile);
519    fclose(old);
520    return 0;
521  }
522
523  if (fchmod(fileno(new), st.st_mode & 0777) != 0) {
524    owl_function_error("Cannot set permissions on %s: %s",
525                       filename, strerror(errno));
526    unlink(newfile);
527    fclose(new);
528    free(newfile);
529    fclose(old);
530    return 0;
531  }
532
533  while (owl_getline_chomp(&buf, old))
534    if (strcasecmp(buf, line) != 0)
535      fprintf(new, "%s\n", buf);
536    else
537      numremoved++;
538  owl_free(buf);
539
540  fclose(new);
541  fclose(old);
542
543  if (backup) {
544    backupfile = owl_sprintf("%s.backup", filename);
545    unlink(backupfile);
546    if (link(filename, backupfile) != 0) {
547      owl_function_error("Cannot link %s: %s", backupfile, strerror(errno));
548      owl_free(backupfile);
549      unlink(newfile);
550      owl_free(newfile);
551      return 0;
552    }
553    owl_free(backupfile);
554  }
555
556  if (rename(newfile, filename) != 0) {
557    owl_function_error("Cannot move %s to %s: %s",
558                       newfile, filename, strerror(errno));
559    numremoved = 0;
560  }
561
562  unlink(newfile);
563  owl_free(newfile);
564
565  return numremoved;
566}
567
568int owl_util_max(int a, int b)
569{
570  if (a>b) return(a);
571  return(b);
572}
573
574int owl_util_min(int a, int b)
575{
576  if (a<b) return(a);
577  return(b);
578}
579
580/* Return the base class or instance from a zephyr class, by removing
581   leading `un' or trailing `.d'.
582   The caller is responsible for freeing the allocated string.
583*/
584char * owl_util_baseclass(const char * class)
585{
586  char *start, *end;
587
588  while(!strncmp(class, "un", 2)) {
589    class += 2;
590  }
591
592  start = owl_strdup(class);
593  end = start + strlen(start) - 1;
594  while(end > start && *end == 'd' && *(end-1) == '.') {
595    end -= 2;
596  }
597  *(end + 1) = 0;
598
599  return start;
600}
601
602const char * owl_get_datadir(void)
603{
604  const char * datadir = getenv("BARNOWL_DATA_DIR");
605  if(datadir != NULL)
606    return datadir;
607  return DATADIR;
608}
609
610const char * owl_get_bindir(void)
611{
612  const char * bindir = getenv("BARNOWL_BIN_DIR");
613  if(bindir != NULL)
614    return bindir;
615  return BINDIR;
616}
617
618/* Strips format characters from a valid utf-8 string. Returns the
619   empty string if 'in' does not validate. */
620char * owl_strip_format_chars(const char *in)
621{
622  char *r;
623  if (g_utf8_validate(in, -1, NULL)) {
624    const char *s, *p;
625    r = owl_malloc(strlen(in)+1);
626    r[0] = '\0';
627    s = in;
628    p = strchr(s, OWL_FMTEXT_UC_STARTBYTE_UTF8);
629    while(p) {
630      /* If it's a format character, copy up to it, and skip all
631         immediately following format characters. */
632      if (owl_fmtext_is_format_char(g_utf8_get_char(p))) {
633        strncat(r, s, p-s);
634        p = g_utf8_next_char(p);
635        while (owl_fmtext_is_format_char(g_utf8_get_char(p))) {
636          p = g_utf8_next_char(p);
637        }
638        s = p;
639        p = strchr(s, OWL_FMTEXT_UC_STARTBYTE_UTF8);
640      }
641      else {
642        p = strchr(p+1, OWL_FMTEXT_UC_STARTBYTE_UTF8);
643      }
644    }
645    if (s) strcat(r,s);
646  }
647  else {
648    r = owl_strdup("");
649  }
650  return r;
651}
652
653/* If in is not UTF-8, convert from ISO-8859-1. We may want to allow
654 * the caller to specify an alternative in the future. We also strip
655 * out characters in Unicode Plane 16, as we use that plane internally
656 * for formatting.
657 */
658char * owl_validate_or_convert(const char *in)
659{
660  if (g_utf8_validate(in, -1, NULL)) {
661    return owl_strip_format_chars(in);
662  }
663  else {
664    return g_convert(in, -1,
665                     "UTF-8", "ISO-8859-1",
666                     NULL, NULL, NULL);
667  }
668}
669/*
670 * Validate 'in' as UTF-8, and either return a copy of it, or an empty
671 * string if it is invalid utf-8.
672 */
673char * owl_validate_utf8(const char *in)
674{
675  char *out;
676  if (g_utf8_validate(in, -1, NULL)) {
677    out = owl_strdup(in);
678  } else {
679    out = owl_strdup("");
680  }
681  return out;
682}
683
684/* This is based on _extract() and _isCJ() from perl's Text::WrapI18N */
685int owl_util_can_break_after(gunichar c)
686{
687 
688  if (c == ' ') return 1;
689  if (c >= 0x3000 && c <= 0x312f) {
690    /* CJK punctuations, Hiragana, Katakana, Bopomofo */
691    if (c == 0x300a || c == 0x300c || c == 0x300e ||
692        c == 0x3010 || c == 0x3014 || c == 0x3016 ||
693        c == 0x3018 || c == 0x301a)
694      return 0;
695    return 1;
696  }
697  if (c >= 0x31a0 && c <= 0x31bf) {return 1;}  /* Bopomofo */
698  if (c >= 0x31f0 && c <= 0x31ff) {return 1;}  /* Katakana extension */
699  if (c >= 0x3400 && c <= 0x9fff) {return 1;}  /* Han Ideogram */
700  if (c >= 0xf900 && c <= 0xfaff) {return 1;}  /* Han Ideogram */
701  if (c >= 0x20000 && c <= 0x2ffff) {return 1;}  /* Han Ideogram */
702  return 0;
703}
704
705char *owl_escape_highbit(const char *str)
706{
707  GString *out = g_string_new("");
708  unsigned char c;
709  while((c = (*str++))) {
710    if(c == '\\') {
711      g_string_append(out, "\\\\");
712    } else if(c & 0x80) {
713      g_string_append_printf(out, "\\x%02x", (int)c);
714    } else {
715      g_string_append_c(out, c);
716    }
717  }
718  return g_string_free(out, 0);
719}
720
721/* innards of owl_getline{,_chomp} below */
722static int owl_getline_internal(char **s, FILE *fp, int newline)
723{
724  int size = 0;
725  int target = 0;
726  int count = 0;
727  int c;
728
729  while (1) {
730    c = getc(fp);
731    if ((target + 1) > size) {
732      size += BUFSIZ;
733      *s = owl_realloc(*s, size);
734    }
735    if (c == EOF)
736      break;
737    count++;
738    if (c != '\n' || newline)
739        (*s)[target++] = c;
740    if (c == '\n')
741      break;
742  }
743  (*s)[target] = 0;
744
745  return count;
746}
747
748/* Read a line from fp, allocating memory to hold it, returning the number of
749 * byte read.  *s should either be NULL or a pointer to memory allocated with
750 * owl_malloc; it will be owl_realloc'd as appropriate.  The caller must
751 * eventually free it.  (This is roughly the interface of getline in the gnu
752 * libc).
753 *
754 * The final newline will be included if it's there.
755 */
756int owl_getline(char **s, FILE *fp)
757{
758  return owl_getline_internal(s, fp, 1);
759}
760
761/* As above, but omitting the final newline */
762int owl_getline_chomp(char **s, FILE *fp)
763{
764  return owl_getline_internal(s, fp, 0);
765}
766
767/* Read the rest of the input available in fp into a string. */
768char *owl_slurp(FILE *fp)
769{
770  char *buf = NULL;
771  char *p;
772  int size = 0;
773  int count;
774
775  while (1) {
776    buf = owl_realloc(buf, size + BUFSIZ);
777    p = &buf[size];
778    size += BUFSIZ;
779
780    if ((count = fread(p, 1, BUFSIZ, fp)) < BUFSIZ)
781      break;
782  }
783  p[count] = 0;
784
785  return buf;
786}
787
788typedef struct { /*noproto*/
789  GObject  *sender;
790  gulong    signal_id;
791} SignalData;
792
793static void _closure_invalidated(gpointer data, GClosure *closure);
794
795/*
796 * GObject's g_signal_connect_object has a documented bug. This function is
797 * identical except it does not leak the signal handler.
798 */
799gulong owl_signal_connect_object(gpointer sender, const gchar *detailed_signal, GCallback c_handler, gpointer receiver, GConnectFlags connect_flags)
800{
801  g_return_val_if_fail (G_TYPE_CHECK_INSTANCE (sender), 0);
802  g_return_val_if_fail (detailed_signal != NULL, 0);
803  g_return_val_if_fail (c_handler != NULL, 0);
804
805  if (receiver) {
806    SignalData *sdata;
807    GClosure *closure;
808    gulong signal_id;
809
810    g_return_val_if_fail (G_IS_OBJECT (receiver), 0);
811
812    closure = ((connect_flags & G_CONNECT_SWAPPED) ? g_cclosure_new_object_swap : g_cclosure_new_object) (c_handler, receiver);
813    signal_id = g_signal_connect_closure (sender, detailed_signal, closure, connect_flags & G_CONNECT_AFTER);
814
815    /* Register the missing hooks */
816    sdata = g_slice_new0(SignalData);
817    sdata->sender = sender;
818    sdata->signal_id = signal_id;
819
820    g_closure_add_invalidate_notifier(closure, sdata, _closure_invalidated);
821
822    return signal_id;
823  } else {
824    return g_signal_connect_data(sender, detailed_signal, c_handler, NULL, NULL, connect_flags);
825  }
826}
827
828/*
829 * There are three ways the signal could come to an end:
830 *
831 * 1. The user explicitly disconnects it with the returned signal_id.
832 *    - In that case, the disconnection unref's the closure, causing it
833 *      to first be invalidated. The handler's already disconnected, so
834 *      we have no work to do.
835 * 2. The sender gets destroyed.
836 *    - GObject will disconnect each signal which then goes into the above
837 *      case. Our handler does no work.
838 * 3. The receiver gets destroyed.
839 *    - The GClosure was created by g_cclosure_new_object_{,swap} which gets
840 *      invalidated when the receiver is destroyed. We then follow through case 1
841 *      again, but *this* time, the handler has not been disconnected. We then
842 *      clean up ourselves.
843 *
844 * We can't actually hook into this process earlier with weakrefs as GObject
845 * will, on object dispose, first disconnect signals, then invalidate closures,
846 * and notify weakrefs last.
847 */
848static void _closure_invalidated(gpointer data, GClosure *closure)
849{
850  SignalData *sdata = data;
851  if (g_signal_handler_is_connected(sdata->sender, sdata->signal_id)) {
852    g_signal_handler_disconnect(sdata->sender, sdata->signal_id);
853  }
854  g_slice_free(SignalData, sdata);
855}
856
Note: See TracBrowser for help on using the repository browser.