1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/gtk/cursor.cpp
3 // Purpose: wxCursor implementation
4 // Author: Robert Roebling
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
10 // For compilers that support precompilation, includes "wx.h".
11 #include "wx/wxprec.h"
13 #include "wx/cursor.h"
16 #include "wx/window.h"
19 #include "wx/bitmap.h"
24 #include "wx/gtk/private/object.h"
25 #include "wx/gtk/private/gtk2-compat.h"
27 //-----------------------------------------------------------------------------
29 //-----------------------------------------------------------------------------
31 class wxCursorRefData
: public wxGDIRefData
35 virtual ~wxCursorRefData();
37 virtual bool IsOk() const { return m_cursor
!= NULL
; }
42 // There is no way to copy m_cursor so we can't implement a copy ctor
44 wxDECLARE_NO_COPY_CLASS(wxCursorRefData
);
47 wxCursorRefData::wxCursorRefData()
52 wxCursorRefData::~wxCursorRefData()
54 if (m_cursor
) gdk_cursor_unref( m_cursor
);
58 //-----------------------------------------------------------------------------
60 //-----------------------------------------------------------------------------
62 #define M_CURSORDATA static_cast<wxCursorRefData*>(m_refData)
64 IMPLEMENT_DYNAMIC_CLASS(wxCursor
, wxGDIObject
)
66 // used in the following two ctors
67 extern GtkWidget
*wxGetRootWindow();
75 wxCursor::wxCursor(const wxString
& cursor_file
,
77 int hotSpotX
, int hotSpotY
)
80 if (!img
.LoadFile(cursor_file
, type
))
83 // eventually set the hotspot:
84 if (!img
.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X
))
85 img
.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_X
, hotSpotX
);
86 if (!img
.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y
))
87 img
.SetOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y
, hotSpotY
);
92 wxCursor::wxCursor(const wxImage
& img
)
98 wxCursor::wxCursor(const char bits
[], int width
, int height
,
99 int hotSpotX
, int hotSpotY
,
100 const char maskBits
[], const wxColour
*fg
, const wxColour
*bg
)
108 if (hotSpotX
< 0 || hotSpotX
>= width
)
110 if (hotSpotY
< 0 || hotSpotY
>= height
)
113 GdkBitmap
* data
= gdk_bitmap_create_from_data(
114 gtk_widget_get_window(wxGetRootWindow()), const_cast<char*>(bits
), width
, height
);
115 GdkBitmap
* mask
= gdk_bitmap_create_from_data(
116 gtk_widget_get_window(wxGetRootWindow()), const_cast<char*>(maskBits
), width
, height
);
118 m_refData
= new wxCursorRefData
;
119 M_CURSORDATA
->m_cursor
= gdk_cursor_new_from_pixmap(
120 data
, mask
, fg
->GetColor(), bg
->GetColor(),
121 hotSpotX
, hotSpotY
);
123 g_object_unref (data
);
124 g_object_unref (mask
);
127 wxCursor::~wxCursor()
131 void wxCursor::InitFromStock( wxStockCursor cursorId
)
133 m_refData
= new wxCursorRefData();
135 GdkCursorType gdk_cur
= GDK_LEFT_PTR
;
140 const char bits
[] = { 0 };
141 const GdkColor color
= { 0, 0, 0, 0 };
143 GdkPixmap
*pixmap
= gdk_bitmap_create_from_data(NULL
, bits
, 1, 1);
144 M_CURSORDATA
->m_cursor
= gdk_cursor_new_from_pixmap(pixmap
,
149 g_object_unref(pixmap
);
153 case wxCURSOR_ARROW
: // fall through to default
154 case wxCURSOR_DEFAULT
: gdk_cur
= GDK_LEFT_PTR
; break;
155 case wxCURSOR_RIGHT_ARROW
: gdk_cur
= GDK_RIGHT_PTR
; break;
156 case wxCURSOR_HAND
: gdk_cur
= GDK_HAND2
; break;
157 case wxCURSOR_CROSS
: gdk_cur
= GDK_CROSSHAIR
; break;
158 case wxCURSOR_SIZEWE
: gdk_cur
= GDK_SB_H_DOUBLE_ARROW
; break;
159 case wxCURSOR_SIZENS
: gdk_cur
= GDK_SB_V_DOUBLE_ARROW
; break;
160 case wxCURSOR_ARROWWAIT
:
162 case wxCURSOR_WATCH
: gdk_cur
= GDK_WATCH
; break;
163 case wxCURSOR_SIZING
: gdk_cur
= GDK_SIZING
; break;
164 case wxCURSOR_SPRAYCAN
: gdk_cur
= GDK_SPRAYCAN
; break;
165 case wxCURSOR_IBEAM
: gdk_cur
= GDK_XTERM
; break;
166 case wxCURSOR_PENCIL
: gdk_cur
= GDK_PENCIL
; break;
167 case wxCURSOR_NO_ENTRY
: gdk_cur
= GDK_PIRATE
; break;
168 case wxCURSOR_SIZENWSE
:
169 case wxCURSOR_SIZENESW
: gdk_cur
= GDK_FLEUR
; break;
170 case wxCURSOR_QUESTION_ARROW
: gdk_cur
= GDK_QUESTION_ARROW
; break;
171 case wxCURSOR_PAINT_BRUSH
: gdk_cur
= GDK_SPRAYCAN
; break;
172 case wxCURSOR_MAGNIFIER
: gdk_cur
= GDK_PLUS
; break;
173 case wxCURSOR_CHAR
: gdk_cur
= GDK_XTERM
; break;
174 case wxCURSOR_LEFT_BUTTON
: gdk_cur
= GDK_LEFTBUTTON
; break;
175 case wxCURSOR_MIDDLE_BUTTON
: gdk_cur
= GDK_MIDDLEBUTTON
; break;
176 case wxCURSOR_RIGHT_BUTTON
: gdk_cur
= GDK_RIGHTBUTTON
; break;
177 case wxCURSOR_BULLSEYE
: gdk_cur
= GDK_TARGET
; break;
179 case wxCURSOR_POINT_LEFT
: gdk_cur
= GDK_SB_LEFT_ARROW
; break;
180 case wxCURSOR_POINT_RIGHT
: gdk_cur
= GDK_SB_RIGHT_ARROW
; break;
182 case wxCURSOR_DOUBLE_ARROW: gdk_cur = GDK_DOUBLE_ARROW; break;
183 case wxCURSOR_CROSS_REVERSE: gdk_cur = GDK_CROSS_REVERSE; break;
184 case wxCURSOR_BASED_ARROW_UP: gdk_cur = GDK_BASED_ARROW_UP; break;
185 case wxCURSOR_BASED_ARROW_DOWN: gdk_cur = GDK_BASED_ARROW_DOWN; break;
189 wxFAIL_MSG(wxT("unsupported cursor type"));
190 // will use the standard one
194 M_CURSORDATA
->m_cursor
= gdk_cursor_new( gdk_cur
);
199 static void GetHotSpot(const wxImage
& image
, int& x
, int& y
)
201 if (image
.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_X
))
202 x
= image
.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_X
);
206 if (image
.HasOption(wxIMAGE_OPTION_CUR_HOTSPOT_Y
))
207 y
= image
.GetOptionInt(wxIMAGE_OPTION_CUR_HOTSPOT_Y
);
211 if (x
< 0 || x
>= image
.GetWidth())
213 if (y
< 0 || y
>= image
.GetHeight())
217 void wxCursor::InitFromImage( const wxImage
& image
)
219 int w
= image
.GetWidth() ;
220 int h
= image
.GetHeight();
221 bool bHasMask
= image
.HasMask();
222 int hotSpotX
, hotSpotY
;
223 GetHotSpot(image
, hotSpotX
, hotSpotY
);
224 m_refData
= new wxCursorRefData
;
225 wxImage
image_copy(image
);
227 GdkDisplay
* display
= gdk_drawable_get_display(gtk_widget_get_window(wxGetRootWindow()));
228 if (gdk_display_supports_cursor_color(display
))
230 if (!image
.HasAlpha())
232 // add alpha, so wxBitmap will convert to pixbuf format
233 image_copy
.InitAlpha();
235 wxBitmap
bitmap(image_copy
);
236 wxASSERT(bitmap
.HasPixbuf());
237 M_CURSORDATA
->m_cursor
= gdk_cursor_new_from_pixbuf
244 else // no colour cursor support
246 unsigned long keyMaskColor
= 0;
250 keyMaskColor
= wxImageHistogram::MakeKey(
251 image
.GetMaskRed(), image
.GetMaskGreen(), image
.GetMaskBlue());
252 // get mask before image is modified
253 wxBitmap
bitmap(image
, 1);
254 maskRaw
= bitmap
.GetMask()->GetBitmap();
255 g_object_ref(maskRaw
);
259 const int size
= ((w
+ 7) / 8) * h
;
260 char* bits
= new char[size
];
261 memset(bits
, 0xff, size
);
262 maskRaw
= gdk_bitmap_create_from_data(
263 gtk_widget_get_window(wxGetRootWindow()), bits
, w
, h
);
267 // assign the raw pointer to wxGtkObject to ensure it is unref'd later
268 wxGtkObject
<GdkPixmap
> mask(maskRaw
);
270 // modify image so wxBitmap can be used to convert to pixmap
271 image_copy
.SetMask(false);
272 wxByte
* data
= image_copy
.GetData();
273 for (int j
= 0; j
< h
; j
++)
275 for (int i
= 0; i
< w
; i
++, data
+= 3)
277 // if average value of the pixel is > mid grey, convert it to
278 // background (0), otherwise to foreground (255, using wxBitmap
282 data
[2] = int(data
[0]) + data
[1] + data
[2] >= 3 * 128 ? 0 : 255;
285 wxBitmap
bitmap(image_copy
, 1);
287 // find the most frequent color(s)
288 wxImageHistogram histogram
;
289 image
.ComputeHistogram(histogram
);
291 long colMostFreq
= 0;
292 unsigned long nMost
= 0;
293 long colNextMostFreq
= 0;
294 unsigned long nNext
= 0;
295 for ( wxImageHistogram::iterator entry
= histogram
.begin();
296 entry
!= histogram
.end();
299 unsigned long key
= entry
->first
;
300 if ( !bHasMask
|| (key
!= keyMaskColor
) )
302 unsigned long value
= entry
->second
.value
;
306 colNextMostFreq
= colMostFreq
;
310 else if (value
> nNext
)
313 colNextMostFreq
= key
;
318 wxColour fg
= wxColour ( (unsigned char)(colMostFreq
>> 16),
319 (unsigned char)(colMostFreq
>> 8),
320 (unsigned char)(colMostFreq
) );
322 wxColour bg
= wxColour ( (unsigned char)(colNextMostFreq
>> 16),
323 (unsigned char)(colNextMostFreq
>> 8),
324 (unsigned char)(colNextMostFreq
) );
326 int fg_intensity
= fg
.Red() + fg
.Green() + fg
.Blue();
327 int bg_intensity
= bg
.Red() + bg
.Green() + bg
.Blue();
329 if (bg_intensity
> fg_intensity
)
337 M_CURSORDATA
->m_cursor
= gdk_cursor_new_from_pixmap
341 fg
.GetColor(), bg
.GetColor(),
347 #endif // wxUSE_IMAGE
349 GdkCursor
*wxCursor::GetCursor() const
351 return M_CURSORDATA
->m_cursor
;
354 wxGDIRefData
*wxCursor::CreateGDIRefData() const
356 return new wxCursorRefData
;
360 wxCursor::CloneGDIRefData(const wxGDIRefData
* WXUNUSED(data
)) const
362 // TODO: We can't clone GDK cursors at the moment. To do this we'd need
363 // to remember the original data from which the cursor was created
364 // (i.e. standard cursor type or the bitmap) or use
365 // gdk_cursor_get_cursor_type() (which is in 2.22+ only) and
366 // gdk_cursor_get_image().
367 wxFAIL_MSG( wxS("Cloning cursors is not implemented in wxGTK.") );
369 return new wxCursorRefData
;
372 //-----------------------------------------------------------------------------
373 // busy cursor routines
374 //-----------------------------------------------------------------------------
376 /* Current cursor, in order to hang on to
377 * cursor handle when setting the cursor globally */
378 wxCursor g_globalCursor
;
380 static wxCursor gs_savedCursor
;
381 static int gs_busyCount
= 0;
383 const wxCursor
&wxBusyCursor::GetStoredCursor()
385 return gs_savedCursor
;
388 const wxCursor
wxBusyCursor::GetBusyCursor()
390 return wxCursor(wxCURSOR_WATCH
);
393 static void UpdateCursors(const wxWindowList
& list
, GdkDisplay
*& display
)
395 wxWindowList::const_iterator i
= list
.begin();
396 for (size_t n
= list
.size(); n
--; ++i
)
399 if (display
== NULL
&& win
->m_widget
)
401 GdkWindow
* w
= gtk_widget_get_window(win
->m_widget
);
403 display
= gdk_drawable_get_display(w
);
405 win
->GTKUpdateCursor(true, false);
406 UpdateCursors(win
->GetChildren(), display
);
410 void wxEndBusyCursor()
412 if (--gs_busyCount
> 0)
415 g_globalCursor
= gs_savedCursor
;
416 gs_savedCursor
= wxNullCursor
;
417 GdkDisplay
* unused
= NULL
;
418 UpdateCursors(wxTopLevelWindows
, unused
);
421 void wxBeginBusyCursor(const wxCursor
* cursor
)
423 if (gs_busyCount
++ > 0)
426 wxASSERT_MSG( !gs_savedCursor
.IsOk(),
427 wxT("forgot to call wxEndBusyCursor, will leak memory") );
429 gs_savedCursor
= g_globalCursor
;
430 g_globalCursor
= *cursor
;
431 GdkDisplay
* display
= NULL
;
432 UpdateCursors(wxTopLevelWindows
, display
);
434 gdk_display_flush(display
);
439 return gs_busyCount
> 0;
442 void wxSetCursor( const wxCursor
& cursor
)
444 g_globalCursor
= cursor
;
445 GdkDisplay
* unused
= NULL
;
446 UpdateCursors(wxTopLevelWindows
, unused
);