1///////////////////////////////////////////////////////////////////////////// 2// Name: src/gtk/minifram.cpp 3// Purpose: 4// Author: Robert Roebling 5// Id: $Id: minifram.cpp 49712 2007-11-07 10:23:21Z RR $ 6// Copyright: (c) 1998 Robert Roebling 7// Licence: wxWindows licence 8///////////////////////////////////////////////////////////////////////////// 9 10// For compilers that support precompilation, includes "wx.h". 11#include "wx/wxprec.h" 12 13#if wxUSE_MINIFRAME 14 15#include "wx/minifram.h" 16 17#ifndef WX_PRECOMP 18 #include "wx/settings.h" 19 #include "wx/dcclient.h" 20 #include "wx/image.h" 21#endif 22 23#include "wx/gtk/win_gtk.h" 24#include "wx/gtk/private.h" 25 26//----------------------------------------------------------------------------- 27// data 28//----------------------------------------------------------------------------- 29 30extern bool g_blockEventsOnDrag; 31extern bool g_blockEventsOnScroll; 32extern GtkWidget *wxGetRootWindow(); 33 34//----------------------------------------------------------------------------- 35// "expose_event" of m_mainWidget 36//----------------------------------------------------------------------------- 37 38// StepColour() it a utility function that simply darkens 39// or lightens a color, based on the specified percentage 40static wxColor StepColour(const wxColor& c, int percent) 41{ 42 int r = c.Red(), g = c.Green(), b = c.Blue(); 43 return wxColour((unsigned char)wxMin((r*percent)/100,255), 44 (unsigned char)wxMin((g*percent)/100,255), 45 (unsigned char)wxMin((b*percent)/100,255)); 46} 47 48static wxColor LightContrastColour(const wxColour& c) 49{ 50 int amount = 120; 51 52 // if the color is especially dark, then 53 // make the contrast even lighter 54 if (c.Red() < 128 && c.Green() < 128 && c.Blue() < 128) 55 amount = 160; 56 57 return StepColour(c, amount); 58} 59 60extern "C" { 61static gboolean gtk_window_own_expose_callback(GtkWidget* widget, GdkEventExpose* gdk_event, wxMiniFrame* win) 62{ 63 // don't need to install idle handler, its done from "event" signal 64 65 if (!win->m_hasVMT || gdk_event->count > 0) 66 return false; 67 68 GtkPizza *pizza = GTK_PIZZA(widget); 69 70 gtk_paint_shadow (widget->style, 71 pizza->bin_window, 72 GTK_STATE_NORMAL, 73 GTK_SHADOW_OUT, 74 NULL, NULL, NULL, // FIXME: No clipping? 75 0, 0, 76 win->m_width, win->m_height); 77 78 int style = win->GetWindowStyle(); 79 80 wxClientDC dc(win); 81 // Hack alert 82 dc.m_window = pizza->bin_window; 83 84 if (style & wxRESIZE_BORDER) 85 { 86 dc.SetBrush( *wxGREY_BRUSH ); 87 dc.SetPen( *wxTRANSPARENT_PEN ); 88 dc.DrawRectangle( win->m_width - 14, win->m_height-14, 14, 14 ); 89 } 90 91 if (!win->GetTitle().empty() && 92 ((style & wxCAPTION) || 93 (style & wxTINY_CAPTION_HORIZ) || 94 (style & wxTINY_CAPTION_VERT))) 95 { 96 dc.SetFont( *wxSMALL_FONT ); 97 98 wxBrush brush( LightContrastColour( wxSystemSettings::GetColour(wxSYS_COLOUR_HIGHLIGHT) ) ); 99 dc.SetBrush( brush ); 100 dc.SetPen( *wxTRANSPARENT_PEN ); 101 dc.DrawRectangle( win->m_miniEdge-1, 102 win->m_miniEdge-1, 103 win->m_width - (2*(win->m_miniEdge-1)), 104 15 ); 105 106 dc.SetTextForeground( *wxWHITE ); 107 dc.DrawText( win->GetTitle(), 6, 4 ); 108 109 if (style & wxCLOSE_BOX) 110 dc.DrawBitmap( win->m_closeButton, win->m_width-18, 3, true ); 111 } 112 return false; 113} 114} 115 116//----------------------------------------------------------------------------- 117// "button_press_event" of m_mainWidget 118//----------------------------------------------------------------------------- 119 120extern "C" { 121static gint gtk_window_button_press_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxMiniFrame *win ) 122{ 123 // don't need to install idle handler, its done from "event" signal 124 125 if (!win->m_hasVMT) return FALSE; 126 if (g_blockEventsOnDrag) return TRUE; 127 if (g_blockEventsOnScroll) return TRUE; 128 129 if (win->m_isDragging) return TRUE; 130 131 GtkPizza *pizza = GTK_PIZZA(widget); 132 if (gdk_event->window != pizza->bin_window) return TRUE; 133 134 int style = win->GetWindowStyle(); 135 136 int y = (int)gdk_event->y; 137 int x = (int)gdk_event->x; 138 139 if ((style & wxRESIZE_BORDER) && 140 (x > win->m_width-14) && (y > win->m_height-14)) 141 { 142 GtkWidget *ancestor = gtk_widget_get_toplevel( widget ); 143 144 GdkWindow *source = GTK_PIZZA(widget)->bin_window; 145 146 int org_x = 0; 147 int org_y = 0; 148 gdk_window_get_origin( source, &org_x, &org_y ); 149 150 gtk_window_begin_resize_drag (GTK_WINDOW (ancestor), 151 GDK_WINDOW_EDGE_SOUTH_EAST, 152 1, 153 org_x + x, 154 org_y + y, 155 0); 156 157 return TRUE; 158 } 159 160 if ((style & wxCLOSE_BOX) && 161 ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT))) 162 { 163 if ((y > 3) && (y < 19) && (x > win->m_width-19) && (x < win->m_width-3)) 164 { 165 win->Close(); 166 return TRUE; 167 } 168 } 169 170 if (y > win->m_miniEdge-1 + 15) return TRUE; 171 172 gdk_window_raise( win->m_widget->window ); 173 174 gdk_pointer_grab( widget->window, FALSE, 175 (GdkEventMask) 176 (GDK_BUTTON_PRESS_MASK | 177 GDK_BUTTON_RELEASE_MASK | 178 GDK_POINTER_MOTION_MASK | 179 GDK_POINTER_MOTION_HINT_MASK | 180 GDK_BUTTON_MOTION_MASK | 181 GDK_BUTTON1_MOTION_MASK), 182 (GdkWindow *) NULL, 183 (GdkCursor *) NULL, 184 (unsigned int) GDK_CURRENT_TIME ); 185 186 win->m_diffX = x; 187 win->m_diffY = y; 188 win->m_oldX = 0; 189 win->m_oldY = 0; 190 191 win->m_isDragging = true; 192 193 return TRUE; 194} 195} 196 197//----------------------------------------------------------------------------- 198// "button_release_event" of m_mainWidget 199//----------------------------------------------------------------------------- 200 201extern "C" { 202static gint gtk_window_button_release_callback( GtkWidget *widget, GdkEventButton *gdk_event, wxMiniFrame *win ) 203{ 204 // don't need to install idle handler, its done from "event" signal 205 206 if (!win->m_hasVMT) return FALSE; 207 if (g_blockEventsOnDrag) return TRUE; 208 if (g_blockEventsOnScroll) return TRUE; 209 210 if (!win->m_isDragging) return TRUE; 211 212 win->m_isDragging = false; 213 214 int x = (int)gdk_event->x; 215 int y = (int)gdk_event->y; 216 217 gdk_pointer_ungrab ( (guint32)GDK_CURRENT_TIME ); 218 int org_x = 0; 219 int org_y = 0; 220 gdk_window_get_origin( widget->window, &org_x, &org_y ); 221 x += org_x - win->m_diffX; 222 y += org_y - win->m_diffY; 223 win->m_x = x; 224 win->m_y = y; 225 gtk_window_move( GTK_WINDOW(win->m_widget), x, y ); 226 227 return TRUE; 228} 229} 230 231//----------------------------------------------------------------------------- 232// "leave_notify_event" of m_mainWidget 233//----------------------------------------------------------------------------- 234 235extern "C" { 236static gboolean 237gtk_window_leave_callback( GtkWidget *widget, GdkEventCrossing *gdk_event, wxMiniFrame *win ) 238{ 239 // don't need to install idle handler, its done from "event" signal 240 241 if (!win->m_hasVMT) return FALSE; 242 if (g_blockEventsOnDrag) return FALSE; 243 244 gdk_window_set_cursor( widget->window, NULL ); 245 246 return FALSE; 247} 248} 249 250//----------------------------------------------------------------------------- 251// "motion_notify_event" of m_mainWidget 252//----------------------------------------------------------------------------- 253 254extern "C" { 255static gint 256gtk_window_motion_notify_callback( GtkWidget *widget, GdkEventMotion *gdk_event, wxMiniFrame *win ) 257{ 258 // don't need to install idle handler, its done from "event" signal 259 260 if (!win->m_hasVMT) return FALSE; 261 if (g_blockEventsOnDrag) return TRUE; 262 if (g_blockEventsOnScroll) return TRUE; 263 264 if (gdk_event->is_hint) 265 { 266 int x = 0; 267 int y = 0; 268 GdkModifierType state; 269 gdk_window_get_pointer(gdk_event->window, &x, &y, &state); 270 gdk_event->x = x; 271 gdk_event->y = y; 272 gdk_event->state = state; 273 } 274 275 int style = win->GetWindowStyle(); 276 277 int x = (int)gdk_event->x; 278 int y = (int)gdk_event->y; 279 280 if (!win->m_isDragging) 281 { 282 if (style & wxRESIZE_BORDER) 283 { 284 if ((x > win->m_width-14) && (y > win->m_height-14)) 285 gdk_window_set_cursor( widget->window, gdk_cursor_new( GDK_BOTTOM_RIGHT_CORNER ) ); 286 else 287 gdk_window_set_cursor( widget->window, NULL ); 288 } 289 return TRUE; 290 } 291 292 win->m_oldX = x - win->m_diffX; 293 win->m_oldY = y - win->m_diffY; 294 295 int org_x = 0; 296 int org_y = 0; 297 gdk_window_get_origin( widget->window, &org_x, &org_y ); 298 x += org_x - win->m_diffX; 299 y += org_y - win->m_diffY; 300 win->m_x = x; 301 win->m_y = y; 302 gtk_window_move( GTK_WINDOW(win->m_widget), x, y ); 303 304 305 return TRUE; 306} 307} 308 309//----------------------------------------------------------------------------- 310// wxMiniFrame 311//----------------------------------------------------------------------------- 312 313static unsigned char close_bits[]={ 314 0xff, 0xff, 0xff, 0xff, 0x07, 0xf0, 0xfb, 0xef, 0xdb, 0xed, 0x8b, 0xe8, 315 0x1b, 0xec, 0x3b, 0xee, 0x1b, 0xec, 0x8b, 0xe8, 0xdb, 0xed, 0xfb, 0xef, 316 0x07, 0xf0, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; 317 318 319IMPLEMENT_DYNAMIC_CLASS(wxMiniFrame,wxFrame) 320 321bool wxMiniFrame::Create( wxWindow *parent, wxWindowID id, const wxString &title, 322 const wxPoint &pos, const wxSize &size, 323 long style, const wxString &name ) 324{ 325 if ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT)) 326 m_miniTitle = 16; 327 328 if (style & wxRESIZE_BORDER) 329 m_miniEdge = 4; 330 else 331 m_miniEdge = 3; 332 m_isDragging = false; 333 m_oldX = -1; 334 m_oldY = -1; 335 m_diffX = 0; 336 m_diffY = 0; 337 338 wxFrame::Create( parent, id, title, pos, size, style, name ); 339 340 if (m_parent && (GTK_IS_WINDOW(m_parent->m_widget))) 341 { 342 gtk_window_set_transient_for( GTK_WINDOW(m_widget), GTK_WINDOW(m_parent->m_widget) ); 343 } 344 345 if ((style & wxCLOSE_BOX) && 346 ((style & wxCAPTION) || (style & wxTINY_CAPTION_HORIZ) || (style & wxTINY_CAPTION_VERT))) 347 { 348 wxImage img = wxBitmap((const char*)close_bits, 16, 16).ConvertToImage(); 349 img.Replace(0,0,0,123,123,123); 350 img.SetMaskColour(123,123,123); 351 m_closeButton = wxBitmap( img ); 352 } 353 354 /* these are called when the borders are drawn */ 355 g_signal_connect (m_mainWidget, "expose_event", 356 G_CALLBACK (gtk_window_own_expose_callback), this ); 357 358 /* these are required for dragging the mini frame around */ 359 g_signal_connect (m_mainWidget, "button_press_event", 360 G_CALLBACK (gtk_window_button_press_callback), this); 361 g_signal_connect (m_mainWidget, "button_release_event", 362 G_CALLBACK (gtk_window_button_release_callback), this); 363 g_signal_connect (m_mainWidget, "motion_notify_event", 364 G_CALLBACK (gtk_window_motion_notify_callback), this); 365 g_signal_connect (m_mainWidget, "leave_notify_event", 366 G_CALLBACK (gtk_window_leave_callback), this); 367 return true; 368} 369 370void wxMiniFrame::SetTitle( const wxString &title ) 371{ 372 wxFrame::SetTitle( title ); 373 374 if (GTK_PIZZA(m_mainWidget)->bin_window) 375 gdk_window_invalidate_rect( GTK_PIZZA(m_mainWidget)->bin_window, NULL, true ); 376} 377 378#endif // wxUSE_MINIFRAME 379