+ int height;
+
+ if (m_font.Ok())
+ wxGetTextExtent (GetXDisplay(), m_font, 1.0,
+ "x", NULL, &height, NULL, NULL);
+ else
+ wxGetTextExtent (this, "x", NULL, &height, NULL, NULL);
+
+ return height;
+}
+
+int wxWindow::GetCharWidth() const
+{
+ int width;
+
+ if (m_font.Ok())
+ wxGetTextExtent (GetXDisplay(), m_font, 1.0,
+ "x", &width, NULL, NULL, NULL);
+ else
+ wxGetTextExtent (this, "x", &width, NULL, NULL, NULL);
+
+ return width;
+}
+
+void wxWindow::DoGetTextExtent(const wxString& string,
+ int *x, int *y,
+ int *descent,
+ int *externalLeading,
+ const wxFont *theFont) const
+{
+ const wxFont *fontToUse = theFont ? theFont : &m_font;
+
+ if (externalLeading)
+ *externalLeading = 0;
+ if (fontToUse->Ok())
+ wxGetTextExtent (GetXDisplay(), *fontToUse, 1.0,
+ string, x, y, NULL, descent);
+ else
+ wxGetTextExtent (this, string, x, y, NULL, descent);
+}
+
+// ----------------------------------------------------------------------------
+// painting
+// ----------------------------------------------------------------------------
+
+void wxWindow::AddUpdateRect(int x, int y, int w, int h)
+{
+ m_updateRegion.Union( x, y, w, h );
+}
+
+void wxWindow::Refresh(bool eraseBack, const wxRect *rect)
+{
+ Widget widget = (Widget) GetMainWidget();
+ if (!widget)
+ return;
+ m_needsRefresh = true;
+ Display *display = XtDisplay(widget);
+ Window thisWindow = XtWindow(widget);
+
+ XExposeEvent dummyEvent;
+ int width, height;
+ GetSize(&width, &height);
+
+ dummyEvent.type = Expose;
+ dummyEvent.display = display;
+ dummyEvent.send_event = True;
+ dummyEvent.window = thisWindow;
+ if (rect)
+ {
+ dummyEvent.x = rect->x;
+ dummyEvent.y = rect->y;
+ dummyEvent.width = rect->width;
+ dummyEvent.height = rect->height;
+ }
+ else
+ {
+ dummyEvent.x = 0;
+ dummyEvent.y = 0;
+ dummyEvent.width = width;
+ dummyEvent.height = height;
+ }
+ dummyEvent.count = 0;
+
+ if (eraseBack)
+ {
+ wxClientDC dc(this);
+ wxBrush backgroundBrush(GetBackgroundColour(), wxSOLID);
+ dc.SetBackground(backgroundBrush);
+
+ wxClientDCImpl * const
+ dcimpl = static_cast<wxClientDCImpl *>(dc.GetImpl());
+ if (rect)
+ dcimpl->Clear(*rect);
+ else
+ dcimpl->Clear();
+ }
+
+ XSendEvent(display, thisWindow, False, ExposureMask, (XEvent *)&dummyEvent);
+}
+
+void wxWindow::DoPaint()
+{
+ //TODO : make a temporary gc so we can do the XCopyArea below
+ if (m_backingPixmap && !m_needsRefresh)
+ {
+ wxPaintDC dc(this);
+
+ wxPaintDCImpl * const
+ dcimpl = static_cast<wxPaintDCImpl *>(dc.GetImpl());
+
+ GC tempGC = (GC) dcimpl->GetBackingGC();
+
+ Widget widget = (Widget) GetMainWidget();
+
+ int scrollPosX = 0;
+ int scrollPosY = 0;
+
+ // We have to test whether it's a wxScrolledWindow (hack!) because
+ // otherwise we don't know how many pixels have been scrolled. We might
+ // solve this in the future by defining virtual wxWindow functions to get
+ // the scroll position in pixels. Or, each kind of scrolled window has to
+ // implement backing stores itself, using generic wxWidgets code.
+ wxScrolledWindow* scrolledWindow = wxDynamicCast(this, wxScrolledWindow);
+ if ( scrolledWindow )
+ {
+ int x, y;
+ scrolledWindow->CalcScrolledPosition(0, 0, &x, &y);
+
+ scrollPosX = - x;
+ scrollPosY = - y;
+ }
+
+ // TODO: This could be optimized further by only copying the areas in the
+ // current update region.
+
+ // Only blit the part visible in the client area. The backing pixmap
+ // always starts at 0, 0 but we may be looking at only a portion of it.
+ wxSize clientArea = GetClientSize();
+ int toBlitX = m_pixmapWidth - scrollPosX;
+ int toBlitY = m_pixmapHeight - scrollPosY;
+
+ // Copy whichever is samller, the amount of pixmap we have to copy,
+ // or the size of the client area.
+ toBlitX = wxMin(toBlitX, clientArea.x);
+ toBlitY = wxMin(toBlitY, clientArea.y);
+
+ // Make sure we're not negative
+ toBlitX = wxMax(0, toBlitX);
+ toBlitY = wxMax(0, toBlitY);
+
+ XCopyArea
+ (
+ XtDisplay(widget),
+ (Pixmap) m_backingPixmap,
+ XtWindow (widget),
+ tempGC,
+ scrollPosX, scrollPosY, // Start at the scroll position
+ toBlitX, toBlitY, // How much of the pixmap to copy
+ 0, 0 // Destination
+ );
+ }
+ else
+ {
+ wxWindowDC dc(this);
+ // Set an erase event first
+ wxEraseEvent eraseEvent(GetId(), &dc);
+ eraseEvent.SetEventObject(this);
+ HandleWindowEvent(eraseEvent);
+
+ wxPaintEvent event(GetId());
+ event.SetEventObject(this);
+ HandleWindowEvent(event);
+
+ m_needsRefresh = false;
+ }
+}
+
+// ----------------------------------------------------------------------------
+// event handlers
+// ----------------------------------------------------------------------------
+
+// Responds to colour changes: passes event on to children.
+void wxWindow::OnSysColourChanged(wxSysColourChangedEvent& event)
+{
+ wxWindowList::compatibility_iterator node = GetChildren().GetFirst();
+ while ( node )
+ {
+ // Only propagate to non-top-level windows
+ wxWindow *win = node->GetData();
+ if ( win->GetParent() )
+ {
+ wxSysColourChangedEvent event2;
+ event.SetEventObject(win);
+ win->HandleWindowEvent(event2);
+ }
+
+ node = node->GetNext();
+ }
+}
+
+void wxWindow::OnInternalIdle()
+{
+ // This calls the UI-update mechanism (querying windows for
+ // menu/toolbar/control state information)
+ if (wxUpdateUIEvent::CanUpdate(this) && IsShownOnScreen())
+ UpdateWindowUI(wxUPDATE_UI_FROMIDLE);
+}
+
+// ----------------------------------------------------------------------------
+// accelerators
+// ----------------------------------------------------------------------------
+
+bool wxWindow::ProcessAccelerator(wxKeyEvent& event)
+{
+#if wxUSE_ACCEL
+ if (!m_acceleratorTable.Ok())
+ return false;
+
+ int count = m_acceleratorTable.GetCount();
+ wxAcceleratorEntry* entries = m_acceleratorTable.GetEntries();
+ int i;
+ for (i = 0; i < count; i++)
+ {
+ wxAcceleratorEntry* entry = & (entries[i]);
+ if (entry->MatchesEvent(event))
+ {
+ // Bingo, we have a match. Now find a control that matches the
+ // entry command id.
+
+ // Need to go up to the top of the window hierarchy, since it might
+ // be e.g. a menu item
+ wxWindow* parent = this;
+ while ( parent && !parent->IsTopLevel() )
+ parent = parent->GetParent();
+
+ if (!parent)
+ return false;
+
+ wxFrame* frame = wxDynamicCast(parent, wxFrame);
+ if ( frame )
+ {
+#if wxUSE_MENUS
+ // Try for a menu command
+ if (frame->GetMenuBar())
+ {
+ wxMenuItem* item = frame->GetMenuBar()->FindItem(entry->GetCommand());
+ if (item)
+ {
+ wxCommandEvent commandEvent(wxEVT_COMMAND_MENU_SELECTED, entry->GetCommand());
+ commandEvent.SetEventObject(frame);
+
+ // If ProcessEvent returns true (it was handled), then
+ // the calling code will skip the event handling.
+ return frame->HandleWindowEvent(commandEvent);
+ }
+ }
+#endif
+ }
+
+ // Find a child matching the command id
+ wxWindow* child = parent->FindWindow(entry->GetCommand());
+
+ // No such child
+ if (!child)
+ return false;
+
+ // Now we process those kinds of windows that we can.
+ // For now, only buttons.
+ if ( wxDynamicCast(child, wxButton) )
+ {
+ wxCommandEvent commandEvent (wxEVT_COMMAND_BUTTON_CLICKED, child->GetId());
+ commandEvent.SetEventObject(child);
+ return child->HandleWindowEvent(commandEvent);
+ }
+
+ return false;
+ } // matches event
+ }// for
+#endif
+
+ // We didn't match the key event against an accelerator.
+ return false;
+}
+
+// ============================================================================
+// Motif-specific stuff from here on
+// ============================================================================
+
+// ----------------------------------------------------------------------------
+// function which maintain the global hash table mapping Widgets to wxWidgets
+// ----------------------------------------------------------------------------
+
+bool wxAddWindowToTable(Widget w, wxWindow *win)
+{
+ const long key = (long)w;
+ if ( wxWidgetHashTable->Get(key))
+ {
+ wxLogDebug("Widget table clash: new widget is %ld, %s",
+ key, win->GetClassInfo()->GetClassName());
+ return false;
+ }
+
+ wxWidgetHashTable->Put(key, win);
+
+ wxLogTrace("widget", "Widget 0x%p <-> window %p (%s)",
+ w, win, win->GetClassInfo()->GetClassName());
+
+ return true;
+}
+
+wxWindow *wxGetWindowFromTable(Widget w)
+{
+ return (wxWindow *)wxWidgetHashTable->Get((long) w);
+}
+
+void wxDeleteWindowFromTable(Widget w)
+{
+ wxLogTrace("widget", "Widget 0x%p", (WXWidget)w);
+
+ wxWidgetHashTable->Delete((long)w);
+}
+
+// ----------------------------------------------------------------------------
+// add/remove window from the table
+// ----------------------------------------------------------------------------
+
+// Add to hash table, add event handler
+bool wxWindow::AttachWidget (wxWindow* WXUNUSED(parent), WXWidget mainWidget,
+ WXWidget formWidget, int x, int y, int width, int height)
+{
+ wxAddWindowToTable((Widget) mainWidget, this);
+ XtAddEventHandler( (Widget) mainWidget,
+ ButtonPressMask | ButtonReleaseMask
+ | PointerMotionMask,
+ False,
+ wxPanelItemEventHandler,
+ (XtPointer) this);
+
+ if (!formWidget)
+ {
+ XtTranslations ptr;
+ XtOverrideTranslations ((Widget) mainWidget,
+ ptr = XtParseTranslationTable ("<Configure>: resize()"));
+ XtFree ((char *) ptr);
+ }
+
+ // Some widgets have a parent form widget, e.g. wxRadioBox
+ if (formWidget)
+ {
+ if (!wxAddWindowToTable((Widget) formWidget, this))
+ return false;
+
+ XtTranslations ptr;
+ XtOverrideTranslations ((Widget) formWidget,
+ ptr = XtParseTranslationTable ("<Configure>: resize()"));
+ XtFree ((char *) ptr);
+ }
+
+ if (x == -1)
+ x = 0;
+ if (y == -1)
+ y = 0;
+ DoSetSize (x, y, width, height, wxSIZE_USE_EXISTING);
+
+ return true;
+}
+
+// Remove event handler, remove from hash table
+bool wxWindow::DetachWidget(WXWidget widget)
+{
+ XtRemoveEventHandler( (Widget) widget,
+ ButtonPressMask | ButtonReleaseMask
+ | PointerMotionMask,
+ False,
+ wxPanelItemEventHandler,
+ (XtPointer)this);
+
+ wxDeleteWindowFromTable((Widget) widget);
+ return true;
+}
+
+// ----------------------------------------------------------------------------
+// Motif-specific accessors
+// ----------------------------------------------------------------------------
+
+WXWindow wxWindow::GetClientXWindow() const
+{
+ Widget wMain = (Widget)GetClientWidget();
+ if ( wMain )
+ return (WXWindow) XtWindow(wMain);
+ else
+ return (WXWindow) 0;
+}
+
+// Get the underlying X window
+WXWindow wxWindow::GetXWindow() const
+{
+ Widget wMain = (Widget)GetMainWidget();
+ if ( wMain )
+ return (WXWindow) XtWindow(wMain);
+ else
+ return (WXWindow) 0;
+}
+
+// Get the underlying X display
+WXDisplay *wxWindow::GetXDisplay() const
+{
+ Widget wMain = (Widget)GetMainWidget();
+ if ( wMain )
+ return (WXDisplay*) XtDisplay(wMain);
+ else
+ return NULL;
+}
+
+WXWidget wxWindow::GetMainWidget() const
+{
+ if (m_drawingArea)
+ return m_drawingArea;
+ else
+ return m_mainWidget;
+}
+
+WXWidget wxWindow::GetClientWidget() const
+{
+ if (m_drawingArea != (WXWidget) 0)
+ return m_drawingArea;
+ else
+ return GetMainWidget();
+}
+
+WXWidget wxWindow::GetTopWidget() const
+{
+ return GetMainWidget();
+}
+
+WXWidget wxWindow::GetLabelWidget() const
+{
+ return GetMainWidget();
+}
+
+// ----------------------------------------------------------------------------
+// Motif callbacks
+// ----------------------------------------------------------------------------
+
+// All widgets should have this as their resize proc.
+// OnSize sent to wxWindow via client data.
+void wxWidgetResizeProc(Widget w, XConfigureEvent *WXUNUSED(event),
+ String WXUNUSED(args)[], int *WXUNUSED(num_args))
+{
+ wxWindow *win = wxGetWindowFromTable(w);
+ if (!win)
+ return;
+
+ if (win->PreResize())
+ {
+ wxSize newSize(win->GetSize());
+ wxSizeEvent sizeEvent(newSize, win->GetId());
+ sizeEvent.SetEventObject(win);
+ win->HandleWindowEvent(sizeEvent);
+ }
+}
+
+static void wxCanvasRepaintProc(Widget drawingArea,
+ XtPointer clientData,
+ XmDrawingAreaCallbackStruct * cbs)
+{
+ if (!wxGetWindowFromTable(drawingArea))
+ return;
+
+ XEvent * event = cbs->event;
+ wxWindow * win = (wxWindow *) clientData;
+
+ switch (event->type)
+ {
+ case Expose:
+ {
+ win->AddUpdateRect(event->xexpose.x, event->xexpose.y,
+ event->xexpose.width, event->xexpose.height);
+
+ if (event -> xexpose.count == 0)
+ {
+ win->DoPaint();
+ }
+ break;
+ }
+ }
+}
+
+// Unable to deal with Enter/Leave without a separate EventHandler (Motif 1.1.4)
+static void wxCanvasEnterLeave(Widget drawingArea,
+ XtPointer WXUNUSED(clientData),
+ XCrossingEvent * event)
+{
+ XmDrawingAreaCallbackStruct cbs;
+ XEvent ev;
+
+ ((XCrossingEvent &) ev) = *event;
+
+ cbs.reason = XmCR_INPUT;
+ cbs.event = &ev;
+
+ wxCanvasInputEvent(drawingArea, (XtPointer) NULL, &cbs);
+}
+
+// Fix to make it work under Motif 1.0 (!)
+static void wxCanvasMotionEvent (Widget WXUNUSED(drawingArea),
+ XButtonEvent *WXUNUSED(event))
+{
+#if XmVersion <= 1000
+ XmDrawingAreaCallbackStruct cbs;
+ XEvent ev;
+
+ ev = *((XEvent *) event);
+ cbs.reason = XmCR_INPUT;
+ cbs.event = &ev;
+
+ wxCanvasInputEvent (drawingArea, (XtPointer) NULL, &cbs);
+#endif // XmVersion <= 1000
+}
+
+static void wxCanvasInputEvent(Widget drawingArea,
+ XtPointer WXUNUSED(data),
+ XmDrawingAreaCallbackStruct * cbs)
+{
+ wxWindow *canvas = wxGetWindowFromTable(drawingArea);
+ XEvent* xevent = cbs->event;
+
+ if (canvas==NULL)
+ return;
+
+ if (cbs->reason != XmCR_INPUT)
+ return;
+
+ switch (xevent->xany.type)
+ {
+ case EnterNotify:
+ case LeaveNotify:
+ case ButtonPress:
+ case ButtonRelease:
+ case MotionNotify:
+ {
+ wxMouseEvent wxevent(0);
+ if (wxTranslateMouseEvent(wxevent, canvas, drawingArea, xevent))
+ {
+ canvas->HandleWindowEvent(wxevent);
+ }
+ break;
+ }
+ case KeyPress:
+ {
+ wxKeyEvent event (wxEVT_CHAR);
+ if (wxTranslateKeyEvent (event, canvas, (Widget) 0, xevent))
+ {
+ // Implement wxFrame::OnCharHook by checking ancestor.
+ wxWindow *parent = canvas;
+ while (parent && !parent->IsTopLevel())
+ parent = parent->GetParent();
+
+ if (parent)
+ {
+ event.SetEventType(wxEVT_CHAR_HOOK);
+ if (parent->HandleWindowEvent(event))
+ return;
+ }
+
+ // For simplicity, OnKeyDown is the same as OnChar
+ // TODO: filter modifier key presses from OnChar
+ event.SetEventType(wxEVT_KEY_DOWN);
+
+ // Only process OnChar if OnKeyDown didn't swallow it
+ if (!canvas->HandleWindowEvent (event))
+ {
+ event.SetEventType(wxEVT_CHAR);
+ canvas->HandleWindowEvent (event);
+ }
+ }
+ break;
+ }
+ case KeyRelease:
+ {
+ wxKeyEvent event (wxEVT_KEY_UP);
+ if (wxTranslateKeyEvent (event, canvas, (Widget) 0, xevent))
+ {
+ canvas->HandleWindowEvent (event);
+ }
+ break;
+ }
+ case FocusIn:
+ {
+ if (xevent->xfocus.detail != NotifyPointer)
+ {
+ wxFocusEvent event(wxEVT_SET_FOCUS, canvas->GetId());
+ event.SetEventObject(canvas);
+ canvas->HandleWindowEvent(event);
+ }
+ break;
+ }
+ case FocusOut:
+ {
+ if (xevent->xfocus.detail != NotifyPointer)
+ {
+ wxFocusEvent event(wxEVT_KILL_FOCUS, canvas->GetId());
+ event.SetEventObject(canvas);
+ canvas->HandleWindowEvent(event);
+ }
+ break;
+ }
+ default:
+ break;
+ }