~jordunn/meenix/Master

« back to all changes in this revision

Viewing changes to src/gtk/dccgui.c

  • Committer: Jordan Dunn
  • Date: 2011-05-01 22:42:27 UTC
  • Revision ID: git-v1:1ba1788f6af88891278ff2926d536c40144bc581
MovingĀ filesĀ around

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* X-Chat
 
2
 * Copyright (C) 1998-2006 Peter Zelezny.
 
3
 *
 
4
 * This program is free software; you can redistribute it and/or modify
 
5
 * it under the terms of the GNU General Public License as published by
 
6
 * the Free Software Foundation; either version 2 of the License, or
 
7
 * (at your option) any later version.
 
8
 *
 
9
 * This program is distributed in the hope that it will be useful,
 
10
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
11
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
12
 * GNU General Public License for more details.
 
13
 *
 
14
 * You should have received a copy of the GNU General Public License
 
15
 * along with this program; if not, write to the Free Software
 
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
 
17
 */
 
18
 
 
19
#include <stdio.h>
 
20
#include <stdlib.h>
 
21
#include <string.h>
 
22
#include <sys/stat.h>
 
23
#include <fcntl.h>
 
24
#include <time.h>
 
25
 
 
26
#define WANTSOCKET
 
27
#define WANTARPA
 
28
#include "../common/inet.h"
 
29
#include "fe-gtk.h"
 
30
 
 
31
#include <gtk/gtkhbbox.h>
 
32
#include <gtk/gtkhbox.h>
 
33
#include <gtk/gtklabel.h>
 
34
#include <gtk/gtkstock.h>
 
35
#include <gtk/gtkmessagedialog.h>
 
36
#include <gtk/gtktable.h>
 
37
#include <gtk/gtktreeview.h>
 
38
#include <gtk/gtkexpander.h>
 
39
#include <gtk/gtkliststore.h>
 
40
#include <gtk/gtktreeselection.h>
 
41
#include <gtk/gtkcellrendererpixbuf.h>
 
42
#include <gtk/gtkcellrenderertext.h>
 
43
#include <gtk/gtkcheckmenuitem.h>
 
44
#include <gtk/gtkradiobutton.h>
 
45
#include <gtk/gtkversion.h>
 
46
 
 
47
#include "../common/xchat.h"
 
48
#include "../common/xchatc.h"
 
49
#include "../common/fe.h"
 
50
#include "../common/util.h"
 
51
#include "../common/network.h"
 
52
#include "gtkutil.h"
 
53
#include "palette.h"
 
54
#include "maingui.h"
 
55
 
 
56
 
 
57
enum    /* DCC SEND/RECV */
 
58
{
 
59
        COL_TYPE,
 
60
        COL_STATUS,
 
61
        COL_FILE,
 
62
        COL_SIZE,
 
63
        COL_POS,
 
64
        COL_PERC,
 
65
        COL_SPEED,
 
66
        COL_ETA,
 
67
        COL_NICK,
 
68
        COL_DCC, /* struct DCC * */
 
69
        COL_COLOR,      /* GdkColor */
 
70
        N_COLUMNS
 
71
};
 
72
 
 
73
enum    /* DCC CHAT */
 
74
{
 
75
        CCOL_STATUS,
 
76
        CCOL_NICK,
 
77
        CCOL_RECV,
 
78
        CCOL_SENT,
 
79
        CCOL_START,
 
80
        CCOL_DCC,       /* struct DCC * */
 
81
        CCOL_COLOR,     /* GdkColor * */
 
82
        CN_COLUMNS
 
83
};
 
84
 
 
85
struct dccwindow
 
86
{
 
87
        GtkWidget *window;
 
88
 
 
89
        GtkWidget *list;
 
90
        GtkListStore *store;
 
91
        GtkTreeSelection *sel;
 
92
 
 
93
        GtkWidget *abort_button;
 
94
        GtkWidget *accept_button;
 
95
        GtkWidget *resume_button;
 
96
        GtkWidget *open_button;
 
97
 
 
98
        GtkWidget *file_label;
 
99
        GtkWidget *address_label;
 
100
};
 
101
 
 
102
struct my_dcc_send
 
103
{
 
104
        struct session *sess;
 
105
        char *nick;
 
106
        int maxcps;
 
107
        int passive;
 
108
};
 
109
 
 
110
static struct dccwindow dccfwin = {NULL, };     /* file */
 
111
static struct dccwindow dcccwin = {NULL, };     /* chat */
 
112
static GdkPixbuf *pix_up = NULL;        /* down arrow */
 
113
static GdkPixbuf *pix_dn = NULL;        /* up arrow */
 
114
static int win_width = 600;
 
115
static int win_height = 256;
 
116
static short view_mode; /* 1=download 2=upload 3=both */
 
117
#define VIEW_DOWNLOAD 1
 
118
#define VIEW_UPLOAD 2
 
119
#define VIEW_BOTH 3
 
120
 
 
121
#define KILOBYTE 1024
 
122
#define MEGABYTE (KILOBYTE * 1024)
 
123
#define GIGABYTE (MEGABYTE * 1024)
 
124
 
 
125
 
 
126
static void
 
127
proper_unit (DCC_SIZE size, char *buf, int buf_len)
 
128
{
 
129
        if (size <= KILOBYTE)
 
130
        {
 
131
                snprintf (buf, buf_len, "%"DCC_SFMT"B", size);
 
132
        }
 
133
        else if (size > KILOBYTE && size <= MEGABYTE)
 
134
        {
 
135
                snprintf (buf, buf_len, "%"DCC_SFMT"kB", size / KILOBYTE);
 
136
        }
 
137
        else
 
138
        {
 
139
                snprintf (buf, buf_len, "%.2fMB", (float)size / MEGABYTE);
 
140
        }
 
141
}
 
142
 
 
143
static void
 
144
dcc_send_filereq_file (struct my_dcc_send *mdc, char *file)
 
145
{
 
146
        if (file)
 
147
                dcc_send (mdc->sess, mdc->nick, file, mdc->maxcps, mdc->passive);
 
148
        else
 
149
        {
 
150
                free (mdc->nick);
 
151
                free (mdc);
 
152
        }
 
153
}
 
