ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/kgsueme/chat.pl
Revision: 1.11
Committed: Wed Jun 2 14:13:56 2004 UTC (20 years ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.10: +3 -2 lines
Log Message:
*** empty log message ***

File Contents

# User Rev Content
1 root 1.4 use utf8;
2    
3 pcg 1.1 package chat;
4    
5 root 1.4 # waaay cool widget. well... maybe at one point in the future
6    
7 pcg 1.1 use Gtk2;
8    
9     use Glib::Object::Subclass
10     Gtk2::VBox,
11     signals => {
12     command => {
13     flags => [qw/run-first/],
14     return_type => undef, # void return
15     param_types => [Glib::Scalar, Glib::Scalar],
16     },
17     };
18    
19 root 1.4 sub INIT_INSTANCE {
20     my $self = shift;
21 pcg 1.1
22 root 1.4 my $tagtable = new Gtk2::TextTagTable;
23    
24     {
25     my @tags = (
26     [default => { foreground => "black" }],
27     [node => { foreground => "#0000b0", event => 1 }],
28     [move => { foreground => "#0000b0", event => 1 }],
29 root 1.11 #[user => { foreground => "#0000b0", wrap_mode => "none", event => 1 }],
30     [user => { foreground => "#0000b0", event => 1 }],
31 root 1.4 [coord => { foreground => "#0000b0", event => 1 }],
32     [score => { foreground => "#0000b0", event => 1 }],
33     [error => { foreground => "#ff0000", event => 1 }],
34 root 1.9 [leader => { weight => 800, pixels_above_lines => 6 }],
35 root 1.4 [header => { weight => 800, pixels_above_lines => 6 }],
36     [undo => { foreground => "#ffff00", background => "#ff0000", weight => 800, pixels_above_lines => 6 }],
37     [challenge => { weight => 800, pixels_above_lines => 6, background => "#ffffb0" }],
38     [description => { weight => 800, foreground => "blue" }],
39     [infoblock => { weight => 700, foreground => "blue" }],
40     );
41    
42     for (@tags) {
43     my ($k, $v) = @$_;
44     my $tag = new Gtk2::TextTag $k;
45     if (delete $v->{event}) {
46     ###
47     }
48     $tag->set (%$v);
49     $tagtable->add ($tag);
50 pcg 1.1 }
51     }
52    
53 root 1.4 $self->{tagtable} = $tagtable;
54 pcg 1.1
55 pcg 1.3 $self->signal_connect (destroy => sub {
56     remove Glib::Source delete $self->{idle} if $self->{idle};
57     %{$_[0]} = ();
58     });
59    
60 root 1.4 $self->{buffer} = new Gtk2::TextBuffer $self->{tagtable};
61 pcg 1.1
62     $self->{widget} = new Gtk2::ScrolledWindow;
63 root 1.11 $self->{widget}->set_policy ("automatic", "always");
64 pcg 1.1 $self->pack_start ($self->{widget}, 1, 1, 0);
65    
66     $self->{widget}->add ($self->{view} = new_with_buffer Gtk2::TextView $self->{buffer});
67 root 1.8 $self->{view}->set_wrap_mode ("word-char");
68 pcg 1.1 $self->{view}->set_cursor_visible (0);
69    
70     $self->{view}->set_editable (0);
71    
72     $self->{view}->signal_connect (motion_notify_event => sub {
73     my ($widget, $event) = @_;
74    
75     my $window = $widget->get_window ("text");
76     if ($event->window == $window) {
77     my ($win, $x, $y, $mask) = $window->get_pointer;
78     # warn "TAG EVENT @_ ($window, $win, $x, $y, $mask)\n";
79     #gtk_text_view_window_to_buffer_coords (text_view,
80     # GTK_TEXT_WINDOW_TEXT,
81     # text_view->drag_start_x,
82     # text_view->drag_start_y,
83     # &buffer_x,
84     # &buffer_y);
85     #
86     # gtk_text_layout_get_iter_at_pixel (text_view->layout,
87     # &iter,
88     # buffer_x, buffer_y);
89     #
90     # gtk_text_view_start_selection_dnd (text_view, &iter, event);
91     # return TRUE;
92     }
93     0;
94     });
95    
96     $self->pack_start (($self->{entry} = new Gtk2::Entry), 0, 1, 0);
97    
98 root 1.4 $self->{entry}->signal_connect (activate => sub {
99 pcg 1.1 my ($entry) = @_;
100     my $text = $entry->get_text;
101     $entry->set_text("");
102    
103     my ($cmd, $arg);
104    
105     if ($text =~ /^\/(\S+)\s*(.*)$/) {
106     ($cmd, $arg) = ($1, $2);
107     } else {
108 pcg 1.2 ($cmd, $arg) = ("say", $text);
109 pcg 1.1 }
110    
111     $self->signal_emit (command => $cmd, $arg);
112     });
113    
114 root 1.4 #$self->{end} = $self->{buffer}->create_mark (undef, $self->{buffer}->get_end_iter, 0);#d##todo# use this one for gtk-1.050+
115     $self->{end} = $self->{buffer}->create_mark (++$USELESSNAME, $self->{buffer}->get_end_iter, 0); # workaround for gtk-perl bug
116 pcg 1.1
117     $self->set_end;
118     }
119    
120     sub do_command {
121     my ($self, $cmd, $arg, %arg) = @_;
122     }
123    
124     sub set_end {
125     my ($self) = @_;
126    
127     # this is probably also a hack...
128     $self->{idle} ||= add Glib::Idle sub {
129     $self->{view}->scroll_to_iter ($self->{buffer}->get_end_iter, 0, 0, 0, 0)
130     if $self->{view};
131     delete $self->{idle};
132     };
133     }
134    
135     sub at_end {
136     my ($self) = @_;
137    
138     # this is, maybe, a bad hack :/
139     my $adj = $self->{widget}->get_vadjustment;
140     $adj->value + $adj->page_size >= $adj->upper - 0.5;
141     }
142    
143     sub append_text {
144     my ($self, $text) = @_;
145    
146 root 1.4 $self->_append_text ($self->{end}, $text);
147     }
148    
149     sub _append_text {
150     my ($self, $mark, $text) = @_;
151    
152 pcg 1.1 my $at_end = $self->at_end;
153    
154     $text = "<default>$text</default>";
155    
156 root 1.4 my @tag;
157 pcg 1.1 # pseudo-simplistic-xml-parser
158     for (;;) {
159     $text =~ /\G<([^>]+)>/gc or last;
160     my $tag = $1;
161     if ($tag =~ s/^\///) {
162     pop @tag;
163     } else {
164     push @tag, $tag;
165     }
166    
167     $text =~ /\G([^<]*)/gc or last;
168 root 1.4 $self->{buffer}->insert_with_tags_by_name ($self->{buffer}->get_iter_at_mark ($mark), util::xmlto $1, @tag)
169 pcg 1.1 if length $1;
170     }
171    
172     $self->set_end if $at_end;
173     }
174    
175     sub set_text {
176     my ($self, $text) = @_;
177    
178     my $at_end = $self->at_end;
179    
180     $self->{buffer}->set_text ("");
181     $self->append_text ($text);
182    
183     $self->set_end if $at_end;
184 root 1.4 }
185    
186     sub new_eventtag {
187     my ($self, $cb) = @_;
188    
189     my $tag = new Gtk2::TextTag;
190     $tag->signal_connect (event => $cb);
191     $self->{tagtable}->add ($tag);
192    
193     $tag
194     }
195    
196     # create a new "subbuffer"
197     sub new_inlay {
198     my ($self) = @_;
199    
200     my $end = $self->{buffer}->get_end_iter;
201    
202     my $self = bless {
203     buffer => $self->{buffer},
204     parent => $self,
205     }, superchat::inlay;
206    
207     # $USELESSNAME is a Gtk-perl < 1.042 workaround
208     $self->{l} = $self->{buffer}->create_mark (++$USELESSNAME, $end, 1);
209     $self->{buffer}->insert ($end, "\x{200d}");
210     $self->{r} = $self->{buffer}->create_mark (++$USELESSNAME, $self->{buffer}->get_iter_at_mark ($self->{l}), 0);
211    
212     Scalar::Util::weaken $self->{buffer};
213     Scalar::Util::weaken $self->{parent};
214     $self;
215     }
216    
217     sub new_switchable_inlay {
218     my ($self, $header, $cb, $visible) = @_;
219    
220     my $inlay;
221    
222     my $tag = $self->new_eventtag (sub {
223     my ($tag, $view, $event, $iter) = @_;
224    
225     if ($event->type eq "button-press") {
226     $inlay->set_visible (!$inlay->{visible});
227     return 1;
228     }
229    
230     0;
231     });
232    
233     $tag->set (background => "#e0e0ff");
234    
235     $inlay = $self->new_inlay;
236    
237     $inlay->{visible} = $visible;
238     $inlay->{header} = $header;
239     $inlay->{tag} = $tag;
240     $inlay->{cb} = $cb;
241    
242     Scalar::Util::weaken $inlay->{tag};
243    
244     $inlay->refresh;
245    
246     $inlay;
247     }
248    
249     package superchat::inlay;
250    
251     sub liter { $_[0]{buffer}->get_iter_at_mark ($_[0]{l}) }
252     sub riter { $_[0]{buffer}->get_iter_at_mark ($_[0]{r}) }
253    
254     sub clear {
255     my ($self) = @_;
256     $self->{buffer}->delete ($self->liter, $self->riter);
257     }
258    
259     sub append_text {
260     my ($self, $text) = @_;
261    
262     $self->{parent}->_append_text ($self->{r}, $text);
263     }
264    
265     sub append_widget {
266     my ($self, $widget) = @_;
267    
268     $widget->show_all;
269    
270     my $anchor = $self->{buffer}->create_child_anchor ($self->riter);
271     $self->{parent}{view}->add_child_at_anchor ($widget, $anchor);
272 root 1.10
273     $widget;
274 root 1.4 }
275    
276     sub append_optionmenu {
277     my ($self, $ref, @entry) = @_;
278    
279 root 1.10 $self->append_widget (gtk::optionmenu $ref, @entry);
280 root 1.4 }
281    
282     sub append_button {
283     my ($self, $label, $cb) = @_;
284    
285 root 1.10 $self->append_widget (gtk::button $label, $cb);
286 root 1.4 }
287    
288     sub visible { $_[0]{visible} }
289    
290     sub set_visible {
291     my ($self, $visible) = @_;
292    
293     return if $self->{visible} == $visible;
294     $self->{visible} = $visible;
295    
296     $self->refresh;
297     }
298    
299     sub refresh {
300     my ($self) = @_;
301    
302     $self->clear;
303    
304     my $arrow = $self->{visible} ? "⊟" : "⊞";
305    
306     $self->{buffer}->insert ($self->riter, "\n");
307     $self->{buffer}->insert_with_tags ($self->riter, util::xmlto "$arrow $self->{header}", $self->{tag});
308    
309     return unless $self->{visible};
310    
311     $self->{cb}->($self);
312     }
313    
314     sub destroy {
315     my ($self) = @_;
316    
317     return if !$self->{l} || !$self->{buffer} || $self->{l}->get_deleted;
318    
319     $self->clear if $self->{buffer};
320    
321     delete $self->{parent};
322     delete $self->{buffer};
323     delete $self->{l};
324     delete $self->{r};
325     }
326    
327     sub DESTROY {
328     my $self = shift;
329    
330     $self->{parent}{tagtable}->remove (delete $self->{tag}) if $self->{tag} && $self->{parent};
331     #&destroy;
332 pcg 1.1 }
333    
334     1;
335