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