154
 
 
155
void
 
156
fe_dcc_send_filereq (struct session *sess, char *nick, int maxcps, int passive)
 
157
{
 
158
        char tbuf[128];
 
159
        struct my_dcc_send *mdc;
 
160
        
 
161
        mdc = malloc (sizeof (*mdc));
 
162
        mdc->sess = sess;
 
163
        mdc->nick = strdup (nick);
 
164
        mdc->maxcps = maxcps;
 
165
        mdc->passive = passive;
 
166
 
 
167
        snprintf (tbuf, sizeof tbuf, _("Send file to %s"), nick);
 
168
        gtkutil_file_req (tbuf, dcc_send_filereq_file, mdc, NULL, FRF_MULTIPLE);
 
169
}
 
170
 
 
171
static void
 
172
dcc_prepare_row_chat (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter,
 
173
                                                         gboolean update_only)
 
174
{
 
175
        static char pos[16], siz[16];
 
176
        char *date;
 
177
 
 
178
        date = ctime (&dcc->starttime);
 
179
        date[strlen (date) - 1] = 0;    /* remove the \n */
 
180
 
 
181
        proper_unit (dcc->pos, pos, sizeof (pos));
 
182
        proper_unit (dcc->size, siz, sizeof (siz));
 
183
 
 
184
        gtk_list_store_set (store, iter,
 
185
                                                          CCOL_STATUS, _(dccstat[dcc->dccstat].name),
 
186
                                                          CCOL_NICK, dcc->nick,
 
187
                                                          CCOL_RECV, pos,
 
188
                                                          CCOL_SENT, siz,
 
189
                                                          CCOL_START, date,
 
190
                                                          CCOL_DCC, dcc,
 
191
                                                          CCOL_COLOR,
 
192
                                                          dccstat[dcc->dccstat].color == 1 ?
 
193
                                                                NULL :
 
194
                                                                colors + dccstat[dcc->dccstat].color,
 
195
                                                          -1);
 
196
}
 
197
 
 
198
static void
 
199
dcc_prepare_row_send (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter,
 
200
                                                         gboolean update_only)
 
201
{
 
202
        static char pos[16], size[16], kbs[14], perc[14], eta[14];
 
203
        int to_go;
 
204
        float per;
 
205
 
 
206
        if (!pix_up)
 
207
                pix_up = gtk_widget_render_icon (dccfwin.window, "gtk-go-up",
 
208
                                                                                                        GTK_ICON_SIZE_MENU, NULL);
 
209
 
 
210
        /* percentage ack'ed */
 
211
        per = (float) ((dcc->ack * 100.00) / dcc->size);
 
212
        proper_unit (dcc->size, size, sizeof (size));
 
213
        proper_unit (dcc->pos, pos, sizeof (pos));
 
214
        snprintf (kbs, sizeof (kbs), "%.1f", ((float)dcc->cps) / 1024);
 
215
/*      proper_unit (dcc->ack, ack, sizeof (ack));*/
 
216
        snprintf (perc, sizeof (perc), "%.0f%%", per);
 
217
        if (dcc->cps != 0)
 
218
        {
 
219
                to_go = (dcc->size - dcc->ack) / dcc->cps;
 
220
                snprintf (eta, sizeof (eta), "%.2d:%.2d:%.2d",
 
221
                                         to_go / 3600, (to_go / 60) % 60, to_go % 60);
 
222
        } else
 
223
                strcpy (eta, "--:--:--");
 
224
 
 
225
        if (update_only)
 
226
                gtk_list_store_set (store, iter,
 
227
                                                                  COL_STATUS, _(dccstat[dcc->dccstat].name),
 
228
                                                                  COL_POS, pos,
 
229
                                                                  COL_PERC, perc,
 
230
                                                                  COL_SPEED, kbs,
 
231
                                                                  COL_ETA, eta,
 
232
                                                                  COL_COLOR,
 
233
                                                                  dccstat[dcc->dccstat].color == 1 ?
 
234
                                                                        NULL :
 
235
                                                                        colors + dccstat[dcc->dccstat].color,
 
236
                                                                        -1);
 
237
        else
 
238
                gtk_list_store_set (store, iter,
 
239
                                                                  COL_TYPE, pix_up,
 
240
                                                                  COL_STATUS, _(dccstat[dcc->dccstat].name),
 
241
                                                                  COL_FILE, file_part (dcc->file),
 
242
                                                                  COL_SIZE, size,
 
243
                                                                  COL_POS, pos,
 
244
                                                                  COL_PERC, perc,
 
245
                                                                  COL_SPEED, kbs,
 
246
                                                                  COL_ETA, eta,
 
247
                                                                  COL_NICK, dcc->nick,
 
248
                                                                  COL_DCC, dcc,
 
249
                                                                  COL_COLOR,
 
250
                                                                  dccstat[dcc->dccstat].color == 1 ?
 
251
                                                                        NULL :
 
252
                                                                        colors + dccstat[dcc->dccstat].color,
 
253
                                                                        -1);
 
254
}
 
255
 
 
256
static void
 
257
dcc_prepare_row_recv (struct DCC *dcc, GtkListStore *store, GtkTreeIter *iter,
 
258
                                                         gboolean update_only)
 
