/**
 * @file   Window.cc
 * @author David Reveman <david@waimea.org>
 * @date   02-May-2001 21:43:03
 *
 * @brief Implementation of WaWindow and WaChildWindow classes
 *
 * An instance if this class manages one window. Contains functions for
 * creating window decorations, reading window hints and controlling the
 * window.
 *
 * Copyright (C) David Reveman. All rights reserved.
 *
 */

#ifdef    HAVE_CONFIG_H
#  include "../config.h"
#endif // HAVE_CONFIG_H

extern "C" {
#ifdef    HAVE_STDIO_H
#  include <stdio.h>
#endif // HAVE_STDIO_H

#ifdef    STDC_HEADERS
#  include <string.h>
#endif // STDC_HEADERS
}

#include "Window.hh"

/**
 * @fn    WaWindow(Window win_id, WaScreen *scrn) :
 *        WindowObject(win_id, WindowType)
 * @brief Constructor for WaWindow class
 *
 * Reparents the window, reads window hints and creates decorations ...
 *
 * @param win_id Resource ID of window to manage
 * @param scrn The screen the window should be displayed on
 */
WaWindow::WaWindow(Window win_id, WaScreen *scrn) :
    WindowObject(win_id, WindowType) {
    XWindowAttributes init_attrib;
    char *__m_wastrdup_tmp;

    id = win_id;
    wascreen = scrn;
    display = wascreen->display;
    screen_number = wascreen->screen_number;
    waimea = wascreen->waimea;
    ic = wascreen->ic;
    net = waimea->net;
    wm_strut = NULL;
    move_resize = false;
    classhint = NULL;
    name = __m_wastrdup("");

    XGrabServer(display);
    if (validatedrawable(id))
        XGetWindowAttributes(display, id, &init_attrib);
    else deleted = true;
    XUngrabServer(display);
    
    attrib.colormap = init_attrib.colormap;
    size.win_gravity = init_attrib.win_gravity;
    attrib.x = init_attrib.x;
    attrib.y = init_attrib.y;
    attrib.width  = init_attrib.width;
    attrib.height = init_attrib.height;
    
    want_focus = mapped = dontsend = deleted = ign_config_req = hidden = false;

    desktop_mask = (1L << wascreen->current_desktop->number);

#ifdef SHAPE
    shaped = false;
#endif //SHAPE

#ifdef RENDER
    render_if_opacity = false;
#endif // RENDER
    
    border_w = title_w = handle_w = 0;
    has_focus = false;
    flags.sticky = flags.shaded = flags.max = flags.title = flags.handle =
        flags.border = flags.all = flags.alwaysontop =
        flags.alwaysatbottom = flags.forcedatbottom = false;
    flags.focusable = flags.tasklist = true;
    frameacts = awinacts = pwinacts = titleacts = labelacts = handleacts =
        lgacts = rgacts = NULL;
    transient_for = (Window) 0;
    host = pid = NULL;
    
    int i;
    bacts = new list<WaAction *>*[wascreen->wstyle.b_num];
    for (i = 0; i < wascreen->wstyle.b_num; i++)
        bacts[i] = NULL;

    net->GetWMHints(this);
    net->GetMWMHints(this);
    net->GetWMNormalHints(this);
    net->GetWmPid(this);
    
    Gravitate(ApplyGravity);
    InitPosition();
    
    frame = new WaChildWindow(this, wascreen->id, FrameType);
    handle = new WaChildWindow(this, frame->id, HandleType);
    grip_l = new WaChildWindow(this, frame->id, LGripType);
    grip_r = new WaChildWindow(this, frame->id, RGripType);
    title = new WaChildWindow(this, frame->id, TitleType);

    WaChildWindow *button;
    int left_end = 2;
    int right_end = -2;
    int tw = (signed) wascreen->wstyle.title_height;
    list<ButtonStyle *>::iterator bit =
        wascreen->wstyle.buttonstyles.begin();
    for (i = 0; bit != wascreen->wstyle.buttonstyles.end(); ++bit, ++i) {
        button = new WaChildWindow(this, title->id, ButtonType);
        button->bstyle = *bit;
        button->f_texture = &(*bit)->t_focused;
        button->u_texture = &(*bit)->t_unfocused;
        if ((*bit)->autoplace == WestType)
            button->g_x = left_end;
        else if ((*bit)->autoplace == EastType)
            button->g_x = right_end;
        else button->g_x = (*bit)->x;
        
        if (button->g_x > 0 &&
            (button->g_x + (tw - 2)) > left_end)
            left_end = button->g_x + (tw - 2);
        else if ((button->g_x - (tw - 2)) < right_end)
            right_end = button->g_x - (tw - 2);
        
        buttons.push_back(button);
    }
    label = new WaChildWindow(this, title->id, LabelType);
    label->g_x = left_end + 2;
    label->g_x2 = right_end - 2;

    if (deleted) { delete this; return; }

    net->GetWmState(this);
    net->GetWmType(this);
    net->GetVirtualPos(this);
    net->GetWmStrut(this);
    net->GetDesktop(this);
    net->SetDesktop(this);
    net->SetDesktopMask(this);
    
    ReparentWin();
    if (! net->GetNetName(this)) net->GetXaName(this);
    if (*name == '\0') SetActionLists();
    UpdateGrabs();

#ifdef SHAPE
    Shape();
#endif // SHAPE

    if (deleted) { delete this; return; }
    
    UpdateAllAttributes();

    if (flags.shaded) Shade(NULL, NULL);
    
    waimea->window_table.insert(make_pair(id, this));
    wascreen->wawindow_list.push_back(this);
    wascreen->wawindow_list_map_order.push_back(this);
    if ((! flags.alwaysontop) && (! flags.alwaysatbottom) &&
        (! flags.forcedatbottom))
        wascreen->wa_list_stacking.push_back(this);

    if (deleted) delete this;
}

/**
 * @fn    ~WaWindow(void) 
 * @brief Destructor of WaWindow class
 *
 * Reparents the window back to the root window if it still exists. Destroys
 * all windows used for decorations.
 */
WaWindow::~WaWindow(void) {
    waimea->window_table.erase(id);

    if (transient_for) {
        if (transient_for == wascreen->id) {
            list<WaWindow *>::iterator it =
                wascreen->wawindow_list.begin();
            for (;it != wascreen->wawindow_list.end(); ++it)
                (*it)->transients.remove(id);
        }
        else {
            map<Window, WindowObject *>::iterator hit;
            if ((hit = waimea->window_table.find(transient_for)) !=
                waimea->window_table.end()) {
                if (((*hit).second)->type == WindowType)
                    ((WaWindow *) (*hit).second)->transients.remove(id);
            }
        }
    }
    
    XGrabServer(display);
    if (validatedrawable(id) && validateclient_mapped(id)) {
        XRemoveFromSaveSet(display, id);
        Gravitate(RemoveGravity);
        if (flags.shaded) attrib.height = restore_shade;
        if (attrib.x >= wascreen->width)
            attrib.x = attrib.x % wascreen->width;
        if (attrib.y >= wascreen->height)
            attrib.y = attrib.y % wascreen->height;

        if (attrib.x + attrib.width <= 0)
            attrib.x = wascreen->width + (attrib.x % wascreen->width);
        if (attrib.y + attrib.height <= 0)
            attrib.y = wascreen->height + (attrib.y % wascreen->height);

        XReparentWindow(display, id, wascreen->id, attrib.x, attrib.y);
    }
    XUngrabServer(display);     

    list<WaChildWindow *>::iterator bit = buttons.begin();
    for (; bit != buttons.end(); ++bit)
        delete *bit;
    
    delete grip_l;
    delete grip_r;
    delete handle;
    delete label;
    delete title;
    
    if (name) delete [] name;
    if (host) delete [] host;
    if (pid) delete [] pid;
    if (classhint && classhint->res_name) XFree(classhint->res_name);
    if (classhint && classhint->res_class) XFree(classhint->res_class);

    delete [] bacts;

    wascreen->wawindow_list.remove(this);
    wascreen->wawindow_list_map_order.remove(this);
    wascreen->wa_list_stacking.remove(this);
    if (flags.alwaysontop)
        wascreen->wawindow_list_stacking_aot.remove(this);
    if (flags.forcedatbottom) {
        wascreen->always_at_bottom_list.remove(frame->id);
    } else if (flags.alwaysatbottom)
        wascreen->wawindow_list_stacking_aab.remove(this);
    if (wm_strut) {
        wascreen->strut_list.remove(wm_strut);
        delete wm_strut;
        if (! wascreen->shutdown) wascreen->UpdateWorkarea();
    }
    
    delete frame;

    if (! wascreen->shutdown) {
        wascreen->net->SetClientList(wascreen);
        wascreen->net->SetClientListStacking(wascreen);
        if (! wascreen->wawindow_list.empty())
            wascreen->wawindow_list.front()->Focus(false);
    }
}

/**
 * @fn    GetActionList(list<WaActionExtList *> *e)
 * @brief Finds individual action list
 *
 * Matches windows class name, class and title with WaActionExtLists. Returns
 * WaActionExtLists action list if a match is found. If no match is found,
 * NULL is returned.
 *
 * @param e List with WaActionExtLists to try to match with
 */
list <WaAction *> *WaWindow::GetActionList(list<WaActionExtList *> *e) {
    list<WaActionExtList *>::iterator it;
    for (it = e->begin(); it != e->end(); ++it) {
        if (classhint) {
            if (classhint->res_name &&
                (*it)->name->Match(classhint->res_name))
                return &((*it)->list);
            else if (classhint->res_class &&
                     (*it)->cl->Match(classhint->res_class))
                return &((*it)->list);
        }
        if ((*it)->title->Match(name))
            return &((*it)->list);
    }
    return NULL;
}

/**
 * @fn    SetActionLists(void)
 * @brief Set all actions lists
 *
 * Updates all action lists for the window.
 */
void WaWindow::SetActionLists(void) {
    if (has_focus) {
        actionlist = GetActionList(&wascreen->config.ext_awinacts);
        if (! actionlist) actionlist = &wascreen->config.awinacts;
    }
    else {
        actionlist = GetActionList(&wascreen->config.ext_pwinacts);
        if (! actionlist) actionlist = &wascreen->config.pwinacts;
    }
    frame->actionlist = GetActionList(&wascreen->config.ext_frameacts);
    if (! frame->actionlist) frame->actionlist = &wascreen->config.frameacts;
    title->actionlist = GetActionList(&wascreen->config.ext_titleacts);
    if (! title->actionlist) title->actionlist = &wascreen->config.titleacts;
    label->actionlist = GetActionList(&wascreen->config.ext_labelacts);
    if (! label->actionlist) label->actionlist = &wascreen->config.labelacts;
    handle->actionlist = GetActionList(&wascreen->config.ext_handleacts);
    if (! handle->actionlist)
        handle->actionlist = &wascreen->config.handleacts;
    grip_l->actionlist = GetActionList(&wascreen->config.ext_lgacts);
    if (! grip_l->actionlist)
        grip_l->actionlist = &wascreen->config.lgacts;
    grip_r->actionlist = GetActionList(&wascreen->config.ext_rgacts);
    if (! grip_r->actionlist) grip_r->actionlist = &wascreen->config.rgacts;

    int i;
    list<WaChildWindow *>::iterator it = buttons.begin();
    for (i = 0; it != buttons.end(); ++it, ++i) {
        (*it)->actionlist = GetActionList(wascreen->config.ext_bacts[i]);
        if (! (*it)->actionlist)
            (*it)->actionlist = wascreen->config.bacts[i];
    }
}

/**
 * @fn    Gravitate(int multiplier)
 * @brief Applies or removes window gravity
 *
 * If multiplier parameter is RemoveGravity when we set the window
 * attributes so that the frame will match the windows position.
 * If multiplier parameter is ApplyGravity when we set the window
 * attributes so that the window will match the frames position.
 *
 * @param multiplier ApplyGravity or RemoveGravity
 */
void WaWindow::Gravitate(int multiplier) {
    switch (size.win_gravity) {
        case NorthWestGravity:
            attrib.x += multiplier * border_w * 2;
        case NorthEastGravity:
            attrib.x -= multiplier * border_w;
        case NorthGravity:
            attrib.y += multiplier * border_w;
            if (title_w) attrib.y += multiplier * (title_w + border_w);
            break;
        case SouthWestGravity:
            attrib.x += multiplier * border_w * 2;
        case SouthEastGravity:
            attrib.x -= multiplier * border_w;
        case SouthGravity:
            attrib.y -= multiplier * border_w;
            if (handle_w) attrib.y -= multiplier * (handle_w + border_w);
            break;
        case CenterGravity:
            attrib.x += multiplier * (border_w / 2);
            attrib.y += multiplier * (border_w / 2);
            if (title_w) attrib.y += multiplier * ((title_w + border_w) / 2);
            break;
        case StaticGravity:
            break;
    }
}

/**
 * @fn    InitPosition(void)
 * @brief Sets window position
 *
 * Initializes position for the window.
 */
void WaWindow::InitPosition(void) {
    if (size.min_width > attrib.width) attrib.width = size.min_width;
    if (size.min_height > attrib.height) attrib.height = size.min_height;
    restore_max.x = attrib.x;
    restore_max.y = attrib.y;
    restore_max.width = attrib.width;
    restore_shade = restore_max.height = attrib.height;
    restore_max.misc0 = restore_max.misc1 = 0;
    old_attrib.x = old_attrib.y = old_attrib.height = old_attrib.width =
        - 0xffff;
}

/**
 * @fn    MapWindow(void)
 * @brief Map window
 *
 * Map client window and all child windows.
 */
void WaWindow::MapWindow(void) {
    XGrabServer(display);
    if (validatedrawable(id)) {
        XMapWindow(display, id);
        RedrawWindow();
    } else DELETED;
    XUngrabServer(display);
    if (flags.handle) {
        XMapRaised(display, grip_l->id);
        XMapRaised(display, handle->id);
        XMapRaised(display, grip_r->id);
    } else {
        XUnmapWindow(display, grip_l->id);
        XUnmapWindow(display, handle->id);
        XUnmapWindow(display, grip_r->id);
    }
    if (flags.title) {
        XMapRaised(display, title->id);
        XMapRaised(display, label->id);
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            XMapRaised(display, (*bit)->id);
    } else {
        XUnmapWindow(display, title->id);
        XUnmapWindow(display, label->id);
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            XUnmapWindow(display, (*bit)->id);
    }
    if (desktop_mask & (1L << wascreen->current_desktop->number))
        XMapWindow(display, frame->id);
    else {
        hidden = true;
    }
    mapped = true;
}

/**
 * @fn    Show(void)
 * @brief Show window
 *
 * Map frame window if not mapped.
 */
void WaWindow::Show(void) {
    if (hidden && mapped) {
        XMapWindow(display, frame->id);
        hidden = false;
    }
}

/**
 * @fn    Hide(void)
 * @brief Hide window
 *
 * Unmap frame window if mapped.
 */
void WaWindow::Hide(void) {
    if (! hidden) {
        XUnmapWindow(display, frame->id);
        hidden = true;
    }
}

/**
 * @fn    UpdateAllAttributes(void)
 * @brief Updates all window attrubutes
 *
 * Updates all positions and sizes of all windows in the frame.
 */
