001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017: /**
018: * @author Dmitry A. Durnev, Pavel Dolgov
019: * @version $Revision$
020: */package org.apache.harmony.awt.wtk.windows;
021:
022: import java.awt.Rectangle;
023: import java.awt.event.KeyEvent;
024: import java.util.HashMap;
025: import java.util.Iterator;
026: import java.util.LinkedList;
027: import java.util.Map;
028:
029: import org.apache.harmony.awt.internal.nls.Messages;
030: import org.apache.harmony.awt.nativebridge.Int16Pointer;
031: import org.apache.harmony.awt.nativebridge.NativeBridge;
032: import org.apache.harmony.awt.nativebridge.windows.Win32;
033: import org.apache.harmony.awt.nativebridge.windows.WindowsDefs;
034: import org.apache.harmony.awt.wtk.NativeEvent;
035: import org.apache.harmony.awt.wtk.NativeEventQueue;
036: import org.apache.harmony.awt.wtk.NativeWindow;
037: import org.apache.harmony.misc.accessors.AccessorFactory;
038: import org.apache.harmony.misc.accessors.ObjectAccessor;
039:
040: /**
041: * Handler of Windows messages
042: */
043: public class WinEventQueue extends NativeEventQueue {
044:
045: static final NativeBridge bridge = NativeBridge.getInstance();
046: static final Win32 win32 = Win32.getInstance();
047: static final ObjectAccessor objAccessor = AccessorFactory
048: .getObjectAccessor();
049:
050: /**
051: * Invisible auxiliary window for service messages
052: */
053: private final long javaWindow;
054: /**
055: * Theme handles currently open
056: */
057: private final ThemeMap themeMap = new ThemeMap();
058:
059: /**
060: * The message being processed
061: */
062: private final Win32.MSG lastMsg = win32.createMSG(false);
063:
064: final int dispatchThreadID = win32.GetCurrentThreadId();
065: final WinSystemProperties systemProperties;
066:
067: /**
068: * The last keyboard message
069: */
070: private final Win32.MSG charMsg = win32.createMSG(false);
071: /**
072: * Accumulated typed characters
073: */
074: private final StringBuffer lastTranslation = new StringBuffer();
075: /**
076: * Recently typed character
077: */
078: private char lastChar;
079: /**
080: * Keycodes and characters stored
081: * until WM_KEYUP or WM_SYSKEYUP message is received
082: */
083: private final HashMap<Integer, Character> keyCodeToChar = new HashMap<Integer, Character>();
084:
085: final WinWindowFactory factory;
086:
087: private long utcOffset = -1;
088: /**
089: * Return value for the Windows message being processed
090: */
091: private final long[] result = new long[1];
092:
093: public static final int WM_PERFORM_TASK = WindowsDefs.WM_USER + 1;
094: public static final int WM_PERFORM_LATER = WindowsDefs.WM_USER + 2;
095:
096: private final LinkedList<Preprocessor> preprocessors = new LinkedList<Preprocessor>();
097:
098: /**
099: * Initialize message pump, create auxiliary window for service messages
100: * @param systemProperties
101: */
102: public WinEventQueue(WinSystemProperties systemProperties) {
103: this .systemProperties = systemProperties;
104:
105: WindowProcHandler.registerCallback();
106: factory = new WinWindowFactory(this );
107:
108: javaWindow = createJavaWindow();
109: }
110:
111: public void addPreprocessor(Preprocessor preprocessor) {
112: preprocessors.add(preprocessor);
113: }
114:
115: /**
116: * Wait for next Windows messages. The re-entrant calls to windowProc()
117: * may occur while waiting
118: * @return false when WM_QUIT received
119: */
120: @Override
121: public boolean waitEvent() {
122: win32.GetMessageW(lastMsg, 0, 0, 0);
123:
124: return lastMsg.get_message() != WindowsDefs.WM_QUIT;
125: }
126:
127: /**
128: * Post the service message to the auxiliary window to ensure
129: * the method waitEvent() will leave the waiting state
130: */
131: @Override
132: public void awake() {
133: if (win32.GetCurrentThreadId() != dispatchThreadID) {
134: win32.PostThreadMessageW(dispatchThreadID,
135: WinEvent.WM_AWAKE, 0, 0);
136: }
137: }
138:
139: /**
140: * Handle the service message posted to event dispatch thread
141: * @param msg - the Windows message
142: */
143: private void processThreadMessage(Win32.MSG msg) {
144: handleEvent(0, msg.get_message(), msg.get_wParam(), msg
145: .get_lParam());
146: }
147:
148: /**
149: * Translate key code to typed character for keyboard messages,
150: * do nothing for all other messages
151: * @param msg - the Windows message to translate
152: */
153: private void translateMessage(Win32.MSG msg) {
154: if (win32.TranslateMessage(msg) == 0) {
155: return;
156: }
157:
158: lastChar = KeyEvent.CHAR_UNDEFINED;
159: lastTranslation.setLength(0);
160:
161: switch (msg.get_message()) {
162: case WindowsDefs.WM_KEYDOWN:
163: peekChar(msg, WindowsDefs.WM_CHAR, WindowsDefs.WM_DEADCHAR);
164: break;
165: case WindowsDefs.WM_SYSKEYDOWN:
166: peekChar(msg, WindowsDefs.WM_SYSCHAR,
167: WindowsDefs.WM_SYSDEADCHAR);
168: break;
169: default:
170: peekChar(msg, WindowsDefs.WM_CHAR, WindowsDefs.WM_DEADCHAR);
171: }
172: }
173:
174: /**
175: * Determine typed character for WM_KEYDOWN or WM_SYSKEYDOWN message,
176: * and store the translation for decoding of subsequent
177: * WM_KEYUP or WM_SYSKEYUP messages
178: * @param msg - message to find the typed character for
179: * @param min - the low bound of range of messages to peek
180: * @param max - the high bound of range of messages to peek
181: */
182: private void peekChar(Win32.MSG msg, int min, int max) {
183: while (win32.PeekMessageW(charMsg, msg.get_hwnd(), min, max,
184: WindowsDefs.PM_REMOVE) != 0) {
185:
186: int msgId = charMsg.get_message();
187: int vKey = (int) msg.get_wParam();
188: lastChar = (char) charMsg.get_wParam();
189:
190: if (msgId == WindowsDefs.WM_CHAR
191: || msgId == WindowsDefs.WM_SYSCHAR) {
192: lastTranslation.append(lastChar);
193: }
194:
195: keyCodeToChar.put(new Integer(vKey),
196: new Character(lastChar));
197: }
198: }
199:
200: /**
201: * The callback called by Windows framework to handle the messages
202: * @param hwnd - window handle
203: * @param msg - message code
204: * @param wParam - first message-dependent parameter
205: * @param lParam - second message-dependent parameter
206: * @return - message-dependent value
207: */
208: public long windowProc(long hwnd, int msg, long wParam, long lParam) {
209: if ((utcOffset < 0) && (lastMsg.get_time() != 0l)) {
210: utcOffset = System.currentTimeMillis() - lastMsg.get_time();
211: }
212:
213: if (preProcessMessage(hwnd, msg, wParam, lParam, result)) {
214: return result[0];
215: }
216: if (handleEvent(hwnd, msg, wParam, lParam)) {
217: return 0;
218: }
219:
220: if (msg == WindowsDefs.WM_MOUSEACTIVATE) {
221: return WindowsDefs.MA_NOACTIVATE;
222: }
223: return win32.DefWindowProcW(hwnd, msg, wParam, lParam);
224: }
225:
226: private boolean handleEvent(long hwnd, int msg, long wParam,
227: long lParam) {
228: if (msg == WindowsDefs.WM_KEYUP
229: || msg == WindowsDefs.WM_SYSKEYUP) {
230: Character ch = keyCodeToChar.remove(new Integer(
231: (int) wParam));
232: if (ch != null) {
233: lastChar = ch.charValue();
234: } else {
235: lastChar = KeyEvent.CHAR_UNDEFINED;
236: }
237: }
238:
239: WinEvent event = new WinEvent(hwnd, msg, wParam, lParam,
240: utcOffset + lastMsg.get_time(), lastTranslation,
241: lastChar, factory);
242: if (event.getEventId() != NativeEvent.ID_PLATFORM) {
243: addEvent(event);
244: return !isKeyUpOrKeyDownMessage(msg);
245: }
246: return false;
247: }
248:
249: private boolean isKeyUpOrKeyDownMessage(int msg) {
250: return msg == WindowsDefs.WM_KEYDOWN
251: || msg == WindowsDefs.WM_KEYUP
252: || msg == WindowsDefs.WM_SYSKEYDOWN
253: || msg == WindowsDefs.WM_SYSKEYUP;
254: }
255:
256: /**
257: * Call the message preprocessors, handle the Windows messages that
258: * should not be passed to the default Windows handler
259: *
260: * @param hwnd - window handle
261: * @param msg - message code
262: * @param wParam - first message-dependent parameter
263: * @param lParam - second message-dependent parameter
264: * @param result - result[0] is the return value of the message
265: * @return - false if the default Windows handler should be called;
266: */
267: private boolean preProcessMessage(long hwnd, int msg, long wParam,
268: long lParam, long[] result) {
269: if (msg == WM_PERFORM_TASK && hwnd == javaWindow) {
270: Task t = (Task) objAccessor.getObjectFromReference(lParam);
271: t.perform();
272: return true;
273: }
274:
275: if (msg == WM_PERFORM_LATER && hwnd == javaWindow) {
276: Task t = (Task) objAccessor.getObjectFromReference(lParam);
277: t.perform();
278: objAccessor.releaseGlobalReference(lParam);
279: return true;
280: }
281: for (Iterator<Preprocessor> i = preprocessors.iterator(); i
282: .hasNext();) {
283: if (i.next().preprocess(hwnd, msg, wParam, lParam, result)) {
284: return true;
285: }
286: }
287:
288: switch (msg) {
289: case WindowsDefs.WM_CREATE:
290: factory.onCreateWindow(hwnd);
291: break;
292:
293: case WindowsDefs.WM_NCACTIVATE:
294: // prevent non-focusable window's non-client area from being
295: // changed to indicate its active state
296: long hwndOther = lParam;
297: NativeWindow wnd = factory.getWindowById(hwnd);
298: NativeWindow otherWnd = factory.getWindowById(hwndOther);
299: if (wParam != 0 && !wnd.isFocusable() || wParam == 0
300: && wnd.isFocusable() && otherWnd != null
301: && !otherWnd.isFocusable()) {
302:
303: result[0] = 0;
304: return true;
305: }
306: break;
307:
308: case WindowsDefs.WM_ACTIVATE:
309: if (wParam == WindowsDefs.WA_ACTIVE) {
310: // while activation of Frame/Dialog
311: // [actually focus transfer to focusProxy] is in progress
312: // skip all focus events related to windows being activated/deactivated,
313: // such spurious events are sometimes generated by Win32
314: WinWindow ownd = (WinWindow) factory
315: .getWindowById(lParam);
316: long hOwner = lParam;
317: // find nearest decorated ancestor window of
318: // the window being deactivated
319: while ((ownd != null) && ownd.undecorated) {
320: hOwner = win32.GetParent(hOwner);
321: ownd = (WinWindow) factory.getWindowById(hOwner);
322: }
323: // cancel focus only if
324: // this is the window found
325: if ((ownd != null) && (hOwner == hwnd)) {
326: result[0] = 0;
327: return true;
328: }
329: }
330: break;
331:
332: case WindowsDefs.WM_SETCURSOR:
333: short htCode = (short) lParam;
334: if (htCode == WindowsDefs.HTCLIENT) {
335: result[0] = 0;
336: return true;
337: }
338: break;
339:
340: case WindowsDefs.WM_GETMINMAXINFO:
341: if (processGetMinMaxInfo(hwnd, lParam)) {
342: result[0] = 0;
343: return true;
344: }
345: break;
346:
347: case WindowsDefs.WM_ERASEBKGND:
348: result[0] = 1;
349: return true;
350:
351: case WindowsDefs.WM_SYSCOMMAND:
352: // suppress system menu activation on F10 key
353: if ((wParam & 0xFFF0) == WindowsDefs.SC_KEYMENU) {
354: result[0] = 0;
355: return true;
356: }
357: break;
358:
359: case WindowsDefs.WM_SYSCOLORCHANGE:
360: result[0] = 0;
361: if (hwnd == javaWindow) {
362: systemProperties.resetSystemColors();
363: }
364: break;
365:
366: case WindowsDefs.WM_SETTINGCHANGE:
367: systemProperties.processSettingChange(wParam);
368: break;
369: case WindowsDefs.WM_IME_STARTCOMPOSITION:
370: return WinIM.onStartComposition(hwnd);
371: case WindowsDefs.WM_IME_SETCONTEXT:
372: if (wParam != 0l) {
373: return WinIM.onActivateContext(hwnd);
374: }
375: break;
376: case WindowsDefs.WM_IME_COMPOSITION:
377: WinIM.onComposition(hwnd, lParam);
378: break;
379: }
380:
381: return false;
382: }
383:
384: public ThemeMap getThemeMap() {
385: return themeMap;
386: }
387:
388: /**
389: * create invisible auxlitary window to listen to the service messages
390: * @return HWND of created window
391: */
392: private static long createJavaWindow() {
393: long hwnd = win32.CreateWindowExW(0,
394: WindowProcHandler.windowClassName, "JavaWindow", //$NON-NLS-1$
395: 0, 0, 0, 0, 0, 0, 0, 0, null);
396: int winError = win32.GetLastError();
397:
398: if (hwnd == 0) {
399: // awt.1C=Failure to create JavaWindow GetLastError returned {0}
400: throw new InternalError(Messages.getString("awt.1C", //$NON-NLS-1$
401: winError));
402: }
403:
404: return hwnd;
405: }
406:
407: /**
408: * Apply window's maximized bounds
409: * @param hwnd
410: */
411: private boolean processGetMinMaxInfo(long hwnd, long ptrMinMaxInfo) {
412: WinWindow win = factory.getWinWindowById(hwnd);
413: if (win == null) {
414: return false;
415: }
416: Rectangle maxBounds = win.maximizedBounds;
417: final int MAX = Integer.MAX_VALUE;
418: if (maxBounds == null) {
419: return false;
420: }
421: Win32.MINMAXINFO info = win32.createMINMAXINFO(ptrMinMaxInfo);
422:
423: if (maxBounds.width < MAX) {
424: info.get_ptMaxSize().set_x(maxBounds.width);
425: }
426: if (maxBounds.height < MAX) {
427: info.get_ptMaxSize().set_y(maxBounds.height);
428: }
429: if (maxBounds.x < MAX) {
430: info.get_ptMaxPosition().set_x(maxBounds.x);
431: }
432: if (maxBounds.y < MAX) {
433: info.get_ptMaxPosition().set_y(maxBounds.y);
434: }
435: return true;
436: }
437:
438: @Override
439: public long getJavaWindow() {
440: return javaWindow;
441: }
442:
443: /**
444: * Interface for message pre-processing
445: */
446: public interface Preprocessor {
447:
448: public boolean preprocess(long hwnd, int msg, long wParam,
449: long lParam, long[] result);
450:
451: }
452:
453: /**
454: * The Windows theme handles currently open, and their names
455: */
456: public class ThemeMap {
457: private final HashMap<String, Long> themes = new HashMap<String, Long>();
458: private boolean enabled;
459:
460: ThemeMap() {
461: enabled = (win32.IsThemeActive() != 0);
462: }
463:
464: public long get(String name) {
465: Long obj = themes.get(name);
466: if (obj != null) {
467: return obj.longValue();
468: }
469: long hTheme = open(name);
470: themes.put(name, new Long(hTheme));
471: return hTheme;
472: }
473:
474: private long open(String name) {
475: Int16Pointer namePtr = bridge.createInt16Pointer(name,
476: false);
477: long hTheme = win32.OpenThemeData(javaWindow, namePtr
478: .lock());
479: namePtr.unlock();
480: return hTheme;
481: }
482:
483: public boolean isEnabled() {
484: return enabled;
485: }
486:
487: void refresh() {
488: enabled = (win32.IsThemeActive() != 0);
489:
490: for (Iterator<Map.Entry<String, Long>> it = themes
491: .entrySet().iterator(); it.hasNext();) {
492: Map.Entry<String, Long> e = it.next();
493: long hTheme = e.getValue().longValue();
494: if (hTheme != 0) {
495: win32.CloseThemeData(hTheme);
496: }
497: if (enabled) {
498: hTheme = open(e.getKey());
499: } else {
500: hTheme = 0;
501: }
502: e.setValue(new Long(hTheme));
503: }
504: }
505: }
506:
507: @Override
508: public void dispatchEvent() {
509: if (lastMsg.get_hwnd() == 0) {
510: processThreadMessage(lastMsg);
511: }
512:
513: translateMessage(lastMsg);
514: win32.DispatchMessageW(lastMsg);
515: }
516:
517: @Override
518: public void performTask(Task task) {
519: long ref = objAccessor.getGlobalReference(task);
520: win32.SendMessageW(javaWindow, WM_PERFORM_TASK, 0, ref);
521: objAccessor.releaseGlobalReference(ref);
522: }
523:
524: @Override
525: public void performLater(Task task) {
526: long ref = objAccessor.getGlobalReference(task);
527: win32.PostMessageW(javaWindow, WM_PERFORM_LATER, 0, ref);
528: }
529: }
|