]> git.saurik.com Git - wxWidgets.git/blob - src/gtk/dialog.cpp
wxMotif::wxFont supports encodings too (and shares 99% of font code with wxGTK)
[wxWidgets.git] / src / gtk / dialog.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: dialog.cpp
3 // Purpose:
4 // Author: Robert Roebling
5 // Id: $Id$
6 // Copyright: (c) 1998 Robert Roebling
7 // Licence: wxWindows licence
8 /////////////////////////////////////////////////////////////////////////////
9
10 #ifdef __GNUG__
11 #pragma implementation "dialog.h"
12 #endif
13
14 #include "wx/dialog.h"
15 #include "wx/frame.h"
16 #include "wx/app.h"
17
18 #include "gdk/gdk.h"
19 #include "gtk/gtk.h"
20 #include "wx/gtk/win_gtk.h"
21 #include "wx/cursor.h"
22
23 /*
24 #include "gdk/gdkprivate.h"
25 #include "gdk/gdkx.h"
26 */
27
28 //-----------------------------------------------------------------------------
29 // idle system
30 //-----------------------------------------------------------------------------
31
32 extern void wxapp_install_idle_handler();
33 extern bool g_isIdle;
34
35 //-----------------------------------------------------------------------------
36 // data
37 //-----------------------------------------------------------------------------
38
39 extern wxList wxPendingDelete;
40
41 /*
42 //-----------------------------------------------------------------------------
43 // instruct X to set the WM hint for positioning
44 //-----------------------------------------------------------------------------
45
46 extern "C" {
47
48 static void gdk_window_set_position_hint( GdkWindow *window, gint x, gint y )
49 {
50 GdkWindowPrivate *priv;
51 XSizeHints size_hints;
52
53 g_return_if_fail (window != NULL);
54
55 priv = (GdkWindowPrivate*) window;
56 if (priv->destroyed) return;
57
58 size_hints.flags = PPosition;
59 size_hints.x = x;
60 size_hints.y = y;
61
62 XSetWMNormalHints (priv->xdisplay, priv->xwindow, &size_hints);
63 }
64
65 }
66 */
67
68 //-----------------------------------------------------------------------------
69 // "delete_event"
70 //-----------------------------------------------------------------------------
71
72 bool gtk_dialog_delete_callback( GtkWidget *WXUNUSED(widget), GdkEvent *WXUNUSED(event), wxDialog *win )
73 {
74 if (g_isIdle)
75 wxapp_install_idle_handler();
76
77 win->Close();
78
79 return TRUE;
80 }
81
82 //-----------------------------------------------------------------------------
83 // "size_allocate"
84 //-----------------------------------------------------------------------------
85
86 static void gtk_dialog_size_callback( GtkWidget *WXUNUSED(widget), GtkAllocation* alloc, wxDialog *win )
87 {
88 if (g_isIdle)
89 wxapp_install_idle_handler();
90
91 if (!win->m_hasVMT) return;
92
93 if ((win->m_width != alloc->width) || (win->m_height != alloc->height))
94 {
95 win->m_width = alloc->width;
96 win->m_height = alloc->height;
97 win->UpdateSize();
98 }
99 }
100
101 //-----------------------------------------------------------------------------
102 // "configure_event"
103 //-----------------------------------------------------------------------------
104
105 static gint
106 #if (GTK_MINOR_VERSON > 0)
107 gtk_dialog_configure_callback( GtkWidget *WXUNUSED(widget), GdkEventConfigure *WXUNUSED(event), wxDialog *win )
108 #else
109 gtk_dialog_configure_callback( GtkWidget *WXUNUSED(widget), GdkEventConfigure *event, wxDialog *win )
110 #endif
111 {
112 if (g_isIdle)
113 wxapp_install_idle_handler();
114
115 if (!win->m_hasVMT) return FALSE;
116
117 #if (GTK_MINOR_VERSON > 0)
118 int x = 0;
119 int y = 0;
120 gdk_window_get_root_origin( win->m_widget->window, &x, &y );
121 win->m_x = x;
122 win->m_y = y;
123 #else
124 win->m_x = event->x;
125 win->m_y = event->y;
126 #endif
127
128 wxMoveEvent mevent( wxPoint(win->m_x,win->m_y), win->GetId() );
129 mevent.SetEventObject( win );
130 win->GetEventHandler()->ProcessEvent( mevent );
131
132 return FALSE;
133 }
134
135 //-----------------------------------------------------------------------------
136 // "realize" from m_widget
137 //-----------------------------------------------------------------------------
138
139 /* we cannot MWM hints and icons before the widget has been realized,
140 so we do this directly after realization */
141
142 static gint
143 gtk_dialog_realized_callback( GtkWidget *widget, wxDialog *win )
144 {
145 if (g_isIdle)
146 wxapp_install_idle_handler();
147
148 /* I haven''t been able to set the position of
149 the dialog before it is shown, so I set the
150 position in "realize" and "map" */
151 gtk_widget_set_uposition( widget, win->m_x, win->m_y );
152
153 /*
154 gdk_window_set_position_hint( widget->window, win->m_x, win->m_y );
155 */
156
157 /* reset the icon */
158 if (win->m_icon != wxNullIcon)
159 {
160 wxIcon icon( win->m_icon );
161 win->m_icon = wxNullIcon;
162 win->SetIcon( icon );
163 }
164
165 return FALSE;
166 }
167
168 //-----------------------------------------------------------------------------
169 // "map" from m_widget
170 //-----------------------------------------------------------------------------
171
172 static gint
173 gtk_dialog_map_callback( GtkWidget *widget, wxDialog *win )
174 {
175 if (g_isIdle)
176 wxapp_install_idle_handler();
177
178 /* I haven''t been able to set the position of
179 the dialog before it is shown, so I set the
180 position in "realize" and "map" */
181 gtk_widget_set_uposition( widget, win->m_x, win->m_y );
182
183 /* all this is for Motif Window Manager "hints" and is supposed to be
184 recognized by other WM as well. not tested. */
185 long decor = (long) GDK_DECOR_BORDER;
186 long func = (long) GDK_FUNC_MOVE ;
187
188 if ((win->GetWindowStyle() & wxCAPTION) != 0)
189 decor |= GDK_DECOR_TITLE;
190 if ((win->GetWindowStyle() & wxSYSTEM_MENU) != 0)
191 {
192 decor |= GDK_DECOR_MENU;
193 func |= GDK_FUNC_CLOSE;
194 }
195 if ((win->GetWindowStyle() & wxMINIMIZE_BOX) != 0)
196 {
197 func |= GDK_FUNC_MINIMIZE;
198 decor |= GDK_DECOR_MINIMIZE;
199 }
200 if ((win->GetWindowStyle() & wxMAXIMIZE_BOX) != 0)
201 {
202 decor |= GDK_DECOR_MAXIMIZE;
203 func |= GDK_FUNC_MAXIMIZE;
204 }
205 if ((win->GetWindowStyle() & wxRESIZE_BORDER) != 0)
206 {
207 func |= GDK_FUNC_RESIZE;
208 decor |= GDK_DECOR_RESIZEH;
209 }
210 gdk_window_set_decorations( win->m_widget->window, (GdkWMDecoration)decor);
211 gdk_window_set_functions( win->m_widget->window, (GdkWMFunction)func);
212
213 /* GTK's shrinking/growing policy */
214 if ((win->GetWindowStyle() & wxRESIZE_BORDER) == 0)
215 gtk_window_set_policy(GTK_WINDOW(win->m_widget), 0, 0, 1);
216 else
217 gtk_window_set_policy(GTK_WINDOW(win->m_widget), 1, 1, 1);
218
219 return FALSE;
220 }
221
222 //-----------------------------------------------------------------------------
223 // InsertChild for wxDialog
224 //-----------------------------------------------------------------------------
225
226 /* Callback for wxFrame. This very strange beast has to be used because
227 * C++ has no virtual methods in a constructor. We have to emulate a
228 * virtual function here as wxWindows requires different ways to insert
229 * a child in container classes. */
230
231 static void wxInsertChildInDialog( wxDialog* parent, wxWindow* child )
232 {
233 gtk_myfixed_put( GTK_MYFIXED(parent->m_wxwindow),
234 GTK_WIDGET(child->m_widget),
235 child->m_x,
236 child->m_y,
237 child->m_width,
238 child->m_height );
239
240 if (parent->HasFlag(wxTAB_TRAVERSAL))
241 {
242 /* we now allow a window to get the focus as long as it
243 doesn't have any children. */
244 GTK_WIDGET_UNSET_FLAGS( parent->m_wxwindow, GTK_CAN_FOCUS );
245 }
246 }
247
248 //-----------------------------------------------------------------------------
249 // wxDialog
250 //-----------------------------------------------------------------------------
251
252 BEGIN_EVENT_TABLE(wxDialog,wxPanel)
253 EVT_BUTTON (wxID_OK, wxDialog::OnOK)
254 EVT_BUTTON (wxID_CANCEL, wxDialog::OnCancel)
255 EVT_BUTTON (wxID_APPLY, wxDialog::OnApply)
256 EVT_SIZE (wxDialog::OnSize)
257 EVT_CLOSE (wxDialog::OnCloseWindow)
258 END_EVENT_TABLE()
259
260 IMPLEMENT_DYNAMIC_CLASS(wxDialog,wxPanel)
261
262 void wxDialog::Init()
263 {
264 m_returnCode = 0;
265 m_sizeSet = FALSE;
266 m_modalShowing = FALSE;
267 }
268
269 wxDialog::wxDialog( wxWindow *parent,
270 wxWindowID id, const wxString &title,
271 const wxPoint &pos, const wxSize &size,
272 long style, const wxString &name )
273 {
274 Init();
275
276 Create( parent, id, title, pos, size, style, name );
277 }
278
279 bool wxDialog::Create( wxWindow *parent,
280 wxWindowID id, const wxString &title,
281 const wxPoint &pos, const wxSize &size,
282 long style, const wxString &name )
283 {
284 wxTopLevelWindows.Append( this );
285
286 m_needParent = FALSE;
287
288 if (!PreCreation( parent, pos, size ) ||
289 !CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
290 {
291 wxFAIL_MSG( _T("wxDialog creation failed") );
292 return FALSE;
293 }
294
295 m_insertCallback = (wxInsertChildFunction) wxInsertChildInDialog;
296
297 m_widget = gtk_window_new( GTK_WINDOW_TOPLEVEL );
298
299 if (!name.IsEmpty())
300 gtk_window_set_wmclass( GTK_WINDOW(m_widget), name.mb_str(), name.mb_str() );
301
302 GTK_WIDGET_UNSET_FLAGS( m_widget, GTK_CAN_FOCUS );
303
304 gtk_signal_connect( GTK_OBJECT(m_widget), "delete_event",
305 GTK_SIGNAL_FUNC(gtk_dialog_delete_callback), (gpointer)this );
306
307 m_wxwindow = gtk_myfixed_new();
308 gtk_widget_show( m_wxwindow );
309 GTK_WIDGET_UNSET_FLAGS( m_wxwindow, GTK_CAN_FOCUS );
310
311 gtk_container_add( GTK_CONTAINER(m_widget), m_wxwindow );
312
313 SetTitle( title );
314
315 if (m_parent) m_parent->AddChild( this );
316
317 PostCreation();
318
319 /* we cannot set MWM hints before the widget has
320 been realized, so we do this directly after realization */
321 gtk_signal_connect( GTK_OBJECT(m_widget), "realize",
322 GTK_SIGNAL_FUNC(gtk_dialog_realized_callback), (gpointer) this );
323
324 /* we set the position of the window after the map event. setting it
325 before has no effect (with KWM) */
326 gtk_signal_connect( GTK_OBJECT(m_widget), "map",
327 GTK_SIGNAL_FUNC(gtk_dialog_map_callback), (gpointer) this );
328
329 /* the user resized the frame by dragging etc. */
330 gtk_signal_connect( GTK_OBJECT(m_widget), "size_allocate",
331 GTK_SIGNAL_FUNC(gtk_dialog_size_callback), (gpointer)this );
332
333 gtk_signal_connect( GTK_OBJECT(m_widget), "configure_event",
334 GTK_SIGNAL_FUNC(gtk_dialog_configure_callback), (gpointer)this );
335
336 return TRUE;
337 }
338
339 wxDialog::~wxDialog()
340 {
341 m_isBeingDeleted = TRUE;
342
343 wxTopLevelWindows.DeleteObject( this );
344
345 if (wxTheApp->GetTopWindow() == this)
346 {
347 wxTheApp->SetTopWindow( (wxWindow*) NULL );
348 }
349
350 if (wxTopLevelWindows.Number() == 0)
351 {
352 wxTheApp->ExitMainLoop();
353 }
354 }
355
356 void wxDialog::SetTitle( const wxString& title )
357 {
358 m_title = title;
359 if (m_title.IsNull()) m_title = _T("");
360 gtk_window_set_title( GTK_WINDOW(m_widget), m_title.mbc_str() );
361 }
362
363 wxString wxDialog::GetTitle() const
364 {
365 return (wxString&)m_title;
366 }
367
368 void wxDialog::OnApply( wxCommandEvent &WXUNUSED(event) )
369 {
370 if (Validate()) TransferDataFromWindow();
371 }
372
373 void wxDialog::OnCancel( wxCommandEvent &WXUNUSED(event) )
374 {
375 if (IsModal())
376 {
377 EndModal(wxID_CANCEL);
378 }
379 else
380 {
381 SetReturnCode(wxID_CANCEL);
382 Show(FALSE);
383 }
384 }
385
386 void wxDialog::OnOK( wxCommandEvent &WXUNUSED(event) )
387 {
388 if (Validate() && TransferDataFromWindow())
389 {
390 if (IsModal())
391 {
392 EndModal(wxID_OK);
393 }
394 else
395 {
396 SetReturnCode(wxID_OK);
397 this->Show(FALSE);
398 }
399 }
400 }
401
402 void wxDialog::OnPaint( wxPaintEvent& WXUNUSED(event) )
403 {
404 // yes
405 }
406
407 void wxDialog::OnCloseWindow(wxCloseEvent& event)
408 {
409 // We'll send a Cancel message by default,
410 // which may close the dialog.
411 // Check for looping if the Cancel event handler calls Close().
412
413 // Note that if a cancel button and handler aren't present in the dialog,
414 // nothing will happen when you close the dialog via the window manager, or
415 // via Close().
416 // We wouldn't want to destroy the dialog by default, since the dialog may have been
417 // created on the stack.
418 // However, this does mean that calling dialog->Close() won't delete the dialog
419 // unless the handler for wxID_CANCEL does so. So use Destroy() if you want to be
420 // sure to destroy the dialog.
421 // The default OnCancel (above) simply ends a modal dialog, and hides a modeless dialog.
422
423 static wxList s_closing;
424
425 if (s_closing.Member(this))
426 return; // no loops
427
428 s_closing.Append(this);
429
430 wxCommandEvent cancelEvent(wxEVT_COMMAND_BUTTON_CLICKED, wxID_CANCEL);
431 cancelEvent.SetEventObject( this );
432 GetEventHandler()->ProcessEvent(cancelEvent);
433 s_closing.DeleteObject(this);
434 }
435
436 bool wxDialog::Destroy()
437 {
438 if (!wxPendingDelete.Member(this)) wxPendingDelete.Append(this);
439
440 return TRUE;
441 }
442
443 void wxDialog::OnSize( wxSizeEvent &WXUNUSED(event) )
444 {
445 wxASSERT_MSG( (m_widget != NULL), _T("invalid dialog") );
446
447 #if wxUSE_CONSTRAINTS
448 if (GetAutoLayout())
449 {
450 Layout();
451 }
452 else
453 #endif // wxUSE_CONSTRAINTS
454 {
455 /* no child: go out ! */
456 if (!GetChildren().First()) return;
457
458 /* do we have exactly one child? */
459 wxWindow *child = (wxWindow *) NULL;
460 for(wxNode *node = GetChildren().First(); node; node = node->Next())
461 {
462 wxWindow *win = (wxWindow *)node->Data();
463 if (!wxIS_KIND_OF(win,wxFrame) && !wxIS_KIND_OF(win,wxDialog))
464 {
465 /* it's the second one: do nothing */
466 if (child) return;
467 child = win;
468 }
469 }
470
471 /* yes: set it's size to fill all the frame */
472 int client_x, client_y;
473 GetClientSize( &client_x, &client_y );
474 child->SetSize( 1, 1, client_x-2, client_y);
475 }
476 }
477
478 void wxDialog::DoSetSize( int x, int y, int width, int height, int sizeFlags )
479 {
480 wxASSERT_MSG( (m_widget != NULL), _T("invalid dialog") );
481 wxASSERT_MSG( (m_wxwindow != NULL), _T("invalid dialog") );
482
483 if (m_resizing) return; /* I don't like recursions */
484 m_resizing = TRUE;
485
486 int old_x = m_x;
487 int old_y = m_y;
488 int old_width = m_width;
489 int old_height = m_height;
490
491 if ((sizeFlags & wxSIZE_ALLOW_MINUS_ONE) == 0)
492 {
493 if (x != -1) m_x = x;
494 if (y != -1) m_y = y;
495 if (width != -1) m_width = width;
496 if (height != -1) m_height = height;
497 }
498 else
499 {
500 m_x = x;
501 m_y = y;
502 m_width = width;
503 m_height = height;
504 }
505
506 if ((sizeFlags & wxSIZE_AUTO_WIDTH) == wxSIZE_AUTO_WIDTH)
507 {
508 if (width == -1) m_width = 80;
509 }
510
511 if ((sizeFlags & wxSIZE_AUTO_HEIGHT) == wxSIZE_AUTO_HEIGHT)
512 {
513 if (height == -1) m_height = 26;
514 }
515
516 if ((m_minWidth != -1) && (m_width < m_minWidth)) m_width = m_minWidth;
517 if ((m_minHeight != -1) && (m_height < m_minHeight)) m_height = m_minHeight;
518 if ((m_maxWidth != -1) && (m_width > m_maxWidth)) m_width = m_maxWidth;
519 if ((m_maxHeight != -1) && (m_height > m_maxHeight)) m_height = m_maxHeight;
520
521 if ((m_x != -1) || (m_y != -1))
522 {
523 if ((m_x != old_x) || (m_y != old_y))
524 {
525 /* we set the position here and when showing the dialog
526 for the first time in idle time */
527 gtk_widget_set_uposition( m_widget, m_x, m_y );
528 }
529 }
530
531 if ((m_width != old_width) || (m_height != old_height))
532 {
533 /* actual resizing is deferred to GtkOnSize in idle time and
534 when showing the dialog */
535 m_sizeSet = FALSE;
536 }
537
538 m_resizing = FALSE;
539 }
540
541 void wxDialog::GtkOnSize( int WXUNUSED(x), int WXUNUSED(y), int width, int height )
542 {
543 // due to a bug in gtk, x,y are always 0
544 // m_x = x;
545 // m_y = y;
546
547 if ((m_height == height) && (m_width == width) && (m_sizeSet)) return;
548 if (!m_wxwindow) return;
549
550 m_width = width;
551 m_height = height;
552
553 if ((m_minWidth != -1) && (m_width < m_minWidth)) m_width = m_minWidth;
554 if ((m_minHeight != -1) && (m_height < m_minHeight)) m_height = m_minHeight;
555 if ((m_maxWidth != -1) && (m_width > m_maxWidth)) m_width = m_maxWidth;
556 if ((m_maxHeight != -1) && (m_height > m_maxHeight)) m_height = m_maxHeight;
557
558 /* we actually set the size of a frame here and no-where else */
559 gtk_widget_set_usize( m_widget, m_width, m_height );
560
561 m_sizeSet = TRUE;
562
563 wxSizeEvent event( wxSize(m_width,m_height), GetId() );
564 event.SetEventObject( this );
565 GetEventHandler()->ProcessEvent( event );
566 }
567
568 void wxDialog::Centre( int direction )
569 {
570 wxASSERT_MSG( (m_widget != NULL), _T("invalid dialog") );
571
572 int x = 0;
573 int y = 0;
574
575 if ((direction & wxHORIZONTAL) == wxHORIZONTAL) x = (gdk_screen_width () - m_width) / 2;
576 if ((direction & wxVERTICAL) == wxVERTICAL) y = (gdk_screen_height () - m_height) / 2;
577
578 Move( x, y );
579 }
580
581 void wxDialog::OnInternalIdle()
582 {
583 if (!m_sizeSet && GTK_WIDGET_REALIZED(m_wxwindow))
584 GtkOnSize( m_x, m_y, m_width, m_height );
585
586 wxWindow::OnInternalIdle();
587 }
588
589 bool wxDialog::Show( bool show )
590 {
591 if (!show && IsModal())
592 {
593 EndModal( wxID_CANCEL );
594 }
595
596 if (show && !m_sizeSet)
597 {
598 /* by calling GtkOnSize here, we don't have to call
599 either after showing the frame, which would entail
600 much ugly flicker nor from within the size_allocate
601 handler, because GTK 1.1.X forbids that. */
602
603 GtkOnSize( m_x, m_y, m_width, m_height );
604 }
605
606 bool ret = wxWindow::Show( show );
607
608 if (show) InitDialog();
609
610 return ret;
611 }
612
613 bool wxDialog::IsModal() const
614 {
615 return m_modalShowing;
616 }
617
618 void wxDialog::SetModal( bool WXUNUSED(flag) )
619 {
620 /*
621 if (flag)
622 m_windowStyle |= wxDIALOG_MODAL;
623 else
624 if (m_windowStyle & wxDIALOG_MODAL) m_windowStyle -= wxDIALOG_MODAL;
625 */
626 wxFAIL_MSG( _T("wxDialog:SetModal obsolete now") );
627 }
628
629 int wxDialog::ShowModal()
630 {
631 if (IsModal())
632 {
633 wxFAIL_MSG( _T("wxDialog:ShowModal called twice") );
634 return GetReturnCode();
635 }
636
637 wxBusyCursorSuspender cs; // temporarily suppress the busy cursor
638
639 Show( TRUE );
640
641 m_modalShowing = TRUE;
642
643 gtk_grab_add( m_widget );
644 gtk_main();
645 gtk_grab_remove( m_widget );
646
647 return GetReturnCode();
648 }
649
650 void wxDialog::EndModal( int retCode )
651 {
652 SetReturnCode( retCode );
653
654 if (!IsModal())
655 {
656 wxFAIL_MSG( _T("wxDialog:EndModal called twice") );
657 return;
658 }
659
660 m_modalShowing = FALSE;
661
662 gtk_main_quit();
663
664 Show( FALSE );
665 }
666
667 void wxDialog::InitDialog()
668 {
669 wxWindow::InitDialog();
670 }
671
672 void wxDialog::SetIcon( const wxIcon &icon )
673 {
674 m_icon = icon;
675 if (!icon.Ok()) return;
676
677 if (!m_widget->window) return;
678
679 wxMask *mask = icon.GetMask();
680 GdkBitmap *bm = (GdkBitmap *) NULL;
681 if (mask) bm = mask->GetBitmap();
682
683 gdk_window_set_icon( m_widget->window, (GdkWindow *) NULL, icon.GetPixmap(), bm );
684 }