1#!/usr/bin/env perl
2
3# this extension implements popup-menu functionality for urxvt. it works
4# together with the urxvt::popup class - "no user serviceable parts inside".
5
6sub refresh {
7   my ($self) = @_;
8
9   my $cmd = "\x1b[H";
10
11   my $row = 1;
12   for my $item (@{ $self->{data}{item} }) {
13      my $rend = "normal";
14
15      if ($row == $self->{hover}) {
16         $rend = $self->{press} ? "active" : "hover";
17      }
18
19      $cmd .= "$item->{rend}{$rend}\x1b[K";
20      $cmd .= $self->locale_encode ($item->{render}->($item));
21      $cmd .= "\015\012";
22
23      $row++;
24   }
25
26   $self->cmd_parse (substr $cmd, 0, -2);
27}
28
29sub on_motion_notify {
30   my ($self, $event) = @_;
31
32   delete $self->{hover};
33
34   my ($row, $col) = ($event->{row}, $event->{col});
35   if ($col >= 0 && $col < $self->ncol
36       && $row >= 0 && $row < @{ $self->{data}{item} }) {
37      $self->{hover} = $event->{row} + 1;
38   }
39   $self->refresh;
40
41   1
42}
43
44sub on_button_press {
45   my ($self, $event) = @_;
46
47   $self->{press}[$event->{button}] = 1;
48   $self->refresh;
49
50   1
51}
52
53sub on_button_release {
54   my ($self, $event) = @_;
55
56   $self->{press}[$event->{button}] = 0;
57
58   my ($row, $col) = ($event->{row}, $event->{col});
59   if ($col >= 0 && $col < $self->ncol
60       && $row >= 0 && $row < @{ $self->{data}{item} }) {
61      my $item = $self->{data}{item}[$row];
62      $item->{activate}->($event, $item);
63   }
64
65   $self->refresh;
66
67   if ($event->{button} == $self->{data}{event}{button}) {
68      $self->ungrab;
69      $self->destroy;
70   }
71
72   1
73}
74
75sub on_focus_out {
76   my ($self) = @_;
77
78   delete $self->{hover};
79   $self->refresh;
80
81   ()
82}
83
84sub on_init {
85   my ($self) = @_;
86
87   my $data = $self->{data} = $urxvt::popup::self;
88
89   $_->{width} = $self->strwidth ($_->{text})
90      for @{ $data->{item} };
91
92   $self->resource (title => "URxvt Popup Menu");
93   $self->resource (name => "URxvt.popup");
94
95   $self->resource ($_ => $data->{term}->resource ($_))
96      for qw(font boldFont italicFont boldItalicFont color+0 color+1);
97
98   my $width  = List::Util::max map $_->{width}, @{ $data->{item} };
99   my $height = @{ $data->{item} };
100
101   my $pos = "";
102
103   if ($data->{event}) {
104      my $x = int List::Util::max 0, $data->{event}{x_root} - $width * $data->{term}->fwidth  * 0.5;
105      my $y = int List::Util::max 0, $data->{event}{y_root} -          $data->{term}->fheight * 0.5;
106      $pos = "+$x+$y";
107   }
108
109   $self->resource (geometry => "${width}x${height}$pos");
110
111   $self->{term}{urxvt_popup_init_done} = 1;
112
113   ()
114}
115
116sub on_start {
117   my ($self) = @_;
118
119   $self->cmd_parse ("\x1b[?25l\x1b[?7l");
120   $self->refresh;
121
122   # might fail, but try anyways
123   $self->grab ($self->{data}{event}{time}, 1)
124      and $self->allow_events_async;
125
126   on_button_press $self, $self->{data}{event} if $self->{data}{event}{button};
127
128   ()
129}
130
131sub on_map_notify {
132   my ($self, $event) = @_;
133
134   # should definitely not fail
135   $self->grab ($self->{data}{event}{time}, 1)
136      and $self->allow_events_async;
137}
138
139
140