aboutsummaryrefslogtreecommitdiff
path: root/src/traybutton.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/traybutton.c')
-rw-r--r--src/traybutton.c454
1 files changed, 454 insertions, 0 deletions
diff --git a/src/traybutton.c b/src/traybutton.c
new file mode 100644
index 0000000..b596ba0
--- /dev/null
+++ b/src/traybutton.c
@@ -0,0 +1,454 @@
+/***************************************************************************
+ ***************************************************************************/
+
+#include "jwm.h"
+#include "traybutton.h"
+#include "tray.h"
+#include "icon.h"
+#include "image.h"
+#include "error.h"
+#include "root.h"
+#include "main.h"
+#include "color.h"
+#include "font.h"
+#include "button.h"
+#include "misc.h"
+#include "screen.h"
+#include "desktop.h"
+#include "popup.h"
+#include "timing.h"
+
+#define BUTTON_SIZE 4
+
+typedef struct TrayButtonType {
+
+ TrayComponentType *cp;
+
+ char *label;
+ char *popup;
+ char *iconName;
+ IconNode *icon;
+
+ char *action;
+
+ int mousex;
+ int mousey;
+ TimeType mouseTime;
+
+ struct TrayButtonType *next;
+
+} TrayButtonType;
+
+static TrayButtonType *buttons;
+
+static void CheckedCreate(TrayComponentType *cp);
+static void Create(TrayComponentType *cp);
+static void Destroy(TrayComponentType *cp);
+static void SetSize(TrayComponentType *cp, int width, int height);
+static void Resize(TrayComponentType *cp);
+
+static void ProcessButtonEvent(TrayComponentType *cp,
+ int x, int y, int mask);
+static void ProcessMotionEvent(TrayComponentType *cp,
+ int x, int y, int mask);
+
+/***************************************************************************
+ ***************************************************************************/
+void InitializeTrayButtons() {
+ buttons = NULL;
+}
+
+/***************************************************************************
+ ***************************************************************************/
+void StartupTrayButtons() {
+
+ TrayButtonType *bp;
+
+ for(bp = buttons; bp; bp = bp->next) {
+ if(bp->label) {
+ bp->cp->requestedWidth
+ = GetStringWidth(FONT_TRAYBUTTON, bp->label) + 4;
+ bp->cp->requestedHeight
+ = GetStringHeight(FONT_TRAYBUTTON);
+ } else {
+ bp->cp->requestedWidth = 0;
+ bp->cp->requestedHeight = 0;
+ }
+ if(bp->iconName) {
+ bp->icon = LoadNamedIcon(bp->iconName);
+ if(bp->icon) {
+ bp->cp->requestedWidth += bp->icon->image->width;
+ bp->cp->requestedHeight += bp->icon->image->height;
+ } else {
+ Warning("could not load tray icon: \"%s\"", bp->iconName);
+ }
+ }
+ bp->cp->requestedWidth += 2 * BUTTON_SIZE;
+ bp->cp->requestedHeight += 2 * BUTTON_SIZE;
+ }
+
+}
+
+/***************************************************************************
+ ***************************************************************************/
+void ShutdownTrayButtons() {
+
+}
+
+/***************************************************************************
+ ***************************************************************************/
+void DestroyTrayButtons() {
+
+ TrayButtonType *bp;
+
+ while(buttons) {
+ bp = buttons->next;
+ if(buttons->label) {
+ Release(buttons->label);
+ }
+ if(buttons->iconName) {
+ Release(buttons->iconName);
+ }
+ if(buttons->action) {
+ Release(buttons->action);
+ }
+ if(buttons->popup) {
+ Release(buttons->popup);
+ }
+ Release(buttons);
+ buttons = bp;
+ }
+
+}
+
+/***************************************************************************
+ ***************************************************************************/
+TrayComponentType *CreateTrayButton(const char *iconName,
+ const char *label, const char *action,
+ const char *popup, int width, int height) {
+
+ TrayButtonType *bp;
+ TrayComponentType *cp;
+
+ if((label == NULL || strlen(label) == 0)
+ && (iconName == NULL || strlen(iconName) == 0)) {
+ Warning("no icon or label for TrayButton");
+ return NULL;
+ }
+
+ if(width < 0) {
+ Warning("invalid TrayButton width: %d", width);
+ width = 0;
+ }
+ if(height < 0) {
+ Warning("invalid TrayButton height: %d", height);
+ height = 0;
+ }
+
+ bp = Allocate(sizeof(TrayButtonType));
+ bp->next = buttons;
+ buttons = bp;
+
+ bp->icon = NULL;
+ if(iconName) {
+ bp->iconName = Allocate(strlen(iconName) + 1);
+ strcpy(bp->iconName, iconName);
+ } else {
+ bp->iconName = NULL;
+ }
+
+ if(label) {
+ bp->label = Allocate(strlen(label) + 1);
+ strcpy(bp->label, label);
+ } else {
+ bp->label = NULL;
+ }
+
+ if(action) {
+ bp->action = Allocate(strlen(action) + 1);
+ strcpy(bp->action, action);
+ } else {
+ bp->action = NULL;
+ }
+
+ if(popup) {
+ bp->popup = Allocate(strlen(popup) + 1);
+ strcpy(bp->popup, popup);
+ } else {
+ bp->popup = NULL;
+ }
+
+ cp = CreateTrayComponent();
+ cp->object = bp;
+ bp->cp = cp;
+ cp->requestedWidth = width;
+ cp->requestedHeight = height;
+
+ cp->Create = CheckedCreate;
+ cp->Destroy = Destroy;
+ cp->SetSize = SetSize;
+ cp->Resize = Resize;
+
+ cp->ProcessButtonEvent = ProcessButtonEvent;
+ if(popup || label) {
+ cp->ProcessMotionEvent = ProcessMotionEvent;
+ }
+
+ return cp;
+
+}
+
+/***************************************************************************
+ ***************************************************************************/
+void SetSize(TrayComponentType *cp, int width, int height) {
+
+ TrayButtonType *bp;
+ int labelWidth, labelHeight;
+ int iconWidth, iconHeight;
+ double ratio;
+
+ bp = (TrayButtonType*)cp->object;
+
+ if(bp->icon) {
+
+ if(bp->label) {
+ labelWidth = GetStringWidth(FONT_TRAYBUTTON, bp->label) + 4;
+ labelHeight = GetStringHeight(FONT_TRAYBUTTON);
+ } else {
+ labelWidth = 0;
+ labelHeight = 0;
+ }
+
+ iconWidth = bp->icon->image->width;
+ iconHeight = bp->icon->image->height;
+ ratio = (double)iconWidth / iconHeight;
+
+ if(width > 0) {
+
+ /* Compute height from width. */
+ iconWidth = width - labelWidth - 2 * BUTTON_SIZE;
+ iconHeight = iconWidth / ratio;
+ height = Max(iconHeight, labelHeight) + 2 * BUTTON_SIZE;
+
+ } else if(height > 0) {
+
+ /* Compute width from height. */
+ iconHeight = height - 2 * BUTTON_SIZE;
+ iconWidth = iconHeight * ratio;
+ width = iconWidth + labelWidth + 2 * BUTTON_SIZE;
+
+ }
+
+ cp->width = width;
+ cp->height = height;
+
+ }
+
+}
+
+/***************************************************************************
+ ***************************************************************************/
+void CheckedCreate(TrayComponentType *cp) {
+
+ TrayButtonType *bp;
+
+ bp = (TrayButtonType*)cp->object;
+
+ /* Validate the action for this tray button. */
+ if(bp->action && strlen(bp->action) > 0) {
+ if(!strncmp(bp->action, "exec:", 5)) {
+ /* Valid. */
+ } else if(!strncmp(bp->action, "root:", 5)) {
+ /* Valid. However, the specified root menu may not exist.
+ * This case is handled in ValidateTrayButtons.
+ */
+ } else if(!strcmp(bp->action, "showdesktop")) {
+ /* Valid. */
+ } else {
+ Warning("invalid TrayButton action: \"%s\"", bp->action);
+ }
+ } else {
+ /* Valid. However, root menu 1 may not exist.
+ * This case is handled in ValidateTrayButtons.
+ */
+ }
+
+ Create(cp);
+
+}
+
+/***************************************************************************
+ ***************************************************************************/
+void Create(TrayComponentType *cp) {
+
+ ButtonNode button;
+ TrayButtonType *bp;
+ int labelx;
+
+ bp = (TrayButtonType*)cp->object;
+
+ cp->pixmap = JXCreatePixmap(display, rootWindow,
+ cp->width, cp->height, rootDepth);
+
+ JXSetForeground(display, rootGC, colors[COLOR_TRAYBUTTON_BG]);
+ JXFillRectangle(display, cp->pixmap, rootGC, 0, 0, cp->width, cp->height);
+
+ ResetButton(&button, cp->pixmap, rootGC);
+ button.type = BUTTON_TASK;
+ button.width = cp->width - 3;
+ button.height = cp->height - 3;
+ button.x = 1;
+ button.y = 1;
+ DrawButton(&button);
+
+ /* Compute the offset of the text. */
+ if(bp->label) {
+ if(!bp->icon) {
+ labelx = 2 + cp->width / 2;
+ labelx -= GetStringWidth(FONT_TRAYBUTTON, bp->label) / 2;
+ } else {
+ labelx = cp->width;
+ labelx -= GetStringWidth(FONT_TRAYBUTTON, bp->label) + 4;
+ }
+ } else {
+ labelx = cp->width;
+ }
+ labelx -= BUTTON_SIZE;
+
+ if(bp->icon) {
+ PutIcon(bp->icon, cp->pixmap, BUTTON_SIZE, BUTTON_SIZE,
+ labelx - BUTTON_SIZE, cp->height - BUTTON_SIZE * 2);
+ }
+
+ if(bp->label) {
+ RenderString(cp->pixmap, FONT_TRAYBUTTON, COLOR_TRAYBUTTON_FG,
+ labelx + 2, cp->height / 2 - GetStringHeight(FONT_TRAYBUTTON) / 2,
+ cp->width - labelx, NULL, bp->label);
+ }
+
+}
+
+/***************************************************************************
+ ***************************************************************************/
+void Resize(TrayComponentType *cp) {
+
+ Destroy(cp);
+ Create(cp);
+
+}
+
+/***************************************************************************
+ ***************************************************************************/
+void Destroy(TrayComponentType *cp) {
+ if(cp->pixmap != None) {
+ JXFreePixmap(display, cp->pixmap);
+ }
+}
+
+/***************************************************************************
+ ***************************************************************************/
+void ProcessButtonEvent(TrayComponentType *cp, int x, int y, int mask) {
+
+ const ScreenType *sp;
+ int mwidth, mheight;
+ int button;
+
+ TrayButtonType *bp = (TrayButtonType*)cp->object;
+
+ Assert(bp);
+
+ if(bp->action && strlen(bp->action) > 0) {
+ if(!strncmp(bp->action, "exec:", 5)) {
+ RunCommand(bp->action + 5);
+ return;
+ } else if(!strncmp(bp->action, "root:", 5)) {
+ button = atoi(bp->action + 5);
+ } else if(!strcmp(bp->action, "showdesktop")) {
+ ShowDesktop();
+ return;
+ } else {
+ return;
+ }
+ } else {
+ button = 1;
+ }
+
+ GetRootMenuSize(button, &mwidth, &mheight);
+
+ sp = GetCurrentScreen(cp->screenx, cp->screeny);
+
+ if(cp->tray->layout == LAYOUT_HORIZONTAL) {
+ x = cp->screenx;
+ if(cp->screeny + cp->height / 2 < sp->y + sp->height / 2) {
+ y = cp->screeny + cp->height;
+ } else {
+ y = cp->screeny - mheight;
+ }
+ } else {
+ y = cp->screeny;
+ if(cp->screenx + cp->width / 2 < sp->x + sp->width / 2) {
+ x = cp->screenx + cp->width;
+ } else {
+ x = cp->screenx - mwidth;
+ }
+ }
+
+ ShowRootMenu(button, x, y);
+
+}
+
+/***************************************************************************
+ ***************************************************************************/
+void ProcessMotionEvent(TrayComponentType *cp, int x, int y, int mask) {
+
+ TrayButtonType *bp = (TrayButtonType*)cp->object;
+
+ bp->mousex = cp->screenx + x;
+ bp->mousey = cp->screeny + y;
+ GetCurrentTime(&bp->mouseTime);
+
+}
+
+/***************************************************************************
+ ***************************************************************************/
+void SignalTrayButton(const TimeType *now, int x, int y) {
+
+ TrayButtonType *bp;
+ const char *popup;
+
+ for(bp = buttons; bp; bp = bp->next) {
+ if(bp->popup) {
+ popup = bp->popup;
+ } else if(bp->label) {
+ popup = bp->label;
+ } else {
+ continue;
+ }
+ if(abs(bp->mousex - x) < POPUP_DELTA
+ && abs(bp->mousey - y) < POPUP_DELTA) {
+ if(GetTimeDifference(now, &bp->mouseTime) >= popupDelay) {
+ ShowPopup(x, y, popup);
+ }
+ }
+ }
+
+}
+
+/***************************************************************************
+ ***************************************************************************/
+void ValidateTrayButtons() {
+
+ TrayButtonType *bp;
+ int bindex;
+
+ for(bp = buttons; bp; bp = bp->next) {
+ if(bp->action && !strncmp(bp->action, "root:", 5)) {
+ bindex = atoi(bp->action + 5);
+ if(!IsRootMenuDefined(bindex)) {
+ Warning("tray button: root menu %d not defined", bindex);
+ }
+ }
+ }
+
+}
+