/*
 *  DeskSave.c    v2.0  15 Sep 1988
 *                v2.0a 20 Jul 1990
 *
 *  Windows 3.0 Desk Saving Utility
 *
 *  Written by Jari Salminen, OH2BYQ
 *	UUCP: mcvax!tut!kolvi!jsa
 *	Internet: jsa@kolvi.hut.FI
 *
 *  Usage:
 *	To save desk:
 *	- run DESKSAVE
 *	- select command "Save Desk" from System menu
 *
 *	To arrange desk:
 *	- run DESKSAVE and select command "Arrange Desk"
 *	or
 *	- run DESKSAVE with command line option "-l"
 *	or
 *	- add DESKLOAD.EXE to "Load"-list in WIN.INI as the last name in list
 *	  ( done by DESKSAVE with "Save Desk" command )
 *
 *  This file creates both DESKSAVE.OBJ and DESKLOAD.OBJ
 *  For DESKLOAD.OBJ compile with option /DDESKLOAD
 *
 */

#define NOMINMAX
#include <windows.h>
#include <stdlib.h>
#include <string.h>
#include "desksave.h"

#define MAXINT		32767
#define MAX_WIN		64		/* Max. number of windows that */
					/* can be arranged */
#define MAX_LIST_SIZE	1024

/* Menu command definitions */

#define CMD_SAVEDESK	1
#define CMD_ARRDESK	2
#define CMD_CLEAR	3
#define CMD_ABOUT	4


HANDLE	hInstance;		/* Our instance handle */
HWND	hWndDeskSave;		/* hWnd of our icon */

FARPROC	lpEnumWndProc;		/* Enumerate windows */

char	szLoadList[MAX_LIST_SIZE];	/* List of files to load */
char	szRunList[MAX_LIST_SIZE];	/* List of files to run */
char	szFileName[_MAX_FNAME + _MAX_EXT];	/* Name of file */
char	szFileNameKey[_MAX_FNAME + _MAX_EXT + 1];
char	szKeyList[MAX_LIST_SIZE];	/* List of coordinates for file */
char	szNameList[2 * MAX_LIST_SIZE];	/* List of file handeled */

BOOL	bSaveFlag;
BOOL	bInitialMSDOS;

char	szClass[] =	"DeskSave";	/* Our window class name */
char	szTitle[] =	"DeskSave";	/* Our window title */
char	szSaveDesk[] =	"Save &Desk";
char	szArrDesk[] =	"&Arrange Desk";
char	szClrRun[] =	"C&lear \"Load\" and \"Run\"";
char	szAbout[] =	"A&bout DeskSave...";

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

BOOL		IsRunable( HWND );
BOOL		IsLoadable( HWND );
long FAR PASCAL	DeskSaveWndProc( HWND, unsigned, WORD, LONG );
BOOL		ArrangeWindow( HWND );
int PASCAL	WinMain( HANDLE, HANDLE, LPSTR, int );
BOOL		Initialize( void );
BOOL FAR PASCAL	AboutBox( HWND, unsigned, WORD, LONG );

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/*  DeskSave's window function.
 */

