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