ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/AttrEdit.pm
Revision: 1.47
Committed: Mon Nov 9 15:09:02 2009 UTC (14 years, 6 months ago) by elmex
Branch: MAIN
Changes since 1.46: +1 -1 lines
Log Message:
handle virtual tiles in connect tool a bit more graceful.

File Contents

# Content
1 package GCE::AttrEdit;
2
3 =head1 NAME
4
5 GCE::AttrEdit - an edit wiget for attributes
6
7 =cut
8
9 use Gtk2;
10 use Gtk2::Gdk::Keysyms;
11 use Gtk2::SimpleList;
12
13 use GCE::Util;
14 use GCE::InventoryEditor;
15 use GCE::AttachEditor;
16
17 use Glib::Object::Subclass
18 Gtk2::HPaned;
19
20 use Deliantra;
21 use strict;
22
23 sub save_layout {
24 my ($self) = @_;
25
26 $::CFG->{attr_view_hpane_pos} = $self->get_position;
27 $::CFG->{attr_view_show_use} = $self->{use_btn}->get_active * 1;
28
29 $self->{attach_editor}->save_layout
30 if $self->{attach_editor};
31 }
32
33 sub load_layout {
34 my ($self) = @_;
35
36 $self->set_position ($::CFG->{attr_view_hpane_pos} || 350);
37 $self->{use_btn}->set_active ($::CFG->{attr_view_show_use} * 1);
38
39 $self->{use_al}->remove ($_) for $self->{use_al}->get_children;
40 if ($self->{use_btn}->get_active) {
41 $self->{use_al}->add ($self->{use_lbl});
42 $self->{use_lbl}->show;
43 }
44 }
45
46 sub INIT_INSTANCE {
47 my ($self) = @_;
48
49 my $pb = $self->{arch_pb} = new_arch_pb;
50
51 $self->add (my $topvb = Gtk2::VBox->new);
52 $topvb->pack_start (my $menubar = Gtk2::MenuBar->new, 0, 0, 0);
53 $menubar->append (my $actions_menu = Gtk2::MenuItem->new ('Actions'));
54 my $actmenu = Gtk2::Menu->new;
55 $actmenu->append (my $defitm = Gtk2::MenuItem->new ('reset to defaults'));
56 $defitm->signal_connect (activate => sub {
57 my $ar = $self->{archref};
58 $ar->reset_to_defaults;
59 #XXXAR $self->set_arch ($arch, $self->{change_cb});
60 });
61
62 $actmenu->append (my $attitm = Gtk2::MenuItem->new ('attach'));
63 $attitm->signal_connect (activate => sub {
64 my $ar = $self->{archref};
65 return unless $ar;
66 unless ($self->{attach_editor}) {
67 my $w = GCE::AttachEditor->new;
68 $w->set_attachment (
69 $ar->get ('attach'),
70 sub {
71 if (@{$_[0]}) {
72 $ar->set_silent (attach => $_[0])
73 } else {
74 $ar->set_silent (attach => undef)
75 }
76 }
77 );
78 $self->{attach_editor} = $w;
79 $w->signal_connect (destroy => sub { delete $self->{attach_editor} });
80 $w->show_all;
81 }
82 });
83
84 $actions_menu->set_submenu ($actmenu);
85 $actmenu->show_all;
86
87 $topvb->pack_start (my $hb2 = Gtk2::HBox->new, 0, 1, 0);
88 $hb2->pack_start (my $img = $self->{arch_img} = (new_from_pixbuf Gtk2::Image $pb), 0, 0, 0);
89 $img->set_alignment (0, 0.5);
90
91 $hb2->pack_start (my $lbl = $self->{arch_name_lbl} = Gtk2::Label->new, 0, 0, 0);
92 $lbl->set_alignment (0, 0.5);
93
94 $hb2->pack_start (my $statbtn = $self->{arch_stat_btn} = Gtk2::Button->new, 0, 0, 0);
95
96 $topvb->pack_start (my $docal = Gtk2::Alignment->new (0, 0.5, 0, 1), 0, 1, 0);
97 $topvb->pack_start (my $usebtn = $self->{use_btn} = Gtk2::ToggleButton->new ('show use'), 0, 1, 0);
98 $topvb->pack_start (my $useal = $self->{use_al} = Gtk2::Alignment->new (0, 0.5, 0, 1), 0, 1, 0);
99 $topvb->pack_start (my $ntbook = $self->{ntbook} = Gtk2::Notebook->new, 1, 1, 0);
100 $ntbook->set_scrollable (1);
101 $docal->add ($self->{doc_lbl} = Gtk2::Label->new);
102
103 $usebtn->set_active (0);
104 $self->{use_lbl} = Gtk2::Label->new;
105 $usebtn->signal_connect (toggled => sub {
106 my ($usebtn) = @_;
107
108 $useal->remove ($_) for $useal->get_children;
109 if ($usebtn->get_active) {
110 $useal->add ($self->{use_lbl});
111 $self->{use_lbl}->show;
112 }
113 });
114 $self->{doc_lbl}->set_line_wrap (1);
115 $self->{use_lbl}->set_line_wrap (1);
116
117 $self->add2 (my $sw = Gtk2::ScrolledWindow->new);
118 $sw->set_policy ('automatic', 'automatic');
119 $sw->add_with_viewport (my $inv = $self->{inv_edit} = GCE::InventoryEditor->new);
120 }
121
122 #sub spawn_editor {
123 # my ($arch, $cb) = @_;
124 #
125 # my $w = Gtk2::Window->new;
126 # $w->set_title ("gce - edit attrs");
127 # $w->add (my $ae = GCE::AttrEdit->new);
128 #
129 # main::set_pos_and_size ($w, $main::CFG->{attr_view}, 200, 200);
130 #
131 # $ae->set_arch ($arch, $cb);
132 # $w->set_title ("gce - edit $arch->{_name}");
133 #
134 # $w->show_all;
135 #}
136
137 sub update_arch {
138 my ($self, $ar, $key, $value) = @_;
139 $ar->set ($key, $value);
140 }
141
142 sub set_attr {
143 my ($self, $key, $value) = @_;
144
145 my $attr = $self->{archref}->get ($key);
146
147 unless (ref $attr) {
148
149 $self->{archref}->set ($key, $value);
150 }
151 }
152
153 sub get_arch {
154 my ($self) = @_;
155
156 $self->{archref}
157 }
158
159 #sub update {
160 # my ($self, $narch) = @_;
161 #
162 # if (ref ($narch) ne 'GCE::ArchRef') { require Carp; Carp::confess ("NO ARCH REF!") }
163 #
164 # if ($narch) {
165 # $self->set_arch ($narch);
166 # } else {
167 # $self->set_arch ($self->{archref});
168 # }
169 #}
170
171 sub set_arch {
172 my ($self, $ar, $clear_inv) = @_;
173
174 if ((defined $self->{archref}) && ($self->{archref} != $ar)) {
175 $self->{archref}->remove_on_change ('attredit')
176 if defined $self->{archref};
177 $ar->add_on_change (attredit => sub { $self->set_arch ($_[0]) });
178 $self->{attach_editor}->destroy if $self->{attach_editor};
179
180 } elsif (not defined $self->{archref}) {
181 $ar->add_on_change (attredit => sub { $self->set_arch ($_[0]) });
182 }
183
184 $self->{inv_edit}->clear_inv_hist if $clear_inv;
185 $self->{inv_edit}->set_arch ($ar);
186
187 $self->{archref} = $ar;
188
189 $self->{arch_name_lbl}->set_text ($ar->longname);
190
191 fill_pb_from_arch ($self->{arch_pb}, $ar->getarch);
192 $self->{arch_img}->set_from_pixbuf ($self->{arch_pb});
193 $self->label_set_color ($self->{arch_name_lbl}, 0);
194
195 $self->{arch_stat_btn}->set_label ($ar->{source});
196
197 # get current page (to remember it for later)
198 my $pgnum = $self->{ntbook}->get_current_page;
199 my $curwid = $self->{ntbook}->get_nth_page ($pgnum);
200 my $curpage_text =
201 defined $curwid
202 ? $self->{ntbook}->get_tab_label_text ($curwid)
203 : undef;
204
205 my $al_arch = $ar->archetype;
206
207 $self->hide;
208 $self->{ntbook}->remove ($_)
209 for $self->{ntbook}->get_children;
210
211 $self->{ttip} = Gtk2::Tooltips->new;
212
213 my $type = $ar->type;
214
215 for my $sec (@{$type->{section}}) {
216 my $secname = shift @$sec;
217 $self->add_section_edit_widgets ($self->{ntbook}, $secname, $ar, $sec);
218 }
219
220 for my $key (qw/lore msg/) {
221 $self->{ntbook}->append_page (my $v = Gtk2::VBox->new, $key);
222 $v->pack_start (my $sw = Gtk2::ScrolledWindow->new, 1, 1, 0);
223 $sw->set_policy ('automatic', 'automatic');
224 $sw->add (my $tb = $self->{"${key}_txt"} = Gtk2::TextView->new);
225 my $buf = $tb->get_buffer;
226 $buf->set_text ($ar->get ($key));
227 $buf->signal_connect (changed => sub {
228 my ($buf) = @_;
229 my $txt = $buf->get_text ($buf->get_start_iter, $buf->get_end_iter, 0);
230 if ($txt ne $ar->get ($key)) {
231 $ar->set_silent ($key, $txt);
232 }
233 1
234 });
235 }
236
237 my $desc = pseudohtml2txt $type->{desc};
238 my $use = pseudohtml2txt $type->{use};
239 $self->{doc_lbl}->set_text ($desc);
240 $self->{use_lbl}->set_text ($use);
241
242 $self->{ttip}->enable;
243
244 $self->show_all;
245
246 # reset the current page if found
247 # XXX: it's braindamaged: it has to be done AFTER show all for some reason
248 if (defined $curpage_text) {
249
250 for (my $i = 0; $i <= $self->{ntbook}->get_n_pages; $i++) {
251 my $w = $self->{ntbook}->get_nth_page ($i);
252
253 if ($w && $self->{ntbook}->get_tab_label_text ($w) eq $curpage_text) {
254 $self->{ntbook}->set_current_page ($i);
255 last;
256 }
257 }
258 }
259 }
260
261 sub add_section_edit_widgets {
262 my ($self, $ntbook, $name, $ar, $section) = @_;
263
264 $self->{ntbook}->append_page (my $sw = Gtk2::ScrolledWindow->new, $name);
265 $sw->set_policy ('automatic', 'automatic');
266 $sw->add_with_viewport (my $vb = Gtk2::VBox->new);
267 $vb->pack_start (my $table = new Gtk2::Table (2, (scalar @$section) + 1), 0, 1, 0);
268
269 my $i = 0;
270 for my $sec (@$section) {
271 my ($key, $sec) = ($sec->[0], $sec->[1]);
272
273 next if grep { $key eq $_ } qw/msg lore/;
274
275 my $bwid = Gtk2::EventBox->new;
276 $bwid->add (my $al = Gtk2::Alignment->new (0.0, 0.5, 0, 1));
277 $al->add (Gtk2::Label->new (def ($sec->{name}, $key)));
278
279 if ($sec->{desc} !~ m/^\s*$/s) {
280 $self->{ttip}->set_tip ($bwid, $sec->{desc});
281 }
282
283 $table->attach ($bwid, 0, 1, $i, $i + 1, ['shrink','fill'], 'fill', 5, 0);
284
285 $al = Gtk2::Alignment->new (0.0, 0.5, 1, 0);
286 $table->attach ($al, 1, 2, $i, $i + 1, ['expand', 'fill'], 'expand', 0, 0);
287 $al->add ($self->get_edit_widget ($key, $sec, $ar, $bwid));
288
289 $i++;
290 }
291 }
292
293 sub label_set_color {
294 my ($self, $lbl, $dark) = @_;
295 if ($dark) {
296 for (qw/normal active prelight selected insensitive/) {
297 $lbl->modify_bg ($_, $lbl->get_default_style->bg ('active'));
298 $lbl->modify_fg ($_, $lbl->get_default_style->fg ('active'));
299 }
300
301 } else {
302 for (qw/normal active prelight selected insensitive/) {
303 $lbl->modify_bg ($_, $lbl->get_default_style->bg ($_));
304 }
305 }
306
307 }
308
309 sub label_set_color_default {
310 my ($self, $lbl, $ar, $key, $val) = @_;
311 require Carp; $ar or Carp::confess ("UNDEF");
312 my $al_arch = $ar->archetype;
313 $self->label_set_color ($lbl, $ar->field_value_is_default ($key, $val));
314
315 }
316
317 # XXX: Warning: Ugly code ahead:
318 sub get_edit_widget {
319 my ($self, $key, $edspec, $ar, $lbl) = @_;
320
321 my $type = $edspec->{type};
322 my $al_arch = $ar->archetype;
323
324 if ($type eq 'bool') {
325 my $boolval = def ($edspec->{value}, [0, 1]);
326
327
328 $self->label_set_color_default ($lbl, $ar, $key, $ar->get_or_default ($key));
329
330 my $chk = new Gtk2::CheckButton (def ($edspec->{name}, $key));
331 $chk->set_active ($ar->get_or_default ($key) == $boolval->[1]);
332 $chk->signal_connect (clicked => sub {
333 my ($chk) = @_;
334
335 $ar->set_silent ($key, $boolval->[$chk->get_active * 1], $type);
336
337 $self->label_set_color_default ($lbl, $ar, $key, $boolval->[$chk->get_active * 1]);
338 });
339
340 $self->{ttip}->set_tip ($chk, $al_arch->{$key} * 1);
341
342 return $chk
343
344 } elsif (grep { $type eq $_ } qw/string int treasurelist float/) {
345 $self->label_set_color_default ($lbl, $ar, $key, $ar->get_or_default ($key));
346
347 my $entry = new Gtk2::Entry;
348 $entry->set_text ($ar->get_or_default ($key));
349 $entry->signal_connect (changed => sub {
350 my ($entry) = @_;
351 $self->label_set_color_default ($lbl, $ar, $key, $entry->get_text);
352 $ar->set_silent ($key, $entry->get_text);
353 1
354 });
355
356 $self->{ttip}->set_tip ($entry, $ar->archetype->{$key});
357
358 return $entry
359
360 } elsif ($type eq 'spell' or $type eq 'nz_spell') { # XXX: nz_spell bug in datafiles?
361 my $comb = Gtk2::ComboBox->new_text;
362 my $spells_idx = {};
363 my $spells_cmd_idx = {};
364 my $sp = \%Deliantra::Data::SPELL;
365
366 $comb->append_text ("<none>");
367
368 my $idx = 1; # XXX: replace this idx with a more save/correct method?
369 for (sort { $sp->{$a} cmp $sp->{$b} } keys %$sp) {
370 $spells_cmd_idx->{$idx} = $_;
371 $spells_idx->{$_} = $idx++;
372
373 $comb->append_text ($sp->{$_});
374 }
375 #XXX: FIXME: $self->{ttip}->set_tip ($comb, $sp->{$al_arch->{$key}});
376
377 $comb->set_active ($spells_idx->{$ar->get_or_default ($key)});
378 $self->label_set_color_default ($lbl, $ar, $key, $ar->get_or_default ($key));
379
380 $comb->signal_connect (changed => sub {
381 my ($comb) = @_;
382 $self->label_set_color_default ($lbl, $ar, $key, $spells_cmd_idx->{$comb->get_active});
383 $ar->set_silent ($key, $spells_cmd_idx->{$comb->get_active});
384 });
385 return $comb
386
387 } elsif ($type eq 'bitmask') {
388 my $chval = $ar->get_or_default ($key);
389 my $btn = Gtk2::Button->new;
390 $btn->add (my $lblb = Gtk2::Label->new ("bitmask: " . ($chval * 1)));
391 $self->{ttip}->set_tip ($btn, $al_arch->{$key});
392
393 my $menu = $self->create_bitmask_menu ($edspec->{value}, $lbl, $lblb, $ar, $key, \$chval);
394
395 $self->label_set_color_default ($lbl, $ar, $key, $chval);
396
397 $btn->signal_connect (button_press_event => sub {
398 my ($btn, $ev) = @_;
399 $menu->popup (undef, undef, undef, undef, $ev->button, 0);
400 });
401
402 return $btn;
403
404 } elsif ($type eq 'list') {
405 my $lblb = Gtk2::Label->new ($edspec->{value}->{$ar->get_or_default ($key) * 1});
406 my $btn = Gtk2::Button->new;
407 $self->{ttip}->set_tip ($btn, $edspec->{value}->{$al_arch->{$key}});
408 $btn->add ($lblb);
409 my $menu = $self->create_list_menu ($edspec->{value}, $lbl, $lblb, $ar, $key);
410
411 $self->label_set_color_default ($lbl, $ar, $key, $ar->get_or_default ($key));
412
413 $btn->signal_connect (button_press_event => sub {
414 my ($btn, $ev) = @_;
415 $menu->popup (undef, undef, undef, undef, $ev->button, 0);
416 });
417 return $btn;
418
419 } elsif ($type eq 'fixed') {
420 return Gtk2::Label->new ("$edspec->{name} = $edspec->{value}");
421
422 } elsif ($type eq 'text') {
423 return Gtk2::Label->new ("<see $key tab>");
424
425 } elsif ($type eq 'movement_type') { # ok... this is quite a big one... awww
426 my $a = Gtk2::Alignment->new (0, 0, 0, 0);
427 $a->add (my $v = Gtk2::VBox->new (0, 0));
428 $a->set_padding (4, 4, 0, 0);
429 my $btns1 = Gtk2::HButtonBox->new;
430 my $btns2 = Gtk2::HButtonBox->new;
431 $btns1->set_layout ('start');
432 $btns2->set_layout ('start');
433
434 $v->pack_start ($btns1, 0, 1, 0);
435 $v->pack_start ($btns2, 0, 1, 0);
436
437 my @lblbtns;
438 my $calc_lbl = sub { # update callback for the buttons
439 for (@lblbtns) {
440 my $btn = $_->[0];
441 my $lbl = $_->[1];
442
443 if (defined $ar->get ($key)) {
444 my $res = $ar->get ($key) eq $lbl;
445
446 if (defined $res) {
447 $self->label_set_color ($btn, 0);
448 $lbl = ($res ? "+" : "-") . $lbl;
449
450 } else {
451 $self->label_set_color ($btn, 1);
452 $lbl = "$lbl?";
453 }
454 } else {
455 $self->label_set_color ($btn, 1);
456 $lbl = "$lbl?";
457 }
458
459 $btn->set_label ($lbl);
460 }
461 };
462
463 $self->label_set_color_default ($lbl, $ar, $key, $ar->get_or_default ($key));
464
465 my $mid = (scalar @Deliantra::MOVE_TYPE) / 2;
466 my $cnt = 0;
467 my $box = $btns1;
468
469 for my $mty (@Deliantra::MOVE_TYPE) {
470 $box->pack_start (my $btn = Gtk2::Button->new ($calc_lbl->()), 0, 1, 0);
471 push @lblbtns, [$btn, $mty];
472
473 $self->{ttip}->set_tip ($btn, $ar->archetype->{$key});
474 $btn->signal_connect (clicked => sub {
475 my $v = $ar->get ($key) || Deliantra::MoveType->new;
476
477 $v x= $mty;
478 $v = $v eq '' ? undef : $v;
479 $ar->set_silent ($key, $v);
480
481 $self->label_set_color_default ($lbl, $ar, $key, $v);
482 $calc_lbl->();
483 });
484
485 if (++$cnt >= $mid) { $box = $btns2 } # for wrapping into the second button box
486 }
487 $calc_lbl->();
488
489 return $a;
490
491 } else {
492 return Gtk2::Label->new ("$key => $edspec->{name} ($type)");
493
494 }
495 }
496
497 sub bitmask_to_list {
498 my ($self, $bitlist, $bits) = @_;
499
500 my @l;
501 for (%$bitlist) {
502 if ($bits & (1 << $_)) {
503 push @l, $bitlist->{$_};
504 }
505 }
506 return @l;
507 }
508
509 sub create_list_menu {
510 my ($self, $list, $clbl, $lbl, $ar, $key) = @_;
511
512 my $menu = Gtk2::Menu->new;
513
514 for my $item (sort keys %$list) {
515 my $lbltxt = $list->{$item};
516 my $menuitem = Gtk2::MenuItem->new_with_label ($lbltxt);
517 $menuitem->signal_connect (activate => sub {
518 my ($menuitem) = @_;
519 $lbl->set_text ($list->{$item});
520 $self->label_set_color_default ($clbl, $ar, $key, $item);
521 $ar->set_silent ($key, $item);
522 });
523 $menu->append ($menuitem);
524 $menuitem->show;
525 }
526
527 return $menu;
528 }
529
530 sub create_bitmask_menu {
531 my ($self, $bits, $clbl, $lbl, $ar, $key, $rval) = @_;
532
533 my $menu = Gtk2::Menu->new;
534
535 for my $bit (sort { $a <=> $b } keys %$bits) {
536 my $lbltxt = $bits->{$bit};
537 my $menuitem = Gtk2::CheckMenuItem->new_with_label ($lbltxt);
538 if ($$rval & (1 << $bit)) {
539 $menuitem->set_active (1);#$arch->{$key} & (1 << $bit));
540 }
541 $menuitem->signal_connect (toggled => sub {
542 my ($menuitem) = @_;
543 my $newval = $ar->get ($key);
544 $$rval &= ~(1 << $bit);
545 $$rval |= (1 << $bit) if $menuitem->get_active;
546 $lbl->set_text ("bitmask: " . ($$rval * 1));
547 $self->label_set_color_default ($clbl, $ar, $key, $$rval);
548 $ar->set_silent ($key, $$rval);
549 });
550 $menu->append ($menuitem);
551 $menuitem->show;
552 }
553
554 return $menu;
555 }
556
557
558 =head1 AUTHOR
559
560 Marc Lehmann <schmorp@schmorp.de>
561 http://home.schmorp.de/
562
563 Robin Redeker <elmex@ta-sa.org>
564 http://www.ta-sa.org/
565
566 =cut
567 1;