void WaWindow::UpdateAllAttributes(void) {
    Gravitate(RemoveGravity);
    border_w = (flags.border * wascreen->wstyle.border_width);
    title_w  = (flags.title  * wascreen->wstyle.title_height);
    handle_w = (flags.handle * wascreen->wstyle.handle_width);
    Gravitate(ApplyGravity);
    
    frame->attrib.x = attrib.x - border_w;
    frame->attrib.y = attrib.y - border_w;
    if (flags.title) frame->attrib.y -= title_w + border_w;
    frame->attrib.width = attrib.width;
    frame->attrib.height = attrib.height;
    if (flags.title) frame->attrib.height += title_w + border_w;
    if (flags.handle) frame->attrib.height += handle_w + border_w;

    XSetWindowBorderWidth(display, frame->id, border_w);
    if (! flags.shaded) 
        XResizeWindow(display, frame->id, frame->attrib.width,
                      frame->attrib.height);
    
    XMoveWindow(display, frame->id, frame->attrib.x, frame->attrib.y);
            
    if (flags.title) {
        title->attrib.x = - border_w;
        title->attrib.y = - border_w;
        title->attrib.width  = attrib.width;
        title->attrib.height = title_w;
        XSetWindowBorderWidth(display, title->id, border_w);
        XMoveResizeWindow(display, title->id, title->attrib.x,
                          title->attrib.y, title->attrib.width,
                          title->attrib.height);
                
        label->attrib.x = label->g_x;
        label->attrib.y = 2;
        label->attrib.width = attrib.width + label->g_x2 - label->g_x;
        if (label->attrib.width < 1) label->attrib.width = 1;
        label->attrib.height = title_w - 4;
        XMoveResizeWindow(display, label->id, label->attrib.x,
                          label->attrib.y, label->attrib.width,
                          label->attrib.height);

        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit) {
            (*bit)->attrib.x = ((*bit)->g_x > 0)? (*bit)->g_x:
                (attrib.width + (*bit)->g_x - (title_w - 4));
            (*bit)->attrib.y = 2;
            (*bit)->attrib.width = title_w - 4;
            (*bit)->attrib.height = title_w - 4;
            XMoveResizeWindow(display, (*bit)->id, (*bit)->attrib.x,
                              (*bit)->attrib.y, (*bit)->attrib.width,
                              (*bit)->attrib.height);
        }
        DrawTitlebar(true);
    }
    if (flags.handle) {
        handle->attrib.x = 25;
        handle->attrib.y = frame->attrib.height - handle_w - border_w;
        handle->attrib.width = attrib.width - 50 -
            border_w * 2;
        if (handle->attrib.width < 1) handle->attrib.width = 1;
        handle->attrib.height = wascreen->wstyle.handle_width;
        XSetWindowBorderWidth(display, handle->id, border_w);
        XMoveResizeWindow(display, handle->id, handle->attrib.x,
                          handle->attrib.y, handle->attrib.width,
                          handle->attrib.height);
                
        grip_l->attrib.x = - border_w;
        grip_l->attrib.y = frame->attrib.height - handle_w - border_w;
        grip_l->attrib.width  = 25;
        grip_l->attrib.height = wascreen->wstyle.handle_width;
        XSetWindowBorderWidth(display, grip_l->id, border_w);
        XMoveResizeWindow(display, grip_l->id, grip_l->attrib.x,
                          grip_l->attrib.y, grip_l->attrib.width,
                          grip_l->attrib.height);
        
        grip_r->attrib.x = attrib.width - 25 - border_w;
        grip_r->attrib.y = frame->attrib.height - handle_w - border_w;
        grip_r->attrib.width  = 25;
        grip_r->attrib.height = wascreen->wstyle.handle_width;
        XSetWindowBorderWidth(display, grip_r->id, border_w);
        XMoveResizeWindow(display, grip_r->id, grip_r->attrib.x,
                          grip_r->attrib.y, grip_r->attrib.width,
                          grip_r->attrib.height);
        DrawHandlebar(true);
    }

    XGrabServer(display);
    if (validatedrawable(id)) {
        if (flags.title) XMoveWindow(display, id, 0, title_w + border_w);
        else XMoveWindow(display, id, 0, title_w);
    } else DELETED;
    XUngrabServer(display);

    int m_x, m_y, m_w, m_h;
    if (flags.max) {
        m_x = restore_max.x;
        m_y = restore_max.y;
        m_w = restore_max.width;
        m_h = restore_max.height;
        flags.max = false;
        _Maximize(restore_max.misc0, restore_max.misc1);
        restore_max.x = m_x;
        restore_max.y = m_y;
        restore_max.width = m_w;
        restore_max.height = m_h;
    }
    else
        RedrawWindow();

#ifdef SHAPE    
    Shape();
#endif // SHAPE
}

/**
 * @fn    RedrawWindow(bool force_if_viewable)
 * @brief Redraws Window
 *
 * Redraws the window at it's correct position with it's correct size.
 * We only redraw those things that need to be redrawn.
 *
 * @param force_if_viewable Force redraw if window is viewable
 */
void WaWindow::RedrawWindow(bool force_if_viewable) {
    Bool move = false, resize = false;

    if (old_attrib.x != attrib.x) {
        frame->attrib.x = attrib.x - border_w;
        
        old_attrib.x = attrib.x;

        move = true;
    }
    if (old_attrib.y != attrib.y) {
        frame->attrib.y = attrib.y - border_w;
        if (flags.title) frame->attrib.y -= title_w + border_w;
        old_attrib.y = attrib.y;
            
        move = true;
    }
    if (old_attrib.width != attrib.width) {
        frame->attrib.width = attrib.width;
        old_attrib.width = attrib.width;

        resize = true;

        if (flags.title) {
            title->attrib.width = attrib.width;
            label->attrib.width = attrib.width + label->g_x2 - label->g_x;
            if (label->attrib.width < 1) label->attrib.width = 1;

            list<WaChildWindow *>::iterator bit = buttons.begin();
            for (; bit != buttons.end(); ++bit) {
                (*bit)->attrib.x = ((*bit)->g_x > 0)? (*bit)->g_x:
                    (attrib.width + (*bit)->g_x - (title_w - 4));
                XMoveResizeWindow(display, (*bit)->id, (*bit)->attrib.x,
                                  (*bit)->attrib.y, (*bit)->attrib.width,
                                  (*bit)->attrib.height);
            }
            XResizeWindow(display, title->id, title->attrib.width,
                          title->attrib.height);
            XResizeWindow(display, label->id, label->attrib.width,
                          label->attrib.height);
            
#ifdef XFT
            if (wascreen->config.db) {
                Region region = XCreateRegion();
                XRectangle xrect;
                xrect.x = label->g_x;
                xrect.y = 2;
                xrect.width = label->attrib.width;
                xrect.height = label->attrib.height;
                XUnionRectWithRegion(&xrect, region, region);
                XftDrawSetClip(title->xftdraw, region);
                XDestroyRegion(region);
            }
#endif // XFT
            
            if (! force_if_viewable) DrawTitlebar();
        }
        if (flags.handle) {
            handle->attrib.width = attrib.width - 50 - border_w * 2;
            if (handle->attrib.width < 1) handle->attrib.width = 1;
            grip_r->attrib.x = attrib.width - 25 - border_w;

            XMoveWindow(display, grip_r->id, grip_r->attrib.x,
                        grip_r->attrib.y);
            XResizeWindow(display, handle->id, handle->attrib.width,
                          handle->attrib.height);
            
            if (! force_if_viewable) DrawHandlebar();
        }
    }
    if (old_attrib.height != attrib.height) {
        frame->attrib.height = attrib.height;
        if (flags.title) frame->attrib.height += title_w + border_w;
        if (flags.handle) frame->attrib.height += handle_w + border_w;
        old_attrib.height = attrib.height;
        
        resize = true;
        if (flags.handle) {
            handle->attrib.y = frame->attrib.height - handle_w - border_w;
            grip_l->attrib.y = frame->attrib.height - handle_w - border_w;
            grip_r->attrib.y = frame->attrib.height - handle_w - border_w;

            XMoveWindow(display, handle->id, handle->attrib.x,
                        handle->attrib.y);
            XMoveWindow(display, grip_l->id, grip_l->attrib.x,
                        grip_l->attrib.y);
            XMoveWindow(display, grip_r->id, grip_r->attrib.x,
                        grip_r->attrib.y);
        }
    }
    if (move) {
        if (flags.max) {
            restore_max.misc0 = wascreen->v_x + frame->attrib.x;
            restore_max.misc1 = wascreen->v_y + frame->attrib.y;
            net->SetWmState(this);
        }
        XMoveWindow(display, frame->id, frame->attrib.x, frame->attrib.y);
        
#ifdef RENDER
        if (! resize && ! force_if_viewable) {
            if (! wascreen->config.lazy_trans) {
                render_if_opacity = true;
                DrawTitlebar();
                DrawHandlebar();
                render_if_opacity = false;
            }
        }
#endif // RENDER

    }
    if (force_if_viewable) {
        DrawTitlebar();
        DrawHandlebar();
    }
    if (resize) {
        if (flags.max && (old_attrib.width != attrib.width ||
                          ! flags.shaded)) {
            flags.max = false;
            net->SetWmState(this);
            if (title_w) {
                list<WaChildWindow *>::iterator bit = buttons.begin();
                for (; bit != buttons.end(); ++bit) {
                    if ((*bit)->bstyle->cb == ShadeCBoxType) (*bit)->Render();
                }
            }
            wascreen->UpdateCheckboxes(MaxCBoxType);
        }
        XGrabServer(display);
        if (validatedrawable(id)) {    
            if (flags.shaded)
                XResizeWindow(display, id, attrib.width, restore_shade);
            else
                XResizeWindow(display, id, attrib.width, attrib.height);
            XResizeWindow(display, frame->id, frame->attrib.width,
                          frame->attrib.height);
        } else DELETED;
        XUngrabServer(display);

#ifdef SHAPE
        Shape();
#endif // SHAPE
        
    }
    if ((move || resize) && (! flags.shaded) && (! dontsend)) {
        
#ifdef RENDER
        if (wascreen->config.lazy_trans) {
            render_if_opacity = true;
            DrawTitlebar();
            DrawHandlebar();
            render_if_opacity = false;
        }
#endif // RENDER

        net->SetVirtualPos(this);
        SendConfig();
    }
}

/**
 * @fn    ReparentWin(void)
 * @brief Reparents the window into the frame
 *
 * Sets the input mask for the managed window so that it will report
 * FocusChange and PropertyChange events. Then we reparent the window into
 * our frame and activates needed grab buttons.
 */
void WaWindow::ReparentWin(void) {
    XSetWindowAttributes attrib_set;
    
    XGrabServer(display);
    if (validatedrawable(id)) {
        XSelectInput(display, id, NoEventMask);
        XSetWindowBorderWidth(display, id, 0);
        XReparentWindow(display, id, frame->id, 0, title_w + border_w);
        XChangeSaveSet(display, id, SetModeInsert);
        XFlush(display);

        attrib_set.event_mask =  
            PropertyChangeMask | StructureNotifyMask | FocusChangeMask;
        attrib_set.do_not_propagate_mask =
            ButtonPressMask | ButtonReleaseMask | ButtonMotionMask; 
        
        XChangeWindowAttributes(display, id, CWEventMask | CWDontPropagate,
                                &attrib_set);
        
        
#ifdef SHAPE
        XRectangle *dummy = NULL;
        int n, order;
        if (waimea->shape) {
            XShapeSelectInput(display, id, ShapeNotifyMask);
            dummy = XShapeGetRectangles(display, id, ShapeBounding, &n,
                                        &order);
            if (n > 1) shaped = true;
        }
        XFree(dummy);
#endif // SHAPE
        
    } else DELETED;
    XUngrabServer(display);
}

/**
 * @fn    UpdateGrabs(void)
 * @brief Update window grabs
 *
 * Updates passive window grabs for the window.
 */
void WaWindow::UpdateGrabs(void) {
    XGrabServer(display);
    if (validateclient_mapped(id)) {
        XUngrabButton(display, AnyButton, AnyModifier, id);
        XUngrabKey(display, AnyKey, AnyModifier, id);
        list<WaAction *>::iterator it = actionlist->begin();
        for (; it != actionlist->end(); ++it) {
            if ((*it)->type == ButtonPress || (*it)->type == ButtonRelease ||
                (*it)->type == DoubleClick) {
                XGrabButton(display, (*it)->detail ? (*it)->detail: AnyButton,
                            AnyModifier, id, true, ButtonPressMask |
                            ButtonReleaseMask | ButtonMotionMask,
                            GrabModeSync, GrabModeSync, None, None);     
            } else if ((*it)->type == KeyPress || (*it)->type == KeyRelease) {
                XGrabKey(display, (*it)->detail ? (*it)->detail: AnyKey,
                         AnyModifier, id, true, GrabModeSync, GrabModeSync);
            }
        }
    } else DELETED;
    XUngrabServer(display);
}


#ifdef SHAPE
/**
 * @fn    Shape(void)
 * @brief Set Shape of frame window
 *
 * Shapes frame window after shape of client.
 */
void WaWindow::Shape(void) {
    int n;
    XRectangle xrect[2];
        
    if (shaped) {
        XGrabServer(display);
        if (validatedrawable(id)) {
            XShapeCombineShape(display, frame->id, ShapeBounding,
                               border_w, title_w + border_w, id,
                               ShapeBounding, ShapeSet);
            n = 0;
            if (title_w) {
                xrect[n].x = -border_w;
                xrect[n].y = -border_w;
                xrect[n].width = attrib.width + border_w * 2;
                xrect[n].height = title_w + border_w * 2;
                n++;
            }
            if (handle_w) {
                xrect[n].x = -border_w;
                xrect[n].y = attrib.height + title_w;
                if (title_w) xrect[n].y += border_w;
                xrect[n].width = attrib.width + border_w * 2;
                xrect[n].height = handle_w + border_w * 2;
                n++;
            }
            XShapeCombineRectangles(display, frame->id, ShapeBounding, 0,
                                    0, xrect, n, ShapeUnion, Unsorted);
        } else DELETED;
        XUngrabServer(display);
    }
}
#endif // SHAPE


/**
 * @fn    SendConfig(void)
 * @brief Sends ConfigureNotify to window
 *
 * Sends a ConfigureNotify event to the window with all the current 
 * window attributes.
 */
void WaWindow::SendConfig(void) {
    XConfigureEvent ce;

    ce.type              = ConfigureNotify;
    ce.event             = id;
    ce.window            = id;
    ce.x                 = attrib.x;
    ce.y                 = attrib.y;
    ce.width             = attrib.width;
    ce.border_width      = 0;
    ce.above             = frame->id;
    ce.override_redirect = false;

    if (flags.shaded)
        ce.height = restore_shade;
    else
        ce.height = attrib.height;

    XGrabServer(display);
    if (validatedrawable(id)) {
        XSendEvent(display, id, false, StructureNotifyMask, (XEvent *) &ce);
        XSendEvent(display, wascreen->id, false, StructureNotifyMask,
                   (XEvent *) &ce);
    }
    else DELETED;
    XUngrabServer(display);
}

/**
 * @fn    CreateOutline(void)
 * @brief Creates window outline
 *
 * Creates four windows used for displaying an outline when doing
 * non opaque moving and resizing of the window.
 */