259
{
 
260
        static char size[16], pos[16], kbs[16], perc[14], eta[16];
 
261
        float per;
 
262
        int to_go;
 
263
 
 
264
        if (!pix_dn)
 
265
                pix_dn = gtk_widget_render_icon (dccfwin.window, "gtk-go-down",
 
266
                                                                                                        GTK_ICON_SIZE_MENU, NULL);
 
267
 
 
268
        proper_unit (dcc->size, size, sizeof (size));
 
269
        if (dcc->dccstat == STAT_QUEUED)
 
270
                proper_unit (dcc->resumable, pos, sizeof (pos));
 
271
        else
 
272
                proper_unit (dcc->pos, pos, sizeof (pos));
 
273
        snprintf (kbs, sizeof (kbs), "%.1f", ((float)dcc->cps) / 1024);
 
274
        /* percentage recv'ed */
 
275
        per = (float) ((dcc->pos * 100.00) / dcc->size);
 
276
        snprintf (perc, sizeof (perc), "%.0f%%", per);
 
277
        if (dcc->cps != 0)
 
278
        {
 
279
                to_go = (dcc->size - dcc->pos) / dcc->cps;
 
280
                snprintf (eta, sizeof (eta), "%.2d:%.2d:%.2d",
 
281
                                         to_go / 3600, (to_go / 60) % 60, to_go % 60);
 
282
        } else
 
283
                strcpy (eta, "--:--:--");
 
284
 
 
285
        if (update_only)
 
286
                gtk_list_store_set (store, iter,
 
287
                                                                  COL_STATUS, _(dccstat[dcc->dccstat].name),
 
288
                                                                  COL_POS, pos,
 
289
                                                                  COL_PERC, perc,
 
290
                                                                  COL_SPEED, kbs,
 
291
                                                                  COL_ETA, eta,
 
292
                                                                  COL_COLOR,
 
293
                                                                  dccstat[dcc->dccstat].color == 1 ?
 
294
                                                                        NULL :
 
295
                                                                        colors + dccstat[dcc->dccstat].color,
 
296
                                                                        -1);
 
297
        else
 
298
                gtk_list_store_set (store, iter,
 
299
                                                                  COL_TYPE, pix_dn,
 
300
                                                                  COL_STATUS, _(dccstat[dcc->dccstat].name),
 
301
                                                                  COL_FILE, file_part (dcc->file),
 
302
                                                                  COL_SIZE, size,
 
303
                                                                  COL_POS, pos,
 
304
                                                                  COL_PERC, perc,
 
305
                                                                  COL_SPEED, kbs,
 
306
                                                                  COL_ETA, eta,
 
307
                                                                  COL_NICK, dcc->nick,
 
308
                                                                  COL_DCC, dcc,
 
309
                                                                  COL_COLOR,
 
310
                                                                  dccstat[dcc->dccstat].color == 1 ?
 
311
                                                                        NULL :
 
312
                                                                        colors + dccstat[dcc->dccstat].color,
 
313
                                                                        -1);
 
314
}
 
315
 
 
316
static gboolean
 
317
dcc_find_row (struct DCC *find_dcc, GtkTreeModel *model, GtkTreeIter *iter, int col)
 
318
{
 
319
        struct DCC *dcc;
 
320
 
 
321
        if (gtk_tree_model_get_iter_first (model, iter))
 
322
        {
 
323
                do
 
324
                {
 
325
                        gtk_tree_model_get (model, iter, col, &dcc, -1);
 
326
                        if (dcc == find_dcc)
 
327
                                return TRUE;
 
328
                }
 
329
                while (gtk_tree_model_iter_next (model, iter));
 
330
        }
 
331
 
 
332
        return FALSE;
 
333
}
 
334
 
 
335
static void
 
336
dcc_update_recv (struct DCC *dcc)
 
337
{
 
338
        GtkTreeIter iter;
 
339
 
 
340
        if (!dccfwin.window)
 
341
                return;
 
342
 
 
343
        if (!dcc_find_row (dcc, GTK_TREE_MODEL (dccfwin.store), &iter, COL_DCC))
 
344
                return;
 
345
 
 
346
        dcc_prepare_row_recv (dcc, dccfwin.store, &iter, TRUE);
 
347
}
 
348
 
 
349
static void
 
350
dcc_update_chat (struct DCC *dcc)
 
351
{
 
352
        GtkTreeIter iter;
 
353
 
 
354
        if (!dcccwin.window)
 
355
                return;
 
356
 
 
357
        if (!dcc_find_row (dcc, GTK_TREE_MODEL (dcccwin.store), &iter, CCOL_DCC))
 
358
                return;
 
359
 
 
360
        dcc_prepare_row_chat (dcc, dcccwin.store, &iter, TRUE);
 
361
}
 
362
 
 
363
static void
 
364
dcc_update_send (struct DCC *dcc)
 
365
{
 
366
        GtkTreeIter iter;
 
367
 
 
368
        if (!dccfwin.window)
 
369
                return;
 
370
 
 
371
        if (!dcc_find_row (dcc, GTK_TREE_MODEL (dccfwin.store), &iter, COL_DCC))
 
372
                return;
 
373
 
 
374
        dcc_prepare_row_send (dcc, dccfwin.store, &iter, TRUE);
 
375
}
 
376
 
 
377
static void
 
378
close_dcc_file_window (GtkWindow *win, gpointer data)
 
379
{
 
380
        dccfwin.window = NULL;
 
381
}
 
382
 
 
383
static void
 
384
dcc_append (struct DCC *dcc, GtkListStore *store, gboolean prepend)
 
385
{
 
386
        GtkTreeIter iter;
 
387
 
 
388
        if (prepend)
 
389
                gtk_list_store_prepend (store, &iter);
 
390
        else
 
391
                gtk_list_store_append (store, &iter);
 
392
 
 
393
        if (dcc->type == TYPE_RECV)
 
394
                dcc_prepare_row_recv (dcc, store, &iter, FALSE);
 
395
        else
 
396
                dcc_prepare_row_send (dcc, store, &iter, FALSE);
 
397
}
 
398
 
 
399
static void
 
400
dcc_fill_window (int flags)
 
401
{
 
402
        struct DCC *dcc;
 
403
        GSList *list;
 
404
        GtkTreeIter iter;
 
405
        int i = 0;
 
406
 
 
407
        gtk_list_store_clear (GTK_LIST_STORE (dccfwin.store));
 
408
 
 
409
        if (flags & VIEW_UPLOAD)
 
410
        {
 
411
                list = dcc_list;
 
412
                while (list)
 
413
                {
 
414
                        dcc = list->data;
 
415
                        if (dcc->type == TYPE_SEND)
 
416
                        {
 
417
                                dcc_append (dcc, dccfwin.store, FALSE);
 
418
                                i++;
 
419
                        }
 
420
                        list = list->next;
 
421
                }
 
422
        }
 
423
 
 
424
        if (flags & VIEW_DOWNLOAD)
 
425
        {
 
426
                list = dcc_list;
 
427
                while (list)
 
428
                {
 
429
                        dcc = list->data;
 
430
                        if (dcc->type == TYPE_RECV)
 
431
                        {
 
432
                                dcc_append (dcc, dccfwin.store, FALSE);
 
433
                                i++;
 
434
                        }
 
435
                        list = list->next;
 
436
                }
 
437
        }
 
438
 
 
439
        /* if only one entry, select it (so Accept button can work) */
 
440
        if (i == 1)
 
441
        {
 
442
                gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dccfwin.store), &iter);
 
