1
0
mirror of https://github.com/gryf/wmaker.git synced 2025-12-18 20:10:29 +01:00
Files
wmaker/WINGs/wbutton.c
Christophe CURIS c89e8df33f WINGs: Added 'const' attribute to functions in wbutton, wframe, wlabel, wlist, wwindow
This makes both the API and local function const-correct on their
input parameters.
2013-05-09 16:56:28 +01:00

749 lines
17 KiB
C

#include "WINGsP.h"
typedef struct W_Button {
W_Class widgetClass;
WMView *view;
char *caption;
char *altCaption;
WMFont *font;
WMColor *textColor;
WMColor *altTextColor;
WMColor *disTextColor;
W_Pixmap *image;
W_Pixmap *altImage;
W_Pixmap *dimage;
void *clientData;
WMAction *action;
int tag;
int groupIndex;
float periodicDelay;
float periodicInterval;
WMHandlerID *timer; /* for continuous mode */
struct {
WMButtonType type:4;
WMImagePosition imagePosition:4;
WMAlignment alignment:2;
unsigned int selected:1;
unsigned int enabled:1;
unsigned int dimsWhenDisabled:1;
unsigned int bordered:1;
unsigned int springLoaded:1;
unsigned int pushIn:1; /* change relief while pushed */
unsigned int pushLight:1; /* highlight while pushed */
unsigned int pushChange:1; /* change caption while pushed */
unsigned int stateLight:1; /* state indicated by highlight */
unsigned int stateChange:1; /* state indicated by caption change */
unsigned int statePush:1; /* state indicated by relief */
unsigned int continuous:1; /* continually perform action */
unsigned int prevSelected:1;
unsigned int pushed:1;
unsigned int wasPushed:1;
unsigned int redrawPending:1;
unsigned int addedObserver:1;
} flags;
} Button;
#define DEFAULT_BUTTON_WIDTH 60
#define DEFAULT_BUTTON_HEIGHT 24
#define DEFAULT_BUTTON_ALIGNMENT WACenter
#define DEFAULT_BUTTON_IS_BORDERED True
#define DEFAULT_RADIO_WIDTH 100
#define DEFAULT_RADIO_HEIGHT 20
#define DEFAULT_RADIO_ALIGNMENT WALeft
#define DEFAULT_RADIO_IMAGE_POSITION WIPLeft
#define DEFAULT_RADIO_TEXT "Radio"
#define DEFAULT_SWITCH_WIDTH 100
#define DEFAULT_SWITCH_HEIGHT 20
#define DEFAULT_SWITCH_ALIGNMENT WALeft
#define DEFAULT_SWITCH_IMAGE_POSITION WIPLeft
#define DEFAULT_SWITCH_TEXT "Switch"
static void destroyButton(Button * bPtr);
static void paintButton(Button * bPtr);
static void handleEvents(XEvent * event, void *data);
static void handleActionEvents(XEvent * event, void *data);
static char *WMPushedRadioNotification = "WMPushedRadioNotification";
#define NFONT(b) (b)->view->screen->normalFont
WMButton *WMCreateCustomButton(WMWidget * parent, int behaviourMask)
{
Button *bPtr;
bPtr = wmalloc(sizeof(Button));
bPtr->widgetClass = WC_Button;
bPtr->view = W_CreateView(W_VIEW(parent));
if (!bPtr->view) {
wfree(bPtr);
return NULL;
}
bPtr->view->self = bPtr;
bPtr->flags.type = 0;
bPtr->flags.springLoaded = (behaviourMask & WBBSpringLoadedMask) != 0;
bPtr->flags.pushIn = (behaviourMask & WBBPushInMask) != 0;
bPtr->flags.pushChange = (behaviourMask & WBBPushChangeMask) != 0;
bPtr->flags.pushLight = (behaviourMask & WBBPushLightMask) != 0;
bPtr->flags.stateLight = (behaviourMask & WBBStateLightMask) != 0;
bPtr->flags.stateChange = (behaviourMask & WBBStateChangeMask) != 0;
bPtr->flags.statePush = (behaviourMask & WBBStatePushMask) != 0;
W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
bPtr->flags.enabled = 1;
bPtr->flags.dimsWhenDisabled = 1;
WMCreateEventHandler(bPtr->view, ExposureMask | StructureNotifyMask, handleEvents, bPtr);
WMCreateEventHandler(bPtr->view, ButtonPressMask | ButtonReleaseMask
| EnterWindowMask | LeaveWindowMask, handleActionEvents, bPtr);
W_ResizeView(bPtr->view, DEFAULT_BUTTON_WIDTH, DEFAULT_BUTTON_HEIGHT);
bPtr->flags.alignment = DEFAULT_BUTTON_ALIGNMENT;
bPtr->flags.bordered = DEFAULT_BUTTON_IS_BORDERED;
return bPtr;
}
WMButton *WMCreateButton(WMWidget * parent, WMButtonType type)
{
W_Screen *scrPtr = W_VIEW(parent)->screen;
Button *bPtr;
switch (type) {
case WBTMomentaryPush:
bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushInMask | WBBPushLightMask);
break;
case WBTMomentaryChange:
bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushChangeMask);
break;
case WBTPushOnPushOff:
bPtr = WMCreateCustomButton(parent, WBBPushInMask | WBBStatePushMask | WBBStateLightMask);
break;
case WBTToggle:
bPtr = WMCreateCustomButton(parent, WBBPushInMask | WBBStateChangeMask | WBBStatePushMask);
break;
case WBTOnOff:
bPtr = WMCreateCustomButton(parent, WBBStateLightMask);
break;
case WBTSwitch:
bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
bPtr->flags.bordered = 0;
bPtr->image = WMRetainPixmap(scrPtr->checkButtonImageOff);
bPtr->altImage = WMRetainPixmap(scrPtr->checkButtonImageOn);
break;
case WBTRadio:
bPtr = WMCreateCustomButton(parent, WBBStateChangeMask);
bPtr->flags.bordered = 0;
bPtr->image = WMRetainPixmap(scrPtr->radioButtonImageOff);
bPtr->altImage = WMRetainPixmap(scrPtr->radioButtonImageOn);
break;
default:
case WBTMomentaryLight:
bPtr = WMCreateCustomButton(parent, WBBSpringLoadedMask | WBBPushLightMask);
bPtr->flags.bordered = 1;
break;
}
bPtr->flags.type = type;
if (type == WBTRadio) {
W_ResizeView(bPtr->view, DEFAULT_RADIO_WIDTH, DEFAULT_RADIO_HEIGHT);
WMSetButtonText(bPtr, DEFAULT_RADIO_TEXT);
bPtr->flags.alignment = DEFAULT_RADIO_ALIGNMENT;
bPtr->flags.imagePosition = DEFAULT_RADIO_IMAGE_POSITION;
} else if (type == WBTSwitch) {
W_ResizeView(bPtr->view, DEFAULT_SWITCH_WIDTH, DEFAULT_SWITCH_HEIGHT);
WMSetButtonText(bPtr, DEFAULT_SWITCH_TEXT);
bPtr->flags.alignment = DEFAULT_SWITCH_ALIGNMENT;
bPtr->flags.imagePosition = DEFAULT_SWITCH_IMAGE_POSITION;
}
return bPtr;
}
static void updateDisabledMask(WMButton * bPtr)
{
WMScreen *scr = WMWidgetScreen(bPtr);
Display *dpy = scr->display;
if (bPtr->image) {
XGCValues gcv;
if (bPtr->dimage->mask) {
XFreePixmap(dpy, bPtr->dimage->mask);
bPtr->dimage->mask = None;
}
if (bPtr->flags.dimsWhenDisabled) {
bPtr->dimage->mask = XCreatePixmap(dpy, scr->stipple,
bPtr->dimage->width, bPtr->dimage->height, 1);
XSetForeground(dpy, scr->monoGC, 0);
XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
bPtr->dimage->width, bPtr->dimage->height);
gcv.foreground = 1;
gcv.background = 0;
gcv.stipple = scr->stipple;
gcv.fill_style = FillStippled;
gcv.clip_mask = bPtr->image->mask;
gcv.clip_x_origin = 0;
gcv.clip_y_origin = 0;
XChangeGC(dpy, scr->monoGC, GCForeground | GCBackground | GCStipple
| GCFillStyle | GCClipMask | GCClipXOrigin | GCClipYOrigin, &gcv);
XFillRectangle(dpy, bPtr->dimage->mask, scr->monoGC, 0, 0,
bPtr->dimage->width, bPtr->dimage->height);
gcv.fill_style = FillSolid;
gcv.clip_mask = None;
XChangeGC(dpy, scr->monoGC, GCFillStyle | GCClipMask, &gcv);
}
}
}
void WMSetButtonImageDefault(WMButton * bPtr)
{
WMSetButtonImage(bPtr, WMWidgetScreen(bPtr)->buttonArrow);
WMSetButtonAltImage(bPtr, WMWidgetScreen(bPtr)->pushedButtonArrow);
}
void WMSetButtonImage(WMButton * bPtr, WMPixmap * image)
{
if (bPtr->image != NULL)
WMReleasePixmap(bPtr->image);
bPtr->image = WMRetainPixmap(image);
if (bPtr->dimage) {
bPtr->dimage->pixmap = None;
WMReleasePixmap(bPtr->dimage);
bPtr->dimage = NULL;
}
if (image) {
bPtr->dimage = WMCreatePixmapFromXPixmaps(WMWidgetScreen(bPtr),
image->pixmap, None,
image->width, image->height, image->depth);
updateDisabledMask(bPtr);
}
if (bPtr->view->flags.realized) {
paintButton(bPtr);
}
}
void WMSetButtonAltImage(WMButton * bPtr, WMPixmap * image)
{
if (bPtr->altImage != NULL)
WMReleasePixmap(bPtr->altImage);
bPtr->altImage = WMRetainPixmap(image);
if (bPtr->view->flags.realized) {
paintButton(bPtr);
}
}
void WMSetButtonImagePosition(WMButton * bPtr, WMImagePosition position)
{
bPtr->flags.imagePosition = position;
if (bPtr->view->flags.realized) {
paintButton(bPtr);
}
}
void WMSetButtonTextAlignment(WMButton * bPtr, WMAlignment alignment)
{
bPtr->flags.alignment = alignment;
if (bPtr->view->flags.realized) {
paintButton(bPtr);
}
}
void WMSetButtonText(WMButton * bPtr, const char *text)
{
if (bPtr->caption)
wfree(bPtr->caption);
if (text != NULL) {
bPtr->caption = wstrdup(text);
} else {
bPtr->caption = NULL;
}
if (bPtr->view->flags.realized) {
paintButton(bPtr);
}
}
void WMSetButtonAltText(WMButton * bPtr, const char *text)
{
if (bPtr->altCaption)
wfree(bPtr->altCaption);
if (text != NULL) {
bPtr->altCaption = wstrdup(text);
} else {
bPtr->altCaption = NULL;
}
if (bPtr->view->flags.realized) {
paintButton(bPtr);
}
}
void WMSetButtonTextColor(WMButton * bPtr, WMColor * color)
{
if (bPtr->textColor)
WMReleaseColor(bPtr->textColor);
bPtr->textColor = WMRetainColor(color);
}
void WMSetButtonAltTextColor(WMButton * bPtr, WMColor * color)
{
if (bPtr->altTextColor)
WMReleaseColor(bPtr->altTextColor);
bPtr->altTextColor = WMRetainColor(color);
}
void WMSetButtonDisabledTextColor(WMButton * bPtr, WMColor * color)
{
if (bPtr->disTextColor)
WMReleaseColor(bPtr->disTextColor);
bPtr->disTextColor = WMRetainColor(color);
}
void WMSetButtonSelected(WMButton * bPtr, int isSelected)
{
bPtr->flags.selected = isSelected ? 1 : 0;
if (bPtr->view->flags.realized) {
paintButton(bPtr);
}
if (bPtr->groupIndex > 0)
WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
}
int WMGetButtonSelected(WMButton * bPtr)
{
CHECK_CLASS(bPtr, WC_Button);
return bPtr->flags.selected;
}
void WMSetButtonBordered(WMButton * bPtr, int isBordered)
{
bPtr->flags.bordered = isBordered;
if (bPtr->view->flags.realized) {
paintButton(bPtr);
}
}
void WMSetButtonFont(WMButton * bPtr, WMFont * font)
{
if (bPtr->font)
WMReleaseFont(bPtr->font);
bPtr->font = WMRetainFont(font);
}
void WMSetButtonEnabled(WMButton * bPtr, Bool flag)
{
bPtr->flags.enabled = ((flag == 0) ? 0 : 1);
if (bPtr->view->flags.mapped) {
paintButton(bPtr);
}
}
int WMGetButtonEnabled(WMButton * bPtr)
{
CHECK_CLASS(bPtr, WC_Button);
return bPtr->flags.enabled;
}
void WMSetButtonImageDimsWhenDisabled(WMButton * bPtr, Bool flag)
{
bPtr->flags.dimsWhenDisabled = ((flag == 0) ? 0 : 1);
updateDisabledMask(bPtr);
}
void WMSetButtonTag(WMButton * bPtr, int tag)
{
bPtr->tag = tag;
}
void WMPerformButtonClick(WMButton * bPtr)
{
CHECK_CLASS(bPtr, WC_Button);
if (!bPtr->flags.enabled)
return;
bPtr->flags.pushed = 1;
bPtr->flags.selected = 1;
if (bPtr->view->flags.mapped) {
paintButton(bPtr);
XFlush(WMScreenDisplay(WMWidgetScreen(bPtr)));
wusleep(20000);
}
bPtr->flags.pushed = 0;
if (bPtr->groupIndex > 0) {
WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
}
if (bPtr->action)
(*bPtr->action) (bPtr, bPtr->clientData);
if (bPtr->view->flags.mapped)
paintButton(bPtr);
}
void WMSetButtonAction(WMButton * bPtr, WMAction * action, void *clientData)
{
CHECK_CLASS(bPtr, WC_Button);
bPtr->action = action;
bPtr->clientData = clientData;
}
static void radioPushObserver(void *observerData, WMNotification * notification)
{
WMButton *bPtr = (WMButton *) observerData;
WMButton *pushedButton = (WMButton *) WMGetNotificationObject(notification);
if (bPtr != pushedButton && pushedButton->groupIndex == bPtr->groupIndex && bPtr->groupIndex != 0) {
if (bPtr->flags.selected) {
bPtr->flags.selected = 0;
paintButton(bPtr);
}
}
}
void WMGroupButtons(WMButton * bPtr, WMButton * newMember)
{
static int tagIndex = 0;
CHECK_CLASS(bPtr, WC_Button);
CHECK_CLASS(newMember, WC_Button);
if (!bPtr->flags.addedObserver) {
WMAddNotificationObserver(radioPushObserver, bPtr, WMPushedRadioNotification, NULL);
bPtr->flags.addedObserver = 1;
}
if (!newMember->flags.addedObserver) {
WMAddNotificationObserver(radioPushObserver, newMember, WMPushedRadioNotification, NULL);
newMember->flags.addedObserver = 1;
}
if (bPtr->groupIndex == 0) {
bPtr->groupIndex = ++tagIndex;
}
newMember->groupIndex = bPtr->groupIndex;
}
void WMSetButtonContinuous(WMButton * bPtr, Bool flag)
{
bPtr->flags.continuous = ((flag == 0) ? 0 : 1);
if (bPtr->timer) {
WMDeleteTimerHandler(bPtr->timer);
bPtr->timer = NULL;
}
}
void WMSetButtonPeriodicDelay(WMButton * bPtr, float delay, float interval)
{
bPtr->periodicInterval = interval;
bPtr->periodicDelay = delay;
}
static void paintButton(Button * bPtr)
{
W_Screen *scrPtr = bPtr->view->screen;
WMReliefType relief;
int offset;
char *caption;
WMPixmap *image;
WMColor *textColor;
WMColor *backColor;
backColor = NULL;
caption = bPtr->caption;
if (bPtr->flags.enabled) {
textColor = (bPtr->textColor != NULL ? bPtr->textColor : scrPtr->black);
} else {
textColor = (bPtr->disTextColor != NULL ? bPtr->disTextColor : scrPtr->darkGray);
}
if (bPtr->flags.enabled || !bPtr->dimage)
image = bPtr->image;
else
image = bPtr->dimage;
offset = 0;
if (bPtr->flags.bordered)
relief = WRRaised;
else
relief = WRFlat;
if (bPtr->flags.selected) {
if (bPtr->flags.stateLight) {
backColor = scrPtr->white;
textColor = scrPtr->black;
}
if (bPtr->flags.stateChange) {
if (bPtr->altCaption)
caption = bPtr->altCaption;
if (bPtr->altImage)
image = bPtr->altImage;
if (bPtr->altTextColor)
textColor = bPtr->altTextColor;
}
if (bPtr->flags.statePush && bPtr->flags.bordered) {
relief = WRSunken;
offset = 1;
}
}
if (bPtr->flags.pushed) {
if (bPtr->flags.pushIn) {
relief = WRPushed;
offset = 1;
}
if (bPtr->flags.pushLight) {
backColor = scrPtr->white;
textColor = scrPtr->black;
}
if (bPtr->flags.pushChange) {
if (bPtr->altCaption)
caption = bPtr->altCaption;
if (bPtr->altImage)
image = bPtr->altImage;
if (bPtr->altTextColor)
textColor = bPtr->altTextColor;
}
}
W_PaintTextAndImage(bPtr->view, True, textColor,
(bPtr->font != NULL ? bPtr->font : scrPtr->normalFont),
relief, caption, bPtr->flags.alignment, image,
bPtr->flags.imagePosition, backColor, offset);
}
static void handleEvents(XEvent * event, void *data)
{
Button *bPtr = (Button *) data;
CHECK_CLASS(data, WC_Button);
switch (event->type) {
case Expose:
if (event->xexpose.count != 0)
break;
paintButton(bPtr);
break;
case DestroyNotify:
destroyButton(bPtr);
break;
}
}
static void autoRepeat(void *data)
{
Button *bPtr = (Button *) data;
if (bPtr->action && bPtr->flags.pushed)
(*bPtr->action) (bPtr, bPtr->clientData);
bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicInterval * 1000), autoRepeat, bPtr);
}
static void handleActionEvents(XEvent * event, void *data)
{
Button *bPtr = (Button *) data;
int doclick = 0, dopaint = 0;
CHECK_CLASS(data, WC_Button);
if (!bPtr->flags.enabled)
return;
switch (event->type) {
case EnterNotify:
if (bPtr->groupIndex == 0) {
bPtr->flags.pushed = bPtr->flags.wasPushed;
if (bPtr->flags.pushed) {
bPtr->flags.selected = !bPtr->flags.prevSelected;
dopaint = 1;
}
}
break;
case LeaveNotify:
if (bPtr->groupIndex == 0) {
bPtr->flags.wasPushed = bPtr->flags.pushed;
if (bPtr->flags.pushed) {
bPtr->flags.selected = bPtr->flags.prevSelected;
dopaint = 1;
}
bPtr->flags.pushed = 0;
}
break;
case ButtonPress:
if (event->xbutton.button == Button1) {
bPtr->flags.prevSelected = bPtr->flags.selected;
bPtr->flags.wasPushed = 0;
bPtr->flags.pushed = 1;
if (bPtr->groupIndex > 0) {
bPtr->flags.selected = 1;
dopaint = 1;
break;
}
bPtr->flags.selected = !bPtr->flags.selected;
dopaint = 1;
if (bPtr->flags.continuous && !bPtr->timer) {
bPtr->timer = WMAddTimerHandler((int)(bPtr->periodicDelay * 1000),
autoRepeat, bPtr);
}
}
break;
case ButtonRelease:
if (event->xbutton.button == Button1) {
if (bPtr->flags.pushed) {
if (bPtr->groupIndex == 0 || (bPtr->flags.selected && bPtr->groupIndex > 0))
doclick = 1;
dopaint = 1;
if (bPtr->flags.springLoaded) {
bPtr->flags.selected = bPtr->flags.prevSelected;
}
}
bPtr->flags.pushed = 0;
}
if (bPtr->timer) {
WMDeleteTimerHandler(bPtr->timer);
bPtr->timer = NULL;
}
break;
}
if (dopaint)
paintButton(bPtr);
if (doclick) {
if (bPtr->flags.selected && bPtr->groupIndex > 0) {
WMPostNotificationName(WMPushedRadioNotification, bPtr, NULL);
}
if (bPtr->action)
(*bPtr->action) (bPtr, bPtr->clientData);
}
}
static void destroyButton(Button * bPtr)
{
if (bPtr->flags.addedObserver) {
WMRemoveNotificationObserver(bPtr);
}
if (bPtr->timer)
WMDeleteTimerHandler(bPtr->timer);
if (bPtr->font)
WMReleaseFont(bPtr->font);
if (bPtr->caption)
wfree(bPtr->caption);
if (bPtr->altCaption)
wfree(bPtr->altCaption);
if (bPtr->textColor)
WMReleaseColor(bPtr->textColor);
if (bPtr->altTextColor)
WMReleaseColor(bPtr->altTextColor);
if (bPtr->disTextColor)
WMReleaseColor(bPtr->disTextColor);
if (bPtr->image)
WMReleasePixmap(bPtr->image);
if (bPtr->dimage) {
/* yuck.. kluge */
bPtr->dimage->pixmap = None;
WMReleasePixmap(bPtr->dimage);
}
if (bPtr->altImage)
WMReleasePixmap(bPtr->altImage);
wfree(bPtr);
}