/** Draw a specific tray. */ void DrawSpecificTray(const TrayType *tp) { TrayComponentType *cp; for(cp = tp->components; cp; cp = cp->next) { UpdateSpecificTray(tp, cp); } if(settings.trayDecorations == DECO_MOTIF) { JXSetForeground(display, rootGC, colors[COLOR_TRAY_UP]); JXDrawLine(display, tp->window, rootGC, 0, 0, tp->width - 1, 0); JXDrawLine(display, tp->window, rootGC, 0, tp->height - 1, 0, 0); JXSetForeground(display, rootGC, colors[COLOR_TRAY_DOWN]); JXDrawLine(display, tp->window, rootGC, 0, tp->height - 1, tp->width - 1, tp->height - 1); JXDrawLine(display, tp->window, rootGC, tp->width - 1, 0, tp->width - 1, tp->height - 1); } else { JXSetForeground(display, rootGC, colors[COLOR_TRAY_DOWN]); JXDrawRectangle(display, tp->window, rootGC, 0, 0, tp->width - 1, tp->height - 1); } }
/** Render the dialog to the pixmap. */ void DrawDialog(void) { int yoffset; int x; Assert(dialog); /* Clear the dialog. */ JXSetForeground(display, rootGC, colors[COLOR_MENU_BG]); JXFillRectangle(display, dialog->pmap, rootGC, 0, 0, dialog->width, dialog->height); /* Draw the message. */ yoffset = 4; for(x = 0; x < dialog->lineCount; x++) { RenderString(dialog->pmap, FONT_MENU, COLOR_MENU_FG, 4, yoffset, dialog->width, dialog->message[x]); yoffset += dialog->lineHeight; } /* Draw the buttons. */ DrawButtons(); }
/** Draw a specific tray. */ void DrawSpecificTray(const TrayType *tp) { TrayComponentType *cp; int x; Assert(tp); /* Draw components. */ for(cp = tp->components; cp; cp = cp->next) { UpdateSpecificTray(tp, cp); } /* Draw the border. */ for(x = 0; x < tp->border; x++) { /* Top */ JXSetForeground(display, rootGC, colors[COLOR_TRAY_UP]); JXDrawLine(display, tp->window, rootGC, 0, x, tp->width - x - 1, x); /* Bottom */ JXSetForeground(display, rootGC, colors[COLOR_TRAY_DOWN]); JXDrawLine(display, tp->window, rootGC, x + 1, tp->height - x - 1, tp->width - x - 2, tp->height - x - 1); /* Left */ JXSetForeground(display, rootGC, colors[COLOR_TRAY_UP]); JXDrawLine(display, tp->window, rootGC, x, x, x, tp->height - x - 1); /* Right */ JXSetForeground(display, rootGC, colors[COLOR_TRAY_DOWN]); JXDrawLine(display, tp->window, rootGC, tp->width - x - 1, x + 1, tp->width - x - 1, tp->height - x - 1); } }
/** Draw the tray background on a drawable. */ void ClearTrayDrawable(const TrayComponentType *cp) { const Drawable d = cp->pixmap != None ? cp->pixmap : cp->window; if(colors[COLOR_TRAY_BG1] == colors[COLOR_TRAY_BG2]) { JXSetForeground(display, rootGC, colors[COLOR_TRAY_BG1]); JXFillRectangle(display, d, rootGC, 0, 0, cp->width, cp->height); } else { DrawHorizontalGradient(d, rootGC, colors[COLOR_TRAY_BG1], colors[COLOR_TRAY_BG2], 0, 0, cp->width, cp->height); } }
/** Draw a clock tray component. */ void DrawClock(ClockType *clk, const TimeType *now) { TrayComponentType *cp; const char *timeString; int width; int rwidth; /* Only draw if the time changed. */ if(now->seconds == clk->lastTime.seconds) { return; } /* Clear the area. */ cp = clk->cp; if(colors[COLOR_CLOCK_BG1] == colors[COLOR_CLOCK_BG2]) { JXSetForeground(display, rootGC, colors[COLOR_CLOCK_BG1]); JXFillRectangle(display, cp->pixmap, rootGC, 0, 0, cp->width, cp->height); } else { DrawHorizontalGradient(cp->pixmap, rootGC, colors[COLOR_CLOCK_BG1], colors[COLOR_CLOCK_BG2], 0, 0, cp->width, cp->height); } /* Determine if the clock is the right size. */ timeString = GetTimeString(clk->format, clk->zone); width = GetStringWidth(FONT_CLOCK, timeString); rwidth = width + 4; if(rwidth == clk->cp->requestedWidth || clk->userWidth) { /* Draw the clock. */ RenderString(cp->pixmap, FONT_CLOCK, COLOR_CLOCK_FG, (cp->width - width) / 2, (cp->height - GetStringHeight(FONT_CLOCK)) / 2, cp->width, timeString); UpdateSpecificTray(clk->cp->tray, clk->cp); } else { /* Wrong size. Resize. */ clk->cp->requestedWidth = rwidth; ResizeTray(clk->cp->tray); } }
/** Load a gradient background. */ void LoadGradientBackground(BackgroundNode *bp) { XColor color1; XColor color2; char *temp; char *sep; int len; sep = strchr(bp->value, ':'); if(!sep) { bp->pixmap = None; bp->window = None; return; } /* Get the first color. */ len = (int)(sep - bp->value); temp = AllocateStack(len + 1); memcpy(temp, bp->value, len); temp[len] = 0; ParseColor(temp, &color1); ReleaseStack(temp); /* Get the second color. */ len = strlen(sep + 1); temp = AllocateStack(len + 1); memcpy(temp, sep + 1, len); temp[len] = 0; ParseColor(temp, &color2); ReleaseStack(temp); /* Create the window. */ bp->window = JXCreateSimpleWindow(display, rootWindow, 0, 0, rootWidth, rootHeight, 0, 0, 0); bp->pixmap = JXCreatePixmap(display, bp->window, rootWidth, rootHeight, rootDepth); if(color1.pixel == color2.pixel) { JXSetForeground(display, rootGC, color1.pixel); JXFillRectangle(display, bp->pixmap, rootGC, 0, 0, rootWidth, rootHeight); } else { DrawHorizontalGradient(bp->pixmap, rootGC, color1.pixel, color2.pixel, 0, 0, rootWidth, rootHeight); } }
/** Load an image background. */ void LoadImageBackground(BackgroundNode *bp) { IconNode *ip; int width, height; /* Load the icon. */ ExpandPath(&bp->value); ip = LoadNamedIcon(bp->value); if(JUNLIKELY(!ip)) { bp->pixmap = None; bp->window = None; Warning(_("background image not found: \"%s\""), bp->value); return; } /* We can't use render on these. */ ip->useRender = 0; /* Determine the size of the background pixmap. */ if(bp->type == BACKGROUND_TILE) { width = ip->image->width; height = ip->image->height; } else { width = rootWidth; height = rootHeight; } /* Create the window. */ bp->window = JXCreateSimpleWindow(display, rootWindow, 0, 0, rootWidth, rootHeight, 0, 0, 0); /* Create the pixmap. */ bp->pixmap = JXCreatePixmap(display, bp->window, width, height, rootDepth); /* Clear the pixmap in case it is too small. */ JXSetForeground(display, rootGC, 0); JXFillRectangle(display, bp->pixmap, rootGC, 0, 0, width, height); /* Draw the icon on the background pixmap. */ PutIcon(ip, bp->pixmap, 0, 0, width, height); /* We don't need the icon anymore. */ DestroyIcon(ip); }
/** Load a solid background. */ void LoadSolidBackground(BackgroundNode *bp) { XColor c; ParseColor(bp->value, &c); /* Create the window. */ bp->window = JXCreateSimpleWindow(display, rootWindow, 0, 0, rootWidth, rootHeight, 0, 0, 0); /* Create the pixmap. */ bp->pixmap = JXCreatePixmap(display, bp->window, 1, 1, rootDepth); JXSetForeground(display, rootGC, c.pixel); JXDrawPoint(display, bp->pixmap, rootGC, 0, 0); }
/** Draw a horizontal gradient. */ void DrawHorizontalGradient(Drawable d, GC g, long fromColor, long toColor, int x, int y, unsigned int width, unsigned int height) { const int shift = 15; unsigned int line; XColor colors[2]; int red, green, blue; int ared, agreen, ablue; int bred, bgreen, bblue; int redStep, greenStep, blueStep; /* Return if there's nothing to do. */ if(width == 0 || height == 0) { return; } /* Here we assume that the background was filled elsewhere. */ if(fromColor == toColor) { return; } /* Query the from/to colors. */ colors[0].pixel = fromColor; colors[1].pixel = toColor; JXQueryColors(display, rootColormap, colors, 2); /* Set the "from" color. */ ared = (unsigned int)colors[0].red << shift; agreen = (unsigned int)colors[0].green << shift; ablue = (unsigned int)colors[0].blue << shift; /* Set the "to" color. */ bred = (unsigned int)colors[1].red << shift; bgreen = (unsigned int)colors[1].green << shift; bblue = (unsigned int)colors[1].blue << shift; /* Determine the step. */ redStep = (bred - ared) / (int)height; greenStep = (bgreen - agreen) / (int)height; blueStep = (bblue - ablue) / (int)height; /* Loop over each line. */ red = ared; blue = ablue; green = agreen; for(line = 0; line < height; line++) { /* Determine the color for this line. */ colors[0].red = (unsigned short)(red >> shift); colors[0].green = (unsigned short)(green >> shift); colors[0].blue = (unsigned short)(blue >> shift); GetColor(&colors[0]); /* Draw the line. */ JXSetForeground(display, g, colors[0].pixel); JXDrawLine(display, d, g, x, y + line, x + width - 1, y + line); red += redStep; green += greenStep; blue += blueStep; } }
/** Draw a button. */ void DrawButton(ButtonNode *bp) { ColorType fg; long bg1, bg2; long up, down; DecorationsType decorations; Drawable drawable; GC gc; int x, y; int width, height; int xoffset, yoffset; int iconWidth, iconHeight; int textWidth, textHeight; Assert(bp); drawable = bp->drawable; x = bp->x; y = bp->y; width = bp->width; height = bp->height; gc = JXCreateGC(display, drawable, 0, NULL); /* Determine the colors to use. */ switch(bp->type) { case BUTTON_LABEL: fg = COLOR_MENU_FG; bg1 = colors[COLOR_MENU_BG]; bg2 = colors[COLOR_MENU_BG]; up = colors[COLOR_MENU_UP]; down = colors[COLOR_MENU_DOWN]; decorations = settings.menuDecorations; break; case BUTTON_MENU_ACTIVE: fg = COLOR_MENU_ACTIVE_FG; bg1 = colors[COLOR_MENU_ACTIVE_BG1]; bg2 = colors[COLOR_MENU_ACTIVE_BG2]; down = colors[COLOR_MENU_ACTIVE_UP]; up = colors[COLOR_MENU_ACTIVE_DOWN]; decorations = settings.menuDecorations; break; case BUTTON_TRAY: fg = COLOR_TRAYBUTTON_FG; bg1 = colors[COLOR_TRAYBUTTON_BG1]; bg2 = colors[COLOR_TRAYBUTTON_BG2]; up = colors[COLOR_TRAYBUTTON_UP]; down = colors[COLOR_TRAYBUTTON_DOWN]; decorations = settings.trayDecorations; break; case BUTTON_TRAY_ACTIVE: fg = COLOR_TRAYBUTTON_ACTIVE_FG; bg1 = colors[COLOR_TRAYBUTTON_ACTIVE_BG1]; bg2 = colors[COLOR_TRAYBUTTON_ACTIVE_BG2]; down = colors[COLOR_TRAYBUTTON_ACTIVE_UP]; up = colors[COLOR_TRAYBUTTON_ACTIVE_DOWN]; decorations = settings.trayDecorations; break; case BUTTON_TASK: fg = COLOR_TASKLIST_FG; bg1 = colors[COLOR_TASKLIST_BG1]; bg2 = colors[COLOR_TASKLIST_BG2]; up = colors[COLOR_TASKLIST_UP]; down = colors[COLOR_TASKLIST_DOWN]; decorations = settings.trayDecorations; break; case BUTTON_TASK_ACTIVE: fg = COLOR_TASKLIST_ACTIVE_FG; bg1 = colors[COLOR_TASKLIST_ACTIVE_BG1]; bg2 = colors[COLOR_TASKLIST_ACTIVE_BG2]; down = colors[COLOR_TASKLIST_ACTIVE_UP]; up = colors[COLOR_TASKLIST_ACTIVE_DOWN]; decorations = settings.trayDecorations; break; case BUTTON_MENU: default: fg = COLOR_MENU_FG; bg1 = colors[COLOR_MENU_BG]; bg2 = colors[COLOR_MENU_BG]; up = colors[COLOR_MENU_UP]; down = colors[COLOR_MENU_DOWN]; decorations = settings.menuDecorations; break; } /* Draw the background. */ if(bp->fill) { /* Draw the button background. */ JXSetForeground(display, gc, bg1); if(bg1 == bg2) { /* single color */ JXFillRectangle(display, drawable, gc, x, y, width, height); } else { /* gradient */ DrawHorizontalGradient(drawable, gc, bg1, bg2, x, y, width, height); } } /* Draw the border. */ if(bp->border) { if(decorations == DECO_MOTIF) { JXSetForeground(display, gc, up); JXDrawLine(display, drawable, gc, x, y, x + width - 1, y); JXDrawLine(display, drawable, gc, x, y, x, y + height - 1); JXSetForeground(display, gc, down); JXDrawLine(display, drawable, gc, x, y + height - 1, x + width - 1, y + height - 1); JXDrawLine(display, drawable, gc, x + width - 1, y, x + width - 1, y + height - 1); } else { JXSetForeground(display, gc, down); JXDrawRectangle(display, drawable, gc, x, y, width - 1, height - 1); } } /* Determine the size of the icon (if any) to display. */ iconWidth = 0; iconHeight = 0; if(bp->icon) { if(bp->icon == &emptyIcon) { iconWidth = Min(width - 4, height - 4); iconHeight = iconWidth; } else { const int ratio = (bp->icon->width << 16) / bp->icon->height; iconHeight = height - 4; iconWidth = (iconHeight * ratio) >> 16; if(iconWidth > width - 4) { iconWidth = width - 4; iconHeight = (iconWidth << 16) / ratio; } } } /* Determine how much room is left for text. */ textWidth = 0; textHeight = 0; if(bp->text && (width > height || !bp->icon)) { textWidth = GetStringWidth(bp->font, bp->text); textHeight = GetStringHeight(bp->font); if(iconWidth > 0 && textWidth + iconWidth + 7 > width) { textWidth = width - iconWidth - 7; } else if(iconWidth == 0 && textWidth + 5 > width) { textWidth = width - 5; } textWidth = textWidth < 0 ? 0 : textWidth; } /* Determine the offset of the text in the button. */ if(bp->alignment == ALIGN_CENTER || width <= height) { xoffset = (width - iconWidth - textWidth + 1) / 2; if(xoffset < 0) { xoffset = 0; } } else { xoffset = 2; } /* Display the icon. */ if(bp->icon) { yoffset = (height - iconHeight + 1) / 2; PutIcon(bp->icon, drawable, colors[fg], x + xoffset, y + yoffset, iconWidth, iconHeight); xoffset += iconWidth + 2; } /* Display the label. */ if(textWidth > 0) { yoffset = (height - textHeight + 1) / 2; RenderString(drawable, bp->font, fg, x + xoffset, y + yoffset, textWidth, bp->text); } JXFreeGC(display, gc); }
/** Draw a button. */ void DrawButton(ButtonNode *bp) { long outlinePixel; long topPixel, bottomPixel; ColorType fg; long bg1, bg2; Drawable drawable; GC gc; int x, y; int width, height; int xoffset, yoffset; int iconWidth, iconHeight; int textWidth, textHeight; Assert(bp); drawable = bp->drawable; gc = bp->gc; x = bp->x; y = bp->y; width = bp->width; height = bp->height; /* Determine the colors to use. */ switch(bp->type) { case BUTTON_LABEL: fg = COLOR_MENU_FG; bg1 = colors[COLOR_MENU_BG]; bg2 = colors[COLOR_MENU_BG]; outlinePixel = colors[COLOR_MENU_BG]; topPixel = colors[COLOR_MENU_BG]; bottomPixel = colors[COLOR_MENU_BG]; break; case BUTTON_MENU_ACTIVE: fg = COLOR_MENU_ACTIVE_FG; bg1 = colors[COLOR_MENU_ACTIVE_BG1]; bg2 = colors[COLOR_MENU_ACTIVE_BG2]; if(bg1 == bg2) { outlinePixel = colors[COLOR_MENU_ACTIVE_OL]; } else { outlinePixel = colors[COLOR_MENU_ACTIVE_DOWN]; } topPixel = colors[COLOR_MENU_ACTIVE_UP]; bottomPixel = colors[COLOR_MENU_ACTIVE_DOWN]; break; case BUTTON_TASK: fg = COLOR_TASK_FG; bg1 = colors[COLOR_TASK_BG1]; bg2 = colors[COLOR_TASK_BG2]; topPixel = colors[COLOR_TASK_UP]; bottomPixel = colors[COLOR_TASK_DOWN]; outlinePixel = bottomPixel; break; case BUTTON_TASK_ACTIVE: fg = COLOR_TASK_ACTIVE_FG; bg1 = colors[COLOR_TASK_ACTIVE_BG1]; bg2 = colors[COLOR_TASK_ACTIVE_BG2]; topPixel = colors[COLOR_TASK_ACTIVE_DOWN]; bottomPixel = colors[COLOR_TASK_ACTIVE_UP]; outlinePixel = bottomPixel; break; case BUTTON_MENU: default: fg = COLOR_MENU_FG; bg1 = colors[COLOR_MENU_BG]; bg2 = colors[COLOR_MENU_BG]; outlinePixel = colors[COLOR_MENU_DOWN]; topPixel = colors[COLOR_MENU_UP]; bottomPixel = colors[COLOR_MENU_DOWN]; break; } /* Draw the background. */ switch(bp->type) { case BUTTON_TASK: /* Flat taskbuttons (only icon) for widths < 48 */ if(width < 48) { break; } /* conditional fallthrough is intended */ default: /* Draw the button background. */ JXSetForeground(display, gc, bg1); if(bg1 == bg2) { /* single color */ JXFillRectangle(display, drawable, gc, x + 1, y + 1, width - 1, height - 1); } else { /* gradient */ DrawHorizontalGradient(drawable, gc, bg1, bg2, x + 1, y + 1, width - 2, height - 1); } /* Draw the outline. */ JXSetForeground(display, gc, outlinePixel); #ifdef USE_XMU XmuDrawRoundedRectangle(display, drawable, gc, x, y, width, height, 3, 3); #else JXDrawRectangle(display, drawable, gc, x, y, width, height); #endif break; } /* Determine the size of the icon (if any) to display. */ iconWidth = 0; iconHeight = 0; if (bp->icon) { if(width < height) { GetScaledIconSize(bp->icon, width - 5, &iconWidth, &iconHeight); } else { GetScaledIconSize(bp->icon, height - 5, &iconWidth, &iconHeight); } } /* Determine how much room is left for text. */ textWidth = 0; textHeight = 0; if(bp->text) { textWidth = GetStringWidth(bp->font, bp->text); textHeight = GetStringHeight(bp->font); if(textWidth + iconWidth + 8 > width) { textWidth = width - iconWidth - 8; if(textWidth < 0) { textWidth = 0; } } } /* Determine the offset of the text in the button. */ switch(bp->alignment) { case ALIGN_CENTER: xoffset = width / 2 - (iconWidth + textWidth) / 2; if(xoffset < 0) { xoffset = 0; } break; case ALIGN_LEFT: default: xoffset = 3; break; } /* Display the icon. */ if(bp->icon) { yoffset = height / 2 - iconHeight / 2; PutIcon(bp->icon, drawable, x + xoffset, y + yoffset, iconWidth, iconHeight); xoffset += iconWidth + 2; } /* Display the label. */ if(bp->text && textWidth) { yoffset = height / 2 - textHeight / 2; RenderString(drawable, bp->font, fg, x + xoffset, y + yoffset, textWidth, NULL, bp->text); } }
/** Display a string. */ void RenderString(Drawable d, FontType font, ColorType color, int x, int y, int width, const char *str) { #ifdef USE_ICONV static char isUTF8 = -1; #endif XRectangle rect; Region renderRegion; int len; char *output; #ifdef USE_FRIBIDI FriBidiChar *temp_i; FriBidiChar *temp_o; FriBidiParType type = FRIBIDI_PAR_ON; int unicodeLength; #endif #ifdef USE_XFT XGlyphInfo extents; #endif char *utf8String; /* Early return for empty strings. */ if(!str || !str[0]) { return; } /* Convert to UTF-8 if necessary. */ utf8String = GetUTF8String(str); /* Get the length of the UTF-8 string. */ len = strlen(utf8String); /* Apply the bidi algorithm if requested. */ #ifdef USE_FRIBIDI temp_i = AllocateStack((len + 1) * sizeof(FriBidiChar)); temp_o = AllocateStack((len + 1) * sizeof(FriBidiChar)); unicodeLength = fribidi_charset_to_unicode(FRIBIDI_CHAR_SET_UTF8, utf8String, len, temp_i); fribidi_log2vis(temp_i, unicodeLength, &type, temp_o, NULL, NULL, NULL); output = AllocateStack(4 * len + 1); fribidi_unicode_to_charset(FRIBIDI_CHAR_SET_UTF8, temp_o, unicodeLength, (char*)output); len = strlen(output); #else output = utf8String; #endif /* Get the bounds for the string based on the specified width. */ rect.x = x; rect.y = y; rect.height = GetStringHeight(font); #ifdef USE_XFT JXftTextExtentsUtf8(display, fonts[font], (const unsigned char*)output, len, &extents); rect.width = extents.xOff; #else rect.width = XTextWidth(fonts[font], output, len); #endif rect.width = Min(rect.width, width) + 2; /* Combine the width bounds with the region to use. */ renderRegion = XCreateRegion(); XUnionRectWithRegion(&rect, renderRegion, renderRegion); /* Display the string. */ #ifdef USE_XFT JXftDrawChange(xd, d); JXftDrawSetClip(xd, renderRegion); JXftDrawStringUtf8(xd, GetXftColor(color), fonts[font], x, y + fonts[font]->ascent, (const unsigned char*)output, len); JXftDrawChange(xd, rootWindow); #else JXSetForeground(display, fontGC, colors[color]); JXSetRegion(display, fontGC, renderRegion); JXSetFont(display, fontGC, fonts[font]->fid); JXDrawString(display, d, fontGC, x, y + fonts[font]->ascent, output, len); #endif /* Free any memory used for UTF conversion. */ #ifdef USE_FRIBIDI ReleaseStack(temp_i); ReleaseStack(temp_o); ReleaseStack(output); #endif ReleaseUTF8String(utf8String); XDestroyRegion(renderRegion); }
/** Draw a button. */ void DrawButton(ButtonNode *bp) { ColorType fg; long bg1, bg2; long up, down; DecorationsType decorations; Drawable drawable; GC gc; int x, y; int width, height; int xoffset, yoffset; int iconWidth, iconHeight; int textWidth, textHeight; Assert(bp); char *printableName = bp->text; /* Check for E2 80 AA (LEFT-TO-RIGHT-EMBEDDING) character in the beginning and trim it (for Midori) */ if(strncmp(printableName, "\xe2\x80\xaa", 3) == 0) { printableName += 3; } drawable = bp->drawable; x = bp->x; y = bp->y; width = bp->width; height = bp->height; gc = JXCreateGC(display, drawable, 0, NULL); /* Determine the colors to use. */ switch(bp->type) { case BUTTON_LABEL: fg = COLOR_MENU_FG; bg1 = colors[COLOR_MENU_BG]; bg2 = colors[COLOR_MENU_BG]; up = colors[COLOR_MENU_UP]; down = colors[COLOR_MENU_DOWN]; decorations = settings.menuDecorations; break; case BUTTON_MENU_ACTIVE: fg = COLOR_MENU_ACTIVE_FG; bg1 = colors[COLOR_MENU_ACTIVE_BG1]; bg2 = colors[COLOR_MENU_ACTIVE_BG2]; down = colors[COLOR_MENU_ACTIVE_UP]; up = colors[COLOR_MENU_ACTIVE_DOWN]; decorations = settings.menuDecorations; break; case BUTTON_TRAY: case BUTTON_TASK: fg = COLOR_TRAY_FG; bg1 = colors[COLOR_TRAY_BG1]; bg2 = colors[COLOR_TRAY_BG2]; up = colors[COLOR_TRAY_UP]; down = colors[COLOR_TRAY_DOWN]; decorations = settings.trayDecorations; break; case BUTTON_TRAY_ACTIVE: case BUTTON_TASK_ACTIVE: fg = COLOR_TRAY_ACTIVE_FG; bg1 = colors[COLOR_TRAY_ACTIVE_BG1]; bg2 = colors[COLOR_TRAY_ACTIVE_BG2]; down = colors[COLOR_TRAY_ACTIVE_UP]; up = colors[COLOR_TRAY_ACTIVE_DOWN]; decorations = settings.trayDecorations; break; case BUTTON_MENU: default: fg = COLOR_MENU_FG; bg1 = colors[COLOR_MENU_BG]; bg2 = colors[COLOR_MENU_BG]; up = colors[COLOR_MENU_UP]; down = colors[COLOR_MENU_DOWN]; decorations = settings.menuDecorations; break; } /* Draw the background. */ if(bp->fill) { /* Draw the button background. */ JXSetForeground(display, gc, bg1); /* single color */ JXFillRectangle(display, drawable, gc, x, y, width, height); } /* Draw the border. */ if(bp->border) { JXSetForeground(display, gc, down); JXDrawRectangle(display, drawable, gc, x, y, width - 1, height - 1); } /* Determine the size of the icon (if any) to display. */ iconWidth = 0; iconHeight = 0; if(bp->icon) { if(bp->icon == &emptyIcon) { iconWidth = Min(width/2, height/2); iconHeight = iconWidth; } else { const int ratio = (bp->icon->images->width << 16) / bp->icon->images->height; iconHeight = height/2; iconWidth = (iconHeight * ratio) >> 16; if(iconWidth > width/2) { iconWidth = width/2; iconHeight = (iconWidth << 16) / ratio; } } } /* Determine how much room is left for text. */ textWidth = 0; textHeight = 0; if(printableName) { textWidth = GetStringWidth(bp->font, printableName); textHeight = GetStringHeight(bp->font); if(iconWidth > 0 && textWidth + iconWidth + 7 > width) { textWidth = width - iconWidth - textHeight*3/2; } else if(iconWidth == 0 && textWidth + 5 > width) { textWidth = width - 5; } textWidth = textWidth < 0 ? 0 : textWidth; } /* Determine the offset of the text in the button. */ if(bp->alignment == ALIGN_CENTER) { xoffset = (width - iconWidth - textWidth + 1) / 2; if(xoffset < 0) { xoffset = 0; } } else { xoffset = textHeight/2; } /* Display the icon. */ if(bp->icon) { yoffset = (height - iconHeight + 1) / 2; PutIcon(bp->icon, drawable, colors[fg], x + xoffset, y + yoffset, iconWidth, iconHeight); xoffset += iconWidth + textHeight/2; } /* Display the label. */ if(textWidth > 0) { yoffset = (height - textHeight + 1) / 2; RenderString(drawable, bp->font, fg, x + xoffset, y + yoffset, textWidth, printableName); } JXFreeGC(display, gc); }
/** Get a scaled icon. */ ScaledIconNode *GetScaledIcon(IconNode *icon, long fg, int rwidth, int rheight) { XColor color; XImage *image; ScaledIconNode *np; GC maskGC; int x, y; int scalex, scaley; /* Fixed point. */ int srcx, srcy; /* Fixed point. */ int ratio; /* Fixed point. */ int nwidth, nheight; unsigned char *data; Assert(icon); Assert(icon->image); if(rwidth == 0) { rwidth = icon->image->width; } if(rheight == 0) { rheight = icon->image->height; } ratio = (icon->image->width << 16) / icon->image->height; nwidth = Min(rwidth, (rheight * ratio) >> 16); nheight = Min(rheight, (nwidth << 16) / ratio); nwidth = (nheight * ratio) >> 16; if(nwidth < 1) { nwidth = 1; } if(nheight < 1) { nheight = 1; } /* Check if this size already exists. * Note that XRender scales on the fly. */ for(np = icon->nodes; np; np = np->next) { #ifdef USE_XRENDER if(np->imagePicture != None) { np->width = nwidth; np->height = nheight; return np; } #endif if(np->width == nwidth && np->height == nheight) { if(!icon->image->bitmap || np->fg == fg) { return np; } } } /* See if we can use XRender to create the icon. */ #ifdef USE_XRENDER if(haveRender) { np = CreateScaledRenderIcon(icon, fg, nwidth, nheight); /* Don't keep the image data around after creating the icon. */ Release(icon->image->data); icon->image->data = NULL; return np; } #endif /* Create a new ScaledIconNode the old-fashioned way. */ np = Allocate(sizeof(ScaledIconNode)); np->fg = fg; np->width = nwidth; np->height = nheight; np->next = icon->nodes; #ifdef USE_XRENDER np->imagePicture = None; #endif icon->nodes = np; /* Create a mask. */ np->mask = JXCreatePixmap(display, rootWindow, nwidth, nheight, 1); maskGC = JXCreateGC(display, np->mask, 0, NULL); JXSetForeground(display, maskGC, 0); JXFillRectangle(display, np->mask, maskGC, 0, 0, nwidth, nheight); JXSetForeground(display, maskGC, 1); /* Create a temporary XImage for scaling. */ image = JXCreateImage(display, rootVisual, rootDepth, ZPixmap, 0, NULL, nwidth, nheight, 8, 0); image->data = Allocate(sizeof(unsigned long) * nwidth * nheight); /* Determine the scale factor. */ scalex = (icon->image->width << 16) / nwidth; scaley = (icon->image->height << 16) / nheight; data = icon->image->data; srcy = 0; for(y = 0; y < nheight; y++) { const int yindex = (srcy >> 16) * icon->image->width; srcx = 0; for(x = 0; x < nwidth; x++) { if(icon->image->bitmap) { const int index = yindex + (srcx >> 16); const int offset = index >> 3; const int mask = 1 << (index & 7); if(data[offset] & mask) { JXDrawPoint(display, np->mask, maskGC, x, y); XPutPixel(image, x, y, fg); } } else { const int yindex = (srcy >> 16) * icon->image->width; const int index = 4 * (yindex + (srcx >> 16)); color.red = data[index + 1]; color.red |= color.red << 8; color.green = data[index + 2]; color.green |= color.green << 8; color.blue = data[index + 3]; color.blue |= color.blue << 8; GetColor(&color); XPutPixel(image, x, y, color.pixel); if(data[index] >= 128) { JXDrawPoint(display, np->mask, maskGC, x, y); } } srcx += scalex; } srcy += scaley; }
/** Show a popup window. */ void ShowPopup(int x, int y, const char *text, const PopupMaskType context) { const ScreenType *sp; Assert(text); if(!(settings.popupMask & context)) { return; } if(popup.text) { if(x == popup.x && y == popup.y && !strcmp(popup.text, text)) { // This popup is already shown. return; } Release(popup.text); popup.text = NULL; } if(text[0] == 0) { return; } GetMousePosition(&popup.mx, &popup.my, &popup.mw); popup.text = CopyString(text); popup.height = GetStringHeight(FONT_POPUP) + 2; popup.width = GetStringWidth(FONT_POPUP, popup.text) + 9; sp = GetCurrentScreen(x, y); if(popup.width > sp->width) { popup.width = sp->width; } popup.x = x; if(y + 2 * popup.height + 2 >= sp->height) { popup.y = y - popup.height - 2; } else { popup.y = y + popup.height + 2; } if(popup.width + popup.x > sp->x + sp->width) { popup.x = sp->x + sp->width - popup.width - 2; } if(popup.height + popup.y > sp->y + sp->height) { popup.y = sp->y + sp->height - popup.height - 2; } if(popup.x < 2) { popup.x = 2; } if(popup.y < 2) { popup.y = 2; } if(popup.window == None) { XSetWindowAttributes attr; unsigned long attrMask = 0; attrMask |= CWEventMask; attr.event_mask = ExposureMask | PointerMotionMask | PointerMotionHintMask; attrMask |= CWSaveUnder; attr.save_under = True; attrMask |= CWDontPropagate; attr.do_not_propagate_mask = PointerMotionMask | ButtonPressMask | ButtonReleaseMask; popup.window = JXCreateWindow(display, rootWindow, popup.x, popup.y, popup.width, popup.height, 0, CopyFromParent, InputOutput, CopyFromParent, attrMask, &attr); SetAtomAtom(popup.window, ATOM_NET_WM_WINDOW_TYPE, ATOM_NET_WM_WINDOW_TYPE_NOTIFICATION); JXMapRaised(display, popup.window); } else { JXMoveResizeWindow(display, popup.window, popup.x, popup.y, popup.width, popup.height); JXFreePixmap(display, popup.pmap); } popup.pmap = JXCreatePixmap(display, popup.window, popup.width, popup.height, rootDepth); JXSetForeground(display, rootGC, colors[COLOR_POPUP_BG]); JXFillRectangle(display, popup.pmap, rootGC, 0, 0, popup.width - 1, popup.height - 1); JXSetForeground(display, rootGC, colors[COLOR_POPUP_OUTLINE]); JXDrawRectangle(display, popup.pmap, rootGC, 0, 0, popup.width - 1, popup.height - 1); RenderString(popup.pmap, FONT_POPUP, COLOR_POPUP_FG, 4, 1, popup.width, popup.text); JXCopyArea(display, popup.pmap, popup.window, rootGC, 0, 0, popup.width, popup.height, 0, 0); }