void WaWindow::CreateOutline(void) {
    XSetWindowAttributes attrib_set;
    
    int create_mask = CWOverrideRedirect | CWBackPixel | CWEventMask |
        CWColormap;
    attrib_set.background_pixel = wascreen->wstyle.outline_color.getPixel();
    attrib_set.colormap = wascreen->colormap;
    attrib_set.override_redirect = true;
    attrib_set.event_mask = NoEventMask;
    
    o_west = XCreateWindow(display, wascreen->id, 0, 0, 1, 1, 0,
                           screen_number, CopyFromParent, wascreen->visual,
                           create_mask, &attrib_set);
    o_east = XCreateWindow(display, wascreen->id, 0, 0, 1, 1, 0,
                           screen_number, CopyFromParent, wascreen->visual,
                           create_mask, &attrib_set);
    o_north = XCreateWindow(display, wascreen->id, 0, 0, 1, 1, 0,
                            screen_number, CopyFromParent, wascreen->visual,
                            create_mask, &attrib_set);
    o_south = XCreateWindow(display, wascreen->id, 0, 0, 1, 1, 0,
                            screen_number, CopyFromParent, wascreen->visual,
                            create_mask, &attrib_set);
    wascreen->always_on_top_list.push_back(o_west);
    wascreen->always_on_top_list.push_back(o_east);
    wascreen->always_on_top_list.push_back(o_north);
    wascreen->always_on_top_list.push_back(o_south);
    XMapWindow(display, o_west);
    XMapWindow(display, o_east);
    XMapWindow(display, o_north);
    XMapWindow(display, o_south);
    wascreen->WaRaiseWindow(0);
}

/**
 * @fn    DestroyOutline(void)
 * @brief Destroys window outline
 *
 * Destorys the four outline windows.
 */
void WaWindow::DestroyOutline(void) {
    wascreen->always_on_top_list.remove(o_west);
    wascreen->always_on_top_list.remove(o_east);
    wascreen->always_on_top_list.remove(o_north);
    wascreen->always_on_top_list.remove(o_south);
    XDestroyWindow(display, o_west);
    XDestroyWindow(display, o_east);
    XDestroyWindow(display, o_north);
    XDestroyWindow(display, o_south);
}

/**
 * @fn    DrawOutline(int x, int y, int width, int height)
 * @brief Draw window outline
 *
 * Draws an outer line for a window with the parameters given.
 * 
 * @param x The x position
 * @param y The y position
 * @param width The width
 * @param height The height
 */
void WaWindow::DrawOutline(int x, int y, int width, int height) {
    int bw = (border_w) ? border_w: 2;
    
    XResizeWindow(display, o_west, bw, bw * 2 + title_w + handle_w + height +
                  border_w * 2);
    XResizeWindow(display, o_east, bw, bw * 2 + title_w + handle_w + height +
                  border_w * 2);
    XResizeWindow(display, o_north, width + bw * 2, bw);
    XResizeWindow(display, o_south, width + bw * 2, bw);

    XMoveWindow(display, o_west, x - bw, y - title_w - border_w - bw);
    XMoveWindow(display, o_east, x + width, y - title_w - border_w - bw);
    XMoveWindow(display, o_north, x - bw, y - title_w - border_w - bw);
    XMoveWindow(display, o_south, x - bw, y + height + handle_w + border_w);
}

/**
 * @fn    DrawTitlebar(bool force)
 * @brief Draw window titlebar
 *
 * Renders titlebar pixmaps and draws titlebar foreground.
 *
 * @param force True if rendering should be forced
 */
void WaWindow::DrawTitlebar(bool force) {
    if (force || (title_w &&
                  ((attrib.x + attrib.width) > 0 &&
                   attrib.x < wascreen->width) &&
                  ((attrib.y - border_w) > 0 &&
                   (attrib.y - border_w - title_w) < wascreen->height))) {
        title->Render();
        label->Render();
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            (*bit)->Render();
    }
}

/**
 * @fn    DrawHandlebar(bool force)
 * @brief Draw window handlebar
 *
 * Renders handlebar pixmaps and draws handlebar foreground.
 *
 * @param force True if rendering should be forced
 */
void WaWindow::DrawHandlebar(bool force) {
    if (force || (handle_w &&
                  ((attrib.x + attrib.width) > 0 &&
                   attrib.x < wascreen->width) &&
                  (attrib.y + attrib.height + border_w + handle_w) > 0 &&
                  (attrib.y + attrib.height + border_w) < wascreen->height)) {
        handle->Render();
        grip_r->Render();
        grip_l->Render();
    }
}

/**
 * @fn    FocusWin(void)
 * @brief Sets window to have the look of a focused window
 *
 * Sets window decoration pointers to point so that window decorations will
 * represent a focused window. Redraws needed graphics.
 */
void WaWindow::FocusWin(void) {
    if (has_focus) return;
    has_focus = true;
    if (title_w) DrawTitlebar(true);
    if (handle_w) DrawHandlebar(true);
}

/**
 * @fn    UnFocusWin(void)
 * @brief Sets window to have the look of a unfocused window
 *
 * Sets window decoration pointers to point so that window decorations will
 * represent an unfocused window. Redraws needed graphics.
 */
void WaWindow::UnFocusWin(void) {
    if (! has_focus) return;
    has_focus = false;
    if (title_w)  DrawTitlebar(true);
    if (handle_w) DrawHandlebar(true);
}

/**     
 * @fn    ButtonPressed(int type)
 * @brief Titlebar buttonpress
 *      
 * Performes button press animation on one of the titlebar buttons.
 *      
 * @param button WaChildWindow pointer to button that should be pressed
 */
void WaWindow::ButtonPressed(WaChildWindow *button) {
    XEvent e;
    bool in_window = true;
    
    if (waimea->eh->move_resize != EndMoveResizeType) return;

    XUngrabButton(display, AnyButton, AnyModifier, id);
    XUngrabKey(display, AnyKey, AnyModifier, id);

    button->pressed = true;
    button->Render();
    for (;;) {
        XMaskEvent(display, ButtonReleaseMask | EnterWindowMask |
                   LeaveWindowMask, &e);
        switch (e.type) {
            case EnterNotify:
                in_window = true;
                button->pressed = true;
                button->Render();
                break;
            case LeaveNotify:
                button->pressed = false;
                button->Render();
                in_window = false;
                break;
            case ButtonRelease:
                button->pressed = false;
                button->Render();
                if (in_window) XPutBackEvent(display, &e);
                UpdateGrabs();
                waimea->eh->move_resize = EndMoveResizeType;
                return;
        }
    }
}

/**
 * @fn    IncSizeCheck(int width, int height, int *n_w, int *n_h)
 * @brief Calculate increasement sizes
 *
 * Given a new width and height this functions calculates if the windows
 * increasement sizes allows a resize of the window. The n_w parameter 
 * is used for returning allowed width and the n_h parameter is used for
 * returning allowed height.
 *
 * @param width Width we want to resize to
 * @param height Height we want to resize to
 * @param n_w Return of allowed width
 * @param n_h Return of allowed height
 *
 * @return True if a resize is allowed otherwise false
 */
bool WaWindow::IncSizeCheck(int width, int height, int *n_w, int *n_h) {
    bool resize = false;

    *n_w = attrib.width;
    *n_h = attrib.height;
    if ((width >= (attrib.width + size.width_inc)) ||
        (width <= (attrib.width - size.width_inc)) ||
        attrib.width == width) {
        if (width >= size.min_width && width <= size.max_width) {
            resize = true;
            if (size.width_inc == 1)
                *n_w = width;
            else
                *n_w = width - ((width - size.base_width) % size.width_inc);
        }
    }
    if ((height <= -(handle_w + border_w * 2)) && title_w) {
        if (! flags.shaded) {
            flags.shaded = true;
            restore_shade = attrib.height;
            net->SetWmState(this);
            if (title_w) {
                list<WaChildWindow *>::iterator bit = buttons.begin();
                for (; bit != buttons.end(); ++bit)
                    if ((*bit)->bstyle->cb == ShadeCBoxType) (*bit)->Render();
            }
            wascreen->UpdateCheckboxes(ShadeCBoxType);
        }
        *n_h = -(handle_w + border_w);
        if (handle_w) *n_h -= border_w;
        return resize;
    }
    if ((height >= (attrib.height + size.height_inc)) ||
        (height <= (attrib.height - size.height_inc)) ||
        attrib.height == height) {
        if ((height < 1) && (size.min_height <= 1) && title_w) {
            resize = true;
            if (! flags.shaded) {
                flags.shaded = true;
                restore_shade = attrib.height;
                net->SetWmState(this);
                if (title_w) {
                    list<WaChildWindow *>::iterator bit = buttons.begin();
                    for (; bit != buttons.end(); ++bit)
                        if ((*bit)->bstyle->cb == ShadeCBoxType)
                            (*bit)->Render();
                }
                wascreen->UpdateCheckboxes(ShadeCBoxType);
            }
            if (size.height_inc == 1)
                *n_h = height;
            else
                *n_h = height -
                    ((height - size.base_height) % size.height_inc);
        }
        else if (height >= size.min_height && height <= size.max_height) {
            resize = true;
            if (flags.shaded) {
                flags.shaded = false;
                net->SetWmState(this);
                if (title_w) {
                    list<WaChildWindow *>::iterator bit = buttons.begin();
                    for (; bit != buttons.end(); ++bit)
                        if ((*bit)->bstyle->cb == ShadeCBoxType)
                            (*bit)->Render();
                    
                }
                wascreen->UpdateCheckboxes(ShadeCBoxType);
            }
            if (size.height_inc == 1)
                *n_h = height;
            else
                *n_h = height -
                    ((height - size.base_height) % size.height_inc);
        }
    }
    return resize;
}

/**
 * @fn    Raise(XEvent *, WaAction *)
 * @brief Raises the window
 *
 * Raises the window to the top of the display stack and redraws window 
 * foreground.
 */
void WaWindow::Raise(XEvent *, WaAction *) {
    if (flags.forcedatbottom) return;
    if (! flags.alwaysontop && ! flags.alwaysatbottom) {
        wascreen->wa_list_stacking.remove(this);
        wascreen->wa_list_stacking.push_front(this);
        if (! transients.empty()) {
            list<Window>::iterator it = transients.begin();
            for (;it != transients.end(); ++it) {
                WaWindow *ww = (WaWindow *) waimea->FindWin(*it, WindowType);
                if (ww) {
                    wascreen->wa_list_stacking.remove(ww);
                    wascreen->wa_list_stacking.push_front(ww);
                }
                else {
                    transients.erase(it);
                    it = transients.begin();
                }
            }
        }
        wascreen->WaLowerWindow((Window) 0);
        net->SetClientListStacking(wascreen);
    }
}

/**
 * @fn    Lower(XEvent *, WaAction *)
 * @brief Lowers the window
 *
 * Lowers the window to the bottom of the display stack
 */
void WaWindow::Lower(XEvent *, WaAction *) {
    if (flags.forcedatbottom) return;
    if (! flags.alwaysontop && ! flags.alwaysatbottom) {
        wascreen->wa_list_stacking.remove(this);
        wascreen->wa_list_stacking.push_back(this);
        wascreen->WaLowerWindow(frame->id);
        net->SetClientListStacking(wascreen);
    }
}

/**
 * @fn    Focus(bool vis)
 * @brief Sets input focus to the window
 *
 * Gives window keyboard input focus. If vis parameter is true we make sure
 * the window is viewable.
 *
 * @param vis True if we should make sure the window is viewable
 */
void WaWindow::Focus(bool vis) {
    int newvx, newvy, x, y;
    XEvent e;
    if (! flags.focusable || ((! vis) && hidden)) return;
    
    if (mapped) {
        if (vis) {
            if (! (desktop_mask &
                   (1L << wascreen->current_desktop->number))) {
                list<Desktop *>::iterator dit =
                    wascreen->desktop_list.begin();
                for (; dit != wascreen->desktop_list.end(); dit++)
                    if (desktop_mask & (1L << (*dit)->number)) {
                        wascreen->GoToDesktop((*dit)->number);
                        break;
                    }
            }
            bool x_toolarge = false, y_toolarge = false;
            
            if ((wascreen->v_x + attrib.x) >=
                (wascreen->v_xmax + wascreen->width))
                x_toolarge = true;
            
            if ((wascreen->v_y + attrib.y) >=
                (wascreen->v_ymax + wascreen->height))
                y_toolarge = true;

            if (x_toolarge || y_toolarge) {
                int bw = flags.border * border_w;
                int handleh = handle_w + flags.handle * bw;
                
                int th = attrib.height + bw + handleh;
                int tw = attrib.width + bw;
                
                if (x_toolarge)
                    attrib.x = (wascreen->v_xmax + wascreen->width -
                                wascreen->v_x) - tw;
                if (y_toolarge)
                    attrib.y = (wascreen->v_ymax + wascreen->height -
                                wascreen->v_y) - th;
                RedrawWindow();
            }
            
            if (attrib.x >= wascreen->width ||
                attrib.y >= wascreen->height ||
                (attrib.x + attrib.width) <= 0 ||
                (attrib.y + attrib.height) <= 0) {
                x = wascreen->v_x + attrib.x;
                y = wascreen->v_y + attrib.y;
                newvx = (x / wascreen->width) * wascreen->width;
                newvy = (y / wascreen->height) * wascreen->height;
                wascreen->MoveViewportTo(newvx, newvy);
                XSync(display, false);
                while (XCheckTypedEvent(display, EnterNotify, &e));
            }
        }
        XInstallColormap(display, attrib.colormap);
        XGrabServer(display);
        if (validateclient_mapped(id)) {
            XSetInputFocus(display, id, RevertToPointerRoot, CurrentTime);
        } else DELETED;
        XUngrabServer(display);
    } else
        want_focus = true;
}

/**
 * @fn    Move(XEvent *e)
 * @brief Moves the window
 *
 * Moves the window through displaying an outline of the window while dragging
 * the mouse.
 *
 * @param e XEvent causing start of move
 */
