/*
* icon.cc
*
* Copyright (C) 1995, 1996, 1997, 1997, 1998, 1999, 2000, 2001, 2002 Kenichi Kourai
* Copyright (C) 1999, 2000, 2001, 2002 Luiz Blanes
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with blwm; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#ifdef __EMX__
#include <process.h>
#endif
#include <X11/Xlib.h>
#include <X11/Xutil.h>
#include <X11/Xresource.h>
#include <X11/xpm.h>
#include <X11/extensions/shape.h>
#include "main.h"
#include "misc.h"
#include "blwm.h"
#include "icon.h"
#include "util.h"
#include "event.h"
#include "startmenu.h"
#include "blwmrc.h"
#include "desktop.h"
#include "pixmap_image.h"
#include "callback.h"
#include "tooltip.h"
#include "bitmaps/icon32.xpm"
Icon* Icon::focusIcon = NULL;
XContext Icon::context;
BlImage* Icon::imgIcon;
IconMenu* Icon::ctrlMenu = NULL;
// image must be passed as a duplicated object
Icon::Icon(BlImage* image, char* iconname, char* execname, int x, int y)
: img(image), exec(execname)
{
isBuiltin = False;
init(iconname, x, y);
}
// image must be passed as a duplicated object
Icon::Icon(BlImage* image, char* iconname, FuncNumber func, int x, int y)
: img(image), fn(func)
{
isBuiltin = True;
init(iconname, x, y);
}
void Icon::init(char* iconname, int x, int y)
{
XSetWindowAttributes attributes;
unsigned long valueMask;
rcVirt.x = x;
rcVirt.y = y;
dragging = False;
enableDrop = True;
/*
* Compute icon text format and icon text size/position
*/
AlterIconName(iconname);
rc.width = IconSize + IconHorizontalSpacing;
rc.height = IconSize + IconVerticalSpacing;
if (rc.height < 2 + IconSize + 4 + rcText.height)
rc.height = 2 + IconSize + 4 + rcText.height;
/*
* Create an icon image window.
*/
attributes.background_pixel = DesktopColor.pixel;
attributes.override_redirect = True;
valueMask = CWBackPixel | CWOverrideRedirect;
frame = XCreateWindow(display, root,
0, 0, IconSize, IconSize,
0, CopyFromParent, InputOutput, CopyFromParent,
valueMask, &attributes);
/*
* Create an icon text window.
*/
attributes.event_mask = ExposureMask;
valueMask = CWBackPixel | CWOverrideRedirect | CWEventMask;
text = XCreateWindow(display, root,
0, 0, rcText.width, rcText.height,
0, CopyFromParent, InputOutput, CopyFromParent,
valueMask, &attributes);
/*
* create a shadow window (for dragging)
*/
shadow = XCreateWindow(display, root,
0, 0, rc.width, rc.height,
0, CopyFromParent, InputOutput, CopyFromParent,
valueMask, &attributes);
/*
* Create an icon wapper window.
*/
attributes.event_mask = ButtonPressMask | ButtonReleaseMask |
Button1MotionMask | PointerMotionMask |
EnterWindowMask | LeaveWindowMask;
valueMask = CWOverrideRedirect | CWEventMask;
wrap = XCreateWindow(display, root,
0, 0, rc.width, rc.height,
0, CopyFromParent, InputOnly, CopyFromParent,
valueMask, &attributes);
XSaveContext(display, text, context, (caddr_t)this);
XSaveContext(display, wrap, context, (caddr_t)this);
XSaveContext(display, shadow, context, (caddr_t)this);
shapeMask = XCreatePixmap(display, frame, IconSize, IconSize, 1);
gcShape = XCreateGC(display, shapeMask, 0, 0);
SetIconImage();
img->SetDisplayCallback(new Callback<Icon>(this, &Icon::SetIconImage));
toolTip = new Tooltip(iconname, &fsIcon);
}
Icon::~Icon()
{
delete [] name;
XDeleteContext(display, text, context);
XDeleteContext(display, wrap, context);
XDestroyWindow(display, frame);
XDestroyWindow(display, text);
XDestroyWindow(display, wrap);
XFreePixmap(display, shapeMask);
XFreeGC(display, gcShape);
if (this == focusIcon)
focusIcon = NULL;
delete toolTip;
BlImage::Destroy(img);
}
void Icon::SetIconImage()
{
img->SetBackground(frame);
CreateShapedWindow();
XClearWindow(display, frame);
}
/*
* Create an window shaped by icon image.
*/
void Icon::CreateShapedWindow()
{
// First, make the whole transparent (pixel value 0 is transparent).
XSetForeground(display, gcShape, 0);
XFillRectangle(display, shapeMask, gcShape, 0, 0, IconSize, IconSize);
XSetForeground(display, gcShape, 1);
XSetBackground(display, gcShape, 0);
// Opaque icon pixmap.
if (img->GetMask()) {
Dim size = img->GetSize();
XCopyPlane(display, img->GetMask(), shapeMask, gcShape,
0, 0, size.width, size.height, 0, 0, 1);
}
else
XFillRectangle(display, shapeMask, gcShape, 0, 0, IconSize, IconSize);
if (this == focusIcon) {
// create semi-transparent mask for icon image
GC gcTile = CreateTileGC(shapeMask);
XFillRectangle(display, shapeMask, gcTile, 0, 0, IconSize, IconSize);
XFreeGC(display, gcTile);
}
XShapeCombineMask(display, frame, ShapeBounding, 0, 0, shapeMask, ShapeSet);
}
void Icon::CreateShapedShadowWindow()
{
Pixmap shadowMask;
GC gcShadow;
shadowMask = XCreatePixmap(display, shadow, rc.width, rc.height, 1);
gcShadow = XCreateGC(display, shadowMask, 0, 0);
XSetForeground(display, gcShadow, 0);
XFillRectangle(display, shadowMask, gcShadow, 0, 0, rc.width, rc.height);
XSetForeground(display, gcShadow, 1);
XSetBackground(display, gcShadow, 0);
if (img->GetMask())
XCopyPlane(display, img->GetMask(), shadowMask, gcShadow,
0, 0, IconSize, IconSize, IconHorizontalSpacing / 2, 2, 1);
else
XFillRectangle(display, shadowMask, gcShadow,
IconHorizontalSpacing / 2, 2, IconSize, IconSize);
// create semi-transparent mask for icon image
GC gcTile = CreateTileGC(shadowMask);
XFillRectangle(display, shadowMask, gcTile,
IconHorizontalSpacing / 2, 2, IconSize, IconSize);
XFreeGC(display, gcTile);
// create shaped icon name
XSetForeground(display, gcShadow, 0); // transparent
XFillRectangle(display, shadowMask, gcShadow,
rcText.x, rcText.y, rcText.width, rcText.height);
XSetForeground(display, gcShadow, 1);
DrawText(shadowMask, gcShadow, rcText);
XShapeCombineMask(display, shadow, ShapeBounding, 0, 0, shadowMask,
ShapeSet);
}
/*
* MapIcon --
* Map icon itself and the wrapper.
*/
void Icon::MapIcon()
{
XLowerWindow(display, wrap);
XMapWindow(display, wrap);
XLowerWindow(display, frame);
XMapWindow(display, frame);
XLowerWindow(display, text);
XMapWindow(display, text);
}
/*
* unmapIcon --
* Unmap icon itself and the wrapper.
*/
void Icon::UnmapIcon()
{
XUnmapWindow(display, frame);
XUnmapWindow(display, text);
XUnmapWindow(display, wrap);
}
void Icon::SetFocus()
{
focusIcon = this;
CreateShapedWindow();
DrawIcon(True);
}
void Icon::ResetFocus()
{
focusIcon = NULL;
CreateShapedWindow();
DrawIcon(False);
}
/*
* DrawIcon --
* Draw icon name.
*/
void Icon::DrawIcon(Bool focus)
{
if (dragging && img)
img->Display(shadow, Point(IconHorizontalSpacing / 2, 2));
if (focus)
XSetForeground(display, gc, DesktopActiveColor.pixel);
else
XSetForeground(display, gc, DesktopColor.pixel);
XFillRectangle(display, text, gc, 0, 0, rcText.width, rcText.height);
if (focus)
XSetForeground(display, gc, IconStringActiveColor.pixel);
else
XSetForeground(display, gc, IconStringColor.pixel);
Rect range(0, 0, rcText.width, rcText.height);
DrawText(text, gc, range);
if (dragging) {
XSetForeground(display, gc, black.pixel);
range = Rect(rcText.x, rcText.y, rcText.width, rcText.height);
DrawText(shadow, gc, range);
}
if (focus) {
XSetForeground(display, gcDash, yellow.pixel);
XDrawRectangle(display, text, gcDash,
0, 0, rcText.width - 1, rcText.height - 1);
}
}
/*
* Draw each line of the text.
*/
void Icon::DrawText(Drawable d, GC dGc, Rect rect)
{
char *str, *start, *end;
XRectangle ink, log;
start = end = name;
while (*end) {
// Find delimiter, and save it
while (*end && *end != '\n')
end++;
char prev = *end;
if (prev)
*end = '\0';
// Center this line of text
str = GetFixName(fsIcon, start, IconSize + IconHorizontalSpacing);
XmbTextExtents(fsIcon, str, strlen(str), &ink, &log);
// Draw it
XmbDrawString(display, d, fsIcon, dGc,
rect.x + (rect.width - log.width) / 2,
rect.y - log.y,
str, strlen(str));
if (strlen(start) > strlen(str)) {
XRectangle xr[3] = {
{rect.x + log.width + 2, rect.y - log.y - 2, 1, 2},
{rect.x + log.width + 5, rect.y - log.y - 2, 1, 2},
{rect.x + log.width + 8, rect.y - log.y - 2, 1, 2}
};
XFillRectangles(display, d, dGc, xr, 3);
}
delete [] str;
// Onto the next
rect.y += rcText.height / textLines;
if (prev)
*end++ = prev;
start = end;
}
}
void Icon::MoveIcon(const Point& pt)
{
XMoveWindow(display, frame, pt.x + IconHorizontalSpacing / 2, pt.y + 2);
XMoveWindow(display, text, pt.x + rcText.x, pt.y + rcText.y);
XMoveWindow(display, wrap, pt.x, pt.y);
}
/*
* Button1Press --
* Process press of button1 (mouse left button)
*/
void Icon::Button1Press(Time clickTime, const Point& ptRoot)
{
/*
* Take off the focus of short cut icon, if any.
*/
if (Icon::focusIcon != NULL)
Icon::focusIcon->ResetFocus();
toolTip->Disable();
/*
* Unmap all menus and move focus to root window.
*/
Menu::UnmapAllMenus();
rootBlwm->SetFocus();
/*
* If double click, execute the function.
*/
if (IsDoubleClick(iconClickTime, clickTime, event.ptPrevRoot, ptRoot)) {
if (isBuiltin)
BlFunction::execFunction(fn);
else
ExecCommand(exec);
return;
}
else
SetFocus();
iconClickTime = clickTime;
}
void Icon::Button1Motion(const Point& ptRoot)
{
XEvent ev;
Point ptOld, ptNew, ptCur;
Bool pointer = False;
if (DisableDesktopChange)
return;
ptCur = Point(rc.x, rc.y);
ptOld = ptRoot;
/*
* Process icon movement.
*/
while (1) {
XMaskEvent(display,
ExposureMask | Button1MotionMask | ButtonReleaseMask |
ButtonPressMask | PointerMotionMask,
&ev);
switch (ev.type) {
case Expose:
event.DispatchEvent(ev);
break;
case MotionNotify:
if (!dragging) {
dragging = True;
CreateShapedShadowWindow();
XMoveWindow(display, shadow, ptCur.x, ptCur.y);
XMapRaised(display, shadow);
}
ptNew = Point(ev.xbutton.x_root, ev.xbutton.y_root);
ptCur.x += ptNew.x - ptOld.x;
rcVirt.x += ptNew.x - ptOld.x;
ptCur.y += ptNew.y - ptOld.y;
rcVirt.y += ptNew.y - ptOld.y;
ptOld = ptNew;
XMoveWindow(display, shadow, ptCur.x, ptCur.y);
if (IsPointerInWindow(ptNew)) {
if (enableDrop) {
XDefineCursor(display, root, cursor[DISALLOW]);
enableDrop = False;
}
}
else {
if (!enableDrop) {
XDefineCursor(display, root, cursor[SYS]);
enableDrop = True;
}
}
break;
case ButtonRelease:
goto decide;
case ButtonPress:
if (pointer) {
XUngrabPointer(display, CurrentTime);
goto decide;
}
return;
}
}
decide:
if (dragging) {
XUnmapWindow(display, shadow);
if (enableDrop) {
MoveIcon(Point(ptCur.x, ptCur.y));
rc.x = ptCur.x;
rc.y = ptCur.y;
}
else {
XDefineCursor(display, root, cursor[SYS]);
enableDrop = True;
}
dragging = False;
DrawIcon(True);
}
}
/*
* Button3Press --
* Process press of button3.
*/
void Icon::Button3Release(const Point& ptRoot)
{
Point pt;
int dir;
/*
* Take off the focus of short cut icon, if any.
*/
if (Icon::focusIcon != NULL)
Icon::focusIcon->ResetFocus();
Menu::UnmapAllMenus();
rootBlwm->SetFocus();
SetFocus();
toolTip->Disable();
pt = ctrlMenu->GetFixedMenuPos(ptRoot, dir);
ctrlMenu->SetIcon(this);
ctrlMenu->MapMenu(pt.x, pt.y, dir);
}
void Icon::Enter()
{
toolTip->SetTimer();
}
void Icon::Leave()
{
toolTip->Disable();
}
void Icon::PointerMotion()
{
if (!toolTip->IsMapped())
toolTip->ResetTimer();
}
void Icon::Initialize()
{
Icon::context = XUniqueContext();
if (DefaultShortcutIcon == NULL)
imgIcon = new PixmapImage(icon32);
else {
imgIcon = CreateImageFromFile(DefaultShortcutIcon, timer);
if (imgIcon == NULL)
imgIcon = new PixmapImage(icon32);
}
}
/*
* AlterIconName --
* checks the icon name's size and splits it on several lines if necessary.
*/
void Icon::AlterIconName(char* iconname)
{
XRectangle ink, log;
char *start, *end;
int nblanks, len, i;
int* sizes;
char space;
int spaceSize;
int lineHeight;
// Make a copy of the string
name = new char[strlen(iconname) + 1];
strcpy(name, iconname);
// Compute the width of a space, and the height of a line
space = ' ';
XmbTextExtents(fsIcon, &space, 1, &ink, &log);
spaceSize = log.width;
lineHeight = log.height;
// Compute the width of the name
XmbTextExtents(fsIcon, name, strlen(name), &ink, &log);
// If it fits on a sigle line, no problem.
if (log.width <= IconSize + IconHorizontalSpacing) {
rcText.width = log.width;
rcText.height = log.height;
textLines = 1;
}
// Else try to split the name into several lines
else {
rcText.width = 0;
rcText.height = lineHeight;
textLines = 1;
// First check for blanks.
end = name;
nblanks = 0;
while (*end++)
if (*end == ' ')
nblanks++;
// If the text has blanks, we can break the text
if (nblanks) {
// Compute every word's size
sizes = new int[nblanks + 1];
start = end = name;
for (i = 0; i < nblanks; i++) {
while (*end++ != ' ')
;
*(--end) = 0;
XmbTextExtents(fsIcon, start, (int)(end - start), &ink, &log);
sizes[i] = log.width;
*end = ' ';
start = ++end;
}
XmbTextExtents(fsIcon, end, strlen(end), &ink, &log); // last word
sizes[nblanks] = log.width;
// Insert line breaks in the text.
len = i = 0;
start = end = name;
while (i < nblanks + 1) {
len += sizes[i];
while (*end && *end != ' ')
end++;
// If too large, insert a break before the word
if (len > IconSize + IconHorizontalSpacing) {
if (start != name) { // can't break before first word !
len -= sizes[i];
if (len > rcText.width)
rcText.width = len;
*start = '\n';
len = sizes[i] + spaceSize;
rcText.height += lineHeight;
textLines++;
}
}
else
len += spaceSize;
start = end++;
i++;
}
len -= spaceSize; // no extra space at the end
if (len > rcText.width)
rcText.width = len;
}
else {
rcText.width = IconSize + IconHorizontalSpacing;
rcText.height = log.height;
}
}
rcText.x = (IconSize + IconHorizontalSpacing - rcText.width) / 2;
rcText.y = 2 + IconSize + 4;
if (rcText.width == 0)
rcText.width = 1;
if (rcText.height == 0)
rcText.height = 1;
}
void IconMenu::ExecFunction(FuncNumber fn, int i)
{
switch (fn) {
case Q_EXEC_ICON:
if (icon) {
if (icon->IsBuiltin())
BlFunction::execFunction(icon->GetFunc());
else
ExecCommand(icon->GetExec());
}
break;
case Q_DELETE_ICON:
desktop.GetIconList().Remove(icon);
delete icon;
icon = NULL;
break;
default:
BlwmError("cannot execute a specified function");
break;
}
}
syntax highlighted by Code2HTML, v. 0.9.1