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.
31 #include "openwidedll.h"
32 #include "openwideres.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
76 ghMutex
= CreateMutex(NULL
, TRUE
, OW_MUTEX_NAME
);
79 /* else if( GetLastError() == ERROR_ALREADY_EXISTS)
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
);
93 DWORD dwSize
= regReadDWORD(hk
, NULL
, &bOK
);
94 if( bOK
&& dwSize
== sizeof(OWSharedData
) )
95 pRData
= (POWSharedData
)regReadBinaryData(hk
, "OWData");
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
);
105 // map a view of the 'file'
106 gPowData
= (POWSharedData
)MapViewOfFile(ghSharedMem
, FILE_MAP_WRITE
, 0,0,0);
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
114 CopyMemory(gPowData
, pRData
, sizeof(OWSharedData
));
116 { // Nothing loaded from registry, so
117 // initialise the memory to some useful default values.
119 // Get screen dimensions
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
140 } // if( ghSharedMem )
142 if( pRData
) // free registry data
145 releaseMutex(); // unlock Mutex, so shared memory area can be accessed.
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!!!");
166 // dbg("DLL: Releasing file mapping");
171 UnmapViewOfFile(gPowData
);
174 CloseHandle(ghSharedMem
);
177 ReleaseMutex(ghMutex
);
178 } // END of releaseSharedMem()
181 int doApply(HWND hwnd)
186 dwRes = WaitForSingleObject(ghMutex, INFINITE);
198 int i = GetDlgItemInt(hwnd, IDE_LEFT, &bOK, TRUE);
200 gPowData->ptOrg.x = i;
201 i = GetDlgItemInt(hwnd, IDE_TOP, &bOK, TRUE);
203 gPowData->ptOrg.y = i;
204 i = GetDlgItemInt(hwnd, IDE_WIDTH, &bOK, TRUE);
206 gPowData->szDim.cx = i;
207 i = GetDlgItemInt(hwnd, IDE_HEIGHT, &bOK, TRUE);
209 gPowData->szDim.cy = i;
210 int idx = SendDlgItemMessage(hwnd, IDCB_FOCUS, CB_GETCURSEL, 0, 0);
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);
220 int v = SendDlgItemMessage(hwnd, IDCB_VIEW, CB_GETITEMDATA, idx, 0);
221 if( v >= 0 && v < V_MAX )
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);
230 regWriteDWORD(hk, NULL, sizeof(OWSharedData));
231 regWriteBinaryData(hk, "OWData", (byte *)gPowData, sizeof(OWSharedData));
236 ReleaseMutex(ghMutex);
238 setStartupApp(hwnd, bWinStart);
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
);
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?
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
) )
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
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
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() )
316 bMinimize
= gPowData
->bStartMin
; // get the preferences from the shared memory
317 bIcon
= gPowData
->bShowIcon
;
319 /// released shared data
325 // Show property sheet only if the preferences specify it.
329 ghwPropSheet
= showDlg(hwnd
);
331 // Create the main event hook
334 Warn( "Failed to set hook: %s", geterrmsg() );
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
);
361 GetCursorPos(&pt
); // Find mouse location
364 TrackPopupMenu(hm
, TPM_RIGHTBUTTON
| TPM_BOTTOMALIGN
| TPM_RIGHTALIGN
,
365 pt
.x
, pt
.y
, 0, hwnd
, NULL
);
367 // Delete the menu structure again.
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.
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");
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
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;
427 if(!initListener(hwnd
))
429 Warn("OpenWide failed to initialize properly. Please report this bug:\r\nErr msg: %s", geterrmsg());
432 uTaskbarMsg
= RegisterWindowMessage("TaskbarCreated"); // TODO:: WHAT IS THIS?
435 // Handle trayicon menu commands
440 MessageBox(hwnd
, "OpenWide is written by Luke Hudson. (c)2005\r\nSee http://lingo.atspace.com", "About OpenWide", MB_OK
);
443 showSettingsDlg(hwnd
);
451 // Handle trayicon interaction events
458 case WM_LBUTTONDBLCLK
:
459 showSettingsDlg(hwnd
);
462 /* ShowWindow(hwnd, SW_SHOWNORMAL);
463 EnableWindow(hwnd, TRUE);
470 // dbg("OWApp: listener destroying");
475 if( msg
== giCloseMessage
)
477 // dbg("OWApp: Received dll close msg");
480 // dbg("OWApp: refCount is %d", gPowData->refCount);
481 if( gPowData
->refCount
== 0 && gPowData
->bDisable
)
486 else if( msg
== uTaskbarMsg
) // taskbar re-created, re-add icon if option is enabled.
488 if( !waitForMutex() )
490 BOOL bIcon
= gPowData
->bShowIcon
;
493 if(bIcon
) addTrayIcon(hwnd
);
495 return DefWindowProc(hwnd
, msg
, wp
, lp
);
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.
516 MoveWindow(hwnd
, pr
->left
, pr
->top
, pr
->right
, pr
->bottom
, FALSE
);
523 GetWindowRect(hwnd
, pr
);
532 // Used to limit the minimum dimensions while resizing
533 case WM_GETMINMAXINFO
:
535 MINMAXINFO
* pmm
= (MINMAXINFO
*)lp
;
540 pmm
->ptMinTrackSize
.x
= OW_XP_MINWIDTH
;
541 pmm
->ptMinTrackSize
.y
= OW_XP_MINHEIGHT
;
545 pmm
->ptMinTrackSize
.x
= OW_2K_MINWIDTH
;
546 pmm
->ptMinTrackSize
.y
= OW_2K_MINHEIGHT
;
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
;
581 ZeroMemory(&wc
, sizeof(wc
));
582 wc
.cbSize
= sizeof(wc
);
583 wc
.hInstance
= ghInstance
;
584 wc
.lpfnWndProc
= wpListener
;
585 wc
.lpszClassName
= "Lingo.OpenWide.Listener";
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!
597 ghwMain
= createListenerWindow();
601 // TODO: why the new naming convention ?
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
)
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
));
616 void ow_shutdown(void)
620 DestroyWindow(ghwMain
);
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.
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.
652 while( (ret
= GetMessage(&msg
, NULL
, 0,0) ) != 0 )
656 if( !PropSheet_IsDialogMessage(ghwPropSheet
, &msg
) ) //IsDialogMessage(ghwMain, &msg) )
658 TranslateMessage(&msg
);
659 DispatchMessage(&msg
);
661 if( !PropSheet_GetCurrentPageHwnd(ghwPropSheet
) )
663 DestroyWindow(ghwPropSheet
);
667 // dbg("OWApp: Shutting down");