void WaWindow::Move(XEvent *e, WaAction *) {
    XEvent event, *map_ev;
    int px, py, nx, ny, i;
    list<XEvent *> *maprequest_list;
    bool started = false;
    Window w;
    unsigned int ui;
    
    XQueryPointer(display, wascreen->id, &w, &w, &px, &py, &i, &i, &ui);

    if (waimea->eh->move_resize != EndMoveResizeType) return;
    nx = attrib.x;
    ny = attrib.y;
    waimea->eh->move_resize = MoveType;
    move_resize = true;

    if (e->type == MapRequest) {
        nx = attrib.x = px + border_w;
        ny = attrib.y = py + title_w + border_w;
        CreateOutline();
        DrawOutline(nx, ny, attrib.width, attrib.height);
        started = true;
    }
    maprequest_list = new list<XEvent *>;
    XGrabServer(display);
    if (validatedrawable(id)) {
        if (XGrabPointer(display, (mapped && !hidden) ? id: wascreen->id, true,
                         ButtonReleaseMask | ButtonPressMask |
                         PointerMotionMask | EnterWindowMask |
                         LeaveWindowMask, GrabModeAsync, GrabModeAsync,
                         None, waimea->move_cursor, CurrentTime)
            != GrabSuccess) {
            move_resize = false;
            waimea->eh->move_resize = EndMoveResizeType;
            return;
        }
        if (XGrabKeyboard(display, (mapped && !hidden) ? id: wascreen->id,
                          true, GrabModeAsync, GrabModeAsync, CurrentTime)
            != GrabSuccess) {
            move_resize = false;
            waimea->eh->move_resize = EndMoveResizeType;
            return;
        }
    } else DELETED;
    XUngrabServer(display);
    for (;;) {
        waimea->eh->EventLoop(waimea->eh->moveresize_return_mask, &event);
        switch (event.type) {
            case MotionNotify:
                while (XCheckTypedWindowEvent(display, event.xmotion.window,
                                              MotionNotify, &event));
                nx += event.xmotion.x_root - px;
                ny += event.xmotion.y_root - py;
                px  = event.xmotion.x_root;
                py  = event.xmotion.y_root;
                if (! started) {
                    CreateOutline();
                    started = true;
                }
                DrawOutline(nx, ny, attrib.width, attrib.height);
                break;
            case LeaveNotify:
            case EnterNotify:
                if (wascreen->west->id == event.xcrossing.window ||
                    wascreen->east->id == event.xcrossing.window ||
                    wascreen->north->id == event.xcrossing.window ||
                    wascreen->south->id == event.xcrossing.window) {
                    waimea->eh->HandleEvent(&event);
                } else if (event.type == LeaveNotify) {
                    int cx, cy;
                    XQueryPointer(display, wascreen->id, &w, &w, &cx, &cy, &i,
                                  &i, &ui);
                    nx += cx - px;
                    ny += cy - py;
                    px = cx;
                    py = cy;
                    if (! started) {
                        CreateOutline();
                        started = true;
                    }
                    DrawOutline(nx, ny, attrib.width, attrib.height);
                }
                break;
            case DestroyNotify:
            case UnmapNotify:
                if ((((event.type == UnmapNotify)? event.xunmap.window:
                      event.xdestroywindow.window) == id)) {
                    while (! maprequest_list->empty()) {
                        XPutBackEvent(display, maprequest_list->front());
                        delete maprequest_list->front();
                        maprequest_list->pop_front();
                    }
                    delete maprequest_list;
                    XPutBackEvent(display, &event);
                    if (started) DestroyOutline();
                    XUngrabKeyboard(display, CurrentTime);
                    XUngrabPointer(display, CurrentTime);
                    waimea->eh->move_resize = EndMoveResizeType;
                    move_resize = false;
                    return;
                }
                waimea->eh->EvUnmapDestroy(&event);
                break;
            case ConfigureRequest:
                if (event.xconfigurerequest.window != id)
                    waimea->eh->EvConfigureRequest(&event.xconfigurerequest);
                break;
            case MapRequest:
                map_ev = new XEvent;
                *map_ev = event;
                maprequest_list->push_front(map_ev); break;
            case ButtonPress:
            case ButtonRelease:
                event.xbutton.window = id;
            case KeyPress:
            case KeyRelease:
                if (event.type == KeyPress || event.type == KeyRelease)
                    event.xkey.window = id;
                waimea->eh->HandleEvent(&event);
                DrawOutline(nx, ny, attrib.width, attrib.height);
                if (waimea->eh->move_resize != EndMoveResizeType) break;
                if (started) DestroyOutline();
                attrib.x = nx;
                attrib.y = ny;
                RedrawWindow();
                while (! maprequest_list->empty()) {
                    XPutBackEvent(display, maprequest_list->front());
                    delete maprequest_list->front();
                    maprequest_list->pop_front();
                }
                delete maprequest_list;
                move_resize = false;
                XUngrabKeyboard(display, CurrentTime);
                XUngrabPointer(display, CurrentTime);
                return;
        }
    }
}

/**
 * @fn    MoveOpaque(XEvent *e, WaAction *)
 * @brief Moves the window
 *
 * Moves the window using the opaque moving process, which means that the
 * window is moved after the mouse pointers every motion event.
 *
 * @param e XEvent causing start of move
 */
void WaWindow::MoveOpaque(XEvent *e, WaAction *) {
    XEvent event, *map_ev;
    int sx, sy, px, py, nx, ny, i;
    list<XEvent *> *maprequest_list;
    Window w;
    unsigned int ui;

    if (waimea->eh->move_resize != EndMoveResizeType) return;
    sx = nx = attrib.x;
    sy = ny = attrib.y;
    waimea->eh->move_resize = MoveOpaqueType;
    move_resize = true;

    XQueryPointer(display, wascreen->id, &w, &w, &px, &py, &i, &i, &ui);

    if (e->type == MapRequest) {
        nx = attrib.x = px + border_w;
        ny = attrib.y = py + title_w + border_w;
        RedrawWindow();
        net->SetState(this, NormalState);
        net->SetVirtualPos(this);
    }
    dontsend = true;
    maprequest_list = new list<XEvent *>;
    XGrabServer(display);
    if (validatedrawable(id)) {
        if (XGrabPointer(display, (mapped && !hidden) ? id: wascreen->id, true,
                         ButtonReleaseMask | ButtonPressMask |
                         PointerMotionMask | EnterWindowMask |
                         LeaveWindowMask, GrabModeAsync, GrabModeAsync,
                         None, waimea->move_cursor, CurrentTime)
            != GrabSuccess) {
            move_resize = false;
            waimea->eh->move_resize = EndMoveResizeType;
            return;
        }
        if (XGrabKeyboard(display, (mapped && !hidden) ? id: wascreen->id,
                          true, GrabModeAsync, GrabModeAsync, CurrentTime)
            != GrabSuccess) {
            move_resize = false;
            waimea->eh->move_resize = EndMoveResizeType;
            return;
        }
    } else DELETED;
    XUngrabServer(display);
    for (;;) {
        waimea->eh->EventLoop(waimea->eh->moveresize_return_mask, &event);
        switch (event.type) {
            case MotionNotify:
                while (XCheckTypedWindowEvent(display, event.xmotion.window,
                                              MotionNotify, &event));
                nx += event.xmotion.x_root - px;
                ny += event.xmotion.y_root - py;
                px = event.xmotion.x_root;
                py = event.xmotion.y_root;
                attrib.x = nx;
                attrib.y = ny;
                RedrawWindow();
                break;
            case LeaveNotify:
            case EnterNotify:
                if (wascreen->west->id == event.xcrossing.window ||
                    wascreen->east->id == event.xcrossing.window ||
                    wascreen->north->id == event.xcrossing.window ||
                    wascreen->south->id == event.xcrossing.window) {
                    waimea->eh->HandleEvent(&event);
                } else if (event.type == LeaveNotify) {
                    int cx, cy;
                    XQueryPointer(display, wascreen->id, &w, &w, &cx, &cy, &i,
                                  &i, &ui);
                    nx += cx - px;
                    ny += cy - py;
                    px = cx;
                    py = cy;
                    attrib.x = nx;
                    attrib.y = ny;
                    RedrawWindow();
                }
                break;
            case DestroyNotify:
            case UnmapNotify:
                if ((((event.type == UnmapNotify)? event.xunmap.window:
                      event.xdestroywindow.window) == id)) {
                    while (! maprequest_list->empty()) {
                        XPutBackEvent(display, maprequest_list->front());
                        delete maprequest_list->front();
                        maprequest_list->pop_front();
                    }
                    delete maprequest_list;
                    XPutBackEvent(display, &event);
                    XUngrabKeyboard(display, CurrentTime);
                    XUngrabPointer(display, CurrentTime);
                    waimea->eh->move_resize = EndMoveResizeType;
                    dontsend = move_resize = false;
                    return;
                }
                waimea->eh->EvUnmapDestroy(&event);
                break;
            case ConfigureRequest:
                if (event.xconfigurerequest.window != id)
                    waimea->eh->EvConfigureRequest(&event.xconfigurerequest);
                break;
            case MapRequest:
                map_ev = new XEvent;
                *map_ev = event;
                maprequest_list->push_front(map_ev); break;
            case ButtonPress:
            case ButtonRelease:
                event.xbutton.window = id;
            case KeyPress:
            case KeyRelease:
                if (event.type == KeyPress || event.type == KeyRelease)
                    event.xkey.window = id;
                waimea->eh->HandleEvent(&event);
                if (waimea->eh->move_resize != EndMoveResizeType) break;
                if (attrib.x != sx || attrib.y != sy) {

#ifdef RENDER
                    if (wascreen->config.lazy_trans) {
                        render_if_opacity = true;
                        DrawTitlebar();
                        DrawHandlebar();
                        render_if_opacity = false;
                    }
#endif // RENDER

                    SendConfig();
                    net->SetVirtualPos(this);
                }
                while (! maprequest_list->empty()) {
                    XPutBackEvent(display, maprequest_list->front());
                    delete maprequest_list->front();
                    maprequest_list->pop_front();
                }
                delete maprequest_list;
                dontsend = move_resize = false;
                XUngrabKeyboard(display, CurrentTime);
                XUngrabPointer(display, CurrentTime);
                return;
        }
    }
}

/**
 * @fn    Resize(XEvent *e, int how)
 * @brief Resizes the window
 *
 * Resizes the window through displaying a outline of the window while dragging
 * the mouse. If how parameter is RightType when the window is resized in 
 * south-east direction and if how parameter is LeftType the resize is
 * being performed in south-west direction.
 *
 * @param e XEvent causing start of resize
 */
void WaWindow::Resize(XEvent *e, int how) {
    XEvent event, *map_ev;
    int px, py, width, height, n_w, n_h, o_w, o_h, n_x, o_x, i;
    list<XEvent *> *maprequest_list;
    bool started = false;
    Window w;
    unsigned int ui;
    
    XQueryPointer(display, wascreen->id, &w, &w, &px, &py, &i, &i, &ui);

    if (waimea->eh->move_resize != EndMoveResizeType) return;
    n_x    = o_x = attrib.x;
    width  = n_w = o_w = attrib.width;
    height = n_h = o_h = attrib.height;
    waimea->eh->move_resize = ResizeType;
    move_resize = true;

    if (e->type == MapRequest) {
        if (how > 0) n_x = attrib.x = px - attrib.width - border_w * 2;
        else n_x = attrib.x = px;
        attrib.y = py - attrib.height - title_w - border_w * 4;
        CreateOutline();
        DrawOutline(n_x, attrib.y, n_w, n_h);
        started = true;
    }    
    maprequest_list = new list<XEvent *>;
    XGrabServer(display);
    if (validatedrawable(id)) {
        if (XGrabPointer(display, (mapped && !hidden) ? id: wascreen->id, true,
                         ButtonReleaseMask | ButtonPressMask |
                         PointerMotionMask | EnterWindowMask |
                         LeaveWindowMask, GrabModeAsync, GrabModeAsync,
                         None, (how > 0) ? waimea->resizeright_cursor:
                         waimea->resizeleft_cursor, CurrentTime)
            != GrabSuccess) {
            move_resize = false;
            waimea->eh->move_resize = EndMoveResizeType;
            return;
        }
        if (XGrabKeyboard(display, (mapped && !hidden) ? id: wascreen->id,
                          true, GrabModeAsync, GrabModeAsync, CurrentTime)
            != GrabSuccess) {
            move_resize = false;
            waimea->eh->move_resize = EndMoveResizeType;
            return;
        }
    } else DELETED;
    XUngrabServer(display);
    for (;;) {
        waimea->eh->EventLoop(waimea->eh->moveresize_return_mask, &event);
        switch (event.type) {
            case MotionNotify:
                while (XCheckTypedWindowEvent(display, event.xmotion.window,
                                              MotionNotify, &event));
                width  += (event.xmotion.x_root - px) * how;
                height += event.xmotion.y_root - py;
                px = event.xmotion.x_root;
                py = event.xmotion.y_root;
                if (IncSizeCheck(width, height, &n_w, &n_h)) {
                    if (how == WestType) n_x -= n_w - o_w;
                    if (! started) {
                         CreateOutline();
                         started = true;
                    }
                    o_x = n_x;
                    o_w = n_w;
                    o_h = n_h;
                    DrawOutline(n_x, attrib.y, n_w, n_h);
                }
                break;
            case LeaveNotify:
            case EnterNotify:
                if (wascreen->west->id == event.xcrossing.window ||
                    wascreen->east->id == event.xcrossing.window ||
                    wascreen->north->id == event.xcrossing.window ||
                    wascreen->south->id == event.xcrossing.window) {
                    int old_vx = wascreen->v_x;
                    int old_vy = wascreen->v_y;
                    waimea->eh->HandleEvent(&event);
                    px -= (wascreen->v_x - old_vx);
                    py -= (wascreen->v_y - old_vy);
                    n_x = attrib.x;
                    if (how == WestType) n_x -= n_w - attrib.width;
                    DrawOutline(n_x, attrib.y, n_w, n_h);
                } else if (event.type == LeaveNotify) {
                    int cx, cy;
                    XQueryPointer(display, wascreen->id, &w, &w, &cx, &cy, &i,
                                  &i, &ui);
                    width  += (cx - px) * how;
                    height += cy - py;
                    px = cx;
                    py = cy;
                    if (IncSizeCheck(width, height, &n_w, &n_h)) {
                        if (how == WestType) n_x -= n_w - o_w;
                        if (! started) {
                            CreateOutline();
                            started = true;
                        }
                        o_x = n_x;
                        o_w = n_w;
                        o_h = n_h;
                        DrawOutline(n_x, attrib.y, n_w, n_h);
                    }
                }
                break;
            case DestroyNotify:
            case UnmapNotify:
                if ((((event.type == UnmapNotify)? event.xunmap.window:
                      event.xdestroywindow.window) == id)) {
                    while (! maprequest_list->empty()) {
                        XPutBackEvent(display, maprequest_list->front());
                        delete maprequest_list->front();
                        maprequest_list->pop_front();
                    }
                    delete maprequest_list;
                    XPutBackEvent(display, &event);
                    if (started) DestroyOutline();
                    XUngrabKeyboard(display, CurrentTime);
                    XUngrabPointer(display, CurrentTime);
                    waimea->eh->move_resize = EndMoveResizeType;
                    move_resize = false;
                    return;
                }
                waimea->eh->EvUnmapDestroy(&event);
                break;
            case ConfigureRequest:
                if (event.xconfigurerequest.window != id)
                    waimea->eh->EvConfigureRequest(&event.xconfigurerequest);
                break;
            case MapRequest:
                map_ev = new XEvent;
                *map_ev = event;
                maprequest_list->push_front(map_ev); break;
            case ButtonPress:
            case ButtonRelease:
                event.xbutton.window = id;
            case KeyPress:
            case KeyRelease:
                if (event.type == KeyPress || event.type == KeyRelease)
                    event.xkey.window = id;
                waimea->eh->HandleEvent(&event);
                if (waimea->eh->move_resize != EndMoveResizeType) break;
                if (started) DestroyOutline();
                attrib.width = n_w;
                attrib.height = n_h;
                attrib.x = n_x;
                RedrawWindow();
                while (! maprequest_list->empty()) {
                    XPutBackEvent(display, maprequest_list->front());
                    delete maprequest_list->front();
                    maprequest_list->pop_front();
                }
                delete maprequest_list;
                move_resize = false;
                XUngrabKeyboard(display, CurrentTime);
                XUngrabPointer(display, CurrentTime);
                return;
        }
    }
}

/**
 * @fn    ResizeOpaque(XEvent *e, int how)
 * @brief Resizes the window
 *
 * Resizes the window using the opaque resizing process, which means that 
 * the window is resized after the mouse pointers every motion event. 
 * If how parameter is RightType when the window is resized in 
 * south-east direction and if how parameter is LeftType the resize is
 * being performed in south-west direction.
 *
 * @param e XEvent causing start of resize
 */
