1 /* -*-C++-*-        $NetBSD: rootwindow.cpp,v 1.23 2015/07/11 10:32:45 kamil Exp $        */
2 
3 /*-
4  * Copyright (c) 2001, 2004 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by UCHIYAMA Yasushi.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29  * POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include <hpcmenu.h>
33 #include <menu/window.h>
34 #include <menu/tabwindow.h>
35 #include <menu/rootwindow.h>
36 #include <res/resource.h>
37 #include "../binary/build_number.h"
38 #include <console.h>
39 
40 //
41 // root window
42 //
RootWindow(HpcBootApp & app)43 RootWindow::RootWindow(HpcBootApp &app)
44           : Window(app)
45 {
46           _boot_button        = 0;
47           _base               = 0;
48           _main               = 0;
49           _option   = 0;
50           _console  = 0;
51 }
52 
~RootWindow()53 RootWindow::~RootWindow()
54 {
55           if (_boot_button)
56                     delete _boot_button;
57           if (_cancel_button)
58                     delete _cancel_button;
59           if (_progress_bar)
60                     delete _progress_bar;
61           if (_main)
62                     delete _main;
63           if (_option)
64                     delete _option;
65           if (_console)
66                     delete _console;
67           if (_base)
68                     delete _base;
69 }
70 
71 BOOL
create(LPCREATESTRUCT aux)72 RootWindow::create(LPCREATESTRUCT aux)
73 {
74           TCHAR app_name[32];
75           // Root window's create don't called by Window Procedure.
76           // so aux is NULL
77           HINSTANCE inst = _app._instance;
78           TCHAR *wc_name = reinterpret_cast <TCHAR *>
79               (LoadString(inst, IDS_HPCMENU, 0, 0));
80           wsprintf(app_name, TEXT("%s Build %d"), wc_name, HPCBOOT_BUILD_NUMBER);
81 
82           _window = CreateWindow(wc_name, app_name, WS_VISIBLE,
83               CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
84               0, 0, inst, this);
85           if (!_window)
86                     return FALSE;
87 
88           HpcMenuInterface &menu = HpcMenuInterface::Instance();
89           if (menu._pref.auto_boot > 0)
90                     SetTimer(_window, IDD_TIMER, menu._pref.auto_boot * 1000, 0);
91 
92           ShowWindow(_window, SW_SHOW);
93           UpdateWindow(_window);
94 
95           return TRUE;
96 }
97 
98 BOOL
proc(HWND w,UINT msg,WPARAM wparam,LPARAM lparam)99 RootWindow::proc(HWND w, UINT msg, WPARAM wparam, LPARAM lparam)
100 {
101           LPCREATESTRUCT aux = reinterpret_cast <LPCREATESTRUCT>(lparam);
102           HpcMenuInterface &menu = HpcMenuInterface::Instance();
103 
104           switch(msg) {
105           default: // message can't handle.
106                     return FALSE;
107           case WM_CREATE:
108                     WMCreate(w, aux);
109                     break;
110           case WM_PAINT:
111                     WMPaint(w, aux);
112                     break;
113           case WM_ENTERMENULOOP:
114                     SaveFocus();
115                     break;
116           case WM_EXITMENULOOP:
117                     RestoreFocus();
118                     break;
119           case WM_ACTIVATE:
120                     if ((UINT)LOWORD(wparam) == WA_INACTIVE)
121                               SaveFocus();
122                     else
123                               RestoreFocus();
124                     break;
125           case WM_NOTIFY:
126           {
127                     NMHDR *notify = reinterpret_cast <NMHDR *>(lparam);
128                     // get current selected tab id
129                     int tab_id = TabCtrl_GetCurSel(_base->_window);
130                     // get context
131                     TC_ITEM tc_item;
132                     tc_item.mask = TCIF_PARAM;
133                     TabCtrl_GetItem(_base->_window, tab_id, &tc_item);
134                     TabWindow *tab = reinterpret_cast <TabWindow *>
135                         (tc_item.lParam);
136                     switch(notify->code) {
137                     case TCN_SELCHANGING:
138                               tab->hide();
139                               break;
140                     case TCN_SELCHANGE:
141                               tab->show();
142                               break;
143                     case TCN_KEYDOWN: {
144                               NMTCKEYDOWN *key = reinterpret_cast
145                                         <NMTCKEYDOWN *>(lparam);
146                               return _base->focusManagerHook(key->wVKey, key->flags,
147                                                             _cancel_button->_window);
148                         }
149                     }
150           }
151           break;
152           case WM_TIMER:
153                     disableTimer();
154                     goto boot;
155           case WM_COMMAND:
156                     switch(wparam)
157                     {
158                     case IDC_BOOTBUTTON:
159                               // inquire current options.
160                               menu.get_options();
161                               if (menu._pref.safety_message) {
162                                         UINT mb_icon = menu._pref.pause_before_boot ?
163                                                   MB_ICONQUESTION : MB_ICONWARNING;
164                                         if (MessageBox(_window,
165                                             TEXT("Data in memory will be lost.\nAre you sure?"),
166                                             TEXT("WARNING"),
167                                             mb_icon | MB_YESNO) != IDYES)
168                                                   break;
169                                         UpdateWindow(_window);
170                               }
171                     boot:
172                               SendMessage(_progress_bar->_window, PBM_SETPOS, 0, 0);
173                               menu.print(TEXT("BOOT START\n"));
174                               // inquire current options.
175                               menu.get_options();
176                               // save options to `hpcboot.cnf'
177                               menu.save();
178                               // start boot sequence.
179                               menu.boot();
180                               // NOTREACHED
181                               break;
182                     case IDC_PROGRESSBAR:
183                               break;
184                     case IDC_CANCELBUTTON:
185                               PostQuitMessage(0);
186                               break;
187                     }
188                     break;
189           case WM_DESTROY:
190                     PostQuitMessage(0);
191                     break;
192           }
193           return TRUE;
194 }
195 
196 void
SaveFocus()197 RootWindow::SaveFocus() {
198           _saved_focus = GetFocus();
199 }
200 
201 void
RestoreFocus()202 RootWindow::RestoreFocus() {
203           SetFocus(IsWindowEnabled(_saved_focus) ?
204                      _saved_focus : _boot_button->_window);
205 }
206 
207 void
WMPaint(HWND w,LPCREATESTRUCT aux)208 RootWindow::WMPaint(HWND w, LPCREATESTRUCT aux)
209 {
210           PAINTSTRUCT ps;
211           BeginPaint(w, &ps);
212           EndPaint(w, &ps);
213 }
214 
215 void
WMCreate(HWND w,LPCREATESTRUCT aux)216 RootWindow::WMCreate(HWND w, LPCREATESTRUCT aux)
217 {
218           int cmdbar_height;
219 
220           _window = w;
221           // Command bar.
222           _app._cmdbar = CommandBar_Create(aux->hInstance, w, IDC_CMDBAR);
223           CommandBar_AddAdornments(_app._cmdbar, 0, 0);
224           cmdbar_height = CommandBar_Height(_app._cmdbar);
225 
226           _button_height = cmdbar_height;
227           _button_width = BOOT_BUTTON_WIDTH;
228 
229           HDC hdc = GetDC(0);
230           if (GetDeviceCaps(hdc, HORZRES) > 320)
231               _button_width += _button_width/2;
232           ReleaseDC(0, hdc);
233 
234           RECT rect;
235           GetClientRect(w, &rect);
236           rect.top += cmdbar_height;
237 
238           // BOOT button.
239           _boot_button = new BootButton(_app, *this, rect);
240           _boot_button->create(aux);
241           // CANCEL button.
242           _cancel_button = new CancelButton(_app, *this, rect);
243           _cancel_button->create(aux);
244           // Progress bar
245           _progress_bar = new ProgressBar(_app, *this, rect);
246           _progress_bar->create(aux);
247 
248           // register myself to menu
249           HpcMenuInterface::Instance()._root = this;
250 
251           rect.top += _button_height;
252           // Tab control.
253           _base =  new TabWindowBase(_app, w, rect, IDC_BASE);
254           _base->create(aux);
255           // main/option/console dialog.(register to Menu)
256           _main = _base->boot(IDC_BASE_MAIN);
257           _option = _base->boot(IDC_BASE_OPTION);
258           _console = _base->boot(IDC_BASE_CONSOLE);
259 
260           _main->show();
261           SetFocus(_boot_button->_window);
262 
263           return;
264 }
265 
266 void
disableTimer()267 RootWindow::disableTimer()
268 {
269           KillTimer(_window, IDD_TIMER);
270 }
271 
272 BOOL
isDialogMessage(MSG & msg)273 RootWindow::isDialogMessage(MSG &msg)
274 {
275           HWND tab_window;
276 
277           if (_main && IsWindowVisible(_main->_window))
278                     tab_window = _main->_window;
279           else if (_option && IsWindowVisible(_option->_window))
280                     tab_window = _option->_window;
281           else if (_console && IsWindowVisible(_console->_window))
282                     tab_window = _console->_window;
283 
284           if (focusManagerHook(msg, tab_window))
285                     return TRUE;
286 
287           return IsDialogMessage(tab_window, &msg);
288 }
289 
290 //
291 // XXX !!! XXX !!! XXX !!! XXX !!!
292 //
293 // WinCE 2.11 doesn't support keyboard focus traversal for nested
294 // dialogs, so implement poor man focus manager for our root window.
295 // This function handles focus transition from boot/cancel buttons.
296 // Transition from the tab-control is done on WM_NOTIFY/TCN_KEYDOWN
297 // above.
298 //
299 // XXX: This is a very smplistic implementation that doesn't handle
300 // <TAB> auto-repeat count in LOWORD(msg.lParam), WS_GROUP, etc...
301 //
302 BOOL
focusManagerHook(MSG & msg,HWND tab_window)303 RootWindow::focusManagerHook(MSG &msg, HWND tab_window)
304 {
305           HWND next, prev;
306           HWND dst = 0;
307           LRESULT dlgcode = 0;
308 
309           if (msg.message != WM_KEYDOWN)
310                     return FALSE;
311 
312           if (msg.hwnd == _boot_button->_window) {
313                     next = _cancel_button->_window;
314                     prev = _base->_window;
315           } else if (msg.hwnd == _cancel_button->_window) {
316                     next = _base->_window;
317                     prev = _boot_button->_window;
318           } else if (tab_window == 0) {
319                     return FALSE;
320           } else {
321                     // last focusable control in the tab_window (XXX: WS_GROUP?)
322                     HWND last = GetNextDlgTabItem(tab_window, NULL, TRUE);
323                     if (last == NULL ||
324                         !(last == msg.hwnd || IsChild(last, msg.hwnd)))
325                               return FALSE;
326                     dlgcode = SendMessage(last, WM_GETDLGCODE, NULL, (LPARAM)&msg);
327                     next = _base->_window; // out of the tab window
328                     prev = 0; // let IsDialogMessage handle it
329           }
330 
331 #if 0 // XXX: breaks tabbing out of the console window
332           if (dlgcode & DLGC_WANTALLKEYS)
333                     return FALSE;
334 #endif
335           switch (msg.wParam) {
336           case VK_RIGHT:
337           case VK_DOWN:
338                     if (dlgcode & DLGC_WANTARROWS)
339                               return FALSE;
340                     dst = next;
341                     break;
342 
343           case VK_LEFT:
344           case VK_UP:
345                     if (dlgcode & DLGC_WANTARROWS)
346                               return FALSE;
347                     dst = prev;
348                     break;
349 
350           case VK_TAB:
351                     if (dlgcode & DLGC_WANTTAB)
352                               return FALSE;
353                     if (GetKeyState(VK_SHIFT) & 0x8000) // Shift-Tab
354                               dst = prev;
355                     else
356                               dst = next;
357                     break;
358           }
359 
360           if (dst == 0)
361                     return FALSE;
362 
363           SetFocus(dst);
364           return TRUE;
365 }
366 
367 void
progress(const char * msg)368 RootWindow::progress(const char *msg)
369 {
370 
371           if (msg)
372                     Console::Instance()->print(TEXT("[progress] %S\n"), msg);
373 
374           SendMessage(_progress_bar->_window, PBM_STEPIT, 0, 0);
375 }
376 
377 void
unprogress()378 RootWindow::unprogress()
379 {
380           SendMessage(_progress_bar->_window, PBM_SETPOS, 0, 0);
381 }
382 
383 //
384 // BOOT button
385 //
386 BOOL
create(LPCREATESTRUCT aux)387 BootButton::create(LPCREATESTRUCT aux)
388 {
389           int cx = _root._button_width;
390           int cy = _root._button_height;
391 
392           _window = CreateWindow(TEXT("BUTTON"), TEXT("Boot"),
393               BS_PUSHBUTTON | BS_NOTIFY |
394               WS_VISIBLE | WS_CHILD | WS_TABSTOP,
395               _rect.left, _rect.top, cx, cy, _parent_window,
396               reinterpret_cast <HMENU>(IDC_BOOTBUTTON),
397               aux->hInstance,
398               NULL);
399 
400           return IsWindow(_window) ? TRUE : FALSE;
401 }
402 
403 //
404 // CANCEL button
405 //
406 BOOL
create(LPCREATESTRUCT aux)407 CancelButton::create(LPCREATESTRUCT aux)
408 {
409           int cx = _root._button_width;
410           int cy = _root._button_height;
411           int x = _rect.right - _root._button_width;
412 
413           _window = CreateWindow(TEXT("BUTTON"), TEXT("Cancel"),
414               BS_PUSHBUTTON | BS_NOTIFY | WS_TABSTOP |
415               WS_VISIBLE | WS_CHILD,
416               x, _rect.top, cx, cy, _parent_window,
417               reinterpret_cast <HMENU>(IDC_CANCELBUTTON),
418               aux->hInstance,
419               NULL);
420 
421           return IsWindow(_window) ? TRUE : FALSE;
422 }
423 
424 //
425 // PROGRESS BAR
426 //
427 BOOL
create(LPCREATESTRUCT aux)428 ProgressBar::create(LPCREATESTRUCT aux)
429 {
430           int cx = _rect.right - _rect.left - _root._button_width * 2;
431           int cy = _root._button_height;
432           int x = _rect.left + _root._button_width;
433           _window = CreateWindowEx(WS_EX_CLIENTEDGE,
434               PROGRESS_CLASS, TEXT(""),
435               PBS_SMOOTH | WS_VISIBLE | WS_CHILD,
436               x, _rect.top, cx, cy, _parent_window,
437               reinterpret_cast <HMENU>(IDC_PROGRESSBAR),
438               aux->hInstance, NULL);
439           SendMessage(_window, PBM_SETRANGE, 0, MAKELPARAM(0, 11));
440           SendMessage(_window, PBM_SETSTEP, 1, 0);
441           SendMessage(_window, PBM_SETPOS, 0, 0);
442 
443           return IsWindow(_window) ? TRUE : FALSE;
444 }
445