Use macros instead of raw numbers for login type
[rofl0r-ixchat.git] / src / fe-gtk / maingui.c
blob91e054a8b41a60764610655fdb687f3185860396
1 /* X-Chat
2 * Copyright (C) 1998-2005 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 <stdlib.h>
20 #include <string.h>
21 #include <stdio.h>
22 #include <ctype.h>
24 #include <gtk/gtkarrow.h>
25 #include <gtk/gtktogglebutton.h>
26 #include <gtk/gtkhbox.h>
27 #include <gtk/gtkvbox.h>
28 #include <gtk/gtkeventbox.h>
29 #include <gtk/gtkentry.h>
30 #include <gtk/gtkhpaned.h>
31 #include <gtk/gtkvpaned.h>
32 #include <gtk/gtkframe.h>
33 #include <gtk/gtklabel.h>
34 #include <gtk/gtkmenuitem.h>
35 #include <gtk/gtkprogressbar.h>
36 #include <gtk/gtkscrolledwindow.h>
37 #include <gtk/gtkstock.h>
38 #include <gtk/gtktable.h>
39 #include <gtk/gtknotebook.h>
40 #include <gtk/gtkimage.h>
41 #include <gtk/gtkmessagedialog.h>
42 #include <gtk/gtkcheckmenuitem.h>
43 #include <gtk/gtkcheckbutton.h>
44 #include <gtk/gtkbbox.h>
45 #include <gtk/gtkvscrollbar.h>
47 #include "../common/xchat.h"
48 #include "../common/fe.h"
49 #include "../common/server.h"
50 #include "../common/xchatc.h"
51 #include "../common/outbound.h"
52 #include "../common/inbound.h"
53 #include "../common/plugin.h"
54 #include "../common/modes.h"
55 #include "../common/url.h"
56 #include "fe-gtk.h"
57 #include "banlist.h"
58 #include "gtkutil.h"
59 #include "joind.h"
60 #include "palette.h"
61 #include "maingui.h"
62 #include "menu.h"
63 #include "fkeys.h"
64 #include "userlistgui.h"
65 #include "chanview.h"
66 #include "pixmaps.h"
67 #include "plugin-tray.h"
68 #include "xtext.h"
70 #ifdef USE_GTKSPELL
71 #include <gtk/gtktextview.h>
72 #include <gtkspell/gtkspell.h>
73 #endif
75 #ifdef USE_LIBSEXY
76 #include "sexy-spell-entry.h"
77 #endif
79 #define GUI_SPACING (3)
80 #define GUI_BORDER (0)
81 #define SCROLLBAR_SPACING (2)
83 enum
85 POS_INVALID = 0,
86 POS_TOPLEFT = 1,
87 POS_BOTTOMLEFT = 2,
88 POS_TOPRIGHT = 3,
89 POS_BOTTOMRIGHT = 4,
90 POS_TOP = 5, /* for tabs only */
91 POS_BOTTOM = 6,
92 POS_HIDDEN = 7
95 /* two different types of tabs */
96 #define TAG_IRC 0 /* server, channel, dialog */
97 #define TAG_UTIL 1 /* dcc, notify, chanlist */
99 static void mg_create_entry (session *sess, GtkWidget *box);
100 static void mg_link_irctab (session *sess, int focus);
102 static session_gui static_mg_gui;
103 static session_gui *mg_gui = NULL; /* the shared irc tab */
104 static int ignore_chanmode = FALSE;
105 static const char chan_flags[] = { 't', 'n', 's', 'i', 'p', 'm', 'l', 'k' };
107 static chan *active_tab = NULL; /* active tab */
108 GtkWidget *parent_window = NULL; /* the master window */
110 GtkStyle *input_style;
112 static PangoAttrList *away_list;
113 static PangoAttrList *newdata_list;
114 static PangoAttrList *nickseen_list;
115 static PangoAttrList *newmsg_list;
116 static PangoAttrList *plain_list = NULL;
119 #ifdef USE_GTKSPELL
121 /* use these when it's a GtkTextView instead of GtkEntry */
123 char *
124 SPELL_ENTRY_GET_TEXT (GtkWidget *entry)
126 static char *last = NULL; /* warning: don't overlap 2 GET_TEXT calls! */
127 GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry));
128 GtkTextIter start_iter, end_iter;
130 gtk_text_buffer_get_iter_at_offset (buf, &start_iter, 0);
131 gtk_text_buffer_get_end_iter (buf, &end_iter);
132 g_free (last);
133 last = gtk_text_buffer_get_text (buf, &start_iter, &end_iter, FALSE);
134 return last;
137 void
138 SPELL_ENTRY_SET_POS (GtkWidget *entry, int pos)
140 GtkTextIter iter;
141 GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry));
143 gtk_text_buffer_get_iter_at_offset (buf, &iter, pos);
144 gtk_text_buffer_place_cursor (buf, &iter);
148 SPELL_ENTRY_GET_POS (GtkWidget *entry)
150 GtkTextIter cursor;
151 GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry));
153 gtk_text_buffer_get_iter_at_mark (buf, &cursor, gtk_text_buffer_get_insert (buf));
154 return gtk_text_iter_get_offset (&cursor);
157 void
158 SPELL_ENTRY_INSERT (GtkWidget *entry, const char *text, int len, int *pos)
160 GtkTextIter iter;
161 GtkTextBuffer *buf = gtk_text_view_get_buffer (GTK_TEXT_VIEW (entry));
163 /* len is bytes. pos is chars. */
164 gtk_text_buffer_get_iter_at_offset (buf, &iter, *pos);
165 gtk_text_buffer_insert (buf, &iter, text, len);
166 *pos += g_utf8_strlen (text, len);
169 #endif
171 static PangoAttrList *
172 mg_attr_list_create (GdkColor *col, int size)
174 PangoAttribute *attr;
175 PangoAttrList *list;
177 list = pango_attr_list_new ();
179 if (col)
181 attr = pango_attr_foreground_new (col->red, col->green, col->blue);
182 attr->start_index = 0;
183 attr->end_index = 0xffff;
184 pango_attr_list_insert (list, attr);
187 if (size > 0)
189 attr = pango_attr_scale_new (size == 1 ? PANGO_SCALE_SMALL : PANGO_SCALE_X_SMALL);
190 attr->start_index = 0;
191 attr->end_index = 0xffff;
192 pango_attr_list_insert (list, attr);
195 return list;
198 static void
199 mg_create_tab_colors (void)
201 if (plain_list)
203 pango_attr_list_unref (plain_list);
204 pango_attr_list_unref (newmsg_list);
205 pango_attr_list_unref (newdata_list);
206 pango_attr_list_unref (nickseen_list);
207 pango_attr_list_unref (away_list);
210 plain_list = mg_attr_list_create (NULL, prefs.tab_small);
211 newdata_list = mg_attr_list_create (&colors[COL_NEW_DATA], prefs.tab_small);
212 nickseen_list = mg_attr_list_create (&colors[COL_HILIGHT], prefs.tab_small);
213 newmsg_list = mg_attr_list_create (&colors[COL_NEW_MSG], prefs.tab_small);
214 away_list = mg_attr_list_create (&colors[COL_AWAY], FALSE);
217 #ifdef USE_XLIB
218 #include <gdk/gdkx.h>
220 static void
221 set_window_urgency (GtkWidget *win, gboolean set)
223 XWMHints *hints;
225 hints = XGetWMHints(GDK_WINDOW_XDISPLAY(win->window), GDK_WINDOW_XWINDOW(win->window));
226 if (set)
227 hints->flags |= XUrgencyHint;
228 else
229 hints->flags &= ~XUrgencyHint;
230 XSetWMHints(GDK_WINDOW_XDISPLAY(win->window),
231 GDK_WINDOW_XWINDOW(win->window), hints);
232 XFree(hints);
235 static void
236 flash_window (GtkWidget *win)
238 set_window_urgency (win, TRUE);
241 static void
242 unflash_window (GtkWidget *win)
244 set_window_urgency (win, FALSE);
246 #endif
248 /* flash the taskbar button */
250 void
251 fe_flash_window (session *sess)
253 #if defined(USE_XLIB)
254 if (fe_gui_info (sess, 0) != 1) /* only do it if not focused */
255 flash_window (sess->gui->window);
256 #endif
259 /* set a tab plain, red, light-red, or blue */
261 void
262 fe_set_tab_color (struct session *sess, int col)
264 struct session *server_sess = sess->server->server_session;
265 if (sess->gui->is_tab && (col == 0 || sess != current_tab))
267 switch (col)
269 case 0: /* no particular color (theme default) */
270 sess->new_data = FALSE;
271 sess->msg_said = FALSE;
272 sess->nick_said = FALSE;
273 chan_set_color (sess->res->tab, plain_list);
274 break;
275 case 1: /* new data has been displayed (dark red) */
276 sess->new_data = TRUE;
277 sess->msg_said = FALSE;
278 sess->nick_said = FALSE;
279 chan_set_color (sess->res->tab, newdata_list);
281 if (chan_is_collapsed (sess->res->tab)
282 && !(server_sess->msg_said || server_sess->nick_said))
284 server_sess->new_data = TRUE;
285 server_sess->msg_said = FALSE;
286 server_sess->nick_said = FALSE;
287 chan_set_color (chan_get_parent (sess->res->tab), newdata_list);
290 break;
291 case 2: /* new message arrived in channel (light red) */
292 sess->new_data = FALSE;
293 sess->msg_said = TRUE;
294 sess->nick_said = FALSE;
295 chan_set_color (sess->res->tab, newmsg_list);
297 if (chan_is_collapsed (sess->res->tab) && !server_sess->nick_said)
299 server_sess->new_data = FALSE;
300 server_sess->msg_said = TRUE;
301 server_sess->nick_said = FALSE;
302 chan_set_color (chan_get_parent (sess->res->tab), newmsg_list);
305 break;
306 case 3: /* your nick has been seen (blue) */
307 sess->new_data = FALSE;
308 sess->msg_said = FALSE;
309 sess->nick_said = TRUE;
310 chan_set_color (sess->res->tab, nickseen_list);
312 if (chan_is_collapsed (sess->res->tab))
314 server_sess->new_data = FALSE;
315 server_sess->msg_said = FALSE;
316 server_sess->nick_said = TRUE;
317 chan_set_color (chan_get_parent (sess->res->tab), nickseen_list);
320 break;
325 static void
326 mg_set_myself_away (session_gui *gui, gboolean away)
328 gtk_label_set_attributes (GTK_LABEL (GTK_BIN (gui->nick_label)->child),
329 away ? away_list : NULL);
332 /* change the little icon to the left of your nickname */
334 void
335 mg_set_access_icon (session_gui *gui, GdkPixbuf *pix, gboolean away)
337 if (gui->op_xpm)
339 if (pix == gtk_image_get_pixbuf (GTK_IMAGE (gui->op_xpm))) /* no change? */
341 mg_set_myself_away (gui, away);
342 return;
345 gtk_widget_destroy (gui->op_xpm);
346 gui->op_xpm = NULL;
349 if (pix)
351 gui->op_xpm = gtk_image_new_from_pixbuf (pix);
352 gtk_box_pack_start (GTK_BOX (gui->nick_box), gui->op_xpm, 0, 0, 0);
353 gtk_widget_show (gui->op_xpm);
356 mg_set_myself_away (gui, away);
359 static gboolean
360 mg_inputbox_focus (GtkWidget *widget, GdkEventFocus *event, session_gui *gui)
362 GSList *list;
363 session *sess;
365 if (gui->is_tab)
366 return FALSE;
368 list = sess_list;
369 while (list)
371 sess = list->data;
372 if (sess->gui == gui)
374 current_sess = sess;
375 if (!sess->server->server_session)
376 sess->server->server_session = sess;
377 break;
379 list = list->next;
382 return FALSE;
385 void
386 mg_inputbox_cb (GtkWidget *igad, session_gui *gui)
388 char *cmd;
389 static int ignore = FALSE;
390 GSList *list;
391 session *sess = NULL;
393 if (ignore)
394 return;
396 cmd = SPELL_ENTRY_GET_TEXT (igad);
397 if (cmd[0] == 0)
398 return;
400 cmd = strdup (cmd);
402 /* avoid recursive loop */
403 ignore = TRUE;
404 SPELL_ENTRY_SET_TEXT (igad, "");
405 ignore = FALSE;
407 /* where did this event come from? */
408 if (gui->is_tab)
410 sess = current_tab;
411 } else
413 list = sess_list;
414 while (list)
416 sess = list->data;
417 if (sess->gui == gui)
418 break;
419 list = list->next;
421 if (!list)
422 sess = NULL;
425 if (sess)
426 handle_multiline (sess, cmd, TRUE, FALSE);
428 free (cmd);
431 static gboolean
432 has_key (char *modes)
434 if (!modes)
435 return FALSE;
436 /* this is a crude check, but "-k" can't exist, so it works. */
437 while (*modes)
439 if (*modes == 'k')
440 return TRUE;
441 if (*modes == ' ')
442 return FALSE;
443 modes++;
445 return FALSE;
448 void
449 fe_set_title (session *sess)
451 char tbuf[512];
452 int type;
454 if (sess->gui->is_tab && sess != current_tab)
455 return;
457 type = sess->type;
459 if (sess->server->connected == FALSE && sess->type != SESS_DIALOG)
460 goto def;
462 switch (type)
464 case SESS_DIALOG:
465 snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s %s @ %s",
466 _("Dialog with"), sess->channel, server_get_network (sess->server, TRUE));
467 break;
468 case SESS_SERVER:
469 snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s @ %s",
470 sess->server->nick, server_get_network (sess->server, TRUE));
471 break;
472 case SESS_CHANNEL:
473 /* don't display keys in the titlebar */
474 if ((!(prefs.gui_tweaks & 16)) && has_key (sess->current_modes))
475 snprintf (tbuf, sizeof (tbuf),
476 DISPLAY_NAME": %s @ %s / %s",
477 sess->server->nick, server_get_network (sess->server, TRUE),
478 sess->channel);
479 else
480 snprintf (tbuf, sizeof (tbuf),
481 DISPLAY_NAME": %s @ %s / %s (%s)",
482 sess->server->nick, server_get_network (sess->server, TRUE),
483 sess->channel, sess->current_modes ? sess->current_modes : "");
484 if (prefs.gui_tweaks & 1)
485 snprintf (tbuf + strlen (tbuf), 9, " (%d)", sess->total);
486 break;
487 case SESS_NOTICES:
488 case SESS_SNOTICES:
489 snprintf (tbuf, sizeof (tbuf), DISPLAY_NAME": %s @ %s (notices)",
490 sess->server->nick, server_get_network (sess->server, TRUE));
491 break;
492 default:
493 def:
494 gtk_window_set_title (GTK_WINDOW (sess->gui->window), DISPLAY_NAME);
495 return;
498 gtk_window_set_title (GTK_WINDOW (sess->gui->window), tbuf);
501 static gboolean
502 mg_windowstate_cb (GtkWindow *wid, GdkEventWindowState *event, gpointer userdata)
504 prefs.gui_win_state = 0;
505 if (event->new_window_state & GDK_WINDOW_STATE_MAXIMIZED)
506 prefs.gui_win_state = 1;
508 if ((event->changed_mask & GDK_WINDOW_STATE_ICONIFIED) &&
509 (event->new_window_state & GDK_WINDOW_STATE_ICONIFIED) &&
510 (prefs.gui_tray_flags & 4))
512 tray_toggle_visibility (TRUE);
513 gtk_window_deiconify (wid);
516 return FALSE;
519 static gboolean
520 mg_configure_cb (GtkWidget *wid, GdkEventConfigure *event, session *sess)
522 if (sess == NULL) /* for the main_window */
524 if (mg_gui)
526 if (prefs.mainwindow_save)
528 sess = current_sess;
529 gtk_window_get_position (GTK_WINDOW (wid), &prefs.mainwindow_left,
530 &prefs.mainwindow_top);
531 gtk_window_get_size (GTK_WINDOW (wid), &prefs.mainwindow_width,
532 &prefs.mainwindow_height);
537 if (sess)
539 if (sess->type == SESS_DIALOG && prefs.mainwindow_save)
541 gtk_window_get_position (GTK_WINDOW (wid), &prefs.dialog_left,
542 &prefs.dialog_top);
543 gtk_window_get_size (GTK_WINDOW (wid), &prefs.dialog_width,
544 &prefs.dialog_height);
547 if (((GtkXText *) sess->gui->xtext)->transparent)
548 gtk_widget_queue_draw (sess->gui->xtext);
551 return FALSE;
554 /* move to a non-irc tab */
556 static void
557 mg_show_generic_tab (GtkWidget *box)
559 int num;
560 GtkWidget *f = NULL;
562 #if defined(GTK_WIDGET_HAS_FOCUS)
563 if (current_sess && GTK_WIDGET_HAS_FOCUS (current_sess->gui->input_box))
564 #else
565 if (current_sess && gtk_widget_has_focus (current_sess->gui->input_box))
566 #endif
567 f = current_sess->gui->input_box;
569 num = gtk_notebook_page_num (GTK_NOTEBOOK (mg_gui->note_book), box);
570 gtk_notebook_set_current_page (GTK_NOTEBOOK (mg_gui->note_book), num);
571 gtk_tree_view_set_model (GTK_TREE_VIEW (mg_gui->user_tree), NULL);
572 gtk_window_set_title (GTK_WINDOW (mg_gui->window),
573 g_object_get_data (G_OBJECT (box), "title"));
574 gtk_widget_set_sensitive (mg_gui->menu, FALSE);
576 if (f)
577 gtk_widget_grab_focus (f);
580 /* a channel has been focused */
582 static void
583 mg_focus (session *sess)
585 if (sess->gui->is_tab)
586 current_tab = sess;
587 current_sess = sess;
589 /* dirty trick to avoid auto-selection */
590 SPELL_ENTRY_SET_EDITABLE (sess->gui->input_box, FALSE);
591 gtk_widget_grab_focus (sess->gui->input_box);
592 SPELL_ENTRY_SET_EDITABLE (sess->gui->input_box, TRUE);
594 sess->server->front_session = sess;
596 if (sess->server->server_session != NULL)
598 if (sess->server->server_session->type != SESS_SERVER)
599 sess->server->server_session = sess;
600 } else
602 sess->server->server_session = sess;
605 if (sess->new_data || sess->nick_said || sess->msg_said)
607 sess->nick_said = FALSE;
608 sess->msg_said = FALSE;
609 sess->new_data = FALSE;
610 /* when called via mg_changui_new, is_tab might be true, but
611 sess->res->tab is still NULL. */
612 if (sess->res->tab)
613 fe_set_tab_color (sess, 0);
617 static int
618 mg_progressbar_update (GtkWidget *bar)
620 static int type = 0;
621 static float pos = 0;
623 pos += 0.05;
624 if (pos >= 0.99)
626 if (type == 0)
628 type = 1;
629 gtk_progress_bar_set_orientation ((GtkProgressBar *) bar,
630 GTK_PROGRESS_RIGHT_TO_LEFT);
631 } else
633 type = 0;
634 gtk_progress_bar_set_orientation ((GtkProgressBar *) bar,
635 GTK_PROGRESS_LEFT_TO_RIGHT);
637 pos = 0.05;
639 gtk_progress_bar_set_fraction ((GtkProgressBar *) bar, pos);
640 return 1;
643 void
644 mg_progressbar_create (session_gui *gui)
646 gui->bar = gtk_progress_bar_new ();
647 gtk_box_pack_start (GTK_BOX (gui->nick_box), gui->bar, 0, 0, 0);
648 gtk_widget_show (gui->bar);
649 gui->bartag = fe_timeout_add (50, mg_progressbar_update, gui->bar);
652 void
653 mg_progressbar_destroy (session_gui *gui)
655 fe_timeout_remove (gui->bartag);
656 gtk_widget_destroy (gui->bar);
657 gui->bar = 0;
658 gui->bartag = 0;
661 /* switching tabs away from this one, so remember some info about it! */
663 static void
664 mg_unpopulate (session *sess)
666 restore_gui *res;
667 session_gui *gui;
668 int i;
670 gui = sess->gui;
671 res = sess->res;
673 res->input_text = strdup (SPELL_ENTRY_GET_TEXT (gui->input_box));
674 res->topic_text = strdup (GTK_ENTRY (gui->topic_entry)->text);
675 res->limit_text = strdup (GTK_ENTRY (gui->limit_entry)->text);
676 res->key_text = strdup (GTK_ENTRY (gui->key_entry)->text);
677 if (gui->laginfo)
678 res->lag_text = strdup (gtk_label_get_text (GTK_LABEL (gui->laginfo)));
679 if (gui->throttleinfo)
680 res->queue_text = strdup (gtk_label_get_text (GTK_LABEL (gui->throttleinfo)));
682 for (i = 0; i < NUM_FLAG_WIDS - 1; i++)
683 res->flag_wid_state[i] = GTK_TOGGLE_BUTTON (gui->flag_wid[i])->active;
685 res->old_ul_value = userlist_get_value (gui->user_tree);
686 if (gui->lagometer)
687 res->lag_value = gtk_progress_bar_get_fraction (
688 GTK_PROGRESS_BAR (gui->lagometer));
689 if (gui->throttlemeter)
690 res->queue_value = gtk_progress_bar_get_fraction (
691 GTK_PROGRESS_BAR (gui->throttlemeter));
693 if (gui->bar)
695 res->c_graph = TRUE; /* still have a graph, just not visible now */
696 mg_progressbar_destroy (gui);
700 static void
701 mg_restore_label (GtkWidget *label, char **text)
703 if (!label)
704 return;
706 if (*text)
708 gtk_label_set_text (GTK_LABEL (label), *text);
709 free (*text);
710 *text = NULL;
711 } else
713 gtk_label_set_text (GTK_LABEL (label), "");
717 static void
718 mg_restore_entry (GtkWidget *entry, char **text)
720 if (*text)
722 gtk_entry_set_text (GTK_ENTRY (entry), *text);
723 free (*text);
724 *text = NULL;
725 } else
727 gtk_entry_set_text (GTK_ENTRY (entry), "");
729 gtk_editable_set_position (GTK_EDITABLE (entry), -1);
732 static void
733 mg_restore_speller (GtkWidget *entry, char **text)
735 if (*text)
737 SPELL_ENTRY_SET_TEXT (entry, *text);
738 free (*text);
739 *text = NULL;
740 } else
742 SPELL_ENTRY_SET_TEXT (entry, "");
744 SPELL_ENTRY_SET_POS (entry, -1);
747 void
748 mg_set_topic_tip (session *sess)
750 char *text;
752 switch (sess->type)
754 case SESS_CHANNEL:
755 if (sess->topic)
757 text = g_strdup_printf (_("Topic for %s is: %s"), sess->channel,
758 sess->topic);
759 add_tip (sess->gui->topic_entry, text);
760 g_free (text);
761 } else
762 add_tip (sess->gui->topic_entry, _("No topic is set"));
763 break;
764 default:
765 if (GTK_ENTRY (sess->gui->topic_entry)->text &&
766 GTK_ENTRY (sess->gui->topic_entry)->text[0])
767 add_tip (sess->gui->topic_entry, GTK_ENTRY (sess->gui->topic_entry)->text);
768 else
769 add_tip (sess->gui->topic_entry, NULL);
773 static void
774 mg_hide_empty_pane (GtkPaned *pane)
776 #if defined(GTK_WIDGET_VISIBLE)
777 if ((pane->child1 == NULL || !GTK_WIDGET_VISIBLE (pane->child1)) &&
778 (pane->child2 == NULL || !GTK_WIDGET_VISIBLE (pane->child2)))
779 #else
780 if ((pane->child1 == NULL || !gtk_widget_get_visible (pane->child1)) &&
781 (pane->child2 == NULL || !gtk_widget_get_visible (pane->child2)))
782 #endif
784 gtk_widget_hide (GTK_WIDGET (pane));
785 return;
788 gtk_widget_show (GTK_WIDGET (pane));
791 static void
792 mg_hide_empty_boxes (session_gui *gui)
794 /* hide empty vpanes - so the handle is not shown */
795 mg_hide_empty_pane ((GtkPaned*)gui->vpane_right);
796 mg_hide_empty_pane ((GtkPaned*)gui->vpane_left);
799 static void
800 mg_userlist_showhide (session *sess, int show)
802 session_gui *gui = sess->gui;
803 int handle_size;
805 if (show)
807 gtk_widget_show (gui->user_box);
808 gui->ul_hidden = 0;
810 gtk_widget_style_get (GTK_WIDGET (gui->hpane_right), "handle-size", &handle_size, NULL);
811 gtk_paned_set_position (GTK_PANED (gui->hpane_right), GTK_WIDGET (gui->hpane_right)->allocation.width - (prefs.gui_pane_right_size + handle_size));
813 else
815 gtk_widget_hide (gui->user_box);
816 gui->ul_hidden = 1;
819 mg_hide_empty_boxes (gui);
822 static gboolean
823 mg_is_userlist_and_tree_combined (void)
825 if (prefs.tab_pos == POS_TOPLEFT && prefs.gui_ulist_pos == POS_BOTTOMLEFT)
826 return TRUE;
827 if (prefs.tab_pos == POS_BOTTOMLEFT && prefs.gui_ulist_pos == POS_TOPLEFT)
828 return TRUE;
830 if (prefs.tab_pos == POS_TOPRIGHT && prefs.gui_ulist_pos == POS_BOTTOMRIGHT)
831 return TRUE;
832 if (prefs.tab_pos == POS_BOTTOMRIGHT && prefs.gui_ulist_pos == POS_TOPRIGHT)
833 return TRUE;
835 return FALSE;
838 /* decide if the userlist should be shown or hidden for this tab */
840 void
841 mg_decide_userlist (session *sess, gboolean switch_to_current)
843 /* when called from menu.c we need this */
844 if (sess->gui == mg_gui && switch_to_current)
845 sess = current_tab;
847 if (prefs.hideuserlist)
849 mg_userlist_showhide (sess, FALSE);
850 return;
853 switch (sess->type)
855 case SESS_SERVER:
856 case SESS_DIALOG:
857 case SESS_NOTICES:
858 case SESS_SNOTICES:
859 if (mg_is_userlist_and_tree_combined ())
860 mg_userlist_showhide (sess, TRUE); /* show */
861 else
862 mg_userlist_showhide (sess, FALSE); /* hide */
863 break;
864 default:
865 mg_userlist_showhide (sess, TRUE); /* show */
869 static void
870 mg_userlist_toggle_cb (GtkWidget *button, gpointer userdata)
872 prefs.hideuserlist = !prefs.hideuserlist;
873 mg_decide_userlist (current_sess, FALSE);
874 gtk_widget_grab_focus (current_sess->gui->input_box);
877 static int ul_tag = 0;
879 static gboolean
880 mg_populate_userlist (session *sess)
882 session_gui *gui;
884 if (!sess)
885 sess = current_tab;
887 if (is_session (sess))
889 gui = sess->gui;
890 if (sess->type == SESS_DIALOG)
891 mg_set_access_icon (sess->gui, NULL, sess->server->is_away);
892 else
893 mg_set_access_icon (sess->gui, get_user_icon (sess->server, sess->me), sess->server->is_away);
894 userlist_show (sess);
895 userlist_set_value (sess->gui->user_tree, sess->res->old_ul_value);
898 ul_tag = 0;
899 return 0;
902 /* fill the irc tab with a new channel */
904 static void
905 mg_populate (session *sess)
907 session_gui *gui = sess->gui;
908 restore_gui *res = sess->res;
909 int i, render = TRUE;
910 guint16 vis = gui->ul_hidden;
912 switch (sess->type)
914 case SESS_DIALOG:
915 /* show the dialog buttons */
916 gtk_widget_show (gui->dialogbutton_box);
917 /* hide the chan-mode buttons */
918 gtk_widget_hide (gui->topicbutton_box);
919 /* hide the userlist */
920 mg_decide_userlist (sess, FALSE);
921 /* shouldn't edit the topic */
922 gtk_editable_set_editable (GTK_EDITABLE (gui->topic_entry), FALSE);
923 break;
924 case SESS_SERVER:
925 if (prefs.chanmodebuttons)
926 gtk_widget_show (gui->topicbutton_box);
927 /* hide the dialog buttons */
928 gtk_widget_hide (gui->dialogbutton_box);
929 /* hide the userlist */
930 mg_decide_userlist (sess, FALSE);
931 /* shouldn't edit the topic */
932 gtk_editable_set_editable (GTK_EDITABLE (gui->topic_entry), FALSE);
933 break;
934 default:
935 /* hide the dialog buttons */
936 gtk_widget_hide (gui->dialogbutton_box);
937 if (prefs.chanmodebuttons)
938 gtk_widget_show (gui->topicbutton_box);
939 /* show the userlist */
940 mg_decide_userlist (sess, FALSE);
941 /* let the topic be editted */
942 gtk_editable_set_editable (GTK_EDITABLE (gui->topic_entry), TRUE);
945 /* move to THE irc tab */
946 if (gui->is_tab)
947 gtk_notebook_set_current_page (GTK_NOTEBOOK (gui->note_book), 0);
949 /* xtext size change? Then don't render, wait for the expose caused
950 by showing/hidding the userlist */
951 if (vis != gui->ul_hidden && gui->user_box->allocation.width > 1)
952 render = FALSE;
954 gtk_xtext_buffer_show (GTK_XTEXT (gui->xtext), res->buffer, render);
956 if (gui->is_tab)
957 gtk_widget_set_sensitive (gui->menu, TRUE);
959 /* restore all the GtkEntry's */
960 mg_restore_entry (gui->topic_entry, &res->topic_text);
961 mg_restore_speller (gui->input_box, &res->input_text);
962 mg_restore_entry (gui->key_entry, &res->key_text);
963 mg_restore_entry (gui->limit_entry, &res->limit_text);
964 mg_restore_label (gui->laginfo, &res->lag_text);
965 mg_restore_label (gui->throttleinfo, &res->queue_text);
967 mg_focus (sess);
968 fe_set_title (sess);
970 /* this one flickers, so only change if necessary */
971 if (strcmp (sess->server->nick, gtk_button_get_label (GTK_BUTTON (gui->nick_label))) != 0)
972 gtk_button_set_label (GTK_BUTTON (gui->nick_label), sess->server->nick);
974 /* this is slow, so make it a timeout event */
975 if (!gui->is_tab)
977 mg_populate_userlist (sess);
978 } else
980 if (ul_tag == 0)
981 ul_tag = g_idle_add ((GSourceFunc)mg_populate_userlist, NULL);
984 fe_userlist_numbers (sess);
986 /* restore all the channel mode buttons */
987 ignore_chanmode = TRUE;
988 for (i = 0; i < NUM_FLAG_WIDS - 1; i++)
989 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (gui->flag_wid[i]),
990 res->flag_wid_state[i]);
991 ignore_chanmode = FALSE;
993 if (gui->lagometer)
995 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (gui->lagometer),
996 res->lag_value);
997 if (res->lag_tip)
998 add_tip (sess->gui->lagometer->parent, res->lag_tip);
1000 if (gui->throttlemeter)
1002 gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (gui->throttlemeter),
1003 res->queue_value);
1004 if (res->queue_tip)
1005 add_tip (sess->gui->throttlemeter->parent, res->queue_tip);
1008 /* did this tab have a connecting graph? restore it.. */
1009 if (res->c_graph)
1011 res->c_graph = FALSE;
1012 mg_progressbar_create (gui);
1015 /* menu items */
1016 GTK_CHECK_MENU_ITEM (gui->menu_item[MENU_ID_AWAY])->active = sess->server->is_away;
1017 gtk_widget_set_sensitive (gui->menu_item[MENU_ID_AWAY], sess->server->connected);
1018 gtk_widget_set_sensitive (gui->menu_item[MENU_ID_JOIN], sess->server->end_of_motd);
1019 gtk_widget_set_sensitive (gui->menu_item[MENU_ID_DISCONNECT],
1020 sess->server->connected || sess->server->recondelay_tag);
1022 mg_set_topic_tip (sess);
1024 plugin_emit_dummy_print (sess, "Focus Tab");
1027 void
1028 mg_bring_tofront_sess (session *sess) /* IRC tab or window */
1030 if (sess->gui->is_tab)
1031 chan_focus (sess->res->tab);
1032 else
1033 gtk_window_present (GTK_WINDOW (sess->gui->window));
1036 void
1037 mg_bring_tofront (GtkWidget *vbox) /* non-IRC tab or window */
1039 chan *ch;
1041 ch = g_object_get_data (G_OBJECT (vbox), "ch");
1042 if (ch)
1043 chan_focus (ch);
1044 else
1045 gtk_window_present (GTK_WINDOW (gtk_widget_get_toplevel (vbox)));
1048 void
1049 mg_switch_page (int relative, int num)
1051 if (mg_gui)
1052 chanview_move_focus (mg_gui->chanview, relative, num);
1055 /* a toplevel IRC window was destroyed */
1057 static void
1058 mg_topdestroy_cb (GtkWidget *win, session *sess)
1060 /* printf("enter mg_topdestroy. sess %p was destroyed\n", sess);*/
1062 /* kill the text buffer */
1063 gtk_xtext_buffer_free (sess->res->buffer);
1064 /* kill the user list */
1065 g_object_unref (G_OBJECT (sess->res->user_model));
1067 session_free (sess); /* tell xchat.c about it */
1070 /* cleanup an IRC tab */
1072 static void
1073 mg_ircdestroy (session *sess)
1075 GSList *list;
1077 /* kill the text buffer */
1078 gtk_xtext_buffer_free (sess->res->buffer);
1079 /* kill the user list */
1080 g_object_unref (G_OBJECT (sess->res->user_model));
1082 session_free (sess); /* tell xchat.c about it */
1084 if (mg_gui == NULL)
1086 /* puts("-> mg_gui is already NULL");*/
1087 return;
1090 list = sess_list;
1091 while (list)
1093 sess = list->data;
1094 if (sess->gui->is_tab)
1096 /* puts("-> some tabs still remain");*/
1097 return;
1099 list = list->next;
1102 /* puts("-> no tabs left, killing main tabwindow");*/
1103 gtk_widget_destroy (mg_gui->window);
1104 active_tab = NULL;
1105 mg_gui = NULL;
1106 parent_window = NULL;
1109 static void
1110 mg_tab_close_cb (GtkWidget *dialog, gint arg1, session *sess)
1112 GSList *list, *next;
1114 gtk_widget_destroy (dialog);
1115 if (arg1 == GTK_RESPONSE_OK && is_session (sess))
1117 /* force it NOT to send individual PARTs */
1118 sess->server->sent_quit = TRUE;
1120 for (list = sess_list; list;)
1122 next = list->next;
1123 if (((session *)list->data)->server == sess->server &&
1124 ((session *)list->data) != sess)
1125 fe_close_window ((session *)list->data);
1126 list = next;
1129 /* just send one QUIT - better for BNCs */
1130 sess->server->sent_quit = FALSE;
1131 fe_close_window (sess);
1135 void
1136 mg_tab_close (session *sess)
1138 GtkWidget *dialog;
1139 GSList *list;
1140 int i;
1142 if (chan_remove (sess->res->tab, FALSE))
1143 mg_ircdestroy (sess);
1144 else
1146 for (i = 0, list = sess_list; list; list = list->next)
1147 if (((session *)list->data)->server == sess->server)
1148 i++;
1149 dialog = gtk_message_dialog_new (GTK_WINDOW (parent_window), 0,
1150 GTK_MESSAGE_WARNING, GTK_BUTTONS_OK_CANCEL,
1151 _("This server still has %d channels or dialogs associated with it. "
1152 "Close them all?"), i);
1153 g_signal_connect (G_OBJECT (dialog), "response",
1154 G_CALLBACK (mg_tab_close_cb), sess);
1155 if (prefs.tab_layout)
1157 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_MOUSE);
1159 else
1161 gtk_window_set_position (GTK_WINDOW (dialog), GTK_WIN_POS_CENTER_ON_PARENT);
1163 gtk_widget_show (dialog);
1167 static void
1168 mg_menu_destroy (GtkWidget *menu, gpointer userdata)
1170 gtk_widget_destroy (menu);
1171 g_object_unref (menu);
1174 void
1175 mg_create_icon_item (char *label, char *stock, GtkWidget *menu,
1176 void *callback, void *userdata)
1178 GtkWidget *item;
1180 item = create_icon_menu (label, stock, TRUE);
1181 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1182 g_signal_connect (G_OBJECT (item), "activate", G_CALLBACK (callback),
1183 userdata);
1184 gtk_widget_show (item);
1187 static int
1188 mg_count_networks (void)
1190 int cons = 0;
1191 GSList *list;
1193 for (list = serv_list; list; list = list->next)
1195 if (((server *)list->data)->connected)
1196 cons++;
1198 return cons;
1201 static int
1202 mg_count_dccs (void)
1204 GSList *list;
1205 struct DCC *dcc;
1206 int dccs = 0;
1208 list = dcc_list;
1209 while (list)
1211 dcc = list->data;
1212 if ((dcc->type == TYPE_SEND || dcc->type == TYPE_RECV) &&
1213 dcc->dccstat == STAT_ACTIVE)
1214 dccs++;
1215 list = list->next;
1218 return dccs;
1221 void
1222 mg_open_quit_dialog (gboolean minimize_button)
1224 static GtkWidget *dialog = NULL;
1225 GtkWidget *dialog_vbox1;
1226 GtkWidget *table1;
1227 GtkWidget *image;
1228 GtkWidget *checkbutton1;
1229 GtkWidget *label;
1230 GtkWidget *dialog_action_area1;
1231 GtkWidget *button;
1232 char *text, *connecttext;
1233 int cons;
1234 int dccs;
1236 if (dialog)
1238 gtk_window_present (GTK_WINDOW (dialog));
1239 return;
1242 dccs = mg_count_dccs ();
1243 cons = mg_count_networks ();
1244 if (dccs + cons == 0 || !prefs.gui_quit_dialog)
1246 xchat_exit ();
1247 return;
1250 dialog = gtk_dialog_new ();
1251 gtk_container_set_border_width (GTK_CONTAINER (dialog), 6);
1252 gtk_window_set_title (GTK_WINDOW (dialog), _("Quit XChat?"));
1253 gtk_window_set_transient_for (GTK_WINDOW (dialog), GTK_WINDOW (parent_window));
1254 gtk_window_set_resizable (GTK_WINDOW (dialog), FALSE);
1255 gtk_dialog_set_has_separator (GTK_DIALOG (dialog), FALSE);
1257 dialog_vbox1 = GTK_DIALOG (dialog)->vbox;
1258 gtk_widget_show (dialog_vbox1);
1260 table1 = gtk_table_new (2, 2, FALSE);
1261 gtk_widget_show (table1);
1262 gtk_box_pack_start (GTK_BOX (dialog_vbox1), table1, TRUE, TRUE, 0);
1263 gtk_container_set_border_width (GTK_CONTAINER (table1), 6);
1264 gtk_table_set_row_spacings (GTK_TABLE (table1), 12);
1265 gtk_table_set_col_spacings (GTK_TABLE (table1), 12);
1267 image = gtk_image_new_from_stock ("gtk-dialog-warning", GTK_ICON_SIZE_DIALOG);
1268 gtk_widget_show (image);
1269 gtk_table_attach (GTK_TABLE (table1), image, 0, 1, 0, 1,
1270 (GtkAttachOptions) (GTK_FILL),
1271 (GtkAttachOptions) (GTK_FILL), 0, 0);
1273 checkbutton1 = gtk_check_button_new_with_mnemonic (_("Don't ask next time."));
1274 gtk_widget_show (checkbutton1);
1275 gtk_table_attach (GTK_TABLE (table1), checkbutton1, 0, 2, 1, 2,
1276 (GtkAttachOptions) (GTK_EXPAND | GTK_FILL),
1277 (GtkAttachOptions) (0), 0, 4);
1279 connecttext = g_strdup_printf (_("You are connected to %i IRC networks."), cons);
1280 text = g_strdup_printf ("<span weight=\"bold\" size=\"larger\">%s</span>\n\n%s\n%s",
1281 _("Are you sure you want to quit?"),
1282 cons ? connecttext : "",
1283 dccs ? _("Some file transfers are still active.") : "");
1284 g_free (connecttext);
1285 label = gtk_label_new (text);
1286 g_free (text);
1287 gtk_widget_show (label);
1288 gtk_table_attach (GTK_TABLE (table1), label, 1, 2, 0, 1,
1289 (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK | GTK_FILL),
1290 (GtkAttachOptions) (GTK_EXPAND | GTK_SHRINK), 0, 0);
1291 gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
1292 gtk_misc_set_alignment (GTK_MISC (label), 0, 0.5);
1294 dialog_action_area1 = GTK_DIALOG (dialog)->action_area;
1295 gtk_widget_show (dialog_action_area1);
1296 gtk_button_box_set_layout (GTK_BUTTON_BOX (dialog_action_area1),
1297 GTK_BUTTONBOX_END);
1299 if (minimize_button)
1301 button = gtk_button_new_with_mnemonic (_("_Minimize to Tray"));
1302 gtk_widget_show (button);
1303 gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, 1);
1306 button = gtk_button_new_from_stock ("gtk-cancel");
1307 gtk_widget_show (button);
1308 gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button,
1309 GTK_RESPONSE_CANCEL);
1310 gtk_widget_grab_focus (button);
1312 button = gtk_button_new_from_stock ("gtk-quit");
1313 gtk_widget_show (button);
1314 gtk_dialog_add_action_widget (GTK_DIALOG (dialog), button, 0);
1316 gtk_widget_show (dialog);
1318 switch (gtk_dialog_run (GTK_DIALOG (dialog)))
1320 case 0:
1321 if (GTK_TOGGLE_BUTTON (checkbutton1)->active)
1322 prefs.gui_quit_dialog = 0;
1323 xchat_exit ();
1324 break;
1325 case 1: /* minimize to tray */
1326 if (GTK_TOGGLE_BUTTON (checkbutton1)->active)
1328 prefs.gui_tray_flags |= 1;
1329 /*prefs.gui_quit_dialog = 0;*/
1331 /* force tray icon ON, if not already */
1332 if (!prefs.gui_tray)
1334 prefs.gui_tray = 1;
1335 tray_apply_setup ();
1337 tray_toggle_visibility (TRUE);
1338 break;
1341 gtk_widget_destroy (dialog);
1342 dialog = NULL;
1345 void
1346 mg_close_sess (session *sess)
1348 if (sess_list->next == NULL)
1350 mg_open_quit_dialog (FALSE);
1351 return;
1354 fe_close_window (sess);
1357 static int
1358 mg_chan_remove (chan *ch)
1360 /* remove the tab from chanview */
1361 chan_remove (ch, TRUE);
1362 /* any tabs left? */
1363 if (chanview_get_size (mg_gui->chanview) < 1)
1365 /* if not, destroy the main tab window */
1366 gtk_widget_destroy (mg_gui->window);
1367 current_tab = NULL;
1368 active_tab = NULL;
1369 mg_gui = NULL;
1370 parent_window = NULL;
1371 return TRUE;
1373 return FALSE;
1376 /* destroy non-irc tab/window */
1378 static void
1379 mg_close_gen (chan *ch, GtkWidget *box)
1381 char *title = g_object_get_data (G_OBJECT (box), "title");
1383 if (title)
1384 free (title);
1385 if (!ch)
1386 ch = g_object_get_data (G_OBJECT (box), "ch");
1387 if (ch)
1389 /* remove from notebook */
1390 gtk_widget_destroy (box);
1391 /* remove the tab from chanview */
1392 mg_chan_remove (ch);
1393 } else
1395 gtk_widget_destroy (gtk_widget_get_toplevel (box));
1399 /* the "X" close button has been pressed (tab-view) */
1401 static void
1402 mg_xbutton_cb (chanview *cv, chan *ch, int tag, gpointer userdata)
1404 if (tag == TAG_IRC) /* irc tab */
1405 mg_close_sess (userdata);
1406 else /* non-irc utility tab */
1407 mg_close_gen (ch, userdata);
1410 static void
1411 mg_link_gentab (chan *ch, GtkWidget *box)
1413 int num;
1414 GtkWidget *win;
1416 g_object_ref (box);
1418 num = gtk_notebook_page_num (GTK_NOTEBOOK (mg_gui->note_book), box);
1419 gtk_notebook_remove_page (GTK_NOTEBOOK (mg_gui->note_book), num);
1420 mg_chan_remove (ch);
1422 win = gtkutil_window_new (g_object_get_data (G_OBJECT (box), "title"), "",
1423 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (box), "w")),
1424 GPOINTER_TO_INT (g_object_get_data (G_OBJECT (box), "h")),
1426 /* so it doesn't try to chan_remove (there's no tab anymore) */
1427 g_object_steal_data (G_OBJECT (box), "ch");
1428 gtk_container_set_border_width (GTK_CONTAINER (box), 0);
1429 gtk_container_add (GTK_CONTAINER (win), box);
1430 gtk_widget_show (win);
1432 g_object_unref (box);
1435 static void
1436 mg_detach_tab_cb (GtkWidget *item, chan *ch)
1438 if (chan_get_tag (ch) == TAG_IRC) /* IRC tab */
1440 /* userdata is session * */
1441 mg_link_irctab (chan_get_userdata (ch), 1);
1442 return;
1445 /* userdata is GtkWidget * */
1446 mg_link_gentab (ch, chan_get_userdata (ch)); /* non-IRC tab */
1449 static void
1450 mg_destroy_tab_cb (GtkWidget *item, chan *ch)
1452 /* treat it just like the X button press */
1453 mg_xbutton_cb (mg_gui->chanview, ch, chan_get_tag (ch), chan_get_userdata (ch));
1456 static void
1457 mg_color_insert (GtkWidget *item, gpointer userdata)
1459 char buf[32];
1460 char *text;
1461 int num = GPOINTER_TO_INT (userdata);
1463 if (num > 99)
1465 switch (num)
1467 case 100:
1468 text = "\002"; break;
1469 case 101:
1470 text = "\037"; break;
1471 case 102:
1472 text = "\035"; break;
1473 default:
1474 text = "\017"; break;
1476 key_action_insert (current_sess->gui->input_box, 0, text, 0, 0);
1477 } else
1479 sprintf (buf, "\003%02d", num);
1480 key_action_insert (current_sess->gui->input_box, 0, buf, 0, 0);
1484 static void
1485 mg_markup_item (GtkWidget *menu, char *text, int arg)
1487 GtkWidget *item;
1489 item = gtk_menu_item_new_with_label ("");
1490 gtk_label_set_markup (GTK_LABEL (GTK_BIN (item)->child), text);
1491 g_signal_connect (G_OBJECT (item), "activate",
1492 G_CALLBACK (mg_color_insert), GINT_TO_POINTER (arg));
1493 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1494 gtk_widget_show (item);
1497 GtkWidget *
1498 mg_submenu (GtkWidget *menu, char *text)
1500 GtkWidget *submenu, *item;
1502 item = gtk_menu_item_new_with_mnemonic (text);
1503 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1504 gtk_widget_show (item);
1506 submenu = gtk_menu_new ();
1507 gtk_menu_item_set_submenu (GTK_MENU_ITEM (item), submenu);
1508 gtk_widget_show (submenu);
1510 return submenu;
1513 static void
1514 mg_create_color_menu (GtkWidget *menu, session *sess)
1516 GtkWidget *submenu;
1517 GtkWidget *subsubmenu;
1518 char buf[256];
1519 int i;
1521 submenu = mg_submenu (menu, _("Insert Attribute or Color Code"));
1523 mg_markup_item (submenu, _("<b>Bold</b>"), 100);
1524 mg_markup_item (submenu, _("<u>Underline</u>"), 101);
1525 /*mg_markup_item (submenu, _("<i>Italic</i>"), 102);*/
1526 mg_markup_item (submenu, _("Normal"), 103);
1528 subsubmenu = mg_submenu (submenu, _("Colors 0-7"));
1530 for (i = 0; i < 8; i++)
1532 sprintf (buf, "<tt><sup>%02d</sup> <span background=\"#%02x%02x%02x\">"
1533 " </span></tt>",
1534 i, colors[i].red >> 8, colors[i].green >> 8, colors[i].blue >> 8);
1535 mg_markup_item (subsubmenu, buf, i);
1538 subsubmenu = mg_submenu (submenu, _("Colors 8-15"));
1540 for (i = 8; i < 16; i++)
1542 sprintf (buf, "<tt><sup>%02d</sup> <span background=\"#%02x%02x%02x\">"
1543 " </span></tt>",
1544 i, colors[i].red >> 8, colors[i].green >> 8, colors[i].blue >> 8);
1545 mg_markup_item (subsubmenu, buf, i);
1549 static void
1550 mg_set_guint8 (GtkCheckMenuItem *item, guint8 *setting)
1552 session *sess = current_sess;
1553 guint8 logging = sess->text_logging;
1555 *setting = SET_OFF;
1556 if (item->active)
1557 *setting = SET_ON;
1559 /* has the logging setting changed? */
1560 if (logging != sess->text_logging)
1561 log_open_or_close (sess);
1564 static void
1565 mg_perchan_menu_item (char *label, GtkWidget *menu, guint8 *setting, guint global)
1567 guint8 initial_value = *setting;
1569 /* if it's using global value, use that as initial state */
1570 if (initial_value == SET_DEFAULT)
1571 initial_value = global;
1573 menu_toggle_item (label, menu, mg_set_guint8, setting, initial_value);
1576 static void
1577 mg_create_perchannelmenu (session *sess, GtkWidget *menu)
1579 GtkWidget *submenu;
1581 submenu = menu_quick_sub (_("_Settings"), menu, NULL, XCMENU_MNEMONIC, -1);
1583 mg_perchan_menu_item (_("_Log to Disk"), submenu, &sess->text_logging, prefs.logging);
1584 mg_perchan_menu_item (_("_Reload Scrollback"), submenu, &sess->text_scrollback, prefs.text_replay);
1585 if (sess->type == SESS_CHANNEL)
1586 mg_perchan_menu_item (_("_Hide Join/Part Messages"), submenu, &sess->text_hidejoinpart, prefs.confmode);
1589 static void
1590 mg_create_alertmenu (session *sess, GtkWidget *menu)
1592 GtkWidget *submenu;
1594 submenu = menu_quick_sub (_("_Extra Alerts"), menu, NULL, XCMENU_MNEMONIC, -1);
1596 mg_perchan_menu_item (_("Beep on _Message"), submenu, &sess->alert_beep, prefs.input_beep_chans);
1597 mg_perchan_menu_item (_("Blink Tray _Icon"), submenu, &sess->alert_tray, prefs.input_tray_chans);
1598 mg_perchan_menu_item (_("Blink Task _Bar"), submenu, &sess->alert_taskbar, prefs.input_flash_chans);
1601 static void
1602 mg_create_tabmenu (session *sess, GdkEventButton *event, chan *ch)
1604 GtkWidget *menu, *item;
1605 char buf[256];
1607 menu = gtk_menu_new ();
1609 if (sess)
1611 char *name = g_markup_escape_text (sess->channel[0] ? sess->channel : _("<none>"), -1);
1612 snprintf (buf, sizeof (buf), "<span foreground=\"#3344cc\"><b>%s</b></span>", name);
1613 g_free (name);
1615 item = gtk_menu_item_new_with_label ("");
1616 gtk_label_set_markup (GTK_LABEL (GTK_BIN (item)->child), buf);
1617 gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
1618 gtk_widget_show (item);
1620 /* separator */
1621 menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0);
1623 /* per-channel alerts */
1624 mg_create_alertmenu (sess, menu);
1626 /* per-channel settings */
1627 mg_create_perchannelmenu (sess, menu);
1629 /* separator */
1630 menu_quick_item (0, 0, menu, XCMENU_SHADED, 0, 0);
1632 if (sess->type == SESS_CHANNEL)
1633 menu_addfavoritemenu (sess->server, menu, sess->channel);
1636 mg_create_icon_item (_("_Detach"), GTK_STOCK_REDO, menu,
1637 mg_detach_tab_cb, ch);
1638 mg_create_icon_item (_("_Close"), GTK_STOCK_CLOSE, menu,
1639 mg_destroy_tab_cb, ch);
1640 if (sess && tabmenu_list)
1641 menu_create (menu, tabmenu_list, sess->channel, FALSE);
1642 menu_add_plugin_items (menu, "\x4$TAB", sess->channel);
1644 if (event->window)
1645 gtk_menu_set_screen (GTK_MENU (menu), gdk_drawable_get_screen (event->window));
1646 g_object_ref (menu);
1647 g_object_ref_sink (menu);
1648 g_object_unref (menu);
1649 g_signal_connect (G_OBJECT (menu), "selection-done",
1650 G_CALLBACK (mg_menu_destroy), NULL);
1651 gtk_menu_popup (GTK_MENU (menu), NULL, NULL, NULL, NULL, 0, event->time);
1654 static gboolean
1655 mg_tab_contextmenu_cb (chanview *cv, chan *ch, int tag, gpointer ud, GdkEventButton *event)
1657 /* shift-click to close a tab */
1658 if ((event->state & GDK_SHIFT_MASK) && event->type == GDK_BUTTON_PRESS)
1660 mg_xbutton_cb (cv, ch, tag, ud);
1661 return FALSE;
1664 if (event->button != 3)
1665 return FALSE;
1667 if (tag == TAG_IRC)
1668 mg_create_tabmenu (ud, event, ch);
1669 else
1670 mg_create_tabmenu (NULL, event, ch);
1672 return TRUE;
1675 void
1676 mg_dnd_drop_file (session *sess, char *target, char *uri)
1678 char *p, *data, *next, *fname;
1680 p = data = strdup (uri);
1681 while (*p)
1683 next = strchr (p, '\r');
1684 if (strncasecmp ("file:", p, 5) == 0)
1686 if (next)
1687 *next = 0;
1688 fname = g_filename_from_uri (p, NULL, NULL);
1689 if (fname)
1691 /* dcc_send() expects utf-8 */
1692 p = xchat_filename_to_utf8 (fname, -1, 0, 0, 0);
1693 if (p)
1695 dcc_send (sess, target, p, prefs.dcc_max_send_cps, 0);
1696 g_free (p);
1698 g_free (fname);
1701 if (!next)
1702 break;
1703 p = next + 1;
1704 if (*p == '\n')
1705 p++;
1707 free (data);
1711 static void
1712 mg_dialog_dnd_drop (GtkWidget * widget, GdkDragContext * context, gint x,
1713 gint y, GtkSelectionData * selection_data, guint info,
1714 guint32 time, gpointer ud)
1716 if (current_sess->type == SESS_DIALOG)
1717 /* sess->channel is really the nickname of dialogs */
1718 mg_dnd_drop_file (current_sess, current_sess->channel, selection_data->data);
1721 /* add a tabbed channel */
1723 static void
1724 mg_add_chan (session *sess)
1726 GdkPixbuf *icon;
1727 char *name = _("<none>");
1729 if (sess->channel[0])
1730 name = sess->channel;
1732 switch (sess->type)
1734 case SESS_CHANNEL:
1735 icon = pix_channel;
1736 break;
1737 case SESS_SERVER:
1738 icon = pix_server;
1739 break;
1740 default:
1741 icon = pix_dialog;
1744 sess->res->tab = chanview_add (sess->gui->chanview, name, sess->server, sess,
1745 sess->type == SESS_SERVER ? FALSE : TRUE,
1746 TAG_IRC, icon);
1747 if (plain_list == NULL)
1748 mg_create_tab_colors ();
1750 chan_set_color (sess->res->tab, plain_list);
1752 if (sess->res->buffer == NULL)
1754 sess->res->buffer = gtk_xtext_buffer_new (GTK_XTEXT (sess->gui->xtext));
1755 gtk_xtext_set_time_stamp (sess->res->buffer, prefs.timestamp);
1756 sess->res->user_model = userlist_create_model ();
1760 static void
1761 mg_userlist_button (GtkWidget * box, char *label, char *cmd,
1762 int a, int b, int c, int d)
1764 GtkWidget *wid = gtk_button_new_with_label (label);
1765 g_signal_connect (G_OBJECT (wid), "clicked",
1766 G_CALLBACK (userlist_button_cb), cmd);
1767 gtk_table_attach_defaults (GTK_TABLE (box), wid, a, b, c, d);
1768 show_and_unfocus (wid);
1771 static GtkWidget *
1772 mg_create_userlistbuttons (GtkWidget *box)
1774 struct popup *pop;
1775 GSList *list = button_list;
1776 int a = 0, b = 0;
1777 GtkWidget *tab;
1779 tab = gtk_table_new (5, 2, FALSE);
1780 gtk_box_pack_end (GTK_BOX (box), tab, FALSE, FALSE, 0);
1782 while (list)
1784 pop = list->data;
1785 if (pop->cmd[0])
1787 mg_userlist_button (tab, pop->name, pop->cmd, a, a + 1, b, b + 1);
1788 a++;
1789 if (a == 2)
1791 a = 0;
1792 b++;
1795 list = list->next;
1798 return tab;
1801 static void
1802 mg_topic_cb (GtkWidget *entry, gpointer userdata)
1804 session *sess = current_sess;
1805 char *text;
1807 if (sess->channel[0] && sess->server->connected && sess->type == SESS_CHANNEL)
1809 text = GTK_ENTRY (entry)->text;
1810 if (text[0] == 0)
1811 text = NULL;
1812 sess->server->p_topic (sess->server, sess->channel, text);
1813 } else
1814 gtk_entry_set_text (GTK_ENTRY (entry), "");
1815 /* restore focus to the input widget, where the next input will most
1816 likely be */
1817 gtk_widget_grab_focus (sess->gui->input_box);
1820 static void
1821 mg_tabwindow_kill_cb (GtkWidget *win, gpointer userdata)
1823 GSList *list, *next;
1824 session *sess;
1826 /* puts("enter mg_tabwindow_kill_cb");*/
1827 xchat_is_quitting = TRUE;
1829 /* see if there's any non-tab windows left */
1830 list = sess_list;
1831 while (list)
1833 sess = list->data;
1834 next = list->next;
1835 if (!sess->gui->is_tab)
1837 xchat_is_quitting = FALSE;
1838 /* puts("-> will not exit, some toplevel windows left");*/
1839 } else
1841 mg_ircdestroy (sess);
1843 list = next;
1846 current_tab = NULL;
1847 active_tab = NULL;
1848 mg_gui = NULL;
1849 parent_window = NULL;
1852 static GtkWidget *
1853 mg_changui_destroy (session *sess)
1855 GtkWidget *ret = NULL;
1857 if (sess->gui->is_tab)
1859 /* avoid calling the "destroy" callback */
1860 g_signal_handlers_disconnect_by_func (G_OBJECT (sess->gui->window),
1861 mg_tabwindow_kill_cb, 0);
1862 /* remove the tab from the chanview */
1863 if (!mg_chan_remove (sess->res->tab))
1864 /* if the window still exists, restore the signal handler */
1865 g_signal_connect (G_OBJECT (sess->gui->window), "destroy",
1866 G_CALLBACK (mg_tabwindow_kill_cb), 0);
1867 } else
1869 /* avoid calling the "destroy" callback */
1870 g_signal_handlers_disconnect_by_func (G_OBJECT (sess->gui->window),
1871 mg_topdestroy_cb, sess);
1872 /*gtk_widget_destroy (sess->gui->window);*/
1873 /* don't destroy until the new one is created. Not sure why, but */
1874 /* it fixes: Gdk-CRITICAL **: gdk_colormap_get_screen: */
1875 /* assertion `GDK_IS_COLORMAP (cmap)' failed */
1876 ret = sess->gui->window;
1877 free (sess->gui);
1878 sess->gui = NULL;
1880 return ret;
1883 static void
1884 mg_link_irctab (session *sess, int focus)
1886 GtkWidget *win;
1888 if (sess->gui->is_tab)
1890 win = mg_changui_destroy (sess);
1891 mg_changui_new (sess, sess->res, 0, focus);
1892 mg_populate (sess);
1893 xchat_is_quitting = FALSE;
1894 if (win)
1895 gtk_widget_destroy (win);
1896 return;
1899 mg_unpopulate (sess);
1900 win = mg_changui_destroy (sess);
1901 mg_changui_new (sess, sess->res, 1, focus);
1902 /* the buffer is now attached to a different widget */
1903 ((xtext_buffer *)sess->res->buffer)->xtext = (GtkXText *)sess->gui->xtext;
1904 if (win)
1905 gtk_widget_destroy (win);
1908 void
1909 mg_detach (session *sess, int mode)
1911 switch (mode)
1913 /* detach only */
1914 case 1:
1915 if (sess->gui->is_tab)
1916 mg_link_irctab (sess, 1);
1917 break;
1918 /* attach only */
1919 case 2:
1920 if (!sess->gui->is_tab)
1921 mg_link_irctab (sess, 1);
1922 break;
1923 /* toggle */
1924 default:
1925 mg_link_irctab (sess, 1);
1929 static int
1930 check_is_number (char *t)
1932 while (*t)
1934 if (*t < '0' || *t > '9')
1935 return FALSE;
1936 t++;
1938 return TRUE;
1941 static void
1942 mg_change_flag (GtkWidget * wid, session *sess, char flag)
1944 server *serv = sess->server;
1945 char mode[3];
1947 mode[1] = flag;
1948 mode[2] = '\0';
1949 if (serv->connected && sess->channel[0])
1951 if (GTK_TOGGLE_BUTTON (wid)->active)
1952 mode[0] = '+';
1953 else
1954 mode[0] = '-';
1955 serv->p_mode (serv, sess->channel, mode);
1956 serv->p_join_info (serv, sess->channel);
1957 sess->ignore_mode = TRUE;
1958 sess->ignore_date = TRUE;
1962 static void
1963 flagl_hit (GtkWidget * wid, struct session *sess)
1965 char modes[512];
1966 const char *limit_str;
1967 server *serv = sess->server;
1969 if (GTK_TOGGLE_BUTTON (wid)->active)
1971 if (serv->connected && sess->channel[0])
1973 limit_str = gtk_entry_get_text (GTK_ENTRY (sess->gui->limit_entry));
1974 if (check_is_number ((char *)limit_str) == FALSE)
1976 fe_message (_("User limit must be a number!\n"), FE_MSG_ERROR);
1977 gtk_entry_set_text (GTK_ENTRY (sess->gui->limit_entry), "");
1978 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (wid), FALSE);
1979 return;
1981 snprintf (modes, sizeof (modes), "+l %d", atoi (limit_str));
1982 serv->p_mode (serv, sess->channel, modes);
1983 serv->p_join_info (serv, sess->channel);
1985 } else
1986 mg_change_flag (wid, sess, 'l');
1989 static void
1990 flagk_hit (GtkWidget * wid, struct session *sess)
1992 char modes[512];
1993 server *serv = sess->server;
1995 if (serv->connected && sess->channel[0])
1997 snprintf (modes, sizeof (modes), "-k %s",
1998 gtk_entry_get_text (GTK_ENTRY (sess->gui->key_entry)));
2000 if (GTK_TOGGLE_BUTTON (wid)->active)
2001 modes[0] = '+';
2003 serv->p_mode (serv, sess->channel, modes);
2007 static void
2008 mg_flagbutton_cb (GtkWidget *but, char *flag)
2010 session *sess;
2011 char mode;
2013 if (ignore_chanmode)
2014 return;
2016 sess = current_sess;
2017 mode = tolower ((unsigned char) flag[0]);
2019 switch (mode)
2021 case 'l':
2022 flagl_hit (but, sess);
2023 break;
2024 case 'k':
2025 flagk_hit (but, sess);
2026 break;
2027 case 'b':
2028 ignore_chanmode = TRUE;
2029 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sess->gui->flag_b), FALSE);
2030 ignore_chanmode = FALSE;
2031 banlist_opengui (sess);
2032 break;
2033 default:
2034 mg_change_flag (but, sess, mode);
2038 static GtkWidget *
2039 mg_create_flagbutton (char *tip, GtkWidget *box, char *face)
2041 GtkWidget *wid;
2043 wid = gtk_toggle_button_new_with_label (face);
2044 gtk_widget_set_size_request (wid, 18, 0);
2045 add_tip (wid, tip);
2046 gtk_box_pack_start (GTK_BOX (box), wid, 0, 0, 0);
2047 g_signal_connect (G_OBJECT (wid), "toggled",
2048 G_CALLBACK (mg_flagbutton_cb), face);
2049 show_and_unfocus (wid);
2051 return wid;
2054 static void
2055 mg_key_entry_cb (GtkWidget * igad, gpointer userdata)
2057 char modes[512];
2058 session *sess = current_sess;
2059 server *serv = sess->server;
2061 if (serv->connected && sess->channel[0])
2063 snprintf (modes, sizeof (modes), "+k %s",
2064 gtk_entry_get_text (GTK_ENTRY (igad)));
2065 serv->p_mode (serv, sess->channel, modes);
2066 serv->p_join_info (serv, sess->channel);
2070 static void
2071 mg_limit_entry_cb (GtkWidget * igad, gpointer userdata)
2073 char modes[512];
2074 session *sess = current_sess;
2075 server *serv = sess->server;
2077 if (serv->connected && sess->channel[0])
2079 if (check_is_number ((char *)gtk_entry_get_text (GTK_ENTRY (igad))) == FALSE)
2081 gtk_entry_set_text (GTK_ENTRY (igad), "");
2082 fe_message (_("User limit must be a number!\n"), FE_MSG_ERROR);
2083 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sess->gui->flag_l), FALSE);
2084 return;
2086 snprintf (modes, sizeof(modes), "+l %d",
2087 atoi (gtk_entry_get_text (GTK_ENTRY (igad))));
2088 serv->p_mode (serv, sess->channel, modes);
2089 serv->p_join_info (serv, sess->channel);
2093 static void
2094 mg_apply_entry_style (GtkWidget *entry)
2096 gtk_widget_modify_base (entry, GTK_STATE_NORMAL, &colors[COL_BG]);
2097 gtk_widget_modify_text (entry, GTK_STATE_NORMAL, &colors[COL_FG]);
2098 gtk_widget_modify_font (entry, input_style->font_desc);
2101 static void
2102 mg_create_chanmodebuttons (session_gui *gui, GtkWidget *box)
2104 gui->flag_t = mg_create_flagbutton (_("Topic Protection"), box, "T");
2105 gui->flag_n = mg_create_flagbutton (_("No outside messages"), box, "N");
2106 gui->flag_s = mg_create_flagbutton (_("Secret"), box, "S");
2107 gui->flag_i = mg_create_flagbutton (_("Invite Only"), box, "I");
2108 gui->flag_p = mg_create_flagbutton (_("Private"), box, "P");
2109 gui->flag_m = mg_create_flagbutton (_("Moderated"), box, "M");
2110 gui->flag_b = mg_create_flagbutton (_("Ban List"), box, "B");
2112 gui->flag_k = mg_create_flagbutton (_("Keyword"), box, "K");
2113 gui->key_entry = gtk_entry_new ();
2114 gtk_widget_set_name (gui->key_entry, "xchat-inputbox");
2115 gtk_entry_set_max_length (GTK_ENTRY (gui->key_entry), 16);
2116 gtk_widget_set_size_request (gui->key_entry, 30, -1);
2117 gtk_box_pack_start (GTK_BOX (box), gui->key_entry, 0, 0, 0);
2118 g_signal_connect (G_OBJECT (gui->key_entry), "activate",
2119 G_CALLBACK (mg_key_entry_cb), NULL);
2121 if (prefs.style_inputbox)
2122 mg_apply_entry_style (gui->key_entry);
2124 gui->flag_l = mg_create_flagbutton (_("User Limit"), box, "L");
2125 gui->limit_entry = gtk_entry_new ();
2126 gtk_widget_set_name (gui->limit_entry, "xchat-inputbox");
2127 gtk_entry_set_max_length (GTK_ENTRY (gui->limit_entry), 10);
2128 gtk_widget_set_size_request (gui->limit_entry, 30, -1);
2129 gtk_box_pack_start (GTK_BOX (box), gui->limit_entry, 0, 0, 0);
2130 g_signal_connect (G_OBJECT (gui->limit_entry), "activate",
2131 G_CALLBACK (mg_limit_entry_cb), NULL);
2133 if (prefs.style_inputbox)
2134 mg_apply_entry_style (gui->limit_entry);
2137 /*static void
2138 mg_create_link_buttons (GtkWidget *box, gpointer userdata)
2140 gtkutil_button (box, GTK_STOCK_CLOSE, _("Close this tab/window"),
2141 mg_x_click_cb, userdata, 0);
2143 if (!userdata)
2144 gtkutil_button (box, GTK_STOCK_REDO, _("Attach/Detach this tab"),
2145 mg_link_cb, userdata, 0);
2148 static void
2149 mg_dialog_button_cb (GtkWidget *wid, char *cmd)
2151 /* the longest cmd is 12, and the longest nickname is 64 */
2152 char buf[128];
2153 char *host = "";
2154 char *topic;
2156 if (!current_sess)
2157 return;
2159 topic = (char *)(GTK_ENTRY (current_sess->gui->topic_entry)->text);
2160 topic = strrchr (topic, '@');
2161 if (topic)
2162 host = topic + 1;
2164 auto_insert (buf, sizeof (buf), cmd, 0, 0, "", "", "",
2165 server_get_network (current_sess->server, TRUE), host, "",
2166 current_sess->channel);
2168 handle_command (current_sess, buf, TRUE);
2170 /* dirty trick to avoid auto-selection */
2171 SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, FALSE);
2172 gtk_widget_grab_focus (current_sess->gui->input_box);
2173 SPELL_ENTRY_SET_EDITABLE (current_sess->gui->input_box, TRUE);
2176 static void
2177 mg_dialog_button (GtkWidget *box, char *name, char *cmd)
2179 GtkWidget *wid;
2181 wid = gtk_button_new_with_label (name);
2182 gtk_box_pack_start (GTK_BOX (box), wid, FALSE, FALSE, 0);
2183 g_signal_connect (G_OBJECT (wid), "clicked",
2184 G_CALLBACK (mg_dialog_button_cb), cmd);
2185 gtk_widget_set_size_request (wid, -1, 0);
2188 static void
2189 mg_create_dialogbuttons (GtkWidget *box)
2191 struct popup *pop;
2192 GSList *list = dlgbutton_list;
2194 while (list)
2196 pop = list->data;
2197 if (pop->cmd[0])
2198 mg_dialog_button (box, pop->name, pop->cmd);
2199 list = list->next;
2203 static void
2204 mg_create_topicbar (session *sess, GtkWidget *box)
2206 GtkWidget *hbox, *topic, *bbox;
2207 session_gui *gui = sess->gui;
2209 gui->topic_bar = hbox = gtk_hbox_new (FALSE, 0);
2210 gtk_box_pack_start (GTK_BOX (box), hbox, 0, 0, 0);
2212 if (!gui->is_tab)
2213 sess->res->tab = NULL;
2215 gui->topic_entry = topic = gtk_entry_new ();
2216 gtk_widget_set_name (topic, "xchat-inputbox");
2217 gtk_container_add (GTK_CONTAINER (hbox), topic);
2218 g_signal_connect (G_OBJECT (topic), "activate",
2219 G_CALLBACK (mg_topic_cb), 0);
2221 if (prefs.style_inputbox)
2222 mg_apply_entry_style (topic);
2224 gui->topicbutton_box = bbox = gtk_hbox_new (FALSE, 0);
2225 gtk_box_pack_start (GTK_BOX (hbox), bbox, 0, 0, 0);
2226 mg_create_chanmodebuttons (gui, bbox);
2228 gui->dialogbutton_box = bbox = gtk_hbox_new (FALSE, 0);
2229 gtk_box_pack_start (GTK_BOX (hbox), bbox, 0, 0, 0);
2230 mg_create_dialogbuttons (bbox);
2232 if (!prefs.paned_userlist)
2233 gtkutil_button (hbox, GTK_STOCK_GOTO_LAST, _("Show/Hide userlist"),
2234 mg_userlist_toggle_cb, 0, 0);
2237 /* check if a word is clickable */
2239 static int
2240 mg_word_check (GtkWidget * xtext, char *word, int len)
2242 session *sess = current_sess;
2243 int ret;
2245 ret = url_check_word (word, len); /* common/url.c */
2246 if (ret == 0)
2248 if (( (word[0]=='@' || word[0]=='+' || word[0]=='%') && userlist_find (sess, word+1)) || userlist_find (sess, word))
2249 return WORD_NICK;
2251 if (sess->type == SESS_DIALOG)
2252 return WORD_DIALOG;
2255 return ret;
2258 /* mouse click inside text area */
2260 static void
2261 mg_word_clicked (GtkWidget *xtext, char *word, GdkEventButton *even)
2263 session *sess = current_sess;
2265 if (even->button == 1) /* left button */
2267 if (word == NULL)
2269 mg_focus (sess);
2270 return;
2273 if ((even->state & 13) == prefs.gui_url_mod)
2275 switch (mg_word_check (xtext, word, strlen (word)))
2277 case WORD_URL:
2278 case WORD_HOST:
2279 fe_open_url (word);
2282 return;
2285 if (even->button == 2)
2287 if (sess->type == SESS_DIALOG)
2288 menu_middlemenu (sess, even);
2289 else if (even->type == GDK_2BUTTON_PRESS)
2290 userlist_select (sess, word);
2291 return;
2294 switch (mg_word_check (xtext, word, strlen (word)))
2296 case 0:
2297 menu_middlemenu (sess, even);
2298 break;
2299 case WORD_URL:
2300 case WORD_HOST:
2301 menu_urlmenu (even, word);
2302 break;
2303 case WORD_NICK:
2304 menu_nickmenu (sess, even, (word[0]=='@' || word[0]=='+' || word[0]=='%') ?
2305 word+1 : word, FALSE);
2306 break;
2307 case WORD_CHANNEL:
2308 if (*word == '@' || *word == '+' || *word=='^' || *word=='%' || *word=='*')
2309 word++;
2310 menu_chanmenu (sess, even, word);
2311 break;
2312 case WORD_EMAIL:
2314 char *newword = malloc (strlen (word) + 10);
2315 if (*word == '~')
2316 word++;
2317 sprintf (newword, "mailto:%s", word);
2318 menu_urlmenu (even, newword);
2319 free (newword);
2321 break;
2322 case WORD_DIALOG:
2323 menu_nickmenu (sess, even, sess->channel, FALSE);
2324 break;
2328 void
2329 mg_update_xtext (GtkWidget *wid)
2331 GtkXText *xtext = GTK_XTEXT (wid);
2333 gtk_xtext_set_palette (xtext, colors);
2334 gtk_xtext_set_max_lines (xtext, prefs.max_lines);
2335 gtk_xtext_set_tint (xtext, prefs.tint_red, prefs.tint_green, prefs.tint_blue);
2336 gtk_xtext_set_background (xtext, channelwin_pix, prefs.transparent);
2337 gtk_xtext_set_wordwrap (xtext, prefs.wordwrap);
2338 gtk_xtext_set_show_marker (xtext, prefs.show_marker);
2339 gtk_xtext_set_show_separator (xtext, prefs.indent_nicks ? prefs.show_separator : 0);
2340 gtk_xtext_set_indent (xtext, prefs.indent_nicks);
2341 if (!gtk_xtext_set_font (xtext, prefs.font_normal))
2343 fe_message ("Failed to open any font. I'm out of here!", FE_MSG_WAIT | FE_MSG_ERROR);
2344 exit (1);
2347 gtk_xtext_refresh (xtext, FALSE);
2350 /* handle errors reported by xtext */
2352 static void
2353 mg_xtext_error (int type)
2355 switch (type)
2357 case 0:
2358 fe_message (_("Unable to set transparent background!\n\n"
2359 "You may be using a non-compliant window\n"
2360 "manager that is not currently supported.\n"), FE_MSG_WARN);
2361 prefs.transparent = 0;
2362 /* no others exist yet */
2366 static void
2367 mg_create_textarea (session *sess, GtkWidget *box)
2369 GtkWidget *inbox, *vbox, *frame;
2370 GtkXText *xtext;
2371 session_gui *gui = sess->gui;
2372 static const GtkTargetEntry dnd_targets[] =
2374 {"text/uri-list", 0, 1}
2376 static const GtkTargetEntry dnd_dest_targets[] =
2378 {"XCHAT_CHANVIEW", GTK_TARGET_SAME_APP, 75 },
2379 {"XCHAT_USERLIST", GTK_TARGET_SAME_APP, 75 }
2382 vbox = gtk_vbox_new (FALSE, 0);
2383 gtk_container_add (GTK_CONTAINER (box), vbox);
2385 inbox = gtk_hbox_new (FALSE, SCROLLBAR_SPACING);
2386 gtk_container_add (GTK_CONTAINER (vbox), inbox);
2388 frame = gtk_frame_new (NULL);
2389 gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
2390 gtk_container_add (GTK_CONTAINER (inbox), frame);
2392 gui->xtext = gtk_xtext_new (colors, TRUE);
2393 xtext = GTK_XTEXT (gui->xtext);
2394 gtk_xtext_set_max_indent (xtext, prefs.max_auto_indent);
2395 gtk_xtext_set_thin_separator (xtext, prefs.thin_separator);
2396 gtk_xtext_set_error_function (xtext, mg_xtext_error);
2397 gtk_xtext_set_urlcheck_function (xtext, mg_word_check);
2398 gtk_xtext_set_max_lines (xtext, prefs.max_lines);
2399 gtk_container_add (GTK_CONTAINER (frame), GTK_WIDGET (xtext));
2400 mg_update_xtext (GTK_WIDGET (xtext));
2402 g_signal_connect (G_OBJECT (xtext), "word_click",
2403 G_CALLBACK (mg_word_clicked), NULL);
2405 gui->vscrollbar = gtk_vscrollbar_new (GTK_XTEXT (xtext)->adj);
2406 gtk_box_pack_start (GTK_BOX (inbox), gui->vscrollbar, FALSE, TRUE, 0);
2407 gtk_drag_dest_set (gui->vscrollbar, 5, dnd_dest_targets, 2,
2408 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
2409 g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_begin",
2410 G_CALLBACK (mg_drag_begin_cb), NULL);
2411 g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_drop",
2412 G_CALLBACK (mg_drag_drop_cb), NULL);
2413 g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_motion",
2414 G_CALLBACK (mg_drag_motion_cb), gui->vscrollbar);
2415 g_signal_connect (G_OBJECT (gui->vscrollbar), "drag_end",
2416 G_CALLBACK (mg_drag_end_cb), NULL);
2418 gtk_drag_dest_set (gui->xtext, GTK_DEST_DEFAULT_ALL, dnd_targets, 1,
2419 GDK_ACTION_MOVE | GDK_ACTION_COPY | GDK_ACTION_LINK);
2420 g_signal_connect (G_OBJECT (gui->xtext), "drag_data_received",
2421 G_CALLBACK (mg_dialog_dnd_drop), NULL);
2424 static GtkWidget *
2425 mg_create_infoframe (GtkWidget *box)
2427 GtkWidget *frame, *label, *hbox;
2429 frame = gtk_frame_new (0);
2430 gtk_frame_set_shadow_type ((GtkFrame*)frame, GTK_SHADOW_OUT);
2431 gtk_container_add (GTK_CONTAINER (box), frame);
2433 hbox = gtk_hbox_new (0, 0);
2434 gtk_container_add (GTK_CONTAINER (frame), hbox);
2436 label = gtk_label_new (NULL);
2437 gtk_container_add (GTK_CONTAINER (hbox), label);
2439 return label;
2442 static void
2443 mg_create_meters (session_gui *gui, GtkWidget *parent_box)
2445 GtkWidget *infbox, *wid, *box;
2447 gui->meter_box = infbox = box = gtk_vbox_new (0, 1);
2448 gtk_box_pack_start (GTK_BOX (parent_box), box, 0, 0, 0);
2450 if ((prefs.lagometer & 2) || (prefs.throttlemeter & 2))
2452 infbox = gtk_hbox_new (0, 0);
2453 gtk_box_pack_start (GTK_BOX (box), infbox, 0, 0, 0);
2456 if (prefs.lagometer & 1)
2458 gui->lagometer = wid = gtk_progress_bar_new ();
2459 gtk_widget_set_size_request (wid, 1, 8);
2461 wid = gtk_event_box_new ();
2462 gtk_container_add (GTK_CONTAINER (wid), gui->lagometer);
2463 gtk_box_pack_start (GTK_BOX (box), wid, 0, 0, 0);
2465 if (prefs.lagometer & 2)
2467 gui->laginfo = wid = mg_create_infoframe (infbox);
2468 gtk_label_set_text ((GtkLabel *) wid, "Lag");
2471 if (prefs.throttlemeter & 1)
2473 gui->throttlemeter = wid = gtk_progress_bar_new ();
2474 gtk_widget_set_size_request (wid, 1, 8);
2476 wid = gtk_event_box_new ();
2477 gtk_container_add (GTK_CONTAINER (wid), gui->throttlemeter);
2478 gtk_box_pack_start (GTK_BOX (box), wid, 0, 0, 0);
2480 if (prefs.throttlemeter & 2)
2482 gui->throttleinfo = wid = mg_create_infoframe (infbox);
2483 gtk_label_set_text ((GtkLabel *) wid, "Throttle");
2487 void
2488 mg_update_meters (session_gui *gui)
2490 gtk_widget_destroy (gui->meter_box);
2491 gui->lagometer = NULL;
2492 gui->laginfo = NULL;
2493 gui->throttlemeter = NULL;
2494 gui->throttleinfo = NULL;
2496 mg_create_meters (gui, gui->button_box_parent);
2497 gtk_widget_show_all (gui->meter_box);
2500 static void
2501 mg_create_userlist (session_gui *gui, GtkWidget *box)
2503 GtkWidget *frame, *ulist, *vbox;
2505 vbox = gtk_vbox_new (0, 1);
2506 gtk_container_add (GTK_CONTAINER (box), vbox);
2508 frame = gtk_frame_new (NULL);
2509 if (!(prefs.gui_tweaks & 1))
2510 gtk_box_pack_start (GTK_BOX (vbox), frame, 0, 0, GUI_SPACING);
2512 gui->namelistinfo = gtk_label_new (NULL);
2513 gtk_container_add (GTK_CONTAINER (frame), gui->namelistinfo);
2515 gui->user_tree = ulist = userlist_create (vbox);
2517 if (prefs.style_namelistgad)
2519 gtk_widget_set_style (ulist, input_style);
2520 gtk_widget_modify_base (ulist, GTK_STATE_NORMAL, &colors[COL_BG]);
2523 mg_create_meters (gui, vbox);
2525 gui->button_box_parent = vbox;
2526 gui->button_box = mg_create_userlistbuttons (vbox);
2529 static void
2530 mg_leftpane_cb (GtkPaned *pane, GParamSpec *param, session_gui *gui)
2532 prefs.gui_pane_left_size = gtk_paned_get_position (pane);
2535 static void
2536 mg_rightpane_cb (GtkPaned *pane, GParamSpec *param, session_gui *gui)
2538 int handle_size;
2540 /* if (pane->child1 == NULL || (!GTK_WIDGET_VISIBLE (pane->child1)))
2541 return;
2542 if (pane->child2 == NULL || (!GTK_WIDGET_VISIBLE (pane->child2)))
2543 return;*/
2545 gtk_widget_style_get (GTK_WIDGET (pane), "handle-size", &handle_size, NULL);
2546 /* record the position from the RIGHT side */
2547 prefs.gui_pane_right_size = GTK_WIDGET (pane)->allocation.width - gtk_paned_get_position (pane) - handle_size;
2550 static gboolean
2551 mg_add_pane_signals (session_gui *gui)
2553 g_signal_connect (G_OBJECT (gui->hpane_right), "notify::position",
2554 G_CALLBACK (mg_rightpane_cb), gui);
2555 g_signal_connect (G_OBJECT (gui->hpane_left), "notify::position",
2556 G_CALLBACK (mg_leftpane_cb), gui);
2557 return FALSE;
2560 static void
2561 mg_create_center (session *sess, session_gui *gui, GtkWidget *box)
2563 GtkWidget *vbox, *hbox, *book;
2565 /* sep between top and bottom of left side */
2566 gui->vpane_left = gtk_vpaned_new ();
2568 /* sep between top and bottom of right side */
2569 gui->vpane_right = gtk_vpaned_new ();
2571 /* sep between left and xtext */
2572 gui->hpane_left = gtk_hpaned_new ();
2573 gtk_paned_set_position (GTK_PANED (gui->hpane_left), prefs.gui_pane_left_size);
2575 /* sep between xtext and right side */
2576 gui->hpane_right = gtk_hpaned_new ();
2578 if (prefs.gui_tweaks & 4)
2580 gtk_paned_pack2 (GTK_PANED (gui->hpane_left), gui->vpane_left, FALSE, TRUE);
2581 gtk_paned_pack1 (GTK_PANED (gui->hpane_left), gui->hpane_right, TRUE, TRUE);
2583 else
2585 gtk_paned_pack1 (GTK_PANED (gui->hpane_left), gui->vpane_left, FALSE, TRUE);
2586 gtk_paned_pack2 (GTK_PANED (gui->hpane_left), gui->hpane_right, TRUE, TRUE);
2588 gtk_paned_pack2 (GTK_PANED (gui->hpane_right), gui->vpane_right, FALSE, TRUE);
2590 gtk_container_add (GTK_CONTAINER (box), gui->hpane_left);
2592 gui->note_book = book = gtk_notebook_new ();
2593 gtk_notebook_set_show_tabs (GTK_NOTEBOOK (book), FALSE);
2594 gtk_notebook_set_show_border (GTK_NOTEBOOK (book), FALSE);
2595 gtk_paned_pack1 (GTK_PANED (gui->hpane_right), book, TRUE, TRUE);
2597 hbox = gtk_hbox_new (FALSE, 0);
2598 gtk_paned_pack1 (GTK_PANED (gui->vpane_right), hbox, FALSE, TRUE);
2599 mg_create_userlist (gui, hbox);
2601 gui->user_box = hbox;
2603 vbox = gtk_vbox_new (FALSE, 3);
2604 gtk_notebook_append_page (GTK_NOTEBOOK (book), vbox, NULL);
2605 mg_create_topicbar (sess, vbox);
2606 mg_create_textarea (sess, vbox);
2607 mg_create_entry (sess, vbox);
2609 g_idle_add ((GSourceFunc)mg_add_pane_signals, gui);
2612 static void
2613 mg_change_nick (int cancel, char *text, gpointer userdata)
2615 char buf[256];
2617 if (!cancel)
2619 snprintf (buf, sizeof (buf), "nick %s", text);
2620 handle_command (current_sess, buf, FALSE);
2624 static void
2625 mg_nickclick_cb (GtkWidget *button, gpointer userdata)
2627 fe_get_str (_("Enter new nickname:"), current_sess->server->nick,
2628 mg_change_nick, NULL);
2631 /* make sure chanview and userlist positions are sane */
2633 static void
2634 mg_sanitize_positions (int *cv, int *ul)
2636 if (prefs.tab_layout == 2)
2638 /* treeview can't be on TOP or BOTTOM */
2639 if (*cv == POS_TOP || *cv == POS_BOTTOM)
2640 *cv = POS_TOPLEFT;
2643 /* userlist can't be on TOP or BOTTOM */
2644 if (*ul == POS_TOP || *ul == POS_BOTTOM)
2645 *ul = POS_TOPRIGHT;
2647 /* can't have both in the same place */
2648 if (*cv == *ul)
2650 *cv = POS_TOPRIGHT;
2651 if (*ul == POS_TOPRIGHT)
2652 *cv = POS_BOTTOMRIGHT;
2656 static void
2657 mg_place_userlist_and_chanview_real (session_gui *gui, GtkWidget *userlist, GtkWidget *chanview)
2659 int unref_userlist = FALSE;
2660 int unref_chanview = FALSE;
2662 /* first, remove userlist/treeview from their containers */
2663 if (userlist && userlist->parent)
2665 g_object_ref (userlist);
2666 gtk_container_remove (GTK_CONTAINER (userlist->parent), userlist);
2667 unref_userlist = TRUE;
2670 if (chanview && chanview->parent)
2672 g_object_ref (chanview);
2673 gtk_container_remove (GTK_CONTAINER (chanview->parent), chanview);
2674 unref_chanview = TRUE;
2677 if (chanview)
2679 /* incase the previous pos was POS_HIDDEN */
2680 gtk_widget_show (chanview);
2682 gtk_table_set_row_spacing (GTK_TABLE (gui->main_table), 1, 0);
2683 gtk_table_set_row_spacing (GTK_TABLE (gui->main_table), 2, 2);
2685 /* then place them back in their new positions */
2686 switch (prefs.tab_pos)
2688 case POS_TOPLEFT:
2689 gtk_paned_pack1 (GTK_PANED (gui->vpane_left), chanview, FALSE, TRUE);
2690 break;
2691 case POS_BOTTOMLEFT:
2692 gtk_paned_pack2 (GTK_PANED (gui->vpane_left), chanview, FALSE, TRUE);
2693 break;
2694 case POS_TOPRIGHT:
2695 gtk_paned_pack1 (GTK_PANED (gui->vpane_right), chanview, FALSE, TRUE);
2696 break;
2697 case POS_BOTTOMRIGHT:
2698 gtk_paned_pack2 (GTK_PANED (gui->vpane_right), chanview, FALSE, TRUE);
2699 break;
2700 case POS_TOP:
2701 gtk_table_set_row_spacing (GTK_TABLE (gui->main_table), 1, GUI_SPACING-1);
2702 gtk_table_attach (GTK_TABLE (gui->main_table), chanview,
2703 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
2704 break;
2705 case POS_HIDDEN:
2706 gtk_widget_hide (chanview);
2707 /* always attach it to something to avoid ref_count=0 */
2708 if (prefs.gui_ulist_pos == POS_TOP)
2709 gtk_table_attach (GTK_TABLE (gui->main_table), chanview,
2710 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
2712 else
2713 gtk_table_attach (GTK_TABLE (gui->main_table), chanview,
2714 1, 2, 1, 2, GTK_FILL, GTK_FILL, 0, 0);
2715 break;
2716 default:/* POS_BOTTOM */
2717 gtk_table_set_row_spacing (GTK_TABLE (gui->main_table), 2, 3);
2718 gtk_table_attach (GTK_TABLE (gui->main_table), chanview,
2719 1, 2, 3, 4, GTK_FILL, GTK_FILL, 0, 0);
2723 if (userlist)
2725 switch (prefs.gui_ulist_pos)
2727 case POS_TOPLEFT:
2728 gtk_paned_pack1 (GTK_PANED (gui->vpane_left), userlist, FALSE, TRUE);
2729 break;
2730 case POS_BOTTOMLEFT:
2731 gtk_paned_pack2 (GTK_PANED (gui->vpane_left), userlist, FALSE, TRUE);
2732 break;
2733 case POS_BOTTOMRIGHT:
2734 gtk_paned_pack2 (GTK_PANED (gui->vpane_right), userlist, FALSE, TRUE);
2735 break;
2736 /*case POS_HIDDEN:
2737 break;*/ /* Hide using the VIEW menu instead */
2738 default:/* POS_TOPRIGHT */
2739 gtk_paned_pack1 (GTK_PANED (gui->vpane_right), userlist, FALSE, TRUE);
2743 if (unref_chanview)
2744 g_object_unref (chanview);
2745 if (unref_userlist)
2746 g_object_unref (userlist);
2748 mg_hide_empty_boxes (gui);
2751 static void
2752 mg_place_userlist_and_chanview (session_gui *gui)
2754 GtkOrientation orientation;
2755 GtkWidget *chanviewbox = NULL;
2756 int pos;
2758 mg_sanitize_positions (&prefs.tab_pos, &prefs.gui_ulist_pos);
2760 if (gui->chanview)
2762 pos = prefs.tab_pos;
2764 orientation = chanview_get_orientation (gui->chanview);
2765 if ((pos == POS_BOTTOM || pos == POS_TOP) && orientation == GTK_ORIENTATION_VERTICAL)
2766 chanview_set_orientation (gui->chanview, FALSE);
2767 else if ((pos == POS_TOPLEFT || pos == POS_BOTTOMLEFT || pos == POS_TOPRIGHT || pos == POS_BOTTOMRIGHT) && orientation == GTK_ORIENTATION_HORIZONTAL)
2768 chanview_set_orientation (gui->chanview, TRUE);
2769 chanviewbox = chanview_get_box (gui->chanview);
2772 mg_place_userlist_and_chanview_real (gui, gui->user_box, chanviewbox);
2775 void
2776 mg_change_layout (int type)
2778 if (mg_gui)
2780 /* put tabs at the bottom */
2781 if (type == 0 && prefs.tab_pos != POS_BOTTOM && prefs.tab_pos != POS_TOP)
2782 prefs.tab_pos = POS_BOTTOM;
2784 mg_place_userlist_and_chanview (mg_gui);
2785 chanview_set_impl (mg_gui->chanview, type);
2789 static void
2790 mg_inputbox_rightclick (GtkEntry *entry, GtkWidget *menu)
2792 mg_create_color_menu (menu, NULL);
2795 static void
2796 mg_create_entry (session *sess, GtkWidget *box)
2798 GtkWidget *sw, *hbox, *but, *entry;
2799 session_gui *gui = sess->gui;
2801 hbox = gtk_hbox_new (FALSE, 0);
2802 gtk_box_pack_start (GTK_BOX (box), hbox, 0, 0, 0);
2804 gui->nick_box = gtk_hbox_new (FALSE, 0);
2805 gtk_box_pack_start (GTK_BOX (hbox), gui->nick_box, 0, 0, 0);
2807 gui->nick_label = but = gtk_button_new_with_label (sess->server->nick);
2808 gtk_button_set_relief (GTK_BUTTON (but), GTK_RELIEF_NONE);
2809 GTK_WIDGET_UNSET_FLAGS (but, GTK_CAN_FOCUS);
2810 gtk_box_pack_end (GTK_BOX (gui->nick_box), but, 0, 0, 0);
2811 g_signal_connect (G_OBJECT (but), "clicked",
2812 G_CALLBACK (mg_nickclick_cb), NULL);
2814 #ifdef USE_GTKSPELL
2815 gui->input_box = entry = gtk_text_view_new ();
2816 gtk_widget_set_size_request (entry, 0, 1);
2817 gtk_text_view_set_wrap_mode (GTK_TEXT_VIEW (entry), GTK_WRAP_NONE);
2818 gtk_text_view_set_accepts_tab (GTK_TEXT_VIEW (entry), FALSE);
2819 if (prefs.gui_input_spell)
2820 gtkspell_new_attach (GTK_TEXT_VIEW (entry), NULL, NULL);
2822 sw = gtk_scrolled_window_new (NULL, NULL);
2823 gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (sw),
2824 GTK_SHADOW_IN);
2825 gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (sw),
2826 GTK_POLICY_NEVER,
2827 GTK_POLICY_NEVER);
2828 gtk_container_add (GTK_CONTAINER (sw), entry);
2829 gtk_container_add (GTK_CONTAINER (hbox), sw);
2830 #else
2831 #ifdef USE_LIBSEXY
2832 gui->input_box = entry = sexy_spell_entry_new ();
2833 sexy_spell_entry_set_checked ((SexySpellEntry *)entry, prefs.gui_input_spell);
2834 #else
2835 gui->input_box = entry = gtk_entry_new ();
2836 #endif
2837 gtk_entry_set_max_length (GTK_ENTRY (gui->input_box), 2048);
2838 g_signal_connect (G_OBJECT (entry), "activate",
2839 G_CALLBACK (mg_inputbox_cb), gui);
2840 gtk_container_add (GTK_CONTAINER (hbox), entry);
2841 #endif
2843 gtk_widget_set_name (entry, "xchat-inputbox");
2844 g_signal_connect (G_OBJECT (entry), "key_press_event",
2845 G_CALLBACK (key_handle_key_press), NULL);
2846 g_signal_connect (G_OBJECT (entry), "focus_in_event",
2847 G_CALLBACK (mg_inputbox_focus), gui);
2848 g_signal_connect (G_OBJECT (entry), "populate_popup",
2849 G_CALLBACK (mg_inputbox_rightclick), NULL);
2850 gtk_widget_grab_focus (entry);
2852 if (prefs.style_inputbox)
2853 mg_apply_entry_style (entry);
2856 static void
2857 mg_switch_tab_cb (chanview *cv, chan *ch, int tag, gpointer ud)
2859 chan *old;
2860 session *sess = ud;
2862 old = active_tab;
2863 active_tab = ch;
2865 if (tag == TAG_IRC)
2867 if (active_tab != old)
2869 if (old && current_tab)
2870 mg_unpopulate (current_tab);
2871 mg_populate (sess);
2873 } else if (old != active_tab)
2875 /* userdata for non-irc tabs is actually the GtkBox */
2876 mg_show_generic_tab (ud);
2877 if (!mg_is_userlist_and_tree_combined ())
2878 mg_userlist_showhide (current_sess, FALSE); /* hide */
2882 /* compare two tabs (for tab sorting function) */
2884 static int
2885 mg_tabs_compare (session *a, session *b)
2887 /* server tabs always go first */
2888 if (a->type == SESS_SERVER)
2889 return -1;
2891 /* then channels */
2892 if (a->type == SESS_CHANNEL && b->type != SESS_CHANNEL)
2893 return -1;
2894 if (a->type != SESS_CHANNEL && b->type == SESS_CHANNEL)
2895 return 1;
2897 return strcasecmp (a->channel, b->channel);
2900 static void
2901 mg_create_tabs (session_gui *gui)
2903 gboolean use_icons = FALSE;
2905 /* if any one of these PNGs exist, the chanview will create
2906 * the extra column for icons. */
2907 if (pix_channel || pix_dialog || pix_server || pix_util)
2908 use_icons = TRUE;
2910 gui->chanview = chanview_new (prefs.tab_layout, prefs.truncchans,
2911 prefs.tab_sort, use_icons,
2912 prefs.style_namelistgad ? input_style : NULL);
2913 chanview_set_callbacks (gui->chanview, mg_switch_tab_cb, mg_xbutton_cb,
2914 mg_tab_contextmenu_cb, (void *)mg_tabs_compare);
2915 mg_place_userlist_and_chanview (gui);
2918 static gboolean
2919 mg_tabwin_focus_cb (GtkWindow * win, GdkEventFocus *event, gpointer userdata)
2921 current_sess = current_tab;
2922 if (current_sess)
2924 gtk_xtext_check_marker_visibility (GTK_XTEXT (current_sess->gui->xtext));
2925 plugin_emit_dummy_print (current_sess, "Focus Window");
2927 #ifdef USE_XLIB
2928 unflash_window (GTK_WIDGET (win));
2929 #endif
2930 return FALSE;
2933 static gboolean
2934 mg_topwin_focus_cb (GtkWindow * win, GdkEventFocus *event, session *sess)
2936 current_sess = sess;
2937 if (!sess->server->server_session)
2938 sess->server->server_session = sess;
2939 gtk_xtext_check_marker_visibility(GTK_XTEXT (current_sess->gui->xtext));
2940 #ifdef USE_XLIB
2941 unflash_window (GTK_WIDGET (win));
2942 #endif
2943 plugin_emit_dummy_print (sess, "Focus Window");
2944 return FALSE;
2947 static void
2948 mg_create_menu (session_gui *gui, GtkWidget *table, int away_state)
2950 GtkAccelGroup *accel_group;
2952 accel_group = gtk_accel_group_new ();
2953 gtk_window_add_accel_group (GTK_WINDOW (gtk_widget_get_toplevel (table)),
2954 accel_group);
2955 g_object_unref (accel_group);
2957 gui->menu = menu_create_main (accel_group, TRUE, away_state, !gui->is_tab,
2958 gui->menu_item);
2959 gtk_table_attach (GTK_TABLE (table), gui->menu, 0, 3, 0, 1,
2960 GTK_EXPAND | GTK_FILL, GTK_SHRINK | GTK_FILL, 0, 0);
2963 static void
2964 mg_create_irctab (session *sess, GtkWidget *table)
2966 GtkWidget *vbox;
2967 session_gui *gui = sess->gui;
2969 vbox = gtk_vbox_new (FALSE, 0);
2970 gtk_table_attach (GTK_TABLE (table), vbox, 1, 2, 2, 3,
2971 GTK_EXPAND | GTK_FILL, GTK_EXPAND | GTK_FILL, 0, 0);
2972 mg_create_center (sess, gui, vbox);
2975 static void
2976 mg_create_topwindow (session *sess)
2978 GtkWidget *win;
2979 GtkWidget *table;
2981 if (sess->type == SESS_DIALOG)
2982 win = gtkutil_window_new ("XChat", NULL,
2983 prefs.dialog_width, prefs.dialog_height, 0);
2984 else
2985 win = gtkutil_window_new ("XChat", NULL,
2986 prefs.mainwindow_width,
2987 prefs.mainwindow_height, 0);
2988 sess->gui->window = win;
2989 gtk_container_set_border_width (GTK_CONTAINER (win), GUI_BORDER);
2991 g_signal_connect (G_OBJECT (win), "focus_in_event",
2992 G_CALLBACK (mg_topwin_focus_cb), sess);
2993 g_signal_connect (G_OBJECT (win), "destroy",
2994 G_CALLBACK (mg_topdestroy_cb), sess);
2995 g_signal_connect (G_OBJECT (win), "configure_event",
2996 G_CALLBACK (mg_configure_cb), sess);
2998 palette_alloc (win);
3000 table = gtk_table_new (4, 3, FALSE);
3001 /* spacing under the menubar */
3002 gtk_table_set_row_spacing (GTK_TABLE (table), 0, GUI_SPACING);
3003 /* left and right borders */
3004 gtk_table_set_col_spacing (GTK_TABLE (table), 0, 1);
3005 gtk_table_set_col_spacing (GTK_TABLE (table), 1, 1);
3006 gtk_container_add (GTK_CONTAINER (win), table);
3008 mg_create_irctab (sess, table);
3009 mg_create_menu (sess->gui, table, sess->server->is_away);
3011 if (sess->res->buffer == NULL)
3013 sess->res->buffer = gtk_xtext_buffer_new (GTK_XTEXT (sess->gui->xtext));
3014 gtk_xtext_buffer_show (GTK_XTEXT (sess->gui->xtext), sess->res->buffer, TRUE);
3015 gtk_xtext_set_time_stamp (sess->res->buffer, prefs.timestamp);
3016 sess->res->user_model = userlist_create_model ();
3019 userlist_show (sess);
3021 gtk_widget_show_all (table);
3023 if (prefs.hidemenu)
3024 gtk_widget_hide (sess->gui->menu);
3026 if (!prefs.topicbar)
3027 gtk_widget_hide (sess->gui->topic_bar);
3029 if (!prefs.userlistbuttons)
3030 gtk_widget_hide (sess->gui->button_box);
3032 if (prefs.gui_tweaks & 2)
3033 gtk_widget_hide (sess->gui->nick_box);
3035 mg_decide_userlist (sess, FALSE);
3037 if (sess->type == SESS_DIALOG)
3039 /* hide the chan-mode buttons */
3040 gtk_widget_hide (sess->gui->topicbutton_box);
3041 } else
3043 gtk_widget_hide (sess->gui->dialogbutton_box);
3045 if (!prefs.chanmodebuttons)
3046 gtk_widget_hide (sess->gui->topicbutton_box);
3049 mg_place_userlist_and_chanview (sess->gui);
3051 gtk_widget_show (win);
3054 static gboolean
3055 mg_tabwindow_de_cb (GtkWidget *widget, GdkEvent *event, gpointer user_data)
3057 GSList *list;
3058 session *sess;
3060 if ((prefs.gui_tray_flags & 1) && tray_toggle_visibility (FALSE))
3061 return TRUE;
3063 /* check for remaining toplevel windows */
3064 list = sess_list;
3065 while (list)
3067 sess = list->data;
3068 if (!sess->gui->is_tab)
3069 return FALSE;
3070 list = list->next;
3073 mg_open_quit_dialog (TRUE);
3074 return TRUE;
3077 static void
3078 mg_create_tabwindow (session *sess)
3080 GtkWidget *win;
3081 GtkWidget *table;
3083 win = gtkutil_window_new ("XChat", NULL, prefs.mainwindow_width,
3084 prefs.mainwindow_height, 0);
3085 sess->gui->window = win;
3086 gtk_window_move (GTK_WINDOW (win), prefs.mainwindow_left,
3087 prefs.mainwindow_top);
3088 if (prefs.gui_win_state)
3089 gtk_window_maximize (GTK_WINDOW (win));
3090 gtk_container_set_border_width (GTK_CONTAINER (win), GUI_BORDER);
3092 g_signal_connect (G_OBJECT (win), "delete_event",
3093 G_CALLBACK (mg_tabwindow_de_cb), 0);
3094 g_signal_connect (G_OBJECT (win), "destroy",
3095 G_CALLBACK (mg_tabwindow_kill_cb), 0);
3096 g_signal_connect (G_OBJECT (win), "focus_in_event",
3097 G_CALLBACK (mg_tabwin_focus_cb), NULL);
3098 g_signal_connect (G_OBJECT (win), "configure_event",
3099 G_CALLBACK (mg_configure_cb), NULL);
3100 g_signal_connect (G_OBJECT (win), "window_state_event",
3101 G_CALLBACK (mg_windowstate_cb), NULL);
3103 palette_alloc (win);
3105 sess->gui->main_table = table = gtk_table_new (4, 3, FALSE);
3106 /* spacing under the menubar */
3107 gtk_table_set_row_spacing (GTK_TABLE (table), 0, GUI_SPACING);
3108 /* left and right borders */
3109 gtk_table_set_col_spacing (GTK_TABLE (table), 0, 1);
3110 gtk_table_set_col_spacing (GTK_TABLE (table), 1, 1);
3111 gtk_container_add (GTK_CONTAINER (win), table);
3113 mg_create_irctab (sess, table);
3114 mg_create_tabs (sess->gui);
3115 mg_create_menu (sess->gui, table, sess->server->is_away);
3117 mg_focus (sess);
3119 gtk_widget_show_all (table);
3121 if (prefs.hidemenu)
3122 gtk_widget_hide (sess->gui->menu);
3124 mg_decide_userlist (sess, FALSE);
3126 if (!prefs.topicbar)
3127 gtk_widget_hide (sess->gui->topic_bar);
3129 if (!prefs.chanmodebuttons)
3130 gtk_widget_hide (sess->gui->topicbutton_box);
3132 if (!prefs.userlistbuttons)
3133 gtk_widget_hide (sess->gui->button_box);
3135 if (prefs.gui_tweaks & 2)
3136 gtk_widget_hide (sess->gui->nick_box);
3138 mg_place_userlist_and_chanview (sess->gui);
3140 gtk_widget_show (win);
3143 void
3144 mg_apply_setup (void)
3146 GSList *list = sess_list;
3147 session *sess;
3148 int done_main = FALSE;
3150 mg_create_tab_colors ();
3152 while (list)
3154 sess = list->data;
3155 gtk_xtext_set_time_stamp (sess->res->buffer, prefs.timestamp);
3156 ((xtext_buffer *)sess->res->buffer)->needs_recalc = TRUE;
3157 if (!sess->gui->is_tab || !done_main)
3158 mg_place_userlist_and_chanview (sess->gui);
3159 if (sess->gui->is_tab)
3160 done_main = TRUE;
3161 list = list->next;
3165 static chan *
3166 mg_add_generic_tab (char *name, char *title, void *family, GtkWidget *box)
3168 chan *ch;
3170 gtk_notebook_append_page (GTK_NOTEBOOK (mg_gui->note_book), box, NULL);
3171 gtk_widget_show (box);
3173 ch = chanview_add (mg_gui->chanview, name, NULL, box, TRUE, TAG_UTIL, pix_util);
3174 chan_set_color (ch, plain_list);
3175 /* FIXME: memory leak */
3176 g_object_set_data (G_OBJECT (box), "title", strdup (title));
3177 g_object_set_data (G_OBJECT (box), "ch", ch);
3179 if (prefs.newtabstofront)
3180 chan_focus (ch);
3182 return ch;
3185 void
3186 fe_buttons_update (session *sess)
3188 session_gui *gui = sess->gui;
3190 gtk_widget_destroy (gui->button_box);
3191 gui->button_box = mg_create_userlistbuttons (gui->button_box_parent);
3193 if (prefs.userlistbuttons)
3194 gtk_widget_show (sess->gui->button_box);
3195 else
3196 gtk_widget_hide (sess->gui->button_box);
3199 void
3200 fe_clear_channel (session *sess)
3202 char tbuf[CHANLEN+6];
3203 session_gui *gui = sess->gui;
3205 if (sess->gui->is_tab)
3207 if (sess->waitchannel[0])
3209 if (prefs.truncchans > 2 && g_utf8_strlen (sess->waitchannel, -1) > prefs.truncchans)
3211 /* truncate long channel names */
3212 tbuf[0] = '(';
3213 strcpy (tbuf + 1, sess->waitchannel);
3214 g_utf8_offset_to_pointer(tbuf, prefs.truncchans)[0] = 0;
3215 strcat (tbuf, "..)");
3216 } else
3218 sprintf (tbuf, "(%s)", sess->waitchannel);
3221 else
3222 strcpy (tbuf, _("<none>"));
3223 chan_rename (sess->res->tab, tbuf, prefs.truncchans);
3226 if (!sess->gui->is_tab || sess == current_tab)
3228 gtk_entry_set_text (GTK_ENTRY (gui->topic_entry), "");
3230 if (gui->op_xpm)
3232 gtk_widget_destroy (gui->op_xpm);
3233 gui->op_xpm = 0;
3235 } else
3237 if (sess->res->topic_text)
3239 free (sess->res->topic_text);
3240 sess->res->topic_text = NULL;
3245 void
3246 fe_set_nonchannel (session *sess, int state)
3250 void
3251 fe_dlgbuttons_update (session *sess)
3253 GtkWidget *box;
3254 session_gui *gui = sess->gui;
3256 gtk_widget_destroy (gui->dialogbutton_box);
3258 gui->dialogbutton_box = box = gtk_hbox_new (0, 0);
3259 gtk_box_pack_start (GTK_BOX (gui->topic_bar), box, 0, 0, 0);
3260 gtk_box_reorder_child (GTK_BOX (gui->topic_bar), box, 3);
3261 mg_create_dialogbuttons (box);
3263 gtk_widget_show_all (box);
3265 if (current_tab && current_tab->type != SESS_DIALOG)
3266 gtk_widget_hide (current_tab->gui->dialogbutton_box);
3269 void
3270 fe_update_mode_buttons (session *sess, char mode, char sign)
3272 int state, i;
3274 if (sign == '+')
3275 state = TRUE;
3276 else
3277 state = FALSE;
3279 for (i = 0; i < NUM_FLAG_WIDS - 1; i++)
3281 if (chan_flags[i] == mode)
3283 if (!sess->gui->is_tab || sess == current_tab)
3285 ignore_chanmode = TRUE;
3286 if (GTK_TOGGLE_BUTTON (sess->gui->flag_wid[i])->active != state)
3287 gtk_toggle_button_set_active (
3288 GTK_TOGGLE_BUTTON (sess->gui->flag_wid[i]), state);
3289 ignore_chanmode = FALSE;
3290 } else
3292 sess->res->flag_wid_state[i] = state;
3294 return;
3299 void
3300 fe_set_nick (server *serv, char *newnick)
3302 GSList *list = sess_list;
3303 session *sess;
3305 while (list)
3307 sess = list->data;
3308 if (sess->server == serv)
3310 if (current_tab == sess || !sess->gui->is_tab)
3311 gtk_button_set_label (GTK_BUTTON (sess->gui->nick_label), newnick);
3313 list = list->next;
3317 void
3318 fe_set_away (server *serv)
3320 GSList *list = sess_list;
3321 session *sess;
3323 while (list)
3325 sess = list->data;
3326 if (sess->server == serv)
3328 if (!sess->gui->is_tab || sess == current_tab)
3330 GTK_CHECK_MENU_ITEM (sess->gui->menu_item[MENU_ID_AWAY])->active = serv->is_away;
3331 /* gray out my nickname */
3332 mg_set_myself_away (sess->gui, serv->is_away);
3335 list = list->next;
3339 void
3340 fe_set_channel (session *sess)
3342 if (sess->res->tab != NULL)
3343 chan_rename (sess->res->tab, sess->channel, prefs.truncchans);
3346 void
3347 mg_changui_new (session *sess, restore_gui *res, int tab, int focus)
3349 int first_run = FALSE;
3350 session_gui *gui;
3351 struct User *user = NULL;
3353 if (!res)
3355 res = malloc (sizeof (restore_gui));
3356 memset (res, 0, sizeof (restore_gui));
3359 sess->res = res;
3361 if (!sess->server->front_session)
3362 sess->server->front_session = sess;
3364 if (!is_channel (sess->server, sess->channel))
3365 user = userlist_find_global (sess->server, sess->channel);
3367 if (!tab)
3369 gui = malloc (sizeof (session_gui));
3370 memset (gui, 0, sizeof (session_gui));
3371 gui->is_tab = FALSE;
3372 sess->gui = gui;
3373 mg_create_topwindow (sess);
3374 fe_set_title (sess);
3375 if (user && user->hostname)
3376 set_topic (sess, user->hostname, user->hostname);
3377 return;
3380 if (mg_gui == NULL)
3382 first_run = TRUE;
3383 gui = &static_mg_gui;
3384 memset (gui, 0, sizeof (session_gui));
3385 gui->is_tab = TRUE;
3386 sess->gui = gui;
3387 mg_create_tabwindow (sess);
3388 mg_gui = gui;
3389 parent_window = gui->window;
3390 } else
3392 sess->gui = gui = mg_gui;
3393 gui->is_tab = TRUE;
3396 if (user && user->hostname)
3397 set_topic (sess, user->hostname, user->hostname);
3399 mg_add_chan (sess);
3401 if (first_run || (prefs.newtabstofront == FOCUS_NEW_ONLY_ASKED && focus)
3402 || prefs.newtabstofront == FOCUS_NEW_ALL )
3403 chan_focus (res->tab);
3406 GtkWidget *
3407 mg_create_generic_tab (char *name, char *title, int force_toplevel,
3408 int link_buttons,
3409 void *close_callback, void *userdata,
3410 int width, int height, GtkWidget **vbox_ret,
3411 void *family)
3413 GtkWidget *vbox, *win;
3415 if (prefs.tab_pos == POS_HIDDEN && prefs.windows_as_tabs)
3416 prefs.windows_as_tabs = 0;
3418 if (force_toplevel || !prefs.windows_as_tabs)
3420 win = gtkutil_window_new (title, name, width, height, 3);
3421 vbox = gtk_vbox_new (0, 0);
3422 *vbox_ret = vbox;
3423 gtk_container_add (GTK_CONTAINER (win), vbox);
3424 gtk_widget_show (vbox);
3425 if (close_callback)
3426 g_signal_connect (G_OBJECT (win), "destroy",
3427 G_CALLBACK (close_callback), userdata);
3428 return win;
3431 vbox = gtk_vbox_new (0, 2);
3432 g_object_set_data (G_OBJECT (vbox), "w", GINT_TO_POINTER (width));
3433 g_object_set_data (G_OBJECT (vbox), "h", GINT_TO_POINTER (height));
3434 gtk_container_set_border_width (GTK_CONTAINER (vbox), 3);
3435 *vbox_ret = vbox;
3437 if (close_callback)
3438 g_signal_connect (G_OBJECT (vbox), "destroy",
3439 G_CALLBACK (close_callback), userdata);
3441 mg_add_generic_tab (name, title, family, vbox);
3443 /* if (link_buttons)
3445 hbox = gtk_hbox_new (FALSE, 0);
3446 gtk_box_pack_start (GTK_BOX (vbox), hbox, 0, 0, 0);
3447 mg_create_link_buttons (hbox, ch);
3448 gtk_widget_show (hbox);
3451 return vbox;
3454 void
3455 mg_move_tab (session *sess, int delta)
3457 if (sess->gui->is_tab)
3458 chan_move (sess->res->tab, delta);
3461 void
3462 mg_move_tab_family (session *sess, int delta)
3464 if (sess->gui->is_tab)
3465 chan_move_family (sess->res->tab, delta);
3468 void
3469 mg_set_title (GtkWidget *vbox, char *title) /* for non-irc tab/window only */
3471 char *old;
3473 old = g_object_get_data (G_OBJECT (vbox), "title");
3474 if (old)
3476 g_object_set_data (G_OBJECT (vbox), "title", strdup (title));
3477 free (old);
3478 } else
3480 gtk_window_set_title (GTK_WINDOW (vbox), title);
3484 void
3485 fe_server_callback (server *serv)
3487 joind_close (serv);
3489 if (serv->gui->chanlist_window)
3490 mg_close_gen (NULL, serv->gui->chanlist_window);
3492 if (serv->gui->rawlog_window)
3493 mg_close_gen (NULL, serv->gui->rawlog_window);
3495 free (serv->gui);
3498 /* called when a session is being killed */
3500 void
3501 fe_session_callback (session *sess)
3503 if (sess->res->banlist_window)
3504 mg_close_gen (NULL, sess->res->banlist_window);
3506 if (sess->res->input_text)
3507 free (sess->res->input_text);
3509 if (sess->res->topic_text)
3510 free (sess->res->topic_text);
3512 if (sess->res->limit_text)
3513 free (sess->res->limit_text);
3515 if (sess->res->key_text)
3516 free (sess->res->key_text);
3518 if (sess->res->queue_text)
3519 free (sess->res->queue_text);
3520 if (sess->res->queue_tip)
3521 free (sess->res->queue_tip);
3523 if (sess->res->lag_text)
3524 free (sess->res->lag_text);
3525 if (sess->res->lag_tip)
3526 free (sess->res->lag_tip);
3528 if (sess->gui->bartag)
3529 fe_timeout_remove (sess->gui->bartag);
3531 if (sess->gui != &static_mg_gui)
3532 free (sess->gui);
3533 free (sess->res);
3536 /* ===== DRAG AND DROP STUFF ===== */
3538 static gboolean
3539 is_child_of (GtkWidget *widget, GtkWidget *parent)
3541 while (widget)
3543 if (widget->parent == parent)
3544 return TRUE;
3545 widget = widget->parent;
3547 return FALSE;
3550 static void
3551 mg_handle_drop (GtkWidget *widget, int y, int *pos, int *other_pos)
3553 int height;
3554 session_gui *gui = current_sess->gui;
3556 gdk_drawable_get_size (widget->window, NULL, &height);
3558 if (y < height / 2)
3560 if (is_child_of (widget, gui->vpane_left))
3561 *pos = 1; /* top left */
3562 else
3563 *pos = 3; /* top right */
3565 else
3567 if (is_child_of (widget, gui->vpane_left))
3568 *pos = 2; /* bottom left */
3569 else
3570 *pos = 4; /* bottom right */
3573 /* both in the same pos? must move one */
3574 if (*pos == *other_pos)
3576 switch (*other_pos)
3578 case 1:
3579 *other_pos = 2;
3580 break;
3581 case 2:
3582 *other_pos = 1;
3583 break;
3584 case 3:
3585 *other_pos = 4;
3586 break;
3587 case 4:
3588 *other_pos = 3;
3589 break;
3593 mg_place_userlist_and_chanview (gui);
3596 static gboolean
3597 mg_is_gui_target (GdkDragContext *context)
3599 char *target_name;
3601 if (!context || !context->targets || !context->targets->data)
3602 return FALSE;
3604 target_name = gdk_atom_name (context->targets->data);
3605 if (target_name)
3607 /* if it's not XCHAT_CHANVIEW or XCHAT_USERLIST */
3608 /* we should ignore it. */
3609 if (target_name[0] != 'X')
3611 g_free (target_name);
3612 return FALSE;
3614 g_free (target_name);
3617 return TRUE;
3620 /* this begin callback just creates an nice of the source */
3622 gboolean
3623 mg_drag_begin_cb (GtkWidget *widget, GdkDragContext *context, gpointer userdata)
3625 int width, height;
3626 GdkColormap *cmap;
3627 GdkPixbuf *pix, *pix2;
3629 /* ignore file drops */
3630 if (!mg_is_gui_target (context))
3631 return FALSE;
3633 cmap = gtk_widget_get_colormap (widget);
3634 gdk_drawable_get_size (widget->window, &width, &height);
3636 pix = gdk_pixbuf_get_from_drawable (NULL, widget->window, cmap, 0, 0, 0, 0, width, height);
3637 pix2 = gdk_pixbuf_scale_simple (pix, width * 4 / 5, height / 2, GDK_INTERP_HYPER);
3638 g_object_unref (pix);
3640 gtk_drag_set_icon_pixbuf (context, pix2, 0, 0);
3641 g_object_set_data (G_OBJECT (widget), "ico", pix2);
3643 return TRUE;
3646 void
3647 mg_drag_end_cb (GtkWidget *widget, GdkDragContext *context, gpointer userdata)
3649 /* ignore file drops */
3650 if (!mg_is_gui_target (context))
3651 return;
3653 g_object_unref (g_object_get_data (G_OBJECT (widget), "ico"));
3656 /* drop complete */
3658 gboolean
3659 mg_drag_drop_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, gpointer user_data)
3661 /* ignore file drops */
3662 if (!mg_is_gui_target (context))
3663 return FALSE;
3665 switch (context->action)
3667 case GDK_ACTION_MOVE:
3668 /* from userlist */
3669 mg_handle_drop (widget, y, &prefs.gui_ulist_pos, &prefs.tab_pos);
3670 break;
3671 case GDK_ACTION_COPY:
3672 /* from tree - we use GDK_ACTION_COPY for the tree */
3673 mg_handle_drop (widget, y, &prefs.tab_pos, &prefs.gui_ulist_pos);
3674 break;
3675 default:
3676 return FALSE;
3679 return TRUE;
3682 /* draw highlight rectangle in the destination */
3684 gboolean
3685 mg_drag_motion_cb (GtkWidget *widget, GdkDragContext *context, int x, int y, guint time, gpointer scbar)
3687 GdkGC *gc;
3688 GdkColor col;
3689 GdkGCValues val;
3690 int half, width, height;
3691 int ox, oy;
3692 GtkPaned *paned;
3693 GdkDrawable *draw;
3695 /* ignore file drops */
3696 if (!mg_is_gui_target (context))
3697 return FALSE;
3699 if (scbar) /* scrollbar */
3701 ox = widget->allocation.x;
3702 oy = widget->allocation.y;
3703 width = widget->allocation.width;
3704 height = widget->allocation.height;
3705 draw = widget->window;
3707 else
3709 ox = oy = 0;
3710 gdk_drawable_get_size (widget->window, &width, &height);
3711 draw = widget->window;
3714 val.subwindow_mode = GDK_INCLUDE_INFERIORS;
3715 val.graphics_exposures = 0;
3716 val.function = GDK_XOR;
3718 gc = gdk_gc_new_with_values (widget->window, &val, GDK_GC_EXPOSURES | GDK_GC_SUBWINDOW | GDK_GC_FUNCTION);
3719 col.red = rand() % 0xffff;
3720 col.green = rand() % 0xffff;
3721 col.blue = rand() % 0xffff;
3722 gdk_colormap_alloc_color (gtk_widget_get_colormap (widget), &col, FALSE, TRUE);
3723 gdk_gc_set_foreground (gc, &col);
3725 half = height / 2;
3727 #if 0
3728 /* are both tree/userlist on the same side? */
3729 paned = (GtkPaned *)widget->parent->parent;
3730 if (paned->child1 != NULL && paned->child2 != NULL)
3732 gdk_draw_rectangle (draw, gc, 0, 1, 2, width - 3, height - 4);
3733 gdk_draw_rectangle (draw, gc, 0, 0, 1, width - 1, height - 2);
3734 g_object_unref (gc);
3735 return TRUE;
3737 #endif
3739 if (y < half)
3741 gdk_draw_rectangle (draw, gc, FALSE, 1 + ox, 2 + oy, width - 3, half - 4);
3742 gdk_draw_rectangle (draw, gc, FALSE, 0 + ox, 1 + oy, width - 1, half - 2);
3743 gtk_widget_queue_draw_area (widget, ox, half + oy, width, height - half);
3745 else
3747 gdk_draw_rectangle (draw, gc, FALSE, 0 + ox, half + 1 + oy, width - 1, half - 2);
3748 gdk_draw_rectangle (draw, gc, FALSE, 1 + ox, half + 2 + oy, width - 3, half - 4);
3749 gtk_widget_queue_draw_area (widget, ox, oy, width, half);
3752 g_object_unref (gc);
3754 return TRUE;