443
                gtk_tree_selection_select_iter (dccfwin.sel, &iter);
 
444
        }
 
445
}
 
446
 
 
447
/* return list of selected DCCs */
 
448
 
 
449
static GSList *
 
450
treeview_get_selected (GtkTreeModel *model, GtkTreeSelection *sel, int column)
 
451
{
 
452
        GtkTreeIter iter;
 
453
        GSList *list = NULL;
 
454
        void *ptr;
 
455
 
 
456
        if (gtk_tree_model_get_iter_first (model, &iter))
 
457
        {
 
458
                do
 
459
                {
 
460
                        if (gtk_tree_selection_iter_is_selected (sel, &iter))
 
461
                        {
 
462
                                gtk_tree_model_get (model, &iter, column, &ptr, -1);
 
463
                                list = g_slist_prepend (list, ptr);
 
464
                        }
 
465
                }
 
466
                while (gtk_tree_model_iter_next (model, &iter));
 
467
        }
 
468
 
 
469
        return g_slist_reverse (list);
 
470
}
 
471
 
 
472
static GSList *
 
473
dcc_get_selected (void)
 
474
{
 
475
        return treeview_get_selected (GTK_TREE_MODEL (dccfwin.store),
 
476
                                                                                        dccfwin.sel, COL_DCC);
 
477
}
 
478
 
 
479
static void
 
480
resume_clicked (GtkWidget * wid, gpointer none)
 
481
{
 
482
        struct DCC *dcc;
 
483
        char buf[512];
 
484
        GSList *list;
 
485
 
 
486
        list = dcc_get_selected ();
 
487
        if (!list)
 
488
                return;
 
489
        dcc = list->data;
 
490
        g_slist_free (list);
 
491
 
 
492
        if (dcc->type == TYPE_RECV && !dcc_resume (dcc))
 
493
        {
 
494
                switch (dcc->resume_error)
 
495
                {
 
496
                case 0: /* unknown error */
 
497
                        fe_message (_("That file is not resumable."), FE_MSG_ERROR);
 
498
                        break;
 
499
                case 1:
 
500
                        snprintf (buf, sizeof (buf),
 
501
                                                _(      "Cannot access file: %s\n"
 
502
                                                        "%s.\n"
 
503
                                                        "Resuming not possible."), dcc->destfile,       
 
504
                                                        errorstring (dcc->resume_errno));
 
505
                        fe_message (buf, FE_MSG_ERROR);
 
506
                        break;
 
507
                case 2:
 
508
                        fe_message (_("File in download directory is larger "
 
509
                                                        "than file offered. Resuming not possible."), FE_MSG_ERROR);
 
510
                        break;
 
511
                case 3:
 
512
                        fe_message (_("Cannot resume the same file from two people."), FE_MSG_ERROR);
 
513
                }
 
514
        }
 
515
}
 
516
 
 
517
static void
 
518
abort_clicked (GtkWidget * wid, gpointer none)
 
519
{
 
520
        struct DCC *dcc;
 
521
        GSList *start, *list;
 
522
 
 
523
        start = list = dcc_get_selected ();
 
524
        for (; list; list = list->next)
 
525
        {
 
526
                dcc = list->data;
 
527
                dcc_abort (dcc->serv->front_session, dcc);
 
528
        }
 
529
        g_slist_free (start);
 
530
}
 
531
 
 
532
static void
 
533
accept_clicked (GtkWidget * wid, gpointer none)
 
534
{
 
535
        struct DCC *dcc;
 
536
        GSList *start, *list;
 
537
 
 
538
        start = list = dcc_get_selected ();
 
539
        for (; list; list = list->next)
 
540
        {
 
541
                dcc = list->data;
 
542
                if (dcc->type != TYPE_SEND)
 
543
                        dcc_get (dcc);
 
544
        }
 
545
        g_slist_free (start);
 
546
}
 
547
 
 
548
static void
 
549
browse_folder (char *dir)
 
550
{
 
551
#ifdef WIN32
 
552
        /* no need for file:// in ShellExecute() */
 
553
        fe_open_url (dir);
 
554
#else
 
555
        char buf[512];
 
556
 
 
557
        snprintf (buf, sizeof (buf), "file://%s", dir);
 
558
        fe_open_url (buf);
 
559
#endif
 
560
}
 
561
 
 
562
static void
 
563
browse_dcc_folder (void)
 
564
{
 
565
        if (prefs.dcc_completed_dir[0])
 
566
                browse_folder (prefs.dcc_completed_dir);
 
567
        else
 
568
                browse_folder (prefs.dccdir);
 
569
}
 
570
 
 
571
static void
 
572
dcc_details_populate (struct DCC *dcc)
 
573
{
 
574
        char buf[128];
 
575
 
 
576
        if (!dcc)
 
577
        {
 
578
                gtk_label_set_text (GTK_LABEL (dccfwin.file_label), NULL);
 
579
                gtk_label_set_text (GTK_LABEL (dccfwin.address_label), NULL);
 
580
                return;
 
581
        }
 
582
 
 
583
        /* full path */
 
584
        if (dcc->type == TYPE_RECV)
 
585
                gtk_label_set_text (GTK_LABEL (dccfwin.file_label), dcc->destfile);
 
586
        else
 
587
                gtk_label_set_text (GTK_LABEL (dccfwin.file_label), dcc->file);
 
588
 
 
589
        /* address and port */
 
590
        snprintf (buf, sizeof (buf), "%s : %d", net_ip (dcc->addr), dcc->port);
 
591
        gtk_label_set_text (GTK_LABEL (dccfwin.address_label), buf);
 
592
}
 