void WaWindow::ResizeOpaque(XEvent *e, int how) {
    XEvent event, *map_ev;
    int px, py, width, height, n_w, n_h, i, sw, sh;
    list<XEvent *> *maprequest_list;
    Window w;
    unsigned int ui;
    
    XQueryPointer(display, wascreen->id, &w, &w, &px, &py, &i, &i, &ui);

    if (waimea->eh->move_resize != EndMoveResizeType) return;
    dontsend = true;
    sw = width  = n_w = attrib.width;
    sh = height = n_h = attrib.height;
    waimea->eh->move_resize = ResizeOpaqueType;
    move_resize = true;
    
    if (e->type == MapRequest) {
        if (how > 0) attrib.x = px - attrib.width - border_w * 2;
        else attrib.x = px;
        attrib.y = py - attrib.height - title_w - border_w * 4;
        RedrawWindow();
        net->SetState(this, NormalState);
        net->SetVirtualPos(this);
    }
    
    maprequest_list = new list<XEvent *>;
    XGrabServer(display);
    if (validatedrawable(id)) {
        if (XGrabPointer(display, (mapped && !hidden) ? id: wascreen->id, true,
                         ButtonReleaseMask | ButtonPressMask |
                         PointerMotionMask | EnterWindowMask |
                         LeaveWindowMask, GrabModeAsync, GrabModeAsync,
                         None, (how > 0) ? waimea->resizeright_cursor:
                         waimea->resizeleft_cursor, CurrentTime)
            != GrabSuccess) {
            move_resize = false;
            waimea->eh->move_resize = EndMoveResizeType;
            return;
        }
        if (XGrabKeyboard(display, (mapped && !hidden) ? id: wascreen->id,
                          true, GrabModeAsync, GrabModeAsync, CurrentTime)
            != GrabSuccess) {
            move_resize = false;
            waimea->eh->move_resize = EndMoveResizeType;
            return;
        }
    } else DELETED;
    XUngrabServer(display);
    for (;;) {
        waimea->eh->EventLoop(waimea->eh->moveresize_return_mask, &event);
        switch (event.type) {
            case MotionNotify:
                while (XCheckTypedWindowEvent(display, event.xmotion.window,
                                              MotionNotify, &event));
                width  += (event.xmotion.x_root - px) * how;
                height += event.xmotion.y_root - py;
                px = event.xmotion.x_root;
                py = event.xmotion.y_root;
                if (IncSizeCheck(width, height, &n_w, &n_h)) {
                    if (how == WestType) attrib.x -= n_w - attrib.width;
                    attrib.width  = n_w;
                    attrib.height = n_h;
                    RedrawWindow();
                }
                break;
            case LeaveNotify:
            case EnterNotify:
                if (wascreen->west->id == event.xcrossing.window ||
                    wascreen->east->id == event.xcrossing.window ||
                    wascreen->north->id == event.xcrossing.window ||
                    wascreen->south->id == event.xcrossing.window) {
                    int old_vx = wascreen->v_x;
                    int old_vy = wascreen->v_y;
                    waimea->eh->HandleEvent(&event);
                    px -= (wascreen->v_x - old_vx);
                    py -= (wascreen->v_y - old_vy);
                } else if (event.type == LeaveNotify) {
                    int cx, cy;
                    XQueryPointer(display, wascreen->id, &w, &w, &cx, &cy, &i,
                                  &i, &ui);
                    width  += (cx - px) * how;
                    height += cy - py;
                    px = cx;
                    py = cy;
                    if (IncSizeCheck(width, height, &n_w, &n_h)) {
                        if (how == WestType)
                            attrib.x -= n_w - attrib.width;
                        attrib.width  = n_w;
                        attrib.height = n_h;
                        RedrawWindow();
                    }
                }
                break;
            case DestroyNotify:
            case UnmapNotify:
                if ((((event.type == UnmapNotify)? event.xunmap.window:
                      event.xdestroywindow.window) == id)) {
                    while (! maprequest_list->empty()) {
                        XPutBackEvent(display, maprequest_list->front());
                        delete maprequest_list->front();
                        maprequest_list->pop_front();
                    }
                    delete maprequest_list;
                    XPutBackEvent(display, &event);
                    XUngrabKeyboard(display, CurrentTime);
                    XUngrabPointer(display, CurrentTime);
                    waimea->eh->move_resize = EndMoveResizeType;
                    dontsend = move_resize = false;
                    return;
                }
                waimea->eh->EvUnmapDestroy(&event);
                break;
            case ConfigureRequest:
                if (event.xconfigurerequest.window != id)
                    waimea->eh->EvConfigureRequest(&event.xconfigurerequest);
                break;
            case MapRequest:
                map_ev = new XEvent;
                *map_ev = event;
                maprequest_list->push_front(map_ev); break;
            case ButtonPress:
            case ButtonRelease:
                event.xbutton.window = id;
            case KeyPress:
            case KeyRelease:
                if (event.type == KeyPress || event.type == KeyRelease)
                    event.xkey.window = id;
                waimea->eh->HandleEvent(&event);
                width = attrib.width;
                height = attrib.height;
                if (waimea->eh->move_resize != EndMoveResizeType) break;
                if (attrib.width != sw || attrib.height != sh) {
                    SendConfig();
                    net->SetVirtualPos(this);
                }
                while (! maprequest_list->empty()) {
                    XPutBackEvent(display, maprequest_list->front());
                    delete maprequest_list->front();
                    maprequest_list->pop_front();
                }
                delete maprequest_list;
                dontsend = move_resize = false;
                XUngrabKeyboard(display, CurrentTime);
                XUngrabPointer(display, CurrentTime);
                return;
        }
    }
}

/**
 * @fn    EndMoveResize(XEvent *e, WaAction *)
 * @brief Ends move/resize
 *
 * Ends moving and resizing process.
 */
void WaWindow::EndMoveResize(XEvent *, WaAction *) {
    waimea->eh->move_resize = EndMoveResizeType;
}

/**
 * @fn    _Maximize(int x, int y)
 * @brief Maximize the window
 *
 * Maximizes the window so that the window with it's decorations fills up
 * the hole workarea.
 *
 * @param x Virtual x pos where window should be maximized, ignored if negative
 * @param y Virtual y pos where window should be maximized, ignored if negative
 */
void WaWindow::_Maximize(int x, int y) {
    int n_w, n_h, new_width, new_height;

    if (flags.max) return;

    int workx, worky, workw, workh;
    wascreen->GetWorkareaSize(&workx, &worky, &workw, &workh);
    
    new_width = workw - (flags.border * border_w * 2);
    new_height = workh - (flags.border * border_w * 2) -
        title_w - handle_w - (border_w * flags.title) -
        (border_w * flags.handle);

    restore_max.width = attrib.width;
    restore_max.height = attrib.height;
    int rest_x = attrib.x;
    int rest_y = attrib.y;
    
    if (flags.shaded) {
        restore_max.height = restore_shade;
        restore_shade = new_height;
        new_height = attrib.height;
    }
    if (IncSizeCheck(new_width, new_height, &n_w, &n_h)) {
        attrib.x = workx;
        attrib.y = worky;
        restore_max.x = rest_x - attrib.x;
        restore_max.y = rest_y - attrib.y;
        if (x >= 0 && y >= 0) {
            attrib.x = (x - wascreen->v_x);
            attrib.y = (y - wascreen->v_y);
            restore_max.misc0 = x;
            restore_max.misc1 = y;
        } else {
            restore_max.misc0 = wascreen->v_x + attrib.x;
            restore_max.misc1 = wascreen->v_y + attrib.y;
        }
        attrib.x += border_w;
        attrib.y += title_w + border_w + (border_w * flags.title);
        attrib.width = n_w;
        attrib.height = n_h;
        RedrawWindow();
        flags.max = true;
        
        if (title_w) {
            list<WaChildWindow *>::iterator bit = buttons.begin();
            for (; bit != buttons.end(); ++bit)
                if ((*bit)->bstyle->cb == MaxCBoxType) (*bit)->Render();
        }
        net->SetWmState(this);
        wascreen->UpdateCheckboxes(MaxCBoxType);
    }
}

/**
 * @fn    UnMaximize(XEvent *, WaAction *)
 * @brief Unmaximize the window
 *
 * Restores size and position of maximized window.
 */
void WaWindow::UnMaximize(XEvent *, WaAction *) {
    int n_w, n_h, rest_height, tmp_shade_height = 0;
    
    if (flags.max) {
        if (flags.shaded) {
            rest_height = attrib.height;
            tmp_shade_height = restore_max.height;
        }
        else rest_height = restore_max.height;
        if (IncSizeCheck(restore_max.width, rest_height, &n_w, &n_h)) {
            attrib.x = restore_max.x + (restore_max.misc0 - wascreen->v_x);
            attrib.y = restore_max.y + (restore_max.misc1 - wascreen->v_y);
            attrib.width = n_w;
            attrib.height = n_h;
            flags.max = false;
            RedrawWindow();
            if (flags.shaded) restore_shade = tmp_shade_height;
            if (title_w) {
                list<WaChildWindow *>::iterator bit = buttons.begin();
                for (; bit != buttons.end(); ++bit)
                    if ((*bit)->bstyle->cb == MaxCBoxType) (*bit)->Render();
            }
            net->SetWmState(this);
            wascreen->UpdateCheckboxes(MaxCBoxType);
        }
    }
}

/**
 * @fn    ToggleMaximize(XEvent *, WaAction *)
 * @brief Maximizes or unmaximize window
 *
 * If window isn't maximized this function maximizes it and if it is already
 * maximized then function will unmaximized window.
 */
void WaWindow::ToggleMaximize(XEvent *, WaAction *) {
    if (! flags.max) Maximize(NULL, NULL);
    else UnMaximize(NULL, NULL);
}

/**
 * @fn    Close(XEvent *, WaAction *)
 * @brief Close the window
 *
 * Sends a delete message to the client window. A normal running X window
 * should accept this event and destroy itself. 
 */
void WaWindow::Close(XEvent *, WaAction *) {
    XEvent ev;

    ev.type = ClientMessage;
    ev.xclient.window = id;
    ev.xclient.message_type = XInternAtom(display, "WM_PROTOCOLS", false);
    ev.xclient.format = 32;
    ev.xclient.data.l[0] = XInternAtom(display, "WM_DELETE_WINDOW", false);
    ev.xclient.data.l[1] = CurrentTime;

    XGrabServer(display);
    if (validatedrawable(id))
        XSendEvent(display, id, false, NoEventMask, &ev);
    else DELETED;
    XUngrabServer(display);

}

/**
 * @fn    Kill(XEvent *, WaAction *)
 * @brief Kill the window
 *
 * Tells the the X server to remove the window from the screen through
 * killing the process that created it.
 */
void WaWindow::Kill(XEvent *, WaAction *) {
    XGrabServer(display);
    if (validatedrawable(id))
        XKillClient(display, id);
    else DELETED;
    XUngrabServer(display);
}

/**
 * @fn    CloseKill(XEvent *e, WaAction *ac)
 * @brief Close/Kill the window
 *
 * Checks if the window will accept a delete message. If it will, then we
 * use that method for closing the window otherwise we use the kill method.
 *
 * @param e XEvent causing close/kill of window
 * @param ac WaAction object
 */
void WaWindow::CloseKill(XEvent *e, WaAction *ac) {
    int i, n;
    bool close = false;
    Atom *protocols;
    Atom del_atom = XInternAtom(display, "WM_DELETE_WINDOW", false);

    XGrabServer(display);
    if (validatedrawable(id)) {
        if (XGetWMProtocols(display, id, &protocols, &n)) {
            for (i = 0; i < n; i++) if (protocols[i] == del_atom) close = true;
            XFree(protocols);
        }
    } else DELETED;
    XUngrabServer(display);
    if (close) Close(e, ac);
    else Kill(e, ac);
}

/**
 * @fn    MenuMap(XEvent *e, WaAction *ac)
 * @brief Maps a menu
 *
 * Links the window to the menu and maps it at the position of the button
 * event causing mapping. If menu is already mapped nothing is done.
 *
 * @param ac WaAction object
 * @param bool True if we should focus first item in menu
 */
void WaWindow::MenuMap(XEvent *, WaAction *ac, bool focus) {
    Window w;
    int i, x, y, exp;
    unsigned int ui;
    WaMenu *menu = wascreen->GetMenuNamed(ac->param);

    if (! menu) return;
    if (waimea->eh->move_resize != EndMoveResizeType) return;

    int workx, worky, workw, workh;
    wascreen->GetWorkareaSize(&workx, &worky, &workw, &workh);
    
    if (XQueryPointer(display, wascreen->id, &w, &w, &x, &y, &i, &i, &ui)) {
        if (menu->tasksw) menu->Build(wascreen);
        menu->wf = id;
        menu->ftype = MenuWFuncMask;
        list<WaMenuItem *>::iterator it = menu->item_list.begin();
        for (exp = 0; it != menu->item_list.end(); ++it)
            exp += (*it)->ExpandAll(this);
        if (exp) menu->Build(wascreen);
        if ((y + menu->height + wascreen->mstyle.border_width * 2) > 
            (unsigned int) (workh + worky))
            y -= (menu->height + wascreen->mstyle.border_width * 2);
        if ((x + menu->width + wascreen->mstyle.border_width * 2) > 
            (unsigned int) (workw + workx))
            x -= (menu->width + wascreen->mstyle.border_width * 2);
        menu->Map(x, y);
        if (focus) menu->FocusFirst();
    }
}

/**
 * @fn    MenuRemap(XEvent *e)
 * @brief Remaps a menu
 *
 * Links the window to the menu and maps it at the position of the button
 * event causing mapping. If the window is already mapped then we just move
 * it to the new position.
 *
 * @param ac WaAction object
 * @param bool True if we should focus first item in menu
 */
void WaWindow::MenuRemap(XEvent *, WaAction *ac, bool focus) {
    Window w;
    int i, x, y, exp;
    unsigned int ui;
    WaMenu *menu = wascreen->GetMenuNamed(ac->param);

    if (! menu) return;
    if (menu->dynamic && menu->mapped) {
        menu->Unmap(menu->has_focus);
        if (! (menu = wascreen->CreateDynamicMenu(ac->param))) return;
    }
    if (waimea->eh->move_resize != EndMoveResizeType) return;

    int workx, worky, workw, workh;
    wascreen->GetWorkareaSize(&workx, &worky, &workw, &workh);
    
    if (XQueryPointer(display, wascreen->id, &w, &w, &x, &y, &i, &i, &ui)) {
        if (menu->tasksw) menu->Build(wascreen);
        menu->wf = id;
        menu->ftype = MenuWFuncMask;
        list<WaMenuItem *>::iterator it = menu->item_list.begin();
        for (exp = 0; it != menu->item_list.end(); ++it)
            exp += (*it)->ExpandAll(this);
        if (exp) menu->Build(wascreen);
        if ((y + menu->height + wascreen->mstyle.border_width * 2) > 
            (unsigned int) (workh + worky))
            y -= (menu->height + wascreen->mstyle.border_width * 2);
        if ((x + menu->width + wascreen->mstyle.border_width * 2) > 
            (unsigned int) (workw + workx))
            x -= (menu->width + wascreen->mstyle.border_width * 2);
        menu->ignore = true;
        menu->ReMap(x, y);
        menu->ignore = false;
        if (focus) menu->FocusFirst();
    }
}

/**
 * @fn    MenuUnmap(XEvent *, WaAction *ac, bool focus)
 * @brief Unmaps a menu
 *
 * Unmaps a menu and all its submenus.
 *
 * @param ac WaAction object
 * @param bool True if we should focus root item
 */
void WaWindow::MenuUnmap(XEvent *, WaAction *ac, bool focus) {
    WaMenu *menu = wascreen->GetMenuNamed(ac->param);
    
    if (! menu) return;
    if (waimea->eh->move_resize != EndMoveResizeType) return;
    
    menu->Unmap(focus);
    menu->UnmapSubmenus(focus);
}

/**
 * @fn    Shade(XEvent *, WaAction *)
 * @brief Shade window
 *
 * Resizes window to so that only the titlebar is shown. Remembers previous
 * height of window that will be restored when unshading.
 */
void WaWindow::Shade(XEvent *, WaAction *) {
    int n_w, n_h;
    
    if (IncSizeCheck(attrib.width, -(handle_w + border_w * 2), &n_w, &n_h)) {
        attrib.width = n_w;
        attrib.height = n_h;
        RedrawWindow();
        net->SetWmState(this);
        if (title_w) {
            list<WaChildWindow *>::iterator bit = buttons.begin();
            for (; bit != buttons.end(); ++bit)
                if ((*bit)->bstyle->cb == ShadeCBoxType) (*bit)->Render();
        }
        wascreen->UpdateCheckboxes(ShadeCBoxType);
    }
}

