2444 lines
61 KiB
C
2444 lines
61 KiB
C
/* Wharf.c. by Bo Yang.
|
|
*
|
|
* Copyright 1993, Robert Nation.
|
|
*
|
|
* Modifications: Copyright 1995 by Bo Yang.
|
|
*
|
|
* modifications made by Frank Fejes for AfterStep Copyright 1996
|
|
*
|
|
* folder code Copyright 1996 by Beat Christen.
|
|
*
|
|
* swallowed button actions Copyright 1996 by Kaj Groner
|
|
*
|
|
* Various enhancements Copyright 1996 Alfredo K. Kojima
|
|
*
|
|
* button pushing styles
|
|
* configurable border drawing
|
|
* Change of icon creation code. Does not use shape extension anymore.
|
|
* each icon window now contains the whole background
|
|
* OffiX drop support added
|
|
* animation added
|
|
* withdraw on button2 click
|
|
* icon overlaying
|
|
* sound bindings
|
|
*
|
|
* based on GoodStuff.c by Robert Nation
|
|
* The GoodStuff module, and the entire GoodStuff program, and the concept for
|
|
* interfacing that module to the Window Manager, are all original work
|
|
* by 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. */
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
#define DOUBLECLICKTIME 1
|
|
|
|
#include "config.h"
|
|
|
|
#ifdef HAVE_SYS_BSDTYPES_H
|
|
#include <sys/bsdtypes.h> /* Saul */
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <signal.h>
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
#include <sys/wait.h>
|
|
#include <sys/time.h>
|
|
|
|
#if HAVE_SYS_SELECT_H
|
|
#include <sys/select.h>
|
|
#endif
|
|
|
|
#include <unistd.h>
|
|
#include <ctype.h>
|
|
#include <stdlib.h>
|
|
#include "../../fvwm/module.h"
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/Xproto.h>
|
|
#include <X11/Xatom.h>
|
|
#include <X11/Intrinsic.h>
|
|
#include <syslog.h>
|
|
|
|
#include "Wharf.h"
|
|
#define AFTER_ICONS 1
|
|
/*#include "../../afterstep/asbuttons.h" pdg */
|
|
|
|
#ifdef ENABLE_DND
|
|
#include "OffiX/DragAndDrop.h"
|
|
#include "OffiX/DragAndDropTypes.h"
|
|
#endif
|
|
|
|
#include "stepgfx.h"
|
|
|
|
/*
|
|
* You may want to raise the following values if your machine is fast
|
|
*/
|
|
#define ANIM_STEP 2 /* must be >= 1. Greater is smoother and slower */
|
|
#define ANIM_STEP_MAIN 1 /* same for the main folder */
|
|
#define ANIM_DELAY 10
|
|
|
|
#ifdef ENABLE_SOUND
|
|
#define WHEV_PUSH 0
|
|
#define WHEV_CLOSE_FOLDER 1
|
|
#define WHEV_OPEN_FOLDER 2
|
|
#define WHEV_CLOSE_MAIN 3
|
|
#define WHEV_OPEN_MAIN 4
|
|
#define WHEV_DROP 5
|
|
#define MAX_EVENTS 6
|
|
|
|
int SoundActive = 0;
|
|
char *Sounds[6]={".",".",".",".",".","."};
|
|
char *SoundPlayer=NULL;
|
|
char *SoundPath=".";
|
|
|
|
pid_t SoundThread;
|
|
int PlayerChannel[2];
|
|
|
|
/*char *ModulePath=AFTERDIR; pdg */
|
|
#endif
|
|
|
|
char *MyName;
|
|
|
|
Display *dpy;
|
|
int x_fd,fd_width;
|
|
int ROWS = FALSE;
|
|
|
|
Window Root;
|
|
int screen;
|
|
int flags;
|
|
long d_depth;
|
|
Bool NoBorder=0;
|
|
Bool Pushed = 0;
|
|
Bool Pushable = 1;
|
|
Bool ForceSize=0;
|
|
Pixel back_pix, fore_pix, light_grey;
|
|
GC NormalGC, HiReliefGC, HiInnerGC;
|
|
|
|
GC MaskGC, DefGC;
|
|
int AnimationStyle=0,AnimateMain=0;
|
|
int PushStyle=0;
|
|
int AnimationDir=1;
|
|
|
|
Window main_win;
|
|
int Width, Height,win_x,win_y;
|
|
unsigned int display_width, display_height;
|
|
|
|
#define MW_EVENTS (ExposureMask | ButtonReleaseMask |\
|
|
ButtonPressMask | LeaveWindowMask | EnterWindowMask)
|
|
XSizeHints mysizehints;
|
|
int num_buttons = 0;
|
|
int num_folderbuttons = MAX_BUTTONS;
|
|
int num_folders = 0;
|
|
int num_rows = 0;
|
|
int num_columns = 0;
|
|
int max_icon_width = 30,max_icon_height = 0;
|
|
int BUTTONWIDTH, BUTTONHEIGHT;
|
|
int x= -100000,y= -100000,w= -1,h= -1,gravity = NorthWestGravity;
|
|
int new_desk = 0;
|
|
int pageing_enabled = 1;
|
|
int ready = 0;
|
|
|
|
int CurrentButton = -1;
|
|
int fd[2];
|
|
|
|
struct button_info Buttons[BUTTON_ARRAY_LN];
|
|
struct folder_info Folders[FOLDER_ARRAY_LN];
|
|
|
|
char *iconPath = NULL;
|
|
char *pixmapPath = NULL;
|
|
|
|
static Atom wm_del_win;
|
|
Atom _XA_WM_PROTOCOLS;
|
|
Atom _XA_WM_NAME;
|
|
#ifdef ENABLE_DND
|
|
Atom DndProtocol;
|
|
Atom DndSelection;
|
|
#endif
|
|
int TextureType=TEXTURE_BUILTIN;
|
|
char *BgPixmapFile=NULL;
|
|
int FromColor[3]={0x4000,0x4000,0x4000}, ToColor[3]={0x8000,0x8000,0x8000};
|
|
Pixel BgColor=0;
|
|
int MaxColors=16;
|
|
int Withdrawn;
|
|
|
|
#define DIR_TOLEFT 1
|
|
#define DIR_TORIGHT 2
|
|
#define DIR_TOUP 3
|
|
#define DIR_TODOWN 4
|
|
|
|
#ifdef ENABLE_SOUND
|
|
void waitchild(int bullshit)
|
|
{
|
|
int stat;
|
|
|
|
wait(&stat);
|
|
SoundActive=0;
|
|
}
|
|
#endif
|
|
|
|
unsigned int lock_mods[256];
|
|
void FindLockMods(void);
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* Procedure:
|
|
* main - start of afterstep
|
|
*
|
|
***********************************************************************
|
|
*/
|
|
int main(int argc, char **argv)
|
|
{
|
|
char *display_name = NULL;
|
|
int i,j;
|
|
Window root;
|
|
int x,y,border_width,button;
|
|
int depth;
|
|
char *temp, *s;
|
|
temp = argv[0];
|
|
|
|
s=strrchr(argv[0], '/');
|
|
if (s != NULL)
|
|
temp = s + 1;
|
|
|
|
MyName = safemalloc(strlen(temp)+1);
|
|
strcpy(MyName, temp);
|
|
|
|
for(i=0;i<BUTTON_ARRAY_LN;i++)
|
|
{
|
|
#ifdef ENABLE_DND
|
|
Buttons[i].drop_action = NULL;
|
|
#endif
|
|
Buttons[i].title = NULL;
|
|
Buttons[i].action = NULL;
|
|
Buttons[i].iconno = 0;
|
|
for(j=0;j<MAX_OVERLAY;j++) {
|
|
Buttons[i].icons[j].file = NULL;
|
|
Buttons[i].icons[j].w = 0;
|
|
Buttons[i].icons[j].h = 0;
|
|
Buttons[i].icons[j].mask = None; /* pixmap for the icon mask */
|
|
Buttons[i].icons[j].icon = None;
|
|
Buttons[i].icons[j].depth = 0;
|
|
}
|
|
Buttons[i].IconWin = None;
|
|
Buttons[i].completeIcon = None;
|
|
Buttons[i].up = 1; /* Buttons start up */
|
|
Buttons[i].hangon = NULL; /* don't wait on anything yet*/
|
|
Buttons[i].folder = -1;
|
|
}
|
|
signal (SIGPIPE, DeadPipe);
|
|
if((argc != 6)&&(argc != 7))
|
|
{
|
|
fprintf(stderr,"%s Version %s should only be executed by AfterStep!\n",
|
|
MyName, VERSION);
|
|
exit(1);
|
|
}
|
|
fd[0] = atoi(argv[1]);
|
|
fd[1] = atoi(argv[2]);
|
|
|
|
if (!(dpy = XOpenDisplay(display_name)))
|
|
{
|
|
fprintf(stderr,"%s: can't open display %s", MyName,
|
|
XDisplayName(display_name));
|
|
exit (1);
|
|
}
|
|
x_fd = XConnectionNumber(dpy);
|
|
|
|
fd_width = GetFdWidth();
|
|
|
|
screen= DefaultScreen(dpy);
|
|
Root = RootWindow(dpy, screen);
|
|
if(Root == None)
|
|
{
|
|
fprintf(stderr,"%s: Screen %d is not valid ", MyName, screen);
|
|
exit(1);
|
|
}
|
|
display_width = DisplayWidth(dpy, screen);
|
|
display_height = DisplayHeight(dpy, screen);
|
|
|
|
d_depth = DefaultDepth(dpy, screen);
|
|
|
|
SetMessageMask(fd, M_NEW_DESK | M_END_WINDOWLIST | M_MAP | M_WINDOW_NAME |
|
|
M_RES_CLASS | M_CONFIG_INFO | M_END_CONFIG_INFO | M_RES_NAME);
|
|
/*
|
|
sprintf(set_mask_mesg,"SET_MASK %lu\n",
|
|
(unsigned long)(M_NEW_DESK |
|
|
M_END_WINDOWLIST|
|
|
M_MAP|
|
|
M_RES_NAME|
|
|
M_RES_CLASS|
|
|
M_WINDOW_NAME));
|
|
|
|
SendText(fd,set_mask_mesg,0);
|
|
*/
|
|
ParseOptions(argv[3]);
|
|
if(num_buttons == 0)
|
|
{
|
|
fprintf(stderr,"%s: No Buttons defined. Quitting\n", MyName);
|
|
exit(0);
|
|
}
|
|
|
|
#ifdef ENABLE_SOUND
|
|
/* startup sound subsystem */
|
|
if (SoundActive) {
|
|
if (pipe(PlayerChannel)<0) {
|
|
fprintf(stderr,"%s: could not create pipe. Disabling sound\n");
|
|
SoundActive=0;
|
|
} else {
|
|
signal(SIGCHLD,waitchild);
|
|
SoundThread=fork();
|
|
if (SoundThread<0) {
|
|
fprintf(stderr,"%s: could not fork(). Disabling sound",
|
|
MyName);
|
|
perror(".");
|
|
SoundActive=0;
|
|
} else if (SoundThread==0) { /* in the sound player process */
|
|
char *margv[9], *name;
|
|
int i;
|
|
|
|
margv[0]="ASSound";
|
|
name = findIconFile("ASSound",ModulePath,X_OK);
|
|
if(name == NULL) {
|
|
fprintf(stderr,"Wharf: couldn't find ASSound\n");
|
|
SoundActive = 0;
|
|
} else {
|
|
margv[1]=safemalloc(16);
|
|
close(PlayerChannel[1]);
|
|
sprintf(margv[1],"%x",PlayerChannel[0]);
|
|
if (SoundPlayer!=NULL)
|
|
margv[2]=SoundPlayer;
|
|
else
|
|
margv[2]="-";
|
|
for(i=0;i<MAX_EVENTS;i++) {
|
|
if (Sounds[i][0]=='.') {
|
|
margv[i+3]=Sounds[i];
|
|
} else {
|
|
margv[i+3]=safemalloc(strlen(Sounds[i])
|
|
+strlen(SoundPath)+4);
|
|
sprintf(margv[i+3],"%s/%s",SoundPath,Sounds[i]);
|
|
}
|
|
}
|
|
margv[i+3]=NULL;
|
|
execvp(name,margv);
|
|
fprintf(stderr,"Wharf: couldn't spawn ASSound\n");
|
|
exit(1);
|
|
}
|
|
} else { /* in parent */
|
|
close(PlayerChannel[0]);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
CreateShadowGC();
|
|
|
|
switch (TextureType) {
|
|
case TEXTURE_PIXMAP:
|
|
if (BgPixmapFile==NULL) {
|
|
fprintf(stderr,"%s: No Button background pixmap defined.Using default\n", MyName);
|
|
goto Builtin;
|
|
}
|
|
Buttons[BACK_BUTTON].icons[0].file=BgPixmapFile;
|
|
if (GetXPMFile(BACK_BUTTON,0))
|
|
break;
|
|
else goto Solid;
|
|
case TEXTURE_GRADIENT:
|
|
case TEXTURE_HGRADIENT:
|
|
case TEXTURE_HCGRADIENT:
|
|
case TEXTURE_VGRADIENT:
|
|
case TEXTURE_VCGRADIENT:
|
|
if (GetXPMGradient(BACK_BUTTON, FromColor, ToColor, MaxColors,TextureType))
|
|
break;
|
|
else goto Solid;
|
|
|
|
case TEXTURE_BUILTIN:
|
|
Builtin:
|
|
TextureType=TEXTURE_BUILTIN;
|
|
/* if (GetXPMData( BACK_BUTTON, button_xpm))
|
|
break;
|
|
pdg */
|
|
default:
|
|
Solid:
|
|
TextureType=TEXTURE_SOLID;
|
|
if (GetSolidXPM(BACK_BUTTON, BgColor))
|
|
break;
|
|
else {
|
|
fprintf( stderr, "back Wharf button creation\n");
|
|
exit(-1);
|
|
}
|
|
}
|
|
for(i=0;i<num_buttons;i++)
|
|
{
|
|
for(j=0;j<Buttons[i].iconno;j++) {
|
|
LoadIconFile(i,j);
|
|
}
|
|
}
|
|
for(i=num_folderbuttons;i<MAX_BUTTONS;i++) {
|
|
for(j=0;j<Buttons[i].iconno;j++) {
|
|
LoadIconFile(i,j);
|
|
}
|
|
}
|
|
#ifdef ENABLE_DND
|
|
DndProtocol=XInternAtom(dpy,"DndProtocol",False);
|
|
DndSelection=XInternAtom(dpy,"DndSelection",False);
|
|
#endif
|
|
|
|
|
|
CreateWindow();
|
|
for(i=0;i<num_buttons;i++) {
|
|
CreateIconWindow(i, &main_win);
|
|
}
|
|
for(i=num_folderbuttons;i<MAX_BUTTONS;i++)
|
|
CreateIconWindow(i, Buttons[i].parent);
|
|
XGetGeometry(dpy,main_win,&root,&x,&y,
|
|
(unsigned int *)&Width,(unsigned int *)&Height,
|
|
(unsigned int *)&border_width,(unsigned int *)&depth);
|
|
|
|
for(i=0;i<num_rows;i++)
|
|
for(j=0;j<num_columns; j++)
|
|
{
|
|
button = i*num_columns + j;
|
|
ConfigureIconWindow(button,i,j);
|
|
};
|
|
for(i=0;i<num_folders;i++)
|
|
for(j=0;j<Folders[i].count;j++)
|
|
if(num_columns < num_rows) {
|
|
ConfigureIconWindow(Folders[i].firstbutton+j,0, j);
|
|
} else {
|
|
ConfigureIconWindow(Folders[i].firstbutton+j,j, 0);
|
|
}
|
|
/* dirty hack to make swallowed app background be textured */
|
|
XSetWindowBackgroundPixmap(dpy, main_win, Buttons[BACK_BUTTON].icons[0].icon);
|
|
XMapSubwindows(dpy,main_win);
|
|
XMapWindow(dpy,main_win);
|
|
for(i=0;i<num_folders;i++)
|
|
XMapSubwindows(dpy, Folders[i].win);
|
|
|
|
FindLockMods();
|
|
|
|
/* request a window list, since this triggers a response which
|
|
* will tell us the current desktop and paging status, needed to
|
|
* indent buttons correctly */
|
|
SendText(fd,"Send_WindowList",0);
|
|
Loop();
|
|
return 0;
|
|
}
|
|
|
|
/***********************************************************************
|
|
*
|
|
* Procedure:
|
|
* Loop - wait for data to process
|
|
*
|
|
***********************************************************************/
|
|
|
|
void Loop(void)
|
|
{
|
|
Window *CurrentWin=None;
|
|
int x,y,CurrentRow,CurrentColumn,CurrentBase=0;
|
|
XEvent Event;
|
|
int NewButton,i=0,j=0,i2, bl=-1;
|
|
int LastMapped=-1;
|
|
time_t t, tl = (time_t) 0;
|
|
int CancelPush=0;
|
|
|
|
while(1)
|
|
{
|
|
if(My_XNextEvent(dpy,&Event))
|
|
{
|
|
switch(Event.type)
|
|
{
|
|
|
|
case Expose:
|
|
for(x=0;x<num_folders;x++)
|
|
if(Event.xany.window == Folders[x].win )
|
|
{
|
|
RedrawWindow(&Folders[x].win,num_folderbuttons, -1, Folders[x].cols,Folders[x].rows);
|
|
for(y=1;y<=Folders[x].count;y++)
|
|
if(num_columns<num_rows)
|
|
RedrawUnpushedOutline(&Folders[x].win, 1, y);
|
|
else
|
|
RedrawUnpushedOutline(&Folders[x].win, y, 1);
|
|
}
|
|
if (Pushed)
|
|
break;
|
|
if((Event.xexpose.count == 0)&&
|
|
(Event.xany.window == main_win))
|
|
{
|
|
if(ready < 1)
|
|
ready ++;
|
|
RedrawWindow(&main_win,0, -1, num_rows, num_columns);
|
|
}
|
|
break;
|
|
|
|
case ButtonPress:
|
|
if (Event.xbutton.button != Button1) {
|
|
if (Event.xbutton.button == Button2) {
|
|
static int LastX, LastY;
|
|
|
|
if (LastMapped != -1) {
|
|
CloseFolder(LastMapped);
|
|
Folders[LastMapped].mapped = NOTMAPPED;
|
|
LastMapped=-1;
|
|
}
|
|
if (Withdrawn) {
|
|
#ifdef ENABLE_SOUND
|
|
PlaySound(WHEV_OPEN_MAIN);
|
|
#endif
|
|
if (AnimationStyle>0 && AnimateMain)
|
|
OpenFolder(-1,LastX,LastY,Width,Height,
|
|
AnimationDir);
|
|
else
|
|
XMoveResizeWindow(dpy,main_win,LastX,LastY,
|
|
Width,Height);
|
|
Withdrawn=0;
|
|
} else {
|
|
Window junk;
|
|
int junk2,junk3,junk4,junk5;
|
|
int CornerX, CornerY;
|
|
|
|
#ifdef ENABLE_SOUND
|
|
PlaySound(WHEV_CLOSE_MAIN);
|
|
#endif
|
|
XGetGeometry(dpy,main_win,&junk,&LastX,&LastY,
|
|
&junk2,&junk3,&junk4,&junk5);
|
|
XTranslateCoordinates(dpy,main_win,Root,
|
|
LastX,LastY,
|
|
&LastX,&LastY,&junk);
|
|
if (num_rows<num_columns) { /* horizontal */
|
|
if (LastY > display_height/2) {
|
|
CornerY = display_height-BUTTONHEIGHT;
|
|
} else {
|
|
CornerY = 0;
|
|
}
|
|
if (Event.xbutton.x>num_columns*BUTTONWIDTH/2) {
|
|
CornerX = display_width - BUTTONWIDTH;
|
|
AnimationDir = DIR_TOLEFT;
|
|
} else {
|
|
CornerX = 0;
|
|
AnimationDir = DIR_TORIGHT;
|
|
}
|
|
if (AnimationStyle>0 && AnimateMain) {
|
|
CloseFolder(-1);
|
|
XMoveWindow(dpy,main_win, CornerX, CornerY);
|
|
} else {
|
|
XMoveResizeWindow(dpy,main_win,
|
|
CornerX, CornerY,
|
|
BUTTONWIDTH,BUTTONHEIGHT);
|
|
}
|
|
} else { /* vertical */
|
|
if (LastX > display_width/2) {
|
|
CornerX = display_width - BUTTONWIDTH;
|
|
} else {
|
|
CornerX = 0;
|
|
}
|
|
if (Event.xbutton.y>num_rows*BUTTONHEIGHT/2) {
|
|
CornerY = display_height-BUTTONHEIGHT;
|
|
AnimationDir = DIR_TOUP;
|
|
} else {
|
|
CornerY = 0;
|
|
AnimationDir = DIR_TODOWN;
|
|
}
|
|
if (AnimationStyle>0 && AnimateMain) {
|
|
CloseFolder(-1);
|
|
XMoveWindow(dpy,main_win, CornerX, CornerY);
|
|
} else {
|
|
XMoveResizeWindow(dpy,main_win,
|
|
CornerX, CornerY,
|
|
BUTTONWIDTH,BUTTONHEIGHT);
|
|
}
|
|
}
|
|
Withdrawn=1;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
#ifdef ENABLE_SOUND
|
|
PlaySound(WHEV_PUSH);
|
|
#endif
|
|
CancelPush = 0;
|
|
CurrentWin = &Event.xbutton.window;
|
|
CurrentBase = 0;
|
|
CurrentRow = (Event.xbutton.y/BUTTONHEIGHT);
|
|
CurrentColumn = (Event.xbutton.x/BUTTONWIDTH);
|
|
if (*CurrentWin!=main_win) {
|
|
CurrentButton = CurrentBase + CurrentColumn*num_rows
|
|
+ CurrentRow*num_columns;
|
|
} else {
|
|
CurrentButton = CurrentBase + CurrentColumn
|
|
+ CurrentRow*num_columns;
|
|
if (CurrentButton>=num_buttons) {
|
|
CurrentButton = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for(x=0;x<num_buttons;x++)
|
|
{
|
|
if (*CurrentWin == Buttons[x].IconWin)
|
|
{
|
|
CurrentButton = x;
|
|
CurrentRow = x / num_columns;
|
|
CurrentColumn = x % num_columns;
|
|
}
|
|
}
|
|
|
|
for(x=0;x<num_folders;x++)
|
|
if(*CurrentWin == Folders[x].win)
|
|
{
|
|
CurrentBase = Folders[x].firstbutton;
|
|
if (num_rows<num_columns)
|
|
CurrentButton = CurrentBase + CurrentRow;
|
|
else
|
|
CurrentButton = CurrentBase + CurrentColumn;
|
|
}
|
|
i = CurrentRow+1;
|
|
j = CurrentColumn +1;
|
|
|
|
if (Buttons[CurrentButton].swallow == 1 ||
|
|
Buttons[CurrentButton].swallow == 2 ||
|
|
Buttons[CurrentButton].action == NULL)
|
|
break;
|
|
|
|
if (Pushable)
|
|
{
|
|
if (Buttons[CurrentButton].swallow != 3 &&
|
|
Buttons[CurrentButton].swallow != 4)
|
|
{
|
|
Pushed = 1;
|
|
RedrawPushed(CurrentWin, i, j);
|
|
}
|
|
}
|
|
if (strncasecmp(Buttons[CurrentButton].action,"Folder",6)==0) {
|
|
Window junk;
|
|
int junk2,junk3,junk4,junk5;
|
|
XGetGeometry(dpy,main_win,&junk,&x,&y,
|
|
&junk2,&junk3,&junk4,&junk5);
|
|
XTranslateCoordinates(dpy,main_win,Root,
|
|
x,y,
|
|
&x,&y,&junk);
|
|
/* kludge until Beat takes a look */
|
|
if ((num_columns == 1) && (num_rows == 1))
|
|
MapFolder(Buttons[CurrentButton].folder,
|
|
&LastMapped,
|
|
x, y,
|
|
1,1);
|
|
else
|
|
MapFolder(Buttons[CurrentButton].folder,
|
|
&LastMapped,
|
|
x, y,
|
|
CurrentRow, CurrentColumn);
|
|
}
|
|
break;
|
|
case EnterNotify:
|
|
CancelPush = 0;
|
|
break;
|
|
case LeaveNotify:
|
|
CancelPush = 1;
|
|
break;
|
|
#ifdef ENABLE_DND
|
|
case ClientMessage:
|
|
if (Event.xclient.message_type==DndProtocol) {
|
|
unsigned long dummy_r,size;
|
|
Atom dummy_a;
|
|
int dummy_f;
|
|
unsigned char *data, *Command;
|
|
|
|
Window dummy_rt, dummy_c;
|
|
int dummy_x, dummy_y, base, pos_x, pos_y;
|
|
unsigned int dummy;
|
|
|
|
/* if (Event.xclient.data.l[0]!=DndFile ||
|
|
Event.xclient.data.l[0]!=DndFiles ||
|
|
Event.xclient.data.l[0]!=DndExe
|
|
)
|
|
break; */
|
|
|
|
XQueryPointer(dpy,main_win,
|
|
&dummy_rt,&dummy_c,
|
|
&dummy_x,&dummy_y,
|
|
&pos_x,&pos_y,
|
|
&dummy);
|
|
base = 0;
|
|
dummy_y = (pos_y/BUTTONHEIGHT);
|
|
dummy_x= (pos_x/BUTTONWIDTH);
|
|
dummy = base + dummy_x + dummy_y*num_columns;
|
|
|
|
/*
|
|
for(x=0;x<num_folders;x++) {
|
|
if(Event.xbutton.window == Folders[x].win) {
|
|
base = Folders[x].firstbutton;
|
|
dummy = base + dummy_y + dummy_x -1;
|
|
}
|
|
} */
|
|
if (Buttons[dummy].drop_action == NULL)
|
|
break;
|
|
dummy_x++;
|
|
dummy_y++;
|
|
CurrentWin=Buttons[dummy].parent;
|
|
if (Pushable) {
|
|
RedrawPushedOutline(CurrentWin, dummy_y, dummy_x);
|
|
XSync(dpy, 0);
|
|
}
|
|
XGetWindowProperty(dpy, Root, DndSelection, 0L,
|
|
100000L, False, AnyPropertyType,
|
|
&dummy_a, &dummy_f,
|
|
&size,&dummy_r,
|
|
&data);
|
|
if (Event.xclient.data.l[0]==DndFiles) {
|
|
for (dummy_r = 0; dummy_r<size-1; dummy_r++) {
|
|
if (data[dummy_r]==0)
|
|
data[dummy_r]=' ';
|
|
}
|
|
}
|
|
#ifdef ENABLE_SOUND
|
|
PlaySound(WHEV_DROP);
|
|
#endif
|
|
Command=(unsigned char *)safemalloc(strlen((char *)data)
|
|
+ strlen((char *)(Buttons[dummy].drop_action)));
|
|
sprintf((char *)Command,Buttons[dummy].drop_action,
|
|
data,Event.xclient.data.l[0]);
|
|
SendInfo(fd,(char *)Command,0);
|
|
free(Command);
|
|
if (Pushable) {
|
|
usleep(50000);
|
|
XClearWindow(dpy,Buttons[dummy].IconWin);
|
|
RedrawUnpushedOutline(CurrentWin, dummy_y, dummy_x);
|
|
}
|
|
}
|
|
break;
|
|
#endif /* ENABLE_DND */
|
|
case ButtonRelease:
|
|
if ((Event.xbutton.button != Button1) ||
|
|
(Buttons[CurrentButton].swallow == 1) ||
|
|
(Buttons[CurrentButton].swallow == 2) ||
|
|
(Buttons[CurrentButton].action == NULL)) {
|
|
break;
|
|
}
|
|
|
|
CurrentRow = (Event.xbutton.y/BUTTONHEIGHT);
|
|
CurrentColumn = (Event.xbutton.x/BUTTONWIDTH);
|
|
|
|
if (Pushable)
|
|
{
|
|
if (Buttons[CurrentButton].swallow != 3 &&
|
|
Buttons[CurrentButton].swallow != 4)
|
|
{
|
|
Pushed=0;
|
|
RedrawUnpushed(CurrentWin, i, j);
|
|
}
|
|
}
|
|
if (CancelPush)
|
|
break;
|
|
if (*CurrentWin!=main_win) {
|
|
NewButton = CurrentBase + CurrentColumn*num_rows
|
|
+ CurrentRow*num_columns;
|
|
} else {
|
|
NewButton = CurrentBase + CurrentColumn
|
|
+ CurrentRow*num_columns;
|
|
}
|
|
|
|
for(x=0;x<num_folders;x++)
|
|
if(*CurrentWin == Folders[x].win)
|
|
{
|
|
if (num_rows<num_columns)
|
|
NewButton = Folders[x].firstbutton + CurrentRow;
|
|
else
|
|
NewButton = Folders[x].firstbutton + CurrentColumn;
|
|
}
|
|
for (x=0;x<num_buttons;x++)
|
|
{
|
|
if (*CurrentWin == Buttons[x].IconWin)
|
|
{
|
|
NewButton = x;
|
|
CurrentRow = x / num_columns;
|
|
CurrentColumn = x % num_columns;
|
|
}
|
|
}
|
|
|
|
if(NewButton == CurrentButton)
|
|
{
|
|
t = time( 0);
|
|
bl = -1;
|
|
tl = -1;
|
|
if(strncasecmp(Buttons[CurrentButton].action,"Folder",6)!=0)
|
|
{
|
|
if (LastMapped != -1 && CurrentWin != &main_win)
|
|
{
|
|
CloseFolder(LastMapped);
|
|
Folders[LastMapped].mapped = NOTMAPPED;
|
|
LastMapped = -1;
|
|
}
|
|
SendInfo(fd,Buttons[CurrentButton].action,0);
|
|
}
|
|
if((Buttons[CurrentButton].action)&&
|
|
(strncasecmp(Buttons[CurrentButton].action,"exec",4)== 0))
|
|
{
|
|
i=4;
|
|
while((Buttons[CurrentButton].action[i] != 0)&&
|
|
(Buttons[CurrentButton].action[i] != '"'))
|
|
i++;
|
|
i2=i+1;
|
|
|
|
while((Buttons[CurrentButton].action[i2] != 0)&&
|
|
(Buttons[CurrentButton].action[i2] != '"'))
|
|
i2++;
|
|
if(i2 - i >1)
|
|
{
|
|
Buttons[CurrentButton].hangon = safemalloc(i2-i);
|
|
strncpy(Buttons[CurrentButton].hangon,
|
|
&Buttons[CurrentButton].action[i+1],i2-i-1);
|
|
Buttons[CurrentButton].hangon[i2-i-1] = 0;
|
|
Buttons[CurrentButton].up = 0;
|
|
if (Buttons[CurrentButton].swallow == 3 ||
|
|
Buttons[CurrentButton].swallow == 4)
|
|
Buttons[CurrentButton].swallow = 4;
|
|
else
|
|
Buttons[CurrentButton].swallow = 0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
|
|
/*
|
|
case ClientMessage:
|
|
if ((Event.xclient.format==32) &&
|
|
(Event.xclient.data.l[0]==wm_del_win))
|
|
{
|
|
DeadPipe(1);
|
|
}
|
|
break;
|
|
case PropertyNotify:
|
|
if (Pushed)
|
|
break;
|
|
for(i=0;i<num_rows;i++)
|
|
for(j=0;j<num_columns; j++)
|
|
{
|
|
button = i*num_columns + j;
|
|
if(((Buttons[button].swallow == 3)||
|
|
(Buttons[button.swallow == 4))&&
|
|
(Event.xany.window == Buttons[button].IconWin)&&
|
|
(Event.xproperty.atom == XA_WM_NAME))
|
|
{
|
|
XFetchName(dpy, Buttons[button].IconWin, &temp);
|
|
if(strcmp(Buttons[button].title,"-")!=0)
|
|
CopyString(&Buttons[button].title, temp);
|
|
XFree(temp);
|
|
XClearArea(dpy,main_win,j*BUTTONWIDTH,
|
|
i*BUTTONHEIGHT, BUTTONWIDTH,BUTTONHEIGHT,0);
|
|
RedrawWindow(&main_win,0, button, num_rows, num_columns);
|
|
}
|
|
}
|
|
break;
|
|
*/
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void OpenFolder(int folder,int x, int y, int w, int h, int direction)
|
|
{
|
|
int winc, hinc;
|
|
int cx, cy, cw, ch;
|
|
Window win;
|
|
int isize;
|
|
|
|
if (folder<0) {
|
|
winc = BUTTONWIDTH/ANIM_STEP_MAIN;
|
|
hinc = BUTTONHEIGHT/ANIM_STEP_MAIN;
|
|
} else {
|
|
winc = BUTTONWIDTH/ANIM_STEP;
|
|
hinc = BUTTONHEIGHT/ANIM_STEP;
|
|
}
|
|
|
|
if (folder>=0) {
|
|
win = Folders[folder].win;
|
|
Folders[folder].direction = direction;
|
|
if (direction == DIR_TOLEFT || direction == DIR_TORIGHT)
|
|
isize = winc;
|
|
else
|
|
isize = hinc;
|
|
} else {
|
|
win = main_win;
|
|
if (direction == DIR_TOLEFT || direction == DIR_TORIGHT)
|
|
isize = BUTTONWIDTH;
|
|
else
|
|
isize = BUTTONHEIGHT;
|
|
}
|
|
cx = x; cy = y;
|
|
ch = h; cw = w;
|
|
if (AnimationStyle==0) {
|
|
XMapWindow(dpy, win);
|
|
} else
|
|
switch (direction) {
|
|
case DIR_TOLEFT:
|
|
cx = x+w;
|
|
XMoveResizeWindow(dpy,win,cx,y, 1, h);
|
|
XMapWindow(dpy, win);
|
|
for(cw=isize;cw<=w;cw+=winc) {
|
|
cx -= winc;
|
|
usleep(ANIM_DELAY/2);
|
|
XMoveResizeWindow(dpy,win,cx,y, cw,h);
|
|
XSync(dpy,0);
|
|
}
|
|
break;
|
|
case DIR_TORIGHT:
|
|
XMoveResizeWindow(dpy,win,x,y, 1, h);
|
|
XMapWindow(dpy, win);
|
|
for(cw=isize;cw<=w;cw+=winc) {
|
|
usleep(ANIM_DELAY/2);
|
|
XMoveResizeWindow(dpy,win,x,y, cw,h);
|
|
XSync(dpy,0);
|
|
}
|
|
break;
|
|
case DIR_TOUP:
|
|
cy = y+h;
|
|
XMoveResizeWindow(dpy,win,x,cy, w, 1);
|
|
XMapWindow(dpy, win);
|
|
for(ch=isize;ch<=h;ch+=hinc) {
|
|
cy -= hinc;
|
|
usleep(ANIM_DELAY/2);
|
|
XMoveResizeWindow(dpy,win,x,cy, w, ch);
|
|
XSync(dpy,0);
|
|
}
|
|
break;
|
|
case DIR_TODOWN:
|
|
XMoveResizeWindow(dpy,win,x,y, w, 1);
|
|
XMapWindow(dpy, win);
|
|
for(ch=isize;ch<=h;ch+=hinc) {
|
|
usleep(ANIM_DELAY/2);
|
|
XMoveResizeWindow(dpy,win,x,y, w, ch);
|
|
XSync(dpy,0);
|
|
}
|
|
break;
|
|
default:
|
|
XBell(dpy,100);
|
|
fprintf(stderr,"WHARF INTERNAL BUG in OpenFolder()\n");
|
|
exit(-1);
|
|
}
|
|
|
|
if (cw!=w || ch!=h || x != cx || cy != y || AnimationStyle==0)
|
|
XMoveResizeWindow(dpy,win,x,y,w,h);
|
|
}
|
|
|
|
|
|
|
|
void CloseFolder(int folder)
|
|
{
|
|
int winc, hinc;
|
|
int cx, cy, cw, ch;
|
|
int x,y,w,h, junk_depth, junk_bd;
|
|
int fsize, direction;
|
|
Window win, junk_win;
|
|
|
|
#ifdef ENABLE_SOUND
|
|
PlaySound(WHEV_CLOSE_FOLDER);
|
|
#endif
|
|
if (folder<0) {
|
|
winc = BUTTONWIDTH/ANIM_STEP_MAIN;
|
|
hinc = BUTTONHEIGHT/ANIM_STEP_MAIN;
|
|
} else {
|
|
winc = BUTTONWIDTH/ANIM_STEP;
|
|
hinc = BUTTONHEIGHT/ANIM_STEP;
|
|
}
|
|
if (folder < 0) {
|
|
win = main_win;
|
|
direction = AnimationDir;
|
|
if (direction==DIR_TOUP || direction==DIR_TODOWN)
|
|
fsize=BUTTONHEIGHT;
|
|
else
|
|
fsize=BUTTONWIDTH;
|
|
} else {
|
|
direction = Folders[folder].direction;
|
|
win = Folders[folder].win;
|
|
if (direction==DIR_TOUP || direction==DIR_TODOWN)
|
|
fsize=hinc;
|
|
else
|
|
fsize=winc;
|
|
}
|
|
if (AnimationStyle==0) {
|
|
goto end;
|
|
}
|
|
XGetGeometry(dpy,win,&junk_win,&x,&y,&w,&h,&junk_bd,&junk_depth);
|
|
XTranslateCoordinates(dpy,win,Root,x,y,&x,&y,&junk_win);
|
|
switch (direction) {
|
|
case DIR_TOLEFT:
|
|
cx = x;
|
|
for(cw=w;cw >= fsize; cw-=winc) {
|
|
XMoveResizeWindow(dpy,win,cx,y, cw,h);
|
|
XSync(dpy,0);
|
|
usleep(ANIM_DELAY);
|
|
cx += winc;
|
|
}
|
|
break;
|
|
case DIR_TORIGHT:
|
|
for(cw=w;cw >= fsize; cw-=winc) {
|
|
XMoveResizeWindow(dpy,win,x,y, cw,h);
|
|
XSync(dpy,0);
|
|
usleep(ANIM_DELAY);
|
|
}
|
|
break;
|
|
case DIR_TOUP:
|
|
cy = y;
|
|
for(ch=h;ch >= fsize; ch-=hinc) {
|
|
XMoveResizeWindow(dpy,win,x,cy, w,ch);
|
|
XSync(dpy,0);
|
|
usleep(ANIM_DELAY);
|
|
cy += hinc;
|
|
}
|
|
break;
|
|
case DIR_TODOWN:
|
|
for(ch=h;ch >= fsize; ch-=hinc) {
|
|
XMoveResizeWindow(dpy,win,x,y, w, ch);
|
|
XSync(dpy,0);
|
|
usleep(ANIM_DELAY);
|
|
}
|
|
break;
|
|
default:
|
|
XBell(dpy,100);
|
|
fprintf(stderr,"WHARF INTERNAL BUG in CloseFolder()\n");
|
|
exit(-1);
|
|
}
|
|
Folders[folder].direction = 0;
|
|
end:
|
|
if (folder<0) {
|
|
XResizeWindow(dpy,win,BUTTONWIDTH,BUTTONHEIGHT);
|
|
} else {
|
|
XUnmapWindow(dpy,win);
|
|
}
|
|
}
|
|
|
|
|
|
void MapFolder(int folder, int *LastMapped, int base_x, int base_y, int row, int col)
|
|
{
|
|
int dir;
|
|
|
|
if (Folders[folder].mapped ==ISMAPPED)
|
|
{
|
|
CloseFolder(folder);
|
|
Folders[folder].mapped = NOTMAPPED;
|
|
*LastMapped = -1;
|
|
}
|
|
else
|
|
{
|
|
int folderx, foldery, folderw, folderh;
|
|
if (*LastMapped != -1)
|
|
{
|
|
CloseFolder(*LastMapped);
|
|
Folders[*LastMapped].mapped = NOTMAPPED;
|
|
*LastMapped = -1;
|
|
}
|
|
Folders[folder].mapped = ISMAPPED;
|
|
if(num_columns < num_rows)
|
|
{
|
|
if((base_x % display_width) > display_width / 2 ) {
|
|
folderx = base_x+(col-Folders[folder].count)*BUTTONWIDTH-2;
|
|
dir = DIR_TOLEFT;
|
|
}
|
|
else {
|
|
folderx = base_x+(col+1)*BUTTONHEIGHT+1;
|
|
dir = DIR_TORIGHT;
|
|
}
|
|
foldery = base_y+row*BUTTONHEIGHT;
|
|
folderw = Folders[folder].count*BUTTONWIDTH;
|
|
folderh = BUTTONHEIGHT;
|
|
}
|
|
/* more kludgery */
|
|
else if (num_columns == num_rows)
|
|
{
|
|
/*
|
|
if((base_x % display_width) > display_width / 2 )
|
|
folderx = (col-Folders[folder].count)*BUTTONHEIGHT-2;
|
|
else
|
|
folderx = (col+1)*BUTTONHEIGHT+1;
|
|
*/
|
|
if (ROWS)
|
|
{
|
|
if ((base_y % display_height) > display_height / 2) {
|
|
foldery = base_y-(Folders[folder].count)*BUTTONHEIGHT-2;
|
|
dir = DIR_TOUP;
|
|
}
|
|
else {
|
|
foldery = base_y+BUTTONHEIGHT+2;
|
|
dir = DIR_TODOWN;
|
|
}
|
|
folderx = base_x;
|
|
folderw = BUTTONWIDTH;
|
|
folderh = (Folders[folder].count)*BUTTONHEIGHT;
|
|
}
|
|
else
|
|
{
|
|
if((base_x % display_width) > display_width / 2 ) {
|
|
folderx = base_x-(Folders[folder].count)*BUTTONWIDTH-2;
|
|
dir = DIR_TOLEFT;
|
|
}
|
|
else {
|
|
folderx = base_x+BUTTONWIDTH+1;
|
|
dir = DIR_TORIGHT;
|
|
}
|
|
foldery = base_y-1;
|
|
folderh = BUTTONHEIGHT;
|
|
folderw = (Folders[folder].count)*BUTTONWIDTH;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ((base_y % display_height) < display_height / 2) {
|
|
foldery =base_y+(row+1)*BUTTONHEIGHT;
|
|
dir = DIR_TODOWN;
|
|
}
|
|
else {
|
|
foldery = base_y+(row-Folders[folder].count)*BUTTONHEIGHT;
|
|
dir = DIR_TOUP;
|
|
}
|
|
folderx = base_x+col*BUTTONWIDTH;
|
|
folderw = BUTTONWIDTH;
|
|
folderh = (Folders[folder].count)*BUTTONHEIGHT;
|
|
}
|
|
|
|
#ifdef ENABLE_SOUND
|
|
PlaySound(WHEV_OPEN_FOLDER);
|
|
#endif
|
|
XMoveWindow(dpy, Folders[folder].win, folderx, foldery);
|
|
OpenFolder(folder,folderx, foldery, folderw, folderh, dir);
|
|
*LastMapped = folder;
|
|
}
|
|
}
|
|
|
|
void
|
|
DrawOutline(Drawable d, int w, int h)
|
|
{
|
|
if (NoBorder)
|
|
return;
|
|
/* top */
|
|
XDrawLine( dpy, d, HiInnerGC, 0, 0, w-1, 0);
|
|
/*
|
|
XDrawLine( dpy, d, HiInnerGC, 0, 1, w-1, 1);
|
|
*/
|
|
/* bottom */
|
|
XFillRectangle(dpy, d, NormalGC, 0,h-2,w-1,h-1);
|
|
|
|
/* left */
|
|
XDrawLine( dpy, d, HiInnerGC, 0, 1, 0, h-1);
|
|
/*
|
|
XDrawLine( dpy, d, HiInnerGC, 1, 2, 1, h-2);
|
|
*/
|
|
/* right */
|
|
XDrawLine( dpy, d, NormalGC, w-1, 1, w-1, h-1);
|
|
XDrawLine( dpy, d, NormalGC, w-2, 2, w-2, h-2);
|
|
}
|
|
|
|
void RedrawUnpushed(Window *win, int i, int j)
|
|
{
|
|
if (PushStyle!=0) {
|
|
XMoveResizeWindow(dpy, Buttons[CurrentButton].IconWin,
|
|
(j-1)*BUTTONWIDTH ,(i-1)*BUTTONHEIGHT,
|
|
BUTTONWIDTH, BUTTONHEIGHT);
|
|
} else {
|
|
XCopyArea( dpy, Buttons[CurrentButton].completeIcon,
|
|
Buttons[CurrentButton].IconWin, NormalGC, 0, 0,
|
|
Buttons[BACK_BUTTON].icons[0].w,
|
|
Buttons[BACK_BUTTON].icons[0].h,
|
|
0,0);
|
|
}
|
|
RedrawWindow(win,0, CurrentButton, num_rows, num_columns);
|
|
|
|
RedrawUnpushedOutline(win, i, j);
|
|
}
|
|
|
|
void RedrawUnpushedOutline(Window *win, int i, int j)
|
|
{
|
|
/* top */
|
|
if (NoBorder) {
|
|
return;
|
|
}
|
|
|
|
XDrawLine( dpy, *win, HiInnerGC,
|
|
j*BUTTONWIDTH-BUTTONWIDTH, i*BUTTONHEIGHT-BUTTONHEIGHT,
|
|
j*BUTTONWIDTH,i*BUTTONHEIGHT-BUTTONHEIGHT);
|
|
/*
|
|
XDrawLine( dpy, *win, HiInnerGC, j*BUTTONWIDTH-BUTTONWIDTH,
|
|
i*BUTTONHEIGHT-BUTTONHEIGHT+1, j*BUTTONWIDTH,
|
|
i*BUTTONHEIGHT-BUTTONHEIGHT+1);
|
|
*/
|
|
/* left */
|
|
XDrawLine( dpy, *win, HiInnerGC, j*BUTTONWIDTH-BUTTONWIDTH,
|
|
i*BUTTONHEIGHT-BUTTONHEIGHT+1, j*BUTTONWIDTH-BUTTONWIDTH,
|
|
i*BUTTONHEIGHT-1);
|
|
/*
|
|
XDrawLine( dpy, *win, HiInnerGC, j*BUTTONWIDTH
|
|
-BUTTONWIDTH+1, i*BUTTONHEIGHT-BUTTONHEIGHT+2,
|
|
j*BUTTONWIDTH-BUTTONWIDTH+1 ,i*BUTTONHEIGHT-1);
|
|
*/
|
|
/* right */
|
|
XDrawLine( dpy, *win, NormalGC, j*BUTTONWIDTH-BUTTONWIDTH
|
|
+BUTTONWIDTH-2, i*BUTTONHEIGHT-BUTTONHEIGHT+2, j*BUTTONWIDTH
|
|
-BUTTONWIDTH+BUTTONWIDTH-2 ,i*BUTTONHEIGHT-1);
|
|
|
|
XDrawLine( dpy, *win, NormalGC, j*BUTTONWIDTH-BUTTONWIDTH
|
|
+BUTTONWIDTH-1, i*BUTTONHEIGHT-BUTTONHEIGHT+1,
|
|
j*BUTTONWIDTH-BUTTONWIDTH+BUTTONWIDTH-1 ,i*BUTTONHEIGHT-1);
|
|
|
|
/* bottom */
|
|
XDrawLine( dpy, *win, NormalGC, j*BUTTONWIDTH
|
|
-BUTTONWIDTH+1, i*BUTTONHEIGHT-1, j*BUTTONWIDTH-BUTTONWIDTH
|
|
+BUTTONWIDTH-2,i*BUTTONHEIGHT-1);
|
|
|
|
XDrawLine( dpy, *win, NormalGC, j*BUTTONWIDTH-BUTTONWIDTH
|
|
+1, i*BUTTONHEIGHT-2, j*BUTTONWIDTH-BUTTONWIDTH+BUTTONWIDTH-2,
|
|
i*BUTTONHEIGHT-2);
|
|
}
|
|
|
|
void RedrawPushed(Window *win, int i,int j)
|
|
{
|
|
if (PushStyle!=0) {
|
|
XMoveResizeWindow(dpy, Buttons[CurrentButton].IconWin,
|
|
2+(j-1)*BUTTONWIDTH,(i-1)*BUTTONHEIGHT+2,
|
|
BUTTONWIDTH-2, BUTTONHEIGHT-2);
|
|
} else {
|
|
XCopyArea( dpy, Buttons[CurrentButton].completeIcon,
|
|
Buttons[CurrentButton].IconWin, NormalGC, 2, 2,
|
|
Buttons[BACK_BUTTON].icons[0].w-2,
|
|
Buttons[BACK_BUTTON].icons[0].h-2, 4, 4);
|
|
XCopyArea( dpy, Buttons[BACK_BUTTON].icons[0].icon,
|
|
Buttons[CurrentButton].IconWin, NormalGC, 2, 2,
|
|
2, BUTTONHEIGHT, 2, 2);
|
|
XCopyArea( dpy, Buttons[BACK_BUTTON].icons[0].icon,
|
|
Buttons[CurrentButton].IconWin, NormalGC, 2, 2,
|
|
BUTTONWIDTH, 2, 2, 2);
|
|
}
|
|
RedrawWindow(win,0, CurrentButton, num_rows, num_columns);
|
|
RedrawPushedOutline(win, i,j);
|
|
}
|
|
|
|
void RedrawPushedOutline(Window *win, int i, int j)
|
|
{
|
|
GC gc1;
|
|
/* Top Hilite */
|
|
XDrawLine( dpy, *win, NormalGC, j*BUTTONWIDTH-BUTTONWIDTH,
|
|
i*BUTTONHEIGHT-BUTTONHEIGHT, j*BUTTONWIDTH,i*BUTTONHEIGHT
|
|
-BUTTONHEIGHT);
|
|
/*
|
|
XDrawLine( dpy, *win, NormalGC, j*BUTTONWIDTH-BUTTONWIDTH,
|
|
i*BUTTONHEIGHT-BUTTONHEIGHT+1, j*BUTTONWIDTH,i*BUTTONHEIGHT
|
|
-BUTTONHEIGHT+1);
|
|
*/
|
|
/* Left Hilite */
|
|
|
|
XDrawLine( dpy, *win, NormalGC, j*BUTTONWIDTH-BUTTONWIDTH,
|
|
i*BUTTONHEIGHT-BUTTONHEIGHT+1, j*BUTTONWIDTH-BUTTONWIDTH,
|
|
i*BUTTONHEIGHT-1);
|
|
/*
|
|
XDrawLine( dpy, *win, NormalGC, j*BUTTONWIDTH-BUTTONWIDTH
|
|
+1, i*BUTTONHEIGHT-BUTTONHEIGHT+2, j*BUTTONWIDTH-BUTTONWIDTH+1,
|
|
i*BUTTONHEIGHT-1);
|
|
*/
|
|
if (PushStyle!=0) {
|
|
gc1 = HiReliefGC;
|
|
} else {
|
|
gc1 = HiInnerGC;
|
|
}
|
|
|
|
/* Right Hilite */
|
|
|
|
XDrawLine( dpy, *win, HiReliefGC, j*BUTTONWIDTH
|
|
-BUTTONWIDTH+BUTTONWIDTH-2, i*BUTTONHEIGHT-BUTTONHEIGHT+2,
|
|
j*BUTTONWIDTH-BUTTONWIDTH+BUTTONWIDTH-2 ,i*BUTTONHEIGHT-1);
|
|
|
|
XDrawLine( dpy, *win, gc1, j*BUTTONWIDTH
|
|
-BUTTONWIDTH+BUTTONWIDTH-1, i*BUTTONHEIGHT-BUTTONHEIGHT+1,
|
|
j*BUTTONWIDTH-BUTTONWIDTH+BUTTONWIDTH-1 ,i*BUTTONHEIGHT-1);
|
|
|
|
/* Bottom Hilite */
|
|
XDrawLine( dpy, *win, gc1, j*BUTTONWIDTH
|
|
-BUTTONWIDTH+1, i*BUTTONHEIGHT-1, j*BUTTONWIDTH-BUTTONWIDTH
|
|
+BUTTONWIDTH-2,i*BUTTONHEIGHT-1);
|
|
|
|
XDrawLine( dpy, *win, HiReliefGC, j*BUTTONWIDTH
|
|
-BUTTONWIDTH+1, i*BUTTONHEIGHT-2, j*BUTTONWIDTH-BUTTONWIDTH
|
|
+BUTTONWIDTH-2,i*BUTTONHEIGHT-2);
|
|
}
|
|
/************************************************************************
|
|
*
|
|
* Draw the window
|
|
*
|
|
***********************************************************************/
|
|
void RedrawWindow(Window *win, int firstbutton, int newbutton,
|
|
int num_rows, int num_columns)
|
|
{
|
|
int i,j,button;
|
|
XEvent dummy;
|
|
|
|
if(ready < 1)
|
|
return;
|
|
|
|
while (XCheckTypedWindowEvent (dpy, *win, Expose, &dummy));
|
|
|
|
for(i=0;i<num_rows;i++)
|
|
for(j=0;j<num_columns; j++)
|
|
{
|
|
button = firstbutton+i*num_columns + j;
|
|
if((newbutton == -1)||(newbutton == button))
|
|
{
|
|
if(((Buttons[button].swallow == 3)||
|
|
(Buttons[button].swallow == 4))&&
|
|
(Buttons[button].IconWin != None))
|
|
XSetWindowBorderWidth(dpy,Buttons[button].IconWin,0);
|
|
}
|
|
RedrawUnpushedOutline(win,i,j);
|
|
}
|
|
}
|
|
|
|
|
|
/*******************************************************************
|
|
*
|
|
* Create GC's
|
|
*
|
|
******************************************************************/
|
|
void CreateShadowGC(void)
|
|
{
|
|
XGCValues gcv;
|
|
unsigned long gcm;
|
|
|
|
if(d_depth < 2)
|
|
{
|
|
back_pix = GetColor("white");
|
|
fore_pix = GetColor("black");
|
|
}
|
|
else
|
|
{
|
|
if (TextureType>0 && TextureType < 128) {
|
|
MakeShadowColors(dpy, FromColor, ToColor, &fore_pix, &light_grey);
|
|
} else {
|
|
back_pix = GetColor("grey40");
|
|
fore_pix = GetColor("grey17");
|
|
light_grey = GetColor("white");
|
|
}
|
|
}
|
|
gcm = GCForeground|GCBackground|GCSubwindowMode;
|
|
gcv.subwindow_mode = IncludeInferiors;
|
|
|
|
gcv.foreground = fore_pix;
|
|
gcv.background = back_pix;
|
|
NormalGC = XCreateGC(dpy, Root, gcm, &gcv);
|
|
|
|
gcv.foreground = back_pix;
|
|
gcv.background = fore_pix;
|
|
HiReliefGC = XCreateGC(dpy, Root, gcm, &gcv);
|
|
|
|
gcv.foreground = light_grey;
|
|
gcv.background = fore_pix;
|
|
HiInnerGC = XCreateGC(dpy, Root, gcm, &gcv);
|
|
|
|
gcm = GCForeground;
|
|
gcv.foreground = fore_pix;
|
|
MaskGC = XCreateGC(dpy, Root, gcm, &gcv);
|
|
|
|
DefGC = DefaultGC(dpy, screen);
|
|
}
|
|
|
|
/************************************************************************
|
|
*
|
|
* Sizes and creates the window
|
|
*
|
|
***********************************************************************/
|
|
void CreateWindow(void)
|
|
{
|
|
int first_avail_button,i;
|
|
|
|
wm_del_win = XInternAtom(dpy,"WM_DELETE_WINDOW",False);
|
|
_XA_WM_PROTOCOLS = XInternAtom (dpy, "WM_PROTOCOLS", False);
|
|
|
|
/* Allow for multi-width/height buttons */
|
|
first_avail_button = num_buttons;
|
|
|
|
if(num_buttons > MAX_BUTTONS)
|
|
{
|
|
fprintf(stderr,"%s: Out of Buttons!\n",MyName);
|
|
exit(0);
|
|
}
|
|
|
|
/* size and create the window */
|
|
if((num_rows == 0)&&(num_columns == 0))
|
|
num_columns = 1;
|
|
if(num_columns == 0)
|
|
{
|
|
num_columns = num_buttons/num_rows;
|
|
while(num_rows * num_columns < num_buttons)
|
|
num_columns++;
|
|
}
|
|
if(num_rows == 0)
|
|
{
|
|
num_rows = num_buttons/num_columns;
|
|
while(num_rows * num_columns < num_buttons)
|
|
num_rows++;
|
|
}
|
|
|
|
while(num_rows * num_columns < num_buttons)
|
|
num_columns++;
|
|
|
|
mysizehints.flags = PWinGravity| PResizeInc | PBaseSize;
|
|
/* subtract one for the right/bottom border */
|
|
mysizehints.width = BUTTONWIDTH*num_columns;
|
|
mysizehints.height= BUTTONHEIGHT*num_rows;
|
|
mysizehints.width_inc = num_columns;
|
|
mysizehints.height_inc = num_rows;
|
|
mysizehints.base_height = num_rows - 1;
|
|
mysizehints.base_width = num_columns - 1;
|
|
|
|
if(x > -100000)
|
|
{
|
|
if (x <= -1)
|
|
{
|
|
mysizehints.x = DisplayWidth(dpy,screen) + x - mysizehints.width-1;
|
|
gravity = NorthEastGravity;
|
|
}
|
|
else if ((x == 0) && (flags & 16))
|
|
mysizehints.x = DisplayWidth(dpy,screen) - mysizehints.width-2;
|
|
else
|
|
mysizehints.x = x;
|
|
if ( y<0)
|
|
{
|
|
mysizehints.y = DisplayHeight(dpy,screen) + y - mysizehints.height-2;
|
|
gravity = SouthWestGravity;
|
|
}
|
|
else
|
|
mysizehints.y = y;
|
|
|
|
if((x < 0) && (y < 0))
|
|
gravity = SouthEastGravity;
|
|
mysizehints.flags |= USPosition;
|
|
}
|
|
|
|
mysizehints.win_gravity = gravity;
|
|
|
|
main_win = XCreateSimpleWindow(dpy,Root,mysizehints.x,mysizehints.y,
|
|
mysizehints.width,mysizehints.height,
|
|
0,0,back_pix);
|
|
|
|
for(i=0;i<num_folders;i++)
|
|
{
|
|
if(num_columns <num_rows)
|
|
{
|
|
Folders[i].cols = 1;
|
|
Folders[i].rows = Folders[i].count;
|
|
}
|
|
else if ((num_columns == num_rows) && (!ROWS))
|
|
{
|
|
Folders[i].cols = 1;
|
|
Folders[i].rows = Folders[i].count;
|
|
}
|
|
else
|
|
{
|
|
Folders[i].cols = Folders[i].count;
|
|
Folders[i].rows = 1;
|
|
}
|
|
Folders[i].win = XCreateSimpleWindow(dpy, Root, 0,0,
|
|
BUTTONWIDTH*Folders[i].rows,BUTTONHEIGHT*Folders[i].cols,
|
|
0,0,back_pix);
|
|
XSetWMNormalHints(dpy,Folders[i].win,&mysizehints);
|
|
XSelectInput(dpy, Folders[i].win, MW_EVENTS);
|
|
}
|
|
|
|
XSetWMProtocols(dpy,main_win,&wm_del_win,1);
|
|
|
|
XSetWMNormalHints(dpy,main_win,&mysizehints);
|
|
|
|
XSelectInput(dpy, main_win, MW_EVENTS);
|
|
change_window_name(MyName);
|
|
}
|
|
|
|
|
|
void nocolor(char *a, char *b)
|
|
{
|
|
fprintf(stderr,"%s: can't %s %s\n", MyName, a,b);
|
|
}
|
|
|
|
/****************************************************************************
|
|
*
|
|
* Loads a single color
|
|
*
|
|
****************************************************************************/
|
|
Pixel GetColor(char *name)
|
|
{
|
|
XColor color;
|
|
XWindowAttributes attributes;
|
|
|
|
XGetWindowAttributes(dpy,Root,&attributes);
|
|
color.pixel = 0;
|
|
if (!XParseColor (dpy, attributes.colormap, name, &color))
|
|
{
|
|
nocolor("parse",name);
|
|
}
|
|
else if(!XAllocColor (dpy, attributes.colormap, &color))
|
|
{
|
|
nocolor("alloc",name);
|
|
}
|
|
return color.pixel;
|
|
}
|
|
|
|
/************************************************************************
|
|
*
|
|
* Dead pipe handler
|
|
*
|
|
***********************************************************************/
|
|
void DeadPipe(int nonsense)
|
|
{
|
|
int i,j,button;
|
|
|
|
#ifdef ENABLE_SOUND
|
|
int val=-1;
|
|
write(PlayerChannel[1],&val,sizeof(val));
|
|
if (SoundThread != 0)
|
|
kill(SoundThread,SIGUSR1);
|
|
#endif
|
|
for(i=0;i<num_rows;i++)
|
|
for(j=0;j<num_columns; j++)
|
|
{
|
|
button = i*num_columns + j;
|
|
/* delete swallowed windows, but not modules (afterstep handles those) */
|
|
if(((Buttons[button].swallow == 3)||(Buttons[button].swallow == 4))&&
|
|
(Buttons[button].module == 0))
|
|
{
|
|
my_send_clientmessage(Buttons[button].IconWin,wm_del_win,CurrentTime);
|
|
XSync(dpy,0);
|
|
}
|
|
}
|
|
XSync(dpy,0);
|
|
exit(0);
|
|
}
|
|
|
|
int TOTHEFOLDER = -1;
|
|
/*****************************************************************************
|
|
*
|
|
* This routine is responsible for reading and parsing the config file
|
|
*
|
|
****************************************************************************/
|
|
void ParseOptions(char *filename)
|
|
{
|
|
char *tline,*orig_tline,*tmp;
|
|
int Clength, len;
|
|
|
|
GetConfigLine(fd, &tline);
|
|
orig_tline = tline;
|
|
Clength = strlen(MyName);
|
|
while(tline != NULL && tline[0] != '\0')
|
|
{
|
|
int g_x, g_y;
|
|
unsigned width,height;
|
|
while(isspace(*tline))tline++;
|
|
|
|
if((strlen(&tline[0])>1)&&
|
|
(strncasecmp(tline,CatString3("*", MyName, "Geometry"),Clength+9)==0))
|
|
{
|
|
tmp = &tline[Clength+9];
|
|
while(((isspace(*tmp))&&(*tmp != '\n'))&&(*tmp != 0))
|
|
{
|
|
tmp++;
|
|
}
|
|
tmp[strlen(tmp)-1] = 0;
|
|
|
|
flags = XParseGeometry(tmp,&g_x,&g_y,&width,&height);
|
|
if (flags & WidthValue)
|
|
w = width;
|
|
if (flags & HeightValue)
|
|
h = height;
|
|
if (flags & XValue)
|
|
x = g_x;
|
|
if (flags & YValue)
|
|
y = g_y;
|
|
}
|
|
else if((strlen(&tline[0])>1)&&
|
|
(strncasecmp(tline,CatString3("*",MyName,"Rows"),Clength+5)==0))
|
|
{
|
|
len=sscanf(&tline[Clength+5],"%d",&num_rows);
|
|
if(len < 1)
|
|
num_rows = 0;
|
|
ROWS = TRUE;
|
|
}
|
|
else if((strlen(&tline[0])>1)&&
|
|
(strncasecmp(tline,CatString3("*",MyName,"Columns"),Clength+8)==0))
|
|
{
|
|
len=sscanf(&tline[Clength+8],"%d",&num_columns);
|
|
if(len < 1)
|
|
num_columns = 0;
|
|
ROWS = FALSE;
|
|
}
|
|
else if((strlen(&tline[0])>1)&&
|
|
(strncasecmp(tline,CatString3("*",MyName,"NoPush"),Clength+5)==0))
|
|
{
|
|
Pushable = 0;
|
|
} else if((strlen(&tline[0])>1)&&
|
|
(strncasecmp(tline,CatString3("*",MyName,"FullPush"),Clength+9)==0))
|
|
{
|
|
PushStyle = 1;
|
|
} else if((strlen(&tline[0])>1)&&
|
|
(strncasecmp(tline,CatString3("*",MyName,"NoBorder"),Clength+9)==0))
|
|
{
|
|
NoBorder = 1;
|
|
} else if ((strlen(&tline[0])>1)
|
|
&&(strncasecmp(tline,CatString3("*",MyName,"ForceSize"),Clength+10)==0)) {
|
|
ForceSize = 1;
|
|
} else if ((strlen(&tline[0])>1)
|
|
&&(strncasecmp(tline,CatString3("*",MyName,"TextureType"),Clength+12)==0)) {
|
|
if (sscanf(&tline[Clength+12],"%d",&TextureType)<1)
|
|
TextureType = TEXTURE_BUILTIN;
|
|
} else if ((strlen(&tline[0])>1)
|
|
&&(strncasecmp(tline,CatString3("*",MyName,"MaxColors"),Clength+10)==0)) {
|
|
|
|
if (sscanf(&tline[Clength+10],"%d",&MaxColors)<1)
|
|
MaxColors = 16;
|
|
} else if ((strlen(&tline[0])>1)
|
|
&&(strncasecmp(tline,CatString3("*",MyName,"BgColor"),Clength+8)==0)) {
|
|
char *tmp;
|
|
tmp=safemalloc(strlen(tline));
|
|
sscanf(&tline[Clength+8],"%s",tmp);
|
|
BgColor=GetColor(tmp);
|
|
free(tmp);
|
|
} else if ((strlen(&tline[0])>1)
|
|
&&(strncasecmp(tline,CatString3("*",MyName,"TextureColor"),Clength+13)==0)) {
|
|
char *c1, *c2;
|
|
XColor color;
|
|
XWindowAttributes attributes;
|
|
|
|
XGetWindowAttributes(dpy,Root,&attributes);
|
|
len = strlen(&tline[Clength+13]);
|
|
c1 = safemalloc(len);
|
|
c2 = safemalloc(len);
|
|
if (sscanf(&tline[Clength+13],"%s %s",c1,c2)!=2) {
|
|
fprintf(stderr,"%s:You must specify two colors for the texture\n",MyName);
|
|
FromColor[0]=0;
|
|
FromColor[1]=0;
|
|
FromColor[2]=0;
|
|
ToColor[0]=0;
|
|
ToColor[1]=0;
|
|
ToColor[2]=0;
|
|
}
|
|
if (!XParseColor (dpy, attributes.colormap, c1, &color))
|
|
{
|
|
nocolor("parse",c1);
|
|
TextureType=TEXTURE_BUILTIN;
|
|
} else {
|
|
FromColor[0]=color.red;
|
|
FromColor[1]=color.green;
|
|
FromColor[2]=color.blue;
|
|
}
|
|
if (!XParseColor (dpy, attributes.colormap, c2, &color))
|
|
{
|
|
nocolor("parse",c2);
|
|
TextureType=TEXTURE_BUILTIN;
|
|
} else {
|
|
ToColor[0]=color.red;
|
|
ToColor[1]=color.green;
|
|
ToColor[2]=color.blue;
|
|
}
|
|
free(c1);
|
|
free(c2);
|
|
} else if ((strlen(&tline[0])>1)
|
|
&&(strncasecmp(tline,CatString3("*",MyName,"Pixmap"),Clength+7)==0)) {
|
|
CopyString(&BgPixmapFile,&tline[Clength+7]);
|
|
} else if((strlen(&tline[0])>1)&&
|
|
(strncasecmp(tline,CatString3("*",MyName,"AnimateMain"),Clength+12)==0))
|
|
{
|
|
AnimateMain = 1;
|
|
}
|
|
else if((strlen(&tline[0])>1)&&
|
|
(strncasecmp(tline,CatString3("*",MyName,"Animate"),Clength+8)==0))
|
|
{
|
|
if ((tline[Clength+9]!='M') && (tline[Clength+9]!='m'))
|
|
AnimationStyle = 1;
|
|
}
|
|
#ifdef ENABLE_SOUND
|
|
else if((strlen(&tline[0])>1)&&
|
|
(strncasecmp(tline,CatString3("*",MyName,"Player"),Clength+7)==0))
|
|
{
|
|
CopyString(&SoundPlayer, &tline[Clength+7]);
|
|
} else if((strlen(&tline[0])>1)&&
|
|
(strncasecmp(tline,CatString3("*",MyName,"Sound"),Clength+6)==0))
|
|
{
|
|
bind_sound(&tline[Clength+6]);
|
|
SoundActive = 1;
|
|
}
|
|
#endif
|
|
else if((strlen(&tline[0])>1)
|
|
&&(strncasecmp(tline,CatString3("*", MyName, ""),Clength+1)==0)
|
|
&& (num_buttons < MAX_BUTTONS))
|
|
{
|
|
/* check if this is a invalid option */
|
|
if (!isspace(tline[Clength+1]))
|
|
fprintf(stderr,"%s:invalid option %s\n",MyName,tline);
|
|
else
|
|
match_string(&tline[Clength+1]);
|
|
}
|
|
else if((strlen(&tline[0])>1)&&(strncasecmp(tline,"IconPath",8)==0))
|
|
{
|
|
CopyString(&iconPath,&tline[8]);
|
|
}
|
|
else if((strlen(&tline[0])>1)&&(strncasecmp(tline,"PixmapPath",10)==0))
|
|
{
|
|
CopyString(&pixmapPath,&tline[10]);
|
|
}
|
|
#ifdef ENABLE_SOUND
|
|
else if((strlen(&tline[0])>1)&&(strncasecmp(tline,"*AudioDir",9)==0))
|
|
{
|
|
CopyString(&SoundPath,&tline[9]);
|
|
}
|
|
else if((strlen(&tline[0])>1)&&(strncasecmp(tline,"ModulePath",11)==0))
|
|
{
|
|
CopyString(&ModulePath,&tline[11]);
|
|
}
|
|
#endif
|
|
GetConfigLine(fd, &tline);
|
|
orig_tline = tline;
|
|
}
|
|
#ifdef ENABLE_DND
|
|
/* ignore last button if there's nothing bound to it */
|
|
if ((Buttons[num_buttons-1].drop_action!=NULL) &&
|
|
(Buttons[num_buttons-1].iconno==0)) {
|
|
num_buttons--;
|
|
}
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Gets a word of a given index in the line, stripping any blanks
|
|
* The returned word is newly allocated
|
|
*/
|
|
#ifdef ENABLE_SOUND
|
|
char *get_token(char *tline, int index)
|
|
{
|
|
char *start, *end;
|
|
int i,c,size;
|
|
char *word;
|
|
|
|
index++; /* index is 0 based */
|
|
size = strlen(tline);
|
|
i=c=0;
|
|
start=end=tline;
|
|
while (i<index && c<size) {
|
|
start=end;
|
|
while(isspace(*start) && c<size) {
|
|
start++;
|
|
c++;
|
|
}
|
|
end=start;
|
|
while(!isspace(*end) && c<size) {
|
|
end++;
|
|
c++;
|
|
}
|
|
if (end==start) return NULL;
|
|
i++;
|
|
}
|
|
if (i<index) return NULL;
|
|
word=safemalloc(end-start+1);
|
|
strncpy(word, start, end-start);
|
|
word[end-start]=0;
|
|
return word;
|
|
}
|
|
|
|
/**************************************************************************
|
|
*
|
|
* Parses a sound binding
|
|
*
|
|
**************************************************************************/
|
|
void bind_sound(char *tline)
|
|
{
|
|
char *event, *sound;
|
|
|
|
event = get_token(tline,0);
|
|
if (event==NULL) {
|
|
fprintf(stderr,"%s:bad sound binding %s\n",MyName,tline);
|
|
return;
|
|
}
|
|
sound = get_token(tline,1);
|
|
if (sound==NULL) {
|
|
free(event);
|
|
fprintf(stderr,"%s:bad sound binding %s\n",MyName,tline);
|
|
return;
|
|
}
|
|
if (strcmp(event,"open_folder")==0) {
|
|
Sounds[WHEV_OPEN_FOLDER]=sound;
|
|
} else if (strcmp(event,"close_folder")==0) {
|
|
Sounds[WHEV_CLOSE_FOLDER]=sound;
|
|
} else if (strcmp(event,"open_main")==0) {
|
|
Sounds[WHEV_OPEN_MAIN]=sound;
|
|
} else if (strcmp(event,"close_main")==0) {
|
|
Sounds[WHEV_CLOSE_MAIN]=sound;
|
|
} else if (strcmp(event,"push")==0) {
|
|
Sounds[WHEV_PUSH]=sound;
|
|
} else if (strcmp(event,"drop")==0) {
|
|
Sounds[WHEV_DROP]=sound;
|
|
} else {
|
|
fprintf(stderr,"%s:bad event %s in sound binding\n",MyName,event);
|
|
free(sound);
|
|
}
|
|
free(event);
|
|
return;
|
|
}
|
|
#endif /* ENABLE_SOUND */
|
|
|
|
/**************************************************************************
|
|
*
|
|
* Parses a button command line from the config file
|
|
*
|
|
*************************************************************************/
|
|
void match_string(char *tline)
|
|
{
|
|
int len,i,i2,n,j,k;
|
|
char *ptr,*start,*end,*tmp;
|
|
struct button_info *actual;
|
|
|
|
/* skip spaces */
|
|
while(isspace(*tline)&&(*tline != '\n')&&(*tline != 0))
|
|
tline++;
|
|
|
|
/* read next word. Its the button label. Users can specify ""
|
|
* NoIcon, or whatever to skip the label */
|
|
/* read to next space */
|
|
start = tline;
|
|
end = tline;
|
|
while((!isspace(*end))&&(*end!='\n')&&(*end!=0))
|
|
end++;
|
|
len = end - start;
|
|
ptr = safemalloc(len+1);
|
|
strncpy(ptr,start,len);
|
|
ptr[len] = 0;
|
|
|
|
if (strncmp(ptr,"~Folder",7)==0)
|
|
{
|
|
TOTHEFOLDER = -1;
|
|
Folders[num_folders].firstbutton = num_folderbuttons;
|
|
num_folders++;
|
|
free(ptr);
|
|
return;
|
|
}
|
|
|
|
if(TOTHEFOLDER==-1)
|
|
{
|
|
actual = &Buttons[num_buttons++];
|
|
actual->parent = &main_win;
|
|
}
|
|
else
|
|
{
|
|
actual = &Buttons[--num_folderbuttons];
|
|
actual->folder = num_folders;
|
|
actual->parent = &Folders[num_folders].win;
|
|
};
|
|
|
|
actual->title = ptr;
|
|
|
|
/* read next word. Its the icon bitmap/pixmap label. Users can specify ""
|
|
* NoIcon, or whatever to skip the label */
|
|
/* read to next space */
|
|
start = end;
|
|
/* skip spaces */
|
|
while(isspace(*start)&&(*start != '\n')&&(*start != 0))
|
|
start++;
|
|
end = start;
|
|
while((!isspace(*end))&&(*end!='\n')&&(*end!=0))
|
|
end++;
|
|
len = end - start;
|
|
ptr = safemalloc(len+1);
|
|
strncpy(ptr,start,len);
|
|
ptr[len] = 0;
|
|
/* separate icon files to be overlaid */
|
|
i2 = len;
|
|
j=k=0;
|
|
for(i=0;i<MAX_OVERLAY;i++) {
|
|
while (ptr[j]!=',' && j<i2) j++;
|
|
actual->icons[i].file=safemalloc(j-k+1);
|
|
strncpy(actual->icons[i].file,&(ptr[k]),j-k);
|
|
actual->icons[i].file[j-k]=0;
|
|
actual->iconno++;
|
|
j++;
|
|
k=j;
|
|
if (j>=i2) break;
|
|
}
|
|
tline = end;
|
|
for (i=num_buttons - 2;i>=0;i--)
|
|
{
|
|
if (strcmp(Buttons[i].title, actual->title) == 0)
|
|
{
|
|
actual = &Buttons[i];
|
|
num_buttons--;
|
|
for(i=0;i<actual->iconno;i++) {
|
|
free(actual->icons[i].file);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
/* skip spaces */
|
|
while(isspace(*tline)&&(*tline != '\n')&&(*tline != 0))
|
|
tline++;
|
|
#ifdef ENABLE_DND
|
|
if (strncasecmp(tline,"dropexec",8)==0) {
|
|
/* get command to execute for dropped stuff */
|
|
|
|
if(TOTHEFOLDER==-1) {
|
|
num_buttons--; /* make the next parsed thing the button for this */
|
|
free(ptr);
|
|
for(i=0;i<actual->iconno;i++) {
|
|
free(actual->icons[i].file);
|
|
}
|
|
actual->iconno=0;
|
|
} else {
|
|
num_folderbuttons++;
|
|
free(ptr);
|
|
for(i=0;i<actual->iconno;i++) {
|
|
free(actual->icons[i].file);
|
|
}
|
|
actual->iconno=0;
|
|
fprintf(stderr,"Drop in Folders not supported. Ignoring option\n");
|
|
return;
|
|
}
|
|
|
|
tline=strstr(tline,"Exec");
|
|
len = strlen(tline);
|
|
tmp = tline + len -1;
|
|
while(((isspace(*tmp))||(*tmp == '\n'))&&(tmp >=tline)) {
|
|
tmp--;
|
|
len--;
|
|
}
|
|
ptr = safemalloc(len+1);
|
|
actual->drop_action=ptr;
|
|
strncpy(ptr,tline,len);
|
|
ptr[len]=0;
|
|
} else
|
|
#endif
|
|
if(strncasecmp(tline,"swallow",7)==0 || strncasecmp(tline,"maxswallow",10)==0)
|
|
{
|
|
/* Look for swallow "identifier", in which
|
|
case Wharf spawns and gobbles up window */
|
|
i=7;
|
|
while((tline[i] != 0)&&
|
|
(tline[i] != '"'))
|
|
i++;
|
|
i2=i+1;
|
|
while((tline[i2] != 0)&&
|
|
(tline[i2] != '"'))
|
|
i2++;
|
|
actual->maxsize =
|
|
strncasecmp(tline,"maxswallow",10) == 0 ? 1 : 0;
|
|
if(i2 - i >1)
|
|
{
|
|
actual->hangon = safemalloc(i2-i);
|
|
strncpy(actual->hangon,&tline[i+1],i2-i-1);
|
|
actual->hangon[i2-i-1] = 0;
|
|
actual->swallow = 1;
|
|
}
|
|
n = 7;
|
|
n = i2+1;
|
|
while((isspace(tline[n]))&&(tline[n]!=0))
|
|
n++;
|
|
len = strlen(&tline[n]);
|
|
tmp = tline + n + len -1;
|
|
while(((isspace(*tmp))||(*tmp == '\n'))&&(tmp >=(tline + n)))
|
|
{
|
|
tmp--;
|
|
len--;
|
|
}
|
|
ptr = safemalloc(len+6);
|
|
if(strncasecmp(&tline[n],"Module",6)==0)
|
|
{
|
|
ptr[0] = 0;
|
|
actual->module = 1;
|
|
}
|
|
else
|
|
strcpy(ptr,"Exec ");
|
|
i2 = strlen(ptr);
|
|
strncat(ptr,&tline[n],len);
|
|
ptr[i2+len]=0;
|
|
SendText(fd,ptr,0);
|
|
free(ptr);
|
|
actual->action = NULL;
|
|
}
|
|
else
|
|
{
|
|
if(!TOTHEFOLDER)
|
|
{
|
|
Folders[num_folders].count++;
|
|
}
|
|
|
|
len = strlen(tline);
|
|
tmp = tline + len -1;
|
|
while(((isspace(*tmp))||(*tmp == '\n'))&&(tmp >=tline))
|
|
{
|
|
tmp--;
|
|
len--;
|
|
}
|
|
ptr = safemalloc(len+1);
|
|
strncpy(ptr,tline,len);
|
|
ptr[len]=0;
|
|
|
|
if (strncmp(ptr,"Folder",6)==0)
|
|
{
|
|
TOTHEFOLDER = 0;
|
|
Folders[num_folders].count = 0;
|
|
actual->folder = num_folders;
|
|
Folders[num_folders].mapped = NOTMAPPED;
|
|
}
|
|
actual->action = ptr;
|
|
}
|
|
return;
|
|
}
|
|
|
|
/**************************************************************************
|
|
* Change the window name displayed in the title bar.
|
|
**************************************************************************/
|
|
void change_window_name(char *str)
|
|
{
|
|
XTextProperty name;
|
|
int i;
|
|
|
|
if (XStringListToTextProperty(&str,1,&name) == 0)
|
|
{
|
|
fprintf(stderr,"%s: cannot allocate window name",MyName);
|
|
return;
|
|
}
|
|
XSetWMName(dpy,main_win,&name);
|
|
XSetWMIconName(dpy,main_win,&name);
|
|
for(i=0;i<num_folders;i++)
|
|
{
|
|
XSetWMName(dpy, Folders[i].win,&name);
|
|
XSetWMIconName(dpy, Folders[i].win,&name);
|
|
}
|
|
XFree(name.value);
|
|
}
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* Waits for next X event, or for an auto-raise timeout.
|
|
*
|
|
****************************************************************************/
|
|
int My_XNextEvent(Display *dpy, XEvent *event)
|
|
{
|
|
fd_set in_fdset;
|
|
unsigned long header[HEADER_SIZE];
|
|
int count;
|
|
static int miss_counter = 0;
|
|
unsigned long *body;
|
|
|
|
if(XPending(dpy))
|
|
{
|
|
XNextEvent(dpy,event);
|
|
return 1;
|
|
}
|
|
|
|
FD_ZERO(&in_fdset);
|
|
FD_SET(x_fd,&in_fdset);
|
|
FD_SET(fd[1],&in_fdset);
|
|
|
|
select(fd_width, SELECT_TYPE_ARG234 &in_fdset, 0, 0, NULL);
|
|
if(FD_ISSET(x_fd, &in_fdset))
|
|
{
|
|
if(XPending(dpy))
|
|
{
|
|
XNextEvent(dpy,event);
|
|
miss_counter = 0;
|
|
return 1;
|
|
}
|
|
else
|
|
miss_counter++;
|
|
if(miss_counter > 100)
|
|
DeadPipe(0);
|
|
}
|
|
|
|
if(FD_ISSET(fd[1], &in_fdset))
|
|
{
|
|
if((count = ReadFvwmPacket(fd[1], header, &body)) > 0)
|
|
{
|
|
process_message(header[1],body);
|
|
free(body);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
void CheckForHangon(unsigned long *body)
|
|
{
|
|
int button,i,j;
|
|
char *cbody;
|
|
|
|
cbody = (char *)&body[3];
|
|
for(i=0;i<num_rows;i++)
|
|
for(j=0;j<num_columns; j++)
|
|
{
|
|
button = i*num_columns + j;
|
|
if(Buttons[button].hangon != NULL)
|
|
{
|
|
if(strcmp(cbody,Buttons[button].hangon)==0)
|
|
{
|
|
if(Buttons[button].swallow == 1)
|
|
{
|
|
Buttons[button].swallow = 2;
|
|
if(Buttons[button].IconWin != None)
|
|
{
|
|
XDestroyWindow(dpy,Buttons[button].IconWin);
|
|
}
|
|
Buttons[button].IconWin = (Window)body[0];
|
|
free(Buttons[button].hangon);
|
|
Buttons[button].hangon = NULL;
|
|
}
|
|
else
|
|
{
|
|
if (Buttons[button].swallow == 4)
|
|
Buttons[button].swallow = 3;
|
|
Buttons[button].up = 1;
|
|
free(Buttons[button].hangon);
|
|
Buttons[button].hangon = NULL;
|
|
RedrawWindow(&main_win,0, button, num_rows, num_columns);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**************************************************************************
|
|
*
|
|
* Process window list messages
|
|
*
|
|
*************************************************************************/
|
|
void process_message(unsigned long type,unsigned long *body)
|
|
{
|
|
switch(type)
|
|
{
|
|
/* case M_TOGGLE_PAGING:
|
|
pageing_enabled = body[0];
|
|
RedrawWindow(&main_win,0, -1, num_rows, num_columns);
|
|
break;
|
|
pdg */
|
|
case M_NEW_DESK:
|
|
new_desk = body[0];
|
|
RedrawWindow(&main_win,0, -1, num_rows, num_columns);
|
|
break;
|
|
case M_END_WINDOWLIST:
|
|
RedrawWindow(&main_win,0, -1, num_rows, num_columns);
|
|
case M_MAP:
|
|
swallow(body);
|
|
case M_RES_NAME:
|
|
case M_RES_CLASS:
|
|
case M_WINDOW_NAME:
|
|
CheckForHangon(body);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***************************************************************************
|
|
*
|
|
* ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all
|
|
* client messages will have the following form:
|
|
*
|
|
* event type ClientMessage
|
|
* message type _XA_WM_PROTOCOLS
|
|
* window tmp->w
|
|
* format 32
|
|
* data[0] message atom
|
|
* data[1] time stamp
|
|
*
|
|
****************************************************************************/
|
|
void my_send_clientmessage (Window w, Atom a, Time timestamp)
|
|
{
|
|
XClientMessageEvent ev;
|
|
|
|
ev.type = ClientMessage;
|
|
ev.window = w;
|
|
ev.message_type = _XA_WM_PROTOCOLS;
|
|
ev.format = 32;
|
|
ev.data.l[0] = a;
|
|
ev.data.l[1] = timestamp;
|
|
XSendEvent (dpy, w, False, 0L, (XEvent *) &ev);
|
|
}
|
|
|
|
|
|
void swallow(unsigned long *body)
|
|
{
|
|
char *temp;
|
|
int button,i,j;
|
|
long supplied;
|
|
|
|
for(i=0;i<num_rows;i++)
|
|
for(j=0;j<num_columns; j++)
|
|
{
|
|
button = i*num_columns + j;
|
|
if((Buttons[button].IconWin == (Window)body[0])&&
|
|
(Buttons[button].swallow == 2))
|
|
{
|
|
Buttons[button].swallow = 3;
|
|
/* "Swallow" the window! */
|
|
|
|
XReparentWindow(dpy,Buttons[button].IconWin, main_win,
|
|
j*BUTTONWIDTH+4, i*BUTTONHEIGHT+4);
|
|
XMapWindow(dpy,Buttons[button].IconWin);
|
|
XSelectInput(dpy,(Window)body[0],
|
|
PropertyChangeMask);
|
|
if (Buttons[button].action)
|
|
{
|
|
/*
|
|
XGrabButton(dpy, Button1Mask | Button2Mask, None,
|
|
(Window)body[0],
|
|
False, ButtonPressMask | ButtonReleaseMask,
|
|
GrabModeAsync, GrabModeAsync, None, None);
|
|
XGrabButton(dpy, Button1Mask | Button2Mask, LockMask,
|
|
*/
|
|
unsigned *mods = lock_mods;
|
|
do XGrabButton(dpy, Button1Mask | Button2Mask, *mods,
|
|
|
|
(Window)body[0],
|
|
False, ButtonPressMask | ButtonReleaseMask,
|
|
GrabModeAsync, GrabModeAsync, None, None);
|
|
while (*mods++);
|
|
|
|
}
|
|
if (Buttons[button].maxsize) {
|
|
Buttons[button].icons[0].w = 55;
|
|
Buttons[button].icons[0].h = 57;
|
|
}
|
|
else {
|
|
Buttons[button].icons[0].w = ICON_WIN_WIDTH;
|
|
Buttons[button].icons[0].h = ICON_WIN_HEIGHT;
|
|
}
|
|
if (!XGetWMNormalHints (dpy, Buttons[button].IconWin,
|
|
&Buttons[button].hints,
|
|
&supplied))
|
|
Buttons[button].hints.flags = 0;
|
|
|
|
XResizeWindow(dpy,(Window)body[0], Buttons[button].icons[0].w,
|
|
Buttons[button].icons[0].h);
|
|
XMoveWindow(dpy,Buttons[button].IconWin,
|
|
j*BUTTONWIDTH +
|
|
(BUTTONWIDTH - Buttons[button].icons[0].w)/2,
|
|
i*BUTTONHEIGHT +
|
|
(BUTTONHEIGHT - Buttons[button].icons[0].h)/2);
|
|
|
|
XFetchName(dpy, Buttons[button].IconWin, &temp);
|
|
XClearArea(dpy, main_win,j*BUTTONWIDTH, i*BUTTONHEIGHT,
|
|
BUTTONWIDTH,BUTTONHEIGHT,0);
|
|
if(strcmp(Buttons[button].title,"-")!=0)
|
|
CopyString(&Buttons[button].title, temp);
|
|
RedrawWindow(&main_win,0, -1, num_rows, num_columns);
|
|
XFree(temp);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void FindLockMods(void)
|
|
{
|
|
int m, i, knl;
|
|
char* kn;
|
|
KeySym ks;
|
|
KeyCode kc, *kp;
|
|
unsigned lockmask, *mp;
|
|
XModifierKeymap* mm = XGetModifierMapping(dpy);
|
|
lockmask = LockMask;
|
|
if (mm)
|
|
{
|
|
kp = mm->modifiermap;
|
|
for (m = 0; m < 8; m++)
|
|
{
|
|
for (i = 0; i < mm->max_keypermod; i++)
|
|
{
|
|
if ((kc = *kp++) &&
|
|
((ks = XKeycodeToKeysym(dpy, kc, 0)) != NoSymbol))
|
|
{
|
|
kn = XKeysymToString(ks);
|
|
knl = strlen(kn);
|
|
if ((knl > 6) && (strcasecmp(kn + knl - 4, "lock") == 0))
|
|
lockmask |= (1 << m);
|
|
}
|
|
}
|
|
}
|
|
XFreeModifiermap(mm);
|
|
}
|
|
lockmask &= ~(ShiftMask | ControlMask);
|
|
|
|
mp = lock_mods;
|
|
for (m = 0, i = 1; i < 256; i++)
|
|
{
|
|
if ((i & lockmask) > m)
|
|
m = *mp++ = (i & lockmask);
|
|
}
|
|
*mp = 0;
|
|
}
|
|
|
|
|
|
/***********************************************************************
|
|
*
|
|
* Procedure:
|
|
* ConstrainSize - adjust the given width and height to account for the
|
|
* constraints imposed by size hints
|
|
*
|
|
* The general algorithm, especially the aspect ratio stuff, is
|
|
* borrowed from uwm's CheckConsistency routine.
|
|
*
|
|
***********************************************************************/
|
|
void ConstrainSize (XSizeHints *hints, int *widthp, int *heightp)
|
|
{
|
|
#define makemult(a,b) ((b==1) ? (a) : (((int)((a)/(b))) * (b)) )
|
|
#define _min(a,b) (((a) < (b)) ? (a) : (b))
|
|
|
|
|
|
int minWidth, minHeight, maxWidth, maxHeight, xinc, yinc, delta;
|
|
int baseWidth, baseHeight;
|
|
int dwidth = *widthp, dheight = *heightp;
|
|
|
|
if(hints->flags & PMinSize)
|
|
{
|
|
minWidth = hints->min_width;
|
|
minHeight = hints->min_height;
|
|
if(hints->flags & PBaseSize)
|
|
{
|
|
baseWidth = hints->base_width;
|
|
baseHeight = hints->base_height;
|
|
}
|
|
else
|
|
{
|
|
baseWidth = hints->min_width;
|
|
baseHeight = hints->min_height;
|
|
}
|
|
}
|
|
else if(hints->flags & PBaseSize)
|
|
{
|
|
minWidth = hints->base_width;
|
|
minHeight = hints->base_height;
|
|
baseWidth = hints->base_width;
|
|
baseHeight = hints->base_height;
|
|
}
|
|
else
|
|
{
|
|
minWidth = 1;
|
|
minHeight = 1;
|
|
baseWidth = 1;
|
|
baseHeight = 1;
|
|
}
|
|
|
|
if(hints->flags & PMaxSize)
|
|
{
|
|
maxWidth = hints->max_width;
|
|
maxHeight = hints->max_height;
|
|
}
|
|
else
|
|
{
|
|
maxWidth = 10000;
|
|
maxHeight = 10000;
|
|
}
|
|
if(hints->flags & PResizeInc)
|
|
{
|
|
xinc = hints->width_inc;
|
|
yinc = hints->height_inc;
|
|
}
|
|
else
|
|
{
|
|
xinc = 1;
|
|
yinc = 1;
|
|
}
|
|
|
|
/*
|
|
* First, clamp to min and max values
|
|
*/
|
|
if (dwidth < minWidth) dwidth = minWidth;
|
|
if (dheight < minHeight) dheight = minHeight;
|
|
|
|
if (dwidth > maxWidth) dwidth = maxWidth;
|
|
if (dheight > maxHeight) dheight = maxHeight;
|
|
|
|
|
|
/*
|
|
* Second, fit to base + N * inc
|
|
*/
|
|
dwidth = ((dwidth - baseWidth) / xinc * xinc) + baseWidth;
|
|
dheight = ((dheight - baseHeight) / yinc * yinc) + baseHeight;
|
|
|
|
|
|
/*
|
|
* Third, adjust for aspect ratio
|
|
*/
|
|
#define maxAspectX hints->max_aspect.x
|
|
#define maxAspectY hints->max_aspect.y
|
|
#define minAspectX hints->min_aspect.x
|
|
#define minAspectY hints->min_aspect.y
|
|
/*
|
|
* The math looks like this:
|
|
*
|
|
* minAspectX dwidth maxAspectX
|
|
* ---------- <= ------- <= ----------
|
|
* minAspectY dheight maxAspectY
|
|
*
|
|
* If that is multiplied out, then the width and height are
|
|
* invalid in the following situations:
|
|
*
|
|
* minAspectX * dheight > minAspectY * dwidth
|
|
* maxAspectX * dheight < maxAspectY * dwidth
|
|
*
|
|
*/
|
|
|
|
if (hints->flags & PAspect)
|
|
{
|
|
if (minAspectX * dheight > minAspectY * dwidth)
|
|
{
|
|
delta = makemult(minAspectX * dheight / minAspectY - dwidth,
|
|
xinc);
|
|
if (dwidth + delta <= maxWidth)
|
|
dwidth += delta;
|
|
else
|
|
{
|
|
delta = makemult(dheight - dwidth*minAspectY/minAspectX,
|
|
yinc);
|
|
if (dheight - delta >= minHeight) dheight -= delta;
|
|
}
|
|
}
|
|
|
|
if (maxAspectX * dheight < maxAspectY * dwidth)
|
|
{
|
|
delta = makemult(dwidth * maxAspectY / maxAspectX - dheight,
|
|
yinc);
|
|
if (dheight + delta <= maxHeight)
|
|
dheight += delta;
|
|
else
|
|
{
|
|
delta = makemult(dwidth - maxAspectX*dheight/maxAspectY,
|
|
xinc);
|
|
if (dwidth - delta >= minWidth) dwidth -= delta;
|
|
}
|
|
}
|
|
}
|
|
|
|
*widthp = dwidth;
|
|
*heightp = dheight;
|
|
return;
|
|
}
|
|
|
|
|
|
#ifdef ENABLE_SOUND
|
|
void PlaySound(int event)
|
|
{
|
|
int timestamp;
|
|
|
|
if (!SoundActive)
|
|
return;
|
|
if (Sounds[event]==NULL) return;
|
|
write(PlayerChannel[1],&event,sizeof(event));
|
|
timestamp = clock();
|
|
write(PlayerChannel[1],×tamp,sizeof(timestamp));
|
|
/*
|
|
kill(SoundThread,SIGUSR1);
|
|
*/
|
|
}
|
|
#endif
|