593
 
 
594
static void
 
595
dcc_row_cb (GtkTreeSelection *sel, gpointer user_data)
 
596
{
 
597
        struct DCC *dcc;
 
598
        GSList *list;
 
599
 
 
600
        list = dcc_get_selected ();
 
601
        if (!list)
 
602
        {
 
603
                gtk_widget_set_sensitive (dccfwin.accept_button, FALSE);
 
604
                gtk_widget_set_sensitive (dccfwin.resume_button, FALSE);
 
605
                gtk_widget_set_sensitive (dccfwin.abort_button, FALSE);
 
606
                dcc_details_populate (NULL);
 
607
                return;
 
608
        }
 
609
 
 
610
        gtk_widget_set_sensitive (dccfwin.abort_button, TRUE);
 
611
 
 
612
        if (list->next) /* multi selection */
 
613
        {
 
614
                gtk_widget_set_sensitive (dccfwin.accept_button, TRUE);
 
615
                gtk_widget_set_sensitive (dccfwin.resume_button, TRUE);
 
616
                dcc_details_populate (list->data);
 
617
        }
 
618
        else
 
619
        {
 
620
                /* turn OFF/ON appropriate buttons */
 
621
                dcc = list->data;
 
622
                if (dcc->dccstat == STAT_QUEUED && dcc->type == TYPE_RECV)
 
623
                {
 
624
                        gtk_widget_set_sensitive (dccfwin.accept_button, TRUE);
 
625
                        gtk_widget_set_sensitive (dccfwin.resume_button, TRUE);
 
626
                }
 
627
                else
 
628
                {
 
629
                        gtk_widget_set_sensitive (dccfwin.accept_button, FALSE);
 
630
                        gtk_widget_set_sensitive (dccfwin.resume_button, FALSE);
 
631
                }
 
632
 
 
633
                dcc_details_populate (dcc);
 
634
        }
 
635
 
 
636
        g_slist_free (list);
 
637
}
 
638
 
 
639
static void
 
640
dcc_dclick_cb (GtkTreeView *view, GtkTreePath *path,
 
641
                                        GtkTreeViewColumn *column, gpointer data)
 
642
{
 
643
        struct DCC *dcc;
 
644
        GSList *list;
 
645
 
 
646
        list = dcc_get_selected ();
 
647
        if (!list)
 
648
                return;
 
649
        dcc = list->data;
 
650
        g_slist_free (list);
 
651
 
 
652
        if (dcc->type == TYPE_RECV)
 
653
        {
 
654
                accept_clicked (0, 0);
 
655
                return;
 
656
        }
 
657
 
 
658
        switch (dcc->dccstat)
 
659
        {
 
660
        case STAT_FAILED:
 
661
        case STAT_ABORTED:
 
662
        case STAT_DONE:
 
663
                dcc_abort (dcc->serv->front_session, dcc);
 
664
        }
 
665
}
 
666
 
 
667
static void
 
668
dcc_add_column (GtkWidget *tree, int textcol, int colorcol, char *title, gboolean right_justified)
 
669
{
 
670
        GtkCellRenderer *renderer;
 
671
 
 
672
        renderer = gtk_cell_renderer_text_new ();
 
673
        if (right_justified)
 
674
                g_object_set (G_OBJECT (renderer), "xalign", (float) 1.0, NULL);
 
675
        gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree), -1, title, renderer,
 
676
                                                                                                                                "text", textcol, "foreground-gdk", colorcol,
 
677
                                                                                                                                NULL);
 
678
        gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
 
679
}
 
680
 
 
681
static GtkWidget *
 
682
dcc_detail_label (char *text, GtkWidget *box, int num)
 
683
{
 
684
        GtkWidget *label;
 
685
        char buf[64];
 
686
 
 
687
        label = gtk_label_new (NULL);
 
688
        snprintf (buf, sizeof (buf), "<b>%s</b>", text);
 
689
        gtk_label_set_markup (GTK_LABEL (label), buf);
 
690
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
 
691
        gtk_table_attach (GTK_TABLE (box), label, 0, 1, 0 + num, 1 + num, GTK_FILL, GTK_FILL, 0, 0);
 
692
 
 
693
        label = gtk_label_new (NULL);
 
694
        gtk_label_set_selectable (GTK_LABEL (label), TRUE);
 
695
        gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
 
696
        gtk_table_attach (GTK_TABLE (box), label, 1, 2, 0 + num, 1 + num, GTK_FILL, GTK_FILL, 0, 0);
 
697
 
 
698
        return label;
 
699
}
 
700
 
 
701
static void
 
702
dcc_exp_cb (GtkWidget *exp, GtkWidget *box)
 
703
{
 
704
#if GTK_CHECK_VERSION(2,20,0)
 
705
        if (gtk_widget_get_visible (box))
 
706
#else
 
707
        if (GTK_WIDGET_VISIBLE (box))
 
708
#endif
 
709
                gtk_widget_hide (box);
 
710
        else
 
711
                gtk_widget_show (box);
 
712
}
 
713
 
 
714
static void
 
715
dcc_toggle (GtkWidget *item, gpointer data)
 
716
{
 
717
        if (GTK_TOGGLE_BUTTON (item)->active)
 
718
        {
 
719
                view_mode = GPOINTER_TO_INT (data);
 
720
                dcc_fill_window (GPOINTER_TO_INT (data));
 
721
        }
 
722
}
 
723
 
 
724
static gboolean
 
725
dcc_configure_cb (GtkWindow *win, GdkEventConfigure *event, gpointer data)
 
726
{
 
727
        /* remember the window size */
 
728
        gtk_window_get_size (win, &win_width, &win_height);
 
729
        return FALSE;
 
730
}
 
731
 
 
732
int
 
733
fe_dcc_open_recv_win (int passive)
 
