/**
 * @file   Dockapp.cc
 * @author David Reveman <david@waimea.org>
 * @date   29-Nov-2001 22:13:22
 *
 * @brief Implementation of DockappHandler class  
 *
 * This class handles docking of 'dockapp' programs.
 *
 * Copyright (C) David Reveman. All rights reserved.
 *
 */

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

#include "Dockapp.hh"

/**
 * @fn    DockappHandler(void)
 * @brief Constructor for DockappHandler class
 *
 * Creates the dockapp handler window.
 *
 * @param scrn WaScreen to create dockapp handler window on.
 * @param ds Style structure to use when creating this dockapphandler
 */
DockappHandler::DockappHandler(WaScreen *scrn, DockStyle *ds) :
    WindowObject(0, DockHandlerType) {
    XSetWindowAttributes attrib_set;

    style = ds;
    wascreen = scrn;
    waimea = wascreen->waimea;
    display = waimea->display;
    background = (Pixmap) 0;
    hidden = true;
    x = 1;
    y = 1;
    if (style->geometry & XValue ||
        style->geometry & YValue) {
        if (style->geometry & XValue) {
            x = style->x;
        }
        if (style->geometry & YValue) {
            y = style->y;
        }
    } else
        style->geometry = XValue | YValue | XNegative;
    
    width = 0;
    height = 0;

    dockapp_list = new list<Dockapp *>;

    attrib_set.background_pixel = None;
    attrib_set.border_pixel = style->style.border_color.getPixel();
    attrib_set.colormap = wascreen->colormap;
    attrib_set.override_redirect = true;
    attrib_set.event_mask = SubstructureRedirectMask | ButtonPressMask |
        EnterWindowMask | LeaveWindowMask;

    id = XCreateWindow(display, wascreen->id, 0, 0, 1, 1,
                       style->style.border_width, wascreen->screen_depth,
                       CopyFromParent, wascreen->visual, CWOverrideRedirect |
                       CWBackPixel | CWEventMask | CWColormap | CWBorderPixel,
                       &attrib_set);

    if (style->stacking == AlwaysOnTop)
        wascreen->always_on_top_list.push_back(id);
    else
        wascreen->always_at_bottom_list.push_back(id);

    if (! style->inworkspace) {
        wm_strut = new WMstrut;
        wm_strut->window = id;
        wm_strut->left = 0;
        wm_strut->right = 0;
        wm_strut->top = 0;
        wm_strut->bottom = 0;
        wascreen->strut_list.push_back(wm_strut);
    }
    waimea->window_table.insert(make_pair(id, this));
}

/**
 * @fn    ~DockappHandler(void)
 * @brief Destructor for DockappHandler class
 *
 * Removes all dockapps and destroys the dockapp handler window.
 */
DockappHandler::~DockappHandler(void) {
    if (style->stacking == AlwaysOnTop)
        wascreen->always_on_top_list.remove(id);
    else
        wascreen->always_at_bottom_list.remove(id);
    LISTPTRDELITEMS(dockapp_list);
    XDestroyWindow(display, id);
    if (! style->inworkspace) {
        wascreen->strut_list.remove(wm_strut);
        delete wm_strut;
    }
    waimea->window_table.erase(id);

    delete dockapp_list;
}


/**
 * @fn    Update(void)
 * @brief Update the dockapp handler
 *
 * Repositions all dockapps. Moves and resizes dockapp handler.
 */