/**
 * @fn    UnShade(XEvent *, WaAction *)
 * @brief Unshade window
 *
 * Restores height of shaded window.
 */
void WaWindow::UnShade(XEvent *, WaAction *) {
    if (flags.shaded) {
        attrib.height = restore_shade;
        RedrawWindow();
        flags.shaded = false;
        net->SetWmState(this);
        if (title_w) {
            list<WaChildWindow *>::iterator bit = buttons.begin();
            for (; bit != buttons.end(); ++bit)
                if ((*bit)->bstyle->cb == ShadeCBoxType) (*bit)->Render();
        }
        wascreen->UpdateCheckboxes(ShadeCBoxType);
    }
}

/**
 * @fn    ToggleShade(XEvent *, WaAction *)
 * @brief Shades window or unshades it
 *
 * If window isn't shaded this function will shade it and if it is already
 * shaded function unshades it.
 */
void WaWindow::ToggleShade(XEvent *, WaAction *) {
    if (flags.shaded) UnShade(NULL, NULL);
    else Shade(NULL, NULL);
}

/**
 * @fn    Sticky(XEvent *, WaAction *)
 * @brief Makes window sticky
 *
 * Sets the sticky flag to true. This makes viewport moving functions
 * to ignore this window.
 */
void WaWindow::Sticky(XEvent *, WaAction *) {
    flags.sticky = true;
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == StickCBoxType) (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(StickCBoxType);
}

/**
 * @fn    UnSticky(XEvent *, WaAction *)
 * @brief Makes window normal
 *
 * Sets the sticky flag to false. This makes viewport moving functions
 * treat this window as a normal window.
 */
void WaWindow::UnSticky(XEvent *, WaAction *) {
    flags.sticky = false;
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == StickCBoxType) (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(StickCBoxType);
}

/**
 * @fn    ToggleSticky(XEvent *, WaAction *)
 * @brief Toggles sticky flag
 *
 * Inverts the sticky flag.
 */
void WaWindow::ToggleSticky(XEvent *, WaAction *) {
    flags.sticky = !flags.sticky;
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == StickCBoxType) (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(StickCBoxType);
}

/**
 * @fn    TaskSwitcher(XEvent *, WaAction *)
 * @brief Maps task switcher menu
 *
 * Maps task switcher menu at middle of screen and sets input focus to
 * first window in list.
 */
void WaWindow::TaskSwitcher(XEvent *, WaAction *) {
    if (waimea->eh->move_resize != EndMoveResizeType) return;

    int workx, worky, workw, workh;
    wascreen->GetWorkareaSize(&workx, &worky, &workw, &workh);

    wascreen->window_menu->Build(wascreen);
    wascreen->window_menu->ReMap(workx + (workw / 2 -
                                          wascreen->window_menu->width / 2),
                                 worky + (workh / 2 -
                                          wascreen->window_menu->height / 2));
    wascreen->window_menu->FocusFirst();
}

/**
 * @fn    PreviousTask(XEvent *, WaAction *)
 * @brief Switches to previous task
 *
 * Switches to the previous focused window.
 */
void WaWindow::PreviousTask(XEvent *, WaAction *) {
    if (waimea->eh->move_resize != EndMoveResizeType) return;
    list<WaWindow *>::iterator it = wascreen->wawindow_list.begin();
    it++;
    (*it)->Raise(NULL, NULL);
    (*it)->FocusVis(NULL, NULL);
}

/**
 * @fn    NextTask(XEvent *, WaAction *)
 * @brief Switches to next task
 *
 * Switches to the window that haven't had focus for longest time.
 */
void WaWindow::NextTask(XEvent *, WaAction *) {
    if (waimea->eh->move_resize != EndMoveResizeType) return;
    wascreen->wawindow_list.back()->Raise(NULL, NULL);
    wascreen->wawindow_list.back()->FocusVis(NULL, NULL);
}

/**
 * @fn    DecorTitleOn(XEvent *, WaAction *)
 * @brief Turn on title decoration
 *
 * Turn on titlebar decorations for the window.
 */
void WaWindow::DecorTitleOn(XEvent *, WaAction *) {
    if (flags.title) return;
    
    flags.title = true;
    flags.all = flags.title && flags.handle && flags.border;
    UpdateAllAttributes();
    MapWindow();
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == TitleCBoxType ||
                (flags.all && (*bit)->bstyle->cb == AllCBoxType))
                (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(TitleCBoxType);
    if (flags.all) wascreen->UpdateCheckboxes(AllCBoxType);
}

/**
 * @fn    DecorHandleOn(XEvent *, WaAction *)
 * @brief Turn on handle decoration
 *
 * Turn on handlebar decorations for the window.
 */
void WaWindow::DecorHandleOn(XEvent *, WaAction *) {
    if (flags.handle) return;
    
    flags.handle = true;
    flags.all = flags.title && flags.handle && flags.border;
    UpdateAllAttributes();
    MapWindow();
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == TitleCBoxType ||
                (flags.all && (*bit)->bstyle->cb == AllCBoxType))
                (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(HandleCBoxType);
    if (flags.all) wascreen->UpdateCheckboxes(AllCBoxType);
}

/**
 * @fn    DecorBorderOn(XEvent *, WaAction *)
 * @brief Turn on border decoration
 *
 * Turn on border decorations for the window.
 */
void WaWindow::DecorBorderOn(XEvent *, WaAction *) {
    if (flags.border) return;
    
    flags.border = true;
    flags.all = flags.title && flags.handle && flags.border;
    UpdateAllAttributes();
    MapWindow();
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == BorderCBoxType ||
                (flags.all && (*bit)->bstyle->cb == AllCBoxType))
                (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(BorderCBoxType);
    if (flags.all) wascreen->UpdateCheckboxes(AllCBoxType);
}

/**
 * @fn    DecorAllOn(XEvent *, WaAction *)
 * @brief Turn on all decorations
 *
 * Turn on all decorations for the window.
 */
void WaWindow::DecorAllOn(XEvent *, WaAction *) {
    if (flags.all) return;

    flags.all = true;
    flags.border = true;
    flags.title = true;
    flags.handle = true;
    UpdateAllAttributes();
    MapWindow();
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == TitleCBoxType ||
                (*bit)->bstyle->cb == HandleCBoxType ||
                (*bit)->bstyle->cb == BorderCBoxType ||
                (*bit)->bstyle->cb == AllCBoxType)
                (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(TitleCBoxType);
    wascreen->UpdateCheckboxes(HandleCBoxType);
    wascreen->UpdateCheckboxes(BorderCBoxType);
    wascreen->UpdateCheckboxes(AllCBoxType);
}

/**
 * @fn    DecorTitleOff(XEvent *, WaAction *)
 * @brief Turn off title decoration
 *
 * Turn off title decorations for the window.
 */
void WaWindow::DecorTitleOff(XEvent *, WaAction *) {
    if (flags.shaded || ! flags.title) return;
    
    flags.title = false;
    flags.all = false;
    UpdateAllAttributes();
    MapWindow();
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == TitleCBoxType ||
                (flags.all && (*bit)->bstyle->cb == AllCBoxType))
                (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(TitleCBoxType);
    wascreen->UpdateCheckboxes(AllCBoxType);
}

/**
 * @fn    DecorHandleOff(XEvent *, WaAction *)
 * @brief Turn off hendle decoration
 *
 * Turn off handlebar decorations for the window.
 */
void WaWindow::DecorHandleOff(XEvent *, WaAction *) {
    if (! flags.handle) return;
    
    flags.handle = false;
    flags.all = false;
    UpdateAllAttributes();
    MapWindow();
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == HandleCBoxType ||
                (flags.all && (*bit)->bstyle->cb == AllCBoxType))
                (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(HandleCBoxType);
    wascreen->UpdateCheckboxes(AllCBoxType);
}

/**
 * @fn    DecorBorderOff(XEvent *, WaAction *)
 * @brief Turn off border decoration
 *
 * Turn off border decorations for the window.
 */
void WaWindow::DecorBorderOff(XEvent *, WaAction *) {
    if (! flags.border) return;
    
    flags.border = false;
    flags.all = false;
    UpdateAllAttributes();
    MapWindow();
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == BorderCBoxType ||
                (flags.all && (*bit)->bstyle->cb == AllCBoxType))
                (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(BorderCBoxType);
    wascreen->UpdateCheckboxes(AllCBoxType);
}

/**
 * @fn    DecorAllOff(XEvent *, WaAction *)
 * @brief Turn off all decorations
 *
 * Turn off all decorations for the window.
 */
void WaWindow::DecorAllOff(XEvent *, WaAction *) {
    if (flags.shaded || ! flags.all) return;

    flags.all = false;
    flags.border = false;
    flags.title = false;
    flags.handle = false;
    UpdateAllAttributes();
    MapWindow();
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == TitleCBoxType ||
                (*bit)->bstyle->cb == HandleCBoxType ||
                (*bit)->bstyle->cb == BorderCBoxType ||
                (*bit)->bstyle->cb == AllCBoxType)
                (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(TitleCBoxType);
    wascreen->UpdateCheckboxes(HandleCBoxType);
    wascreen->UpdateCheckboxes(BorderCBoxType);
    wascreen->UpdateCheckboxes(AllCBoxType);
}

/**
 * @fn    DecorTitleToggle(XEvent *, WaAction *)
 * @brief Toggle title decoration
 *
 * Toggle titlebar decorations for the window.
 */
void WaWindow::DecorTitleToggle(XEvent *, WaAction *) {
    if (flags.title) DecorTitleOff(NULL, NULL);
    else DecorTitleOn(NULL, NULL);
}

/**
 * @fn    DecorHandleToggle(XEvent *, WaAction *)
 * @brief Toggle handle decoration
 *
 * Toggle handlebar decorations for the window.
 */
void WaWindow::DecorHandleToggle(XEvent *, WaAction *) {
    if (flags.handle) DecorHandleOff(NULL, NULL);
    else DecorHandleOn(NULL, NULL);
}

/**
 * @fn    DecorBorderToggle(XEvent *, WaAction *)
 * @brief Toggle border decoration
 *
 * Toggle border decorations for the window.
 */
void WaWindow::DecorBorderToggle(XEvent *, WaAction *) {
    if (flags.border) DecorBorderOff(NULL, NULL);
    else DecorBorderOn(NULL, NULL);
}

/**
 * @fn    AlwaysontopOn(XEvent *, WaAction *)
 * @brief Make window always on top
 *
 * Add window to list of always on top windows and update always on top
 * windows.
 */
void WaWindow::AlwaysontopOn(XEvent *, WaAction *) {
    if (flags.forcedatbottom) return;
    flags.alwaysontop = true;
    flags.alwaysatbottom = false;
    wascreen->wa_list_stacking.remove(this);
    wascreen->wawindow_list_stacking_aab.remove(this);
    wascreen->wawindow_list_stacking_aot.push_back(this);
    wascreen->WaRaiseWindow(0);
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == AOTCBoxType ||
                (*bit)->bstyle->cb == AABCBoxType)
                (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(AOTCBoxType);
    wascreen->UpdateCheckboxes(AABCBoxType);
    net->SetClientListStacking(wascreen);
}

/**
 * @fn    AlwaysatbottomOn(XEvent *, WaAction *)
 * @brief Make window always at bottom
 *
 * Add window to list of always at bottom windows and update always at bottom
 * windows.
 */
void WaWindow::AlwaysatbottomOn(XEvent *, WaAction *) {
    if (flags.forcedatbottom) return;
    flags.alwaysontop = false;
    flags.alwaysatbottom = true;
    wascreen->wa_list_stacking.remove(this);
    wascreen->wawindow_list_stacking_aot.remove(this);
    wascreen->wawindow_list_stacking_aab.push_back(this);
    wascreen->WaLowerWindow(frame->id);
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == AOTCBoxType ||
                (*bit)->bstyle->cb == AABCBoxType)
                (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(AOTCBoxType);
    wascreen->UpdateCheckboxes(AABCBoxType);
    net->SetClientListStacking(wascreen);
}

/**
 * @fn    AlwaysontopOff(XEvent *, WaAction *)
 * @brief Make window not always on top
 *
 * Removes window from list of always on top windows.
 */
void WaWindow::AlwaysontopOff(XEvent *, WaAction *) {
    if (flags.forcedatbottom) return;
    flags.alwaysontop = false;
    wascreen->wawindow_list_stacking_aot.remove(this);
    wascreen->wa_list_stacking.push_front(this);
    wascreen->WaRaiseWindow(0);
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == AOTCBoxType)
                (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(AOTCBoxType);
    net->SetClientListStacking(wascreen);
}

/**
 * @fn    AlwaysatbottomOff(XEvent *, WaAction *)
 * @brief Make window not always at bottom
 *
 * Removes window from list of always at bottom windows.
 */
void WaWindow::AlwaysatbottomOff(XEvent *, WaAction *) {
    if (flags.forcedatbottom) return;
    flags.alwaysatbottom = false;
    wascreen->wawindow_list_stacking_aab.remove(this);
    wascreen->wa_list_stacking.push_back(this);
    wascreen->WaLowerWindow(frame->id);
    net->SetWmState(this);
    if (title_w) {
        list<WaChildWindow *>::iterator bit = buttons.begin();
        for (; bit != buttons.end(); ++bit)
            if ((*bit)->bstyle->cb == AABCBoxType)
                (*bit)->Render();
    }
    wascreen->UpdateCheckboxes(AABCBoxType);
    net->SetClientListStacking(wascreen);
}

/**
 * @fn    AlwaysontopToggle(XEvent *, WaAction *)
 * @brief Toggle always on top flag
 *
 * If window is always on top we removed it from always on top list, or if
 * window isn't always on top we add it to always on top list.
 */
void WaWindow::AlwaysontopToggle(XEvent *, WaAction *) {
    if (flags.alwaysontop) AlwaysontopOff(NULL, NULL);
    else AlwaysontopOn(NULL, NULL);
}

/**
 * @fn    AlwaysatbottomToggle(XEvent *, WaAction *)
 * @brief Toggle always at bottom flag
 *
 * If window is always at bottom we removed it from always at bottom list,
 * or if window isn't always at bottom we add it to always at bottom list.
 */
void WaWindow::AlwaysatbottomToggle(XEvent *, WaAction *) {
    if (flags.alwaysatbottom) AlwaysatbottomOff(NULL, NULL);
    else AlwaysatbottomOn(NULL, NULL);
}


/**
 * @fn    AcceptConfigRequestOn(XEvent *, WaAction *)
 * @brief Accept ConfigureRequests from this window
 *
 * Sets flag so the configure request events from this window are handled as
 * usual.
 */
void WaWindow::AcceptConfigRequestOn(XEvent *, WaAction *) {
    ign_config_req = false;
}

/**
 * @fn    AcceptConfigRequestOff(XEvent *, WaAction *)
 * @brief Don't accept ConfigureRequests from this window
 *
 * Sets flag so the configure request events from this window are ignored.
 */
void WaWindow::AcceptConfigRequestOff(XEvent *, WaAction *) {
    ign_config_req = true;
}

/**
 * @fn    AcceptConfigRequestToggle(XEvent *, WaAction *)
 * @brief Toggle ign_config_req flag
 *
 * Toggles configure request ignore flag.
 */
void WaWindow::AcceptConfigRequestToggle(XEvent *, WaAction *) {
    ign_config_req = !ign_config_req;
}

/**
 * @fn    MoveResize(XEvent *e, WaAction *ac)
 * @brief Moves and resizes window
 *
 * X geomtry string as WaAction parameter. Moves and resizes window to
 * the size and position in the actual screen area specified by X geometry
 * string.
 *
 * @param e XEvent causing function call
 * @param ac WaAction object
 */
