ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/kgsueme/kgsueme/superchat.pl
Revision: 1.18
Committed: Wed Jun 2 04:39:07 2004 UTC (19 years, 11 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.17: +0 -0 lines
State: FILE REMOVED
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 pcg 1.14 [score => { foreground => "#0000b0", event => 1 }],
32 pcg 1.2 [error => { foreground => "#ff0000", event => 1 }],
33     [header => { weight => 800, pixels_above_lines => 6 }],
34 pcg 1.12 [undo => { foreground => "#ffff00", background => "#ff0000", weight => 800, pixels_above_lines => 6 }],
35 pcg 1.2 [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 pcg 1.1 }
49     }
50    
51 pcg 1.2 $self->{tagtable} = $tagtable;
52 pcg 1.1
53     $self->signal_connect (destroy => sub {
54     remove Glib::Source delete $self->{idle} if $self->{idle};
55     %{$_[0]} = ();
56     });
57    
58 pcg 1.2 $self->{buffer} = new Gtk2::TextBuffer $self->{tagtable};
59 pcg 1.1
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 pcg 1.2 $self->{entry}->signal_connect (activate => sub {
97 pcg 1.1 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 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+
113 pcg 1.8 $self->{end} = $self->{buffer}->create_mark (++$USELESSNAME, $self->{buffer}->get_end_iter, 0); # workaround for gtk-perl bug
114 pcg 1.1
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 pcg 1.2 $self->_append_text ($self->{end}, $text);
145     }
146    
147     sub _append_text {
148     my ($self, $mark, $text) = @_;
149 pcg 1.1
150 pcg 1.5 my $at_end = $self->at_end;
151    
152 pcg 1.1 $text = "<default>$text</default>";
153    
154 pcg 1.2 my @tag;
155 pcg 1.1 # 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 pcg 1.2 $self->{buffer}->insert_with_tags_by_name ($self->{buffer}->get_iter_at_mark ($mark), util::xmlto $1, @tag)
167 pcg 1.1 if length $1;
168     }
169 pcg 1.5
170     $self->set_end if $at_end;
171 pcg 1.1 }
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 pcg 1.2 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 root 1.17 # $USELESSNAME is a Gtk-perl < 1.042 workaround
206 root 1.16 $self->{l} = $self->{buffer}->create_mark (++$USELESSNAME, $end, 1);
207 pcg 1.2 $self->{buffer}->insert ($end, "\x{200d}");
208 root 1.16 $self->{r} = $self->{buffer}->create_mark (++$USELESSNAME, $self->{buffer}->get_iter_at_mark ($self->{l}), 0);
209 pcg 1.2
210     Scalar::Util::weaken $self->{buffer};
211     Scalar::Util::weaken $self->{parent};
212     $self;
213     }
214    
215     sub new_switchable_inlay {
216     my ($self, $header, $cb, $visible) = @_;
217    
218     my $inlay;
219    
220     my $tag = $self->new_eventtag (sub {
221     my ($tag, $view, $event, $iter) = @_;
222    
223     if ($event->type eq "button-press") {
224 pcg 1.4 $inlay->set_visible (!$inlay->{visible});
225 pcg 1.10 return 1;
226 pcg 1.2 }
227    
228 pcg 1.10 0;
229 pcg 1.2 });
230    
231     $tag->set (background => "#e0e0ff");
232    
233     $inlay = $self->new_inlay;
234    
235 pcg 1.13 $inlay->{visible} = $visible;
236 pcg 1.6 $inlay->{header} = $header;
237 pcg 1.2 $inlay->{tag} = $tag;
238     $inlay->{cb} = $cb;
239    
240 pcg 1.3 Scalar::Util::weaken $inlay->{tag};
241    
242 pcg 1.13 $inlay->refresh;
243 pcg 1.2
244     $inlay;
245     }
246    
247     package superchat::inlay;
248    
249     sub liter { $_[0]{buffer}->get_iter_at_mark ($_[0]{l}) }
250     sub riter { $_[0]{buffer}->get_iter_at_mark ($_[0]{r}) }
251    
252     sub clear {
253     my ($self) = @_;
254     $self->{buffer}->delete ($self->liter, $self->riter);
255     }
256    
257     sub append_text {
258     my ($self, $text) = @_;
259    
260     $self->{parent}->_append_text ($self->{r}, $text);
261     }
262    
263 pcg 1.11 sub append_widget {
264     my ($self, $widget) = @_;
265    
266     $widget->show_all;
267    
268     my $anchor = $self->{buffer}->create_child_anchor ($self->riter);
269     $self->{parent}{view}->add_child_at_anchor ($widget, $anchor);
270     }
271    
272     sub append_optionmenu {
273     my ($self, $ref, @entry) = @_;
274    
275     my @vals;
276    
277     my $widget = new Gtk2::OptionMenu;
278     $widget->set (menu => my $menu = new Gtk2::Menu);
279    
280     my $idx = 0;
281    
282     while (@entry >= 2) {
283     my $value = shift @entry;
284     my $label = shift @entry;
285    
286     $menu->append (new Gtk2::MenuItem $label);
287     push @vals, $value;
288    
289     if ($value eq $$ref && $idx >= 0) {
290     $widget->set_history ($idx);
291     $idx = -1e6;
292     }
293     $idx++;
294     }
295    
296     my $cb = shift @entry;
297    
298     $widget->signal_connect (changed => sub {
299     my $new = $vals[$_[0]->get_history];
300    
301     if ($new ne $$ref) {
302     $$ref = $new;
303     $cb->($new) if $cb;
304     }
305     });
306    
307     $self->append_widget ($widget);
308    
309     $widget;
310     }
311    
312     sub append_entry {
313     my ($self, $ref, $width, $cb) = @_;
314    
315     my $widget = new Gtk2::Entry;
316 root 1.15 $widget->set (text => $$ref, width_chars => $width);
317     eval { $widget->set (xalign => 1) }; # workaround für 2.2
318 pcg 1.11 $widget->signal_connect (changed => sub {
319     $$ref = $_[0]->get_text;
320     $cb->($$ref) if $cb;
321     });
322    
323     $self->append_widget ($widget);
324     $widget;
325     }
326    
327     sub append_button {
328     my ($self, $label, $cb) = @_;
329    
330     my $widget = new_with_label Gtk2::Button $label;
331     $widget->signal_connect (clicked => sub { $cb->() if $cb });
332    
333     $self->append_widget ($widget);
334     $widget;
335     }
336    
337 pcg 1.4 sub visible { $_[0]{visible} }
338 pcg 1.11
339 pcg 1.4 sub set_visible {
340     my ($self, $visible) = @_;
341    
342     return if $self->{visible} == $visible;
343     $self->{visible} = $visible;
344    
345     $self->refresh;
346     }
347    
348     sub refresh {
349     my ($self) = @_;
350    
351     $self->clear;
352 pcg 1.6
353 pcg 1.7 my $arrow = $self->{visible} ? "⊟" : "⊞";
354 pcg 1.6
355     $self->{buffer}->insert ($self->riter, "\n");
356     $self->{buffer}->insert_with_tags ($self->riter, util::xmlto "$arrow $self->{header}", $self->{tag});
357    
358 pcg 1.4 return unless $self->{visible};
359    
360     $self->{cb}->($self);
361     }
362    
363 pcg 1.11 sub destroy {
364 pcg 1.2 my ($self) = @_;
365    
366 pcg 1.11 return if !$self->{l} || !$self->{buffer} || $self->{l}->get_deleted;
367    
368     $self->clear if $self->{buffer};
369    
370     delete $self->{parent};
371     delete $self->{buffer};
372     delete $self->{l};
373     delete $self->{r};
374     }
375    
376     sub DESTROY {
377 pcg 1.12 my $self = shift;
378    
379     $self->{parent}{tagtable}->remove (delete $self->{tag}) if $self->{tag} && $self->{parent};
380     #&destroy;
381 pcg 1.2 }
382    
383 pcg 1.1 1;
384