ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/gde/GCE/AttrEdit.pm
Revision: 1.49
Committed: Wed Dec 16 17:25:27 2009 UTC (14 years, 5 months ago) by elmex
Branch: MAIN
Changes since 1.48: +6 -3 lines
Log Message:
handle default move types correctly.

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_or_default ($key)) {
444 my $res =
445 Deliantra::MoveType->new ($ar->get_or_default ($key))
446 >= $lbl;
447
448 if (defined $res) {
449 $self->label_set_color ($btn, 0);
450 $lbl = ($res ? "+" : "-") . $lbl;
451
452 } else {
453 $self->label_set_color ($btn, 1);
454 $lbl = "$lbl?";
455 }
456 } else {
457 $self->label_set_color ($btn, 1);
458 $lbl = "$lbl?";
459 }
460
461 $btn->set_label ($lbl);
462 }
463 };
464
465 $self->label_set_color_default ($lbl, $ar, $key, $ar->get_or_default ($key));
466
467 my $mid = (scalar @Deliantra::MOVE_TYPE) / 2;
468 my $cnt = 0;
469 my $box = $btns1;
470
471 for my $mty (@Deliantra::MOVE_TYPE) {
472 $box->pack_start (my $btn = Gtk2::Button->new ($calc_lbl->()), 0, 1, 0);
473 push @lblbtns, [$btn, $mty];
474
475 $self->{ttip}->set_tip ($btn, $ar->archetype->{$key});
476 $btn->signal_connect (clicked => sub {
477 my $v = $ar->get ($key) || Deliantra::MoveType->new;
478
479 $v x= $mty;
480 $v = $v eq '' ? undef : $v;
481 $ar->set_silent ($key, $v);
482
483 $self->label_set_color_default (
484 $lbl, $ar, $key, $ar->get_or_default ($key));
485 $calc_lbl->();
486 });
487
488 if (++$cnt >= $mid) { $box = $btns2 } # for wrapping into the second button box
489 }
490 $calc_lbl->();
491
492 return $a;
493
494 } else {
495 return Gtk2::Label->new ("$key => $edspec->{name} ($type)");
496
497 }
498 }
499
500 sub bitmask_to_list {
501 my ($self, $bitlist, $bits) = @_;
502
503 my @l;
504 for (%$bitlist) {
505 if ($bits & (1 << $_)) {
506 push @l, $bitlist->{$_};
507 }
508 }
509 return @l;
510 }
511
512 sub create_list_menu {
513 my ($self, $list, $clbl, $lbl, $ar, $key) = @_;
514
515 my $menu = Gtk2::Menu->new;
516
517 for my $item (sort keys %$list) {
518 my $lbltxt = $list->{$item};
519 my $menuitem = Gtk2::MenuItem->new_with_label ($lbltxt);
520 $menuitem->signal_connect (activate => sub {
521 my ($menuitem) = @_;
522 $lbl->set_text ($list->{$item});
523 $self->label_set_color_default ($clbl, $ar, $key, $item);
524 $ar->set_silent ($key, $item);
525 });
526 $menu->append ($menuitem);
527 $menuitem->show;
528 }
529
530 return $menu;
531 }
532
533 sub create_bitmask_menu {
534 my ($self, $bits, $clbl, $lbl, $ar, $key, $rval) = @_;
535
536 my $menu = Gtk2::Menu->new;
537
538 for my $bit (sort { $a <=> $b } keys %$bits) {
539 my $lbltxt = $bits->{$bit};
540 my $menuitem = Gtk2::CheckMenuItem->new_with_label ($lbltxt);
541 if ($$rval & (1 << $bit)) {
542 $menuitem->set_active (1);#$arch->{$key} & (1 << $bit));
543 }
544 $menuitem->signal_connect (toggled => sub {
545 my ($menuitem) = @_;
546 my $newval = $ar->get ($key);
547 $$rval &= ~(1 << $bit);
548 $$rval |= (1 << $bit) if $menuitem->get_active;
549 $lbl->set_text ("bitmask: " . ($$rval * 1));
550 $self->label_set_color_default ($clbl, $ar, $key, $$rval);
551 $ar->set_silent ($key, $$rval);
552 });
553 $menu->append ($menuitem);
554 $menuitem->show;
555 }
556
557 return $menu;
558 }
559
560
561 =head1 AUTHOR
562
563 Marc Lehmann <schmorp@schmorp.de>
564 http://home.schmorp.de/
565
566 Robin Redeker <elmex@ta-sa.org>
567 http://www.ta-sa.org/
568
569 =cut
570 1;