void WaWindow::MoveResize(XEvent *e, WaAction *ac) {
    int x, y, geometry;
    unsigned int width, height;
    
    if (waimea->eh->move_resize != EndMoveResizeType || ! ac->param) return;
    width = attrib.width;
    height = attrib.height;

    geometry = XParseGeometry(ac->param, &x, &y, &width, &height);
    IncSizeCheck(width, height, &attrib.width, &attrib.height);
    
    if (geometry & XValue) {
        if (geometry & XNegative)
            attrib.x = wascreen->width + x - attrib.width;
        else attrib.x = x;
    }
    if (geometry & YValue) {
        if (geometry & YNegative)
            attrib.y = wascreen->height + y - attrib.height;
        else attrib.y = y;
    }

    RedrawWindow();
}

/**
 * @fn    MoveResizeVirtual(XEvent *e, WaAction *ac)
 * @brief Moves and resizes window
 *
 * X geomtry string as WaAction parameter. Moves and resizes window to
 * the size and position in the virtual screen area specified by X geometry
 * string.
 *
 * @param e XEvent causing function call
 * @param ac WaAction object
 */
void WaWindow::MoveResizeVirtual(XEvent *e, WaAction *ac) {
    int x, y, geometry;
    unsigned int width, height;
    
    if (waimea->eh->move_resize != EndMoveResizeType || ! ac->param) return;
    width = attrib.width;
    height = attrib.height;

    geometry = XParseGeometry(ac->param, &x, &y, &width, &height);
    IncSizeCheck(width, height, &attrib.width, &attrib.height);
    
    if (geometry & XValue) {
        if (geometry & XNegative)
            attrib.x = ((wascreen->v_xmax + wascreen->width) +
                        x - attrib.width) - wascreen->v_x;
        else attrib.x = x - wascreen->v_x;
    }
    if (geometry & YValue) {
        if (geometry & YNegative)
            attrib.y = ((wascreen->v_ymax + wascreen->height) +
                        y - attrib.height) - wascreen->v_y;
        else attrib.y = y - wascreen->v_y;
    }

    RedrawWindow();
}

/**
 * @fn    MoveWindowToPointer(XEvent *e, WaAction *)
 * @brief Moves window to pointer position
 *
 * Moves window to mouse pointer position and makes sure that it isn't
 * moved outside screen.
 *
 * @param e XEvent causing function call
 */
void WaWindow::MoveWindowToPointer(XEvent *e, WaAction *) {
    int total_h = border_w * 2;
    if (title_w) total_h += border_w;
    if (handle_w) total_h += border_w;
    total_h += attrib.height;

    attrib.x = e->xbutton.x_root - attrib.width / 2;
    attrib.y = e->xbutton.y_root - attrib.height / 2;
    
    if (attrib.x + border_w * 2 + attrib.width > wascreen->width)
        attrib.x = wascreen->width - attrib.width - border_w;
    else if (attrib.x < 0) attrib.x = border_w;
    
    if (attrib.y + total_h > wascreen->height)
        attrib.y = wascreen->height - handle_w - border_w - attrib.height -
            ((handle_w)? border_w: 0);
    else if (attrib.y < 0)
        attrib.y = title_w + border_w + ((title_w)? border_w: 0);

    RedrawWindow();
}

/**
 * @fn    MoveWindowToSmartPlace(XEvent *, WaAction *)
 * @brief Moves window to smart position
 *
 * Moves window using Smart Placement algorithm.
 */
void WaWindow::MoveWindowToSmartPlace(XEvent *, WaAction *) {
    int temp_h, temp_w;
    Gravitate(RemoveGravity);
    int workx, worky, workw, workh;
    wascreen->GetWorkareaSize(&workx, &worky, &workw, &workh);
    int test_x = attrib.x - workx;
    int test_y = attrib.y - worky - 1;
    int loc_ok = False, tw, tx, ty, th;
    int bw = flags.border * border_w;
    int titleh = title_w + flags.title * bw;
    int handleh = handle_w + flags.handle * bw;
    temp_h = attrib.height + bw * 2 + titleh + handleh;
    temp_w = attrib.width + bw * 2;

    while (((test_y + temp_h) < workh) && !loc_ok) {
        test_x = 0;
        while (((test_x + temp_w) < workw) && !loc_ok) {
            loc_ok = True;
            list<WaWindow *>::iterator it = wascreen->wawindow_list.begin();
            for (; it != wascreen->wawindow_list.end() &&
                     (loc_ok == True); it++) {
                if ((*it != this) && ((*it)->flags.tasklist) &&
                    ((*it)->desktop_mask &
                     (1L << wascreen->current_desktop->number)) &&
                    ((((*it)->attrib.x + (*it)->attrib.width) > 0 &&
                      (*it)->attrib.x < workw) && 
                     (((*it)->attrib.y + (*it)->attrib.height) > 0 &&
                      (*it)->attrib.y < workh))) {
                    bw = (*it)->flags.border * (*it)->border_w;
                    titleh = (*it)->title_w + (*it)->flags.title * bw;
                    handleh = (*it)->handle_w + (*it)->flags.handle * bw;
                    
                    th = (*it)->attrib.height + bw * 2 + titleh + handleh;
                    tw = (*it)->attrib.width + bw * 2;

                    (*it)->Gravitate(RemoveGravity);
                    tx = (*it)->attrib.x - workx - 1;
                    ty = (*it)->attrib.y - worky - 1;
                    (*it)->Gravitate(ApplyGravity);

                    if ((tx < (test_x + temp_w)) &&
                        ((tx + tw) > test_x) &&
                        (ty < (test_y + temp_h)) &&
                        ((ty + th) > test_y)) {
                        loc_ok = False;
                        test_x = tx + tw;
                    }
                }
            }
            test_x += 1;
        }
        test_y += 1;
    }
    if (loc_ok != False) {
        attrib.x = test_x + workx - 1;
        attrib.y = test_y + worky;
        Gravitate(ApplyGravity);
        RedrawWindow();
    }
    else
        Gravitate(ApplyGravity);
}

/**
 * @fn    DesktopMask(XEvent *, WaAction *ac)
 * @brief Set desktop mask
 *
 * Sets the mask for in which desktops the window should be a member.
 *
 * @param ac WaAction object
 */
void WaWindow::DesktopMask(XEvent *, WaAction *ac) {
    if (ac->param) {
        if (! strncasecmp("all", ac->param, 3))
            desktop_mask = (1L << 16) - 1;
        else {
            desktop_mask = 0;
            char *token = strtok(ac->param, " \t");
            while (token) {
                unsigned int desk = (unsigned int) atoi(token);
                if (desk < wascreen->config.desktops)
                    desktop_mask |= (1L << desk);
                token = strtok(NULL, " \t");
            }
        }
        if (desktop_mask == 0) desktop_mask = (1L << 0);
        
        if (desktop_mask & (1L << wascreen->current_desktop->number))
            Show();
        else
            Hide();

        net->SetDesktop(this);
        net->SetDesktopMask(this);
    }
}

/**
 * @fn    JoinDesktop(XEvent *, WaAction *ac)
 * @brief Join desktop
 *
 * Join window to desktop specified by action parameter.
 *
 * @param ac WaAction object
 */
void WaWindow::JoinDesktop(XEvent *, WaAction *ac) {
    if (ac->param) {
        unsigned int desk = (unsigned int) atoi(ac->param);
        if (desk < wascreen->config.desktops) {
            desktop_mask |= (1L << desk);
            if (desktop_mask & (1L << wascreen->current_desktop->number))
                Show();
            net->SetDesktop(this);
            net->SetDesktopMask(this);
        }
    }
}

/**
 * @fn    PartDesktop(XEvent *, WaAction *ac)
 * @brief Part desktop
 *
 * Part window from desktop specified by action parameter.
 *
 * @param ac WaAction object
 */
void WaWindow::PartDesktop(XEvent *, WaAction *ac) {
    if (ac->param) {
        unsigned int desk = (unsigned int) atoi(ac->param);
        if (desk < wascreen->config.desktops) {
            long int new_mask = desktop_mask & ~(1L << desk);
            if (new_mask) {
                desktop_mask = new_mask;
                if (! (desktop_mask &
                       (1L << wascreen->current_desktop->number))) 
                    Hide();
                net->SetDesktop(this);
                net->SetDesktopMask(this);
            }
        }
    }
}

/**
 * @fn    PartCurrentDesktop(XEvent *, WaAction *)
 * @brief Part current desktop
 *
 * Part window from current desktop if window is part of another desktop.
 */
void WaWindow::PartCurrentDesktop(XEvent *, WaAction *) {
    long int new_mask = desktop_mask &
        ~(1L << wascreen->current_desktop->number);
        if (new_mask) {
            desktop_mask = new_mask;
            Hide();
            net->SetDesktop(this);
            net->SetDesktopMask(this);
        }
}

/**
 * @fn    JoinAllDesktops(XEvent *, WaAction *)
 * @brief Join all desktops
 *
 * Join window to all desktops.
 */
void WaWindow::JoinAllDesktops(XEvent *, WaAction *) {
    desktop_mask = (1L << 16) - 1;
    Show();
    net->SetDesktop(this);
    net->SetDesktopMask(this);
}

/**
 * @fn    PartAllDesktopsExceptCurrent(XEvent *, WaAction *)
 * @brief Part all desktops
 *
 * Part window from all desktops except current desktop.
 */
void WaWindow::PartAllDesktopsExceptCurrent(XEvent *, WaAction *) {
    desktop_mask = (1L << wascreen->current_desktop->number);
    Show();
    net->SetDesktop(this);
    net->SetDesktopMask(this);
}

/**
 * @fn    PartCurrentJoinDesktop(XEvent *, WaAction *ac)
 * @brief Part current desktop and join desktop
 *
 * Part current desktop and join desktop specified by parameter.
 *
 * @param ac WaAction object
 */
void WaWindow::PartCurrentJoinDesktop(XEvent *, WaAction *ac) {
    if (ac->param) {
        unsigned int desk = (unsigned int) atoi(ac->param);
        if (desk < wascreen->config.desktops) {
            desktop_mask = desktop_mask &
                ~(1L << wascreen->current_desktop->number);
            desktop_mask |= (1L << desk);
            if (desktop_mask & (1L << wascreen->current_desktop->number))
                Show();
            else Hide();
            net->SetDesktop(this);
            net->SetDesktopMask(this);
        }
    }
}

/**
 * @fn    EvAct(XEvent *e, EventDetail *ed, list<WaAction *> *acts,
 *              int etype)
 * @brief Calls WaWindow function
 *
 * Tries to match an occurred X event with the actions in an action list.
 * If we have a match then we execute that action.
 *
 * @param e X event that have occurred
 * @param ed Event details
 * @param acts List with actions to match event with
 * @param etype Type of window event occurred on
 */
void WaWindow::EvAct(XEvent *e, EventDetail *ed, list<WaAction *> *acts,
                     int etype) {
    XEvent fev;
    bool replay = false, wait_release = false, match = false;
    
    list<WaAction *>::iterator it = acts->begin();
    if (waimea->eh->move_resize != EndMoveResizeType)
        ed->mod |= MoveResizeMask;
    else if (etype == WindowType) {
        if (ed->type == ButtonPress) {
            for (; it != acts->end(); ++it) {
                if ((*it)->type == ButtonRelease &&
                    (*it)->detail == ed->detail &&
                    (! ((*it)->mod & MoveResizeMask)))
                    wait_release = match = true;
            }
        }
        else if (ed->type == KeyPress) {
            for (; it != acts->end(); ++it) {
                if ((*it)->type == KeyRelease &&
                    (*it)->detail == ed->detail &&
                    (! ((*it)->mod & MoveResizeMask))) {
                    wait_release = match = true;
                    XAutoRepeatOff(display);
                }
            }
        }
    }
    it = acts->begin();
    for (; it != acts->end(); ++it) {
        if (eventmatch(*it, ed)) {
            match = true;
            XAutoRepeatOn(display);
            if ((*it)->replay && ! wait_release) replay = true;
            if ((*it)->delay.tv_sec || (*it)->delay.tv_usec) {
                Interrupt *i = new Interrupt(*it, e, id);                
                waimea->timer->AddInterrupt(i);
            } else {
                if ((*it)->exec)
                    waexec((*it)->exec, wascreen->displaystring);
                else
                    ((*this).*((*it)->winfunc))(e, *it);
            }
        }
    }
    if (waimea->eh->move_resize != EndMoveResizeType) {
        if (deleted) delete this;
        return;
    }
    
    XSync(display, false);
    while (XCheckTypedEvent(display, FocusOut, &fev))
        waimea->eh->EvFocus(&fev.xfocus);
    while (XCheckTypedEvent(display, FocusIn, &fev))
        waimea->eh->EvFocus(&fev.xfocus);
    switch (etype) {
        case WindowType:
            if (ed->type == ButtonPress || ed->type == ButtonRelease ||
                ed->type == DoubleClick) {
                if (replay || ! match)
                    XAllowEvents(display, ReplayPointer, e->xbutton.time);
                else
                    XAllowEvents(display, AsyncPointer, e->xbutton.time);
            }
            else if (ed->type == KeyPress || ed->type == KeyRelease) {
                if (replay || ! match)
                    XAllowEvents(display, ReplayKeyboard, e->xbutton.time);
                else
                    XAllowEvents(display, AsyncKeyboard, e->xbutton.time);
            }
            else if (ed->type == MapRequest && ! mapped) {
                net->SetState(this, NormalState);
                net->SetVirtualPos(this);
            }
            break;
    }
    if (deleted) delete this;
}


/**
 * @fn    WaChildWindow(WaWindow *wa_win, Window parent, int bw, int type) :
 *        WindowObject(0, type)
 * @brief Constructor for WaChildWindow class
 *
 * Creates a child window, could be of one of these types: FrameType,
 * TitleType, LabelType, HandleType, CButtonType, IButtonType, MButtonType,
 * LGripType, RGripType.
 *
 * @param wa_win WaWindow who wish to use the child window
 * @param parent Parent window to child window
 * @param bw Border width
 * @param type Type of window
 */
WaChildWindow::WaChildWindow(WaWindow *wa_win, Window parent, int type) :
    WindowObject(0, type) {
    XSetWindowAttributes attrib_set;
    
    wa = wa_win;
    wascreen = wa->wascreen;
    display = wa->display;
    ic = wascreen->ic;

    pressed = false;
    int create_mask = CWOverrideRedirect | CWBorderPixel | CWEventMask |
        CWColormap;
    attrib_set.border_pixel = wa->wascreen->wstyle.border_color.getPixel();
    attrib_set.colormap = wa->wascreen->colormap;
    attrib_set.override_redirect = true;
    attrib_set.event_mask = ButtonPressMask | ButtonReleaseMask |
        EnterWindowMask | LeaveWindowMask;
    attrib.x = 0;
    attrib.y = 0;
    attrib.width  = 1;
    attrib.height = 1;
    
    switch (type) {
        case FrameType:
            attrib_set.event_mask |= SubstructureRedirectMask;
            create_mask |= CWBackPixmap;
            attrib_set.background_pixmap = ParentRelative;
            attrib.x = wa->attrib.x - wa->border_w;
            attrib.y = wa->attrib.y - wa->title_w - wa->border_w * 2;
            attrib.width = wa->attrib.width;
            attrib.height = wa->attrib.height + wa->title_w + wa->handle_w +
                wa->border_w * 2;
            break;
        case LabelType:
            f_texture = &wascreen->wstyle.l_focus;
            u_texture = &wascreen->wstyle.l_unfocus;
            attrib_set.event_mask |= ExposureMask;
            break;
        case TitleType:
            f_texture = &wascreen->wstyle.t_focus;
            u_texture = &wascreen->wstyle.t_unfocus;
            break;
        case HandleType:
            f_texture = &wascreen->wstyle.h_focus;
            u_texture = &wascreen->wstyle.h_unfocus;
            break;
        case ButtonType:
            attrib_set.event_mask |= ExposureMask;
            break;
        case LGripType:
            f_texture = &wascreen->wstyle.g_focus;
            u_texture = &wascreen->wstyle.g_unfocus;
            create_mask |= CWCursor;
            attrib_set.cursor = wa->waimea->resizeleft_cursor;
            break;
        case RGripType:
            f_texture = &wascreen->wstyle.g_focus;
            u_texture = &wascreen->wstyle.g_unfocus;
            create_mask |= CWCursor;
            attrib_set.cursor = wa->waimea->resizeright_cursor;
            break;
    }
    id = XCreateWindow(display, parent, attrib.x, attrib.y,
                       attrib.width, attrib.height, 0, CopyFromParent,
                       CopyFromParent, CopyFromParent, create_mask,
                       &attrib_set);    

#ifdef XFT
    if (type == LabelType || type == TitleType) {
        xftdraw = XftDrawCreate(display, (Drawable) id,
                                wascreen->visual, wascreen->colormap);
    }
#endif // XFT
    
    wa->waimea->window_table.insert(make_pair(id, this));
}

