ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/kgsueme/chat.pl
Revision: 1.6
Committed: Wed Jun 2 04:55:57 2004 UTC (19 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.5: +1 -1 lines
Log Message:
*** empty log message ***

File Contents

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