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

# User Rev Content
1 pcg 1.6 use utf8;
2    
3 pcg 1.1 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 pcg 1.2 sub INIT_INSTANCE {
20     my $self = shift;
21 pcg 1.1
22 pcg 1.2 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 pcg 1.12 [undo => { foreground => "#ffff00", background => "#ff0000", weight => 800, pixels_above_lines => 6 }],
34 pcg 1.2 [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 pcg 1.1 }
48     }
49    
50 pcg 1.2 $self->{tagtable} = $tagtable;
51 pcg 1.1
52     $self->signal_connect (destroy => sub {
53     remove Glib::Source delete $self->{idle} if $self->{idle};
54     %{$_[0]} = ();
55     });
56    
57 pcg 1.2 $self->{buffer} = new Gtk2::TextBuffer $self->{tagtable};
58 pcg 1.1
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 pcg 1.2 $self->{entry}->signal_connect (activate => sub {
96 pcg 1.1 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 pcg 1.9 #$self->{end} = $self->{buffer}->create_mark (undef, $self->{buffer}->get_end_iter, 0);#d##todo# use this one for gtk-1.050+
112 pcg 1.8 $self->{end} = $self->{buffer}->create_mark (++$USELESSNAME, $self->{buffer}->get_end_iter, 0); # workaround for gtk-perl bug
113 pcg 1.1
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 pcg 1.2 $self->_append_text ($self->{end}, $text);
144     }
145    
146     sub _append_text {
147     my ($self, $mark, $text) = @_;
148 pcg 1.1
149 pcg 1.5 my $at_end = $self->at_end;
150    
151 pcg 1.1 $text = "<default>$text</default>";
152    
153 pcg 1.2 my @tag;
154 pcg 1.1 # 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 pcg 1.2 $self->{buffer}->insert_with_tags_by_name ($self->{buffer}->get_iter_at_mark ($mark), util::xmlto $1, @tag)
166 pcg 1.1 if length $1;
167     }
168 pcg 1.5
169     $self->set_end if $at_end;
170 pcg 1.1 }
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 pcg 1.2 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 pcg 1.4 $inlay->set_visible (!$inlay->{visible});
223 pcg 1.10 return 1;
224 pcg 1.2 }
225    
226 pcg 1.10 0;
227 pcg 1.2 });
228    
229     $tag->set (background => "#e0e0ff");
230    
231     $inlay = $self->new_inlay;
232    
233 pcg 1.13 $inlay->{visible} = $visible;
234 pcg 1.6 $inlay->{header} = $header;
235 pcg 1.2 $inlay->{tag} = $tag;
236     $inlay->{cb} = $cb;
237    
238 pcg 1.3 Scalar::Util::weaken $inlay->{tag};
239    
240 pcg 1.13 $inlay->refresh;
241 pcg 1.2
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 pcg 1.11 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 pcg 1.4 sub visible { $_[0]{visible} }
335 pcg 1.11
336 pcg 1.4 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 pcg 1.6
350 pcg 1.7 my $arrow = $self->{visible} ? "⊟" : "⊞";
351 pcg 1.6
352     $self->{buffer}->insert ($self->riter, "\n");
353     $self->{buffer}->insert_with_tags ($self->riter, util::xmlto "$arrow $self->{header}", $self->{tag});
354    
355 pcg 1.4 return unless $self->{visible};
356    
357     $self->{cb}->($self);
358     }
359    
360 pcg 1.11 sub destroy {
361 pcg 1.2 my ($self) = @_;
362    
363 pcg 1.11 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 pcg 1.12 my $self = shift;
375    
376     $self->{parent}{tagtable}->remove (delete $self->{tag}) if $self->{tag} && $self->{parent};
377     #&destroy;
378 pcg 1.2 }
379    
380 pcg 1.1 1;
381