Logo Search packages:      
Sourcecode: fbdesk version File versions  Download package

Icon.cc

// Icon.cc for fbdesk
// Copyright (c) 2002 - 2003 Henrik Kinnunen (fluxgen at linuxmail.org)
// 
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

// $Id$

#include "Icon.hh"

#include "Font.hh"
#include "FbWindow.hh"
#include "App.hh"
#include "Color.hh"

#include "Image.hh"
#include "Surface.hh"

#include "default.xpm"

#include <X11/xpm.h>
#include <X11/Xatom.h>
#include <assert.h>

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

#ifdef HAVE_SHAPE
#include <X11/extensions/shape.h>
#endif // HAVE_SHAPE

#include <iostream>
using namespace std;

namespace FbDesk {

using namespace FbTk;

const FbTk::Font *Icon::s_font = 0;
GC Icon::s_gc = 0;
Icon::CacheList Icon::m_cachelist;
Atom Icon::s_xdnd_atom = 0;
Icon::TextPlacement Icon::s_textplacement = Icon::TEXTPLACE_BOTTOM;

Icon::Icon(const std::string &imagefilename, const std::string &command, const std::string &label):
    m_command(command),
    m_image_filename(imagefilename),
    m_label(label),
    m_width(32), m_height(32), // default size
    m_x(0), m_y(0),            // default pos
    m_icon_window(0, // screen number
                  0, 0, // pos
                  32, 32, // size
                  // event mask
                  ButtonReleaseMask | ButtonPressMask | PointerMotionMask | ExposureMask,
                  //                 PropertyChangeMask,
                  true), // override redirect
    m_label_window(0, // screen number
                   0, 0, // pos
                   10, 10, // size
                   // event mask
                   ButtonPressMask | ButtonReleaseMask | PointerMotionMask | ExposureMask,
                   true), //override redirect
    m_icon_pixmap(0) {
    assert(s_font); // must set this before using this class

    if (s_gc == 0) { 
        s_gc = DefaultGC(FbTk::App::instance()->display(), 0); // get gc from screen
    }

    setupDragAndDrop();

    loadImage(imagefilename); // load file
    
    update(); // update windows
    move(0, 0); // start position
}

Icon::~Icon() {
    removeFromCache(m_image_filename);
}

void Icon::setTextPlacement(TextPlacement place) { 
    s_textplacement = place;
}

void Icon::setTextColor(const FbTk::Color &color) {
    Display *disp = FbTk::App::instance()->display();
    if (s_gc == 0)
        s_gc = DefaultGC(disp, 0);
    XGCValues gcval;
    gcval.foreground = color.pixel();
    XChangeGC(disp, s_gc, GCForeground, &gcval);
}

void Icon::show() {
    m_icon_window.show();
    m_label_window.show();
}

void Icon::setCommand(const std::string &command) {
    m_command = command;
}

void Icon::setImage(const std::string &imagefilename) {
      
    if (loadImage(imagefilename)) {
        removeFromCache(m_image_filename); // remove from cache and set new filename
        m_image_filename = imagefilename;
    }

}

void Icon::setLabel(const std::string &label) {
    m_label = label;
    updateTextAlignment(); // realign it
    updateLabel();
}

// move this icon to X, Y pos
void Icon::move(int x, int y) {
    m_icon_window.move(x, y);
    m_x = x;
    m_y = y;
    updateTextAlignment();
    updateLabel();
}

void Icon::resize(unsigned int width, unsigned int height) {
    if (width == 0)
        width = 1;
    if (height == 0)
        height = 1;
    m_width = width;
    m_height = height;
    m_icon_window.resize(width, height);
}

void Icon::update() {
    updateLabel();
    updateWindow();
}

void Icon::raise() {
    m_label_window.raise();
    m_icon_window.raise();

}

void Icon::lower() {
    m_label_window.lower();
    m_icon_window.lower();
}

void Icon::redrawLabel() {
    assert(s_font);
    m_label_window.clear();

    if (m_label.size() != 0) {
        // setup start position of text
        int y_start = s_font->ascent();
        int x_start = 0;
        if (s_textplacement == TEXTPLACE_LEFT ||
            s_textplacement == TEXTPLACE_RIGHT) {
            x_start = y_start;
            y_start = s_font->textWidth(m_label.c_str(), m_label.size());
        }

        s_font->drawText(m_label_window.window(), 0, s_gc,
                         m_label.c_str(), m_label.size(),
                         x_start, y_start);
    }
}

void Icon::updateLabel() {

    // get root pixmap for transparency
    Pixmap rootpixmap = 0;
    Display *disp = FbTk::App::instance()->display();
    Atom real_type;
    int real_format;
    unsigned long items_read, items_left;
    unsigned int *data;
    if (XGetWindowProperty(disp, RootWindow(disp, m_label_window.screenNumber()), 
                           XInternAtom(disp, "_XROOTPMAP_ID", false),
                           0L, 1L, 
                           false, XA_PIXMAP, &real_type,
                           &real_format, &items_read, &items_left, 
                           (unsigned char **) &data) == Success && 
        items_read) { 
        rootpixmap = (Pixmap) (*data);
        XFree(data);
    }
    if (rootpixmap != 0) {
        XCopyArea(disp,
                  rootpixmap, m_label_window.window(),
                  DefaultGC(disp, m_label_window.screenNumber()),
                  m_label_window.x(), m_label_window.y(),
                  m_label_window.width(), m_label_window.height(),
                  0, 0);
    }
    if (s_textplacement == TEXTPLACE_LEFT ||
        s_textplacement == TEXTPLACE_RIGHT) {
        m_label_window.resize(s_font->height(), 
                              s_font->textWidth(m_label.c_str(), m_label.size()));
    } else {
        m_label_window.resize(s_font->textWidth(m_label.c_str(), 
                                                m_label.size()), s_font->height()); 
    }
    redrawLabel();
}

void Icon::updateWindow() {
    m_icon_window.resize(m_width, m_height);
    if (m_icon_pixmap != 0) {
        m_icon_window.setBackgroundPixmap(m_icon_pixmap);
    }
    m_icon_window.clear();
}

bool Icon::loadImage(const std::string &image_filename) {

    Display *disp = FbTk::App::instance()->display();

    Cache *cache_item = findCache(image_filename);
    if (cache_item != 0) {
        cache_item->m_count++;
        m_icon_pixmap = cache_item->m_pixmap;
#ifdef HAVE_SHAPE
        XShapeCombineMask(disp, m_icon_window.window(),
                          ShapeBounding, 0, 0, cache_item->m_mask, ShapeSet);
#endif // HAVE_SHAPE
        resize(cache_item->m_width, cache_item->m_height);
        return true;
    }

    Pixmap pixmap, mask;
    unsigned int pm_width = 1, pm_height = 1;
    
    Window rootwindow = m_icon_window.window();

    // try loading filename first
    std::auto_ptr<FbTk::Surface> img(FbTk::Image::load(image_filename.c_str()));
    if (img.get() == 0) {
        XpmAttributes xpm_attr;
        xpm_attr.valuemask = 0;
        // lets try xpm loading
        int retvalue = XpmReadFileToPixmap(disp,
                                           rootwindow,
                                           const_cast<char *>(image_filename.c_str()),
                                           &pixmap,
                                           &mask, &xpm_attr);
        if (retvalue != 0) {

            cerr<<"Load pixmap failed! ("<<XpmGetErrorString(retvalue)<<")"<<endl;
            cerr<<"Using default pixmap"<<endl;

            retvalue = XpmCreatePixmapFromData(disp,
                                               rootwindow,
                                               default_xpm, &pixmap, &mask, &xpm_attr);
            if (pixmap == None || retvalue != XpmSuccess) // fatal error
                throw string("FbDesk::Icon: Can't load default pixmap!");
        }  
        pm_width = xpm_attr.width;
        pm_height = xpm_attr.height;
    } else {
        // ok image loading successfull
        pm_width = img->width();
        pm_height = img->height();
        // create a converter surface
        FbTk::Surface newsurf(img->width(), img->height(), m_icon_window.bpp());
        if (img->hasColorKey())
            newsurf.setColorKey(img->getColorKey(), true);
        img->blit(newsurf, 0, 0, 0, 0, img->width(), img->height());
        newsurf.createPixmap(rootwindow, pixmap, mask);
    }

    // create new cache item and add it to cachelist
    Cache *new_cache_item = new Cache;
    new_cache_item->m_pixmap = pixmap;
    new_cache_item->m_mask = mask;
    new_cache_item->m_filename = image_filename;
    new_cache_item->m_count = 1;
    new_cache_item->m_width = pm_width;
    new_cache_item->m_height = pm_height;

    m_cachelist.push_back(new_cache_item);

    m_icon_pixmap = pixmap;

    resize(pm_width, pm_height);
#ifdef HAVE_SHAPE
    XShapeCombineMask(disp, m_icon_window.window(),
                      ShapeBounding, 0, 0, mask, ShapeSet);
#endif // HAVE_SHAPE
    return true;
}

Icon::Cache * const Icon::findCache(const std::string &filename) {
    CacheList::iterator cache_item = m_cachelist.begin();
    CacheList::iterator cache_item_end = m_cachelist.end();
    for (; cache_item != cache_item_end; ++cache_item) {
        if ((*cache_item)->m_filename == filename)
            return *cache_item;
    }
      
    return 0; // we didn't find filename in cache
}

void Icon::removeFromCache(const std::string &filename) {
    Cache *item = findCache(filename);
    if (item) {
        item->m_count--;
        if (item->m_count == 0) { // if nobody else is using this one remove it from the list
            m_cachelist.remove(item);
            XFreePixmap(App::instance()->display(), item->m_pixmap);
            XFreePixmap(App::instance()->display(), item->m_mask);
            delete item;
            item = 0;
        }
    }
}

void Icon::setupDragAndDrop() {
    static int xnd_version = 3;
    Display *disp = FbTk::App::instance()->display();

    if (s_xdnd_atom == 0)
        s_xdnd_atom = XInternAtom(disp, "XdndAware", false);

    XChangeProperty(disp,
                    m_icon_window.window(), 
                    s_xdnd_atom, XA_ATOM,
                    32, PropModeReplace,
                    (unsigned char *)(&xnd_version), 1);
                    
}

void Icon::updateTextAlignment() {
    if (s_font == 0)
        return;

    int textwidth = s_font->textWidth(m_label.c_str(), m_label.size());
    if (m_label.size() == 0 || textwidth == 0)
        return;

    switch (s_textplacement) {
    case TEXTPLACE_BOTTOM:
        // center text under icon   
        m_label_window.move(m_x + m_icon_window.width()/2 - textwidth/2, m_y + height());
        break;
    case TEXTPLACE_TOP:
        // center text above icon
        m_label_window.move(m_x + m_icon_window.width()/2 - textwidth/2, m_y - s_font->height());
        break;
    case TEXTPLACE_LEFT:
        m_label_window.move(m_x - s_font->height(), m_y + m_icon_window.height()/2 - textwidth/2);
        break;
    case TEXTPLACE_RIGHT:
        m_label_window.move(m_x + width(), m_y + m_icon_window.height()/2 - textwidth/2);
        break;
    }

}

}; // end namespace FbDesk

Generated by  Doxygen 1.6.0   Back to index