Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / libs / asl / cycleclass.c
blobd1f0b4ad54a22e1f61918fe6c0bc0d03e974e147
1 /*
2 Copyright © 1995-2005, The AROS Development Team. All rights reserved.
3 $Id$
5 Desc:
6 Lang: english
7 */
10 #include <proto/exec.h>
11 #include <proto/dos.h>
12 #include <proto/utility.h>
13 #include <proto/intuition.h>
14 #include <proto/graphics.h>
15 #include <proto/cybergraphics.h>
16 #include <exec/memory.h>
17 #include <intuition/screens.h>
18 #include <intuition/icclass.h>
19 #include <intuition/cghooks.h>
20 #include <intuition/imageclass.h>
21 #include <intuition/gadgetclass.h>
22 #include <graphics/gfx.h>
23 #include <cybergraphx/cybergraphics.h>
25 #include <string.h>
27 #include "asl_intern.h"
28 #include "layout.h"
30 #define SDEBUG 0
31 #define DEBUG 0
33 #include <aros/debug.h>
35 #define CLASS_ASLBASE ((struct AslBase_intern *)cl->cl_UserData)
36 #define HOOK_ASLBASE ((struct AslBase_intern *)hook->h_Data)
38 #define AslBase CLASS_ASLBASE
40 /********************** ASL CYCLE CLASS **************************************************/
42 #define CYCLEIMAGEWIDTH 19
43 #define ARROWWIDTH 7
44 #define ARROWHEIGHT 4
46 #define ARROW_DOWN 1
47 #define ARROW_UP 2
49 /* it is possible to scroll faster through the
50 ** popup after TURBOCOUNTDOWN intuiticks
51 ** (= 1/10 sec) by moving around the mouse
52 ** (to force mousemove events to be sent to
53 ** the gadget)
56 #define TURBOCOUNTDOWN 5
58 /***********************************************************************************/
60 IPTR AslCycle__OM_SET(Class * cl, Object * o, struct opSet *msg);
62 /***********************************************************************************/
64 static void RenderObject_Update(Class *cl, Object *o, struct GadgetInfo *gi)
66 struct RastPort *rp;
68 if ((rp = ObtainGIRPort(gi)))
70 DoMethod(o, GM_RENDER,
71 (IPTR) gi,
72 (IPTR) rp,
73 GREDRAW_UPDATE);
75 ReleaseGIRPort(rp);
79 /***********************************************************************************/
81 static void DrawArrow(Class *cl, struct RastPort *rp, WORD x1, WORD y1, WORD type)
83 WORD dy = 1;
85 if (type == ARROW_UP)
87 y1 += 3; dy = -1;
90 RectFill(rp, x1, y1, x1 + 6, y1); y1 += dy;
91 RectFill(rp, x1 + 1, y1, x1 + 5, y1); y1 += dy;
92 RectFill(rp, x1 + 2, y1, x1 + 4, y1); y1 += dy;
93 WritePixel(rp, x1 + 3, y1);
96 /***********************************************************************************/
98 static void DrawUpArrow(Class *cl, struct AslCycleData *data, struct DrawInfo *dri, BOOL force)
100 WORD x;
101 BOOL black;
103 if (data->visible == data->numitems) return;
105 black = data->top > 0 ? TRUE : FALSE;
106 if (force || (black != data->uparrowblack))
108 data->uparrowblack = black;
110 SetAPen(data->rp, dri->dri_Pens[black ? SHADOWPEN : BACKGROUNDPEN]);
112 x = data->borderleft + (data->itemwidth - ARROWWIDTH) / 2;
114 DrawArrow(cl, data->rp, data->menuleft + x, data->menutop + data->menuy1 - ARROWHEIGHT - 1, ARROW_UP);
118 /***********************************************************************************/
120 static void DrawDownArrow(Class *cl, struct AslCycleData *data, struct DrawInfo *dri, BOOL force)
122 WORD x;
123 BOOL black;
125 if (data->visible == data->numitems) return;
127 black = data->top + data->visible < data->numitems ? TRUE : FALSE;
128 if (force || (black != data->downarrowblack))
130 data->downarrowblack = black;
132 SetAPen(data->rp, dri->dri_Pens[black ? SHADOWPEN : BACKGROUNDPEN]);
134 x = data->borderleft + (data->itemwidth - ARROWWIDTH) / 2;
136 DrawArrow(cl, data->rp, data->menuleft + x, data->menutop + data->menuy2 + 2, ARROW_DOWN);
140 /***********************************************************************************/
142 static void DrawItem(Class *cl, struct AslCycleData *data, struct DrawInfo *dri, WORD num, BOOL nowhitefill)
144 struct CycleItem *item;
145 WORD off,x,y;
147 off = num - data->top;
149 if ((off >= 0) && (off < data->visible))
151 item = &data->itemmemory[num];
153 x = data->menuleft + data->menux1;
154 y = data->menutop + data->menuy1 + off * data->itemheight;
156 if ((num == data->selected) || !nowhitefill)
158 SetAPen(data->rp, dri->dri_Pens[(num == data->selected) ? SHADOWPEN : SHINEPEN]);
159 RectFill(data->rp, x, y,
160 x + data->itemwidth - 1,
161 y + data->itemheight - 1);
164 x = (x + x + data->itemwidth - item->pixellen) / 2;
165 y += data->font->tf_Baseline + 1;
167 SetAPen(data->rp, dri->dri_Pens[(num == data->selected) ? SHINEPEN : TEXTPEN]);
168 Move(data->rp, x, y);
169 Text(data->rp, item->string, item->charlen);
171 } /* if ((off >= 0) && (off < data->visible)) */
174 /***********************************************************************************/
176 static void DrawMenu(Class *cl, struct AslCycleData *data, struct DrawInfo *dri)
178 WORD i, x1, y1, x2, y2;
180 x1 = data->menuleft;
181 y1 = data->menutop;
183 x2 = x1 + data->menuwidth - 1;
184 y2 = y1 + data->menuheight - 1;
186 SetAPen(data->rp, dri->dri_Pens[SHADOWPEN]);
187 RectFill(data->rp, x1, y1, x2, y1);
188 RectFill(data->rp, x2, y1 + 1, x2, y2);
189 RectFill(data->rp, x1, y2, x2 - 1, y2);
190 RectFill(data->rp, x1, y1 + 1, x1, y2 - 1);
192 SetAPen (data->rp, dri->dri_Pens[SHINEPEN]);
193 RectFill(data->rp, x1 + 1, y1 + 1, x2 - 1, y2 - 1);
195 for(i = data->top;i < data->top + data->visible;i++)
197 DrawItem(cl, data, dri, i,TRUE);
200 DrawUpArrow(cl, data, dri, TRUE);
201 DrawDownArrow(cl, data, dri,TRUE);
204 /***********************************************************************************/
206 static void UnselectActiveItem(Class *cl, struct AslCycleData *data, struct DrawInfo *dri)
208 WORD old;
210 old = data->selected;
211 data->selected = -1;
212 DrawItem(cl, data, dri, old, FALSE);
215 /***********************************************************************************/
217 IPTR AslCycle__OM_NEW(Class * cl, Object * o, struct opSet *msg)
219 struct AslCycleData *data;
220 struct TagItem fitags[] =
222 {IA_FrameType, FRAME_BUTTON},
223 {IA_EdgesOnly, FALSE },
224 {TAG_DONE , 0UL }
227 struct Gadget *g = (struct Gadget *)DoSuperMethodA(cl, o, (Msg)msg);
228 if (g)
230 data = INST_DATA(cl, g);
232 /* We want to get a GM_LAYOUT message, no matter if gadget is GFLG_RELRIGHT/RELBOTTOM/
233 RELWIDTH/RELHEIGHT or not */
234 g->Flags |= GFLG_RELSPECIAL;
236 data->frame = NewObjectA(NULL, FRAMEICLASS, fitags);
237 if (data->frame)
239 AslCycle__OM_SET(cl, (Object *)g, msg);
240 } else {
242 CoerceMethod(cl, (Object *)g, OM_DISPOSE);
247 return (IPTR)g;
250 /***********************************************************************************/
252 IPTR AslCycle__OM_DISPOSE(Class * cl, Object * o, Msg msg)
254 struct AslCycleData *data = INST_DATA(cl, o);
256 if (data->itemmemory) FreeVec(data->itemmemory);
257 if (data->frame) DisposeObject(data->frame);
259 return DoSuperMethodA(cl, o, msg);
262 /***********************************************************************************/
264 IPTR AslCycle__OM_SET(Class * cl, Object * o, struct opSet *msg)
266 struct AslCycleData *data = INST_DATA(cl, o);
267 struct TagItem *tag;
268 const struct TagItem *tstate = msg->ops_AttrList;
269 IPTR retval, tidata;
271 retval = DoSuperMethod(cl, o, OM_SET, (IPTR) msg->ops_AttrList, (IPTR) msg->ops_GInfo);
273 while((tag = NextTagItem(&tstate)))
275 tidata = tag->ti_Data;
277 switch(tag->ti_Tag)
279 case ASLCY_Active:
280 data->active = tidata;
281 retval += 1;
282 break;
284 case ASLCY_Labels:
285 data->labels = (char **)tidata;
287 if (data->itemmemory)
289 FreeVec(data->itemmemory);
290 data->itemmemory = 0;
293 data->active = 0;
294 data->numitems = 0;
296 if (data->labels)
298 WORD i;
300 for(i = 0; data->labels[i]; i++)
304 if (i) data->itemmemory = AllocVec(i * sizeof(struct CycleItem), MEMF_PUBLIC | MEMF_CLEAR);
306 if (!data->itemmemory)
308 data->labels = 0;
309 } else {
310 struct CycleItem *item = data->itemmemory;
312 data->numitems = i;
314 for(i = 0; i < data->numitems;i++,item++)
316 item->string = data->labels[i];
317 item->charlen = strlen(item->string);
320 if (msg->ops_GInfo)
322 struct gpLayout gpl;
324 gpl.MethodID = GM_LAYOUT;
325 gpl.gpl_GInfo = msg->ops_GInfo;
326 gpl.gpl_Initial = 0;
328 DoMethodA(o, (Msg)&gpl);
331 } /* if (data->itemmemory) */
333 } /* if (data->labels) */
335 retval += 1;
336 break;
338 case ASLCY_Font:
339 data->font = (struct TextFont *)tidata;
340 retval += 1;
341 break;
343 } /* switch(tag->ti_Tag) */
345 } /* while((tag = NextTagItem(&tstate))) */
347 if (retval)
349 RenderObject_Update(cl, o, msg->ops_GInfo);
352 return retval;
356 /***********************************************************************************/
358 IPTR AslCycle__OM_GET(Class * cl, Object * o, struct opGet *msg)
360 struct AslCycleData *data = INST_DATA(cl, o);
362 IPTR retval = 1;
364 switch(msg->opg_AttrID)
366 case ASLCY_Active:
367 *msg->opg_Storage = data->active;
368 break;
370 default:
371 retval = DoSuperMethodA(cl, o, (Msg)msg);
372 break;
375 return retval;
378 /***********************************************************************************/
380 IPTR AslCycle__GM_LAYOUT(Class * cl, struct Gadget * g, struct gpLayout *msg)
382 struct AslCycleData *data = INST_DATA(cl, g);
383 struct CycleItem *item;
384 WORD len, i;
385 WORD x, y, w, h;
387 getgadgetcoords(g, msg->gpl_GInfo, &x, &y, &w, &h);
389 if (!data->font) data->font = msg->gpl_GInfo->gi_DrInfo->dri_Font;
391 item = data->itemmemory;
392 len = 0;
394 InitRastPort(&data->clonerp);
395 SetFont(&data->clonerp, data->font);
397 for (i = 0; i < data->numitems; i++, item++)
399 item->pixellen = TextLength(&data->clonerp, item->string, item->charlen);
400 if (item->pixellen > len) len = item->pixellen;
403 data->itemwidth = len + 2;
404 i = w - CYCLEIMAGEWIDTH - BORDERCYCLESPACINGX;
405 if (data->itemwidth < i) data->itemwidth = i;
407 data->itemheight = data->font->tf_YSize + 2;
409 data->borderleft = 4;
410 data->borderright = 4;
411 data->bordertop = 4;
412 data->borderbottom = 4;
414 data->menuwidth = data->itemwidth + data->borderleft + data->borderright;
416 data->menuheight = data->numitems * data->itemheight +
417 data->bordertop +
418 data->borderbottom;
420 data->menux1 = data->borderleft;
422 data->maypopup = TRUE;
424 if (data->menuheight > msg->gpl_GInfo->gi_Screen->Height)
426 data->visible = (msg->gpl_GInfo->gi_Screen->Height -
427 data->bordertop -
428 data->borderbottom -
429 (ARROWHEIGHT + 2) * 2) / data->itemheight;
431 if (data->visible < 2)
433 data->maypopup = FALSE;
434 } else {
435 data->menuheight = data->bordertop +
436 data->visible * data->itemheight +
437 data->borderbottom +
438 (ARROWHEIGHT + 2) * 2;
440 data->menuy1 = data->bordertop + ARROWHEIGHT + 2;
442 } else {
443 data->visible = data->numitems;
444 data->menuy1 = data->bordertop;
447 data->menux2 = data->menux1 + data->itemwidth - 1;
448 data->menuy2 = data->menuy1 + data->visible * data->itemheight - 1;
450 return 0;
453 /***********************************************************************************/
455 IPTR AslCycle__GM_RENDER(Class * cl, struct Gadget * g, struct gpRender *msg)
457 struct AslCycleData *data = INST_DATA(cl, g);
458 struct RastPort *rp = msg->gpr_RPort;
459 struct DrawInfo *dri = msg->gpr_GInfo->gi_DrInfo;
460 struct CycleItem *item;
461 WORD gadx, gady, gadw, gadh, x, y, y2, a1, a2;
463 BOOL selected;
465 if (rp)
467 struct TagItem im_tags[] =
469 {IA_Width , 0 },
470 {IA_Height , 0 },
471 {TAG_DONE }
474 getgadgetcoords(g, msg->gpr_GInfo, &gadx, &gady, &gadw, &gadh);
476 im_tags[0].ti_Data = gadw;
477 im_tags[1].ti_Data = gadh;
479 SetAttrsA(data->frame, im_tags);
481 DrawImageState(msg->gpr_RPort,
482 (struct Image *)data->frame,
483 gadx,
484 gady,
485 (g->Flags & GFLG_SELECTED) ? IDS_SELECTED : IDS_NORMAL,
486 msg->gpr_GInfo->gi_DrInfo);
489 if (data->labels)
491 selected = (g->Flags & GFLG_SELECTED) ? TRUE: FALSE;
492 SetABPenDrMd(rp, dri->dri_Pens[selected ? FILLTEXTPEN : TEXTPEN], 0, JAM1);
494 item = &data->itemmemory[data->active];
496 a1 = gadx + BORDERCYCLESPACINGX;
497 a2 = gadx + gadw - CYCLEIMAGEWIDTH - BORDERCYCLESPACINGX;
498 x = (a1 + a2 - item->pixellen) / 2;
500 y = gady + (gadh - data->font->tf_YSize) / 2 + data->font->tf_Baseline;
502 SetFont(rp, data->font);
504 Move(rp, x, y);
505 Text(rp, item->string, item->charlen);
508 x = gadx + gadw - CYCLEIMAGEWIDTH;
509 y = gady + 2;
510 y2 = gady + gadh - 1 - 2;
512 /* separator bar */
514 SetAPen(rp, dri->dri_Pens[SHINEPEN]);
515 RectFill(rp, x + 1, y , x + 1, y2);
516 SetAPen(rp, dri->dri_Pens[SHADOWPEN]);
517 RectFill(rp, x, y, x, y2);
519 y = gady + (gadh - 4) / 2;
520 x += 6;
522 DrawArrow(cl, rp, x, y, ARROW_DOWN);
524 } /* if (rp) */
526 return 0;
529 /***********************************************************************************/
531 IPTR AslCycle__GM_GOACTIVE(Class * cl, struct Gadget * g, struct gpInput *msg)
533 struct AslCycleData *data = INST_DATA(cl, g);
534 WORD x, y, x2, y2, gadx, gady, gadw, gadh;
536 IPTR rc = GMR_MEACTIVE;
538 if (!data->labels || !msg->gpi_IEvent) return GMR_NOREUSE;
540 data->popup = FALSE;
541 data->sentgadgetup = FALSE;
542 data->turbocountdown = TURBOCOUNTDOWN;
544 getgadgetcoords(g, msg->gpi_GInfo, &gadx, &gady, &gadw, &gadh);
546 x = msg->gpi_GInfo->gi_Window->MouseX;
548 if (data->maypopup && (data->numitems > 2) && (x < gadx + gadw - CYCLEIMAGEWIDTH))
550 x = msg->gpi_GInfo->gi_Window->LeftEdge + gadx;
551 y = msg->gpi_GInfo->gi_Window->TopEdge + gady + gadh;
553 if (x < 0) x = 0;
554 if ((x + data->menuwidth) > msg->gpi_GInfo->gi_Screen->Width)
556 x = msg->gpi_GInfo->gi_Screen->Width - data->menuwidth;
559 if (y < 0) y = 0;
560 if ((y + data->menuheight) > msg->gpi_GInfo->gi_Screen->Height)
562 y = msg->gpi_GInfo->gi_Screen->Height - data->menuheight;
565 x2 = x + data->menuwidth - 1;
566 y2 = y + data->menuheight - 1;
568 if ((data->popupwindow = OpenWindowTags(0,WA_CustomScreen, (IPTR) msg->gpi_GInfo->gi_Screen,
569 WA_Left, x,
570 WA_Top, y,
571 WA_Width, data->menuwidth,
572 WA_Height, data->menuheight,
573 WA_Flags, WFLG_BORDERLESS,
574 WA_BackFill, (IPTR) LAYERS_NOBACKFILL,
575 TAG_DONE)))
577 data->menuleft = 0;
578 data->menutop = 0;
579 data->rp = data->popupwindow->RPort;
583 if (data->popupwindow)
585 data->layerx1 = x; data->layery1 = y;
586 data->layerx2 = x2; data->layery2 = y2;
588 data->top = 0;
589 data->selected = -1;
590 SetDrMd(data->rp, JAM1);
591 SetFont(data->rp, data->font);
593 data->popup = TRUE;
594 DrawMenu(cl, data, msg->gpi_GInfo->gi_DrInfo);
597 } /* if (data->maypopup && (data->numitems > 2) && (x < gadx + gadw - CYCLEIMAGEWIDTH)) */
598 else
600 g->Flags |= GFLG_SELECTED;
601 RenderObject_Update(cl, (Object *)g, msg->gpi_GInfo);
604 return rc;
607 /***********************************************************************************/
609 IPTR AslCycle__GM_HANDLEINPUT(Class * cl, struct Gadget * g, struct gpInput *msg)
611 struct AslCycleData *data = INST_DATA(cl, g);
612 WORD gadx, gady, gadw, gadh, x, y, i;
613 IPTR rc = GMR_MEACTIVE;
615 getgadgetcoords(g, msg->gpi_GInfo, &gadx, &gady, &gadw, &gadh);
617 if (data->popup)
619 if ((msg->gpi_IEvent->ie_Class == IECLASS_RAWMOUSE) &&
620 (msg->gpi_IEvent->ie_Code == (IECODE_LBUTTON | IECODE_UP_PREFIX)))
622 rc = GMR_NOREUSE;
623 if (data->selected != -1)
625 data->active = data->selected;
626 data->sentgadgetup = TRUE;
628 rc |= GMR_VERIFY;
629 *msg->gpi_Termination = data->active;
631 goto done;
635 x = msg->gpi_GInfo->gi_Screen->MouseX - data->layerx1;
636 y = msg->gpi_GInfo->gi_Screen->MouseY - data->layery1;
638 if ((x < data->menux1) || (x > data->menux2))
640 UnselectActiveItem(cl, data, msg->gpi_GInfo->gi_DrInfo);
641 } else {
642 i = (y - data->menuy1) / data->itemheight;
644 if ((i >= data->visible) || (y < data->menuy1))
646 UnselectActiveItem(cl, data, msg->gpi_GInfo->gi_DrInfo);
648 if (data->turbocountdown && (msg->gpi_IEvent->ie_Class == IECLASS_TIMER))
650 data->turbocountdown--;
653 if (!data->turbocountdown || (msg->gpi_IEvent->ie_Class == IECLASS_TIMER))
655 if (i >= data->visible)
657 // scroll down
658 if (data->top + data->visible < data->numitems)
660 ClipBlit(data->rp, data->menux1 + data->menuleft, data->menuy1 + data->menutop + data->itemheight,
661 data->rp, data->menux1 + data->menuleft, data->menuy1 + data->menutop, data->itemwidth, data->itemheight * (data->visible - 1), 192);
662 data->top++;
663 DrawItem(cl, data, msg->gpi_GInfo->gi_DrInfo, data->top + data->visible - 1, FALSE);
664 DrawUpArrow(cl, data, msg->gpi_GInfo->gi_DrInfo, FALSE);
665 DrawDownArrow(cl, data, msg->gpi_GInfo->gi_DrInfo, FALSE);
667 } else {
668 // scroll up
669 if (data->top > 0)
671 ClipBlit(data->rp, data->menux1 + data->menuleft, data->menuy1 + data->menutop,
672 data->rp, data->menux1 + data->menuleft, data->menuy1 + data->menutop + data->itemheight, data->itemwidth, data->itemheight * (data->visible - 1), 192);
673 data->top--;
674 DrawItem(cl, data, msg->gpi_GInfo->gi_DrInfo, data->top, FALSE);
675 DrawUpArrow(cl, data, msg->gpi_GInfo->gi_DrInfo, FALSE);
676 DrawDownArrow(cl, data, msg->gpi_GInfo->gi_DrInfo, FALSE);
679 } /* if (!data->turbocountdown || (msg->gpi_IEvent->ie_Class == IECLASS_TIMER)) */
681 } else {
683 data->turbocountdown = TURBOCOUNTDOWN;
684 x = data->selected;
685 data->selected = data->top + i;
686 if (data->selected != x)
688 DrawItem(cl, data, msg->gpi_GInfo->gi_DrInfo, x, FALSE);
689 DrawItem(cl, data, msg->gpi_GInfo->gi_DrInfo, data->selected, FALSE);
694 } else { /* if (data->popup) */
696 /* like a GadTools cycle gadget */
698 switch(msg->gpi_IEvent->ie_Class)
700 case IECLASS_RAWMOUSE:
701 switch(msg->gpi_IEvent->ie_Code)
703 case IECODE_LBUTTON | IECODE_UP_PREFIX:
704 if (g->Flags & GFLG_SELECTED)
706 data->active++;
707 if (data->active >= data->numitems) data->active = 0;
709 g->Flags &= (~GFLG_SELECTED);
710 RenderObject_Update(cl, (Object *)g, msg->gpi_GInfo);
711 rc |= GMR_VERIFY;
712 *msg->gpi_Termination = g->GadgetID;
714 rc |= GMR_NOREUSE;
715 break;
717 default:
718 if ((msg->gpi_Mouse.X >= 0 ) &&
719 (msg->gpi_Mouse.Y >= 0 ) &&
720 (msg->gpi_Mouse.X < gadw) &&
721 (msg->gpi_Mouse.Y < gadh))
723 if (!(g->Flags & GFLG_SELECTED))
725 g->Flags |= GFLG_SELECTED;
726 RenderObject_Update(cl, (Object *)g, msg->gpi_GInfo);
728 } else {
729 if (g->Flags & GFLG_SELECTED)
731 g->Flags &= (~GFLG_SELECTED);
732 RenderObject_Update(cl, (Object *)g, msg->gpi_GInfo);
735 rc = GMR_MEACTIVE;
737 break;
739 } /* switch(msg->gpi_IEvent->ie_Code) */
741 } /* switch(msg->gpi_IEvent->ie_Class) */
743 } /* if (data->popup) {...} else { */
745 done:
747 return rc;
751 /***********************************************************************************/
753 IPTR AslCycle__GM_GOINACTIVE(Class * cl, Object * o, struct gpGoInactive *msg)
755 struct AslCycleData *data = INST_DATA(cl, o);
757 if (data->popupwindow)
759 CloseWindow(data->popupwindow);
760 data->popupwindow = 0;
763 if (data->sentgadgetup)
765 RenderObject_Update(cl, o, msg->gpgi_GInfo);
768 return 0;
771 /***********************************************************************************/