/**
 * @fn    ~WaChildWindow()
 * @brief Destructor for WaChildWindow class
 *
 * Destroys the window and removes it from the window_table hash_map.
 */
WaChildWindow::~WaChildWindow(void) {
    
#ifdef XFT
    if (type == LabelType) XftDrawDestroy(xftdraw);
#endif // XFT
    
    wa->waimea->window_table.erase(id);
    XDestroyWindow(display, id);
}

/**
 * @fn    Render(void)
 * @brief Render WaChildWindow background
 *
 * Renders WaChildWindow background pixmap for the current window state.
 */
void WaChildWindow::Render(void) {
    bool done = false;
    WaTexture *texture = (wa->has_focus)? f_texture: u_texture;
    Pixmap pixmap = None;

#ifdef RENDER
    Pixmap xpixmap;
    int pos_x = wa->attrib.x + attrib.x + wa->border_w;
    int pos_y = wa->attrib.y - wa->title_w + attrib.y;
    if (texture->getOpacity()) {
        xpixmap = XCreatePixmap(wascreen->pdisplay, wascreen->id,
                                attrib.width, attrib.height,
                                wascreen->screen_depth);
    } else if (wa->render_if_opacity && IsDrawable()) return;
#endif // RENDER
    
    switch (type) {
        case ButtonType: {
            bool flag = false;
            done = true;
            switch (bstyle->cb) {
                case MaxCBoxType: flag = wa->flags.max; break;
                case ShadeCBoxType: flag = wa->flags.shaded; break;
                case StickCBoxType: flag = wa->flags.sticky; break;
                case TitleCBoxType: flag = wa->flags.title; break;
                case HandleCBoxType: flag = wa->flags.handle; break;
                case BorderCBoxType: flag = wa->flags.border; break;
                case AllCBoxType: flag = wa->flags.all; break;
                case AOTCBoxType: flag = wa->flags.alwaysontop; break;
                case AABCBoxType: flag = wa->flags.alwaysatbottom; break;
            }
            if (flag) {
                pixmap = (pressed) ? bstyle->p_pressed2:
                    ((wa->has_focus)? bstyle->p_focused2:
                     bstyle->p_unfocused2);
                texture = (pressed) ? &bstyle->t_pressed2:
                    ((wa->has_focus)? &bstyle->t_focused2:
                     &bstyle->t_unfocused2);
            } else {
                pixmap = (pressed) ? bstyle->p_pressed:
                    ((wa->has_focus)? bstyle->p_focused:
                     bstyle->p_unfocused);
                texture = (pressed) ? &bstyle->t_pressed:
                    ((wa->has_focus)? &bstyle->t_focused:
                     &bstyle->t_unfocused);
            }

#ifdef RENDER
            if (texture->getOpacity())
                pixmap = ic->xrender(pixmap, attrib.width, attrib.height,
                                     texture, wascreen->xrootpmap_id, pos_x,
                                     pos_y, xpixmap);
#endif // RENDER
                
        } break;
        case LGripType:
        case RGripType:
            done = true;
#ifdef RENDER
            if (texture->getOpacity())
                pixmap = ic->xrender((wa->has_focus)? wascreen->fgrip:
                                     wascreen->ugrip,
                                     attrib.width, attrib.height,
                                     texture, wascreen->xrootpmap_id, pos_x,
                                     pos_y, xpixmap);
            else
#endif // RENDER
                pixmap = (wa->has_focus)? wascreen->fgrip: wascreen->ugrip;
            
            break;
    }
    if (! done) {
        if (texture->getTexture() == (WaImage_Flat | WaImage_Solid)) {
            pixmap = None;
#ifdef RENDER
            if (texture->getOpacity())
                pixmap = ic->xrender(None, attrib.width, attrib.height,
                                     texture, wascreen->xrootpmap_id, pos_x,
                                     pos_y, xpixmap);
#endif // RENDER
            
        } else
            pixmap = ic->renderImage(attrib.width,
                                     attrib.height, texture
                                      
#ifdef RENDER
                                     , wascreen->xrootpmap_id, pos_x, pos_y,
                                     xpixmap
#endif // RENDER
                                      
                                     );
    }
    
    if (pixmap) {
        if (wascreen->config.db) Draw((Drawable) pixmap);
        else XSetWindowBackgroundPixmap(display, id, pixmap);
        
#ifdef PIXMAP
        if (
            
#ifdef RENDER        
            (! texture->getOpacity()) &&
#endif // RENDER
            
            (texture->getTexture() & WaImage_Pixmap)) {
            XSync(display, false);
            imlib_context_push(*texture->getContext());
            imlib_free_pixmap_and_mask(pixmap);
            imlib_context_pop();
            pixmap = None;
        }
#endif // PIXMAP
        
    }
    else {
        if (wascreen->config.db) Draw((Drawable) 2);
        else XSetWindowBackground(display, id,
                                  texture->getColor()->getPixel());
    }

    if (! wascreen->config.db) Draw();

#ifdef RENDER
    if (pixmap && texture->getOpacity()) {
        XSync(display, false);
        XFreePixmap(wascreen->pdisplay, pixmap);
    }
#endif // RENDER
    
}

/**
 * @fn    Draw(Drawable drawable)
 * @brief Draw text/decorations
 *
 * Draws text in window title window and button graphics for button windows.
 * If drawable is other then NULL then we draw on this instead.
 *
 * @param drawable Drawable to draw on
 */
void WaChildWindow::Draw(Drawable drawable) {
    int x = 0, y = 0, length, text_w;
    
    if (! drawable) XClearWindow(display, id);
    switch (type) {
        case TitleType:
            if (! drawable) return;
            if (wa->label->IsDrawable()) {
                if (drawable == (Drawable) 2) {
                    WaTexture *texture = (wa->has_focus)? f_texture: u_texture;
                    XSetWindowBackground(display, id,
                                         texture->getColor()->getPixel());
                } else
                    XSetWindowBackgroundPixmap(display, id, drawable);
                XClearWindow(display, id);
                return;
            }
            x = wa->label->g_x;
            y = 2;
        case LabelType: {
            if (type == LabelType && drawable) {
                if (drawable == ParentRelative) {
                    if (wa->title->IsDrawable()) {
                        XSetWindowBackgroundPixmap(display, id, drawable);
                        XClearWindow(display, id);
                        return;
                    }
                    drawable = 0;
                }
            }
            Pixmap p_tmp;
            if (drawable) {
                p_tmp = XCreatePixmap(display, wascreen->id,
                                      attrib.width, attrib.height,
                                      wascreen->screen_depth);
                if (drawable == (Drawable) 2) {
                    XGCValues values;
                    WaTexture *texture = (wa->has_focus)? f_texture: u_texture;
                    values.foreground = texture->getColor()->getPixel();
                    GC gc = XCreateGC(display, wascreen->id, GCForeground,
                                      &values);
                    XFillRectangle(display, p_tmp, gc, 0, 0, attrib.width,
                                   attrib.height);
                    XFreeGC(display, gc);
                } else {
                    GC gc = DefaultGC(display, wascreen->screen_number);
                    XCopyArea(display, drawable, p_tmp, gc, 0, 0, attrib.width,
                              attrib.height, 0, 0);
                }
            }
            length = strlen(wa->name);
            WaFont *wafont = (wa->has_focus)? &wascreen->wstyle.wa_font:
                &wascreen->wstyle.wa_font_u;
            text_w = wafont->Width(display, wa->name, length);
    
            if (text_w > (wa->label->attrib.width - 10)) x += 2;
            else {
                switch (wascreen->wstyle.justify) {
                    case LeftJustify: x += 2; break;
                    case CenterJustify:
                        x += (wa->label->attrib.width / 2) - (text_w / 2);
                        break;
                    case RightJustify:
                        x += (wa->label->attrib.width - text_w) - 2;
                        break;
                }
            }

            y += wascreen->wstyle.y_pos;

#ifdef    XFT
            if (drawable) XftDrawChange(xftdraw, p_tmp);
#endif // XFT            
            
            wafont->Draw(display, (drawable)? p_tmp : id,
                         
#ifdef    XFT
                     xftdraw,
#endif // XFT
                 
                     x, y, wa->name, length);

            if (drawable) {
                XSetWindowBackgroundPixmap(display, id, p_tmp);
                XClearWindow(display, id);
                XFreePixmap(display, p_tmp);
            }
        } break;
        case ButtonType: {
            if (drawable) {
                if (drawable == (Drawable) 2) {
                    WaTexture *texture = (wa->has_focus)? f_texture: u_texture;
                    XSetWindowBackground(display, id,
                                         texture->getColor()->getPixel());
                } else
                    XSetWindowBackgroundPixmap(display, id, drawable);
                XClearWindow(display, id);
            }
            GC *gc;
            if (bstyle->fg) {
                bool flag = false;
                switch (bstyle->cb) {
                    case MaxCBoxType: flag = wa->flags.max; break;
                    case ShadeCBoxType: flag = wa->flags.shaded; break;
                    case StickCBoxType: flag = wa->flags.sticky; break;
                    case TitleCBoxType: flag = wa->flags.title; break;
                    case HandleCBoxType: flag = wa->flags.handle; break;
                    case BorderCBoxType: flag = wa->flags.border; break;
                    case AllCBoxType: flag = wa->flags.all; break;
                    case AOTCBoxType: flag = wa->flags.alwaysontop; break;
                    case AABCBoxType: flag = wa->flags.alwaysatbottom; break;
                }
                if (flag) {
                    gc = (pressed) ? &bstyle->g_pressed2:
                        ((wa->has_focus)? &bstyle->g_focused2:
                         &bstyle->g_unfocused2);
                }
                else {
                    gc = (pressed) ? &bstyle->g_pressed:
                        ((wa->has_focus)? &bstyle->g_focused:
                         &bstyle->g_unfocused);
                }                    
                
                switch (bstyle->cb) {
                    case ShadeCBoxType:
                        XDrawRectangle(display, id, *gc, 2, 3,
                                       wa->title_w - 9, 2);
                        break;
                    case CloseCBoxType:
                        XDrawLine(display, id, *gc, 2, 2, wa->title_w - 7,
                                  wa->title_w - 7);
                        XDrawLine(display, id, *gc, 2, wa->title_w - 7,
                                  wa->title_w - 7, 2);
                        break;
                    case MaxCBoxType:
                        if (wa->flags.max) {
                            int w = (2*(wa->title_w - 8))/3;
                            int h = (2*(wa->title_w - 8))/3 - 1;
                            int y = (wa->title_w - 8) - h + 1;
                            int x = (wa->title_w - 8) - w + 1;
                            XDrawRectangle(display, id, *gc, 2, y, w,h);
                            XDrawLine(display, id, *gc, 2, y + 1, 2 + w,
                                      y + 1);
                            XDrawLine(display, id, *gc, x, 2, x + w, 2);
                            XDrawLine(display, id, *gc, x, 3, x + w, 3);
                            XDrawLine(display, id, *gc, x, 2, x, y);
                            XDrawLine(display, id, *gc, x + w, 2, x + w,
                                      2 + h);
                            XDrawLine(display, id, *gc, 2 + w, 2 + h, x + w,
                                      2 + h);
                        } else {
                            XDrawRectangle(display, id, *gc, 2, 2,
                                           wa->title_w - 9, wa->title_w - 9);
                            XDrawLine(display, id, *gc, 2, 3, wa->title_w - 8,
                                      3);
                        }
                        break;
                    default:
                        XFillRectangle(display, id, *gc, 4, 4,
                                       wa->title_w - 11, wa->title_w - 11);
                }
            }
        } break;
        default:
            if (drawable) {
                if (drawable == (Drawable) 2) {
                    WaTexture *texture = (wa->has_focus)? f_texture: u_texture;
                    XSetWindowBackground(display, id,
                                         texture->getColor()->getPixel());
                } else
                    XSetWindowBackgroundPixmap(display, id, drawable);
                XClearWindow(display, id);
            }
    }
}

/**
 * @fn    IsDrawable(void)
 * @brief Check if drawable
 *
 * Checks if if window is double buffer drawable (if it will have a pixmap
 * background).
 */
bool WaChildWindow::IsDrawable(void) {
    WaTexture *texture = (wa->has_focus)? f_texture:
        u_texture;
    if (texture->getTexture() & WaImage_ParentRelative)
        return false;
    else
        return true;
}


/**
 * Wrapper functions
 */
void WaWindow::ViewportMove(XEvent *e, WaAction *wa) {
    wascreen->ViewportMove(e, wa);
}
void WaWindow::ViewportRelativeMove(XEvent *e, WaAction *wa) {
    wascreen->ViewportRelativeMove(e, wa);
}
void WaWindow::ViewportFixedMove(XEvent *e, WaAction *wa) {
    wascreen->ViewportFixedMove(e, wa);
}
void WaWindow::MoveViewportLeft(XEvent *, WaAction *) {
    wascreen->MoveViewport(WestDirection);
}
void WaWindow::MoveViewportRight(XEvent *, WaAction *) {
    wascreen->MoveViewport(EastDirection);
}
void WaWindow::MoveViewportUp(XEvent *, WaAction *) {
    wascreen->MoveViewport(NorthDirection);
}
void WaWindow::MoveViewportDown(XEvent *, WaAction *) {
    wascreen->MoveViewport(SouthDirection);
}
void WaWindow::PointerRelativeWarp(XEvent *e, WaAction *ac) {
    wascreen->PointerRelativeWarp(e, ac);
}
void WaWindow::PointerFixedWarp(XEvent *e, WaAction *ac) {
    wascreen->PointerFixedWarp(e, ac);
}
void WaWindow::GoToDesktop(XEvent *, WaAction *ac) {
    if (ac->param) wascreen->GoToDesktop((unsigned int) atoi(ac->param));
}
void WaWindow::NextDesktop(XEvent *, WaAction *) {
    wascreen->NextDesktop(NULL, NULL);
}
void WaWindow::PreviousDesktop(XEvent *, WaAction *) {
    wascreen->PreviousDesktop(NULL, NULL);
}
void WaWindow::Restart(XEvent *e, WaAction *ac) {
    wascreen->Restart(e, ac);
}
void WaWindow::Exit(XEvent *e, WaAction *ac) {
    wascreen->Exit(e, ac);
}


syntax highlighted by Code2HTML, v. 0.9.1