| |
1 /* |
| |
2 * gaim - WinGaim Options Plugin |
| |
3 * |
| |
4 * File: gtkappbar.c |
| |
5 * Date: August 2, 2003 |
| |
6 * Description: Appbar functionality for Windows GTK+ applications |
| |
7 * |
| |
8 * Copyright (C) 2003, Herman Bloggs <hermanator12002@yahoo.com> |
| |
9 * |
| |
10 * This program is free software; you can redistribute it and/or modify |
| |
11 * it under the terms of the GNU General Public License as published by |
| |
12 * the Free Software Foundation; either version 2 of the License, or |
| |
13 * (at your option) any later version. |
| |
14 * |
| |
15 * This program is distributed in the hope that it will be useful, |
| |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| |
18 * GNU General Public License for more details. |
| |
19 * |
| |
20 * You should have received a copy of the GNU General Public License |
| |
21 * along with this program; if not, write to the Free Software |
| |
22 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| |
23 * |
| |
24 */ |
| |
25 /* |
| |
26 * TODO: |
| |
27 * - Move 'App on top' feature from Trans plugin to here |
| |
28 * - Bug: Multiple Show/Hide Desktop calls causes client area to disapear |
| |
29 */ |
| |
30 #include <windows.h> |
| |
31 #include <winver.h> |
| |
32 #include <stdio.h> |
| |
33 #include <gtk/gtk.h> |
| |
34 #include <gdk/gdkwin32.h> |
| |
35 #include "gtkappbar.h" |
| |
36 #include "debug.h" |
| |
37 |
| |
38 #define APPBAR_CALLBACK WM_USER + 1010 |
| |
39 |
| |
40 static void get_window_normal_rc(HWND hwnd, RECT *rc) { |
| |
41 WINDOWPLACEMENT wplc; |
| |
42 GetWindowPlacement(hwnd, &wplc); |
| |
43 CopyRect(rc, &wplc.rcNormalPosition); |
| |
44 } |
| |
45 |
| |
46 static void print_rect(RECT *rc) { |
| |
47 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "RECT: L:%ld R:%ld T:%ld B:%ld\n", |
| |
48 rc->left, rc->right, rc->top, rc->bottom); |
| |
49 } |
| |
50 |
| |
51 static void set_toolbar(HWND hwnd, gboolean val) { |
| |
52 LONG style=0; |
| |
53 |
| |
54 style = GetWindowLong(hwnd, GWL_EXSTYLE); |
| |
55 if(val && !(style & WS_EX_TOOLWINDOW)) |
| |
56 style |= WS_EX_TOOLWINDOW; |
| |
57 else if(!val && style & WS_EX_TOOLWINDOW) |
| |
58 style &= ~WS_EX_TOOLWINDOW; |
| |
59 else |
| |
60 return; |
| |
61 SetWindowLong(hwnd, GWL_EXSTYLE, style); |
| |
62 SetWindowPos(hwnd, 0, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOZORDER | SWP_FRAMECHANGED); |
| |
63 } |
| |
64 |
| |
65 static gboolean gtk_appbar_register(GtkAppBar *ab, HWND hwnd) { |
| |
66 APPBARDATA abd; |
| |
67 |
| |
68 abd.cbSize = sizeof(APPBARDATA); |
| |
69 abd.hWnd = hwnd; |
| |
70 abd.uCallbackMessage = APPBAR_CALLBACK; |
| |
71 |
| |
72 ab->registered = SHAppBarMessage(ABM_NEW, &abd); |
| |
73 |
| |
74 return ab->registered; |
| |
75 } |
| |
76 |
| |
77 static gboolean gtk_appbar_unregister(GtkAppBar *ab, HWND hwnd) { |
| |
78 APPBARDATA abd; |
| |
79 |
| |
80 if(!ab->registered) |
| |
81 return TRUE; |
| |
82 |
| |
83 abd.cbSize = sizeof(APPBARDATA); |
| |
84 abd.hWnd = hwnd; |
| |
85 |
| |
86 ab->registered = !SHAppBarMessage(ABM_REMOVE, &abd); |
| |
87 |
| |
88 if(!ab->registered) { |
| |
89 ab->docked = FALSE; |
| |
90 ab->docking = FALSE; |
| |
91 } |
| |
92 return !ab->registered; |
| |
93 } |
| |
94 |
| |
95 static void gtk_appbar_querypos(GtkAppBar *ab, HWND hwnd, RECT *rc) { |
| |
96 APPBARDATA abd; |
| |
97 int iWidth = 0; |
| |
98 |
| |
99 if(!ab->registered) |
| |
100 gtk_appbar_register(ab, hwnd); |
| |
101 |
| |
102 abd.hWnd = hwnd; |
| |
103 abd.cbSize = sizeof(APPBARDATA); |
| |
104 CopyRect(&abd.rc, rc); |
| |
105 abd.uEdge = ab->side; |
| |
106 |
| |
107 iWidth = abd.rc.right - abd.rc.left; |
| |
108 |
| |
109 abd.rc.top = 0; |
| |
110 abd.rc.bottom = GetSystemMetrics(SM_CYSCREEN); |
| |
111 switch (abd.uEdge) |
| |
112 { |
| |
113 case ABE_LEFT: |
| |
114 abd.rc.left = 0; |
| |
115 abd.rc.right = iWidth; |
| |
116 break; |
| |
117 |
| |
118 case ABE_RIGHT: |
| |
119 abd.rc.right = GetSystemMetrics(SM_CXSCREEN); |
| |
120 abd.rc.left = abd.rc.right - iWidth; |
| |
121 break; |
| |
122 } |
| |
123 |
| |
124 /* Ask the system for the screen space */ |
| |
125 SHAppBarMessage(ABM_QUERYPOS, &abd); |
| |
126 |
| |
127 switch (abd.uEdge) |
| |
128 { |
| |
129 case ABE_LEFT: |
| |
130 abd.rc.right = abd.rc.left + iWidth; |
| |
131 break; |
| |
132 |
| |
133 case ABE_RIGHT: |
| |
134 abd.rc.left = abd.rc.right - iWidth; |
| |
135 break; |
| |
136 } |
| |
137 |
| |
138 CopyRect(rc, &abd.rc); |
| |
139 } |
| |
140 |
| |
141 static void gtk_appbar_setpos(GtkAppBar *ab, HWND hwnd) { |
| |
142 APPBARDATA abd; |
| |
143 |
| |
144 if(!ab->registered) |
| |
145 gtk_appbar_register(ab, hwnd); |
| |
146 |
| |
147 abd.hWnd = hwnd; |
| |
148 abd.cbSize = sizeof(APPBARDATA); |
| |
149 CopyRect(&abd.rc, &(ab->docked_rect)); |
| |
150 abd.uEdge = ab->side; |
| |
151 |
| |
152 SHAppBarMessage(ABM_SETPOS, &abd); |
| |
153 } |
| |
154 |
| |
155 static GdkFilterReturn wnd_moving(GtkAppBar *ab, GdkXEvent *xevent) { |
| |
156 MSG *msg = (MSG*)xevent; |
| |
157 POINT cp; |
| |
158 LONG cxScreen; |
| |
159 RECT *rc = (RECT*)msg->lParam; |
| |
160 int side = -1; |
| |
161 |
| |
162 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "wnd_moving\n"); |
| |
163 |
| |
164 cxScreen = GetSystemMetrics(SM_CXSCREEN); |
| |
165 GetCursorPos(&cp); |
| |
166 |
| |
167 /* Which part of the screen are we in ? */ |
| |
168 if( cp.x > (cxScreen - (cxScreen / 10)) ) |
| |
169 side = ABE_RIGHT; |
| |
170 else if( cp.x < (cxScreen / 10) ) |
| |
171 side = ABE_LEFT; |
| |
172 |
| |
173 if(!ab->docked) { |
| |
174 if( (side == ABE_RIGHT || side == ABE_LEFT) ) { |
| |
175 if( !ab->docking ) { |
| |
176 ab->side = side; |
| |
177 GetWindowRect(msg->hwnd, &(ab->docked_rect)); |
| |
178 gtk_appbar_querypos(ab, msg->hwnd, &(ab->docked_rect)); |
| |
179 |
| |
180 /* save pre-docking height */ |
| |
181 ab->undocked_height = rc->bottom - rc->top; |
| |
182 ab->docking = TRUE; |
| |
183 } |
| |
184 } |
| |
185 else |
| |
186 ab->docking = FALSE; |
| |
187 } |
| |
188 else if(side < 0) { |
| |
189 gtk_appbar_unregister(ab, msg->hwnd); |
| |
190 rc->bottom = rc->top + ab->undocked_height; |
| |
191 } |
| |
192 |
| |
193 /* Switch to toolbar/regular caption*/ |
| |
194 if(ab->docking) |
| |
195 set_toolbar(msg->hwnd, TRUE); |
| |
196 else if(!ab->docked) |
| |
197 set_toolbar(msg->hwnd, FALSE); |
| |
198 |
| |
199 return GDK_FILTER_CONTINUE; |
| |
200 } |
| |
201 |
| |
202 static GdkFilterReturn wnd_sizing(GtkAppBar *ab, GdkXEvent *xevent) { |
| |
203 MSG *msg = (MSG*)xevent; |
| |
204 |
| |
205 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "wnd_sizing\n"); |
| |
206 if(ab->docked) { |
| |
207 RECT *rc = (RECT*)msg->lParam; |
| |
208 if(ab->side == ABE_LEFT && msg->wParam == WMSZ_RIGHT) { |
| |
209 ab->docked_rect.right = rc->right; |
| |
210 gtk_appbar_setpos(ab, msg->hwnd); |
| |
211 } |
| |
212 else if(ab->side == ABE_RIGHT && msg->wParam == WMSZ_LEFT) { |
| |
213 ab->docked_rect.left = rc->left; |
| |
214 gtk_appbar_setpos(ab, msg->hwnd); |
| |
215 } |
| |
216 return GDK_FILTER_REMOVE; |
| |
217 } |
| |
218 return GDK_FILTER_CONTINUE; |
| |
219 } |
| |
220 |
| |
221 static GdkFilterReturn wnd_poschanging(GtkAppBar *ab, GdkXEvent *xevent) { |
| |
222 MSG *msg = (MSG*)xevent; |
| |
223 WINDOWPOS *wpos = (WINDOWPOS*)msg->lParam; |
| |
224 |
| |
225 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "wnd_poschanging\n"); |
| |
226 |
| |
227 if(ab->docked || ab->docking) { |
| |
228 wpos->x = ab->docked_rect.left; |
| |
229 wpos->y = ab->docked_rect.top; |
| |
230 wpos->cx = ab->docked_rect.right - ab->docked_rect.left; |
| |
231 wpos->cy = ab->docked_rect.bottom - ab->docked_rect.top; |
| |
232 if(IsIconic(msg->hwnd)) |
| |
233 set_toolbar(msg->hwnd, FALSE); |
| |
234 /*return GDK_FILTER_REMOVE;*/ |
| |
235 } |
| |
236 return GDK_FILTER_CONTINUE; |
| |
237 } |
| |
238 |
| |
239 static GdkFilterReturn wnd_exitsizemove(GtkAppBar *ab, GdkXEvent *xevent) { |
| |
240 MSG *msg = (MSG*)xevent; |
| |
241 |
| |
242 if(ab->docking) { |
| |
243 gtk_appbar_setpos(ab, msg->hwnd); |
| |
244 ab->docking = FALSE; |
| |
245 ab->docked = TRUE; |
| |
246 } |
| |
247 else if(!ab->docked) { |
| |
248 gtk_appbar_unregister(ab, msg->hwnd); |
| |
249 } |
| |
250 |
| |
251 return GDK_FILTER_CONTINUE; |
| |
252 } |
| |
253 |
| |
254 static GdkFilterReturn wnd_showwindow(GtkAppBar *ab, GdkXEvent *xevent) { |
| |
255 MSG *msg = (MSG*)xevent; |
| |
256 |
| |
257 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "wnd_showwindow\n"); |
| |
258 if(msg->wParam && ab->docked) { |
| |
259 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "shown\n"); |
| |
260 ab->docked = FALSE; |
| |
261 gtk_appbar_dock(ab, ab->side); |
| |
262 |
| |
263 } |
| |
264 else if(!msg->wParam && ab->docked) { |
| |
265 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "hidden\n"); |
| |
266 gtk_appbar_unregister(ab, GDK_WINDOW_HWND(ab->win->window)); |
| |
267 set_toolbar(GDK_WINDOW_HWND(ab->win->window), FALSE); |
| |
268 ab->docked = TRUE; |
| |
269 } |
| |
270 return GDK_FILTER_CONTINUE; |
| |
271 } |
| |
272 |
| |
273 static GdkFilterReturn wnd_size(GtkAppBar *ab, GdkXEvent *xevent) { |
| |
274 MSG *msg = (MSG*)xevent; |
| |
275 |
| |
276 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "wnd_size\n"); |
| |
277 |
| |
278 if(msg->wParam == SIZE_MINIMIZED) { |
| |
279 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "Minimize\n"); |
| |
280 if(ab->docked) { |
| |
281 gtk_appbar_unregister(ab, GDK_WINDOW_HWND(ab->win->window)); |
| |
282 ab->docked = TRUE; |
| |
283 } |
| |
284 } |
| |
285 else if(msg->wParam == SIZE_RESTORED) { |
| |
286 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "Restore\n"); |
| |
287 if(ab->docked) { |
| |
288 gtk_appbar_dock(ab, ab->side); |
| |
289 } |
| |
290 } |
| |
291 return GDK_FILTER_CONTINUE; |
| |
292 } |
| |
293 |
| |
294 static GdkFilterReturn wnd_nchittest(GtkAppBar *ab, GdkXEvent *xevent) { |
| |
295 MSG *msg = (MSG*)xevent; |
| |
296 |
| |
297 if(ab->docked) { |
| |
298 UINT ret = DefWindowProc(msg->hwnd, msg->message, msg->wParam, msg->lParam); |
| |
299 |
| |
300 switch(ret) { |
| |
301 case HTBOTTOM: |
| |
302 case HTBOTTOMLEFT: |
| |
303 case HTBOTTOMRIGHT: |
| |
304 case HTTOP: |
| |
305 case HTTOPLEFT: |
| |
306 case HTTOPRIGHT: |
| |
307 return GDK_FILTER_REMOVE; |
| |
308 case HTLEFT: |
| |
309 if(ab->side == ABE_LEFT) |
| |
310 return GDK_FILTER_REMOVE; |
| |
311 break; |
| |
312 case HTRIGHT: |
| |
313 if(ab->side == ABE_RIGHT) |
| |
314 return GDK_FILTER_REMOVE; |
| |
315 break; |
| |
316 } |
| |
317 } |
| |
318 return GDK_FILTER_CONTINUE; |
| |
319 } |
| |
320 |
| |
321 #if 0 |
| |
322 static GdkFilterReturn wnd_initmenupopup(GtkAppBar *ab, GdkXEvent *xevent) { |
| |
323 MSG *msg = (MSG*)xevent; |
| |
324 |
| |
325 if(ab->docked && HIWORD(msg->lParam)) { |
| |
326 HMENU sysmenu = GetSystemMenu(msg->hwnd, FALSE); |
| |
327 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "wnd_initpopupmenu: docked: %d ismenu: %d\n", ab->docked, IsMenu(sysmenu)); |
| |
328 if(EnableMenuItem(sysmenu, SC_MAXIMIZE, MF_BYCOMMAND|MF_GRAYED)<0) |
| |
329 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "SC_MAXIMIZE Menu item does not exist\n"); |
| |
330 if(EnableMenuItem(sysmenu, SC_MOVE, MF_BYCOMMAND|MF_GRAYED)<0) |
| |
331 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "SC_MOVE Menu item does not exist\n"); |
| |
332 return GDK_FILTER_CONTINUE; |
| |
333 } |
| |
334 else |
| |
335 GetSystemMenu(msg->hwnd, TRUE); |
| |
336 return GDK_FILTER_CONTINUE; |
| |
337 } |
| |
338 #endif |
| |
339 |
| |
340 static GdkFilterReturn gtk_appbar_callback(GtkAppBar *ab, GdkXEvent *xevent) { |
| |
341 MSG *msg = (MSG*)xevent; |
| |
342 |
| |
343 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "gtk_appbar_callback\n"); |
| |
344 switch (msg->wParam) { |
| |
345 case ABN_STATECHANGE: |
| |
346 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "gtk_appbar_callback: ABN_STATECHANGE\n"); |
| |
347 break; |
| |
348 |
| |
349 case ABN_FULLSCREENAPP: |
| |
350 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "gtk_appbar_callback: ABN_FULLSCREENAPP: %d\n", (BOOL)msg->lParam); |
| |
351 break; |
| |
352 |
| |
353 case ABN_POSCHANGED: |
| |
354 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "gtk_appbar_callback: ABN_POSCHANGED\n"); |
| |
355 gtk_appbar_querypos(ab, msg->hwnd, &(ab->docked_rect)); |
| |
356 MoveWindow(msg->hwnd, ab->docked_rect.left, ab->docked_rect.top, |
| |
357 ab->docked_rect.right - ab->docked_rect.left, |
| |
358 ab->docked_rect.bottom - ab->docked_rect.top, TRUE); |
| |
359 gtk_appbar_setpos(ab, msg->hwnd); |
| |
360 break; |
| |
361 } |
| |
362 return GDK_FILTER_CONTINUE; |
| |
363 } |
| |
364 |
| |
365 static GdkFilterReturn gtk_appbar_event_filter(GdkXEvent *xevent, GdkEvent *event, gpointer data) { |
| |
366 MSG *msg = (MSG*)xevent; |
| |
367 |
| |
368 /*printf("MSG: %s\n", message_to_string (msg->message));*/ |
| |
369 switch(msg->message) { |
| |
370 case WM_EXITSIZEMOVE: |
| |
371 return wnd_exitsizemove(data, xevent); |
| |
372 case WM_WINDOWPOSCHANGING: |
| |
373 return wnd_poschanging(data, xevent); |
| |
374 case WM_SIZING: |
| |
375 return wnd_sizing(data, xevent); |
| |
376 case WM_MOVING: |
| |
377 return wnd_moving(data, xevent); |
| |
378 case WM_SHOWWINDOW: |
| |
379 return wnd_showwindow(data, xevent); |
| |
380 case WM_NCHITTEST: |
| |
381 return wnd_nchittest(data, xevent); |
| |
382 #if 0 |
| |
383 case WM_INITMENUPOPUP: |
| |
384 return wnd_initmenupopup(data, xevent); |
| |
385 #endif |
| |
386 case WM_SIZE: |
| |
387 return wnd_size(data, xevent); |
| |
388 case APPBAR_CALLBACK: |
| |
389 return gtk_appbar_callback(data, xevent); |
| |
390 default: |
| |
391 } |
| |
392 return GDK_FILTER_CONTINUE; |
| |
393 } |
| |
394 |
| |
395 void gtk_appbar_dock(GtkAppBar *ab, UINT side) { |
| |
396 RECT orig; |
| |
397 |
| |
398 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "gtk_appbar_dock\n"); |
| |
399 |
| |
400 if(!ab || !IsWindow(GDK_WINDOW_HWND(ab->win->window))) |
| |
401 return; |
| |
402 |
| |
403 ab->side = side; |
| |
404 get_window_normal_rc(GDK_WINDOW_HWND(ab->win->window), &(ab->docked_rect)); |
| |
405 CopyRect(&orig, &(ab->docked_rect)); |
| |
406 print_rect(&(ab->docked_rect)); |
| |
407 gtk_appbar_querypos(ab, GDK_WINDOW_HWND(ab->win->window), &(ab->docked_rect)); |
| |
408 if(EqualRect(&orig, &(ab->docked_rect)) == 0) |
| |
409 MoveWindow(GDK_WINDOW_HWND(ab->win->window), |
| |
410 ab->docked_rect.left, |
| |
411 ab->docked_rect.top, |
| |
412 ab->docked_rect.right - ab->docked_rect.left, |
| |
413 ab->docked_rect.bottom - ab->docked_rect.top, TRUE); |
| |
414 gtk_appbar_setpos(ab, GDK_WINDOW_HWND(ab->win->window)); |
| |
415 set_toolbar(GDK_WINDOW_HWND(ab->win->window), TRUE); |
| |
416 ab->docked = TRUE; |
| |
417 } |
| |
418 |
| |
419 GtkAppBar *gtk_appbar_add(GtkWidget *win) { |
| |
420 GtkAppBar *ab; |
| |
421 |
| |
422 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "gtk_appbar_add\n"); |
| |
423 |
| |
424 if(!win) |
| |
425 return NULL; |
| |
426 ab = g_new0(GtkAppBar, 1); |
| |
427 ab->win = win; |
| |
428 |
| |
429 /* init docking coords */ |
| |
430 get_window_normal_rc(GDK_WINDOW_HWND(win->window), &(ab->docked_rect)); |
| |
431 |
| |
432 /* Add main window filter */ |
| |
433 gdk_window_add_filter(win->window, |
| |
434 gtk_appbar_event_filter, |
| |
435 ab); |
| |
436 return ab; |
| |
437 } |
| |
438 |
| |
439 void gtk_appbar_remove(GtkAppBar *ab) { |
| |
440 gaim_debug(GAIM_DEBUG_INFO, "gtkappbar", "gtk_appbar_remove\n"); |
| |
441 |
| |
442 if(!ab) |
| |
443 return; |
| |
444 gdk_window_remove_filter(ab->win->window, |
| |
445 gtk_appbar_event_filter, |
| |
446 ab); |
| |
447 if(ab->docked) { |
| |
448 gtk_window_resize(GTK_WINDOW(ab->win), |
| |
449 ab->docked_rect.right - ab->docked_rect.left, |
| |
450 ab->undocked_height); |
| |
451 set_toolbar(GDK_WINDOW_HWND(ab->win->window), FALSE); |
| |
452 } |
| |
453 gtk_appbar_unregister(ab, GDK_WINDOW_HWND(ab->win->window)); |
| |
454 |
| |
455 g_free(ab); |
| |
456 } |