Use macros instead of raw numbers for login type
[rofl0r-ixchat.git] / src / fe-gtk / chanlist.c
blobce994dd3d8894fbf97dd78dea8c3e639b3c3f337
1 /* X-Chat
2 * Copyright (C) 1998-2006 Peter Zelezny.
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.
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.
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
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <fcntl.h>
24 #include <time.h>
26 #include "fe-gtk.h"
28 #include <gtk/gtkalignment.h>
29 #include <gtk/gtkcellrenderertext.h>
30 #include <gtk/gtkcheckbutton.h>
31 #include <gtk/gtkcombobox.h>
32 #include <gtk/gtkentry.h>
33 #include <gtk/gtkhbox.h>
34 #include <gtk/gtklabel.h>
35 #include <gtk/gtkliststore.h>
36 #include <gtk/gtkscrolledwindow.h>
37 #include <gtk/gtkspinbutton.h>
38 #include <gtk/gtkstock.h>
39 #include <gtk/gtktable.h>
40 #include <gtk/gtktreeselection.h>
41 #include <gtk/gtkvbox.h>
42 #include <gtk/gtkvseparator.h>
43 #include <gdk/gdkkeysyms.h>
45 #include "../common/xchat.h"
46 #include "../common/xchatc.h"
47 #include "../common/cfgfiles.h"
48 #include "../common/outbound.h"
49 #include "../common/util.h"
50 #include "../common/fe.h"
51 #include "../common/server.h"
52 #include "gtkutil.h"
53 #include "maingui.h"
56 #include "custom-list.h"
59 enum
61 COL_CHANNEL,
62 COL_USERS,
63 COL_TOPIC,
64 N_COLUMNS
67 #ifndef CUSTOM_LIST
68 typedef struct /* this is now in custom-list.h */
70 char *topic;
71 char *collation_key;
72 guint32 pos;
73 guint32 users;
74 /* channel string lives beyond "users" */
75 #define GET_CHAN(row) (((char *)row)+sizeof(chanlistrow))
77 chanlistrow;
78 #endif
80 #define GET_MODEL(xserv) (gtk_tree_view_get_model(GTK_TREE_VIEW(xserv->gui->chanlist_list)))
83 static gboolean
84 chanlist_match (server *serv, const char *str)
86 switch (serv->gui->chanlist_search_type)
88 case 1:
89 return match (GTK_ENTRY (serv->gui->chanlist_wild)->text, str);
90 case 2:
91 if (!serv->gui->have_regex)
92 return 0;
93 /* regex returns 0 if it's a match: */
94 return !regexec (&serv->gui->chanlist_match_regex, str, 1, NULL, REG_NOTBOL);
95 default: /* case 0: */
96 return nocasestrstr (str, GTK_ENTRY (serv->gui->chanlist_wild)->text) ? 1 : 0;
101 * Updates the caption to reflect the number of users and channels
103 static void
104 chanlist_update_caption (server *serv)
106 gchar tbuf[256];
108 snprintf (tbuf, sizeof tbuf,
109 _("Displaying %d/%d users on %d/%d channels."),
110 serv->gui->chanlist_users_shown_count,
111 serv->gui->chanlist_users_found_count,
112 serv->gui->chanlist_channels_shown_count,
113 serv->gui->chanlist_channels_found_count);
115 gtk_label_set_text (GTK_LABEL (serv->gui->chanlist_label), tbuf);
116 serv->gui->chanlist_caption_is_stale = FALSE;
119 static void
120 chanlist_update_buttons (server *serv)
122 if (serv->gui->chanlist_channels_shown_count)
124 gtk_widget_set_sensitive (serv->gui->chanlist_join, TRUE);
125 gtk_widget_set_sensitive (serv->gui->chanlist_savelist, TRUE);
127 else
129 gtk_widget_set_sensitive (serv->gui->chanlist_join, FALSE);
130 gtk_widget_set_sensitive (serv->gui->chanlist_savelist, FALSE);
134 static void
135 chanlist_reset_counters (server *serv)
137 serv->gui->chanlist_users_found_count = 0;
138 serv->gui->chanlist_users_shown_count = 0;
139 serv->gui->chanlist_channels_found_count = 0;
140 serv->gui->chanlist_channels_shown_count = 0;
142 chanlist_update_caption (serv);
143 chanlist_update_buttons (serv);
146 /* free up our entire linked list and all the nodes */
148 static void
149 chanlist_data_free (server *serv)
151 GSList *rows;
152 chanlistrow *data;
154 if (serv->gui->chanlist_data_stored_rows)
156 for (rows = serv->gui->chanlist_data_stored_rows; rows != NULL;
157 rows = rows->next)
159 data = rows->data;
160 g_free (data->topic);
161 g_free (data->collation_key);
162 free (data);
165 g_slist_free (serv->gui->chanlist_data_stored_rows);
166 serv->gui->chanlist_data_stored_rows = NULL;
169 g_slist_free (serv->gui->chanlist_pending_rows);
170 serv->gui->chanlist_pending_rows = NULL;
173 /* add any rows we received from the server in the last 0.25s to the GUI */
175 static void
176 chanlist_flush_pending (server *serv)
178 GSList *list = serv->gui->chanlist_pending_rows;
179 GtkTreeModel *model;
180 chanlistrow *row;
182 if (!list)
184 if (serv->gui->chanlist_caption_is_stale)
185 chanlist_update_caption (serv);
186 return;
188 model = GET_MODEL (serv);
190 while (list)
192 row = list->data;
193 custom_list_append (CUSTOM_LIST (model), row);
194 list = list->next;
197 g_slist_free (serv->gui->chanlist_pending_rows);
198 serv->gui->chanlist_pending_rows = NULL;
199 chanlist_update_caption (serv);
202 static gboolean
203 chanlist_timeout (server *serv)
205 chanlist_flush_pending (serv);
206 return TRUE;
210 * Places a data row into the gui GtkTreeView, if and only if the row matches
211 * the user and regex/search requirements.
213 static void
214 chanlist_place_row_in_gui (server *serv, chanlistrow *next_row, gboolean force)
216 GtkTreeModel *model;
218 /* First, update the 'found' counter values */
219 serv->gui->chanlist_users_found_count += next_row->users;
220 serv->gui->chanlist_channels_found_count++;
222 if (serv->gui->chanlist_channels_shown_count == 1)
223 /* join & save buttons become live */
224 chanlist_update_buttons (serv);
226 if (next_row->users < serv->gui->chanlist_minusers)
228 serv->gui->chanlist_caption_is_stale = TRUE;
229 return;
232 if (next_row->users > serv->gui->chanlist_maxusers
233 && serv->gui->chanlist_maxusers > 0)
235 serv->gui->chanlist_caption_is_stale = TRUE;
236 return;
239 if (GTK_ENTRY (serv->gui->chanlist_wild)->text[0])
241 /* Check what the user wants to match. If both buttons or _neither_
242 * button is checked, look for match in both by default.
244 if (serv->gui->chanlist_match_wants_channel ==
245 serv->gui->chanlist_match_wants_topic)
247 if (!chanlist_match (serv, GET_CHAN (next_row))
248 && !chanlist_match (serv, next_row->topic))
250 serv->gui->chanlist_caption_is_stale = TRUE;
251 return;
255 else if (serv->gui->chanlist_match_wants_channel)
257 if (!chanlist_match (serv, GET_CHAN (next_row)))
259 serv->gui->chanlist_caption_is_stale = TRUE;
260 return;
264 else if (serv->gui->chanlist_match_wants_topic)
266 if (!chanlist_match (serv, next_row->topic))
268 serv->gui->chanlist_caption_is_stale = TRUE;
269 return;
274 if (force || serv->gui->chanlist_channels_shown_count < 20)
276 model = GET_MODEL (serv);
277 /* makes it appear fast :) */
278 custom_list_append (CUSTOM_LIST (model), next_row);
279 chanlist_update_caption (serv);
281 else
282 /* add it to GUI at the next update interval */
283 serv->gui->chanlist_pending_rows = g_slist_prepend (serv->gui->chanlist_pending_rows, next_row);
285 /* Update the 'shown' counter values */
286 serv->gui->chanlist_users_shown_count += next_row->users;
287 serv->gui->chanlist_channels_shown_count++;
290 /* Performs the LIST download from the IRC server. */
292 static void
293 chanlist_do_refresh (server *serv)
295 if (serv->gui->chanlist_flash_tag)
297 g_source_remove (serv->gui->chanlist_flash_tag);
298 serv->gui->chanlist_flash_tag = 0;
301 if (!serv->connected)
303 fe_message (_("Not connected."), FE_MSG_ERROR);
304 return;
307 custom_list_clear ((CustomList *)GET_MODEL (serv));
308 gtk_widget_set_sensitive (serv->gui->chanlist_refresh, FALSE);
310 chanlist_data_free (serv);
311 chanlist_reset_counters (serv);
313 /* can we request a list with minusers arg? */
314 if (serv->use_listargs)
316 /* yes - it will download faster */
317 serv->p_list_channels (serv, "", serv->gui->chanlist_minusers);
318 /* don't allow the spin button below this value from now on */
319 serv->gui->chanlist_minusers_downloaded = serv->gui->chanlist_minusers;
321 else
323 /* download all, filter minusers locally only */
324 serv->p_list_channels (serv, "", 1);
325 serv->gui->chanlist_minusers_downloaded = 1;
328 /* gtk_spin_button_set_range ((GtkSpinButton *)serv->gui->chanlist_min_spin,
329 serv->gui->chanlist_minusers_downloaded, 999999);*/
332 static void
333 chanlist_refresh (GtkWidget * wid, server *serv)
335 chanlist_do_refresh (serv);
339 * Fills the gui GtkTreeView with stored items from the GSList.
341 static void
342 chanlist_build_gui_list (server *serv)
344 GSList *rows;
346 /* first check if the list is present */
347 if (serv->gui->chanlist_data_stored_rows == NULL)
349 /* start a download */
350 chanlist_do_refresh (serv);
351 return;
354 custom_list_clear ((CustomList *)GET_MODEL (serv));
356 /* discard pending rows FIXME: free the structs? */
357 g_slist_free (serv->gui->chanlist_pending_rows);
358 serv->gui->chanlist_pending_rows = NULL;
360 /* Reset the counters */
361 chanlist_reset_counters (serv);
363 /* Refill the list */
364 for (rows = serv->gui->chanlist_data_stored_rows; rows != NULL;
365 rows = rows->next)
367 chanlist_place_row_in_gui (serv, rows->data, TRUE);
370 custom_list_resort ((CustomList *)GET_MODEL (serv));
374 * Accepts incoming channel data from inbound.c, allocates new space for a
375 * chanlistrow, adds it to our linked list and calls chanlist_place_row_in_gui.
377 void
378 fe_add_chan_list (server *serv, char *chan, char *users, char *topic)
380 chanlistrow *next_row;
381 int len = strlen (chan) + 1;
383 /* we allocate the struct and channel string in one go */
384 next_row = malloc (sizeof (chanlistrow) + len);
385 memcpy (((char *)next_row) + sizeof (chanlistrow), chan, len);
386 next_row->topic = strip_color (topic, -1, STRIP_ALL);
387 next_row->collation_key = g_utf8_collate_key (chan, len-1);
388 if (!(next_row->collation_key))
389 next_row->collation_key = g_strdup (chan);
390 next_row->users = atoi (users);
392 /* add this row to the data */
393 serv->gui->chanlist_data_stored_rows =
394 g_slist_prepend (serv->gui->chanlist_data_stored_rows, next_row);
396 /* _possibly_ add the row to the gui */
397 chanlist_place_row_in_gui (serv, next_row, FALSE);
400 void
401 fe_chan_list_end (server *serv)
403 /* download complete */
404 chanlist_flush_pending (serv);
405 gtk_widget_set_sensitive (serv->gui->chanlist_refresh, TRUE);
406 custom_list_resort ((CustomList *)GET_MODEL (serv));
409 static void
410 chanlist_search_pressed (GtkButton * button, server *serv)
412 chanlist_build_gui_list (serv);
415 static void
416 chanlist_find_cb (GtkWidget * wid, server *serv)
418 /* recompile the regular expression. */
419 if (serv->gui->have_regex)
421 serv->gui->have_regex = 0;
422 regfree (&serv->gui->chanlist_match_regex);
425 if (regcomp (&serv->gui->chanlist_match_regex, GTK_ENTRY (wid)->text,
426 REG_ICASE | REG_EXTENDED | REG_NOSUB) == 0)
427 serv->gui->have_regex = 1;
430 static void
431 chanlist_match_channel_button_toggled (GtkWidget * wid, server *serv)
433 serv->gui->chanlist_match_wants_channel = GTK_TOGGLE_BUTTON (wid)->active;
436 static void
437 chanlist_match_topic_button_toggled (GtkWidget * wid, server *serv)
439 serv->gui->chanlist_match_wants_topic = GTK_TOGGLE_BUTTON (wid)->active;
442 static char *
443 chanlist_get_selected (server *serv, gboolean get_topic)
445 char *chan;
446 GtkTreeSelection *sel = gtk_tree_view_get_selection (GTK_TREE_VIEW (serv->gui->chanlist_list));
447 GtkTreeModel *model;
448 GtkTreeIter iter;
450 if (!gtk_tree_selection_get_selected (sel, &model, &iter))
451 return NULL;
453 gtk_tree_model_get (model, &iter, get_topic ? COL_TOPIC : COL_CHANNEL, &chan, -1);
454 return chan;
457 static void
458 chanlist_join (GtkWidget * wid, server *serv)
460 char tbuf[CHANLEN + 6];
461 char *chan = chanlist_get_selected (serv, FALSE);
462 if (chan)
464 if (serv->connected && (strcmp (chan, "*") != 0))
466 snprintf (tbuf, sizeof (tbuf), "join %s", chan);
467 handle_command (serv->server_session, tbuf, FALSE);
468 } else
469 gdk_beep ();
470 g_free (chan);
474 static void
475 chanlist_filereq_done (server *serv, char *file)
477 time_t t = time (0);
478 int fh, users;
479 char *chan, *topic;
480 char buf[1024];
481 GtkTreeModel *model = GET_MODEL (serv);
482 GtkTreeIter iter;
484 if (!file)
485 return;
487 fh = xchat_open_file (file, O_TRUNC | O_WRONLY | O_CREAT, 0600,
488 XOF_DOMODE | XOF_FULLPATH);
489 if (fh == -1)
490 return;
492 snprintf (buf, sizeof buf, "XChat Channel List: %s - %s\n",
493 serv->servername, ctime (&t));
494 write (fh, buf, strlen (buf));
496 if (gtk_tree_model_get_iter_first (model, &iter))
500 gtk_tree_model_get (model, &iter,
501 COL_CHANNEL, &chan,
502 COL_USERS, &users,
503 COL_TOPIC, &topic, -1);
504 snprintf (buf, sizeof buf, "%-16s %-5d%s\n", chan, users, topic);
505 g_free (chan);
506 g_free (topic);
507 write (fh, buf, strlen (buf));
509 while (gtk_tree_model_iter_next (model, &iter));
512 close (fh);
515 static void
516 chanlist_save (GtkWidget * wid, server *serv)
518 GtkTreeIter iter;
519 GtkTreeModel *model = GET_MODEL (serv);
521 if (gtk_tree_model_get_iter_first (model, &iter))
522 gtkutil_file_req (_("Select an output filename"), chanlist_filereq_done,
523 serv, NULL, FRF_WRITE);
526 static gboolean
527 chanlist_flash (server *serv)
529 if (serv->gui->chanlist_refresh->state != GTK_STATE_ACTIVE)
530 gtk_widget_set_state (serv->gui->chanlist_refresh, GTK_STATE_ACTIVE);
531 else
532 gtk_widget_set_state (serv->gui->chanlist_refresh, GTK_STATE_PRELIGHT);
534 return TRUE;
537 static void
538 chanlist_minusers (GtkSpinButton *wid, server *serv)
540 serv->gui->chanlist_minusers = gtk_spin_button_get_value_as_int (wid);
542 if (serv->gui->chanlist_minusers < serv->gui->chanlist_minusers_downloaded)
544 if (serv->gui->chanlist_flash_tag == 0)
545 serv->gui->chanlist_flash_tag = g_timeout_add (500, (GSourceFunc)chanlist_flash, serv);
547 else
549 if (serv->gui->chanlist_flash_tag)
551 g_source_remove (serv->gui->chanlist_flash_tag);
552 serv->gui->chanlist_flash_tag = 0;
557 static void
558 chanlist_maxusers (GtkSpinButton *wid, server *serv)
560 serv->gui->chanlist_maxusers = gtk_spin_button_get_value_as_int (wid);
563 static void
564 chanlist_dclick_cb (GtkTreeView *view, GtkTreePath *path,
565 GtkTreeViewColumn *column, gpointer data)
567 chanlist_join (0, (server *) data); /* double clicked a row */
570 static void
571 chanlist_menu_destroy (GtkWidget *menu, gpointer userdata)
573 gtk_widget_destroy (menu);
574 g_object_unref (menu);
577 static void
578 chanlist_copychannel (GtkWidget *item, server *serv)
580 char *chan = chanlist_get_selected (serv, FALSE);
581 if (chan)
583 gtkutil_copy_to_clipboard (item, NULL, chan);
584 g_free (chan);
588 static void
589 chanlist_copytopic (GtkWidget *item, server *serv)
591 char *topic = chanlist_get_selected (serv, TRUE);
592 if (topic)
594 gtkutil_copy_to_clipboard (item, NULL, topic);
595 g_free (topic);
599 static gboolean
600 chanlist_button_cb (GtkTreeView *tree, GdkEventButton *event, server *serv)
602 GtkWidget *menu;
603 GtkTreeSelection *sel;
604 GtkTreePath *path;
605 char *chan;
607 if (event->button != 3)
608 return FALSE;
610 if (!gtk_tree_view_get_path_at_pos (tree, event->x, event->y, &path, 0, 0, 0))
611 return FALSE;
613 /* select what they right-clicked on */
614 sel = gtk_tree_view_get_selection (tree);
615 gtk_tree_selection_unselect_all (sel);
616 gtk_tree_selection_select_path (sel, path);
617 gtk_tree_path_free (path);
619 menu = gtk_menu_new ();
620 if (event->window)
621 gtk_menu_set_screen (GTK_MENU (menu), gdk_drawable_get_screen (event->window));
622 g_object_ref (menu);
623 g_object_ref_sink (menu);
624 g_object_unref (menu);
625 g_signal_connect (G_OBJECT (menu), "selection-done",
626 G_CALLBACK (chanlist_menu_destroy), NULL);
627 mg_create_icon_item (_("_Join Channel"), GTK_STOCK_JUMP_TO, menu,
628 chanlist_join, serv);
629 mg_create_icon_item (_("_Copy Channel Name"), GTK_STOCK_COPY, menu,
630 chanlist_copychannel, serv);
631 mg_create_icon_item (_("Copy _Topic Text"), GTK_STOCK_COPY, menu,
632 chanlist_copytopic, serv);
634 chan = chanlist_get_selected (serv, FALSE);
635 menu_addfavoritemenu (serv, menu, chan);
636 g_free (chan);
638 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, event->time);
640 return TRUE;
643 static void
644 chanlist_destroy_widget (GtkWidget *wid, server *serv)
646 custom_list_clear ((CustomList *)GET_MODEL (serv));
647 chanlist_data_free (serv);
649 if (serv->gui->chanlist_flash_tag)
651 g_source_remove (serv->gui->chanlist_flash_tag);
652 serv->gui->chanlist_flash_tag = 0;
655 if (serv->gui->chanlist_tag)
657 g_source_remove (serv->gui->chanlist_tag);
658 serv->gui->chanlist_tag = 0;
661 if (serv->gui->have_regex)
663 regfree (&serv->gui->chanlist_match_regex);
664 serv->gui->have_regex = 0;
668 static void
669 chanlist_closegui (GtkWidget *wid, server *serv)
671 if (is_server (serv))
672 serv->gui->chanlist_window = NULL;
675 static void
676 chanlist_add_column (GtkWidget *tree, int textcol, int size, char *title, gboolean right_justified)
678 GtkCellRenderer *renderer;
679 GtkTreeViewColumn *col;
681 renderer = gtk_cell_renderer_text_new ();
682 if (right_justified)
683 g_object_set (G_OBJECT (renderer), "xalign", (gfloat) 1.0, NULL);
684 g_object_set (G_OBJECT (renderer), "ypad", (gint) 0, NULL);
685 gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (tree), -1, title,
686 renderer, "text", textcol, NULL);
687 gtk_cell_renderer_text_set_fixed_height_from_font (GTK_CELL_RENDERER_TEXT (renderer), 1);
689 col = gtk_tree_view_get_column (GTK_TREE_VIEW (tree), textcol);
690 gtk_tree_view_column_set_sort_column_id (col, textcol);
691 gtk_tree_view_column_set_resizable (col, TRUE);
692 if (textcol != COL_TOPIC)
694 gtk_tree_view_column_set_sizing (col, GTK_TREE_VIEW_COLUMN_FIXED);
695 gtk_tree_view_column_set_fixed_width (col, size);
699 static void
700 chanlist_combo_cb (GtkWidget *combo, server *serv)
702 serv->gui->chanlist_search_type = gtk_combo_box_get_active (GTK_COMBO_BOX (combo));
705 void
706 chanlist_opengui (server *serv, int do_refresh)
708 GtkWidget *vbox, *hbox, *table, *wid, *view;
709 char tbuf[256];
710 GtkListStore *store;
712 if (serv->gui->chanlist_window)
714 mg_bring_tofront (serv->gui->chanlist_window);
715 return;
718 snprintf (tbuf, sizeof tbuf, _("XChat: Channel List (%s)"),
719 server_get_network (serv, TRUE));
721 serv->gui->chanlist_pending_rows = NULL;
722 serv->gui->chanlist_tag = 0;
723 serv->gui->chanlist_flash_tag = 0;
724 serv->gui->chanlist_data_stored_rows = NULL;
726 if (!serv->gui->chanlist_minusers)
727 serv->gui->chanlist_minusers = 5;
729 if (!serv->gui->chanlist_maxusers)
730 serv->gui->chanlist_maxusers = 9999;
732 serv->gui->chanlist_window =
733 mg_create_generic_tab ("ChanList", tbuf, FALSE, TRUE, chanlist_closegui,
734 serv, 640, 480, &vbox, serv);
736 gtk_container_set_border_width (GTK_CONTAINER (vbox), 6);
737 gtk_box_set_spacing (GTK_BOX (vbox), 12);
739 /* make a label to store the user/channel info */
740 wid = gtk_label_new (NULL);
741 gtk_box_pack_start (GTK_BOX (vbox), wid, 0, 0, 0);
742 gtk_widget_show (wid);
743 serv->gui->chanlist_label = wid;
745 /* ============================================================= */
747 store = (GtkListStore *) custom_list_new();
748 view = gtkutil_treeview_new (vbox, GTK_TREE_MODEL (store), NULL, -1);
749 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (view->parent),
750 GTK_SHADOW_IN);
751 serv->gui->chanlist_list = view;
753 g_signal_connect (G_OBJECT (view), "row_activated",
754 G_CALLBACK (chanlist_dclick_cb), serv);
755 g_signal_connect (G_OBJECT (view), "button-press-event",
756 G_CALLBACK (chanlist_button_cb), serv);
758 chanlist_add_column (view, COL_CHANNEL, 96, _("Channel"), FALSE);
759 chanlist_add_column (view, COL_USERS, 50, _("Users"), TRUE);
760 chanlist_add_column (view, COL_TOPIC, 50, _("Topic"), FALSE);
761 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (view), TRUE);
762 /* this is a speed up, but no horizontal scrollbar :( */
763 /*gtk_tree_view_set_fixed_height_mode (GTK_TREE_VIEW (view), TRUE);*/
764 gtk_widget_show (view);
766 /* ============================================================= */
768 table = gtk_table_new (4, 4, FALSE);
769 gtk_table_set_col_spacings (GTK_TABLE (table), 12);
770 gtk_table_set_row_spacings (GTK_TABLE (table), 3);
771 gtk_box_pack_start (GTK_BOX (vbox), table, 0, 1, 0);
772 gtk_widget_show (table);
774 wid = gtkutil_button (NULL, GTK_STOCK_FIND, 0, chanlist_search_pressed, serv,
775 _("_Search"));
776 serv->gui->chanlist_search = wid;
777 gtk_table_attach (GTK_TABLE (table), wid, 3, 4, 3, 4,
778 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
780 wid = gtkutil_button (NULL, GTK_STOCK_REFRESH, 0, chanlist_refresh, serv,
781 _("_Download List"));
782 serv->gui->chanlist_refresh = wid;
783 gtk_table_attach (GTK_TABLE (table), wid, 3, 4, 2, 3,
784 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
786 wid = gtkutil_button (NULL, GTK_STOCK_SAVE_AS, 0, chanlist_save, serv,
787 _("Save _List..."));
788 serv->gui->chanlist_savelist = wid;
789 gtk_table_attach (GTK_TABLE (table), wid, 3, 4, 1, 2,
790 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
792 wid = gtkutil_button (NULL, GTK_STOCK_JUMP_TO, 0, chanlist_join, serv,
793 _("_Join Channel"));
794 serv->gui->chanlist_join = wid;
795 gtk_table_attach (GTK_TABLE (table), wid, 3, 4, 0, 1,
796 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
798 /* ============================================================= */
800 wid = gtk_label_new (_("Show only:"));
801 gtk_misc_set_alignment (GTK_MISC (wid), 0, 0.5);
802 gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 3, 4,
803 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
804 gtk_widget_show (wid);
806 hbox = gtk_hbox_new (0, 0);
807 gtk_box_set_spacing (GTK_BOX (hbox), 9);
808 gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 3, 4,
809 GTK_FILL, GTK_FILL, 0, 0);
810 gtk_widget_show (hbox);
812 wid = gtk_label_new (_("channels with"));
813 gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
814 gtk_widget_show (wid);
816 wid = gtk_spin_button_new_with_range (1, 999999, 1);
817 gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid),
818 serv->gui->chanlist_minusers);
819 g_signal_connect (G_OBJECT (wid), "value_changed",
820 G_CALLBACK (chanlist_minusers), serv);
821 gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
822 gtk_widget_show (wid);
823 serv->gui->chanlist_min_spin = wid;
825 wid = gtk_label_new (_("to"));
826 gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
827 gtk_widget_show (wid);
829 wid = gtk_spin_button_new_with_range (1, 999999, 1);
830 gtk_spin_button_set_value (GTK_SPIN_BUTTON (wid),
831 serv->gui->chanlist_maxusers);
832 g_signal_connect (G_OBJECT (wid), "value_changed",
833 G_CALLBACK (chanlist_maxusers), serv);
834 gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
835 gtk_widget_show (wid);
837 wid = gtk_label_new (_("users."));
838 gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
839 gtk_widget_show (wid);
841 /* ============================================================= */
843 wid = gtk_label_new (_("Look in:"));
844 gtk_misc_set_alignment (GTK_MISC (wid), 0, 0.5);
845 gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 2, 3,
846 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
847 gtk_widget_show (wid);
849 hbox = gtk_hbox_new (0, 0);
850 gtk_box_set_spacing (GTK_BOX (hbox), 12);
851 gtk_table_attach (GTK_TABLE (table), hbox, 1, 2, 2, 3,
852 GTK_FILL, GTK_FILL, 0, 0);
853 gtk_widget_show (hbox);
855 wid = gtk_check_button_new_with_label (_("Channel name"));
856 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), TRUE);
857 gtk_signal_connect (GTK_OBJECT (wid), "toggled",
858 GTK_SIGNAL_FUNC
859 (chanlist_match_channel_button_toggled), serv);
860 gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
861 gtk_widget_show (wid);
863 wid = gtk_check_button_new_with_label (_("Topic"));
864 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), TRUE);
865 gtk_signal_connect (GTK_OBJECT (wid), "toggled",
866 GTK_SIGNAL_FUNC (chanlist_match_topic_button_toggled),
867 serv);
868 gtk_box_pack_start (GTK_BOX (hbox), wid, 0, 0, 0);
869 gtk_widget_show (wid);
871 serv->gui->chanlist_match_wants_channel = 1;
872 serv->gui->chanlist_match_wants_topic = 1;
874 /* ============================================================= */
876 wid = gtk_label_new (_("Search type:"));
877 gtk_misc_set_alignment (GTK_MISC (wid), 0, 0.5);
878 gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 1, 2,
879 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
880 gtk_widget_show (wid);
882 wid = gtk_combo_box_new_text ();
883 gtk_combo_box_append_text (GTK_COMBO_BOX (wid), _("Simple Search"));
884 gtk_combo_box_append_text (GTK_COMBO_BOX (wid), _("Pattern Match (Wildcards)"));
885 gtk_combo_box_append_text (GTK_COMBO_BOX (wid), _("Regular Expression"));
886 gtk_combo_box_set_active (GTK_COMBO_BOX (wid), serv->gui->chanlist_search_type);
887 gtk_table_attach (GTK_TABLE (table), wid, 1, 2, 1, 2,
888 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
889 g_signal_connect (G_OBJECT (wid), "changed",
890 G_CALLBACK (chanlist_combo_cb), serv);
891 gtk_widget_show (wid);
893 /* ============================================================= */
895 wid = gtk_label_new (_("Find:"));
896 gtk_misc_set_alignment (GTK_MISC (wid), 0, 0.5);
897 gtk_table_attach (GTK_TABLE (table), wid, 0, 1, 0, 1,
898 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
899 gtk_widget_show (wid);
901 wid = gtk_entry_new_with_max_length (255);
902 gtk_signal_connect (GTK_OBJECT (wid), "changed",
903 GTK_SIGNAL_FUNC (chanlist_find_cb), serv);
904 gtk_signal_connect (GTK_OBJECT (wid), "activate",
905 GTK_SIGNAL_FUNC (chanlist_search_pressed),
906 (gpointer) serv);
907 gtk_table_attach (GTK_TABLE (table), wid, 1, 2, 0, 1,
908 GTK_EXPAND | GTK_FILL, 0, 0, 0);
909 gtk_widget_show (wid);
910 serv->gui->chanlist_wild = wid;
912 chanlist_find_cb (wid, serv);
914 /* ============================================================= */
916 wid = gtk_vseparator_new ();
917 gtk_table_attach (GTK_TABLE (table), wid, 2, 3, 0, 5,
918 GTK_SHRINK | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
919 gtk_widget_show (wid);
921 g_signal_connect (G_OBJECT (serv->gui->chanlist_window), "destroy",
922 G_CALLBACK (chanlist_destroy_widget), serv);
924 /* reset the counters. */
925 chanlist_reset_counters (serv);
927 serv->gui->chanlist_tag = g_timeout_add (250, (GSourceFunc)chanlist_timeout, serv);
929 if (do_refresh)
930 chanlist_do_refresh (serv);
932 chanlist_update_buttons (serv);
933 gtk_widget_show (serv->gui->chanlist_window);
934 gtk_widget_grab_focus (serv->gui->chanlist_refresh);