2 * Openwide -- control Windows common dialog
4 * Copyright (c) 2000 Luke Hudson
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.
36 #include "openwidedll.h"
37 #include "openwideres.h"
39 #include "owSharedUtil.h"
40 #include "openwide_proto.h"
43 // Command identifiers for the trayicon popup menu.
44 enum TrayCommands
{ IDM_ABOUT
= 100, IDM_SETTINGS
, IDM_QUIT
};
46 // Window handles for the various windows
47 HWND ghwMain
= NULL
, ghwPropSheet
= NULL
;
49 // Global instance handle
50 HINSTANCE ghInstance
= NULL
;
52 // Icon handles; these icons are used in a few places
53 HICON ghIconLG
= NULL
, ghIconSm
= NULL
;
55 // This is a pointer shared data structure, containing all the useful info
56 POWSharedData gPowData
= NULL
;
58 // Handle to our shared memory area -- access to this is controlled by a mutex
59 HANDLE ghSharedMem
= NULL
;
61 // Mutex handle -- used to limit access to the shared memory area.
62 HANDLE ghMutex
= NULL
;
64 // Message id, from RegisterWindowMessage(...), which is used to notify closure of : TODO: find out!
65 UINT giCloseMessage
= 0;
70 * Initialise the shared memory area
72 * initShareMem( mainWindowHandle );
74 * @returns 1 on success, 0 on failure
76 int initSharedMem(HWND hwnd
)
78 int rVal
= 0; // return value
81 ghMutex
= CreateMutex(NULL
, TRUE
, OW_MUTEX_NAME
);
84 /* else if( GetLastError() == ERROR_ALREADY_EXISTS)
91 POWSharedData pRData
= NULL
; // Pointer to registry data
93 // try to read in saved settings from registry
94 HKEY hk
= regCreateKey(HKEY_CURRENT_USER
, OW_REGKEY_NAME
);
98 DWORD dwSize
= regReadDWORD(hk
, NULL
, &bOK
);
99 if( bOK
&& dwSize
== sizeof(OWSharedData
) )
100 pRData
= (POWSharedData
)regReadBinaryData(hk
, "OWData");
104 // Create a file mapping object against the system virtual memory.
105 // This will be used to share data between the application and instances of the dll.
106 ghSharedMem
= CreateFileMapping(INVALID_HANDLE_VALUE
, NULL
, PAGE_READWRITE
, 0,
107 sizeof(OWSharedData
), OW_SHARED_FILE_MAPPING
);
110 // map a view of the 'file'
111 gPowData
= (POWSharedData
)MapViewOfFile(ghSharedMem
, FILE_MAP_WRITE
, 0,0,0);
114 // clear the shared mem
115 ZeroMemory(gPowData
, sizeof(OWSharedData
));
116 // If we succeeded in loading saved data from the registry,
117 // then copy it to the shared memory area
119 CopyMemory(gPowData
, pRData
, sizeof(OWSharedData
));
121 { // Nothing loaded from registry, so
122 // initialise the memory to some useful default values.
124 // Get screen dimensions
126 w
= GetSystemMetrics(SM_CXSCREEN
);
127 h
= GetSystemMetrics(SM_CYSCREEN
);
129 // Set the Open/Save dialog origin and width from the screen dimensions
130 gPowData
->ptOrg
.x
= w
/2 - w
/4;
131 gPowData
->ptOrg
.y
= (h
- 2*h
/3)/2;
132 gPowData
->szDim
.cx
= w
/2; // Screen width / 2
133 gPowData
->szDim
.cy
= 2*h
/3; // 2/3 of screen height
134 gPowData
->iView
= V_DETAILS
; // Use details view
135 gPowData
->iFocus
= F_DIRLIST
; // Focus directory listing
136 gPowData
->bShowIcon
= 1; // Show the tray icon
138 gPowData
->hwListener
= hwnd
; // Listener window for trayicon events, etc.
139 gPowData
->bDisable
= 0; // Enable the hook!
140 gPowData
->refCount
= 0; // reference counting TODO: add explanation
141 giCloseMessage
= gPowData
->iCloseMsg
142 = RegisterWindowMessage("Lingo.OpenWide.ProcessFinished.Message");
143 rVal
= 1; // Return value to indicate success
145 } // if( ghSharedMem )
147 if( pRData
) // free registry data
150 releaseMutex(); // unlock Mutex, so shared memory area can be accessed.
152 } // END of initSharedMem(...)
157 * Free the shared memory area, once it is no longer in use.
159 void releaseSharedMem(void)
161 if( ghMutex
) // Obtain a lock on the shared memory access mutex
163 DWORD dwRes
= WaitForSingleObject(ghMutex
, INFINITE
);
164 if(dwRes
!= WAIT_OBJECT_0
)
166 // dbg("DLL: Failed to get ownership of mutex in releaseSharedMem!!!");
171 // dbg("DLL: Releasing file mapping");
176 UnmapViewOfFile(gPowData
);
179 CloseHandle(ghSharedMem
);
182 ReleaseMutex(ghMutex
);
183 } // END of releaseSharedMem()
186 int doApply(HWND hwnd)
191 dwRes = WaitForSingleObject(ghMutex, INFINITE);
203 int i = GetDlgItemInt(hwnd, IDE_LEFT, &bOK, TRUE);
205 gPowData->ptOrg.x = i;
206 i = GetDlgItemInt(hwnd, IDE_TOP, &bOK, TRUE);
208 gPowData->ptOrg.y = i;
209 i = GetDlgItemInt(hwnd, IDE_WIDTH, &bOK, TRUE);
211 gPowData->szDim.cx = i;
212 i = GetDlgItemInt(hwnd, IDE_HEIGHT, &bOK, TRUE);
214 gPowData->szDim.cy = i;
215 int idx = SendDlgItemMessage(hwnd, IDCB_FOCUS, CB_GETCURSEL, 0, 0);
218 int f = SendDlgItemMessage(hwnd, IDCB_FOCUS, CB_GETITEMDATA, idx, 0);
219 if( f >= 0 && f < F_MAX )
220 gPowData->iFocus = f;
222 idx = SendDlgItemMessage(hwnd, IDCB_VIEW, CB_GETCURSEL, 0, 0);
225 int v = SendDlgItemMessage(hwnd, IDCB_VIEW, CB_GETITEMDATA, idx, 0);
226 if( v >= 0 && v < V_MAX )
229 gPowData->bStartMin = IsDlgButtonChecked(hwnd, IDC_STARTMIN) == BST_CHECKED;
230 BOOL bWinStart = IsDlgButtonChecked(hwnd, IDC_WSTARTUP) == BST_CHECKED;
232 HKEY hk = regCreateKey(HKEY_CURRENT_USER, OW_REGKEY_NAME);
235 regWriteDWORD(hk, NULL, sizeof(OWSharedData));
236 regWriteBinaryData(hk, "OWData", (byte *)gPowData, sizeof(OWSharedData));
241 ReleaseMutex(ghMutex);
243 setStartupApp(hwnd, bWinStart);
249 * Determines if this application has a shortcut in the 'Start Menu->Startup' directory.
251 BOOL
isStartupApp(HWND hwnd
)
253 static char szBuf
[MAX_PATH
+1]; // path buffer
255 // Retrieve path to windows Startup folder
256 HRESULT hRes
= SHGetFolderPath(hwnd
, CSIDL_STARTUP
, NULL
, SHGFP_TYPE_CURRENT
, szBuf
);
259 PathAppend(szBuf
, "OpenWide.lnk"); // Append the name of the link :: TODO - this should be cleverer, perhaps.
260 return fileExists(szBuf
); // Check the shortcut exists :: TODO: Check if it points to the right place, too?
266 * Makes this program start with Windows, by creating a shortcut in the Windows Startup folder
268 * setStartupApp( parentWindowOfErrors, isStartupApp )
270 * @param isStartupApp -- set to 1 to create the shortcut, 0 to remove it.
271 * @returns 1 on success, 0 on failure
273 int setStartupApp(HWND hwnd
, BOOL bSet
)
275 char szBuf
[MAX_PATH
+1], szModule
[MAX_PATH
+1]; // some path buffers
277 // Empty the string buffers
278 ZeroMemory(szBuf
, sizeof(szBuf
)/sizeof(char));
279 ZeroMemory(szModule
, sizeof(szBuf
)/sizeof(char));
281 // Retrieve the path to this executable
282 if( !GetModuleFileName(NULL
, szModule
, MAX_PATH
) )
285 // Get the windows startup folder
286 HRESULT hRes
= SHGetFolderPath(hwnd
, CSIDL_STARTUP
, NULL
, SHGFP_TYPE_CURRENT
, szBuf
);
287 if( hRes
!= S_OK
|| !PathAppend(szBuf
, "OpenWide.lnk")) // Append link path to startup folder path as side-effect
289 if( bSet
) // Create the shortcut?
291 if( fileExists(szBuf
) )
292 hRes
= delFile(hwnd
, szBuf
); // Delete existing if found
293 hRes
= CreateLink(szModule
, szBuf
, "OpenWide - control Open&Save dialogs..."); // Create new shortcut
296 else // Delete the shortcut
298 return delFile(hwnd
, szBuf
);
300 } // END of setStartupApp(...)
304 * Create an event-listener window, which will be used for communication between the
305 * app and DLL instances ??? TODO:: Check this!!
307 * NOTE: Looks like this is badly named, doesn't really do SFA with the listener win!!! @see createListenerWindow()
308 * @param hwnd -- parent window?
310 int initListener(HWND hwnd
)
312 if(!initSharedMem(hwnd
))
313 return 0; // Make sure the shared memory area is set up.
315 BOOL bMinimize
= FALSE
, bIcon
= TRUE
;
317 // wait for then lock shared data
318 if( !waitForMutex() )
321 bMinimize
= gPowData
->bStartMin
; // get the preferences from the shared memory
322 bIcon
= gPowData
->bShowIcon
;
324 /// released shared data
330 // Show property sheet only if the preferences specify it.
334 ghwPropSheet
= showDlg(hwnd
);
336 // Create the main event hook
339 Warn( "Failed to set hook: %s", geterrmsg() );
343 } // END of initListener(...)
347 * Create, and show the popup menu for the trayicon
349 * @param hwnd -- window to receive WM_COMMAND messages.
351 void doTrayMenu(HWND hwnd
)
353 if( ghwPropSheet
) // Show the property sheet window
354 SetForegroundWindow(ghwPropSheet
);
356 // Create the popup menu structure
357 HMENU hm
= CreatePopupMenu();
358 AppendMenu(hm
, MF_STRING
, IDM_ABOUT
, "&About OpenWide...");
359 AppendMenu(hm
, MF_GRAYED
| MF_SEPARATOR
, 0, NULL
);
360 AppendMenu(hm
, MF_STRING
| (ghwPropSheet
? MF_GRAYED
: 0), IDM_SETTINGS
, "&Settings...");
361 AppendMenu(hm
, MF_STRING
| (ghwPropSheet
? MF_GRAYED
: 0), IDM_QUIT
, "&Quit");
363 SetMenuDefaultItem(hm
, IDM_SETTINGS
, FALSE
);
366 GetCursorPos(&pt
); // Find mouse location
369 TrackPopupMenu(hm
, TPM_RIGHTBUTTON
| TPM_BOTTOMALIGN
| TPM_RIGHTALIGN
,
370 pt
.x
+8, pt
.y
+8, 0, hwnd
, NULL
);
372 // Delete the menu structure again.
374 } // END of doTrayMenu(...)
377 * Display the main property sheet dialog
379 * @param hwnd -- parent window
381 void showSettingsDlg(HWND hwnd
)
383 if( ghwPropSheet
) // un-hide it if it exists already.
385 ShowWindow(ghwPropSheet
, SW_SHOWNORMAL
);
386 SetForegroundWindow(ghwPropSheet
);
388 else // create and show the dialog if it doesn't already exist
389 ghwPropSheet
= showDlg(hwnd
);
390 } // END of showSettingsDlg(...)
394 * Quit the application, running cleanup tasks first.
396 * This is a high-level function which makes up part of the 'public' API of the program,
397 * at least, as far as it has such an clear designed concept :(
399 * TODO: Make sure the above statement is actually correct, and sort the functions into these sorts of categories somewhere.
402 if( ghwPropSheet
) // don't quit with the settings dialog open
404 SetForegroundWindow(ghwPropSheet
);
405 Warn("Please close the settings dialog box before trying to quit");
411 gPowData
->bDisable
= TRUE
; // Tell hook not to subclass any more windows
413 if(gPowData
->refCount
== 0) // if there are no more subclassed windows
415 PostQuitMessage(0); // quit program
419 remTrayIcon(ghwMain
);
424 * Listener window callback
426 LRESULT WINAPI CALLBACK
wpListener(HWND hwnd
, UINT msg
, WPARAM wp
, LPARAM lp
)
428 static UINT uTaskbarMsg
=0;
432 if(!initListener(hwnd
))
434 Warn("OpenWide failed to initialize properly. Please report this bug:\r\nErr msg: %s", geterrmsg());
437 uTaskbarMsg
= RegisterWindowMessage("TaskbarCreated"); // TODO:: WHAT IS THIS?
440 // Handle trayicon menu commands
445 MessageBox(hwnd
, "OpenWide is written by Luke Hudson. (c)2005-2011\r\nSee http://speak.geek.nz", "About OpenWide", MB_OK
);
448 showSettingsDlg(hwnd
);
456 // Handle trayicon interaction events
463 case WM_LBUTTONDBLCLK
:
464 showSettingsDlg(hwnd
);
467 /* ShowWindow(hwnd, SW_SHOWNORMAL);
468 EnableWindow(hwnd, TRUE);
475 // dbg("OWApp: listener destroying");
480 if( msg
== giCloseMessage
)
482 // dbg("OWApp: Received dll close msg");
485 // dbg("OWApp: refCount is %d", gPowData->refCount);
486 if( gPowData
->refCount
== 0 && gPowData
->bDisable
)
491 else if( msg
== uTaskbarMsg
) // taskbar re-created, re-add icon if option is enabled.
493 if( !waitForMutex() )
495 BOOL bIcon
= gPowData
->bShowIcon
;
498 if(bIcon
) addTrayIcon(hwnd
);
500 return DefWindowProc(hwnd
, msg
, wp
, lp
);
507 * Callback for the placement window. This is the proxy window which can be
508 * resized and moved to set the desired Open/Save dialog dimensions.
510 BOOL WINAPI CALLBACK
wpPlacement(HWND hwnd
, UINT msg
, WPARAM wp
, LPARAM lp
)
512 static LPRECT pr
= NULL
;
513 static BOOL bXP
; // Are we using WinXP ? -- Changes the minimum dialog dimensions.
521 MoveWindow(hwnd
, pr
->left
, pr
->top
, pr
->right
, pr
->bottom
, FALSE
);
528 GetWindowRect(hwnd
, pr
);
537 // Used to limit the minimum dimensions while resizing
538 case WM_GETMINMAXINFO
:
540 MINMAXINFO
* pmm
= (MINMAXINFO
*)lp
;
545 pmm
->ptMinTrackSize
.x
= OW_XP_MINWIDTH
;
546 pmm
->ptMinTrackSize
.y
= OW_XP_MINHEIGHT
;
550 pmm
->ptMinTrackSize
.x
= OW_2K_MINWIDTH
;
551 pmm
->ptMinTrackSize
.y
= OW_2K_MINHEIGHT
;
566 /* Create our trayicon */
567 int addTrayIcon(HWND hwnd
)
569 return Add_TrayIcon( ghIconSm
, "OpenWide\r\nRight-Click for menu...", hwnd
, WM_TRAYICON
, 0);
572 /* Remove the trayicon! */
573 void remTrayIcon(HWND hwnd
)
575 Rem_TrayIcon( hwnd
, WM_TRAYICON
, 0 );
579 /* Creates the listener window */
580 HWND
createListenerWindow(void)
582 static BOOL bRegd
= FALSE
;
586 ZeroMemory(&wc
, sizeof(wc
));
587 wc
.cbSize
= sizeof(wc
);
588 wc
.hInstance
= ghInstance
;
589 wc
.lpfnWndProc
= wpListener
;
590 wc
.lpszClassName
= "Lingo.OpenWide.Listener";
592 wc
.hIconSm
= ghIconSm
;
593 bRegd
= (RegisterClassEx(&wc
) != 0);
595 return CreateWindowEx( WS_EX_NOACTIVATE
, "Lingo.OpenWide.Listener", "Lingo.OpenWide",
596 WS_POPUP
, 0,0, 1,1, NULL
, NULL
, ghInstance
, NULL
);
599 // TODO: Remove this useless function!
602 ghwMain
= createListenerWindow();
606 // TODO: why the new naming convention ?
609 HRESULT hRes
= CoInitialize(NULL
); // Initialise COM -- for some reason I forget, we need to do this.
610 if( hRes
!= S_OK
&& hRes
!= S_FALSE
)
612 // Load the icons from the .exe resource section
613 ghIconSm
= (HICON
)LoadImage(ghInstance
, MAKEINTRESOURCE(IDI_TRAY
), IMAGE_ICON
, 16,16, LR_SHARED
| LR_VGACOLOR
);
614 ghIconLG
= (HICON
)LoadIcon(ghInstance
, MAKEINTRESOURCE(IDI_TRAY
));
621 void ow_shutdown(void)
625 DestroyWindow(ghwMain
);
632 int WINAPI
WinMain(HINSTANCE hi
, HINSTANCE hiPrv
, LPSTR fakeCmdLine
, int iShow
)
634 // This Mutex is used to ensure that only one instance of this app can be running at a time.
635 HANDLE hMutex
= NULL
;
636 hMutex
= CreateMutex(NULL
, FALSE
, "OpenWide_App_Mutex");
637 if( hMutex
&& GetLastError()==ERROR_ALREADY_EXISTS
)
639 // This app is already loaded, so find its window and send it a message.
641 HWND hw
= FindWindowEx(NULL
, NULL
, "Lingo.OpenWide.Listener", "Lingo.OpenWide");
642 if(hw
) // Fake a double click on the trayicon -- to show the window.
643 SendMessage(hw
, WM_TRAYICON
, 0, WM_LBUTTONDBLCLK
);
644 return 0; // Exit this instance of the app.
657 while( (ret
= GetMessage(&msg
, NULL
, 0,0) ) != 0 )
661 if( !PropSheet_IsDialogMessage(ghwPropSheet
, &msg
) ) //IsDialogMessage(ghwMain, &msg) )
663 TranslateMessage(&msg
);
664 DispatchMessage(&msg
);
666 if( !PropSheet_GetCurrentPageHwnd(ghwPropSheet
) )
668 DestroyWindow(ghwPropSheet
);
669 ghwPropSheet
=(void *)NULL
;
672 // dbg("OWApp: Shutting down");