#include #include #include #include "widget.h" static widgetVtbl vtbl; /* * Forward declarations */ static void widgetDrawChildren(widget *self, cairo_t *cr); /** * Prepares the widget classes vtable. */ static void widgetInitVtbl(widget *self) { static bool initialised = false; if (!initialised) { vtbl.addChild = widgetAddChildImpl; vtbl.removeChild = widgetRemoveChildImpl; vtbl.fireCallbacks = widgetFireCallbacksImpl; vtbl.addEventHandler = widgetAddEventHandlerImpl; vtbl.removeEventHandler = widgetRemoveEventHandlerImpl; vtbl.handleEvent = widgetHandleEventImpl; vtbl.focus = widgetFocusImpl; vtbl.blur = widgetBlurImpl; vtbl.enable = widgetEnableImpl; vtbl.disable = widgetDisableImpl; vtbl.doDraw = NULL; vtbl.destroy = widgetDestroyImpl; initialised = true; } // Set the classes vtable self->vtbl = &vtbl; // Do any overloading of inherited methods here } /* * Widget class constructor */ void widgetInit(widget *self, const char *id) { // Prepare our vtable widgetInitVtbl(self); // Prepare our container self->children = vectorCreate((destroyCallback) widgetDestroy); // Prepare our events table self->eventVtbl = vectorCreate(free); // Copy the ID of the widget self->id = strdup(id); // Default parent is none self->parent = NULL; } /* * Widget class destructor (virtual). */ void widgetDestroyImpl(widget *self) { // Release the container vectorDestroy(self->children); // Release the event handler table vectorDestroy(self->eventVtbl); // Free the ID free(self->id); // Free ourself free(self); } /* * Draws and widget and its child widgets */ void widgetDraw(widget *self, cairo_t *cr) { // Draw ourself widgetDoDraw(self, cr); // Draw our children widgetDrawChildren(self, cr); } /* * Draws the child widgets of self. */ static void widgetDrawChildren(widget *self, cairo_t *cr) { int i; // Draw our children for (i = 0; i < vectorSize(self->children); i++) { widget *child = WIDGET(vectorAt(self->children, i)); cairo_matrix_t current; // Translate such that (0,0) is the location of the widget cairo_get_matrix(cr, ¤t); cairo_translate(cr, child->bounds.topLeft.x, child->bounds.topLeft.y); widgetDraw(child, cr); // Restore the matrix cairo_set_matrix(cr, ¤t); } } widget *widgetGetRoot(widget *self) { // If we are the root widget, return early if (self->parent == NULL) { return self; } // Otherwise search the hierarchy else { widget *current; for (current = self->parent; current->parent; current = current->parent); return current; } } /* * */ widget *widgetFindById(widget *self, const char *id) { // See if we have that ID if (strcmp(self->id, id) == 0) { return self; } // Try our children else { int i; for (i = 0; i < vectorSize(self->children); i++) { // Get the child widget widget *child = vectorAt(self->children, i); // Call its findById method widget *match = widgetFindById(child, id); // If it matched, return if (match) { return match; } } } // If we found nothing return NULL return NULL; } /* * */ bool widgetAddChildImpl(widget *self, widget *child) { // TODO: We need to assert that id is unique; // widgetFindById(widgetGetRoot(self), child->id) == NULL should do // Add the widget vectorAdd(self->children, child); // FIXME: We need to do some arbitration // Set ourself as its parent child->parent = self; return true; } /* * */ void widgetRemoveChildImpl(widget *self, widget *child) { int i; for (i = 0; i < vectorSize(self->children); i++) { // If the child is the to-be-removed widget, remove it if (vectorAt(self->children, i) == child) { // vectorRemoveAt will take care of calling the widgets destructor vectorRemoveAt(self->children, i); } // See if it is one of its children else { widgetRemoveChild(vectorAt(self->children, i), child); } } } /* * */ int widgetAddEventHandlerImpl(widget *self, eventType type, callback handler, void *userData) { eventTableEntry *entry = malloc(sizeof(eventTableEntry)); entry->type = type; entry->callback = handler; entry->userData = userData; // Add the handler to the table vectorAdd(self->eventVtbl, entry); // Offset = size - 1 return vectorSize(self->eventVtbl) - 1; } /* * */ void widgetRemoveEventHandlerImpl(widget *self, int id) { vectorRemoveAt(self->eventVtbl, id); } bool widgetFireCallbacksImpl(widget *self, event *evt) { int i; for (i = 0; i < vectorSize(self->eventVtbl); i++) { eventTableEntry *handler = vectorAt(self->eventVtbl, i); if (handler->type == evt->type) { handler->callback(self, evt, handler->userData); } } // FIXIME return true; } /* * */ void widgetEnableImpl(widget *self) { int i; // First make sure our parent is enabled if (self->parent && !self->parent->isEnabled) { return; } // Enable ourself self->isEnabled = true; // Enable all of our children for (i = 0; i < vectorSize(self->children); i++) { widgetEnable(WIDGET(vectorAt(self->children, i))); } } void widgetDisableImpl(widget *self) { int i; // If we are currently disabled, return if (!self->isEnabled) { return; } // Disable ourself self->isEnabled = false; // Disable our children for (i = 0; i < vectorSize(self->children); i++) { widgetDisable(WIDGET(vectorAt(self->children, i))); } } void widgetFocusImpl(widget *self) { // Check that we are not currently focused if (self->hasFocus) { int i; // Blur any of our currently focused child widgets for (i = 0; i < vectorSize(self->children); i++) { widget *child = vectorAt(self->children, i); if (child->hasFocus) { widgetBlur(child); } } return; } // If we have a parent, focus it if (self->parent) { widgetFocus(self->parent); } // Focus ourself self->hasFocus = true; // Fire our on-focus callbacks event evt; evt.type = EVT_FOCUS; widgetFireCallbacks(self, &evt); } void widgetBlurImpl(widget *self) { widget *current; // Make sure we have focus if (!self->hasFocus) { // TODO: We should log this eventuality return; } // First blur any focused child widgets while ((current = widgetGetCurrentlyFocused(self)) != self) { widgetBlur(current); } // Blur ourself self->hasFocus = false; // Fire off the on-blur callbacks event evt; evt.type = EVT_BLUR; widgetFireCallbacks(self, &evt); } widget *widgetGetCurrentlyFocused(widget *self) { int i; if (!self->hasFocus) { return NULL; } for (i = 0; i < vectorSize(self->children); i++) { widget *child = vectorAt(self->children, i); if (child->hasFocus) { return widgetGetCurrentlyFocused(child); } } // None of our children are focused, return ourself return self; } bool widgetHandleEventImpl(widget *self, event *evt) { switch (evt->type) { case EVT_MOUSE_MOVE: { eventMouse evtMouse = *((eventMouse *) evt); bool newHasMouse = pointInRect(evtMouse.loc, self->bounds); // If we have just `got' the mouse if (newHasMouse && !self->hasMouse) { // Generate a EVT_MOUSE_ENTER event evtMouse.event.type = EVT_MOUSE_ENTER; // Fire the event handler widgetFireCallbacks(self, (event *) &evtMouse); } // If we have just lost the mouse else if (!newHasMouse && self->hasMouse) { // Generate a EVT_MOUSE_LEAVE event evtMouse.event.type = EVT_MOUSE_LEAVE; // Fire the handler widgetFireCallbacks(self, (event *) &evtMouse); } // We had and still have the mouse else if (newHasMouse && self->hasMouse) { // Pass the event as-is widgetFireCallbacks(self, (event *) &evtMouse); } // Update the status of the mouse self->hasMouse = newHasMouse; break; } default: break; } return true; } bool widgetAddChild(widget *self, widget *child) { return WIDGET_GET_VTBL(self)->addChild(self, child); } void widgetRemoveChild(widget *self, widget *child) { WIDGET_GET_VTBL(self)->removeChild(self, child); } int widgetAddEventHandler(widget *self, eventType type, callback handler, void *userData) { return WIDGET_GET_VTBL(self)->addEventHandler(self, type, handler, userData); } void widgetRemoveEventHandler(widget *self, int id) { WIDGET_GET_VTBL(self)->removeEventHandler(self, id); } bool widgetFireCallbacks(widget *self, event *evt) { return WIDGET_GET_VTBL(self)->fireCallbacks(self, evt); } void widgetEnable(widget *self) { WIDGET_GET_VTBL(self)->enable(self); } void widgetDisable(widget *self) { WIDGET_GET_VTBL(self)->disable(self); } void widgetFocus(widget *self) { WIDGET_GET_VTBL(self)->focus(self); } void widgetBlur(widget *self) { WIDGET_GET_VTBL(self)->blur(self); } void widgetDoDraw(widget *self, cairo_t *cr) { WIDGET_GET_VTBL(self)->doDraw(self, cr); } bool widgetHandleEvent(widget *self, event *evt) { return WIDGET_GET_VTBL(self)->handleEvent(self, evt); } void widgetDestroy(widget *self) { return WIDGET_GET_VTBL(self)->destroy(self); }