1: /*
2: ============================================================================
3: xoblite™ -> an advanced "extended shell" for Microsoft® Windows® 11
4: Copyright © 2002-2026 Karl Henrik Henriksson [qwilk/@xoblite]
5: Copyright © 2001-2004 The Blackbox for Windows Development Team
6: http://xoblite.net/ + https://github.com/xoblite/
7: ============================================================================
8:
9: Blackbox for Windows is free software, released under the
10: GNU General Public License (GPL version 2 or later), with an extension
11: that allows linking of proprietary modules under a controlled interface.
12: What this means is that plugins etc. are allowed to be released
13: under any license the author wishes. Please note, however, that the
14: original Blackbox gradient math code used in Blackbox for Windows
15: is available under the BSD license.
16:
17: http://www.fsf.org/licenses/gpl.html
18: http://www.fsf.org/licenses/gpl-faq.html#LinkingOverControlledInterface
19: http://www.xfree86.org/3.3.6/COPYRIGHT2.html#5
20:
21: This program is distributed in the hope that it will be useful,
22: but WITHOUT ANY WARRANTY; without even the implied warranty of
23: MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24: GNU General Public License for more details.
25:
26: For additional license information, please read the included license.html.
27:
28: ============================================================================
29: */
30:
31: #include "FolderItem.h"
32: #include "Menu.h"
33: #include "MenuCommon.h"
34: #include "MenuItem.h"
35: #include "..\Settings\Settings.h"
36: #include "..\Sounds\Sounds.h"
37:
38: extern MenuCommon *pMenuCommon;
39: extern Settings *pSettings;
40:
41: //===========================================================================
42:
43: FolderItem::FolderItem(Menu* pSubMenu, char* pszTitleANSI, wchar_t* pszTitleUnicode) : MenuItem(MENUITEM_FOLDER, pszTitleANSI, pszTitleUnicode, NULL, NULL, false)
44: {
45: m_pSubMenu = pSubMenu;
46: m_nTimerId = 0;
47: SetSortPriority(PRIORITY_FOLDER);
48: }
49:
50: FolderItem::~FolderItem()
51: {
52: m_pParent->RemoveChild(m_pSubMenu);
53: if (m_pSubMenu) delete m_pSubMenu;
54: m_pSubMenu = NULL;
55: }
56:
57: //===========================================================================
58:
59: void FolderItem::Attached(Menu* pMenu)
60: {
61: MenuItem::Attached(pMenu);
62:
63: m_pParent->AddChild(m_pSubMenu);
64: m_pSubMenu->SetParent(m_pParent);
65: }
66:
67: //====================
68:
69: void FolderItem::Invoke()
70: {
71: Active(true);
72: }
73:
74: //====================
75:
76: bool FolderItem::Active(bool bActivate)
77: {
78: bool result = MenuItem::Active(bActivate);
79:
80: if (bActivate)
81: {
82: if (!IsWindowVisible(m_pSubMenu->GetWindow()) && m_nTimerId == 0)
83: {
84: m_nTimerId = (UINT)this;
85: SetTimer(GetWindow(), m_nTimerId, pSettings->submenuDelay, NULL);
86: }
87: }
88: else
89: {
90: if (m_nTimerId != 0) KillTimer(GetWindow(), m_nTimerId);
91: m_nTimerId = 0;
92:
93: if (m_pSubMenu != NULL)
94: {
95: if (IsWindowVisible(m_pSubMenu->GetWindow())) m_pSubMenu->Hide(HIDE_CHILDREN);
96: }
97: }
98:
99: return result;
100: }
101:
102: //====================
103:
104: // Handles timer messages... (folders use a
105: // timer to wait before submenus are opened)
106:
107: void FolderItem::Timer(int nTimer)
108: {
109: if ((UINT)nTimer == m_nTimerId)
110: {
111: ShowSubMenu();
112: KillTimer(GetWindow(), m_nTimerId);
113: m_nTimerId = 0;
114: }
115: }
116:
117: //====================
118:
119: void FolderItem::ShowSubMenu()
120: {
121: if (m_pSubMenu->IsPinned()) return;
122:
123: //====================
124:
125: RECT r;
126: RECT s;
127: RECT screen;
128: RECT rParent;
129: Menu* pParent;
130: int subMenuX, subMenuY;
131: int subMenuWidth, subMenuHeight;
132:
133: //====================
134:
135: // Current menu...
136: GetWindowRect(GetWindow(), &r);
137:
138: // Get the dimensions of the monitor where the current menu is located...
139: POINT pt = {r.left, r.top};
140: HMONITOR hMon = MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
141:
142: MONITORINFO mi;
143: mi.cbSize = sizeof(mi);
144:
145: if (GetMonitorInfo(hMon, &mi))
146: {
147: screen.left = mi.rcMonitor.left;
148: screen.top = mi.rcMonitor.top;
149: screen.right = mi.rcMonitor.right;
150: screen.bottom = mi.rcMonitor.bottom;
151: }
152: else
153: {
154: screen.left = 0;
155: screen.top = 0;
156: screen.right = GetSystemMetrics(SM_CXVIRTUALSCREEN);
157: screen.bottom = GetSystemMetrics(SM_CYVIRTUALSCREEN);
158: }
159:
160: //====================
161:
162: // Submenu...
163: GetWindowRect(m_pSubMenu->GetWindow(), &s);
164: subMenuWidth = s.right - s.left;
165: subMenuHeight = s.bottom - s.top;
166:
167: // Parent menu...
168: pParent = m_pParent->GetParent();
169: if (pParent) GetWindowRect(pParent->GetWindow(), &rParent);
170: else
171: {
172: // Fallbacks if the current menu has no parent...
173: rParent.left = screen.left;
174: rParent.top = screen.top;
175: }
176:
177: //====================
178:
179: // By default we try opening submenus in the same direction
180: // as the current menu was opened (if it too has a parent)...
181: if (rParent.left < r.left)
182: {
183: // Parent menu to the left of the current menu or no parent menu
184: // -> default submenu position to the right of the current menu
185: // subMenuX = r.right - pSettings->MenuFrame->borderWidth - pSettings->MenuFrame->marginWidth;
186: subMenuX = r.right - pSettings->MenuFrame->borderWidth;
187: if (pSettings->menuRoundedCorners || !pSettings->MenuFrameBorder->parentRelative) subMenuX += pSettings->submenuGap;
188:
189: // If the submenu is partly or fully off screen we
190: // move it to the left of the current menu instead...
191: if ((subMenuX + subMenuWidth) > screen.right)
192: {
193: // subMenuX = r.left - subMenuWidth + pSettings->menuBorderWidth + pSettings->MenuFrame->marginWidth;
194: subMenuX = r.left - subMenuWidth + pSettings->MenuFrame->borderWidth;
195: if (pSettings->menuRoundedCorners || !pSettings->MenuFrameBorder->parentRelative) subMenuX -= pSettings->submenuGap;
196: }
197: }
198: else
199: {
200: // Parent menu to the right of the current menu
201: // -> default submenu position to the left of the current menu
202: // subMenuX = r.left - subMenuWidth + pSettings->menuBorderWidth + pSettings->MenuFrame->marginWidth;
203: subMenuX = r.left - subMenuWidth + pSettings->MenuFrame->borderWidth;
204: if (pSettings->menuRoundedCorners || !pSettings->MenuFrameBorder->parentRelative) subMenuX -= pSettings->submenuGap;
205:
206: // If the submenu is partly or fully off screen we
207: // move it to the right of the current menu instead...
208: if (subMenuX < screen.left)
209: {
210: subMenuX = r.right - pSettings->MenuFrame->borderWidth;
211: if (pSettings->menuRoundedCorners || !pSettings->MenuFrameBorder->parentRelative) subMenuX += pSettings->submenuGap;
212: }
213: }
214:
215: // Calculating the y position is slightly easier... :)
216: // subMenuY = r.top + m_nTop - pMenuCommon->m_nTitleHeight - pSettings->MenuFrame->marginWidth - pSettings->MenuFrame->borderWidth;
217: subMenuY = r.top + m_nTop - pMenuCommon->m_nTitleHeight - pSettings->MenuFrame->marginWidth;
218: // Check if *only* the legacy menu.borderWidth was defined...
219: if ((pSettings->menuBorderWidth > 0) && (pSettings->menuBorderWidth == pSettings->MenuFrame->borderWidth) && (pSettings->menuBorderWidth == pSettings->MenuTitle->borderWidth))
220: {
221: // ...and if so do nothing... (I think ;))
222: }
223: // ...otherwise adjust the y position as applicable for modern syntax rendering... (including support for the bbLean menu title/frame borders bug/feature, see Menu.cpp)
224: else if (!pMenuCommon->m_nTitleDisabled && (pSettings->MenuFrame->borderWidth >= pSettings->MenuTitle->borderWidth)) subMenuY -= pSettings->MenuFrame->borderWidth;
225:
226: if (subMenuY < screen.top) subMenuY = screen.top;
227: if ((subMenuY + subMenuHeight) > screen.bottom) subMenuY = screen.bottom - subMenuHeight;
228:
229: //====================
230:
231: // Move focus to the newly shown submenu..
232: SetForegroundWindow(GetWindow());
233:
234: m_pSubMenu->Show(subMenuX, subMenuY);
235: SendMessage(GetBBWnd(), BB_SUBMENU, 0, 0);
236: PlaySoundFX(SFX_MENU_SUBFOLDER);
237: }
238:
239: //===========================================================================
240:
241: // ====================================================================
242: // Below: SpecialFolder is an extension to the FolderItem that supports
243: // dynamic reloading of the folder file structure when activated...
244: // ====================================================================
245:
246: SpecialFolder::SpecialFolder(char* pszTitleANSI, wchar_t* pszTitleUnicode, char* pszPath, int menuType) : FolderItem(NULL, pszTitleANSI, pszTitleUnicode)
247: {
248: m_pszPath = pszPath ? _strdup(pszPath) : _strdup("");
249: m_MenuType = menuType;
250: }
251:
252: SpecialFolder::~SpecialFolder()
253: {
254: if (m_pszPath) free(m_pszPath);
255: m_pszPath = NULL;
256:
257: if (m_pSubMenu)
258: {
259: m_pParent->RemoveChild(m_pSubMenu);
260: delete m_pSubMenu;
261: }
262: m_pSubMenu = NULL;
263: }
264:
265: //===========================================================================
266:
267: void SpecialFolder::Invoke(int button)
268: {
269: char folder[MAX_LINE_LENGTH], arg[MAX_LINE_LENGTH];
270: strcpy_s(folder, sizeofArray(folder), m_pszPath);
271:
272: if (button == 2)
273: {
274: // m_pParent->Hide(HIDE_PARENTS);
275: pMenuCommon->HideAfterInvoke(this, button);
276: MenuItem::Invoke(button);
277:
278: if (strchr(folder, '|'))
279: {
280: // If this is a merged [path] item we use
281: // the first path when right clicking on it...
282: Tokenize(folder, arg, "|");
283: strcpy_s(folder, sizeofArray(folder), arg); // Note: Just using arg as a temp variable here...
284: }
285:
286: if (folder[0] == '\"') StrRemoveEncap(folder);
287: strcpy_s(arg, sizeofArray(arg), "/e,");
288: strcat_s(arg, sizeofArray(arg), folder);
289:
290: BBExecute(GetDesktopWindow(), NULL, "explorer.exe", arg, folder, SW_SHOWNORMAL, false);
291: }
292: else if (button == 3)
293: {
294: if (folder[0] == '\"') StrRemoveEncap(folder);
295: CopyStringToClipboard(folder);
296: }
297: }
298:
299: //====================
300:
301: bool SpecialFolder::Active(bool bActivate)
302: {
303: if (m_bActive == bActivate && m_pSubMenu)
304: {
305: if (IsWindowVisible(m_pSubMenu->GetWindow())) return bActivate;
306: }
307:
308: if (bActivate)
309: {
310: if (m_pSubMenu)
311: {
312: if (m_pSubMenu->IsPinned()) return FolderItem::Active(bActivate);
313: ShowWindow(m_pSubMenu->GetWindow(), SW_HIDE);
314: m_pParent->RemoveChild(m_pSubMenu);
315: delete m_pSubMenu;
316: }
317:
318: m_pSubMenu = new Menu(pMenuCommon->hInst);
319:
320: pMenuCommon->AddHeaderItem(m_pSubMenu, m_pszTitleANSI);
321: pMenuCommon->InsertFolder(m_pSubMenu, m_pszPath, m_MenuType, true);
322: pMenuCommon->AddFooterItem(m_pSubMenu, "");
323:
324: m_pSubMenu->Invalidate();
325: m_pSubMenu->Validate();
326:
327: m_pParent->AddChild(m_pSubMenu);
328: m_pSubMenu->SetParent(m_pParent);
329: m_pSubMenu->SetMenuFolderPath(m_pszPath);
330: }
331:
332: return FolderItem::Active(bActivate);
333: }
334:
335: //====================
336:
337: void SpecialFolder::Attached(Menu* pMenu)
338: {
339: m_pParent = pMenu;
340: }
341:
342: //===========================================================================
343: