Prefs/ScreenMode: change the way depth is selected
[AROS.git] / workbench / tools / commodities / DepthMenu.c
blob898f8abf690a34f32c478493ca016fa8d91f43b8
1 /*
2 Copyright © 2007, The AROS Development Team. All rights reserved.
3 $Id$
5 DepthMenu commodity -- shows list of window/screens in popupmenu.
6 */
8 /******************************************************************************
10 NAME
12 DepthMenu
14 SYNOPSIS
16 CX_PRIORITY/N/K
18 LOCATION
20 SYS:Tools/Commodities
22 FUNCTION
24 Shows popup menu with list of screens and windows after user clicks on
25 depth gadget of screen or window.
27 INPUTS
29 CX_PRIORITY -- The priority of the commodity
31 RESULT
33 NOTES
35 EXAMPLE
37 BUGS
39 SEE ALSO
41 INTERNALS
43 ******************************************************************************/
45 #include <aros/symbolsets.h>
46 #include <intuition/intuition.h>
47 #include <intuition/intuitionbase.h>
48 #include <libraries/commodities.h>
49 #include <libraries/locale.h>
50 #include <proto/exec.h>
51 #include <proto/dos.h>
52 #include <proto/intuition.h>
53 #include <proto/graphics.h>
54 #include <proto/layers.h>
55 #include <proto/commodities.h>
56 #include <proto/input.h>
57 #include <proto/alib.h>
58 #include <proto/locale.h>
60 #include <stdio.h>
61 #include <string.h>
63 #define DEBUG 0
64 #include <aros/debug.h>
66 #define CATCOMP_ARRAY
67 #include "strings.h"
69 #define CATALOG_NAME "System/Tools/Commodities.catalog"
70 #define CATALOG_VERSION 3
73 /***************************************************************************/
75 UBYTE version[] = "$VER: DepthMenu 0.3 (30.05.2007)";
77 #define ARG_TEMPLATE "CX_PRIORITY=PRI/N/K"
79 #define ARG_PRI 0
80 #define NUM_ARGS 1
82 #define DM_WINDOW 1
83 #define DM_SCREEN 2
85 #define DM_MAXENTRY 15
86 #define DM_MAXSTRLEN 30
88 #define DM_BORDERWIDTH 4
90 #define SYSGADTYPE(gad) ((gad)->GadgetType & GTYP_SYSTYPEMASK)
92 struct Device *InputBase = NULL;
93 struct Catalog *catalog;
94 struct IOStdReq *inputIO;
97 /* The Depth broker */
98 static struct NewBroker nb =
100 NB_VERSION,
101 NULL,
102 NULL,
103 NULL,
104 NBU_NOTIFY | NBU_UNIQUE,
107 NULL,
112 typedef struct _DMState
114 CxObj *cs_broker;
115 struct MsgPort *cs_msgPort;
116 } DMState;
119 typedef struct _DMData
121 struct Window *popupwindow;
122 WORD entries;
123 WORD mode;
124 void *address[DM_MAXENTRY]; // struct Window * or struct Screen *
125 char title[DM_MAXENTRY][DM_MAXSTRLEN];
126 ULONG ibaselock;
127 BOOL locked;
128 WORD selected; // -1 means no entry selected
129 struct DrawInfo *drawinfo;
130 UBYTE bgpen;
131 UBYTE txtpen;
132 UBYTE highpen;
133 UBYTE shinepen;
134 UBYTE shadowpen;
135 } DMData;
137 static DMData dmdata;
139 /************************************************************************************/
141 static void freeResources(DMState *cs);
142 static BOOL initiate(int argc, char **argv, DMState *cs);
143 static void depthMenu(CxMsg *msg, CxObj *co);
144 static void handleMenuUp(CxMsg *cxm);
145 static void handleMenuDown(CxMsg *cxm);
146 static void handleMouseMove(CxMsg *cxm);
147 static void showPopup(WORD mode, struct Screen *screen, WORD xpos, WORD ypos);
148 static void lockIBaseSave(void);
149 static void unlockIBaseSave(void);
150 static CONST_STRPTR _(ULONG id);
151 static BOOL Locale_Initialize(VOID);
152 static VOID Locale_Deinitialize(VOID);
153 static void showSimpleMessage(CONST_STRPTR msgString);
154 static void drawEntry(LONG entry, BOOL selstate);
155 static BOOL depthGadHit(struct Window *window, WORD mousex, WORD mousey);
157 /************************************************************************************/
159 static CONST_STRPTR _(ULONG id)
161 if (LocaleBase != NULL && catalog != NULL)
163 return GetCatalogStr(catalog, id, CatCompArray[id].cca_Str);
165 else
167 return CatCompArray[id].cca_Str;
171 /************************************************************************************/
173 static BOOL Locale_Initialize(VOID)
175 if (LocaleBase != NULL)
177 catalog = OpenCatalog
179 NULL, CATALOG_NAME, OC_Version, CATALOG_VERSION, TAG_DONE
182 else
184 catalog = NULL;
187 return TRUE;
190 /************************************************************************************/
192 static VOID Locale_Deinitialize(VOID)
194 if(LocaleBase != NULL && catalog != NULL) CloseCatalog(catalog);
197 /************************************************************************************/
199 static void showSimpleMessage(CONST_STRPTR msgString)
201 struct EasyStruct easyStruct;
203 easyStruct.es_StructSize = sizeof(easyStruct);
204 easyStruct.es_Flags = 0;
205 easyStruct.es_Title = _(MSG_DEPTHMENU_CXNAME);
206 easyStruct.es_TextFormat = msgString;
207 easyStruct.es_GadgetFormat = _(MSG_OK);
209 if (IntuitionBase != NULL && !Cli() )
211 EasyRequestArgs(NULL, &easyStruct, NULL, NULL);
213 else
215 PutStr(msgString);
219 /************************************************************************************/
221 static BOOL initiate(int argc, char **argv, DMState *cs)
223 CxObj *customObj;
225 memset(cs, 0, sizeof(DMState));
227 if (Cli() != NULL)
229 struct RDArgs *rda;
230 IPTR args[] = { (IPTR) NULL, (IPTR) NULL, FALSE };
232 rda = ReadArgs(ARG_TEMPLATE, args, NULL);
233 if (rda != NULL)
235 if (args[ARG_PRI] != (IPTR) NULL)
237 nb.nb_Pri = *(LONG *)args[ARG_PRI];
240 FreeArgs(rda);
242 else
244 UBYTE **array = ArgArrayInit(argc, (UBYTE **)argv);
245 nb.nb_Pri = ArgInt(array, "CX_PRIORITY", 0);
246 ArgArrayDone();
249 nb.nb_Name = _(MSG_DEPTHMENU_CXNAME);
250 nb.nb_Title = _(MSG_DEPTHMENU_CXTITLE);
251 nb.nb_Descr = _(MSG_DEPTHMENU_CXDESCR);
253 cs->cs_msgPort = CreateMsgPort();
254 if (cs->cs_msgPort == NULL)
256 showSimpleMessage(_(MSG_CANT_CREATE_MSGPORT));
257 return FALSE;
260 nb.nb_Port = cs->cs_msgPort;
261 cs->cs_broker = CxBroker(&nb, 0);
262 if (cs->cs_broker == NULL)
264 return FALSE;
267 customObj = CxCustom(depthMenu, 0);
268 if (customObj == NULL)
270 showSimpleMessage(_(MSG_CANT_CREATE_MSGPORT));
271 return FALSE;
274 AttachCxObj(cs->cs_broker, customObj);
275 ActivateCxObj(cs->cs_broker, TRUE);
277 inputIO = (struct IOStdReq *)CreateIORequest(cs->cs_msgPort,
278 sizeof(struct IOStdReq));
280 if (inputIO == NULL)
282 showSimpleMessage(_(MSG_CANT_ALLOCATE_MEM));
283 return FALSE;
286 if ((OpenDevice("input.device", 0, (struct IORequest *)inputIO, 0)) != 0)
288 showSimpleMessage(_(MSG_CANT_OPEN_INPUTDEVICE));
289 return FALSE;
292 InputBase = (struct Device *)inputIO->io_Device;
294 return TRUE;
297 /************************************************************************************/
299 static void freeResources(DMState *cs)
301 struct Message *cxm;
303 if (cs->cs_broker != NULL)
305 DeleteCxObjAll(cs->cs_broker);
308 if (cs->cs_msgPort != NULL)
310 while ((cxm = GetMsg(cs->cs_msgPort)))
312 ReplyMsg(cxm);
314 DeleteMsgPort(cs->cs_msgPort);
317 if (inputIO != NULL)
319 CloseDevice((struct IORequest *)inputIO);
320 DeleteIORequest((struct IORequest *)inputIO);
324 /************************************************************************************/
326 static void depthMenu(CxMsg *cxm, CxObj *co)
328 struct InputEvent *ie = (struct InputEvent *)CxMsgData(cxm);
329 if (ie->ie_Class == IECLASS_RAWMOUSE)
331 switch (ie->ie_Code)
333 case MENUDOWN:
334 handleMenuDown(cxm);
335 break;
336 case MENUUP:
337 handleMenuUp(cxm);
338 break;
339 case IECODE_NOBUTTON:
340 handleMouseMove(cxm);
341 break;
346 /************************************************************************************/
348 static void handleMenuDown(CxMsg *cxm)
350 struct Screen *screen;
351 struct Layer *layer;
352 struct Window *window;
354 dmdata.selected = -1;
355 lockIBaseSave();
356 screen = IntuitionBase->FirstScreen; // frontmost screen
357 LockLayerInfo(&screen->LayerInfo);
358 layer = WhichLayer(&screen->LayerInfo, screen->MouseX, screen->MouseY);
359 UnlockLayerInfo(&screen->LayerInfo);
361 if (layer)
363 if (layer == screen->BarLayer) // Title bar of screen
365 D(bug("DepthMenu: BarLayer\n"));
366 if (
367 screen->Title
368 && (screen->MouseX > screen->Width - 25) // FIXME real width/pos. of depth gadget
369 && (screen->MouseX < screen->Width)
370 && (screen->MouseY > 0)
371 && (screen->MouseY < screen->BarHeight)
374 showPopup(DM_SCREEN, screen, screen->MouseX, screen->MouseY);
375 DisposeCxMsg(cxm);
378 else
380 D(bug("DepthMenu: other Layer\n"));
381 window = layer->Window;
382 if (
383 window
384 && depthGadHit(window, screen->MouseX, screen->MouseY)
387 showPopup(DM_WINDOW, screen, screen->MouseX, screen->MouseY);
388 DisposeCxMsg(cxm);
392 unlockIBaseSave();
395 /************************************************************************************/
397 static void handleMenuUp(CxMsg *cxm)
399 if (dmdata.popupwindow)
401 struct Screen *screen = dmdata.popupwindow->WScreen;
403 LONG entry = dmdata.selected;
405 FreeScreenDrawInfo(screen, dmdata.drawinfo);
406 dmdata.drawinfo = NULL;
408 CloseWindow(dmdata.popupwindow);
409 dmdata.popupwindow = NULL;
411 D(bug("DepthMenu: selected entry %d\n", entry));
412 if (entry != -1)
414 if (dmdata.mode == DM_WINDOW)
416 // does selected window still exist?
417 lockIBaseSave();
418 struct Window *checkwin = screen->FirstWindow;
419 while (checkwin)
421 if (checkwin == dmdata.address[entry])
423 unlockIBaseSave();
424 WindowToFront(dmdata.address[entry]);
425 ActivateWindow(dmdata.address[entry]);
426 break;
428 checkwin = checkwin->NextWindow;
431 else
433 // does selected screen still exist?
434 lockIBaseSave();
435 struct Screen *checkscreen = IntuitionBase->FirstScreen;
436 while (checkscreen)
438 if (checkscreen == dmdata.address[entry])
440 unlockIBaseSave();
441 ScreenToFront(dmdata.address[entry]);
442 break;
444 checkscreen = checkscreen->NextScreen;
449 // when we release mouse outside window it's still open
450 if (dmdata.popupwindow)
452 FreeScreenDrawInfo(screen, dmdata.drawinfo);
453 dmdata.drawinfo = NULL;
454 CloseWindow(dmdata.popupwindow);
455 dmdata.popupwindow = NULL;
458 unlockIBaseSave();
461 /************************************************************************************/
463 static void handleMouseMove(CxMsg *cxm)
465 if ( ! dmdata.popupwindow) return;
467 struct Screen *screen = dmdata.popupwindow->WScreen;
468 LONG entry = -1;
470 // are we within popupwindow?
471 if (
472 (screen->MouseX > dmdata.popupwindow->LeftEdge)
473 && (screen->MouseX < dmdata.popupwindow->LeftEdge + dmdata.popupwindow->Width)
474 && (screen->MouseY > dmdata.popupwindow->TopEdge)
475 && (screen->MouseY < dmdata.popupwindow->TopEdge + dmdata.popupwindow->Height)
478 entry = (screen->MouseY - dmdata.popupwindow->TopEdge - DM_BORDERWIDTH)
479 / dmdata.popupwindow->RPort->TxHeight;
480 if ((entry < 0) || (entry >= dmdata.entries)) entry = -1;
482 if (entry != dmdata.selected)
484 // undraw old entry
485 if (dmdata.selected != -1)
487 drawEntry(dmdata.selected, FALSE);
490 // draw selected
491 if (entry != -1)
493 drawEntry(entry, TRUE);
495 dmdata.selected = entry;
499 /************************************************************************************/
501 static void showPopup(WORD mode, struct Screen *screen, WORD xpos, WORD ypos)
503 lockIBaseSave();
505 // screen for popupmenu, check if it's a public screen
506 struct Screen *popupscreen = screen;
507 if ((popupscreen->Flags & (PUBLICSCREEN | WBENCHSCREEN)) == 0)
509 bug("DepthMenu: Frontmost screen isn't a public screen\n");
510 return;
513 if ( ! popupscreen->RastPort.Font)
515 bug("DepthMenu: Frontmost screen doesn't have Font\n");
516 return;
519 if (mode == DM_WINDOW)
521 D(bug("DepthMenu: window\n"));
522 struct Window *window = screen->FirstWindow;
523 dmdata.entries = 0;
524 while (window && (dmdata.entries < DM_MAXENTRY))
526 if (window->Title && (window->WScreen == screen))
528 dmdata.mode = DM_WINDOW;
529 dmdata.address[dmdata.entries] = window;
530 strncpy(dmdata.title[dmdata.entries], window->Title, DM_MAXSTRLEN);
531 dmdata.title[dmdata.entries][DM_MAXSTRLEN - 1] = 0;
532 dmdata.entries++;
534 window = window->NextWindow;
537 else
539 D(bug("DepthMenu: Screen\n"));
540 dmdata.entries = 0;
541 screen = screen->NextScreen; // Don't add frontmost screen, start with second screen
542 while (screen && (dmdata.entries < DM_MAXENTRY))
544 if (screen->Title)
546 dmdata.mode = DM_SCREEN;
547 dmdata.address[dmdata.entries] = screen;
548 strncpy(dmdata.title[dmdata.entries], screen->Title, DM_MAXSTRLEN);
549 dmdata.title[dmdata.entries][DM_MAXSTRLEN - 1] = 0;
550 dmdata.entries++;
552 screen = screen->NextScreen;
556 if (dmdata.entries > 0)
558 // calculate max. text length
559 LONG maxtextwidth = 0;
560 LONG textwidth;
561 int i;
562 for (i=0 ; i < dmdata.entries ; i++)
564 textwidth = TextLength(&popupscreen->RastPort, dmdata.title[i], strlen(dmdata.title[i]));
565 if (textwidth > maxtextwidth) maxtextwidth = textwidth;
568 LONG width = maxtextwidth + 2 * DM_BORDERWIDTH;
569 LONG height = dmdata.entries * popupscreen->RastPort.TxHeight + 2 * DM_BORDERWIDTH;
570 LONG left = xpos - width / 2;
571 LONG top = ypos - height / 2;
572 if (left < 0) left = 0;
573 if (left + width > popupscreen->Width) left = popupscreen->Width - width;
574 if (top < 0) top = 0;
575 if (top + height > popupscreen->Height) top = popupscreen->Height - height;
577 unlockIBaseSave();
579 dmdata.popupwindow = OpenWindowTags
581 NULL,
582 WA_Left, left,
583 WA_Top, top,
584 WA_InnerWidth, width,
585 WA_InnerHeight, height,
586 WA_Borderless, TRUE,
587 WA_Activate, TRUE,
588 WA_SmartRefresh, TRUE,
589 WA_PubScreen, popupscreen,
590 TAG_DONE
592 if (dmdata.popupwindow)
594 struct RastPort *rp = dmdata.popupwindow->RPort;
595 SetFont(rp, popupscreen->RastPort.Font); // use the screen's font for the menu
596 // so we can calculate the window size with the screen font
597 SetDrMd(rp, JAM1); // for text rendering
599 dmdata.drawinfo = GetScreenDrawInfo(popupscreen);
600 if (dmdata.drawinfo)
602 dmdata.bgpen = dmdata.drawinfo->dri_Pens[BACKGROUNDPEN];
603 dmdata.txtpen = dmdata.drawinfo->dri_Pens[TEXTPEN];
604 dmdata.highpen = dmdata.drawinfo->dri_Pens[FILLPEN];
605 dmdata.shinepen =dmdata.drawinfo->dri_Pens[SHINEPEN];
606 dmdata.shadowpen = dmdata.drawinfo->dri_Pens[SHADOWPEN];
608 else
610 bug("DepthMenu: GetScreenDrawInfo failed\n");
611 dmdata.bgpen = 0;
612 dmdata.txtpen = 1;
613 dmdata.highpen = 3;
614 dmdata.shinepen = 1;
615 dmdata.shadowpen = 1;
618 SetAPen(rp, dmdata.shinepen);
619 Move(rp, width-1, 0);
620 Draw(rp, 0, 0);
621 Draw(rp, 0, height-1);
622 SetAPen(rp, dmdata.shadowpen);
623 Draw(rp, width-1, height-1);
624 Draw(rp, width-1, 0);
626 int i;
627 for(i=0; i<dmdata.entries; i++)
629 drawEntry(i, FALSE);
632 else
634 bug("DepthMenu: Can't open popup window\n");
639 /************************************************************************************/
641 static void drawEntry(LONG entry, BOOL selstate)
643 struct RastPort *rp = dmdata.popupwindow->RPort;
645 if (selstate)
647 SetAPen(rp, dmdata.highpen);
649 else
651 SetAPen(rp, dmdata.bgpen);
653 RectFill(rp, DM_BORDERWIDTH, entry * rp->TxHeight + DM_BORDERWIDTH,
654 dmdata.popupwindow->Width - DM_BORDERWIDTH , (entry+1) * rp->TxHeight + DM_BORDERWIDTH);
656 SetAPen(rp, dmdata.txtpen);
657 Move(rp, DM_BORDERWIDTH, entry * rp->TxHeight + rp->TxBaseline + DM_BORDERWIDTH);
658 Text(rp, dmdata.title[entry], strlen(dmdata.title[entry]));
661 /************************************************************************************/
663 static BOOL depthGadHit(struct Window *window, WORD mousex, WORD mousey)
665 struct Gadget *gad;
667 for(gad = window->FirstGadget; gad; gad = gad->NextGadget)
669 if ( ! (gad->Flags & GFLG_DISABLED))
671 WORD x = window->LeftEdge + gad->LeftEdge;
672 WORD y = window->TopEdge + gad->TopEdge;
673 WORD w = gad->Width;
674 WORD h = gad->Height;
676 if (gad->Flags & GFLG_RELRIGHT) x += window->Width - 1;
677 if (gad->Flags & GFLG_RELBOTTOM) y += window->Height - 1;
678 if (gad->Flags & GFLG_RELWIDTH) w += window->Width;
679 if (gad->Flags & GFLG_RELHEIGHT) h += window->Height;
681 if (
682 (mousex >= x) &&
683 (mousey >= y) &&
684 (mousex < x + w) &&
685 (mousey < y + h)
688 if (SYSGADTYPE(gad) == GTYP_WDEPTH)
690 /* found depth */
691 return TRUE;
696 return FALSE;
699 /************************************************************************************/
701 static void lockIBaseSave(void)
703 if ( ! dmdata.locked)
705 dmdata.ibaselock = LockIBase(0);
706 dmdata.locked = TRUE;
710 /************************************************************************************/
712 static void unlockIBaseSave(void)
714 if (dmdata.locked)
716 UnlockIBase(dmdata.ibaselock);
717 dmdata.locked = FALSE;
721 /************************************************************************************/
723 static void handleCx(DMState *cs)
725 CxMsg *msg;
726 BOOL quit = FALSE;
727 LONG signals;
729 while (!quit)
731 signals = Wait((1 << nb.nb_Port->mp_SigBit) | SIGBREAKF_CTRL_C);
733 if (signals & (1 << nb.nb_Port->mp_SigBit))
735 while ((msg = (CxMsg *)GetMsg(cs->cs_msgPort)))
737 switch (CxMsgType(msg))
739 case CXM_COMMAND:
740 switch (CxMsgID(msg))
742 case CXCMD_DISABLE:
743 ActivateCxObj(cs->cs_broker, FALSE);
744 break;
746 case CXCMD_ENABLE:
747 ActivateCxObj(cs->cs_broker, TRUE);
748 break;
750 case CXCMD_UNIQUE:
751 /* Running the program twice is the same as shutting
752 down the existing program... */
753 /* Fall through */
755 case CXCMD_KILL:
756 quit = TRUE;
757 break;
759 } /* switch (CxMsgID(msg)) */
760 break;
761 } /* switch (CxMsgType(msg))*/
763 ReplyMsg((struct Message *)msg);
765 } /* while ((msg = (CxMsg *)GetMsg(cs->cs_msgPort))) */
768 if (signals & SIGBREAKF_CTRL_C)
770 quit = TRUE;
773 } /* while(!quit) */
776 /************************************************************************************/
778 int main(int argc, char **argv)
780 DMState cState;
781 int error = RETURN_OK;
783 if (initiate(argc, argv, &cState))
785 handleCx(&cState);
787 else
789 error = RETURN_FAIL;
792 freeResources(&cState);
794 return error;
797 /************************************************************************************/
799 ADD2INIT(Locale_Initialize, 90);
800 ADD2EXIT(Locale_Deinitialize, 90);