2
* caller id related functions
4
* This file is part of ANT (Ant is Not a Telephone)
6
* Copyright 2002, 2003 Roland Stigge
8
* ANT is free software; you can redistribute it and/or modify
9
* it under the terms of the GNU General Public License as published by
10
* the Free Software Foundation; either version 2 of the License, or
11
* (at your option) any later version.
13
* ANT is distributed in the hope that it will be useful,
14
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16
* GNU General Public License for more details.
18
* You should have received a copy of the GNU General Public License
19
* along with ANT; if not, write to the Free Software
20
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26
/* regular GNU system includes */
41
#include <gdk/gdkkeysyms.h>
43
/* own header files */
50
/* graphical symbols */
56
* returns a new data structure (for association with a row)
57
* filled with default values
59
static gpointer cid_row_new(void) {
60
struct cid_row_t *rowdata =
61
(struct cid_row_t *) malloc(sizeof(struct cid_row_t));
63
rowdata->marked_unanswered = 0;
64
return (gpointer) rowdata;
68
* callback to be called for additional row data on destruction of row
70
static void cid_row_destroy(gpointer rowdata) {
72
fprintf(stderr, "debug: destroying rowdata at %p.\n", rowdata);
76
/* called when caller id monitor state check button is toggled */
77
void cid_toggle_cb(GtkWidget *widget _U_, gpointer data, guint action _U_) {
78
session_t *session = (session_t *) data;
80
if (GTK_CHECK_MENU_ITEM (session->cid_check_menu_item)->active) {
81
gtk_widget_show(session->cid);
82
session->option_show_callerid = 1;
84
gtk_widget_hide(session->cid);
85
session->option_show_callerid = 0;
86
gtk_window_resize(GTK_WINDOW(session->main_window), 1, 1);
91
* Callback: called when a button was clicked in row delete dialog
93
static void cid_delete_response_cb(GtkWidget* widget, gint response_id,
96
session_t* session = (session_t*) data;
97
guint row = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(widget), "row"));
98
char* filename = (char*) g_object_get_data(G_OBJECT(widget), "filename");
99
GtkWidget* checkbutton =
100
(GtkWidget*) g_object_get_data(G_OBJECT(widget), "checkbutton");
102
if (response_id == GTK_RESPONSE_OK) {
104
gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(checkbutton)))
108
cid_mark_row(session, row, FALSE); /* to count unanswered calls */
110
/* decrease before removal: "select-row" signal submission -> other cb */
111
gtk_clist_remove(GTK_CLIST(session->cid_list), row);
114
if (filename) free(filename);
115
gtk_widget_destroy(widget);
119
* called on cid delete request
121
* -> deletes specified row without confirmation
123
static void cid_request_delete(GtkWidget *widget _U_, gpointer data,
125
session_t* session = (session_t *) data;
128
GtkWidget* checkbutton;
129
char *filename = cid_get_record_filename(session, row);
131
GtkWidget* dialog = gtk_dialog_new_with_buttons(_("Delete Entry"),
132
GTK_WINDOW(session->main_window),
133
GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_MODAL,
134
GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
135
GTK_STOCK_OK, GTK_RESPONSE_OK,
138
vbox = gtk_vbox_new(FALSE, 16);
139
gtk_container_set_border_width(GTK_CONTAINER(vbox), 16);
140
gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), vbox);
141
gtk_widget_show(vbox);
143
label = gtk_label_new(_("Are you sure you want to\ndelete this entry?"));
144
gtk_label_set_justify(GTK_LABEL(label), GTK_JUSTIFY_CENTER);
145
gtk_box_pack_start_defaults(GTK_BOX(vbox), label);
146
gtk_widget_show(label);
148
checkbutton = gtk_check_button_new_with_label(_("Delete recording"));
149
if (filename) { /* default: checked */
150
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbutton), TRUE);
151
} else { /* if file doesn't exist, grey out */
152
gtk_widget_set_sensitive(checkbutton, FALSE);
154
gtk_box_pack_start_defaults(GTK_BOX(vbox), checkbutton);
155
gtk_widget_show(checkbutton);
157
g_object_set_data(G_OBJECT(dialog), "row", GUINT_TO_POINTER(row));
158
g_object_set_data(G_OBJECT(dialog), "filename", (gpointer) filename);
159
g_object_set_data(G_OBJECT(dialog), "checkbutton", (gpointer) checkbutton);
160
g_signal_connect(dialog, "response", G_CALLBACK(cid_delete_response_cb),
162
gtk_widget_show(dialog);
166
* called on key pressed in cid list
168
static gint cid_key_cb(GtkWidget *widget, GdkEventKey *event, gpointer data) {
170
switch (event->keyval) {
171
case GDK_Delete: /* Delete dialog */
173
/* portability: GtkCList.selection is private! */
174
if (GTK_CLIST(((session_t *) data)->cid_list)->selection) {
175
cid_request_delete(widget, data,
176
GPOINTER_TO_INT(GTK_CLIST(((session_t *) data)->cid_list)->selection->data));
178
return TRUE; /* event handled */
185
* Callback: called on playback request
187
static void cid_playback(GtkWidget *widget _U_, gpointer data, guint row) {
188
session_t *session = (session_t *) data;
190
session->effect_filename = cid_get_record_filename(session, row);
191
session->effect_playback_start_time = time(NULL);
192
session_set_state(session, STATE_PLAYBACK);
196
* Callback: called on "OK" click in "Save as..." file selection
198
static void cid_save_as_filename_cb(GtkWidget *fs) {
199
char* sourcename = g_object_get_data(G_OBJECT(fs), "sourcename");
200
const char* destbase =
201
gtk_file_selection_get_filename(GTK_FILE_SELECTION(fs));
202
if (*destbase && *basename(destbase)) {
203
/* copy if something was entered */
205
char* extension = filename_extension(sourcename);
206
if (0 <= asprintf(&destination, "%s.%s", destbase, extension)) {
207
/* actually copy if source and destination known */
208
int buffer_size = 65536;
209
char* buffer = (char*) malloc(buffer_size);
211
int fd_in = open(sourcename, O_RDONLY);
213
int fd_out = open(destination, O_WRONLY | O_CREAT | O_TRUNC,
214
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH);
218
int to_write = just_read = read(fd_in, buffer, buffer_size);
220
char* buffer_index = buffer;
221
while (to_write && written) {
222
written = write(fd_out, buffer_index, to_write);
224
buffer_index += written;
228
fprintf(stderr, "Error on closing destination file.\n");
232
"Error on destination file (%s) open().\n", destination);
235
fprintf(stderr, "Error on source file.\n");
239
"Error on source file (%s) open().\n", sourcename);
243
fprintf(stderr, "Error on malloc().\n");
247
fprintf(stderr, "Error on asprintf().\n");
251
gtk_widget_destroy(GTK_WIDGET(fs));
255
* Callback: called on "Save as..." request
257
static void cid_save_as(GtkWidget *widget _U_, gpointer data, guint row) {
258
session_t* session = (session_t*) data;
259
char* sourcename = cid_get_record_filename(session, row);
260
char* extension = filename_extension(sourcename);
266
if (0 > asprintf(&title, _("Enter the base filename for %s file"),
269
fprintf(stderr, "Error on asprintf().\n");
272
fs = gtk_file_selection_new(title);
274
g_signal_connect_swapped(GTK_OBJECT(GTK_FILE_SELECTION(fs)->cancel_button),
275
"clicked", G_CALLBACK(gtk_widget_destroy),
277
g_object_set_data(G_OBJECT(fs), "sourcename", sourcename);
278
g_signal_connect_swapped(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
279
"clicked", G_CALLBACK(cid_save_as_filename_cb), fs);
282
fprintf(stderr, "Error: no filename extension found.\n");
287
* Callback: called if something happened in the delete recording dialog
289
static void cid_delete_rec_response_cb(GtkWidget* widget, gint response_id,
292
session_t *session = (session_t *) data;
294
if (response_id == GTK_RESPONSE_OK) {
296
guint row = GPOINTER_TO_UINT(g_object_get_data(G_OBJECT(widget), "row"));
297
unlink(temp = cid_get_record_filename(session, row));
299
cid_row_mark_record(session, row);
301
gtk_widget_destroy(widget);
305
* Callback: called on delete recording request
307
static void cid_delete_rec(GtkWidget *widget _U_, gpointer data, guint row) {
308
session_t *session = (session_t *) data;
310
GtkWidget* dialog = gtk_message_dialog_new(GTK_WINDOW(session->main_window),
311
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_WARNING,
312
GTK_BUTTONS_OK_CANCEL, _("Do you really want to delete this recording?"));
313
g_signal_connect(dialog, "response", G_CALLBACK(cid_delete_rec_response_cb),
315
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
316
g_object_set_data(G_OBJECT(dialog), "row", GINT_TO_POINTER(row));
317
gtk_widget_show(dialog);
321
* called on mouse button pressed in cid list
323
static gint cid_mouse_cb(GtkWidget *widget _U_,
324
GdkEventButton *event, gpointer data) {
325
session_t *session = (session_t *) data;
327
if (event->button == 3) { /* popup menu */
331
if (gtk_clist_get_selection_info(GTK_CLIST(session->cid_list),
332
event->x, event->y, &row, &column)) {
334
GtkItemFactoryEntry menu_items[] = {
335
/*path accel. callb. cb param. kind extra */
336
{_("/_Playback"), NULL, cid_playback, row, "", NULL},
337
{_("/_Save as..."), NULL, cid_save_as, row, "", NULL},
338
{_("/Delete _Recording"),NULL, cid_delete_rec,row, "", NULL},
339
{ "/Sep", NULL, NULL, 0, "<Separator>", NULL},
340
{_("/_Delete Row"), NULL, cid_request_delete, row, "", NULL}
342
GtkMenu *menu = GTK_MENU(gtk_menu_new());
343
GtkItemFactory *item_factory;
344
GtkAccelGroup *accel_group;
346
gint nmenu_items = sizeof(menu_items) / sizeof(menu_items[0]);
348
GtkWidget *playback_item;
349
GtkWidget *save_as_item;
350
GtkWidget *delete_record_item;
351
GtkWidget *delete_item;
355
accel_group = gtk_accel_group_new();
356
item_factory = gtk_item_factory_new(GTK_TYPE_MENU, "<popup>",
358
gtk_item_factory_create_items_ac(item_factory, nmenu_items, menu_items,
361
if (!(playback_item = gtk_item_factory_get_item(item_factory,
362
temp = stripchr(_("/_Playback"), '_'))))
363
fprintf(stderr, "Error getting playback_item.\n");
365
if (!(save_as_item = gtk_item_factory_get_item(item_factory,
366
temp = stripchr(_("/_Save as..."), '_'))))
367
fprintf(stderr, "Error getting save_as_item.\n");
369
if (!(delete_record_item = gtk_item_factory_get_item(item_factory,
370
temp = stripchr(_("/Delete _Recording"), '_'))))
371
fprintf(stderr, "Error getting delete_record_item_item.\n");
373
if (!(delete_item = gtk_item_factory_get_item(item_factory,
374
temp = stripchr(_("/_Delete Row"), '_'))))
375
fprintf(stderr, "Error getting delete_item.\n");
378
if (!(session->state == STATE_READY &&
379
(fn = cid_get_record_filename(session, row)))) {
380
gtk_widget_set_sensitive(playback_item, FALSE);
381
gtk_widget_set_sensitive(save_as_item, FALSE);
382
gtk_widget_set_sensitive(delete_record_item, FALSE);
387
menu = GTK_MENU(gtk_item_factory_get_widget(item_factory, "<popup>"));
388
gtk_menu_set_accel_group(menu, accel_group);
390
gtk_menu_popup(menu, NULL, NULL, NULL, NULL, event->button, event->time);
392
return TRUE; /* event handled */
399
* called on row select (to unmark lines)
401
static void cid_select_row_cb(GtkCList *list _U_, gint row, gint column _U_,
402
GdkEventButton *button _U_, gpointer data) {
403
session_t *session = (session_t *) data;
405
if (row < session->cid_num)
406
cid_mark_row(session, row, FALSE);
410
* Returns the Caller ID section as GtkWidget,
411
* sets CID related members in session
412
* NOTE: caller has to gtk_widget_show() this widget
414
GtkWidget *cid_new(session_t *session) {
415
GtkWidget *frame; /* frame with comment */
416
GtkWidget *scrolled_window; /* the home for the clist */
417
GtkWidget *clist; /* the list itself */
418
GtkStyle *style; /* needed for preparing symbols */
419
GtkWidget *pixmapwidget; /* record symbol */
420
gchar *titles[CID_COL_NUMBER];
422
style = gtk_widget_get_style(session->main_window);
423
frame = gtk_frame_new(_("Caller ID"));
425
titles[CID_COL_FLAGS ] = "";
426
titles[CID_COL_TIME ] = _("Date/Time");
427
titles[CID_COL_TYPE ] = _("Type");
428
titles[CID_COL_FROM ] = _("From");
429
titles[CID_COL_TO ] = _("To");
430
titles[CID_COL_DURATION] = _("Duration");
432
gtk_container_set_border_width(GTK_CONTAINER(frame), 8);
434
/* the scrolled window: no special adjustments */
435
scrolled_window = gtk_scrolled_window_new(NULL, NULL);
436
/* always vertical scroll bar, horizontal... well... automatic means no ;) */
437
gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
438
GTK_POLICY_AUTOMATIC, GTK_POLICY_ALWAYS);
439
gtk_container_add(GTK_CONTAINER(frame), scrolled_window);
440
gtk_container_set_border_width(GTK_CONTAINER(scrolled_window), 8);
441
gtk_widget_show(scrolled_window);
443
/* the list widget */
444
clist = gtk_clist_new_with_titles(CID_COL_NUMBER, titles);
445
session->symbol_record_pixmap = gdk_pixmap_create_from_xpm_d(
446
session->main_window->window,
447
&session->symbol_record_bitmap,
448
&style->bg[GTK_STATE_NORMAL],
449
(gchar **) record_xpm);
450
pixmapwidget = gtk_pixmap_new(session->symbol_record_pixmap,
451
session->symbol_record_bitmap);
452
gtk_clist_set_column_widget(GTK_CLIST(clist), CID_COL_FLAGS, pixmapwidget);
454
/* default: GTK_SELECTION_MULTIPLE: */
455
gtk_clist_set_selection_mode(GTK_CLIST(clist), GTK_SELECTION_BROWSE);
457
gtk_clist_set_shadow_type(GTK_CLIST(clist), GTK_SHADOW_IN);
458
gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_FLAGS, 14);
459
gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_TIME, 128);
460
gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_TYPE, 30);
461
gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_FROM, 100);
462
gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_TO, 100);
463
gtk_clist_set_column_width(GTK_CLIST(clist), CID_COL_DURATION, 80);
464
/*gtk_clist_set_column_width(GTK_CLIST(clist), 5, 80);*/
465
gtk_container_add(GTK_CONTAINER(scrolled_window), clist);
466
gtk_clist_column_titles_show(GTK_CLIST(clist));
467
gtk_widget_set_size_request(clist, 500, 128);
469
gtk_signal_connect(GTK_OBJECT(clist), "key-press-event",
470
GTK_SIGNAL_FUNC(cid_key_cb), session);
471
gtk_signal_connect(GTK_OBJECT(clist), "button-press-event",
472
GTK_SIGNAL_FUNC(cid_mouse_cb), session);
473
gtk_signal_connect(GTK_OBJECT(clist), "select-row",
474
GTK_SIGNAL_FUNC(cid_select_row_cb), session);
476
gtk_widget_show(clist);
478
/* set up IN/OUT symbols */
479
session->symbol_in_pixmap = gdk_pixmap_create_from_xpm_d(
480
session->main_window->window,
481
&session->symbol_in_bitmap,
482
&style->bg[GTK_STATE_NORMAL],
484
session->symbol_out_pixmap = gdk_pixmap_create_from_xpm_d(
485
session->main_window->window,
486
&session->symbol_out_bitmap,
487
&style->bg[GTK_STATE_NORMAL],
489
session->cid = frame;
490
session->cid_list = clist;
491
session->cid_scrolled_window = scrolled_window;
497
* select last item in cid list and adjust view to end of list
499
void cid_jump_to_end(session_t *session) {
502
gtk_clist_select_row(GTK_CLIST(session->cid_list),
503
session->cid_num - 1, 0);
505
adj = gtk_scrolled_window_get_vadjustment(
506
GTK_SCROLLED_WINDOW(session->cid_scrolled_window));
507
gtk_adjustment_set_value(adj, fmax(adj->lower, adj->upper - adj->page_size));
511
* remove unneeded entries from caller id list (shorten log)
513
void cid_normalize(session_t *session) {
514
while (session->cid_num_max && session->cid_num > session->cid_num_max) {
515
gtk_clist_remove(GTK_CLIST(session->cid_list), 0);
521
* returns a dynamically allocated string in cid conformant time format
522
* caller has got to take care of freeing this memory after usage
524
static char *cid_timestring(time_t t) {
525
char *date = (char *) malloc(20);
529
len = strftime(date, 20, "%Y-%m-%d %H:%M:%S", localtime(&t));
530
if (len == 0 && date[0] != '\0') {
531
fprintf(stderr, "cid: Error calculating time with strftime.\n");
539
* insert new entry at end of list
541
* from == NULL, if still unknown
543
void cid_add_line(session_t *session,
544
enum call_type_t ct, gchar *from, gchar *to) {
545
gchar *line[CID_COL_NUMBER];
552
line[CID_COL_TIME] = cid_timestring(session->ring_time);
554
line[CID_COL_FLAGS] = "";
555
line[CID_COL_TYPE] = ""; /* call type */
557
if (from == NULL) /* the from field */
558
line[CID_COL_FROM] = "???";
560
line[CID_COL_FROM] = from;
561
line[CID_COL_TO] = to;
563
line[CID_COL_DURATION] = "???"; /* duration */
566
session->cid_num = gtk_clist_append(GTK_CLIST(session->cid_list), line) + 1;
567
gtk_clist_set_row_data_full(GTK_CLIST(session->cid_list),
568
session->cid_num - 1,
569
cid_row_new(), cid_row_destroy);
571
cid_normalize(session);
573
/* actually set pixmap */
575
pixmap = session->symbol_in_pixmap;
576
bitmap = session->symbol_in_bitmap;
579
pixmap = session->symbol_out_pixmap;
580
bitmap = session->symbol_out_bitmap;
583
gtk_clist_set_pixtext(GTK_CLIST(session->cid_list), session->cid_num - 1 ,
584
CID_COL_TYPE, typestring, 50, pixmap, bitmap);
586
cid_jump_to_end(session);
589
free(line[CID_COL_TIME]);
593
* set "date" field in last line
595
void cid_set_date(session_t *session, time_t date) {
598
gtk_clist_set_text(GTK_CLIST(session->cid_list),
599
session->cid_num - 1, CID_COL_TIME,
600
temp = cid_timestring(date));
606
* set "from" field in last line
608
void cid_set_from(session_t *session, gchar *from) {
610
gtk_clist_set_text(GTK_CLIST(session->cid_list),
611
session->cid_num - 1, CID_COL_FROM, from);
615
* complete last line with "duration"
617
* if message == NULL, current time will be used to calculate a duration
618
* if message != NULL, this string will be displayed in the Duration field (#4)
620
void cid_set_duration(session_t *session, gchar *message) {
621
char *buf = timediff_str(time(NULL), session->vcon_time);
624
gtk_clist_set_text(GTK_CLIST(session->cid_list), session->cid_num - 1,
625
CID_COL_DURATION, message);
627
gtk_clist_set_text(GTK_CLIST(session->cid_list), session->cid_num - 1,
628
CID_COL_DURATION, buf);
633
* Add a line to clist (e.g. from saved caller id file)
635
void cid_add_saved_line(session_t *session, char *date, char *type,
636
char *from, char *to, char *duration) {
638
fprintf(stderr, "Caller ID add:\n"
639
"Date: |%s|, Type: |%s|, From: |%s|, To: |%s|, Dur: |%s|\n",
640
date, type, from, to, duration);
642
cid_add_line(session,
643
!strncmp(type, "IN", 2) ? CALL_IN : CALL_OUT, from, to);
644
gtk_clist_set_text(GTK_CLIST(session->cid_list),
645
session->cid_num - 1, CID_COL_TIME, date);
646
gtk_clist_set_text(GTK_CLIST(session->cid_list),
647
session->cid_num - 1, CID_COL_DURATION, duration);
649
cid_row_mark_record(session, session->cid_num - 1);
654
* Merges isdnlog data into existing cid list
656
void cid_calls_merge(session_t *session) {
657
int low, high, mid; /* bounds and helper index */
658
time_t start = time(NULL)
659
- 60 * 60 * 24 * session->option_calls_merge_max_days;
660
int callstime; /* time of calls file line */
661
char *cidstr; /* time of cid_list entry */
662
size_t linelength = 100; /* buffer size */
663
char *line = (char *) malloc (linelength); /* initial buffer for getline() */
666
char from[41]; /* data to read from calls file */
674
int cidline; /* index into cid list */
675
gchar *linearr[CID_COL_NUMBER]; /* line to insert to cid_list */
679
char *cache; /* tracks successive dates */
681
char* calls_filename;
683
/* try to find isdnlog data file */
684
calls_filename = isdn_get_calls_filename();
685
if (calls_filename && (f = fopen(calls_filename, "r"))) {
687
fprintf(stderr, "Using %s as source for isdnlog data.\n", calls_filename);
689
if (session->option_calls_merge_max_days) {
690
/* binary search on the file for the desired starting time if needed */
692
fprintf(stderr, "Binary search in calls file...\n");
695
fseek(f, 0, SEEK_END);
698
while (high - low > 200) {
700
fprintf(stderr, "low = %d, high = %d\n", low, high);
702
mid = (low + high) / 2;
703
fseek(f, mid, SEEK_SET);
704
getline(&line, &linelength, f); /* discard potentially partial line */
705
if (-1 != getline(&line, &linelength, f)) {
706
if (!sscanf(line, "%*40[^|]|%*40[^|]|%*40[^|]|%*40[^|]|%*40[^|]|%d",
709
if (callstime < start)
716
fseek(f, low, SEEK_SET);
717
getline(&line, &linelength, f);
720
/* read in all remaining lines: merge */
724
/* get new calls line */
725
if (-1 == getline(&line, &linelength, f)) break;
727
"%*40[^|]|%40[^|]|%40[^|]|%d|%*40[^|]|%d|%*40[^|]|%1c",
728
from, to, &duration, &date, type) != 5) {
729
fprintf(stderr, "Warning: Incomplete data input from calls file.\n");
732
if ((temp = strchr(from, ' '))) *temp = '\0';
733
if ((temp = strchr(to, ' '))) *temp = '\0';
734
if (type[0] == 'I') {
737
pixmap = session->symbol_in_pixmap;
738
bitmap = session->symbol_in_bitmap;
743
pixmap = session->symbol_out_pixmap;
744
bitmap = session->symbol_out_bitmap;
747
/* prepare (text) line to merge */
748
linearr[CID_COL_TIME] = cid_timestring((time_t)date);
749
linearr[CID_COL_TYPE] = type;
751
linearr[CID_COL_FROM] = from;
753
linearr[CID_COL_FROM] = "???";
755
linearr[CID_COL_TO] = to;
757
linearr[CID_COL_TO] = "???";
759
linearr[CID_COL_DURATION] = strdup(_("(UNKNOWN)"));
761
linearr[CID_COL_DURATION] = timediff_str(duration, (time_t) 0);
764
/* where to merge it in? */
765
rel = 1; /* init to merge by default */
766
while (cidline < session->cid_num &&
767
(gtk_clist_get_text(GTK_CLIST(session->cid_list),
768
cidline, CID_COL_TIME, &cidstr),
769
(rel = strcmp(cidstr, linearr[CID_COL_TIME])) < 0)) {
774
if ((rel > 0 && strcmp(linearr[CID_COL_TIME], cache) > 0)
775
|| cidline == session->cid_num) {
776
/* only merge new and successive entries */
778
gtk_clist_insert(GTK_CLIST(session->cid_list), cidline, linearr);
779
gtk_clist_set_row_data_full(GTK_CLIST(session->cid_list), cidline,
780
cid_row_new(), cid_row_destroy);
781
gtk_clist_set_pixtext(GTK_CLIST(session->cid_list),
782
cidline, CID_COL_TYPE, type, 50, pixmap, bitmap);
786
cache = linearr[CID_COL_TIME];
787
free(linearr[CID_COL_DURATION]);
791
cid_normalize(session);
794
fprintf(stderr, "Error closing %s.\n", calls_filename);
795
} else { /* error on fopen() */
797
"Warning: Couldn't open isdnlog calls logfile. Proceeding without it.\n");
803
* - marks caller id row as unanswered
804
* - sets window title to reflect number of unanswered calls
807
* row number of specified row
808
* state 1 for mark, 0 for unmark
810
void cid_mark_row(session_t *session, int row, int state) {
811
struct cid_row_t *rowdata = (struct cid_row_t *) gtk_clist_get_row_data(
812
GTK_CLIST(session->cid_list), row);
816
if (rowdata->marked_unanswered != state) {
818
gdk_color_parse("#FF8888", &color);
819
session->unanswered++;
820
rowdata->marked_unanswered = 1;
822
gdk_color_parse("#FFFFFF", &color);
823
session->unanswered--;
824
rowdata->marked_unanswered = 0;
826
gtk_clist_set_background(GTK_CLIST(session->cid_list), row, &color);
827
if (session->unanswered
828
&& 0 < asprintf(&title, _("ANT: %d unanswered"), session->unanswered)) {
829
gtk_window_set_title(GTK_WINDOW(session->main_window), title);
832
gtk_window_set_title(GTK_WINDOW(session->main_window), "ANT " VERSION);
838
* returns timestring with just digits
839
* - caller has got to free() the result
841
char* cid_purify_timestring(char *s) {
852
result_index = result = (char*) malloc (length + 1);
856
*(result_index++) = *t;
859
*(result_index++) = '\0';
865
* returns name of sound filename, if it exists; NULL otherwise
866
* - caller has go to free() result
868
char* cid_get_record_filename(session_t* session, int row) {
873
char* result; /* found something */
875
gtk_clist_get_text(GTK_CLIST(session->cid_list), row, CID_COL_TIME, ×tr);
876
timestr = cid_purify_timestring(timestr);
878
if (!(homedir = get_homedir())) {
879
fprintf(stderr, "Warning: Couldn't get home dir.\n");
883
if (asprintf(&pattern, "%s/." PACKAGE "/recordings/%s.*",
884
homedir, timestr) < 0) {
885
fprintf(stderr, "Warning: "
886
"Couldn't allocate memory for filename globbing pattern.\n");
890
switch (glob(pattern, 0, NULL, &g)) {
892
result = strdup(*(g.gl_pathv));
898
fprintf(stderr, "Warning: "
899
"globbing error while looking up recorded conversation.\n");
911
* marks row with record sign if recording available
912
* - deletes mark if recording was removed before
914
void cid_row_mark_record(session_t* session, int row) {
917
if ((fn = cid_get_record_filename(session, row))) {
918
gtk_clist_set_pixmap(GTK_CLIST(session->cid_list), row, CID_COL_FLAGS,
919
session->symbol_record_pixmap, session->symbol_record_bitmap);
922
gtk_clist_set_text(GTK_CLIST(session->cid_list), row, CID_COL_FLAGS, "");