void DockappHandler::Update(void) {
    int dock_x = style->gridspace;
    int dock_y = style->gridspace;    
    
    if (dockapp_list->empty()) {
        if (! style->inworkspace) {
            wm_strut->left = 0;
            wm_strut->right = 0;
            wm_strut->top = 0;
            wm_strut->bottom = 0;
            wascreen->UpdateWorkarea();
        }
        XUnmapWindow(display, id);
        return;
    }
    map_x = x;
    map_y = y;
    width = style->gridspace;
    height = style->gridspace;


    list<Dockapp *>::iterator it = dockapp_list->begin();
    for (; it != dockapp_list->end(); ++it)
        (*it)->added = false;

    list<Dockapp *>::reverse_iterator d_it;
    list<Dockapp *> *tmp_list = new list<Dockapp *>;
    list<Regex *>::reverse_iterator reg_it = style->order.rbegin();
    list<int>::reverse_iterator regt_it = style->order_type.rbegin();
    for (; reg_it != style->order.rend(); ++reg_it, ++regt_it) {
        d_it = dockapp_list->rbegin();
        if (*regt_it == NameMatchType) {
            for (; d_it != dockapp_list->rend(); ++d_it) {
                if ((*d_it)->c_hint &&
                    ((*reg_it)->Match((*d_it)->c_hint->res_name))) {
                    (*d_it)->added = true;
                    tmp_list->push_front(*d_it);
                }
            }
        } else if (*regt_it == ClassMatchType) {
            for (; d_it != dockapp_list->rend(); ++d_it) {
                if ((*d_it)->c_hint &&
                    ((*reg_it)->Match((*d_it)->c_hint->res_class))) {
                    (*d_it)->added = true;
                    tmp_list->push_front(*d_it);
                }
            }
        } else if (*regt_it == TitleMatchType) {
            for (; d_it != dockapp_list->rend(); ++d_it) {
                if ((*d_it)->title &&
                    ((*reg_it)->Match((*d_it)->title))) {
                    (*d_it)->added = true;
                    tmp_list->push_front(*d_it);
                }
            }
        }
    }
    it = dockapp_list->begin();
    for (; it != dockapp_list->end(); ++it)
        if (! (*it)->added) {
            (*it)->added = true;
            tmp_list->push_back(*it);
        }
    
    while (! dockapp_list->empty())
        dockapp_list->pop_back();
    delete dockapp_list;
    dockapp_list = tmp_list;
    
    it = dockapp_list->begin();
    for (; it != dockapp_list->end(); ++it) {
        switch (style->direction) {
            case VerticalDock:
                if (((*it)->width + style->gridspace * 2) > width)
                    width = (*it)->width + style->gridspace * 2;
                break;
            case HorizontalDock:
                if (((*it)->height + style->gridspace * 2) > height)
                    height = (*it)->height + style->gridspace * 2;
                break;
        }
    }
    it = dockapp_list->begin();
    XGrabServer(display);
    for (; it != dockapp_list->end(); ++it) {
        if (validatedrawable((*it)->id)) {
            switch (style->direction) {
                case VerticalDock:
                    dock_y = height;
                    height += (*it)->height + style->gridspace;
                    dock_x = (((width - style->gridspace * 2) -
                               (*it)->width) / 2) + style->gridspace;
                    break;
                case HorizontalDock:
                    dock_x = width;
                    width += (*it)->width + style->gridspace;
                    dock_y = (((height - style->gridspace * 2) -
                               (*it)->height) / 2) + style->gridspace;
                    break;
            }
            (*it)->x = dock_x;
            (*it)->y = dock_y;
            XMoveWindow(display, (*it)->id, dock_x, dock_y);
        }
    }
    XUngrabServer(display);

    if (! style->inworkspace)
        wm_strut->left = wm_strut->right = wm_strut->top =
            wm_strut->bottom = 0;
    if (style->geometry & XNegative) {
        map_x = wascreen->width - style->style.border_width * 2 -
            width + x;
        if (! style->inworkspace)
            wm_strut->right = wascreen->width - map_x;
    } else {
        if (! style->inworkspace)
            wm_strut->left = map_x + style->style.border_width * 2 + width;
    }
    
    if (style->geometry & YNegative) {
        map_y = wascreen->height - style->style.border_width * 2 -
            height + y;
        if (style->direction == HorizontalDock && (! style->inworkspace)) {
            wm_strut->bottom = wascreen->height - map_y;
            wm_strut->right = wm_strut->left = 0;
        }
    } else
        if (style->direction == HorizontalDock && (! style->inworkspace)) {
            wm_strut->top = map_y + style->style.border_width * 2 + height;
            wm_strut->right = wm_strut->left = 0;
        }

    if (style->centered) {
        switch (style->direction) {
            case VerticalDock: map_y = wascreen->height / 2 - height / 2;
                break;
            case HorizontalDock: map_x = wascreen->width / 2 - width / 2;
                break;
        }
    }
    XResizeWindow(display, id, width, height);
    XMoveWindow(display, id, map_x, map_y);
    if (style->desktop_mask & (1L << wascreen->current_desktop->number)) {
        XMapWindow(display, id);
        hidden = false;
        Render();
        wascreen->UpdateWorkarea();
    }
}