734
{
 
735
        GtkWidget *radio, *table, *vbox, *bbox, *view, *exp, *detailbox;
 
736
        GtkListStore *store;
 
737
        GSList *group;
 
738
 
 
739
        if (dccfwin.window)
 
740
        {
 
741
                if (!passive)
 
742
                        mg_bring_tofront (dccfwin.window);
 
743
                return TRUE;
 
744
        }
 
745
        dccfwin.window = mg_create_generic_tab ("Transfers", _("XChat: Uploads and Downloads"),
 
746
                                                                                                                 FALSE, TRUE, close_dcc_file_window, NULL,
 
747
                                                                                                                 win_width, win_height, &vbox, 0);
 
748
        gtk_container_set_border_width (GTK_CONTAINER (dccfwin.window), 3);
 
749
        gtk_box_set_spacing (GTK_BOX (vbox), 3);
 
750
 
 
751
        store = gtk_list_store_new (N_COLUMNS, GDK_TYPE_PIXBUF, G_TYPE_STRING,
 
752
                                                                                 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
 
753
                                                                                 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
 
754
                                                                                 G_TYPE_STRING, G_TYPE_POINTER, GDK_TYPE_COLOR);
 
755
        view = gtkutil_treeview_new (vbox, GTK_TREE_MODEL (store), NULL, -1);
 
756
        gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
 
757
        /* Up/Down Icon column */
 
758
        gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view), -1, NULL,
 
759
                                                                                                                                gtk_cell_renderer_pixbuf_new (),
 
760
                                                                                                                                "pixbuf", COL_TYPE, NULL);
 
761
        dcc_add_column (view, COL_STATUS, COL_COLOR, _("Status"), FALSE);
 
762
        dcc_add_column (view, COL_FILE,   COL_COLOR, _("File"), FALSE);
 
763
        dcc_add_column (view, COL_SIZE,   COL_COLOR, _("Size"), TRUE);
 
764
        dcc_add_column (view, COL_POS,    COL_COLOR, _("Position"), TRUE);
 
765
        dcc_add_column (view, COL_PERC,   COL_COLOR, "%", TRUE);
 
766
        dcc_add_column (view, COL_SPEED,  COL_COLOR, "KB/s", TRUE);
 
767
        dcc_add_column (view, COL_ETA,    COL_COLOR, _("ETA"), FALSE);
 
768
        dcc_add_column (view, COL_NICK,   COL_COLOR, _("Nick"), FALSE);
 
769
 
 
770
        gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (view), COL_FILE), TRUE);
 
771
        gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (view), COL_NICK), TRUE);
 
772
 
 
773
        dccfwin.list = view;
 
774
        dccfwin.store = store;
 
775
        dccfwin.sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
 
776
        view_mode = VIEW_BOTH;
 
777
        gtk_tree_selection_set_mode (dccfwin.sel, GTK_SELECTION_MULTIPLE);
 
778
 
 
779
        if (!prefs.windows_as_tabs)
 
780
                g_signal_connect (G_OBJECT (dccfwin.window), "configure_event",
 
781
                                                                G_CALLBACK (dcc_configure_cb), 0);
 
782
        g_signal_connect (G_OBJECT (dccfwin.sel), "changed",
 
783
                                                        G_CALLBACK (dcc_row_cb), NULL);
 
784
        /* double click */
 
785
        g_signal_connect (G_OBJECT (view), "row-activated",
 
786
                                                        G_CALLBACK (dcc_dclick_cb), NULL);
 
787
 
 
788
        table = gtk_table_new (1, 3, FALSE);
 
789
        gtk_table_set_col_spacings (GTK_TABLE (table), 16);
 
790
        gtk_box_pack_start (GTK_BOX (vbox), table, 0, 0, 0);
 
791
 
 
792
        radio = gtk_radio_button_new_with_mnemonic (NULL, _("Both"));
 
793
        g_signal_connect (G_OBJECT (radio), "toggled",
 
794
                                                        G_CALLBACK (dcc_toggle), GINT_TO_POINTER (VIEW_BOTH));
 
795
        gtk_table_attach (GTK_TABLE (table), radio, 3, 4, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
 
796
        group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio));
 
797
 
 
798
        radio = gtk_radio_button_new_with_mnemonic (group, _("Uploads"));
 
799
        g_signal_connect (G_OBJECT (radio), "toggled",
 
800
                                                        G_CALLBACK (dcc_toggle), GINT_TO_POINTER (VIEW_UPLOAD));
 
801
        gtk_table_attach (GTK_TABLE (table), radio, 1, 2, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
 
802
        group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (radio));
 
803
 
 
804
        radio = gtk_radio_button_new_with_mnemonic (group, _("Downloads"));
 
805
        g_signal_connect (G_OBJECT (radio), "toggled",
 
806
                                                        G_CALLBACK (dcc_toggle), GINT_TO_POINTER (VIEW_DOWNLOAD));
 
807
        gtk_table_attach (GTK_TABLE (table), radio, 2, 3, 0, 1, GTK_FILL, GTK_FILL, 0, 0);
 
808
 
 
809
        exp = gtk_expander_new (_("Details"));
 
810
        gtk_table_attach (GTK_TABLE (table), exp, 0, 1, 0, 1, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
 
811
 
 
812
        detailbox = gtk_table_new (3, 3, FALSE);
 
813
        gtk_table_set_col_spacings (GTK_TABLE (detailbox), 6);
 
814
        gtk_table_set_row_spacings (GTK_TABLE (detailbox), 2);
 
815
        gtk_container_set_border_width (GTK_CONTAINER (detailbox), 6);
 
816
        g_signal_connect (G_OBJECT (exp), "activate",
 
817
                                                        G_CALLBACK (dcc_exp_cb), detailbox);
 
818
        gtk_table_attach (GTK_TABLE (table), detailbox, 0, 4, 1, 2, GTK_EXPAND | GTK_FILL, GTK_FILL, 0, 0);
 
819
 
 
820
        dccfwin.file_label = dcc_detail_label (_("File:"), detailbox, 0);
 
821
        dccfwin.address_label = dcc_detail_label (_("Address:"), detailbox, 1);
 
822
 
 
823
        bbox = gtk_hbutton_box_new ();
 
824
        gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_SPREAD);
 
825
        gtk_box_pack_end (GTK_BOX (vbox), bbox, FALSE, FALSE, 2);
 
826
 
 
827
        dccfwin.abort_button = gtkutil_button (bbox, GTK_STOCK_CANCEL, 0, abort_clicked, 0, _("Abort"));
 
