Rev 29187 | Blame | Last modification | View Log | Download | RSS feed
/** GraphApp - Cross-Platform Graphics Programming Library.** File: windows.c -- manipulating on-screen windows.* Platform: Windows Version: 2.35 Date: 1998/03/03** Version: 1.00 Changes: Original version by Lachlan Patrick.* Version: 1.50 Changes: New rectangle type implemented.* Version: 2.00 Changes: New object class system implemented.* Version: 2.15 Changes: Windows are White by default.* Version: 2.30 Changes: Now uses atexit for mainloop.*//* Copyright (C) 1993-1998 Lachlan PatrickThis file is part of GraphApp, a cross-platform C graphics library.GraphApp is free software; you can redistribute it and/or modify itunder the terms of the GNU Library General Public License.GraphApp is distributed in the hope that it will be useful, butWITHOUT ANY WARRANTY.See the file COPYLIB.TXT for details.*/#include "internal.h"/** A brief explanation of how Windows handles windows, and how* we make it all work here:** In MS-Windows, a window is a rectangular area on the screen, which* has a winproc and a variety of properties. There are many kinds* of windows, from free-standing application-controlled windows,* to buttons, and scrollbars, to windows within windows (called* MDI windows, Multiple Document Interface).** What we are trying to do is make free-standing windows, and* MDI windows appear as one data type. There is a problem in* doing this within Windows. MDI windows are manipulated in* quite different ways to normal windows. To create MDI windows* one must first create a "Frame window", then create a "Client* window" which sits within the frame window and passes messages* between the application and the MDI windows. The MDI windows* are special kinds of windows which sit on the surface of the* client window.** Our newwindow() function is hence very complex, because it must* handle these three different sorts of windows: normal windows,* "Frame windows" also known as Workspace windows, and "MDI child* windows" also known as Document windows.** The newwindow() function looks at the window creation bit-field* it is given, and determines what sort of window to create and* whether previous windows of this type have been created, since* Windows demands that before a certain kind of window is created* it must first be "registered" using the RegisterClass function.** Another problem we have is in handling Modal windows (windows* which gain exclusive access to user input when they are visible).* We wish to provide the illusion that modal windows are just* another kind of window which can be created, in other words,* modality is a property of a window, not a property of how that* window is manipulated. This is tricky to achieve under Windows.*//** Windows-version library variables.*/PROTECTED window current_window = NULL;PROTECTED int active_windows = 0;PROTECTED int child_id = MinChildID;/** Handles to special windows.*/PROTECTED HWND hwndMain = 0; /* normal window */PROTECTED HWND hwndClient = 0; /* MDI client window */PROTECTED HWND hwndFrame = 0; /* MDI frame window */PROTECTED window MDIFrame=0, MDIToolbar=0;PROTECTED HWND MDIStatus = 0;/** System screen dimensions.*/#define screen_dx (GetSystemMetrics(SM_CXSCREEN))#define screen_dy (GetSystemMetrics(SM_CYSCREEN))#define titlebar_height (GetSystemMetrics(SM_CYCAPTION)-1)#define menubar_height (GetSystemMetrics(SM_CYMENU)+1)#define border_width (GetSystemMetrics(SM_CXBORDER))#define border_height (GetSystemMetrics(SM_CYBORDER))#define frame_width (GetSystemMetrics(SM_CXFRAME))#define frame_height (GetSystemMetrics(SM_CYFRAME))#define dlgframe_width (GetSystemMetrics(SM_CXDLGFRAME))#define dlgframe_height (GetSystemMetrics(SM_CYDLGFRAME))#define scrollbar_height (GetSystemMetrics(SM_CYHSCROLL))#define scrollbar_width (GetSystemMetrics(SM_CXVSCROLL))/** Windows class names.*/static char * win_class_name = NULL;static char * mdi_class_name = NULL;static char * work_class_name = NULL;/** Private variables.*/static int mainloop_started = 0;/** Find the screen co-ordinates of an object.*/PROTECTEDrect screen_coords(object obj){rect r;POINT p;r = getrect(obj);if (! obj->handle)return r;p.x = p.y = 0;ClientToScreen(obj->handle, &p);r.x = p.x;r.y = p.y;return r;}/** Fix an ordinary window's System Menu to reflect it's window style.* This entails removing unnecessary commands such as Minimize etc.*/static void fix_sys_menu(HWND hwnd, unsigned long win_style){HMENU sys_menu;sys_menu = GetSystemMenu(hwnd, FALSE);if ((! (win_style & WS_MINIMIZEBOX)) &&(! (win_style & WS_MAXIMIZEBOX))){ /* remove the separator above and below Close */RemoveMenu(sys_menu, 7, MF_BYPOSITION);RemoveMenu(sys_menu, 5, MF_BYPOSITION);/* also remove Restore and Task Swap commands */RemoveMenu(sys_menu, SC_RESTORE, MF_BYCOMMAND);RemoveMenu(sys_menu, SC_TASKLIST, MF_BYCOMMAND);}/* remove the un-needed commands */if (! (win_style & WS_MINIMIZEBOX))RemoveMenu(sys_menu, SC_MINIMIZE, MF_BYCOMMAND);if (! (win_style & WS_MAXIMIZEBOX))RemoveMenu(sys_menu, SC_MAXIMIZE, MF_BYCOMMAND);if (! (win_style & WS_THICKFRAME))RemoveMenu(sys_menu, SC_SIZE, MF_BYCOMMAND);}/** Make an MDI client window.*/static void make_client_window(HWND hwnd){CLIENTCREATESTRUCT clientcreate;clientcreate.hWindowMenu = 0;clientcreate.idFirstChild = MinDocID;hwndClient =CreateWindow("MDICLIENT", NULL,WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE| WS_HSCROLL | WS_VSCROLL| WS_CLIPSIBLINGS| MDIS_ALLCHILDSTYLES,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,hwnd, 0 , this_instance,(void *) & clientcreate);}/** Register a class based on an app_name extension string and function.* Return the new class name.*/static char *register_new_class(char *extra, WNDPROC proc){WNDCLASS wndclass;int length;char *new_class_name;HICON AppIcon;length = strlen(app_name)+strlen(extra)+1;new_class_name = array (length, char);strcpy(new_class_name, app_name);strcat(new_class_name, extra);if (! prev_instance){if ((AppIcon = LoadIcon(this_instance,app_name)) == 0)AppIcon = LoadIcon(0, IDI_APPLICATION);wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS;wndclass.lpfnWndProc = proc;wndclass.cbClsExtra = 0;wndclass.cbWndExtra = 0;wndclass.hInstance = this_instance;wndclass.hIcon = AppIcon;wndclass.hCursor = 0;wndclass.hbrBackground = 0;wndclass.lpszMenuName = NULL;wndclass.lpszClassName = new_class_name;RegisterClass (&wndclass) ;}return new_class_name;}static HWND new_mdi_window(char *name, rect r, unsigned long sty){HWND hwnd;MDICREATESTRUCT mdi;if (! hwndClient)return NULL;if (! mdi_class_name)mdi_class_name = register_new_class(" Document", app_doc_proc);mdi.szClass = mdi_class_name;mdi.szTitle = name;mdi.hOwner = this_instance;mdi.x = r.x;mdi.y = r.y;mdi.cx = r.width;mdi.cy = r.height;mdi.style = sty;mdi.lParam = 0;hwnd = (HWND) sendmessage(hwndClient, WM_MDICREATE, 0,(long) (LPMDICREATESTRUCT) &mdi);return hwnd;}/** The fixwinrect function returns the rectangle to be used in the* call to CreateWindow. It adjusts the supplied rectangle to* account for the existence of titlebars, menubars, window borders* etc.** The rectangle passed to this function is the actual area on screen* which the programmer wants available for drawing. So this function* will usually make this rectangle bigger to include the various* window features.** The rectangle can also have negative values in its coordinates,* in which case the coordinates are taken from the edge of the* screen. I.e. a rectangle (-4,-4,-4,-4) will create a window which* is 4 pixels in from the edge of the screen all the way around.*/static rect fix_win_rect(rect r, long flags){rect win_rect;rect max_rect;win_rect = r;if ((r.width==0)||(r.height==0))return rect(CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT);/* Find out the maximum rectangle we are allowed. */if ((flags & Document) && (hwndClient)) {GetClientRect(hwndClient, (RECT *) &max_rect);}else if ((flags & ChildWindow) && (current_window)) {GetClientRect(current_window->handle, (RECT *) &max_rect);}else {max_rect = rect(0,0,screen_dx,screen_dy);}if (flags & Resize) {win_rect = growr(win_rect, frame_width, frame_height);}else if (flags & Border) {win_rect = growr(win_rect, dlgframe_width, dlgframe_height);}else if (!(flags & ChildWindow)) {win_rect = growr(win_rect, border_width, border_height);}if (flags & HScrollbar)win_rect.height += scrollbar_height;if (flags & VScrollbar)win_rect.width += scrollbar_width;if (flags & Titlebar)win_rect.height += titlebar_height;if ((flags & Menubar) && !(flags & Document))win_rect.height += menubar_height;if (flags & Centered) {win_rect = rcenter(win_rect, max_rect);r = win_rect;}if (r.x <= 0)win_rect.x = max_rect.x - r.x;if (r.y <= 0)win_rect.y = max_rect.y - r.y;if (r.width <= 0)win_rect.width = max_rect.width + r.width - win_rect.x;if (r.height <= 0)win_rect.height = max_rect.height + r.height - win_rect.y;return rcanon(win_rect);}/** Fix the window flag and style variables.*/static void fix_win_style(long *flags_ptr, long *state_ptr,unsigned long *style_ptr){long flags = *flags_ptr;long state = *state_ptr;unsigned long style = 0UL;state |= Enabled;if (flags & Workspace)style |= (MDIS_ALLCHILDSTYLES|WS_CLIPCHILDREN|WS_OVERLAPPED);/*if (flags & Document)flags |= StandardWindow;*/if (flags & Resize)style |= WS_THICKFRAME;else if (flags & Border)style |= (WS_DLGFRAME|DS_3DLOOK);else if (!(flags & ChildWindow))style |= WS_BORDER;if (flags & HScrollbar)style |= WS_HSCROLL;if (flags & VScrollbar)style |= WS_VSCROLL;if (flags & Titlebar) {style |= WS_CAPTION;if (flags & Maximize)style |= WS_MAXIMIZEBOX;if (flags & Minimize)style |= WS_MINIMIZEBOX;if (flags & Closebox)style |= WS_SYSMENU;} else if (!(flags & ChildWindow)) {style |= WS_POPUP;flags &= ~(Maximize | Minimize | Closebox);}if (flags & ChildWindow) {style |= (WS_CHILD | WS_VISIBLE);state |= Visible;}if (state & Visible) {style |= WS_VISIBLE;}*flags_ptr = flags;*state_ptr = state;*style_ptr = style;}/** Private window destructor.*/static void private_delwindow(window obj){if (obj->call) /* Forced termination - no user interference! */if (obj->call->close)obj->call->close = NULL;if (isvisible(obj))hide(obj);if (obj->flags & Document)sendmessage(hwndClient, WM_MDIDESTROY, obj->handle, 0L);elseDestroyWindow(obj->handle);}/** Private object constructor.*/static object new_window_object(HWND hwnd, char *name, rect r,long flags, long state){object obj;obj = new_object(WindowObject, hwnd,(flags & ChildWindow) ? current_window : NULL);if (obj) {obj->rect = r;obj->flags = flags;obj->state = state;obj->bg = White;obj->die = private_delwindow;obj->text = new_string(name);obj->drawstate = copydrawstate();obj->drawstate->dest = obj;if (flags & ChildWindow)obj->id = child_id++;elsecurrent_window = obj;}return obj;}/** Create and return a new window.*/static int MDIsizeSet=0;window newwindow(char *name, rect r, long flags){object obj;HWND hwnd;unsigned long win_style;unsigned long ex_style;long state = flags & 0x0F;initapp(0,0);if ((flags & Document) && (! hwndClient))flags &= ~Document;if ((flags & Menubar) && (flags & Document))flags &= ~Menubar;if (flags & Workspace && r.width != 0) MDIsizeSet = 1;fix_win_style(&flags, &state, &win_style);r = fix_win_rect(r, flags);ex_style = 0L; /* extended style */if (flags & Floating)ex_style |= WS_EX_TOPMOST;if (flags & Modal)ex_style |= WS_EX_DLGMODALFRAME;if (flags & Document) {hwnd = new_mdi_window(name, r, win_style);}else {if ((flags & Workspace) && (! work_class_name)) {work_class_name = register_new_class(" Workspace", app_work_proc);}else if (! win_class_name)win_class_name = register_new_class("", app_win_proc);hwnd = CreateWindowEx (ex_style,(flags & Workspace) ?work_class_name : win_class_name,name, win_style,r.x, r.y, r.width, r.height,(HWND) ((flags & ChildWindow) ?current_window->handle : 0),((HMENU) ((flags & ChildWindow) ? child_id : 0)),this_instance, NULL);}if (! hwnd)return NULL;if (flags & Closebox)fix_sys_menu(hwnd, win_style);obj = new_window_object(hwnd, name, r, flags, state);if (flags & Workspace) {make_client_window(hwnd);if (!hwndClient) {del(obj);return NULL;}MDIFrame = obj;hwndFrame = hwnd;}return (window) obj;}/** Find the next valid window, start looking from the given one.*/PROTECTEDobject find_valid_sibling(object obj){object first = obj;if (! obj)return NULL;do {if ((obj->kind & ControlObject)&& (isenabled(obj)) && (isvisible(obj))){if (obj->kind != LabelObject)return obj;}obj = obj->next;} while (obj != first);return first;}/** Set focus to an object. It must be visible and enabled.*/static void select_sibling(object obj){obj = find_valid_sibling(obj);if (obj) {if (obj->flags & Document)sendmessage(hwndClient, WM_MDIACTIVATE,obj->handle, 0L);elseSetFocus(obj->handle);}}/** Disable every other window except the supplied modal window.* If the supplied window is not modal, do nothing.** We accomplish this by stepping through the entire list of* registered windows and disable all the visible ones.*/static void disable_one_window(window obj){if ((obj->kind == WindowObject) && (isvisible(obj)))disable(obj);}static void disablewindows(object obj){/* If this window is not modal, do nothing. */if (!(obj->flags & Modal))return;/* Otherwise, move the window to the front of the list. */move_to_front(obj);/* And disable the ones behind it. */if (obj != obj->next)apply_to_list(obj->next, disable_one_window);/* We must have a modal window if we've gotten this far. */menus_active = 0; /* Can't use menus in modal windows. */}/** When you dismiss a modal window it should reactivate all the* windows behind it, unless the first visible window behind it is* a modal window too. In that case, hiding the frontmost modal* window should only reactivate the second modal window.*/static void enable_one_window(window obj){if ((obj->kind == WindowObject) && (isvisible(obj)))enable(obj);}static void enablewindows(object obj){object next;/* if this window is not modal, we do nothing */if (!(obj->flags & Modal))return;/* Find next window in the list, if any. */for (next=obj->next; next!=obj; next=next->next)if ((next->kind == WindowObject) && (isvisible(next)))break;if (next != obj) {if (next->flags & Modal) {enable(next);return;}elseapply_to_list(next, enable_one_window);}/* Enable menus again, unless we have a modal window again. */menus_active = 1;}static int is_top_level_window(window w){if (w->kind != WindowObject) return 0;if (w->flags & Document) return 0;if (w->flags & ChildWindow) return 0;return 1;}static int MDIFrameFirstTime = 1;PROTECTEDvoid show_window(object obj){HWND hwnd = obj->handle;int incremented_aw = 0;if (! mainloop_started) {mainloop_started = 1;atexit(app_cleanup);atexit(gamainloop);}if (! IsWindowVisible(hwnd)){/* Disable windows behind a modal window. */disablewindows(obj);/* Remember how many real windows active. */if (is_top_level_window(obj)) {incremented_aw = 1;active_windows ++ ;}}obj->state |= Visible;if (hwndClient && (hwnd==hwndFrame) && (MDIFrameFirstTime)) {ShowWindow(hwnd, MDIsizeSet ? SW_SHOWNORMAL : SW_SHOWMAXIMIZED);MDIFrameFirstTime = 0;}elseShowWindow(hwnd, SW_SHOWNORMAL);/* workaround for Show bug */if (incremented_aw && !IsWindowVisible(hwnd) ) active_windows -- ;if (obj->menubar) {if (hwndClient) {menu mdi = (obj->menubar)->menubar;SendMessage(hwndClient, WM_MDISETMENU,(WPARAM)obj->menubar->handle,(LPARAM)(mdi?(mdi->handle):0));DrawMenuBar(hwndFrame);}elseDrawMenuBar(hwnd);}if (obj->toolbar) {if (MDIToolbar) hide(MDIToolbar);MDIToolbar = obj->toolbar;SendMessage(hwndFrame,WM_PAINT,(WPARAM) 0,(LPARAM) 0);}SetFocus(hwnd);UpdateWindow(hwnd);select_sibling(obj->child);}PROTECTEDvoid hide_window(object obj){HWND hwnd = obj->handle;if (IsWindowVisible(hwnd)){del_context(obj);/* Re-enable any disabled windows. */enablewindows(obj);if (obj->flags & Document)sendmessage(hwndClient, WM_MDIRESTORE, hwnd, 0L);/* Remember how many real windows active. */if (is_top_level_window(obj))active_windows -- ;ShowWindow(hwnd, SW_HIDE);obj->state &= ~Visible;}if (current->dest == obj) {current->dest = NULL;del_context(obj);}}int ismdi() {return (hwndClient!=NULL);}int isiconic(window w) {return IsIconic(w->handle);}PROTECTEDrect GetCurrentWinPos(object obj){rect r;WINDOWPLACEMENT W;if (! obj || obj->kind != WindowObject) return rect(0,0,0,0);W.length = sizeof(WINDOWPLACEMENT);GetWindowPlacement(obj->handle, &W);r.x = W.rcNormalPosition.left;r.y = W.rcNormalPosition.top;r.width = W.rcNormalPosition.right - r.x;r.height = W.rcNormalPosition.bottom - r.y;return r;}