/**
 * @fn    Render(void)
 * @brief Render background
 *
 * Renders background for dockapp holder.
 */
void DockappHandler::Render(void) {
    WaTexture *texture = &style->style.texture;
    
#ifdef RENDER
    if (texture->getOpacity()) {
        background = XCreatePixmap(wascreen->pdisplay, wascreen->id, width,
                                   height, wascreen->screen_depth);
    }
#endif // RENDER
    
    if (texture->getTexture() == (WaImage_Flat | WaImage_Solid)) {
        background = None;
        background_pixel = texture->getColor()->getPixel();
#ifdef RENDER        
        if (texture->getOpacity()) {
            background = wascreen->ic->xrender(None, width, height, texture,
                                               wascreen->xrootpmap_id,
                                               map_x +
                                               style->style.border_width,
                                               map_y +
                                               style->style.border_width,
                                               background);
            XSetWindowBackgroundPixmap(display, id, background);
        } else
            XSetWindowBackground(display, id, background_pixel);
#else // ! RENDER
        XSetWindowBackground(display, id, background_pixel);
#endif // RENDER
        
    } else {
        background = wascreen->ic->renderImage(width, height, texture

#ifdef RENDER
                                               , wascreen->xrootpmap_id,
                                               map_x +
                                               style->style.border_width,
                                               map_y +
                                               style->style.border_width,
                                               background
#endif // RENDER
                                               
                                               );
        XSetWindowBackgroundPixmap(display, id, background);
    }
    XClearWindow(display, id);
    
#ifdef RENDER    
    if (texture->getOpacity()) XFreePixmap(wascreen->pdisplay, background);
#endif // RENDER
}
    
/**
 * @fn    Dockapp(Window win, DockappHandler *dhand)
 * @brief Constructor for Dockapp class
 *
 * Reparents window to dockapp handler window and adds it to the window_table
 * hash_map.
 */
Dockapp::Dockapp(Window win, DockappHandler *dhand) :
    WindowObject(win, DockAppType) {
    XWindowAttributes attrib;
    dh = dhand;
    client_id = win;
    display = dh->display;
    deleted = false;
    c_hint = NULL;
    title = NULL;

    XWMHints *wmhints = XGetWMHints(display, win);
    if (wmhints) {
        if ((wmhints->flags & IconWindowHint) &&
            (wmhints->icon_window != None)) {
            XUnmapWindow(display, client_id);
            icon_id = wmhints->icon_window;
            id = icon_id;
        } else {
            icon_id = None;
            id = client_id;
        }
        XFree(wmhints);
    } else {
        icon_id = None;
        id = client_id;
    }    
    XGrabServer(display);
    if (validatedrawable(id)) {
        if (XGetWindowAttributes(display, id, &attrib)) {
            width = attrib.width;
            height = attrib.height;
        } else
            width = height = 64;
        
        XSetWindowBorderWidth(display, id, 0);
        XReparentWindow(display, id, dh->id, dh->width, dh->height);
        XMapRaised(display, id);
        XSelectInput(display, id, StructureNotifyMask |
                     SubstructureNotifyMask);
    } else {
        XUngrabServer(display);
        delete this;
        return;
    }
    XUngrabServer(display);
    dh->waimea->window_table.insert(make_pair(id, this));
    dh->dockapp_list->push_back(this);
}

/**
 * @fn    ~Dockapp(void)
 * @brief Destructor for Dockapp class
 *
 * Reparents dockapp window back to root if it still exists and removes it
 * from the window_table hash_map and dockapp list.
 */
Dockapp::~Dockapp(void) {
    dh->dockapp_list->remove(this);
    dh->waimea->window_table.erase(id);
    if (! deleted) {
        XGrabServer(display);
        if (validatedrawable(id)) {
            if (icon_id) XUnmapWindow(display, id);
            XReparentWindow(display, id, dh->wascreen->id,
                            dh->map_x + x, dh->map_y + y);
            XMapWindow(display, client_id);
        }
        XUngrabServer(display);
    }
    if (c_hint) {
        XFree(c_hint->res_name);
        XFree(c_hint->res_class);
        XFree(c_hint);
    }
    if (title) XFree(title);

}


syntax highlighted by Code2HTML, v. 0.9.1