828
        dccfwin.accept_button = gtkutil_button (bbox, GTK_STOCK_APPLY, 0, accept_clicked, 0, _("Accept"));
 
829
        dccfwin.resume_button = gtkutil_button (bbox, GTK_STOCK_REFRESH, 0, resume_clicked, 0, _("Resume"));
 
830
        dccfwin.open_button = gtkutil_button (bbox, 0, 0, browse_dcc_folder, 0, _("Open Folder..."));
 
831
        gtk_widget_set_sensitive (dccfwin.accept_button, FALSE);
 
832
        gtk_widget_set_sensitive (dccfwin.resume_button, FALSE);
 
833
        gtk_widget_set_sensitive (dccfwin.abort_button, FALSE);
 
834
 
 
835
        dcc_fill_window (3);
 
836
        gtk_widget_show_all (dccfwin.window);
 
837
        gtk_widget_hide (detailbox);
 
838
 
 
839
        return FALSE;
 
840
}
 
841
 
 
842
int
 
843
fe_dcc_open_send_win (int passive)
 
844
{
 
845
        /* combined send/recv GUI */
 
846
        return fe_dcc_open_recv_win (passive);
 
847
}
 
848
 
 
849
 
 
850
/* DCC CHAT GUIs BELOW */
 
851
 
 
852
static GSList *
 
853
dcc_chat_get_selected (void)
 
854
{
 
855
        return treeview_get_selected (GTK_TREE_MODEL (dcccwin.store),
 
856
                                                                                        dcccwin.sel, CCOL_DCC);
 
857
}
 
858
 
 
859
static void
 
860
accept_chat_clicked (GtkWidget * wid, gpointer none)
 
861
{
 
862
        struct DCC *dcc;
 
863
        GSList *start, *list;
 
864
 
 
865
        start = list = dcc_chat_get_selected ();
 
866
        for (; list; list = list->next)
 
867
        {
 
868
                dcc = list->data;
 
869
                dcc_get (dcc);
 
870
        }
 
871
        g_slist_free (start);
 
872
}
 
873
 
 
874
static void
 
875
abort_chat_clicked (GtkWidget * wid, gpointer none)
 
876
{
 
877
        struct DCC *dcc;
 
878
        GSList *start, *list;
 
879
 
 
880
        start = list = dcc_chat_get_selected ();
 
881
        for (; list; list = list->next)
 
882
        {
 
883
                dcc = list->data;
 
884
                dcc_abort (dcc->serv->front_session, dcc);
 
885
        }
 
886
        g_slist_free (start);
 
887
}
 
888
 
 
889
static void
 
890
dcc_chat_close_cb (void)
 
891
{
 
892
        dcccwin.window = NULL;
 
893
}
 
894
 
 
895
static void
 
896
dcc_chat_append (struct DCC *dcc, GtkListStore *store, gboolean prepend)
 
897
{
 
898
        GtkTreeIter iter;
 
899
 
 
900
        if (prepend)
 
901
                gtk_list_store_prepend (store, &iter);
 
902
        else
 
903
                gtk_list_store_append (store, &iter);
 
904
 
 
905
        dcc_prepare_row_chat (dcc, store, &iter, FALSE);
 
906
}
 
907
 
 
908
static void
 
909
dcc_chat_fill_win (void)
 
910
{
 
911
        struct DCC *dcc;
 
912
        GSList *list;
 
913
        GtkTreeIter iter;
 
914
        int i = 0;
 
915
 
 
916
        gtk_list_store_clear (GTK_LIST_STORE (dcccwin.store));
 
917
 
 
918
        list = dcc_list;
 
919
        while (list)
 
920
        {
 
921
                dcc = list->data;
 
922
                if (dcc->type == TYPE_CHATSEND || dcc->type == TYPE_CHATRECV)
 
923
                {
 
924
                        dcc_chat_append (dcc, dcccwin.store, FALSE);
 
925
                        i++;
 
926
                }
 
927
                list = list->next;
 
928
        }
 
929
 
 
930
        /* if only one entry, select it (so Accept button can work) */
 
931
        if (i == 1)
 
932
        {
 
933
                gtk_tree_model_get_iter_first (GTK_TREE_MODEL (dcccwin.store), &iter);
 
934
                gtk_tree_selection_select_iter (dcccwin.sel, &iter);
 
935
        }
 
936
}
 
937
 
 
938
static void
 
939
dcc_chat_row_cb (GtkTreeSelection *sel, gpointer user_data)
 
940
{
 
941
        struct DCC *dcc;
 
942
        GSList *list;
 
943
 
 
944
        list = dcc_chat_get_selected ();
 
945
        if (!list)
 
946
        {
 
947
                gtk_widget_set_sensitive (dcccwin.accept_button, FALSE);
 
948
                gtk_widget_set_sensitive (dcccwin.abort_button, FALSE);
 
949
                return;
 
950
        }
 
951
 
 
952
        gtk_widget_set_sensitive (dcccwin.abort_button, TRUE);
 
953
 
 
954
        if (list->next) /* multi selection */
 
955
                gtk_widget_set_sensitive (dcccwin.accept_button, TRUE);
 
956
        else
 
957
        {
 
958
                /* turn OFF/ON appropriate buttons */
 
959
                dcc = list->data;
 
960
                if (dcc->dccstat == STAT_QUEUED && dcc->type == TYPE_CHATRECV)
 
961
                        gtk_widget_set_sensitive (dcccwin.accept_button, TRUE);
 
962
                else
 
963
                        gtk_widget_set_sensitive (dcccwin.accept_button, FALSE);
 
964
        }
 
965
 
 
966
        g_slist_free (list);
 
967
}
 
968
 
 
969
static void
 
970
dcc_chat_dclick_cb (GtkTreeView *view, GtkTreePath *path,
 
971
                                                  GtkTreeViewColumn *column, gpointer data)
 
972
{
 
973
        accept_chat_clicked (0, 0);
 
974
}
 
975
 
 
976
int
 
977
fe_dcc_open_chat_win (int passive)
 
