Release/readme.txt updated
[openwide.git] / openwide.c
blob541fff305794c46430b8e54f3ee0de134a98d5be
1 /*
2 * Openwide -- control Windows common dialog
3 *
4 * Copyright (c) 2000 Luke Hudson
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 #include <windows.h>
24 #include <objbase.h>
25 #include <shobjidl.h>
26 #include <shlguid.h>
27 #include <shlobj.h>
28 #include <shlwapi.h>
29 #include <shellapi.h>
30 #include <stdio.h>
31 #include "openwidedll.h"
32 #include "openwideres.h"
33 #include "owutil.h"
34 #include "owSharedUtil.h"
35 #include "openwide_proto.h"
38 // Command identifiers for the trayicon popup menu.
39 enum TrayCommands { IDM_ABOUT = 100, IDM_SETTINGS, IDM_QUIT };
41 // Window handles for the various windows
42 HWND ghwMain = NULL, ghwPropSheet = NULL;
44 // Global instance handle
45 HINSTANCE ghInstance = NULL;
47 // Icon handles; these icons are used in a few places
48 HICON ghIconLG = NULL, ghIconSm = NULL;
50 // This is a pointer shared data structure, containing all the useful info
51 POWSharedData gPowData = NULL;
53 // Handle to our shared memory area -- access to this is controlled by a mutex
54 HANDLE ghSharedMem = NULL;
56 // Mutex handle -- used to limit access to the shared memory area.
57 HANDLE ghMutex = NULL;
59 // Message id, from RegisterWindowMessage(...), which is used to notify closure of : TODO: find out!
60 UINT giCloseMessage = 0;
65 * Initialise the shared memory area
67 * initShareMem( mainWindowHandle );
69 * @returns 1 on success, 0 on failure
71 int initSharedMem(HWND hwnd)
73 int rVal = 0; // return value
75 // Create the mutex
76 ghMutex = CreateMutex(NULL, TRUE, OW_MUTEX_NAME);
77 if( !ghMutex )
78 return 0;
79 /* else if( GetLastError() == ERROR_ALREADY_EXISTS)
81 CloseHandle(ghMutex);
82 ghMutex = NULL;
83 return 0;
84 }*/
86 POWSharedData pRData = NULL; // Pointer to registry data
88 // try to read in saved settings from registry
89 HKEY hk = regCreateKey(HKEY_CURRENT_USER, OW_REGKEY_NAME);
90 if( hk )
92 BOOL bOK;
93 DWORD dwSize = regReadDWORD(hk, NULL, &bOK);
94 if( bOK && dwSize == sizeof(OWSharedData) )
95 pRData = (POWSharedData)regReadBinaryData(hk, "OWData");
96 regCloseKey(hk);
99 // Create a file mapping object against the system virtual memory.
100 // This will be used to share data between the application and instances of the dll.
101 ghSharedMem = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0,
102 sizeof(OWSharedData), OW_SHARED_FILE_MAPPING);
103 if(ghSharedMem)
105 // map a view of the 'file'
106 gPowData = (POWSharedData)MapViewOfFile(ghSharedMem, FILE_MAP_WRITE, 0,0,0);
107 if( gPowData )
109 // clear the shared mem
110 ZeroMemory(gPowData, sizeof(OWSharedData));
111 // If we succeeded in loading saved data from the registry,
112 // then copy it to the shared memory area
113 if( pRData )
114 CopyMemory(gPowData, pRData, sizeof(OWSharedData));
115 else
116 { // Nothing loaded from registry, so
117 // initialise the memory to some useful default values.
119 // Get screen dimensions
120 int w,h;
121 w = GetSystemMetrics(SM_CXSCREEN);
122 h = GetSystemMetrics(SM_CYSCREEN);
124 // Set the Open/Save dialog origin and width from the screen dimensions
125 gPowData->ptOrg.x = w/2 - w/4;
126 gPowData->ptOrg.y = (h - 2*h/3)/2;
127 gPowData->szDim.cx = w/2; // Screen width / 2
128 gPowData->szDim.cy = 2*h/3; // 2/3 of screen height
129 gPowData->iView = V_DETAILS; // Use details view
130 gPowData->iFocus = F_DIRLIST; // Focus directory listing
131 gPowData->bShowIcon = 1; // Show the tray icon
133 gPowData->hwListener = hwnd; // Listener window for trayicon events, etc.
134 gPowData->bDisable = 0; // Enable the hook!
135 gPowData->refCount = 0; // reference counting TODO: add explanation
136 giCloseMessage = gPowData->iCloseMsg
137 = RegisterWindowMessage("Lingo.OpenWide.ProcessFinished.Message");
138 rVal = 1; // Return value to indicate success
139 } // if( gPowData )
140 } // if( ghSharedMem )
142 if( pRData ) // free registry data
143 free(pRData);
145 releaseMutex(); // unlock Mutex, so shared memory area can be accessed.
146 return rVal;
147 } // END of initSharedMem(...)
152 * Free the shared memory area, once it is no longer in use.
154 void releaseSharedMem(void)
156 if( ghMutex ) // Obtain a lock on the shared memory access mutex
158 DWORD dwRes = WaitForSingleObject(ghMutex, INFINITE);
159 if(dwRes != WAIT_OBJECT_0)
161 // dbg("DLL: Failed to get ownership of mutex in releaseSharedMem!!!");
162 return;
166 // dbg("DLL: Releasing file mapping");
167 if( ghSharedMem )
169 if( gPowData )
171 UnmapViewOfFile(gPowData);
172 gPowData = NULL;
174 CloseHandle(ghSharedMem);
175 ghSharedMem = NULL;
177 ReleaseMutex(ghMutex);
178 } // END of releaseSharedMem()
181 int doApply(HWND hwnd)
183 DWORD dwRes;
184 if( ghMutex )
186 dwRes = WaitForSingleObject(ghMutex, INFINITE);
187 switch(dwRes)
189 case WAIT_OBJECT_0:
190 // okay, continue
191 break;
192 case WAIT_ABANDONED:
193 default:
194 return 0;
197 BOOL bOK;
198 int i = GetDlgItemInt(hwnd, IDE_LEFT, &bOK, TRUE);
199 if( bOK )
200 gPowData->ptOrg.x = i;
201 i = GetDlgItemInt(hwnd, IDE_TOP, &bOK, TRUE);
202 if( bOK )
203 gPowData->ptOrg.y = i;
204 i = GetDlgItemInt(hwnd, IDE_WIDTH, &bOK, TRUE);
205 if( bOK )
206 gPowData->szDim.cx = i;
207 i = GetDlgItemInt(hwnd, IDE_HEIGHT, &bOK, TRUE);
208 if( bOK )
209 gPowData->szDim.cy = i;
210 int idx = SendDlgItemMessage(hwnd, IDCB_FOCUS, CB_GETCURSEL, 0, 0);
211 if( idx != CB_ERR )
213 int f = SendDlgItemMessage(hwnd, IDCB_FOCUS, CB_GETITEMDATA, idx, 0);
214 if( f >= 0 && f < F_MAX )
215 gPowData->iFocus = f;
217 idx = SendDlgItemMessage(hwnd, IDCB_VIEW, CB_GETCURSEL, 0, 0);
218 if( idx != CB_ERR )
220 int v = SendDlgItemMessage(hwnd, IDCB_VIEW, CB_GETITEMDATA, idx, 0);
221 if( v >= 0 && v < V_MAX )
222 gPowData->iView = v;
224 gPowData->bStartMin = IsDlgButtonChecked(hwnd, IDC_STARTMIN) == BST_CHECKED;
225 BOOL bWinStart = IsDlgButtonChecked(hwnd, IDC_WSTARTUP) == BST_CHECKED;
227 HKEY hk = regCreateKey(HKEY_CURRENT_USER, OW_REGKEY_NAME);
228 if( hk )
230 regWriteDWORD(hk, NULL, sizeof(OWSharedData));
231 regWriteBinaryData(hk, "OWData", (byte *)gPowData, sizeof(OWSharedData));
232 regCloseKey(hk);
235 if( ghMutex )
236 ReleaseMutex(ghMutex);
238 setStartupApp(hwnd, bWinStart);
239 return 1;
244 * Determines if this application has a shortcut in the 'Start Menu->Startup' directory.
246 BOOL isStartupApp(HWND hwnd)
248 static char szBuf[MAX_PATH+1]; // path buffer
250 // Retrieve path to windows Startup folder
251 HRESULT hRes = SHGetFolderPath(hwnd, CSIDL_STARTUP, NULL, SHGFP_TYPE_CURRENT, szBuf);
252 if( hRes == S_OK )
254 PathAppend(szBuf, "OpenWide.lnk"); // Append the name of the link :: TODO - this should be cleverer, perhaps.
255 return fileExists(szBuf); // Check the shortcut exists :: TODO: Check if it points to the right place, too?
257 return FALSE;
261 * Makes this program start with Windows, by creating a shortcut in the Windows Startup folder
263 * setStartupApp( parentWindowOfErrors, isStartupApp )
265 * @param isStartupApp -- set to 1 to create the shortcut, 0 to remove it.
266 * @returns 1 on success, 0 on failure
268 int setStartupApp(HWND hwnd, BOOL bSet)
270 char szBuf[MAX_PATH+1], szModule[MAX_PATH+1]; // some path buffers
272 // Empty the string buffers
273 ZeroMemory(szBuf, sizeof(szBuf)/sizeof(char));
274 ZeroMemory(szModule, sizeof(szBuf)/sizeof(char));
276 // Retrieve the path to this executable
277 if( !GetModuleFileName(NULL, szModule, MAX_PATH) )
278 return 0;
280 // Get the windows startup folder
281 HRESULT hRes = SHGetFolderPath(hwnd, CSIDL_STARTUP, NULL, SHGFP_TYPE_CURRENT, szBuf);
282 if( hRes != S_OK || !PathAppend(szBuf, "OpenWide.lnk")) // Append link path to startup folder path as side-effect
283 return 0;
284 if( bSet ) // Create the shortcut?
286 if( fileExists(szBuf) )
287 hRes = delFile(hwnd, szBuf); // Delete existing if found
288 hRes = CreateLink(szModule, szBuf, "OpenWide - control Open&Save dialogs..."); // Create new shortcut
289 return hRes == S_OK;
291 else // Delete the shortcut
293 return delFile(hwnd, szBuf);
295 } // END of setStartupApp(...)
299 * Create an event-listener window, which will be used for communication between the
300 * app and DLL instances ??? TODO:: Check this!!
302 * NOTE: Looks like this is badly named, doesn't really do SFA with the listener win!!! @see createListenerWindow()
303 * @param hwnd -- parent window?
305 int initListener(HWND hwnd)
307 if(!initSharedMem(hwnd))
308 return 0; // Make sure the shared memory area is set up.
310 BOOL bMinimize = FALSE, bIcon = TRUE;
312 // wait for then lock shared data
313 if( !waitForMutex() )
314 return 0;
316 bMinimize = gPowData->bStartMin; // get the preferences from the shared memory
317 bIcon = gPowData->bShowIcon;
319 /// released shared data
320 releaseMutex();
322 if(bIcon)
323 addTrayIcon(hwnd);
325 // Show property sheet only if the preferences specify it.
326 if( bMinimize )
327 ghwPropSheet = NULL;
328 else
329 ghwPropSheet = showDlg(hwnd);
331 // Create the main event hook
332 if( !setHook() )
334 Warn( "Failed to set hook: %s", geterrmsg() );
335 return 0;
337 return 1;
338 } // END of initListener(...)
342 * Create, and show the popup menu for the trayicon
344 * @param hwnd -- window to receive WM_COMMAND messages.
346 void doTrayMenu(HWND hwnd)
348 if( ghwPropSheet ) // Show the property sheet window
349 SetForegroundWindow(ghwPropSheet);
351 // Create the popup menu structure
352 HMENU hm = CreatePopupMenu();
353 AppendMenu(hm, MF_STRING, IDM_ABOUT, "&About OpenWide...");
354 AppendMenu(hm, MF_GRAYED | MF_SEPARATOR, 0, NULL);
355 AppendMenu(hm, MF_STRING | (ghwPropSheet ? MF_GRAYED : 0), IDM_SETTINGS, "&Settings...");
356 AppendMenu(hm, MF_STRING | (ghwPropSheet ? MF_GRAYED : 0), IDM_QUIT, "&Quit");
358 SetMenuDefaultItem(hm, IDM_SETTINGS, FALSE);
360 POINT pt;
361 GetCursorPos(&pt); // Find mouse location
363 // Popup the menu
364 TrackPopupMenu(hm, TPM_RIGHTBUTTON | TPM_BOTTOMALIGN | TPM_RIGHTALIGN,
365 pt.x, pt.y, 0, hwnd, NULL);
367 // Delete the menu structure again.
368 DestroyMenu(hm);
369 } // END of doTrayMenu(...)
372 * Display the main property sheet dialog
374 * @param hwnd -- parent window
376 void showSettingsDlg(HWND hwnd)
378 if( ghwPropSheet ) // un-hide it if it exists already.
380 ShowWindow(ghwPropSheet, SW_SHOWNORMAL);
381 SetForegroundWindow(ghwPropSheet);
383 else // create and show the dialog if it doesn't already exist
384 ghwPropSheet = showDlg(hwnd);
385 } // END of showSettingsDlg(...)
389 * Quit the application, running cleanup tasks first.
391 * This is a high-level function which makes up part of the 'public' API of the program,
392 * at least, as far as it has such an clear designed concept :(
394 * TODO: Make sure the above statement is actually correct, and sort the functions into these sorts of categories somewhere.
396 void doQuit(void)
397 if( ghwPropSheet ) // don't quit with the settings dialog open
399 SetForegroundWindow(ghwPropSheet);
400 Warn("Please close the settings dialog box before trying to quit");
401 return;
403 //rmvHook();
404 if( waitForMutex() )
406 gPowData->bDisable = TRUE; // Tell hook not to subclass any more windows
408 if(gPowData->refCount == 0) // if there are no more subclassed windows
410 PostQuitMessage(0); // quit program
412 releaseMutex();
414 remTrayIcon(ghwMain);
419 * Listener window callback
421 LRESULT WINAPI CALLBACK wpListener(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
423 static UINT uTaskbarMsg=0;
424 switch(msg)
426 case WM_CREATE:
427 if(!initListener(hwnd))
429 Warn("OpenWide failed to initialize properly. Please report this bug:\r\nErr msg: %s", geterrmsg());
430 return -1;
432 uTaskbarMsg = RegisterWindowMessage("TaskbarCreated"); // TODO:: WHAT IS THIS?
433 break;
435 // Handle trayicon menu commands
436 case WM_COMMAND:
437 switch(LOWORD(wp))
439 case IDM_ABOUT:
440 MessageBox(hwnd, "OpenWide is written by Luke Hudson. (c)2005\r\nSee http://lingo.atspace.com", "About OpenWide", MB_OK);
441 break;
442 case IDM_SETTINGS:
443 showSettingsDlg(hwnd);
444 break;
445 case IDM_QUIT:
446 doQuit();
447 break;
449 break;
451 // Handle trayicon interaction events
452 case WM_TRAYICON:
453 switch(lp)
455 case WM_RBUTTONDOWN:
456 doTrayMenu(hwnd);
457 break;
458 case WM_LBUTTONDBLCLK:
459 showSettingsDlg(hwnd);
460 break;
461 case WM_LBUTTONUP:
462 /* ShowWindow(hwnd, SW_SHOWNORMAL);
463 EnableWindow(hwnd, TRUE);
464 remTrayIcon(hwnd);
465 SetFocus(hwnd);*/
466 break;
468 break;
469 case WM_DESTROY:
470 // dbg("OWApp: listener destroying");
471 rmvHook();
472 releaseSharedMem();
473 break;
474 default:
475 if( msg == giCloseMessage )
477 // dbg("OWApp: Received dll close msg");
478 if( waitForMutex() )
480 // dbg("OWApp: refCount is %d", gPowData->refCount);
481 if( gPowData->refCount == 0 && gPowData->bDisable)
482 PostQuitMessage(0);
483 releaseMutex();
486 else if( msg == uTaskbarMsg ) // taskbar re-created, re-add icon if option is enabled.
488 if( !waitForMutex() )
489 return 0;
490 BOOL bIcon = gPowData->bShowIcon;
491 releaseMutex();
493 if(bIcon) addTrayIcon(hwnd);
495 return DefWindowProc(hwnd, msg, wp, lp);
497 return 0;
502 * Callback for the placement window. This is the proxy window which can be
503 * resized and moved to set the desired Open/Save dialog dimensions.
505 BOOL WINAPI CALLBACK wpPlacement(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
507 static LPRECT pr = NULL;
508 static BOOL bXP; // Are we using WinXP ? -- Changes the minimum dialog dimensions.
509 switch(msg)
511 case WM_INITDIALOG:
512 pr = (LPRECT)lp;
513 if(!pr)
514 EndDialog(hwnd, 0);
515 bXP = isWinXP();
516 MoveWindow(hwnd, pr->left, pr->top, pr->right, pr->bottom, FALSE);
517 break;
519 case WM_COMMAND:
520 switch(LOWORD(wp))
522 case IDOK:
523 GetWindowRect(hwnd, pr);
524 EndDialog(hwnd, 1);
525 break;
526 case IDCANCEL:
527 EndDialog(hwnd, 0);
528 break;
530 break;
532 // Used to limit the minimum dimensions while resizing
533 case WM_GETMINMAXINFO:
535 MINMAXINFO * pmm = (MINMAXINFO *)lp;
536 if(!pmm)
537 break;
538 if( bXP )
540 pmm->ptMinTrackSize.x = OW_XP_MINWIDTH;
541 pmm->ptMinTrackSize.y = OW_XP_MINHEIGHT;
543 else
545 pmm->ptMinTrackSize.x = OW_2K_MINWIDTH;
546 pmm->ptMinTrackSize.y = OW_2K_MINHEIGHT;
549 break;
551 case WM_CLOSE:
552 EndDialog(hwnd, 0);
553 break;
554 default:
555 return 0;
557 return 1;
561 /* Create our trayicon */
562 int addTrayIcon(HWND hwnd)
564 return Add_TrayIcon( ghIconSm, "OpenWide\r\nRight-Click for menu...", hwnd, WM_TRAYICON, 0);
567 /* Remove the trayicon! */
568 void remTrayIcon(HWND hwnd)
570 Rem_TrayIcon( hwnd, WM_TRAYICON, 0 );
574 /* Creates the listener window */
575 HWND createListenerWindow(void)
577 static BOOL bRegd = FALSE;
578 if(!bRegd)
580 WNDCLASSEX wc;
581 ZeroMemory(&wc, sizeof(wc));
582 wc.cbSize = sizeof(wc);
583 wc.hInstance = ghInstance;
584 wc.lpfnWndProc = wpListener;
585 wc.lpszClassName = "Lingo.OpenWide.Listener";
586 wc.hIcon = ghIconLG;
587 wc.hIconSm = ghIconSm;
588 bRegd = (RegisterClassEx(&wc) != 0);
590 return CreateWindowEx( WS_EX_NOACTIVATE, "Lingo.OpenWide.Listener", "Lingo.OpenWide",
591 WS_POPUP, 0,0, 1,1, NULL, NULL, ghInstance, NULL);
594 // TODO: Remove this useless function!
595 int createWin(void)
597 ghwMain = createListenerWindow();
598 return 1;
601 // TODO: why the new naming convention ?
602 int ow_init(void)
604 HRESULT hRes = CoInitialize(NULL); // Initialise COM -- for some reason I forget, we need to do this.
605 if( hRes != S_OK && hRes != S_FALSE )
606 return 0;
607 // Load the icons from the .exe resource section
608 ghIconSm = (HICON)LoadImage(ghInstance, MAKEINTRESOURCE(IDI_TRAY), IMAGE_ICON, 16,16, LR_SHARED | LR_VGACOLOR);
609 ghIconLG = (HICON)LoadIcon(ghInstance, MAKEINTRESOURCE(IDI_TRAY));
610 if( !createWin() )
611 return 0;
612 return 1;
616 void ow_shutdown(void)
618 if(ghwMain)
620 DestroyWindow(ghwMain);
621 ghwMain = NULL;
623 CoUninitialize();
627 int WINAPI WinMain(HINSTANCE hi, HINSTANCE hiPrv, LPSTR fakeCmdLine, int iShow)
629 // This Mutex is used to ensure that only one instance of this app can be running at a time.
630 HANDLE hMutex = NULL;
631 hMutex = CreateMutex(NULL, FALSE, "OpenWide_App_Mutex");
632 if( hMutex && GetLastError()==ERROR_ALREADY_EXISTS )
634 // This app is already loaded, so find its window and send it a message.
635 CloseHandle(hMutex);
636 HWND hw = FindWindowEx(NULL, NULL, "Lingo.OpenWide.Listener", "Lingo.OpenWide");
637 if(hw) // Fake a double click on the trayicon -- to show the window.
638 SendMessage(hw, WM_TRAYICON, 0, WM_LBUTTONDBLCLK);
639 return 0; // Exit this instance of the app.
642 ghInstance = hi;
643 if( !ow_init() )
645 ow_shutdown();
646 return 0;
649 MSG msg;
650 int ret;
652 while( (ret = GetMessage(&msg, NULL, 0,0) ) != 0 )
654 if( ret == -1 )
655 break;
656 if( !PropSheet_IsDialogMessage(ghwPropSheet, &msg) ) //IsDialogMessage(ghwMain, &msg) )
658 TranslateMessage(&msg);
659 DispatchMessage(&msg);
661 if( !PropSheet_GetCurrentPageHwnd(ghwPropSheet) )
663 DestroyWindow(ghwPropSheet);
664 ghwPropSheet=NULL;
667 // dbg("OWApp: Shutting down");
668 ow_shutdown();
669 return 0;