/* -*-c-*- */ /* * FvwmButtons, copyright 1996, Jarl Totland * * This module, and the entire GoodStuff program, and the concept for * interfacing this module to the Window Manager, are all original work * by Robert Nation * * Copyright 1993, Robert Nation. No guarantees or warantees or anything * are provided or implied in any way whatsoever. Use this program at your * own risk. Permission to use this program for any purpose is given, * as long as the copyright is kept intact. * */ /* This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include "FvwmButtons.h" #include "button.h" extern char *MyName; int buttonXPos(const button_info *b, int i) { int column = i % b->parent->c->num_columns; return b->parent->c->xpos + (b->parent->c->width * column / b->parent->c->num_columns); } int buttonYPos(const button_info *b, int i) { int row = i / b->parent->c->num_columns; return b->parent->c->ypos + (b->parent->c->height * row / b->parent->c->num_rows); } int buttonWidth(const button_info *b) { int column = b->n % b->parent->c->num_columns; int column2 = column + b->BWidth; return (b->parent->c->width * column2 / b->parent->c->num_columns) - (b->parent->c->width * column / b->parent->c->num_columns); } int buttonHeight(const button_info *b) { int row = b->n / b->parent->c->num_columns; int row2 = row + b->BHeight; return (b->parent->c->height * row2 / b->parent->c->num_rows) - (b->parent->c->height * row / b->parent->c->num_rows); } int buttonSwallowCount(button_info *b) { return (b->flags.b_Swallow || b->flags.b_Panel) ? (b->swallow & b_Count) : 0; } /** *** buttonInfo() *** Give lots of info for this button: XPos, YPos, XPad, YPad, Frame(signed) **/ void buttonInfo(const button_info *b, int *x, int *y, int *px, int *py, int *f) { ushort bPadding = (b->flags.b_Padding ? 0 : 1); ushort bFrame = (b->flags.b_Frame ? 0 : 1); *x=buttonXPos(b,b->n); *y=buttonYPos(b,b->n); *px=b->xpad; *py=b->ypad; *f=b->framew; if(b->flags.b_Container && bFrame) { *f=0; bFrame = 0; } if((b->flags.b_Container || b->flags.b_Swallow) && bPadding) { *px=*py=0; bPadding = 0; } while((bPadding || bFrame) && (b=b->parent)) { if(bFrame && b->c->flags.b_Frame) { *f=b->c->framew; bFrame = 0; } if(bPadding && b->c->flags.b_Padding) { *px=b->c->xpad; *py=b->c->ypad; bPadding = 0; } } } /** *** GetInternalSize() **/ void GetInternalSize(button_info *b,int *x,int *y,int *w,int *h) { int f; int px,py; buttonInfo(b,x,y,&px,&py,&f); f=abs(f); *w=buttonWidth(b)-2*(px+f); *h=buttonHeight(b)-2*(py+f); *x+=f+px; *y+=f+py; if (*w < 1) *w = 1; if (*h < 1) *h = 1; return; } /** *** buttonFrameSigned() *** Give the signed framewidth for this button. **/ int buttonFrameSigned(button_info *b) { if(b->flags.b_Frame) return b->framew; if(b->flags.b_Container) /* Containers usually gets 0 relief */ return 0; while((b=b->parent)) if(b->c->flags.b_Frame) return b->c->framew; #ifdef DEBUG fprintf(stderr,"%s: BUG: No relief width definition?\n",MyName); #endif return 0; } /** *** buttonXPad() *** Give the x padding for this button **/ int buttonXPad(button_info *b) { if(b->flags.b_Padding) return b->xpad; if(b->flags.b_Container || b->flags.b_Swallow) /* Normally no padding for these */ return 0; while((b=b->parent)) if(b->c->flags.b_Padding) return b->c->xpad; #ifdef DEBUG fprintf(stderr,"%s: BUG: No padding definition?\n",MyName); #endif return 0; } /** *** buttonYPad() *** Give the y padding for this button **/ int buttonYPad(button_info *b) { if(b->flags.b_Padding) return b->ypad; if(b->flags.b_Container || b->flags.b_Swallow) /* Normally no padding for these */ return 0; while((b=b->parent)) if(b->c->flags.b_Padding) return b->c->ypad; #ifdef DEBUG fprintf(stderr,"%s: BUG: No padding definition?\n",MyName); #endif return 0; } /** *** buttonFont() *** Give the font pointer for this button **/ FlocaleFont *buttonFont(button_info *b) { if(b->flags.b_Font) return b->Ffont; while((b=b->parent)) if(b->c->flags.b_Font) return b->c->Ffont; #ifdef DEBUG fprintf(stderr,"%s: BUG: No font definition?\n",MyName); #endif return None; } /** *** buttonFore() *** Give the foreground pixel of this button **/ Pixel buttonFore(const button_info *b) { if(b->flags.b_Fore) return b->fc; while ((b=b->parent)) { if(b->c->flags.b_Fore) return b->c->fc; } #ifdef DEBUG fprintf(stderr,"%s: BUG: No foreground definition?\n",MyName); #endif return None; } /** *** buttonBack() *** Give the background pixel of this button **/ Pixel buttonBack(const button_info *b) { if(b->flags.b_Back) return b->bc; while((b=b->parent)) { if(b->c->flags.b_Back) return b->c->bc; } #ifdef DEBUG fprintf(stderr,"%s: BUG: No background definition?\n",MyName); #endif return None; } /** *** buttonHilite() *** Give the relief pixel of this button **/ Pixel buttonHilite(button_info *b) { if(b->flags.b_Back) return b->hc; while((b=b->parent)) { if(b->c->flags.b_Back) return b->c->hc; } #ifdef DEBUG fprintf(stderr,"%s: BUG: No background definition?\n",MyName); #endif return None; } /** *** buttonShadow() *** Give the shadow pixel of this button **/ Pixel buttonShadow(button_info *b) { if(b->flags.b_Back) return b->sc; while((b=b->parent)) { if(b->c->flags.b_Back) return b->c->sc; } #ifdef DEBUG fprintf(stderr,"%s: BUG: No background definition?\n",MyName); #endif return None; } int buttonColorset(button_info *b) { if (b->flags.b_Hangon) { if (b->flags.b_PressColorset) return b->pressColorset; if (UberButton->c->flags.b_PressColorset) return UberButton->c->pressColorset; } else if (b == ActiveButton && b->flags.b_ActiveColorset) return b->activeColorset; else if (b == ActiveButton && UberButton->c->flags.b_ActiveColorset) return UberButton->c->activeColorset; else if (b == CurrentButton && b->flags.b_PressColorset) return b->pressColorset; else if (b == CurrentButton && UberButton->c->flags.b_PressColorset) return UberButton->c->pressColorset; if (b->flags.b_Colorset) return b->colorset; else if (b->flags.b_Container && b->c->flags.b_Colorset) return b->c->colorset; while ((b = b->parent)) { if (b->c->flags.b_Colorset) return b->c->colorset; } return -1; } char *buttonTitle (button_info *b) { if (b->flags.b_Hangon) { if (b->flags.b_PressTitle) return b->pressTitle; } /* If this is the current active button but no explicit ActiveTitle was specified, use the Title (if there is one). Similarly for PressTitle. */ else if (b == ActiveButton && b->flags.b_ActiveTitle) return b->activeTitle; else if (b == CurrentButton && b->flags.b_PressTitle) return b->pressTitle; if (b->flags.b_Title) return b->title; return NULL; } FvwmPicture *buttonIcon (button_info *b) { if (b->flags.b_Hangon) { if (b->flags.b_PressIcon) return b->pressicon; } else if (b == ActiveButton && b->flags.b_ActiveIcon) return b->activeicon; else if (b == CurrentButton && b->flags.b_PressIcon) return b->pressicon; /* b->icon == None if no icon specified */ return b->icon; } unsigned short iconFlagSet (button_info *b) { if (b->flags.b_Hangon) { if (b->flags.b_PressIcon) return 1; } else if (b == ActiveButton && b->flags.b_ActiveIcon) return 1; else if (b == CurrentButton && b->flags.b_PressIcon) return 1; return (b->flags.b_Icon ? 1 : 0); } int buttonBackgroundButton(button_info *b, button_info **r_b) { button_info *tmpb, *pb; Bool done = False; tmpb = pb = b; while(tmpb->parent != NULL && !done) { pb = tmpb; if (tmpb->flags.b_Container) { if(tmpb->c->flags.b_Colorset) { done = True; } else if (!(tmpb->c->flags.b_IconParent) && !(tmpb->c->flags.b_ColorsetParent)) { done = True; } } else if (tmpb->flags.b_Swallow && buttonSwallowCount(b) == 3 && tmpb->flags.b_Colorset) { done = True; } else if (tmpb->flags.b_Colorset) { done = True; } else if (!tmpb->flags.b_IconBack && !tmpb->flags.b_IconParent && !tmpb->flags.b_Swallow && !tmpb->flags.b_ColorsetParent) { done = True; } tmpb = tmpb->parent; } if (done) { if (r_b != NULL) *r_b = pb; return True; } return False; } /** *** buttonSwallow() *** Give the swallowing flags for this button **/ byte buttonSwallow(button_info *b) { byte s=0,t=0; if(b->flags.b_Swallow || b->flags.b_Panel) { s=b->swallow; t=b->swallow_mask; } while((b=b->parent)) { if(b->c->flags.b_Swallow || b->c->flags.b_Panel) { s&=~(b->c->swallow_mask&~t); s|=(b->c->swallow&b->c->swallow_mask&~t); t|=b->c->swallow_mask; } } return s; } /** *** buttonJustify() *** Give the justify flags for this button **/ byte buttonJustify(button_info *b) { byte j=1,i=0; if(b->flags.b_Justify) { i=b->justify_mask; j=b->justify; } while((b=b->parent)) { if(b->c->flags.b_Justify) { j&=~(b->c->justify_mask&~i); j|=(b->c->justify&b->c->justify_mask&~i); i|=b->c->justify_mask; } } return j; } /* ---------------------------- button creation ---------------------------- */ /** *** alloc_buttonlist() *** Makes sure the list of butten_info's is long enough, if not it reallocates *** a longer one. This happens in steps of 32. Inital length is 0. **/ void alloc_buttonlist(button_info *ub,int num) { button_info **bb; int i; /* needed to prevent a gcc -O3 bug */ volatile int old; if(num>=ub->c->allocated_buttons) { old=ub->c->allocated_buttons; if(num(old+32)) /* test for numc->allocated_buttons<=num) ub->c->allocated_buttons+=32; bb=(button_info**) mymalloc(ub->c->allocated_buttons*sizeof(button_info*)); for(i=old;ic->allocated_buttons;i++) bb[i]=NULL; if(ub->c->buttons) { for(i=0;ic->buttons[i]; free(ub->c->buttons); } ub->c->buttons=bb; } } /** *** alloc_button() *** Allocates memory for a new button struct. Calles alloc_buttonlist to *** assure enough space is present. Also initiates most elements of the struct. **/ button_info *alloc_button(button_info *ub,int num) { button_info *b; if(num>=ub->c->allocated_buttons) alloc_buttonlist(ub,num); if(ub->c->buttons[num]) { fprintf(stderr,"%s: Allocated button twice, report bug twice\n",MyName); exit(2); } b=(button_info*)mymalloc(sizeof(button_info)); ub->c->buttons[num]=b; memset((void *)b, 0, sizeof(*b)); b->swallow = 0; b->BWidth = b->BHeight = 1; b->BPosX = b->BPosY = 0; b->parent = ub; b->n = -1; b->IconWin = 0; b->PanelWin = 0; b->framew = 1; b->xpad = 2; b->ypad = 4; b->w=1; b->h=1; b->bw=1; #if 0 if(b->parent != NULL) { if (b->parent->c->flags & b_Colorset) b->flags |= b_ColorsetParent; } #endif return(b); } /** *** MakeContainer() *** Allocs and sets the container-specific fields of a button. **/ void MakeContainer(button_info *b) { b->c=(container_info*)mymalloc(sizeof(container_info)); memset((void *)b->c, 0, sizeof(container_info)); b->flags.b_Container = 1; if(b->parent != NULL) { if (b->parent->c->flags.b_IconBack || b->parent->c->flags.b_IconParent) b->c->flags.b_IconParent = 1; if (b->parent->c->flags.b_Colorset || b->parent->c->flags.b_ColorsetParent) b->c->flags.b_ColorsetParent = 1; } else /* This applies to the UberButton */ { b->c->flags.b_Font = 1; b->c->flags.b_Padding = 1; b->c->flags.b_Frame = 1; b->c->flags.b_Back = 1; b->c->flags.b_Fore = 1; b->c->font_string=safestrdup("fixed"); b->c->xpad=2; b->c->ypad=4; b->c->back=safestrdup("rgb:90/80/90"); b->c->fore=safestrdup("black"); b->c->framew=2; } } /* -------------------------- button administration ------------------------ */ /** *** NumberButtons() *** Prepare the n fields in each button **/ void NumberButtons(button_info *b) { int i=-1; while(++ic->num_buttons) if(b->c->buttons[i]) { b->c->buttons[i]->n=i; if(b->c->buttons[i]->flags.b_Container) NumberButtons(b->c->buttons[i]); } } /** *** PlaceAndExpandButton() *** Places a button in it's container and claims all needed slots. **/ char PlaceAndExpandButton(int x, int y, button_info *b, button_info *ub) { int i,j,k; container_info *c=ub->c; i = x+y*c->num_columns; if (x>=c->num_columns || x<0) { fprintf(stderr,"%s: Button out of horizontal range. Quitting.\n",MyName); fprintf(stderr,"Button=%d num_columns=%d BPosX=%d\n", i,c->num_columns,b->BPosX); exit(1); } if (y>=c->num_rows || y<0) { if (b->flags.b_PosFixed || !ub->c->flags.b_SizeSmart || y<0) { fprintf(stderr,"%s: Button out of vertical range. Quitting.\n", MyName); fprintf(stderr,"Button=%d num_rows=%d BPosY=%d\n", i,c->num_rows,b->BPosY); exit(1); } c->num_rows=y+b->BHeight; c->num_buttons=c->num_columns*c->num_rows; alloc_buttonlist(ub,c->num_buttons); } if(x+b->BWidth>c->num_columns) { fprintf(stderr,"%s: Button too wide. giving up\n",MyName); fprintf(stderr,"Button=%d num_columns=%d bwidth=%d w=%d\n", i,c->num_columns,b->BWidth,x); b->BWidth = c->num_columns-x; } if(y+b->BHeight>c->num_rows) { if (c->flags.b_SizeSmart) { c->num_rows=y+b->BHeight; c->num_buttons=c->num_columns*c->num_rows; alloc_buttonlist(ub,c->num_buttons); } else { fprintf(stderr,"%s: Button too tall. Giving up\n",MyName); fprintf(stderr,"Button=%d num_rows=%d bheight=%d h=%d\n", i,c->num_rows,b->BHeight,y); b->BHeight = c->num_rows-y; } } /* check if buttons are free */ for(k=0;kBHeight;k++) for(j=0;jBWidth;j++) if (c->buttons[i+j+k*c->num_columns]) return 1; /* claim all buttons */ for(k=0;kBHeight;k++) for(j=0;jBWidth;j++) c->buttons[i+j+k*c->num_columns] = b; b->BPosX = x; b->BPosY = y; return 0; } /** *** ShrinkButton() *** Frees all but the upper left slot a button uses in it's container. **/ void ShrinkButton(button_info *b, container_info *c) { int i,j,k,l; if (!b) { fprintf(stderr,"error: shrink1: button is empty but shouldn't\n"); exit(1); } i = b->BPosX+b->BPosY*c->num_columns; /* free all buttons but the upper left corner */ for(k=0;kBHeight;k++) { for(j=0;jBWidth;j++) { if(j||k) { l = i+j+k*c->num_columns; if (c->buttons[l] != b) { fprintf(stderr,"error: shrink2: button was stolen\n"); exit(1); } c->buttons[l] = NULL; } } } } /** *** ShuffleButtons() *** Orders and sizes the buttons in the UberButton, corrects num_rows and *** num_columns in containers. **/ void ShuffleButtons(button_info *ub) { int i,actual_buttons_used; int next_button_x, next_button_y, num_items; button_info *b; button_info **local_buttons; container_info *c=ub->c; /* make local copy of buttons in ub */ num_items = c->num_buttons; local_buttons=(button_info**)mymalloc(sizeof(button_info)*num_items); for(i=0;ibuttons[i]; c->buttons[i] = NULL; } /* Allow for multi-width/height buttons */ actual_buttons_used = 0; for(i=0;iBWidth*local_buttons[i]->BHeight; if (!c->flags.b_SizeFixed||!(c->num_rows)||!(c->num_columns)) { /* Size and create the window */ if(c->num_rows==0 && c->num_columns==0) c->num_rows=2; if(c->num_columns==0) c->num_columns=1+(actual_buttons_used-1)/c->num_rows; if(c->num_rows==0) c->num_rows=1+(actual_buttons_used-1)/c->num_columns; while(c->num_rows * c->num_columns < actual_buttons_used) c->num_columns++; if (!c->flags.b_SizeFixed) { while(c->num_rows*c->num_columns >= actual_buttons_used + c->num_columns) c->num_rows--; } } if (c->flags.b_SizeSmart) { /* Set rows/columns to at least the height/width of largest button */ for(i=0;inum_rowsBHeight) c->num_rows=b->BHeight; if (c->num_columnsBWidth) c->num_columns=b->BWidth; if (b->flags.b_PosFixed && c->num_columnsBWidth+b->BPosX) c->num_columns=b->BWidth+b->BPosX; if (b->flags.b_PosFixed && c->num_columnsBWidth-b->BPosX) c->num_columns=b->BWidth-b->BPosX; if (b->flags.b_PosFixed && c->num_rowsBHeight+b->BPosY) c->num_rows=b->BHeight+b->BPosY; if (b->flags.b_PosFixed && c->num_rowsBHeight-b->BPosY) c->num_rows=b->BHeight-b->BPosY; } } /* this was buggy before */ c->num_buttons = c->num_rows*c->num_columns; alloc_buttonlist(ub,c->num_buttons); /* Shuffle subcontainers */ for(i=0;iflags.b_Container) ShuffleButtons(b); } /* Place fixed buttons as given in BPosX and BPosY */ for(i=0;iflags.b_PosFixed) continue; /* recalculate position for negative offsets */ if (b->BPosX<0) b->BPosX=b->BPosX+c->num_columns-b->BWidth+1; if (b->BPosY<0) b->BPosY=b->BPosY+c->num_rows-b->BHeight+1; /* Move button if position given by user */ if (PlaceAndExpandButton(b->BPosX,b->BPosY,b,ub)) { fprintf(stderr, "%s: Overlapping fixed buttons. Quitting.\n",MyName); fprintf(stderr, "Button=%d, x=%d, y=%d\n", i,b->BPosX,b->BPosY); exit(1); } } /* place floating buttons dynamically */ next_button_x = next_button_y = 0; for(i=0;iflags.b_PosFixed) continue; if (next_button_x+b->BWidth>c->num_columns) { next_button_y++; next_button_x=0; } /* Search for next free position to accomodate button */ while (PlaceAndExpandButton(next_button_x,next_button_y,b,ub)) { next_button_x++; if (next_button_x+b->BWidth>c->num_columns) { next_button_y++; next_button_x=0; if (next_button_y>=c->num_rows) { /* could not place button */ fprintf(stderr,"%s: Button confusion! Quitting\n", MyName); exit(1); } } } } /* shrink buttons in Container */ for(i=0;ic->num_buttons && !(*ub)->c->buttons[*i]) (*i)++; /* End of contained buttons */ if((*i)>=(*ub)->c->num_buttons) { *b=*ub; *ub=(*b)->parent; /* End of the world as we know it */ if(!(*ub)) { *b=NULL; return *b; } *i=(*b)->n; if((*i)>=(*ub)->c->num_buttons) { fprintf(stderr,"%s: BUG: Couldn't return to uberbutton\n",MyName); exit(2); } NextButton(ub,b,i,all); return *b; } *b=(*ub)->c->buttons[*i]; /* Found new container */ if((*b)->flags.b_Container) { *i=-1; *ub=*b; if(!all) NextButton(ub,b,i,all); return *b; } return *b; } /* --------------------------- button navigation --------------------------- */ /** *** button_belongs_to() *** Function that finds out which button a given position belongs to. *** Returns -1 if not part of any, button if a proper button. **/ int button_belongs_to(button_info *ub,int button) { int x,y,xx,yy; button_info *b; if(!ub || button<0 || button>ub->c->num_buttons) return -1; if(ub->c->buttons[button]) return button; yy=button/ub->c->num_columns; xx=button%ub->c->num_columns; for(y=yy;y>=0;y--) { for(x=xx;x>=0;x--) { b=ub->c->buttons[x+y*ub->c->num_columns]; if(b && (x+b->BWidth > xx) && (y+b->BHeight > yy)) { return x+y*ub->c->num_columns; } } } return -1; } /** *** get_xy_button() *** Function that returns the button that covers the given row/column in the *** button matrix. Returns NULL if none. **/ button_info *get_xy_button(button_info *ub, int row, int column) { int i; if (!ub->flags.b_Container) { return NULL; } i = button_belongs_to(ub, column + row * ub->c->num_columns); if (i == -1) { return NULL; } return ub->c->buttons[i]; } /** *** select_button() *** Given (x,y) and uberbutton, returns pointer to referred button, or NULL **/ button_info *select_button(button_info *ub,int x,int y) { int i; int row; int column; button_info *b; if (!ub->flags.b_Container) return ub; x -= buttonXPad(ub) + buttonFrame(ub); y -= buttonYPad(ub) + buttonFrame(ub); if(x >= ub->c->width || x < 0 || y >= ub->c->height || y < 0) return ub; column = x * ub->c->num_columns / ub->c->width; row = y * ub->c->num_rows / ub->c->height; i = button_belongs_to(ub, column + row * ub->c->num_columns); if (i == -1) return ub; b = ub->c->buttons[i]; return select_button( b, x + ub->c->xpos - buttonXPos(b, i), y + ub->c->ypos - buttonYPos(b, i)); } void get_button_root_geometry(rectangle *r, const button_info *b) { int x; int y; int f; Window win; r->width = buttonWidth(b); r->height = buttonHeight(b); buttonInfo(b, &r->x, &r->y, &x, &y, &f); XTranslateCoordinates( Dpy, MyWindow, Root, r->x, r->y, &r->x, &r->y, &win); return; }