| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
# Copyright 2007, 2008, 2009, 2010 Kevin Ryde |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
# This file is part of Gtk2-Ex-WidgetCursor. |
|
4
|
|
|
|
|
|
|
# |
|
5
|
|
|
|
|
|
|
# Gtk2-Ex-WidgetCursor is free software; you can redistribute it and/or |
|
6
|
|
|
|
|
|
|
# modify it under the terms of the GNU General Public License as published |
|
7
|
|
|
|
|
|
|
# by the Free Software Foundation; either version 3, or (at your option) any |
|
8
|
|
|
|
|
|
|
# later version. |
|
9
|
|
|
|
|
|
|
# |
|
10
|
|
|
|
|
|
|
# Gtk2-Ex-WidgetCursor is distributed in the hope that it will be useful, |
|
11
|
|
|
|
|
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12
|
|
|
|
|
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
13
|
|
|
|
|
|
|
# Public License for more details. |
|
14
|
|
|
|
|
|
|
# |
|
15
|
|
|
|
|
|
|
# You should have received a copy of the GNU General Public License along |
|
16
|
|
|
|
|
|
|
# with Gtk2-Ex-WidgetCursor. If not, see . |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
package Gtk2::Ex::WidgetCursor; |
|
19
|
4
|
|
|
4
|
|
74592
|
use 5.006; |
|
|
4
|
|
|
|
|
14
|
|
|
|
4
|
|
|
|
|
162
|
|
|
20
|
4
|
|
|
4
|
|
19
|
use strict; |
|
|
4
|
|
|
|
|
6
|
|
|
|
4
|
|
|
|
|
102
|
|
|
21
|
4
|
|
|
4
|
|
18
|
use warnings; |
|
|
4
|
|
|
|
|
9
|
|
|
|
4
|
|
|
|
|
99
|
|
|
22
|
4
|
|
|
4
|
|
17
|
use Carp; |
|
|
4
|
|
|
|
|
7
|
|
|
|
4
|
|
|
|
|
325
|
|
|
23
|
4
|
|
|
4
|
|
6280
|
use Gtk2; |
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
use List::Util; |
|
25
|
|
|
|
|
|
|
use POSIX (); |
|
26
|
|
|
|
|
|
|
use Scalar::Util 1.18; # 1.18 for pure-perl refaddr() fix |
|
27
|
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
# uncomment this to run the ### lines |
|
29
|
|
|
|
|
|
|
#use Smart::Comments; |
|
30
|
|
|
|
|
|
|
|
|
31
|
|
|
|
|
|
|
our $VERSION = 15; |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
# Gtk 2.2 for get_display() |
|
34
|
|
|
|
|
|
|
# could work without it, but probably not worth bothering |
|
35
|
|
|
|
|
|
|
Gtk2->CHECK_VERSION(2,2,0) |
|
36
|
|
|
|
|
|
|
or die "WidgetCursor requires Gtk 2.2 or higher"; |
|
37
|
|
|
|
|
|
|
|
|
38
|
|
|
|
|
|
|
|
|
39
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
40
|
|
|
|
|
|
|
# Cribs on widgets using gdk_window_set_cursor directly: |
|
41
|
|
|
|
|
|
|
# |
|
42
|
|
|
|
|
|
|
# GtkAboutDialog [not handled] |
|
43
|
|
|
|
|
|
|
# Puts "email" and "link" tags on text in the credits GtkTextView and |
|
44
|
|
|
|
|
|
|
# then does set_cursor on entering or leaving those. |
|
45
|
|
|
|
|
|
|
# |
|
46
|
|
|
|
|
|
|
# GtkCombo [ok mostly, with a hack] |
|
47
|
|
|
|
|
|
|
# Does a single set_cursor for a 'top-left-arrow' on a GtkEventBox in |
|
48
|
|
|
|
|
|
|
# its popup when realized. We dig that out for include_children, |
|
49
|
|
|
|
|
|
|
# primarily so a busy() shows the watch on the popup window if it |
|
50
|
|
|
|
|
|
|
# happens to be open. Of course GtkCombo is one of the ever-lengthening |
|
51
|
|
|
|
|
|
|
# parade of working and well-defined widgets which Gtk says you're not |
|
52
|
|
|
|
|
|
|
# meant to use any more. |
|
53
|
|
|
|
|
|
|
# |
|
54
|
|
|
|
|
|
|
# GtkCurve [not handled] |
|
55
|
|
|
|
|
|
|
# Multiple set_cursor calls according to mode and motion. A rarely used |
|
56
|
|
|
|
|
|
|
# widget so ignore it for now. |
|
57
|
|
|
|
|
|
|
# |
|
58
|
|
|
|
|
|
|
# GtkEntry [ok, with a hack] |
|
59
|
|
|
|
|
|
|
# An Entry uses a private GdkWindow subwindow 4 pixels smaller than the |
|
60
|
|
|
|
|
|
|
# main and sets a GDK_CURSOR_XTERM there when sensitive. That window |
|
61
|
|
|
|
|
|
|
# isn't presented in the public fields/functions but can be dug out from |
|
62
|
|
|
|
|
|
|
# $win->get_children. We set the cursor on both the main window and the |
|
63
|
|
|
|
|
|
|
# subwindow then have a hack to restore the insertion point cursor on |
|
64
|
|
|
|
|
|
|
# the latter when done. Getting the subwindow is fast since Gtk |
|
65
|
|
|
|
|
|
|
# maintains the list of children for gdk_window_get_children() itself |
|
66
|
|
|
|
|
|
|
# (as opposed to the way plain Xlib queries the server). |
|
67
|
|
|
|
|
|
|
# |
|
68
|
|
|
|
|
|
|
# The Entry can be made to restore the insertion cursor by toggling |
|
69
|
|
|
|
|
|
|
# 'sensitive'. Hard to know which is worse: toggling sensitive |
|
70
|
|
|
|
|
|
|
# needlessly, or forcibly setting the cursor back. The latter is needed |
|
71
|
|
|
|
|
|
|
# for the SpinButton subclass below, so it's easier to do that. |
|
72
|
|
|
|
|
|
|
# |
|
73
|
|
|
|
|
|
|
# GtkFileChooser [probably ok] |
|
74
|
|
|
|
|
|
|
# Sets a GDK_CURSOR_WATCH temporarily when busy. That probably kills |
|
75
|
|
|
|
|
|
|
# any WidgetCursor setting, but probably GtkFileChooser isn't something |
|
76
|
|
|
|
|
|
|
# you'll manipulate externally. |
|
77
|
|
|
|
|
|
|
# |
|
78
|
|
|
|
|
|
|
# GtkLabel [not handled] |
|
79
|
|
|
|
|
|
|
# Puts GDK_XTERM on a private selection window when sensitive and |
|
80
|
|
|
|
|
|
|
# selectable text, or something. This misses out on include_children |
|
81
|
|
|
|
|
|
|
# for now. |
|
82
|
|
|
|
|
|
|
# |
|
83
|
|
|
|
|
|
|
# GtkLinkButton [not very good] |
|
84
|
|
|
|
|
|
|
# A GtkButton subclass which does 'hand' set_cursor on its windowed |
|
85
|
|
|
|
|
|
|
# parent for enter and leave events on its input-only event window. |
|
86
|
|
|
|
|
|
|
# |
|
87
|
|
|
|
|
|
|
# The cursor applied to the event window (per GtkButton above) trumps |
|
88
|
|
|
|
|
|
|
# the hand on the parent, so that gets the right effect. But any |
|
89
|
|
|
|
|
|
|
# WidgetCursor setting on the parent is lost when LinkButton turns off |
|
90
|
|
|
|
|
|
|
# its hand under a leave-event. Might have to make a hack connecting to |
|
91
|
|
|
|
|
|
|
# leave-event and re-applying the parent window. |
|
92
|
|
|
|
|
|
|
# |
|
93
|
|
|
|
|
|
|
# GtkPaned [not handled] |
|
94
|
|
|
|
|
|
|
# Puts a cursor on its GdkWindow handle when sensitive. Not covered by |
|
95
|
|
|
|
|
|
|
# include_children for now. |
|
96
|
|
|
|
|
|
|
# |
|
97
|
|
|
|
|
|
|
# GtkRecentChooser [probably ok] |
|
98
|
|
|
|
|
|
|
# A GDK_WATCH when busy, similar to GtkFileChooser above. Hopefully ok |
|
99
|
|
|
|
|
|
|
# most of the time with no special attention. |
|
100
|
|
|
|
|
|
|
# |
|
101
|
|
|
|
|
|
|
# GtkSpinButton [imperfect] |
|
102
|
|
|
|
|
|
|
# Subclass of GtkEntry, but adds a "panel" window of arrows. In Gtk |
|
103
|
|
|
|
|
|
|
# 2.12 it was overlaid on the normal Entry widget window, ie. the main |
|
104
|
|
|
|
|
|
|
# outer one. In Gtk 2.14 it's a child of that outer window. |
|
105
|
|
|
|
|
|
|
# |
|
106
|
|
|
|
|
|
|
# For 2.12 it can be dug out by looking for sibling windows with events |
|
107
|
|
|
|
|
|
|
# directed to the widget. Then it's a case of operating on three |
|
108
|
|
|
|
|
|
|
# windows: the Entry main, the Entry 4-pixel smaller subwindow and the |
|
109
|
|
|
|
|
|
|
# SpinButton panel. |
|
110
|
|
|
|
|
|
|
# |
|
111
|
|
|
|
|
|
|
# As of Gtk 2.12 toggling sensitive doesn't work to restore the |
|
112
|
|
|
|
|
|
|
# insertion point cursor for a SpinButton, unlike its Entry superclass. |
|
113
|
|
|
|
|
|
|
# Something not chaining up presumably, so the only choice is to |
|
114
|
|
|
|
|
|
|
# forcibly put the cursor back. |
|
115
|
|
|
|
|
|
|
# |
|
116
|
|
|
|
|
|
|
# GtkStatusBar [not handled] |
|
117
|
|
|
|
|
|
|
# A cursor on its private grip GdkWindow. |
|
118
|
|
|
|
|
|
|
# |
|
119
|
|
|
|
|
|
|
# GtkTextView [ok] |
|
120
|
|
|
|
|
|
|
# Sets a GDK_XTERM insertion point cursor on its get_window('text') |
|
121
|
|
|
|
|
|
|
# sub-window when sensitive. We operate on the get_window('widget') and |
|
122
|
|
|
|
|
|
|
# get_window('text') both. |
|
123
|
|
|
|
|
|
|
# |
|
124
|
|
|
|
|
|
|
# Toggling sensitive will put back the insertion point cursor, like for |
|
125
|
|
|
|
|
|
|
# a GtkEntry above, and like for the Entry it's hard to know whether |
|
126
|
|
|
|
|
|
|
# it's worse to toggle sensitive or forcibly set back the cursor. For |
|
127
|
|
|
|
|
|
|
# now the latter can share code with Entry and SpinButton and thus is |
|
128
|
|
|
|
|
|
|
# what's used. |
|
129
|
|
|
|
|
|
|
# |
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
|
|
132
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
use Glib::Object::Subclass |
|
135
|
|
|
|
|
|
|
'Glib::Object', |
|
136
|
|
|
|
|
|
|
properties => [ Glib::ParamSpec->object |
|
137
|
|
|
|
|
|
|
('widget', |
|
138
|
|
|
|
|
|
|
'widget', |
|
139
|
|
|
|
|
|
|
'The widget to show the cursor in, if just one widget.', |
|
140
|
|
|
|
|
|
|
'Gtk2::Widget', |
|
141
|
|
|
|
|
|
|
Glib::G_PARAM_READWRITE), |
|
142
|
|
|
|
|
|
|
|
|
143
|
|
|
|
|
|
|
Glib::ParamSpec->scalar |
|
144
|
|
|
|
|
|
|
('widgets', |
|
145
|
|
|
|
|
|
|
'widgets', |
|
146
|
|
|
|
|
|
|
'An arrayref of widgets to show the cursor in.', |
|
147
|
|
|
|
|
|
|
Glib::G_PARAM_READWRITE), |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
Glib::ParamSpec->object |
|
150
|
|
|
|
|
|
|
('add-widget', |
|
151
|
|
|
|
|
|
|
'add-widget', |
|
152
|
|
|
|
|
|
|
'Pseudo-property to add a widget to the cursor in.', |
|
153
|
|
|
|
|
|
|
'Gtk2::Widget', |
|
154
|
|
|
|
|
|
|
['writable']), |
|
155
|
|
|
|
|
|
|
|
|
156
|
|
|
|
|
|
|
Glib::ParamSpec->scalar |
|
157
|
|
|
|
|
|
|
('cursor', |
|
158
|
|
|
|
|
|
|
'cursor', |
|
159
|
|
|
|
|
|
|
'Cursor to use while dragging, as any name or object accepted by Gtk2::Ex::WidgetCursor.', |
|
160
|
|
|
|
|
|
|
Glib::G_PARAM_READWRITE), |
|
161
|
|
|
|
|
|
|
# |
|
162
|
|
|
|
|
|
|
# when glib 1.240 has fix for this pspec/get/set style |
|
163
|
|
|
|
|
|
|
# { |
|
164
|
|
|
|
|
|
|
# pspec => ..., |
|
165
|
|
|
|
|
|
|
# get => \&cursor, |
|
166
|
|
|
|
|
|
|
# set => \&cursor, |
|
167
|
|
|
|
|
|
|
# } |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
Glib::ParamSpec->string |
|
170
|
|
|
|
|
|
|
('cursor-name', |
|
171
|
|
|
|
|
|
|
'cursor-name', |
|
172
|
|
|
|
|
|
|
'Cursor to use while dragging, as cursor type enum nick plus "invisible".', |
|
173
|
|
|
|
|
|
|
'', # FIXME: default is undef when gtk2-perl allows that |
|
174
|
|
|
|
|
|
|
Glib::G_PARAM_READWRITE), |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
Glib::ParamSpec->boxed |
|
177
|
|
|
|
|
|
|
('cursor-object', |
|
178
|
|
|
|
|
|
|
'cursor-object', |
|
179
|
|
|
|
|
|
|
'Cursor to use while dragging, as cursor object.', |
|
180
|
|
|
|
|
|
|
'Gtk2::Gdk::Cursor', |
|
181
|
|
|
|
|
|
|
Glib::G_PARAM_READWRITE), |
|
182
|
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
Glib::ParamSpec->boolean |
|
184
|
|
|
|
|
|
|
('active', |
|
185
|
|
|
|
|
|
|
'active', |
|
186
|
|
|
|
|
|
|
'Whether to show this cursor.', |
|
187
|
|
|
|
|
|
|
0, # default no |
|
188
|
|
|
|
|
|
|
Glib::G_PARAM_READWRITE), |
|
189
|
|
|
|
|
|
|
# when glib 1.240 has fix for this pspec/get/set style |
|
190
|
|
|
|
|
|
|
# { |
|
191
|
|
|
|
|
|
|
# pspec => '', |
|
192
|
|
|
|
|
|
|
# get => \&active, |
|
193
|
|
|
|
|
|
|
# set => \&active, |
|
194
|
|
|
|
|
|
|
# } |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
Glib::ParamSpec->double |
|
197
|
|
|
|
|
|
|
('priority', |
|
198
|
|
|
|
|
|
|
'priority', |
|
199
|
|
|
|
|
|
|
'The priority of this cursor among multiple WidgetCursors on a given widget. Higher numbers are higher priority.', |
|
200
|
|
|
|
|
|
|
- POSIX::DBL_MAX(), # min |
|
201
|
|
|
|
|
|
|
POSIX::DBL_MAX(), # max |
|
202
|
|
|
|
|
|
|
0, # default |
|
203
|
|
|
|
|
|
|
Glib::G_PARAM_READWRITE), |
|
204
|
|
|
|
|
|
|
|
|
205
|
|
|
|
|
|
|
Glib::ParamSpec->boolean |
|
206
|
|
|
|
|
|
|
('include-children', |
|
207
|
|
|
|
|
|
|
'include-children', |
|
208
|
|
|
|
|
|
|
'Whether to apply the cursor to child widgets too.', |
|
209
|
|
|
|
|
|
|
0, # default no |
|
210
|
|
|
|
|
|
|
Glib::G_PARAM_READWRITE), |
|
211
|
|
|
|
|
|
|
|
|
212
|
|
|
|
|
|
|
]; |
|
213
|
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
# @wobjs is all the WidgetCursor objects which currently exist, sorted from |
|
215
|
|
|
|
|
|
|
# highest to lowest priority, and from newest to oldest among those of equal |
|
216
|
|
|
|
|
|
|
# priority |
|
217
|
|
|
|
|
|
|
# |
|
218
|
|
|
|
|
|
|
# Elements are weakened so they don't keep the objects alive. The DESTROY |
|
219
|
|
|
|
|
|
|
# method strips elements and undefs from here, but not sure if undef could |
|
220
|
|
|
|
|
|
|
# still be seen in here by certain funcs at certain times. |
|
221
|
|
|
|
|
|
|
# |
|
222
|
|
|
|
|
|
|
my @wobjs = (); |
|
223
|
|
|
|
|
|
|
|
|
224
|
|
|
|
|
|
|
sub INIT_INSTANCE { |
|
225
|
|
|
|
|
|
|
my ($self) = @_; |
|
226
|
|
|
|
|
|
|
$self->{'installed_widgets'} = []; |
|
227
|
|
|
|
|
|
|
_wobjs_insert ($self); |
|
228
|
|
|
|
|
|
|
} |
|
229
|
|
|
|
|
|
|
sub FINALIZE_INSTANCE { |
|
230
|
|
|
|
|
|
|
my ($self) = @_; |
|
231
|
|
|
|
|
|
|
### FINALIZE_INSTANCE: "$self" |
|
232
|
|
|
|
|
|
|
_splice_out (\@wobjs, $self); |
|
233
|
|
|
|
|
|
|
if (delete $self->{'active'}) { |
|
234
|
|
|
|
|
|
|
_wobj_deactivated ($self); |
|
235
|
|
|
|
|
|
|
} |
|
236
|
|
|
|
|
|
|
} |
|
237
|
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
sub GET_PROPERTY { |
|
239
|
|
|
|
|
|
|
my ($self, $pspec) = @_; |
|
240
|
|
|
|
|
|
|
### WidgetCursor GET_PROPERTY(): $pspec->get_name |
|
241
|
|
|
|
|
|
|
my $pname = $pspec->get_name; |
|
242
|
|
|
|
|
|
|
|
|
243
|
|
|
|
|
|
|
if ($pname eq 'cursor_name') { |
|
244
|
|
|
|
|
|
|
my $cursor = $self->{'cursor'}; |
|
245
|
|
|
|
|
|
|
if (Scalar::Util::blessed($cursor)) { |
|
246
|
|
|
|
|
|
|
$cursor = $cursor->type; |
|
247
|
|
|
|
|
|
|
} |
|
248
|
|
|
|
|
|
|
return $cursor; |
|
249
|
|
|
|
|
|
|
} |
|
250
|
|
|
|
|
|
|
if ($pname eq 'cursor_object') { |
|
251
|
|
|
|
|
|
|
my $cursor = $self->{'cursor'}; |
|
252
|
|
|
|
|
|
|
return (Scalar::Util::blessed($cursor) |
|
253
|
|
|
|
|
|
|
&& $cursor->isa('Gtk2::Gdk::Cursor') |
|
254
|
|
|
|
|
|
|
&& $cursor); |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
|
|
257
|
|
|
|
|
|
|
return $self->{$pname}; |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
sub SET_PROPERTY { |
|
261
|
|
|
|
|
|
|
my ($self, $pspec, $newval) = @_; |
|
262
|
|
|
|
|
|
|
### WidgetCursor SET_PROPERTY(): $pspec->get_name |
|
263
|
|
|
|
|
|
|
my $pname = $pspec->get_name; |
|
264
|
|
|
|
|
|
|
|
|
265
|
|
|
|
|
|
|
if ($pname eq 'active') { |
|
266
|
|
|
|
|
|
|
$self->active($newval); |
|
267
|
|
|
|
|
|
|
return; |
|
268
|
|
|
|
|
|
|
} |
|
269
|
|
|
|
|
|
|
if ($pname =~ /^cursor/) { |
|
270
|
|
|
|
|
|
|
$self->cursor($newval); |
|
271
|
|
|
|
|
|
|
return; |
|
272
|
|
|
|
|
|
|
} |
|
273
|
|
|
|
|
|
|
if ($pname eq 'add_widget') { |
|
274
|
|
|
|
|
|
|
$self->add_widgets ($newval); |
|
275
|
|
|
|
|
|
|
return; |
|
276
|
|
|
|
|
|
|
} |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
if ($pname eq 'widget') { |
|
279
|
|
|
|
|
|
|
$pname = 'widgets'; |
|
280
|
|
|
|
|
|
|
$newval = [ $newval ]; |
|
281
|
|
|
|
|
|
|
} |
|
282
|
|
|
|
|
|
|
if ($pname eq 'widgets') { |
|
283
|
|
|
|
|
|
|
my @array; |
|
284
|
|
|
|
|
|
|
push @array, @$newval; |
|
285
|
|
|
|
|
|
|
foreach (@array) { Scalar::Util::weaken ($_); } |
|
286
|
|
|
|
|
|
|
$self->{'widgets'} = \@array; |
|
287
|
|
|
|
|
|
|
if ($self->{'active'}) { |
|
288
|
|
|
|
|
|
|
_wobj_activated ($self); |
|
289
|
|
|
|
|
|
|
} |
|
290
|
|
|
|
|
|
|
return; |
|
291
|
|
|
|
|
|
|
} |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
$self->{$pname} = $newval; |
|
294
|
|
|
|
|
|
|
|
|
295
|
|
|
|
|
|
|
if ($pname eq 'priority') { |
|
296
|
|
|
|
|
|
|
_wobjs_insert ($self); |
|
297
|
|
|
|
|
|
|
if ($self->{'active'}) { |
|
298
|
|
|
|
|
|
|
_wobj_activated ($self); |
|
299
|
|
|
|
|
|
|
} |
|
300
|
|
|
|
|
|
|
} |
|
301
|
|
|
|
|
|
|
} |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
# insert $self into @wobjs according to $self->{'priority'} |
|
304
|
|
|
|
|
|
|
sub _wobjs_insert { |
|
305
|
|
|
|
|
|
|
my ($self) = @_; |
|
306
|
|
|
|
|
|
|
|
|
307
|
|
|
|
|
|
|
my $priority = ($self->{'priority'}||0); |
|
308
|
|
|
|
|
|
|
my $pos = 0; |
|
309
|
|
|
|
|
|
|
while ($pos < @wobjs) { |
|
310
|
|
|
|
|
|
|
# FINALIZE_INSTANCE removes on destroy, but beware of undef just in case |
|
311
|
|
|
|
|
|
|
if (my $wobj = $wobjs[$pos]) { |
|
312
|
|
|
|
|
|
|
if ($wobj == $self) { |
|
313
|
|
|
|
|
|
|
splice @wobjs,$pos,1; # remove from old position |
|
314
|
|
|
|
|
|
|
next; |
|
315
|
|
|
|
|
|
|
} |
|
316
|
|
|
|
|
|
|
if ($priority >= ($wobj->{'priority'}||0)) { |
|
317
|
|
|
|
|
|
|
last; |
|
318
|
|
|
|
|
|
|
} |
|
319
|
|
|
|
|
|
|
} |
|
320
|
|
|
|
|
|
|
$pos++; |
|
321
|
|
|
|
|
|
|
} |
|
322
|
|
|
|
|
|
|
splice @wobjs,$pos,0, $self; |
|
323
|
|
|
|
|
|
|
Scalar::Util::weaken ($wobjs[$pos]); |
|
324
|
|
|
|
|
|
|
} |
|
325
|
|
|
|
|
|
|
|
|
326
|
|
|
|
|
|
|
# get or set "active" |
|
327
|
|
|
|
|
|
|
sub active { |
|
328
|
|
|
|
|
|
|
my ($self, $newval) = @_; |
|
329
|
|
|
|
|
|
|
if (@_ < 2) { return $self->{'active'}; } # get |
|
330
|
|
|
|
|
|
|
|
|
331
|
|
|
|
|
|
|
# set |
|
332
|
|
|
|
|
|
|
$newval = ($newval ? 1 : 0); # don't capture arbitrary input |
|
333
|
|
|
|
|
|
|
my $oldval = $self->{'active'}; |
|
334
|
|
|
|
|
|
|
$self->{'active'} = $newval; |
|
335
|
|
|
|
|
|
|
if ($oldval && ! $newval) { |
|
336
|
|
|
|
|
|
|
_wobj_deactivated ($self); |
|
337
|
|
|
|
|
|
|
$self->notify('active'); |
|
338
|
|
|
|
|
|
|
} elsif ($newval && ! $oldval) { |
|
339
|
|
|
|
|
|
|
_wobj_activated ($self); |
|
340
|
|
|
|
|
|
|
$self->notify('active'); |
|
341
|
|
|
|
|
|
|
} |
|
342
|
|
|
|
|
|
|
} |
|
343
|
|
|
|
|
|
|
|
|
344
|
|
|
|
|
|
|
# newly turned off or destroyed |
|
345
|
|
|
|
|
|
|
sub _wobj_deactivated { |
|
346
|
|
|
|
|
|
|
my ($self) = @_; |
|
347
|
|
|
|
|
|
|
### _wobj_deactivated() |
|
348
|
|
|
|
|
|
|
my $aref = $self->{'installed_widgets'}; |
|
349
|
|
|
|
|
|
|
### $aref |
|
350
|
|
|
|
|
|
|
$self->{'installed_widgets'} = []; |
|
351
|
|
|
|
|
|
|
foreach my $widget (@$aref) { |
|
352
|
|
|
|
|
|
|
_update_widget ($widget); |
|
353
|
|
|
|
|
|
|
} |
|
354
|
|
|
|
|
|
|
} |
|
355
|
|
|
|
|
|
|
|
|
356
|
|
|
|
|
|
|
# newly turned on or created on |
|
357
|
|
|
|
|
|
|
sub _wobj_activated { |
|
358
|
|
|
|
|
|
|
my ($self) = @_; |
|
359
|
|
|
|
|
|
|
### _wobj_activated() |
|
360
|
|
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
if ($self->{'include_children'}) { |
|
362
|
|
|
|
|
|
|
# go through widgets in other wobjs as well as ourselves since they may |
|
363
|
|
|
|
|
|
|
# be affected if they're children of one of ours (%done skips duplicates |
|
364
|
|
|
|
|
|
|
# among the lists) |
|
365
|
|
|
|
|
|
|
### include_children other wobjs |
|
366
|
|
|
|
|
|
|
my %done; |
|
367
|
|
|
|
|
|
|
foreach my $wobj (@wobjs) { |
|
368
|
|
|
|
|
|
|
foreach my $widget (@{$wobj->{'widgets'}}) { |
|
369
|
|
|
|
|
|
|
if (! $widget) { next; } # possible undef by weakening |
|
370
|
|
|
|
|
|
|
$done{Scalar::Util::refaddr($widget)} |
|
371
|
|
|
|
|
|
|
||= do { _update_widget ($widget); 1 }; |
|
372
|
|
|
|
|
|
|
} |
|
373
|
|
|
|
|
|
|
} |
|
374
|
|
|
|
|
|
|
|
|
375
|
|
|
|
|
|
|
# special handling for children of certain types that might be present |
|
376
|
|
|
|
|
|
|
# deep in the tree |
|
377
|
|
|
|
|
|
|
### include_children special TextView etc |
|
378
|
|
|
|
|
|
|
foreach my $widget (@{$self->{'widgets'}}) { |
|
379
|
|
|
|
|
|
|
if (! $widget) { next; } # possible undef by weakening |
|
380
|
|
|
|
|
|
|
|
|
381
|
|
|
|
|
|
|
foreach my $widget (_container_recursively ($widget)) { |
|
382
|
|
|
|
|
|
|
if ($widget->isa('Gtk2::Entry') |
|
383
|
|
|
|
|
|
|
|| $widget->isa('Gtk2::TextView') |
|
384
|
|
|
|
|
|
|
|| _widget_is_combo_eventbox ($widget)) { |
|
385
|
|
|
|
|
|
|
$done{Scalar::Util::refaddr($widget)} |
|
386
|
|
|
|
|
|
|
||= do { _update_widget ($widget); 1 }; |
|
387
|
|
|
|
|
|
|
} |
|
388
|
|
|
|
|
|
|
} |
|
389
|
|
|
|
|
|
|
} |
|
390
|
|
|
|
|
|
|
|
|
391
|
|
|
|
|
|
|
} else { |
|
392
|
|
|
|
|
|
|
# simple non-include-children for this wobj, look only at its immediate |
|
393
|
|
|
|
|
|
|
# widgets |
|
394
|
|
|
|
|
|
|
foreach my $widget (@{$self->{'widgets'}}) { |
|
395
|
|
|
|
|
|
|
_update_widget ($widget); |
|
396
|
|
|
|
|
|
|
} |
|
397
|
|
|
|
|
|
|
} |
|
398
|
|
|
|
|
|
|
} |
|
399
|
|
|
|
|
|
|
|
|
400
|
|
|
|
|
|
|
sub _update_widget { |
|
401
|
|
|
|
|
|
|
my ($widget) = @_; |
|
402
|
|
|
|
|
|
|
if (! $widget) { return; } # possible undef from weakening |
|
403
|
|
|
|
|
|
|
### _update_widget: "$widget", $widget->get_name |
|
404
|
|
|
|
|
|
|
|
|
405
|
|
|
|
|
|
|
# find wobj with priority on this $widget |
|
406
|
|
|
|
|
|
|
my $wobj = List::Util::first |
|
407
|
|
|
|
|
|
|
{ $_->{'active'} && _wobj_applies_to_widget($_,$widget)} @wobjs; |
|
408
|
|
|
|
|
|
|
|
|
409
|
|
|
|
|
|
|
my $old_wobj = $widget->{__PACKAGE__.'.installed'}; |
|
410
|
|
|
|
|
|
|
### wobj was: defined $old_wobj && $old_wobj->{'cursor'} |
|
411
|
|
|
|
|
|
|
### now: defined $wobj && $wobj->{'cursor'} |
|
412
|
|
|
|
|
|
|
### window: "@{[$widget->window||'undef']}" |
|
413
|
|
|
|
|
|
|
|
|
414
|
|
|
|
|
|
|
if (($wobj||0) == ($old_wobj||0)) { return; } # unchanged |
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# forget this widget under $old_wobj |
|
417
|
|
|
|
|
|
|
if ($old_wobj) { |
|
418
|
|
|
|
|
|
|
_splice_out ($old_wobj->{'installed_widgets'}, $widget); |
|
419
|
|
|
|
|
|
|
} |
|
420
|
|
|
|
|
|
|
|
|
421
|
|
|
|
|
|
|
if (! $wobj) { |
|
422
|
|
|
|
|
|
|
# no wobj applies to this widget any more |
|
423
|
|
|
|
|
|
|
delete $widget->{__PACKAGE__.'.installed'}; |
|
424
|
|
|
|
|
|
|
delete $widget->{__PACKAGE__.'.realize_ids'}; |
|
425
|
|
|
|
|
|
|
|
|
426
|
|
|
|
|
|
|
my ($hack_win, $hack_cursor) = $widget->Gtk2_Ex_WidgetCursor_hack_restore; |
|
427
|
|
|
|
|
|
|
$hack_win ||= 0; # avoid undef |
|
428
|
|
|
|
|
|
|
foreach my $win ($widget->Gtk2_Ex_WidgetCursor_windows) { |
|
429
|
|
|
|
|
|
|
$win || next; |
|
430
|
|
|
|
|
|
|
my $cursor = ($win == $hack_win |
|
431
|
|
|
|
|
|
|
? Gtk2::Gdk::Cursor->new_for_display ($widget->get_display, |
|
432
|
|
|
|
|
|
|
$hack_cursor) |
|
433
|
|
|
|
|
|
|
: undef); |
|
434
|
|
|
|
|
|
|
### set_cursor back to: "$win", $cursor && $cursor->type |
|
435
|
|
|
|
|
|
|
$win->set_cursor ($cursor); |
|
436
|
|
|
|
|
|
|
} |
|
437
|
|
|
|
|
|
|
return; |
|
438
|
|
|
|
|
|
|
} |
|
439
|
|
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
# install wobj on this widget |
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
# remember this widget under wobj |
|
443
|
|
|
|
|
|
|
# this is when unrealized too, so remember to cleanup realize handler |
|
444
|
|
|
|
|
|
|
{ my $aref = $wobj->{'installed_widgets'}; |
|
445
|
|
|
|
|
|
|
push @$aref, $widget; |
|
446
|
|
|
|
|
|
|
Scalar::Util::weaken ($aref->[-1]); |
|
447
|
|
|
|
|
|
|
### gives installed_widgets: join(' ',@$aref) |
|
448
|
|
|
|
|
|
|
} |
|
449
|
|
|
|
|
|
|
|
|
450
|
|
|
|
|
|
|
# note this wobj under the widget |
|
451
|
|
|
|
|
|
|
$widget->{__PACKAGE__.'.installed'} = $wobj; |
|
452
|
|
|
|
|
|
|
Scalar::Util::weaken ($widget->{__PACKAGE__.'.installed'}); |
|
453
|
|
|
|
|
|
|
|
|
454
|
|
|
|
|
|
|
my @windows = $widget->Gtk2_Ex_WidgetCursor_windows; |
|
455
|
|
|
|
|
|
|
if (! defined $windows[0]) { |
|
456
|
|
|
|
|
|
|
### not realized, defer setting |
|
457
|
|
|
|
|
|
|
$widget->{__PACKAGE__.'.realize_ids'} ||= do { |
|
458
|
|
|
|
|
|
|
require Glib::Ex::SignalIds; |
|
459
|
|
|
|
|
|
|
Glib::Ex::SignalIds->new |
|
460
|
|
|
|
|
|
|
($widget, |
|
461
|
|
|
|
|
|
|
$widget->signal_connect (realize => \&_do_widget_realize)) |
|
462
|
|
|
|
|
|
|
}; |
|
463
|
|
|
|
|
|
|
return; |
|
464
|
|
|
|
|
|
|
} |
|
465
|
|
|
|
|
|
|
|
|
466
|
|
|
|
|
|
|
# and finally actually set the cursor |
|
467
|
|
|
|
|
|
|
my $cursor = _resolve_cursor ($wobj, $widget); |
|
468
|
|
|
|
|
|
|
foreach my $win (@windows) { |
|
469
|
|
|
|
|
|
|
$win or next; |
|
470
|
|
|
|
|
|
|
### set_cursor: "$win", $cursor && $cursor->type |
|
471
|
|
|
|
|
|
|
$win->set_cursor ($cursor); |
|
472
|
|
|
|
|
|
|
} |
|
473
|
|
|
|
|
|
|
} |
|
474
|
|
|
|
|
|
|
|
|
475
|
|
|
|
|
|
|
# 'realize' signal handler on a WidgetCursor affected widget |
|
476
|
|
|
|
|
|
|
sub _do_widget_realize { |
|
477
|
|
|
|
|
|
|
my ($widget) = @_; |
|
478
|
|
|
|
|
|
|
### _do_widget_realize(): "$widget" |
|
479
|
|
|
|
|
|
|
delete $widget->{__PACKAGE__.'.realize_ids'}; |
|
480
|
|
|
|
|
|
|
_update_widget ($widget); |
|
481
|
|
|
|
|
|
|
} |
|
482
|
|
|
|
|
|
|
|
|
483
|
|
|
|
|
|
|
|
|
484
|
|
|
|
|
|
|
# Return true if $wobj is applicable to $widget, either because $widget is |
|
485
|
|
|
|
|
|
|
# in its widgets list or is a child of one of them for "include_children". |
|
486
|
|
|
|
|
|
|
# Note $w->is_ancestor($w) is false, ie. it doesn't include itself. |
|
487
|
|
|
|
|
|
|
# |
|
488
|
|
|
|
|
|
|
sub _wobj_applies_to_widget { |
|
489
|
|
|
|
|
|
|
my ($wobj, $widget) = @_; |
|
490
|
|
|
|
|
|
|
return List::Util::first |
|
491
|
|
|
|
|
|
|
{ defined $_ # possible weakening during destroy |
|
492
|
|
|
|
|
|
|
&& ($_ == $widget |
|
493
|
|
|
|
|
|
|
|| ($wobj->{'include_children'} && $widget->is_ancestor($_))) } |
|
494
|
|
|
|
|
|
|
@{$wobj->{'widgets'}}; |
|
495
|
|
|
|
|
|
|
} |
|
496
|
|
|
|
|
|
|
|
|
497
|
|
|
|
|
|
|
# get or set "cursor" |
|
498
|
|
|
|
|
|
|
sub cursor { |
|
499
|
|
|
|
|
|
|
my ($self, $newval) = @_; |
|
500
|
|
|
|
|
|
|
if (@_ < 2) { return $self->{'cursor'}; } # get |
|
501
|
|
|
|
|
|
|
|
|
502
|
|
|
|
|
|
|
# set |
|
503
|
|
|
|
|
|
|
if (_cursor_equal ($self->{'cursor'}, $newval)) { return; } |
|
504
|
|
|
|
|
|
|
$self->{'cursor'} = $newval; |
|
505
|
|
|
|
|
|
|
|
|
506
|
|
|
|
|
|
|
if ($self->{'active'}) { |
|
507
|
|
|
|
|
|
|
foreach my $widget (@{$self->{'installed_widgets'}}) { |
|
508
|
|
|
|
|
|
|
foreach my $win ($widget->Gtk2_Ex_WidgetCursor_windows) { |
|
509
|
|
|
|
|
|
|
$win || next; # only if realized |
|
510
|
|
|
|
|
|
|
$win->set_cursor (_resolve_cursor ($self, $widget)); |
|
511
|
|
|
|
|
|
|
} |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
} |
|
514
|
|
|
|
|
|
|
|
|
515
|
|
|
|
|
|
|
$self->notify('cursor'); |
|
516
|
|
|
|
|
|
|
$self->notify('cursor-name'); |
|
517
|
|
|
|
|
|
|
$self->notify('cursor-object'); |
|
518
|
|
|
|
|
|
|
} |
|
519
|
|
|
|
|
|
|
|
|
520
|
|
|
|
|
|
|
# return true if two cursor settings $x and $y are the same |
|
521
|
|
|
|
|
|
|
sub _cursor_equal { |
|
522
|
|
|
|
|
|
|
my ($x, $y) = @_; |
|
523
|
|
|
|
|
|
|
return ((! defined $x && ! defined $y) # undef == undef |
|
524
|
|
|
|
|
|
|
|| (ref $x && ref $y && $x == $y) # objects identical address |
|
525
|
|
|
|
|
|
|
|| (defined $x && defined $y && $x eq $y)); # strings by value |
|
526
|
|
|
|
|
|
|
} |
|
527
|
|
|
|
|
|
|
|
|
528
|
|
|
|
|
|
|
# get widgets in wobj |
|
529
|
|
|
|
|
|
|
sub widgets { |
|
530
|
|
|
|
|
|
|
my ($self) = @_; |
|
531
|
|
|
|
|
|
|
return grep {defined} @{$self->{'widgets'}}; |
|
532
|
|
|
|
|
|
|
} |
|
533
|
|
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
sub add_widgets { |
|
535
|
|
|
|
|
|
|
my ($self, @widgets) = @_; |
|
536
|
|
|
|
|
|
|
my $aref = $self->{'widgets'}; |
|
537
|
|
|
|
|
|
|
|
|
538
|
|
|
|
|
|
|
# only those not already in our list |
|
539
|
|
|
|
|
|
|
@widgets = grep { my $widget = @_; |
|
540
|
|
|
|
|
|
|
! List::Util::first {defined $_ && $_==$widget} |
|
541
|
|
|
|
|
|
|
@$aref } @widgets; |
|
542
|
|
|
|
|
|
|
if (! @widgets) { return; } |
|
543
|
|
|
|
|
|
|
|
|
544
|
|
|
|
|
|
|
foreach my $widget (@widgets) { |
|
545
|
|
|
|
|
|
|
push @$aref, $widget; |
|
546
|
|
|
|
|
|
|
Scalar::Util::weaken ($aref->[-1]); |
|
547
|
|
|
|
|
|
|
} |
|
548
|
|
|
|
|
|
|
|
|
549
|
|
|
|
|
|
|
if ($self->{'include_children'}) { |
|
550
|
|
|
|
|
|
|
# for include_children must have a deep look down through the new |
|
551
|
|
|
|
|
|
|
# widgets, let the full code of _wobj_activated() do that (though it's a |
|
552
|
|
|
|
|
|
|
# little wasteful to look again at the previously covered widgets) |
|
553
|
|
|
|
|
|
|
_wobj_activated ($self); |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
} else { |
|
556
|
|
|
|
|
|
|
# for ordinary only the newly added widgets might change |
|
557
|
|
|
|
|
|
|
foreach my $widget (@widgets) { |
|
558
|
|
|
|
|
|
|
_update_widget ($widget); |
|
559
|
|
|
|
|
|
|
} |
|
560
|
|
|
|
|
|
|
} |
|
561
|
|
|
|
|
|
|
$self->notify('widget'); |
|
562
|
|
|
|
|
|
|
$self->notify('widgets'); |
|
563
|
|
|
|
|
|
|
} |
|
564
|
|
|
|
|
|
|
|
|
565
|
|
|
|
|
|
|
# return an actual Gtk2::Gdk::Cursor from what may be only a string setting |
|
566
|
|
|
|
|
|
|
# in $wobj->{'cursor'} |
|
567
|
|
|
|
|
|
|
sub _resolve_cursor { |
|
568
|
|
|
|
|
|
|
my ($wobj, $widget) = @_; |
|
569
|
|
|
|
|
|
|
my $cursor = $wobj->{'cursor'}; |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
if (! defined $cursor || ref $cursor) { |
|
572
|
|
|
|
|
|
|
# undef or cursor object |
|
573
|
|
|
|
|
|
|
return $cursor; |
|
574
|
|
|
|
|
|
|
|
|
575
|
|
|
|
|
|
|
} elsif ($cursor eq 'invisible') { |
|
576
|
|
|
|
|
|
|
# call through $wobj in case of subclassing |
|
577
|
|
|
|
|
|
|
return $wobj->invisible_cursor ($widget); |
|
578
|
|
|
|
|
|
|
|
|
579
|
|
|
|
|
|
|
} else { |
|
580
|
|
|
|
|
|
|
# string cursor name -- only ever call to resolve here when widget is |
|
581
|
|
|
|
|
|
|
# realized, so get_display() isn't undef |
|
582
|
|
|
|
|
|
|
if ($widget->can('get_display')) { |
|
583
|
|
|
|
|
|
|
# gtk 2.2 up |
|
584
|
|
|
|
|
|
|
my $display = $widget->get_display; |
|
585
|
|
|
|
|
|
|
return Gtk2::Gdk::Cursor->new_for_display ($display, $cursor); |
|
586
|
|
|
|
|
|
|
} else { |
|
587
|
|
|
|
|
|
|
# gtk 2.0.x |
|
588
|
|
|
|
|
|
|
return Gtk2::Gdk::Cursor->new ($cursor); |
|
589
|
|
|
|
|
|
|
} |
|
590
|
|
|
|
|
|
|
} |
|
591
|
|
|
|
|
|
|
} |
|
592
|
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
# Return $widget and all its contained children, grandchildren, etc. |
|
594
|
|
|
|
|
|
|
# Iterative avoids deep recursion warning for the unlikely case of nesting |
|
595
|
|
|
|
|
|
|
# beyond 100 deep. |
|
596
|
|
|
|
|
|
|
# |
|
597
|
|
|
|
|
|
|
sub _container_recursively { |
|
598
|
|
|
|
|
|
|
my @pending = @_; |
|
599
|
|
|
|
|
|
|
my @ret; |
|
600
|
|
|
|
|
|
|
while (@pending) { |
|
601
|
|
|
|
|
|
|
my $widget = pop @pending; |
|
602
|
|
|
|
|
|
|
push @ret, $widget; |
|
603
|
|
|
|
|
|
|
if (my $func = $widget->can('get_children')) { |
|
604
|
|
|
|
|
|
|
push @pending, $widget->$func; |
|
605
|
|
|
|
|
|
|
} |
|
606
|
|
|
|
|
|
|
} |
|
607
|
|
|
|
|
|
|
return @ret; |
|
608
|
|
|
|
|
|
|
} |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
611
|
|
|
|
|
|
|
# operative windows hacks |
|
612
|
|
|
|
|
|
|
# |
|
613
|
|
|
|
|
|
|
# $widget->Gtk2_Ex_WidgetCursor_windows() returns a list of windows in |
|
614
|
|
|
|
|
|
|
# $widget to act on, with hacks to pickup multiple windows on core classes. |
|
615
|
|
|
|
|
|
|
# |
|
616
|
|
|
|
|
|
|
# $widget->Gtk2_Ex_WidgetCursor_hack_restore() returns ($win, $cursor). |
|
617
|
|
|
|
|
|
|
# $cursor is a string cursor name to put back on $win when there's no more |
|
618
|
|
|
|
|
|
|
# WidgetCursor objects. Or the return is an empty list or $win undef when |
|
619
|
|
|
|
|
|
|
# nothing to hack (in which case all windows go back to "undef" cursor). |
|
620
|
|
|
|
|
|
|
# |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
# default to operate on $widget->window alone |
|
623
|
|
|
|
|
|
|
*Gtk2::Widget::Gtk2_Ex_WidgetCursor_windows = \&Gtk2::Widget::window; |
|
624
|
|
|
|
|
|
|
sub Gtk2::Widget::Gtk2_Ex_WidgetCursor_hack_restore { return (); } |
|
625
|
|
|
|
|
|
|
|
|
626
|
|
|
|
|
|
|
# GtkEventBox under a GtkComboBox popup window has a 'top-left-arrow'. It |
|
627
|
|
|
|
|
|
|
# gets overridden by a special case in the recursive updates above, and |
|
628
|
|
|
|
|
|
|
# hack_restore() here puts it back. |
|
629
|
|
|
|
|
|
|
# |
|
630
|
|
|
|
|
|
|
sub Gtk2::EventBox::Gtk2_Ex_WidgetCursor_hack_restore { |
|
631
|
|
|
|
|
|
|
my ($widget) = @_; |
|
632
|
|
|
|
|
|
|
return _widget_is_combo_eventbox($widget) |
|
633
|
|
|
|
|
|
|
&& ($widget->window, 'top-left-arrow'); |
|
634
|
|
|
|
|
|
|
} |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
# GtkTextView operate on 'text' subwindow to override its insertion point |
|
637
|
|
|
|
|
|
|
# cursor there, plus the main 'widget' window to cover the entire widget |
|
638
|
|
|
|
|
|
|
# extent. The 'text' subwindow insertion point is supposed to be on when |
|
639
|
|
|
|
|
|
|
# the widget is sensitive, so hack_restore() that. |
|
640
|
|
|
|
|
|
|
# |
|
641
|
|
|
|
|
|
|
sub Gtk2::TextView::Gtk2_Ex_WidgetCursor_windows { |
|
642
|
|
|
|
|
|
|
my ($widget) = @_; |
|
643
|
|
|
|
|
|
|
return ($widget->get_window ('widget'), |
|
644
|
|
|
|
|
|
|
$widget->get_window ('text')); |
|
645
|
|
|
|
|
|
|
} |
|
646
|
|
|
|
|
|
|
sub Gtk2::TextView::Gtk2_Ex_WidgetCursor_hack_restore { |
|
647
|
|
|
|
|
|
|
my ($widget) = @_; |
|
648
|
|
|
|
|
|
|
return $widget->sensitive && ($widget->get_window('text'), 'xterm'); |
|
649
|
|
|
|
|
|
|
} |
|
650
|
|
|
|
|
|
|
|
|
651
|
|
|
|
|
|
|
# GtkEntry's extra subwindow is included here. And when sensitive it should |
|
652
|
|
|
|
|
|
|
# be put back to an insertion point. For a bit of safety use list context |
|
653
|
|
|
|
|
|
|
# etc to allow for no subwindows, since it's undocumented. |
|
654
|
|
|
|
|
|
|
# |
|
655
|
|
|
|
|
|
|
# In Gtk 2.14 the SpinButton sub-class has the arrow panel as a subwindow |
|
656
|
|
|
|
|
|
|
# too (instead of an overlay in Gtk 2.12 and earlier). So look for the |
|
657
|
|
|
|
|
|
|
# smaller height one among multiple subwindows. |
|
658
|
|
|
|
|
|
|
# |
|
659
|
|
|
|
|
|
|
sub Gtk2::Entry::Gtk2_Ex_WidgetCursor_windows { |
|
660
|
|
|
|
|
|
|
my ($widget) = @_; |
|
661
|
|
|
|
|
|
|
my $win = $widget->window || return; # if unrealized |
|
662
|
|
|
|
|
|
|
return ($win, $win->get_children); |
|
663
|
|
|
|
|
|
|
} |
|
664
|
|
|
|
|
|
|
sub Gtk2::Entry::Gtk2_Ex_WidgetCursor_hack_restore { |
|
665
|
|
|
|
|
|
|
my ($widget) = @_; |
|
666
|
|
|
|
|
|
|
$widget->sensitive or return; |
|
667
|
|
|
|
|
|
|
my $win = $widget->window || return; # if unrealized |
|
668
|
|
|
|
|
|
|
my @children = $win->get_children; |
|
669
|
|
|
|
|
|
|
# by increasing height |
|
670
|
|
|
|
|
|
|
@children = sort {($a->get_size)[1] <=> ($b->get_size)[1]} @children; |
|
671
|
|
|
|
|
|
|
return ($children[0], 'xterm'); |
|
672
|
|
|
|
|
|
|
} |
|
673
|
|
|
|
|
|
|
# GtkSpinButton's extra "panel" overlay window either as a "sibling" (which |
|
674
|
|
|
|
|
|
|
# also finds the main window) for Gtk 2.12 or in the get_children() for Gtk |
|
675
|
|
|
|
|
|
|
# 2.13; plus the GtkEntry subwindow as per GtkEntry above. hack_restore() |
|
676
|
|
|
|
|
|
|
# inherited from GtkEntry above. |
|
677
|
|
|
|
|
|
|
# |
|
678
|
|
|
|
|
|
|
sub Gtk2::SpinButton::Gtk2_Ex_WidgetCursor_windows { |
|
679
|
|
|
|
|
|
|
my ($widget) = @_; |
|
680
|
|
|
|
|
|
|
my $win = $widget->window || return; # if unrealized |
|
681
|
|
|
|
|
|
|
return (_widget_sibling_windows ($widget), |
|
682
|
|
|
|
|
|
|
$win->get_children); |
|
683
|
|
|
|
|
|
|
} |
|
684
|
|
|
|
|
|
|
|
|
685
|
|
|
|
|
|
|
# GtkButton secret input-only "event_window" overlay found as a "sibling". |
|
686
|
|
|
|
|
|
|
# |
|
687
|
|
|
|
|
|
|
sub Gtk2::Button::Gtk2_Ex_WidgetCursor_windows { |
|
688
|
|
|
|
|
|
|
my ($widget) = @_; |
|
689
|
|
|
|
|
|
|
return _widget_sibling_windows ($widget); |
|
690
|
|
|
|
|
|
|
} |
|
691
|
|
|
|
|
|
|
|
|
692
|
|
|
|
|
|
|
# _widget_sibling_windows() returns a list of the "sibling" windows of |
|
693
|
|
|
|
|
|
|
# $widget. This means all the windows which are under $widget's parent and |
|
694
|
|
|
|
|
|
|
# have their events directed to $widget. If $widget is a windowed widget |
|
695
|
|
|
|
|
|
|
# then this will include its main $widget->window (or should do). |
|
696
|
|
|
|
|
|
|
# |
|
697
|
|
|
|
|
|
|
# The search works by seeing where a dummy expose event is directed by |
|
698
|
|
|
|
|
|
|
# gtk_get_event_widget(). It'd also be possible to inspect |
|
699
|
|
|
|
|
|
|
# gdk_window_get_user_data(), but Gtk2-Perl only returns an "unsigned" for |
|
700
|
|
|
|
|
|
|
# that so it'd need some nasty digging for the widget address. |
|
701
|
|
|
|
|
|
|
# |
|
702
|
|
|
|
|
|
|
# In the past the code here cached the result against the widget (what was |
|
703
|
|
|
|
|
|
|
# then just GtkButton's "event_window" sibling), with weakening of course so |
|
704
|
|
|
|
|
|
|
# unrealize would destroy the windows as normal. But don't bother with that |
|
705
|
|
|
|
|
|
|
# now, on the basis that cursor changes hopefully aren't so frequent as to |
|
706
|
|
|
|
|
|
|
# need too much trouble, and that it's less prone to mistakes if not cached |
|
707
|
|
|
|
|
|
|
# :-). |
|
708
|
|
|
|
|
|
|
# |
|
709
|
|
|
|
|
|
|
sub _widget_sibling_windows { |
|
710
|
|
|
|
|
|
|
my ($widget) = @_; |
|
711
|
|
|
|
|
|
|
my $parent_win = ($widget->flags & 'no-window' |
|
712
|
|
|
|
|
|
|
? $widget->window |
|
713
|
|
|
|
|
|
|
: $widget->get_parent_window) |
|
714
|
|
|
|
|
|
|
|| return; # if unrealized |
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
my $event = Gtk2::Gdk::Event->new ('expose'); |
|
717
|
|
|
|
|
|
|
return grep { $event->window ($_); |
|
718
|
|
|
|
|
|
|
($widget == (Gtk2->get_event_widget($event) || 0)) |
|
719
|
|
|
|
|
|
|
} $parent_win->get_children; |
|
720
|
|
|
|
|
|
|
} |
|
721
|
|
|
|
|
|
|
|
|
722
|
|
|
|
|
|
|
# Return true if $widget is the Gtk2::EventBox child of a Gtk2::Combo popup |
|
723
|
|
|
|
|
|
|
# window (it's a child of the popup window, not of the Combo itself). |
|
724
|
|
|
|
|
|
|
# |
|
725
|
|
|
|
|
|
|
sub _widget_is_combo_eventbox { |
|
726
|
|
|
|
|
|
|
my ($widget) = @_; |
|
727
|
|
|
|
|
|
|
my $parent; |
|
728
|
|
|
|
|
|
|
return ($widget->isa('Gtk2::EventBox') |
|
729
|
|
|
|
|
|
|
&& ($parent = $widget->get_parent) # might not have a parent |
|
730
|
|
|
|
|
|
|
&& $parent->get_name eq 'gtk-combo-popup-window'); |
|
731
|
|
|
|
|
|
|
} |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
|
|
734
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
735
|
|
|
|
|
|
|
|
|
736
|
|
|
|
|
|
|
# Could think about documenting this idle level to the world, maybe like the |
|
737
|
|
|
|
|
|
|
# following, but would it be any use? |
|
738
|
|
|
|
|
|
|
# |
|
739
|
|
|
|
|
|
|
# =item C<$Gtk2::Ex::WidgetCursor::busy_idle_priority> |
|
740
|
|
|
|
|
|
|
# |
|
741
|
|
|
|
|
|
|
# The priority level of the (C<< Glib::Idle->add >>) handler installed by |
|
742
|
|
|
|
|
|
|
# C. This is C by default, which is |
|
743
|
|
|
|
|
|
|
# designed to stay busy through Gtk resizing and redrawing at around |
|
744
|
|
|
|
|
|
|
# C, but end the busy before ordinary "default idle" |
|
745
|
|
|
|
|
|
|
# tasks. |
|
746
|
|
|
|
|
|
|
# |
|
747
|
|
|
|
|
|
|
# You can change this depending what things you set running at what idle |
|
748
|
|
|
|
|
|
|
# levels and where you consider the application no longer busy for user |
|
749
|
|
|
|
|
|
|
# purposes. But note changing this variable only affects future C |
|
750
|
|
|
|
|
|
|
# calls, not any currently active one. |
|
751
|
|
|
|
|
|
|
# |
|
752
|
|
|
|
|
|
|
use constant BUSY_IDLE_PRIORITY => Glib::G_PRIORITY_DEFAULT_IDLE - 10; |
|
753
|
|
|
|
|
|
|
|
|
754
|
|
|
|
|
|
|
my $busy_wc; |
|
755
|
|
|
|
|
|
|
my $busy_id; |
|
756
|
|
|
|
|
|
|
my $realize_id; |
|
757
|
|
|
|
|
|
|
|
|
758
|
|
|
|
|
|
|
sub busy { |
|
759
|
|
|
|
|
|
|
my ($class) = @_; |
|
760
|
|
|
|
|
|
|
my @widgets = Gtk2::Window->list_toplevels; |
|
761
|
|
|
|
|
|
|
### busy on toplevels: join(' ',@widgets) |
|
762
|
|
|
|
|
|
|
|
|
763
|
|
|
|
|
|
|
if ($busy_wc) { |
|
764
|
|
|
|
|
|
|
$busy_wc->add_widgets (@widgets); |
|
765
|
|
|
|
|
|
|
} else { |
|
766
|
|
|
|
|
|
|
### new busy with class: $class |
|
767
|
|
|
|
|
|
|
$busy_wc = $class->new (widgets => \@widgets, |
|
768
|
|
|
|
|
|
|
cursor => 'watch', |
|
769
|
|
|
|
|
|
|
include_children => 1, |
|
770
|
|
|
|
|
|
|
priority => 1000, |
|
771
|
|
|
|
|
|
|
active => 1); |
|
772
|
|
|
|
|
|
|
} |
|
773
|
|
|
|
|
|
|
_flush_mapped_widgets (@widgets); |
|
774
|
|
|
|
|
|
|
|
|
775
|
|
|
|
|
|
|
# This is a hack to persuade Gtk2-Perl 1.160 and 1.181 to finish loading |
|
776
|
|
|
|
|
|
|
# Gtk2::Widget. Without this if no Gtk2::Widget has ever been created the |
|
777
|
|
|
|
|
|
|
# signal_add_emission_hook() fails. 1.160 needs the combination of isa() |
|
778
|
|
|
|
|
|
|
# and find_property(). 1.181 is ok with find_property() alone. Either |
|
779
|
|
|
|
|
|
|
# way these can be removed when ready to depend on 1.200 and up. |
|
780
|
|
|
|
|
|
|
Gtk2::Widget->isa ('Gtk2::Widget'); |
|
781
|
|
|
|
|
|
|
Gtk2::Widget->find_property ('name'); |
|
782
|
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
$realize_id ||= Gtk2::Widget->signal_add_emission_hook |
|
784
|
|
|
|
|
|
|
(realize => \&_do_busy_realize_emission); |
|
785
|
|
|
|
|
|
|
|
|
786
|
|
|
|
|
|
|
$busy_id ||= Glib::Idle->add |
|
787
|
|
|
|
|
|
|
(\&_busy_idle_handler, undef, BUSY_IDLE_PRIORITY); |
|
788
|
|
|
|
|
|
|
} |
|
789
|
|
|
|
|
|
|
|
|
790
|
|
|
|
|
|
|
# While busy notice extra toplevels which have been realized. |
|
791
|
|
|
|
|
|
|
# The cursor setting is applied at the realize so it's there ready for when |
|
792
|
|
|
|
|
|
|
# the map is done. |
|
793
|
|
|
|
|
|
|
sub _do_busy_realize_emission { |
|
794
|
|
|
|
|
|
|
my ($invocation_hint, $param_list) = @_; |
|
795
|
|
|
|
|
|
|
my ($widget) = @$param_list; |
|
796
|
|
|
|
|
|
|
### WidgetCursor _do_busy_realize_emission(): "$widget" |
|
797
|
|
|
|
|
|
|
if ($widget->isa ('Gtk2::Window')) { |
|
798
|
|
|
|
|
|
|
$busy_wc->add_widgets (Gtk2::Window->list_toplevels); |
|
799
|
|
|
|
|
|
|
### _do_busy_realize_emission() flush |
|
800
|
|
|
|
|
|
|
$widget->get_display->flush; |
|
801
|
|
|
|
|
|
|
} |
|
802
|
|
|
|
|
|
|
return 1; # stay connected |
|
803
|
|
|
|
|
|
|
} |
|
804
|
|
|
|
|
|
|
|
|
805
|
|
|
|
|
|
|
# Call unbusy() through $busy_wc to allow for possible subclassing. |
|
806
|
|
|
|
|
|
|
# Using unbusy does a flush, which is often unnecessary but will ensure that |
|
807
|
|
|
|
|
|
|
# if there's lower priority idles still to run then our cursors go out |
|
808
|
|
|
|
|
|
|
# before the time they take. |
|
809
|
|
|
|
|
|
|
# |
|
810
|
|
|
|
|
|
|
sub _busy_idle_handler { |
|
811
|
|
|
|
|
|
|
### _busy_idle_handler finished |
|
812
|
|
|
|
|
|
|
$busy_id = undef; |
|
813
|
|
|
|
|
|
|
if ($busy_wc) { $busy_wc->unbusy; } |
|
814
|
|
|
|
|
|
|
return 0; # Glib::SOURCE_REMOVE, one run only |
|
815
|
|
|
|
|
|
|
} |
|
816
|
|
|
|
|
|
|
|
|
817
|
|
|
|
|
|
|
sub unbusy { |
|
818
|
|
|
|
|
|
|
# my ($class_or_self) = @_; |
|
819
|
|
|
|
|
|
|
### WidgetCursor unbusy() |
|
820
|
|
|
|
|
|
|
|
|
821
|
|
|
|
|
|
|
# Some freaky stuff can happen during perl "global destruction" with |
|
822
|
|
|
|
|
|
|
# classes being destroyed and disconecting emission hooks on their own, |
|
823
|
|
|
|
|
|
|
# provoking warnings from code like the following that does a cleanup |
|
824
|
|
|
|
|
|
|
# itself. Fairly confident that doesn't apply to Gtk2::Widget because |
|
825
|
|
|
|
|
|
|
# that class probably, hopefully, maybe, never gets destroyed, or at least |
|
826
|
|
|
|
|
|
|
# not until well after any Perl code might get a chance to call unbusy(). |
|
827
|
|
|
|
|
|
|
# |
|
828
|
|
|
|
|
|
|
if ($realize_id) { |
|
829
|
|
|
|
|
|
|
Gtk2::Widget->signal_remove_emission_hook (realize => $realize_id); |
|
830
|
|
|
|
|
|
|
undef $realize_id; |
|
831
|
|
|
|
|
|
|
} |
|
832
|
|
|
|
|
|
|
|
|
833
|
|
|
|
|
|
|
if ($busy_id) { |
|
834
|
|
|
|
|
|
|
Glib::Source->remove ($busy_id); |
|
835
|
|
|
|
|
|
|
$busy_id = undef; |
|
836
|
|
|
|
|
|
|
} |
|
837
|
|
|
|
|
|
|
if ($busy_wc) { |
|
838
|
|
|
|
|
|
|
my @widgets = $busy_wc->widgets; |
|
839
|
|
|
|
|
|
|
$busy_wc = undef; |
|
840
|
|
|
|
|
|
|
# flush to show new cursors immediately, per busy() below |
|
841
|
|
|
|
|
|
|
_flush_mapped_widgets (@widgets); |
|
842
|
|
|
|
|
|
|
} |
|
843
|
|
|
|
|
|
|
} |
|
844
|
|
|
|
|
|
|
|
|
845
|
|
|
|
|
|
|
# flush the Gtk2::Gdk::Display's of all the given widgets, if they're mapped |
|
846
|
|
|
|
|
|
|
# (with the idea being if they're unmapped then there's nothing to see so no |
|
847
|
|
|
|
|
|
|
# need to flush) |
|
848
|
|
|
|
|
|
|
# |
|
849
|
|
|
|
|
|
|
sub _flush_mapped_widgets { |
|
850
|
|
|
|
|
|
|
my @widget_list = @_; |
|
851
|
|
|
|
|
|
|
my %done; |
|
852
|
|
|
|
|
|
|
### _flush_mapped_widgets |
|
853
|
|
|
|
|
|
|
foreach my $widget (@widget_list) { |
|
854
|
|
|
|
|
|
|
if ($widget->mapped) { |
|
855
|
|
|
|
|
|
|
my $display = $widget->get_display; |
|
856
|
|
|
|
|
|
|
$done{Scalar::Util::refaddr($display)} ||= do { |
|
857
|
|
|
|
|
|
|
### flush display: "$display" |
|
858
|
|
|
|
|
|
|
$display->flush; |
|
859
|
|
|
|
|
|
|
1 |
|
860
|
|
|
|
|
|
|
}; |
|
861
|
|
|
|
|
|
|
} |
|
862
|
|
|
|
|
|
|
} |
|
863
|
|
|
|
|
|
|
} |
|
864
|
|
|
|
|
|
|
|
|
865
|
|
|
|
|
|
|
|
|
866
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
867
|
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
# list_values() creates a slew of hash records, so don't want to do that on |
|
869
|
|
|
|
|
|
|
# every invisible_cursor() call. Doing it once at BEGIN time also allows |
|
870
|
|
|
|
|
|
|
# the result to be inlined and the unused code discarded. |
|
871
|
|
|
|
|
|
|
# |
|
872
|
|
|
|
|
|
|
use constant _HAVE_BLANK_CURSOR |
|
873
|
|
|
|
|
|
|
=> (!! List::Util::first |
|
874
|
|
|
|
|
|
|
{$_->{'nick'} eq 'blank-cursor'} |
|
875
|
|
|
|
|
|
|
Glib::Type->list_values('Gtk2::Gdk::CursorType')); |
|
876
|
|
|
|
|
|
|
### _HAVE_BLANK_CURSOR: _HAVE_BLANK_CURSOR() |
|
877
|
|
|
|
|
|
|
|
|
878
|
|
|
|
|
|
|
sub invisible_cursor { |
|
879
|
|
|
|
|
|
|
my ($class, $target) = @_; |
|
880
|
|
|
|
|
|
|
my $display; |
|
881
|
|
|
|
|
|
|
|
|
882
|
|
|
|
|
|
|
if (! defined $target) { |
|
883
|
|
|
|
|
|
|
$display = Gtk2::Gdk::Display->get_default |
|
884
|
|
|
|
|
|
|
|| croak 'invisible_cursor(): no default display'; |
|
885
|
|
|
|
|
|
|
|
|
886
|
|
|
|
|
|
|
} elsif ($target->isa('Gtk2::Gdk::Display')) { |
|
887
|
|
|
|
|
|
|
$display = $target; |
|
888
|
|
|
|
|
|
|
|
|
889
|
|
|
|
|
|
|
} else { |
|
890
|
|
|
|
|
|
|
$display = $target->get_display |
|
891
|
|
|
|
|
|
|
|| croak "invisible_cursor(): get_display undef on $target"; |
|
892
|
|
|
|
|
|
|
} |
|
893
|
|
|
|
|
|
|
|
|
894
|
|
|
|
|
|
|
if (_HAVE_BLANK_CURSOR) { |
|
895
|
|
|
|
|
|
|
# gdk_cursor_new_for_display() returns same object each time so no need |
|
896
|
|
|
|
|
|
|
# to cache, though being a Glib::Boxed it's a new perl object every time |
|
897
|
|
|
|
|
|
|
return Gtk2::Gdk::Cursor->new_for_display ($display,'blank-cursor'); |
|
898
|
|
|
|
|
|
|
} else { |
|
899
|
|
|
|
|
|
|
return ($display->{__PACKAGE__.'.invisible_cursor'} |
|
900
|
|
|
|
|
|
|
||= do { |
|
901
|
|
|
|
|
|
|
### invisible_cursor() new for: "$display" |
|
902
|
|
|
|
|
|
|
my $window = $display->get_default_screen->get_root_window; |
|
903
|
|
|
|
|
|
|
my $mask = Gtk2::Gdk::Bitmap->create_from_data ($window,"\0",1,1); |
|
904
|
|
|
|
|
|
|
my $color = Gtk2::Gdk::Color->new (0,0,0); |
|
905
|
|
|
|
|
|
|
Gtk2::Gdk::Cursor->new_from_pixmap ($mask,$mask,$color,$color,0,0); |
|
906
|
|
|
|
|
|
|
}); |
|
907
|
|
|
|
|
|
|
} |
|
908
|
|
|
|
|
|
|
} |
|
909
|
|
|
|
|
|
|
|
|
910
|
|
|
|
|
|
|
|
|
911
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
912
|
|
|
|
|
|
|
# generic helpers |
|
913
|
|
|
|
|
|
|
|
|
914
|
|
|
|
|
|
|
sub _splice_out { |
|
915
|
|
|
|
|
|
|
my ($aref, $target) = @_; |
|
916
|
|
|
|
|
|
|
for (my $i = 0; $i < @$aref; $i++) { |
|
917
|
|
|
|
|
|
|
if (! defined $aref->[$i] || $aref->[$i] == $target) { |
|
918
|
|
|
|
|
|
|
splice @$aref, $i,1; |
|
919
|
|
|
|
|
|
|
} |
|
920
|
|
|
|
|
|
|
} |
|
921
|
|
|
|
|
|
|
} |
|
922
|
|
|
|
|
|
|
|
|
923
|
|
|
|
|
|
|
#------------------------------------------------------------------------------ |
|
924
|
|
|
|
|
|
|
1; |
|
925
|
|
|
|
|
|
|
__END__ |