This keyword is not expanded by Git which means it's not replaced with the correct revision value in the releases made using git-based scripts and it's confusing to have lines with unexpanded "$Id$" in the released files. As expanding them with Git is not that simple (it could be done with git archive and export-subst attribute) and there are not many benefits in having them in the first place, just remove all these lines. If nothing else, this will make an eventual transition to Git simpler. Closes #14487. git-svn-id: https://svn.wxwidgets.org/svn/wx/wxWidgets/trunk@74602 c3d73ce0-8a6f-49c7-b76d-6d57e0e08775
855 lines
26 KiB
C++
855 lines
26 KiB
C++
/////////////////////////////////////////////////////////////////////////////
|
|
// Name: src/gtk1/notebook.cpp
|
|
// Purpose:
|
|
// Author: Robert Roebling
|
|
// Copyright: (c) 1998 Robert Roebling, Vadim Zeitlin
|
|
// Licence: wxWindows licence
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
// For compilers that support precompilation, includes "wx.h".
|
|
#include "wx/wxprec.h"
|
|
|
|
#if wxUSE_NOTEBOOK
|
|
|
|
#include "wx/notebook.h"
|
|
|
|
#ifndef WX_PRECOMP
|
|
#include "wx/intl.h"
|
|
#include "wx/log.h"
|
|
#include "wx/utils.h"
|
|
#include "wx/panel.h"
|
|
#include "wx/msgdlg.h"
|
|
#include "wx/bitmap.h"
|
|
#endif
|
|
|
|
#include "wx/imaglist.h"
|
|
#include "wx/fontutil.h"
|
|
|
|
#include "wx/gtk1/private.h"
|
|
#include "wx/gtk1/win_gtk.h"
|
|
|
|
#include <gdk/gdkkeysyms.h>
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// idle system
|
|
//-----------------------------------------------------------------------------
|
|
|
|
extern void wxapp_install_idle_handler();
|
|
extern bool g_isIdle;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// data
|
|
//-----------------------------------------------------------------------------
|
|
|
|
extern bool g_blockEventsOnDrag;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxGtkNotebookPage
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// VZ: this is rather ugly as we keep the pages themselves in an array (it
|
|
// allows us to have quite a few functions implemented in the base class)
|
|
// but the page data is kept in a separate list, so we must maintain them
|
|
// in sync manually... of course, the list had been there before the base
|
|
// class which explains it but it still would be nice to do something
|
|
// about this one day
|
|
|
|
class wxGtkNotebookPage: public wxObject
|
|
{
|
|
public:
|
|
wxGtkNotebookPage()
|
|
{
|
|
m_image = -1;
|
|
m_page = NULL;
|
|
m_box = NULL;
|
|
}
|
|
|
|
wxString m_text;
|
|
int m_image;
|
|
GtkNotebookPage *m_page;
|
|
GtkLabel *m_label;
|
|
GtkWidget *m_box; // in which the label and image are packed
|
|
};
|
|
|
|
|
|
#include "wx/listimpl.cpp"
|
|
WX_DEFINE_LIST(wxGtkNotebookPagesList)
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// "switch_page"
|
|
//-----------------------------------------------------------------------------
|
|
|
|
extern "C" {
|
|
static void gtk_notebook_page_change_callback(GtkNotebook *WXUNUSED(widget),
|
|
GtkNotebookPage *WXUNUSED(page),
|
|
gint page,
|
|
wxNotebook *notebook )
|
|
{
|
|
// are you trying to call SetSelection() from a notebook event handler?
|
|
// you shouldn't!
|
|
wxCHECK_RET( !notebook->m_inSwitchPage,
|
|
wxT("gtk_notebook_page_change_callback reentered") );
|
|
|
|
notebook->m_inSwitchPage = true;
|
|
if (g_isIdle)
|
|
wxapp_install_idle_handler();
|
|
|
|
int old = notebook->GetSelection();
|
|
|
|
if (notebook->m_skipNextPageChangeEvent)
|
|
{
|
|
// this event was programmatically generated by ChangeSelection() and thus must
|
|
// be skipped
|
|
notebook->m_skipNextPageChangeEvent = false;
|
|
|
|
// make wxNotebook::GetSelection() return the correct (i.e. consistent
|
|
// with wxBookCtrlEvent::GetSelection()) value even though the page is
|
|
// not really changed in GTK+
|
|
notebook->SetSelection(page);
|
|
}
|
|
else
|
|
{
|
|
if ( !notebook->SendPageChangingEvent(page) )
|
|
{
|
|
// program doesn't allow the page change
|
|
gtk_signal_emit_stop_by_name(GTK_OBJECT(notebook->m_widget), "switch_page");
|
|
}
|
|
else // change allowed
|
|
{
|
|
// make wxNotebook::GetSelection() return the correct (i.e. consistent
|
|
// with wxBookCtrlEvent::GetSelection()) value even though the page is
|
|
// not really changed in GTK+
|
|
notebook->SetSelection(page);
|
|
|
|
notebook->SendPageChangedEvent(old);
|
|
}
|
|
}
|
|
|
|
notebook->m_inSwitchPage = FALSE;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// "size_allocate"
|
|
//-----------------------------------------------------------------------------
|
|
|
|
extern "C" {
|
|
static void gtk_page_size_callback( GtkWidget *WXUNUSED(widget), GtkAllocation* alloc, wxWindow *win )
|
|
{
|
|
if (g_isIdle)
|
|
wxapp_install_idle_handler();
|
|
|
|
if ((win->m_x == alloc->x) &&
|
|
(win->m_y == alloc->y) &&
|
|
(win->m_width == alloc->width) &&
|
|
(win->m_height == alloc->height))
|
|
{
|
|
return;
|
|
}
|
|
|
|
win->SetSize( alloc->x, alloc->y, alloc->width, alloc->height );
|
|
|
|
/* GTK 1.2 up to version 1.2.5 is broken so that we have to call allocate
|
|
here in order to make repositioning after resizing to take effect. */
|
|
if ((gtk_major_version == 1) &&
|
|
(gtk_minor_version == 2) &&
|
|
(gtk_micro_version < 6) &&
|
|
(win->m_wxwindow) &&
|
|
(GTK_WIDGET_REALIZED(win->m_wxwindow)))
|
|
{
|
|
gtk_widget_size_allocate( win->m_wxwindow, alloc );
|
|
}
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// "realize" from m_widget
|
|
//-----------------------------------------------------------------------------
|
|
|
|
extern "C" {
|
|
static gint
|
|
gtk_notebook_realized_callback( GtkWidget * WXUNUSED(widget), wxWindow *win )
|
|
{
|
|
if (g_isIdle)
|
|
wxapp_install_idle_handler();
|
|
|
|
/* GTK 1.2 up to version 1.2.5 is broken so that we have to call a queue_resize
|
|
here in order to make repositioning before showing to take effect. */
|
|
gtk_widget_queue_resize( win->m_widget );
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// "key_press_event"
|
|
//-----------------------------------------------------------------------------
|
|
|
|
extern "C" {
|
|
static gint gtk_notebook_key_press_callback( GtkWidget *widget, GdkEventKey *gdk_event, wxNotebook *notebook )
|
|
{
|
|
if (g_isIdle)
|
|
wxapp_install_idle_handler();
|
|
|
|
if (!notebook->m_hasVMT) return FALSE;
|
|
if (g_blockEventsOnDrag) return FALSE;
|
|
|
|
/* win is a control: tab can be propagated up */
|
|
if ((gdk_event->keyval == GDK_Left) || (gdk_event->keyval == GDK_Right))
|
|
{
|
|
int page;
|
|
int nMax = notebook->GetPageCount();
|
|
if ( nMax-- ) // decrement it to get the last valid index
|
|
{
|
|
int nSel = notebook->GetSelection();
|
|
|
|
// change selection wrapping if it becomes invalid
|
|
page = (gdk_event->keyval != GDK_Left) ? nSel == nMax ? 0
|
|
: nSel + 1
|
|
: nSel == 0 ? nMax
|
|
: nSel - 1;
|
|
}
|
|
else // notebook is empty, no next page
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
// m_selection = page;
|
|
gtk_notebook_set_page( GTK_NOTEBOOK(widget), page );
|
|
|
|
gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
|
|
return TRUE;
|
|
}
|
|
|
|
/* win is a control: tab can be propagated up */
|
|
if ((gdk_event->keyval == GDK_Tab) || (gdk_event->keyval == GDK_ISO_Left_Tab))
|
|
{
|
|
int sel = notebook->GetSelection();
|
|
if (sel == wxNOT_FOUND)
|
|
return TRUE;
|
|
wxGtkNotebookPage *nb_page = notebook->GetNotebookPage(sel);
|
|
wxCHECK_MSG( nb_page, FALSE, wxT("invalid selection in wxNotebook") );
|
|
|
|
wxNavigationKeyEvent event;
|
|
event.SetEventObject( notebook );
|
|
/* GDK reports GDK_ISO_Left_Tab for SHIFT-TAB */
|
|
event.SetDirection( (gdk_event->keyval == GDK_Tab) );
|
|
/* CTRL-TAB changes the (parent) window, i.e. switch notebook page */
|
|
event.SetWindowChange( (gdk_event->state & GDK_CONTROL_MASK) ||
|
|
(gdk_event->keyval == GDK_Left) || (gdk_event->keyval == GDK_Right) );
|
|
event.SetCurrentFocus( notebook );
|
|
|
|
wxNotebookPage *client = notebook->GetPage(sel);
|
|
if ( !client->HandleWindowEvent( event ) )
|
|
{
|
|
client->SetFocus();
|
|
}
|
|
|
|
gtk_signal_emit_stop_by_name( GTK_OBJECT(widget), "key_press_event" );
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// InsertChild callback for wxNotebook
|
|
//-----------------------------------------------------------------------------
|
|
|
|
static void wxInsertChildInNotebook( wxNotebook* parent, wxWindow* child )
|
|
{
|
|
// Hack Alert! (Part I): This sets the notebook as the parent of the child
|
|
// widget, and takes care of some details such as updating the state and
|
|
// style of the child to reflect its new location. We do this early
|
|
// because without it GetBestSize (which is used to set the initial size
|
|
// of controls if an explicit size is not given) will often report
|
|
// incorrect sizes since the widget's style context is not fully known.
|
|
// See bug #901694 for details
|
|
// (http://sourceforge.net/tracker/?func=detail&aid=901694&group_id=9863&atid=109863)
|
|
gtk_widget_set_parent(child->m_widget, parent->m_widget);
|
|
|
|
// NOTE: This should be considered a temporary workaround until we can
|
|
// work out the details and implement delaying the setting of the initial
|
|
// size of widgets until the size is really needed.
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// wxNotebook
|
|
//-----------------------------------------------------------------------------
|
|
|
|
BEGIN_EVENT_TABLE(wxNotebook, wxBookCtrlBase)
|
|
EVT_NAVIGATION_KEY(wxNotebook::OnNavigationKey)
|
|
END_EVENT_TABLE()
|
|
|
|
void wxNotebook::Init()
|
|
{
|
|
m_padding = 0;
|
|
m_inSwitchPage = false;
|
|
|
|
m_themeEnabled = true;
|
|
}
|
|
|
|
wxNotebook::wxNotebook()
|
|
{
|
|
Init();
|
|
}
|
|
|
|
wxNotebook::wxNotebook( wxWindow *parent, wxWindowID id,
|
|
const wxPoint& pos, const wxSize& size,
|
|
long style, const wxString& name )
|
|
{
|
|
Init();
|
|
Create( parent, id, pos, size, style, name );
|
|
}
|
|
|
|
wxNotebook::~wxNotebook()
|
|
{
|
|
DeleteAllPages();
|
|
}
|
|
|
|
bool wxNotebook::Create(wxWindow *parent, wxWindowID id,
|
|
const wxPoint& pos, const wxSize& size,
|
|
long style, const wxString& name )
|
|
{
|
|
m_needParent = true;
|
|
m_acceptsFocus = true;
|
|
m_insertCallback = (wxInsertChildFunction)wxInsertChildInNotebook;
|
|
|
|
if ( (style & wxBK_ALIGN_MASK) == wxBK_DEFAULT )
|
|
style |= wxBK_TOP;
|
|
|
|
if (!PreCreation( parent, pos, size ) ||
|
|
!CreateBase( parent, id, pos, size, style, wxDefaultValidator, name ))
|
|
{
|
|
wxFAIL_MSG( wxT("wxNoteBook creation failed") );
|
|
return false;
|
|
}
|
|
|
|
|
|
m_widget = gtk_notebook_new();
|
|
|
|
gtk_notebook_set_scrollable( GTK_NOTEBOOK(m_widget), 1 );
|
|
|
|
gtk_signal_connect( GTK_OBJECT(m_widget), "switch_page",
|
|
GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback), (gpointer)this );
|
|
|
|
m_parent->DoAddChild( this );
|
|
|
|
if (m_windowStyle & wxBK_RIGHT)
|
|
gtk_notebook_set_tab_pos( GTK_NOTEBOOK(m_widget), GTK_POS_RIGHT );
|
|
if (m_windowStyle & wxBK_LEFT)
|
|
gtk_notebook_set_tab_pos( GTK_NOTEBOOK(m_widget), GTK_POS_LEFT );
|
|
if (m_windowStyle & wxBK_BOTTOM)
|
|
gtk_notebook_set_tab_pos( GTK_NOTEBOOK(m_widget), GTK_POS_BOTTOM );
|
|
|
|
gtk_signal_connect( GTK_OBJECT(m_widget), "key_press_event",
|
|
GTK_SIGNAL_FUNC(gtk_notebook_key_press_callback), (gpointer)this );
|
|
|
|
PostCreation(size);
|
|
|
|
gtk_signal_connect( GTK_OBJECT(m_widget), "realize",
|
|
GTK_SIGNAL_FUNC(gtk_notebook_realized_callback), (gpointer) this );
|
|
|
|
return true;
|
|
}
|
|
|
|
int wxNotebook::GetSelection() const
|
|
{
|
|
wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid notebook") );
|
|
|
|
if ( m_selection == wxNOT_FOUND )
|
|
{
|
|
GList *nb_pages = GTK_NOTEBOOK(m_widget)->children;
|
|
|
|
if (g_list_length(nb_pages) != 0)
|
|
{
|
|
GtkNotebook *notebook = GTK_NOTEBOOK(m_widget);
|
|
|
|
gpointer cur = notebook->cur_page;
|
|
if ( cur != NULL )
|
|
{
|
|
const_cast<wxNotebook *>(this)->
|
|
SetSelection(g_list_index( nb_pages, cur ));
|
|
}
|
|
}
|
|
}
|
|
|
|
return m_selection;
|
|
}
|
|
|
|
wxString wxNotebook::GetPageText( size_t page ) const
|
|
{
|
|
wxCHECK_MSG( m_widget != NULL, wxEmptyString, wxT("invalid notebook") );
|
|
|
|
wxGtkNotebookPage* nb_page = GetNotebookPage(page);
|
|
if (nb_page)
|
|
return nb_page->m_text;
|
|
else
|
|
return wxEmptyString;
|
|
}
|
|
|
|
int wxNotebook::GetPageImage( size_t page ) const
|
|
{
|
|
wxCHECK_MSG( m_widget != NULL, -1, wxT("invalid notebook") );
|
|
|
|
wxGtkNotebookPage* nb_page = GetNotebookPage(page);
|
|
if (nb_page)
|
|
return nb_page->m_image;
|
|
else
|
|
return -1;
|
|
}
|
|
|
|
wxGtkNotebookPage* wxNotebook::GetNotebookPage( int page ) const
|
|
{
|
|
wxCHECK_MSG( m_widget != NULL, NULL, wxT("invalid notebook") );
|
|
|
|
wxCHECK_MSG( page < (int)m_pagesData.GetCount(), NULL, wxT("invalid notebook index") );
|
|
|
|
return m_pagesData.Item(page)->GetData();
|
|
}
|
|
|
|
int wxNotebook::DoSetSelection( size_t page, int flags )
|
|
{
|
|
wxCHECK_MSG( m_widget != NULL, wxNOT_FOUND, wxT("invalid notebook") );
|
|
|
|
wxCHECK_MSG( page < m_pagesData.GetCount(), -1, wxT("invalid notebook index") );
|
|
|
|
int selOld = GetSelection();
|
|
|
|
if ( !(flags & SetSelection_SendEvent) )
|
|
m_skipNextPageChangeEvent = true;
|
|
|
|
// cache the selection
|
|
m_selection = page;
|
|
gtk_notebook_set_page( GTK_NOTEBOOK(m_widget), page );
|
|
|
|
// gtk_notebook_set_current_page is supposed to emit the switch-page signal
|
|
// which should be caught by our gtk_notebook_page_change_callback which
|
|
// should have reset the flag to false, check it:
|
|
wxASSERT_LEVEL_2_MSG(
|
|
(flags & SetSelection_SendEvent) || !m_skipNextPageChangeEvent,
|
|
"internal error in selection events generation"
|
|
);
|
|
|
|
wxNotebookPage *client = GetPage(page);
|
|
if ( client )
|
|
client->SetFocus();
|
|
|
|
return selOld;
|
|
}
|
|
|
|
bool wxNotebook::SetPageText( size_t page, const wxString &text )
|
|
{
|
|
wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid notebook") );
|
|
|
|
wxGtkNotebookPage* nb_page = GetNotebookPage(page);
|
|
|
|
wxCHECK_MSG( nb_page, FALSE, wxT("SetPageText: invalid page index") );
|
|
|
|
nb_page->m_text = text;
|
|
|
|
gtk_label_set( nb_page->m_label, wxGTK_CONV( nb_page->m_text ) );
|
|
|
|
return true;
|
|
}
|
|
|
|
bool wxNotebook::SetPageImage( size_t page, int image )
|
|
{
|
|
/* HvdH 28-12-98: now it works, but it's a bit of a kludge */
|
|
|
|
wxGtkNotebookPage* nb_page = GetNotebookPage(page);
|
|
|
|
if (!nb_page) return FALSE;
|
|
|
|
/* Optimization posibility: return immediately if image unchanged.
|
|
* Not enabled because it may break existing (stupid) code that
|
|
* manipulates the imagelist to cycle images */
|
|
|
|
/* if (image == nb_page->m_image) return true; */
|
|
|
|
/* For different cases:
|
|
1) no image -> no image
|
|
2) image -> no image
|
|
3) no image -> image
|
|
4) image -> image */
|
|
|
|
if (image == -1 && nb_page->m_image == -1)
|
|
return true; /* Case 1): Nothing to do. */
|
|
|
|
GtkWidget *pixmapwid = NULL;
|
|
|
|
if (nb_page->m_image != -1)
|
|
{
|
|
/* Case 2) or 4). There is already an image in the gtkhbox. Let's find it */
|
|
|
|
GList *child = gtk_container_children(GTK_CONTAINER(nb_page->m_box));
|
|
while (child)
|
|
{
|
|
if (GTK_IS_PIXMAP(child->data))
|
|
{
|
|
pixmapwid = GTK_WIDGET(child->data);
|
|
break;
|
|
}
|
|
child = child->next;
|
|
}
|
|
|
|
/* We should have the pixmap widget now */
|
|
wxASSERT(pixmapwid != NULL);
|
|
|
|
if (image == -1)
|
|
{
|
|
/* If there's no new widget, just remove the old from the box */
|
|
gtk_container_remove(GTK_CONTAINER(nb_page->m_box), pixmapwid);
|
|
nb_page->m_image = -1;
|
|
|
|
return true; /* Case 2) */
|
|
}
|
|
}
|
|
|
|
/* Only cases 3) and 4) left */
|
|
wxASSERT( HasImageList() ); /* Just in case */
|
|
|
|
/* Construct the new pixmap */
|
|
const wxBitmap *bmp = GetImageList()->GetBitmapPtr(image);
|
|
GdkPixmap *pixmap = bmp->GetPixmap();
|
|
GdkBitmap *mask = NULL;
|
|
if ( bmp->GetMask() )
|
|
{
|
|
mask = bmp->GetMask()->GetBitmap();
|
|
}
|
|
|
|
if (pixmapwid == NULL)
|
|
{
|
|
/* Case 3) No old pixmap. Create a new one and prepend it to the hbox */
|
|
pixmapwid = gtk_pixmap_new (pixmap, mask );
|
|
|
|
/* CHECKME: Are these pack flags okay? */
|
|
gtk_box_pack_start(GTK_BOX(nb_page->m_box), pixmapwid, FALSE, FALSE, m_padding);
|
|
gtk_widget_show(pixmapwid);
|
|
}
|
|
else
|
|
{
|
|
/* Case 4) Simply replace the pixmap */
|
|
gtk_pixmap_set(GTK_PIXMAP(pixmapwid), pixmap, mask);
|
|
}
|
|
|
|
nb_page->m_image = image;
|
|
|
|
return true;
|
|
}
|
|
|
|
void wxNotebook::SetPageSize( const wxSize &WXUNUSED(size) )
|
|
{
|
|
wxFAIL_MSG( wxT("wxNotebook::SetPageSize not implemented") );
|
|
}
|
|
|
|
void wxNotebook::SetPadding( const wxSize &padding )
|
|
{
|
|
wxCHECK_RET( m_widget != NULL, wxT("invalid notebook") );
|
|
|
|
m_padding = padding.GetWidth();
|
|
|
|
int i;
|
|
for (i=0; i<int(GetPageCount()); i++)
|
|
{
|
|
wxGtkNotebookPage* nb_page = GetNotebookPage(i);
|
|
wxASSERT(nb_page != NULL);
|
|
|
|
if (nb_page->m_image != -1)
|
|
{
|
|
// gtk_box_set_child_packing sets padding on BOTH sides
|
|
// icon provides left padding, label provides center and right
|
|
int image = nb_page->m_image;
|
|
SetPageImage(i,-1);
|
|
SetPageImage(i,image);
|
|
}
|
|
wxASSERT(nb_page->m_label);
|
|
gtk_box_set_child_packing(GTK_BOX(nb_page->m_box),
|
|
GTK_WIDGET(nb_page->m_label),
|
|
FALSE, FALSE, m_padding, GTK_PACK_END);
|
|
}
|
|
}
|
|
|
|
void wxNotebook::SetTabSize(const wxSize& WXUNUSED(sz))
|
|
{
|
|
wxFAIL_MSG( wxT("wxNotebook::SetTabSize not implemented") );
|
|
}
|
|
|
|
bool wxNotebook::DeleteAllPages()
|
|
{
|
|
wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid notebook") );
|
|
|
|
while (m_pagesData.GetCount() > 0)
|
|
DeletePage( m_pagesData.GetCount()-1 );
|
|
|
|
wxASSERT_MSG( GetPageCount() == 0, wxT("all pages must have been deleted") );
|
|
|
|
InvalidateBestSize();
|
|
return wxNotebookBase::DeleteAllPages();
|
|
}
|
|
|
|
wxNotebookPage *wxNotebook::DoRemovePage( size_t page )
|
|
{
|
|
if ( m_selection != wxNOT_FOUND && (size_t)m_selection >= page )
|
|
{
|
|
// the index will become invalid after the page is deleted
|
|
m_selection = wxNOT_FOUND;
|
|
}
|
|
|
|
wxNotebookPage *client = wxNotebookBase::DoRemovePage(page);
|
|
if ( !client )
|
|
return NULL;
|
|
|
|
gtk_widget_ref( client->m_widget );
|
|
gtk_widget_unrealize( client->m_widget );
|
|
gtk_widget_unparent( client->m_widget );
|
|
|
|
// gtk_notebook_remove_page() sends "switch_page" signal with some strange
|
|
// new page index (when deleting selected page 0, new page is 1 although,
|
|
// clearly, the selection should stay 0), so suppress this
|
|
gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
|
|
GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback), (gpointer) this );
|
|
|
|
gtk_notebook_remove_page( GTK_NOTEBOOK(m_widget), page );
|
|
|
|
gtk_signal_connect( GTK_OBJECT(m_widget), "switch_page",
|
|
GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback), (gpointer)this );
|
|
|
|
wxGtkNotebookPage* p = GetNotebookPage(page);
|
|
m_pagesData.DeleteObject(p);
|
|
delete p;
|
|
|
|
return client;
|
|
}
|
|
|
|
bool wxNotebook::InsertPage( size_t position,
|
|
wxNotebookPage* win,
|
|
const wxString& text,
|
|
bool select,
|
|
int imageId )
|
|
{
|
|
wxCHECK_MSG( m_widget != NULL, FALSE, wxT("invalid notebook") );
|
|
|
|
wxCHECK_MSG( win->GetParent() == this, FALSE,
|
|
wxT("Can't add a page whose parent is not the notebook!") );
|
|
|
|
wxCHECK_MSG( position <= GetPageCount(), FALSE,
|
|
wxT("invalid page index in wxNotebookPage::InsertPage()") );
|
|
|
|
// Hack Alert! (Part II): See above in wxInsertChildInNotebook callback
|
|
// why this has to be done. NOTE: using gtk_widget_unparent here does not
|
|
// work as it seems to undo too much and will cause errors in the
|
|
// gtk_notebook_insert_page below, so instead just clear the parent by
|
|
// hand here.
|
|
win->m_widget->parent = NULL;
|
|
|
|
// don't receive switch page during addition
|
|
gtk_signal_disconnect_by_func( GTK_OBJECT(m_widget),
|
|
GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback), (gpointer) this );
|
|
|
|
if (m_themeEnabled)
|
|
win->SetThemeEnabled(true);
|
|
|
|
GtkNotebook *notebook = GTK_NOTEBOOK(m_widget);
|
|
|
|
wxGtkNotebookPage *nb_page = new wxGtkNotebookPage();
|
|
|
|
if ( position == GetPageCount() )
|
|
m_pagesData.Append( nb_page );
|
|
else
|
|
m_pagesData.Insert( position, nb_page );
|
|
|
|
m_pages.Insert(win, position);
|
|
|
|
nb_page->m_box = gtk_hbox_new( FALSE, 1 );
|
|
gtk_container_border_width( GTK_CONTAINER(nb_page->m_box), 2 );
|
|
|
|
gtk_signal_connect( GTK_OBJECT(win->m_widget), "size_allocate",
|
|
GTK_SIGNAL_FUNC(gtk_page_size_callback), (gpointer)win );
|
|
|
|
gtk_notebook_insert_page( notebook, win->m_widget, nb_page->m_box, position );
|
|
|
|
nb_page->m_page = (GtkNotebookPage*) g_list_last(notebook->children)->data;
|
|
|
|
/* set the label image */
|
|
nb_page->m_image = imageId;
|
|
|
|
if (imageId != -1)
|
|
{
|
|
wxASSERT( HasImageList() );
|
|
|
|
const wxBitmap *bmp = GetImageList()->GetBitmapPtr(imageId);
|
|
GdkPixmap *pixmap = bmp->GetPixmap();
|
|
GdkBitmap *mask = NULL;
|
|
if ( bmp->GetMask() )
|
|
{
|
|
mask = bmp->GetMask()->GetBitmap();
|
|
}
|
|
|
|
GtkWidget *pixmapwid = gtk_pixmap_new (pixmap, mask );
|
|
|
|
gtk_box_pack_start(GTK_BOX(nb_page->m_box), pixmapwid, FALSE, FALSE, m_padding);
|
|
|
|
gtk_widget_show(pixmapwid);
|
|
}
|
|
|
|
/* set the label text */
|
|
|
|
nb_page->m_text = text;
|
|
if (nb_page->m_text.empty()) nb_page->m_text = wxEmptyString;
|
|
|
|
nb_page->m_label = GTK_LABEL( gtk_label_new(wxGTK_CONV(nb_page->m_text)) );
|
|
gtk_box_pack_end( GTK_BOX(nb_page->m_box), GTK_WIDGET(nb_page->m_label), FALSE, FALSE, m_padding );
|
|
|
|
/* apply current style */
|
|
GtkRcStyle *style = CreateWidgetStyle();
|
|
if ( style )
|
|
{
|
|
gtk_widget_modify_style(GTK_WIDGET(nb_page->m_label), style);
|
|
gtk_rc_style_unref(style);
|
|
}
|
|
|
|
/* show the label */
|
|
gtk_widget_show( GTK_WIDGET(nb_page->m_label) );
|
|
if (select && (m_pagesData.GetCount() > 1))
|
|
{
|
|
SetSelection( position );
|
|
}
|
|
|
|
gtk_signal_connect( GTK_OBJECT(m_widget), "switch_page",
|
|
GTK_SIGNAL_FUNC(gtk_notebook_page_change_callback), (gpointer)this );
|
|
|
|
InvalidateBestSize();
|
|
return true;
|
|
}
|
|
|
|
// helper for HitTest(): check if the point lies inside the given widget which
|
|
// is the child of the notebook whose position and border size are passed as
|
|
// parameters
|
|
static bool
|
|
IsPointInsideWidget(const wxPoint& pt, GtkWidget *w,
|
|
gint x, gint y, gint border = 0)
|
|
{
|
|
return
|
|
(pt.x >= w->allocation.x - x - border) &&
|
|
(pt.x <= w->allocation.x - x + border + w->allocation.width) &&
|
|
(pt.y >= w->allocation.y - y - border) &&
|
|
(pt.y <= w->allocation.y - y + border + w->allocation.height);
|
|
}
|
|
|
|
int wxNotebook::HitTest(const wxPoint& pt, long *flags) const
|
|
{
|
|
const gint x = m_widget->allocation.x;
|
|
const gint y = m_widget->allocation.y;
|
|
|
|
const size_t count = GetPageCount();
|
|
size_t i = 0;
|
|
|
|
// MR: Code to fix HitTest index return when tabs are scrolled.
|
|
// No idea if it would work for GTK1
|
|
#if 0
|
|
GtkNotebook * notebook = GTK_NOTEBOOK(m_widget);
|
|
if (gtk_notebook_get_scrollable(notebook));
|
|
i = g_list_position( notebook->children, notebook->first_tab );
|
|
#endif
|
|
|
|
for ( ; i < count; i++ )
|
|
{
|
|
wxGtkNotebookPage* nb_page = GetNotebookPage(i);
|
|
GtkWidget *box = nb_page->m_box;
|
|
|
|
// VZ: don't know how to find the border width in GTK+ 1.2
|
|
const gint border = 0;
|
|
if ( IsPointInsideWidget(pt, box, x, y, border) )
|
|
{
|
|
// ok, we're inside this tab -- now find out where, if needed
|
|
if ( flags )
|
|
{
|
|
GtkWidget *pixmap = NULL;
|
|
|
|
GList *children = gtk_container_children(GTK_CONTAINER(box));
|
|
for ( GList *child = children; child; child = child->next )
|
|
{
|
|
if ( GTK_IS_PIXMAP(child->data) )
|
|
{
|
|
pixmap = GTK_WIDGET(child->data);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( children )
|
|
g_list_free(children);
|
|
|
|
if ( pixmap && IsPointInsideWidget(pt, pixmap, x, y) )
|
|
{
|
|
*flags = wxBK_HITTEST_ONICON;
|
|
}
|
|
else if ( IsPointInsideWidget(pt, GTK_WIDGET(nb_page->m_label), x, y) )
|
|
{
|
|
*flags = wxBK_HITTEST_ONLABEL;
|
|
}
|
|
else
|
|
{
|
|
*flags = wxBK_HITTEST_ONITEM;
|
|
}
|
|
}
|
|
|
|
return i;
|
|
}
|
|
}
|
|
|
|
if ( flags )
|
|
*flags = wxBK_HITTEST_NOWHERE;
|
|
|
|
return wxNOT_FOUND;
|
|
}
|
|
|
|
void wxNotebook::OnNavigationKey(wxNavigationKeyEvent& event)
|
|
{
|
|
if (event.IsWindowChange())
|
|
AdvanceSelection( event.GetDirection() );
|
|
else
|
|
event.Skip();
|
|
}
|
|
|
|
#if wxUSE_CONSTRAINTS
|
|
|
|
// override these 2 functions to do nothing: everything is done in OnSize
|
|
void wxNotebook::SetConstraintSizes( bool WXUNUSED(recurse) )
|
|
{
|
|
// don't set the sizes of the pages - their correct size is not yet known
|
|
wxControl::SetConstraintSizes(FALSE);
|
|
}
|
|
|
|
bool wxNotebook::DoPhase( int WXUNUSED(nPhase) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
#endif
|
|
|
|
void wxNotebook::DoApplyWidgetStyle(GtkRcStyle *style)
|
|
{
|
|
gtk_widget_modify_style(m_widget, style);
|
|
size_t cnt = m_pagesData.GetCount();
|
|
for (size_t i = 0; i < cnt; i++)
|
|
gtk_widget_modify_style(GTK_WIDGET(GetNotebookPage(i)->m_label), style);
|
|
}
|
|
|
|
bool wxNotebook::IsOwnGtkWindow( GdkWindow *window )
|
|
{
|
|
return ((m_widget->window == window) ||
|
|
(NOTEBOOK_PANEL(m_widget) == window));
|
|
}
|
|
|
|
// static
|
|
wxVisualAttributes
|
|
wxNotebook::GetClassDefaultAttributes(wxWindowVariant WXUNUSED(variant))
|
|
{
|
|
return GetDefaultAttributesFromGTKWidget(gtk_notebook_new);
|
|
}
|
|
|
|
#endif
|