long FAR PASCAL DeskSaveWndProc( hWnd, wMsg, wParam, lParam )
HWND	hWnd;		    /* Window handle */
unsigned wMsg;		    /* Message number */
WORD	wParam; 	    /* Word parameter for the message */
LONG	lParam; 	    /* Long parameter for the message */
{
	FARPROC	lpProc; 	    /* ProcInstance for AboutBox */

	switch( wMsg ) {

	/* Destroy-window message - time to quit the application */
	case WM_DESTROY:
		PostQuitMessage( 0 );
		return(0L);

	/* Query open icon message - don't allow icon to be opened! */
	case WM_QUERYOPEN:
		return(0L);

	/* System menu command message - process the command if ours */
	case WM_SYSCOMMAND:
		switch( wParam ) {
		case CMD_SAVEDESK:
			SaveDesk();
			return(0L);

		case CMD_ARRDESK:
			ArrangeDesk();
			return(0L);

		case CMD_CLEAR:
			WriteProfileString((LPSTR)"windows",(LPSTR)"Run",
					(LPSTR)"" );
			WriteProfileString((LPSTR)"windows",(LPSTR)"Load",
					(LPSTR)"" );
			return(0L);

		case CMD_ABOUT:
			lpProc = MakeProcInstance( (FARPROC)AboutBox, hInstance );
			if( ! lpProc )
				return(0L);
			DialogBox( hInstance, MAKEINTRESOURCE(ABOUTBOX),
				hWnd, lpProc );
			FreeProcInstance( lpProc );
			return(0L);
		}
		break;
	}

	/* For all other messages, we pass them on to DefWindowProc */
	return( DefWindowProc( hWnd, wMsg, wParam, lParam ));
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
BOOL FAR PASCAL EnumWndProc( hWnd, lParam )
HWND hWnd;
LONG lParam;
{
	MSG	Msg;

	if ( bSaveFlag ) {
		SaveWindow( hWnd );
	} else {
		ArrangeWindow( hWnd );
	}

#ifdef DESKLOAD
	/* Yield control to other applications !!!
	 * It slows this applications down so that MS-WINDOWS
	 * initial MSDOS.EXE is shown up and DeskLoad can destroy it !
	 */
	PeekMessage( (LPMSG)&Msg, hWndDeskSave, 0, 0, PM_NOREMOVE );
#endif
	return(TRUE);
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


ArrangeDesk()
{
	strcpy( szNameList, "" );
	bInitialMSDOS = FALSE;
	bSaveFlag = FALSE;
	EnumWindows( lpEnumWndProc, 0L );
}

ArrangeWindow( hWnd )
HWND hWnd;
{
	RECT	r;
	char	s[40], *sp;
	int	i, nc;

	if ( !IsRunable( hWnd ) && !IsIconic( hWnd )) return(FALSE);

	GetFileName( hWnd );

	/* Check DESKSAVE and WIN*.OVL */
	switch ( NameOK() ) {
	case 0:		/* OK */
		break;
	case 1:		/* DESKSAVE */
		return(1);
	case 2:		/* WIN*.OVL == MSDOS.EXE */
		strcpy( szFileName, "MSDOS.EXE" );

#ifdef DESKLOAD
		/* Is this first iconic MSDOS.EXE ? If so, kill that window */
		if ( !bInitialMSDOS && IsIconic( hWnd ) ) {
			bInitialMSDOS = TRUE;
			PostMessage( hWnd, WM_SYSCOMMAND, SC_CLOSE, 0L );
			return(1);
		}
#endif
		break;
	}

	/* Keyname for iconic applications begins with 'i' */
	if ( IsIconic( hWnd ) ) {
		strcpy( szFileNameKey, "i" );
		strcat( szFileNameKey, szFileName );
	} else {
		strcpy( szFileNameKey, szFileName );
	}

	if ( GetProfileString( (LPSTR)szTitle, (LPSTR)szFileNameKey,
			(LPSTR)"", (LPSTR)szKeyList, MAX_LIST_SIZE ) == 0 )
		return(NULL);

	/* Has this file been placed before ? */
	nc = GetNameCount( szFileNameKey );
	sp = szKeyList;
	for( i=0 ; i < nc; i++ ) {	/* Search right coordinates */
		char *sp2;
		if ((sp2=strchr( sp, ';' )) == NULL) return(NULL);
		sp = ++sp2;
	}

	if (sscanf( sp, "%d %d %d %d",	&r.left, &r.top,
					&r.right, &r.bottom ) != 4)
		return(NULL);

	/* Name and coordinates found. Add name to list */
	strcat( szNameList, szFileNameKey );
	strcat( szNameList, " " );

	/* Move window to its position */
	SetWindowPos( hWnd, (HWND)NULL,
			r.left, r.top,
			r.right - r.left,
			r.bottom - r.top,
			SWP_NOACTIVATE | SWP_NOZORDER );
	return(NULL);
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/*  These functions save the positions of the windows.
 */

SaveDesk()
{
	HWND	hWnd = NULL;

	strcpy( szLoadList, "" );
	strcpy( szRunList, "" );

	bSaveFlag = TRUE;
	EnumWindows( lpEnumWndProc, 0L );

	/* Add loader program to the last of the LOADLIST !*/
	strcat( szLoadList, "DESKLOAD.EXE" );

	WriteProfileString((LPSTR)"windows",(LPSTR)"Run", (LPSTR)szRunList );
	WriteProfileString((LPSTR)"windows",(LPSTR)"Load",(LPSTR)szLoadList );
}

SaveWindow( hWnd )
HWND hWnd;
{
	if( IsRunable( hWnd ) ) {
		AddRunList( hWnd );
		return(NULL);
	}

	if( IsLoadable( hWnd ) ) {
		AddLoadList( hWnd );
		return(NULL);
	}
}

AddRunList( hWnd )
HWND hWnd;
{
	RECT	rc;
	char	s[40];

	GetFileName( hWnd );

	/* Check DESKSAVE and WIN*.OVL */
	switch ( NameOK() ) {
	case 0:		/* OK */
		break;
	case 1:		/* DESKSAVE */
		return(NULL);
	case 2:		/* WIN*.OVL == MSDOS.EXE */
		strcpy( szFileName, "MSDOS.EXE" );
		break;
	}

	GetWindowRect( hWnd, (LPRECT)&rc );
	if (strstr( szRunList, szFileName ) != NULL) {
		if ( GetProfileString( (LPSTR)szTitle, (LPSTR)szFileName,
				(LPSTR)"", (LPSTR)szKeyList,
				MAX_LIST_SIZE - 30 ) == 0 ) {
			return(FALSE);
		}
		sprintf( s, ";%d %d %d %d",
			rc.left, rc.top, rc.right, rc.bottom);
		strcat( szKeyList, s );
	} else {
		sprintf( szKeyList, "%d %d %d %d",
			rc.left, rc.top, rc.right, rc.bottom);
	}

	WriteProfileString( (LPSTR)szTitle, (LPSTR)szFileName, (LPSTR)szKeyList );

	strcat( szRunList, szFileName );
	strcat( szRunList, " " );
}

AddLoadList( hWnd )
HWND hWnd;
{
	RECT	rc;
	char	s[40];

	GetFileName( hWnd );
	strcpy( szFileNameKey, "i" );
	strcat( szFileNameKey, szFileName );

	/* Check DESKSAVE and WIN*.OVL */
	switch ( NameOK() ) {
	case 0:		/* OK */
		break;
	case 1:		/* DESKSAVE */
		return(NULL);
	case 2:		/* WIN*.OVL == MSDOS.EXE */
		strcpy( szFileNameKey, "iMSDOS.EXE" );
		strcpy( szFileName, "MSDOS.EXE" );
		break;
	}

	GetWindowRect( hWnd, (LPRECT)&rc );
	if (strstr( szLoadList, szFileName ) != NULL) {
		if ( GetProfileString( (LPSTR)szTitle, (LPSTR)szFileNameKey,
				(LPSTR)"", (LPSTR)szKeyList,
				MAX_LIST_SIZE - 30 ) == 0 ) {
			return(FALSE);
		}
		sprintf( s, ";%d %d %d %d",
			rc.left, rc.top, rc.right, rc.bottom);
		strcat( szKeyList, s );
	} else {
		sprintf( szKeyList, "%d %d %d %d",
			rc.left, rc.top, rc.right, rc.bottom);
	}

	WriteProfileString( (LPSTR)szTitle, (LPSTR)szFileNameKey,
			(LPSTR)szKeyList );

	strcat( szLoadList, szFileName );
	strcat( szLoadList, " " );
}


/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/*  Tells whether a window can be run, returns TRUE if so.
 *  We will run only top level, resizable windows that are not
 *  minimized and not maximized.
 */

BOOL IsRunable( hWnd )
HWND hWnd;
{
	DWORD	dwStyle;

	dwStyle = GetWindowLong( hWnd, GWL_STYLE );
	return(
		! ( dwStyle & ( WS_POPUP | WS_MINIMIZE | WS_MAXIMIZE ) )  &&
		  ( dwStyle & WS_VISIBLE )
	);
}

/*  Tells whether a window can be load, returns TRUE if so.
 *  We will load only minimized windows.
 */

BOOL IsLoadable( hWnd )
HWND hWnd;
{
	DWORD	dwStyle;

	dwStyle = GetWindowLong( hWnd, GWL_STYLE );
	return(	( dwStyle & WS_MINIMIZE )  &&
		( dwStyle & WS_VISIBLE ) );
}

GetFileName( hWnd )
HWND hWnd;
{
	HANDLE	hInst;
	char	s[_MAX_PATH];
	static	char	drive[_MAX_DRIVE],
			dir[_MAX_DIR],
			fname[_MAX_FNAME],
			ext[_MAX_EXT];

	hInst = GetWindowWord( hWnd, GWW_HINSTANCE );
	GetModuleFileName( hInst, (LPSTR)s, _MAX_PATH );
	_splitpath( s, drive, dir, fname, ext );
	strcpy( szFileName, fname );
	strcat( szFileName, ext );
}

NameOK()
{
	/* Don't save DESKSAVE to list */
	if ( strnicmp( szFileName, szTitle, strlen(szTitle) ) == 0 )
		return 1;

	/* Don't save WIN*.OVL */
	if ( strnicmp( szFileName, "WIN", 3 ) == 0 &&
	     strnicmp( &szFileName[ strlen(szFileName) - 3], "OVL", 3) == 0 )
		return 2;

	return 0;
}


/* Count occurrences of string s in szNamelist */
GetNameCount( s )
char *s;
{
	int i;
	char *spNames, *sp;

	i = 0;
	spNames = szNameList;
	while( (sp=strstr( spNames, s )) != NULL ) {
		spNames = ( sp + strlen( s ) );
		i++;
	}
	return i;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/*  Application main program.
 */

int PASCAL WinMain( hInst, hPrevInst, lpszCmdLine, nCmdShow )
HANDLE	hInst;		    /* Our instance handle */
HANDLE	hPrevInst;	    /* Previous instance of this application */
LPSTR	lpszCmdLine;	    /* Pointer to any command line params */
int 	nCmdShow;	    /* Parameter to use for first ShowWindow */
{
	MSG 	msg;		    /* Message structure */

	/* Allow only a single instance */
	if( hPrevInst ) {
		MessageBeep( 0 );
		return 0;
	}

	/* Save our instance handle in static variable */
	hInstance = hInst;

	/* Initialize application, quit if any errors */
	if( ! Initialize() )
		return FALSE;

#ifdef DESKLOAD
	ArrangeDesk();
	return 0;
#else
	/* If parameter "-l" arrange desk */
	if (lpszCmdLine[0] == '-' && lpszCmdLine[1] == 'l') {
		ArrangeDesk();
	}

	/* Main message processing loop */
	while( GetMessage( &msg, NULL, 0, 0 ) ) {
		TranslateMessage( &msg );
		DispatchMessage( &msg );
	}

	return msg.wParam;
#endif
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */

/*  Initialize DESKSAVE.  Assumes a single instance.
 *  Returns TRUE if initialization succeeded, FALSE if failed.
 */

BOOL Initialize()
{
	WNDCLASS Class;		    /* Class structure for RegisterClass */
	HMENU	hMenu;		    /* Menu handle of system menu */

	/* Register our window class */
	Class.style		= 0;
	Class.cbClsExtra	= 0;
	Class.cbWndExtra	= 0;
	Class.lpfnWndProc	= DeskSaveWndProc;
	Class.hInstance		= hInstance;
	Class.hIcon 		= LoadIcon( hInstance, szClass );
	Class.hCursor		= LoadCursor( NULL, IDC_ARROW );
	Class.hbrBackground	= COLOR_WINDOW + 1;
	Class.lpszMenuName	= NULL;
	Class.lpszClassName	= szClass;

	if( ! RegisterClass( &Class ) )
		return FALSE;

	/* Create our window but don't iconize it yet */
	hWndDeskSave = CreateWindow(
			szClass, szTitle,
			WS_OVERLAPPED | WS_SYSMENU,
			CW_USEDEFAULT, 0,
			CW_USEDEFAULT, 0,
			NULL, NULL, hInstance, NULL );
	if( ! hWndDeskSave )
		return FALSE;

	lpEnumWndProc = MakeProcInstance( (FARPROC)EnumWndProc, hInstance );

#ifndef DESKLOAD
	/* Add our menu items to the System (Control) menu, at the top of
	 * the menu, so "Tile Columns" becomes the default choice */
	hMenu = GetSystemMenu( hWndDeskSave, FALSE );
	ChangeMenu( hMenu, 0, NULL, MAXINT, MF_APPEND | MF_SEPARATOR );
	ChangeMenu( hMenu, 0, szSaveDesk, CMD_SAVEDESK, MF_APPEND );
	ChangeMenu( hMenu, 0, szArrDesk, CMD_ARRDESK, MF_APPEND );
	ChangeMenu( hMenu, 0, szClrRun, CMD_CLEAR, MF_APPEND );
	ChangeMenu( hMenu, 0, NULL, MAXINT, MF_APPEND | MF_SEPARATOR );
	ChangeMenu( hMenu, 0, szAbout, CMD_ABOUT, MF_APPEND );

	/* Now display our window as an icon */
	ShowWindow( hWndDeskSave, SW_SHOWMINIMIZED );
#endif
	return TRUE;
}

/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*  Dialog function for the About box.
 *  Since this is a simple box with only one button, WM_COMMAND is assumed
 *  to be a click on that button (the command number is not checked).
 */

BOOL FAR PASCAL AboutBox( hDlg, wMsg, wParam, lParam )
HWND	hDlg;			/* Window handle */
unsigned	wMsg;		/* Message number */
WORD	wParam; 		/* Word parameter for the message */
LONG	lParam; 		/* Long parameter for the message */
{
	switch( wMsg ) {
	case WM_COMMAND:
		EndDialog( hDlg, TRUE );
		return TRUE;
	}
	return FALSE;
}
