ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/kgsueme/superchat.pl
Revision: 1.11
Committed: Sat May 29 06:38:27 2004 UTC (20 years ago) by pcg
Content type: text/plain
Branch: MAIN
Changes since 1.10: +88 -2 lines
Log Message:
*** empty log message ***

File Contents

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