978
{
 
979
        GtkWidget *view, *vbox, *bbox;
 
980
        GtkListStore *store;
 
981
 
 
982
        if (dcccwin.window)
 
983
        {
 
984
                if (!passive)
 
985
                        mg_bring_tofront (dcccwin.window);
 
986
                return TRUE;
 
987
        }
 
988
 
 
989
        dcccwin.window =
 
990
                          mg_create_generic_tab ("DCCChat", _("XChat: DCC Chat List"),
 
991
                                                FALSE, TRUE, dcc_chat_close_cb, NULL, 550, 180, &vbox, 0);
 
992
        gtk_container_set_border_width (GTK_CONTAINER (dcccwin.window), 3);
 
993
        gtk_box_set_spacing (GTK_BOX (vbox), 3);
 
994
 
 
995
        store = gtk_list_store_new (CN_COLUMNS, G_TYPE_STRING, G_TYPE_STRING,
 
996
                                                                                 G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
 
997
                                                                                 G_TYPE_POINTER, GDK_TYPE_COLOR);
 
998
        view = gtkutil_treeview_new (vbox, GTK_TREE_MODEL (store), NULL, -1);
 
999
 
 
1000
        dcc_add_column (view, CCOL_STATUS, CCOL_COLOR, _("Status"), FALSE);
 
1001
        dcc_add_column (view, CCOL_NICK,   CCOL_COLOR, _("Nick"), FALSE);
 
1002
        dcc_add_column (view, CCOL_RECV,   CCOL_COLOR, _("Recv"), TRUE);
 
1003
        dcc_add_column (view, CCOL_SENT,   CCOL_COLOR, _("Sent"), TRUE);
 
1004
        dcc_add_column (view, CCOL_START,  CCOL_COLOR, _("Start Time"), FALSE);
 
1005
 
 
1006
        gtk_tree_view_column_set_expand (gtk_tree_view_get_column (GTK_TREE_VIEW (view), 1), TRUE);
 
1007
        gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
 
1008
 
 
1009
        dcccwin.list = view;
 
1010
        dcccwin.store = store;
 
1011
        dcccwin.sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (view));
 
1012
        gtk_tree_selection_set_mode (dcccwin.sel, GTK_SELECTION_MULTIPLE);
 
1013
 
 
1014
        g_signal_connect (G_OBJECT (dcccwin.sel), "changed",
 
1015
                                                        G_CALLBACK (dcc_chat_row_cb), NULL);
 
1016
        /* double click */
 
1017
        g_signal_connect (G_OBJECT (view), "row-activated",
 
1018
                                                        G_CALLBACK (dcc_chat_dclick_cb), NULL);
 
1019
 
 
1020
        bbox = gtk_hbutton_box_new ();
 
1021
        gtk_button_box_set_layout (GTK_BUTTON_BOX (bbox), GTK_BUTTONBOX_SPREAD);
 
1022
        gtk_box_pack_end (GTK_BOX (vbox), bbox, FALSE, FALSE, 2);
 
1023
 
 
1024
        dcccwin.abort_button = gtkutil_button (bbox, GTK_STOCK_CANCEL, 0, abort_chat_clicked, 0, _("Abort"));
 
1025
        dcccwin.accept_button = gtkutil_button (bbox, GTK_STOCK_APPLY, 0, accept_chat_clicked, 0, _("Accept"));
 
1026
        gtk_widget_set_sensitive (dcccwin.accept_button, FALSE);
 
1027
        gtk_widget_set_sensitive (dcccwin.abort_button, FALSE);
 
1028
 
 
1029
        dcc_chat_fill_win ();
 
1030
        gtk_widget_show_all (dcccwin.window);
 
1031
 
 
1032
        return FALSE;
 
1033
}
 
1034
 
 
1035
void
 
1036
fe_dcc_add (struct DCC *dcc)
 
1037
{
 
1038
        switch (dcc->type)
 
1039
        {
 
1040
        case TYPE_RECV:
 
1041
                if (dccfwin.window && (view_mode & VIEW_DOWNLOAD))
 
1042
                        dcc_append (dcc, dccfwin.store, TRUE);
 
1043
                break;
 
1044
 
 
1045
        case TYPE_SEND:
 
1046
                if (dccfwin.window && (view_mode & VIEW_UPLOAD))
 
1047
                        dcc_append (dcc, dccfwin.store, TRUE);
 
1048
                break;
 
1049
 
 
1050
        default: /* chat */
 
1051
                if (dcccwin.window)
 
1052
                        dcc_chat_append (dcc, dcccwin.store, TRUE);
 
1053
        }
 
1054
}
 
1055
 
 
1056
void
 
1057
fe_dcc_update (struct DCC *dcc)
 
1058
{
 
1059
        switch (dcc->type)
 
1060
        {
 
1061
        case TYPE_SEND:
 
1062
                dcc_update_send (dcc);
 
1063
                break;
 
1064
 
 
1065
        case TYPE_RECV:
 
1066
                dcc_update_recv (dcc);
 
1067
                break;
 
1068
 
 
1069
        default:
 
1070
                dcc_update_chat (dcc);
 
1071
        }
 
1072
}
 
1073
 
 
1074
void
 
1075
fe_dcc_remove (struct DCC *dcc)
 
1076
{
 
1077
        GtkTreeIter iter;
 
1078
 
 
1079
        switch (dcc->type)
 
1080
        {
 
1081
        case TYPE_SEND:
 
1082
        case TYPE_RECV:
 
1083
                if (dccfwin.window)
 
1084
                {
 
1085
                        if (dcc_find_row (dcc, GTK_TREE_MODEL (dccfwin.store), &iter, COL_DCC))
 
1086
                                gtk_list_store_remove (dccfwin.store, &iter);
 
1087
                }
 
1088
                break;
 
1089
 
 
1090
        default:        /* chat */
 
1091
                if (dcccwin.window)
 
1092
                {
 
1093
                        if (dcc_find_row (dcc, GTK_TREE_MODEL (dcccwin.store), &iter, CCOL_DCC))
 
1094
                                gtk_list_store_remove (dcccwin.store, &iter);
 
1095
                }
 
1096
                break;
 
1097
        }
 
1098
}