Fixed binary search: no more infinite loops when vendor is unknown.
[tangerine.git] / workbench / libs / muimaster / classes / balance.c
blobba579390ba4ce9311da63bdeeee91bb03505a75b
1 /*
2 Copyright © 2002-2006, The AROS Development Team. All rights reserved.
3 $Id$
4 */
6 #include <string.h>
7 #include <stdlib.h>
9 #include <exec/types.h>
11 #include <clib/alib_protos.h>
12 #include <proto/intuition.h>
13 #include <proto/graphics.h>
14 #include <proto/utility.h>
15 #include <proto/exec.h>
16 #include <proto/muimaster.h>
18 #include "mui.h"
19 #include "muimaster_intern.h"
20 #include "support.h"
21 #include "prefs.h"
22 #include "balance_private.h"
24 /* #define MYDEBUG 0 */
25 #include "debug.h"
27 extern struct Library *MUIMasterBase;
30 * [FirstBound .... <- balance -> .... SecondBound]
34 IPTR Balance__OM_NEW(struct IClass *cl, Object *obj, struct opSet *msg)
36 struct Balance_DATA *data;
37 const struct TagItem *tags;
38 struct TagItem *tag;
40 obj = (Object *)DoSuperMethodA(cl, obj, (Msg)msg);
42 if (!obj)
43 return 0;
45 /* Initial local instance data */
46 data = INST_DATA(cl, obj);
48 /* parse initial taglist */
50 for (tags = msg->ops_AttrList; (tag = NextTagItem(&tags)); )
52 switch (tag->ti_Tag)
54 case MUIA_Balance_Quiet:
55 break;
59 data->ehn.ehn_Events = IDCMP_MOUSEBUTTONS;
60 data->ehn.ehn_Priority = 0;
61 data->ehn.ehn_Flags = 0;
62 data->ehn.ehn_Object = obj;
63 data->ehn.ehn_Class = cl;
65 D(bug("Balance_New(0x%lx)\n",obj));
67 return (IPTR)obj;
70 IPTR Balance__MUIM_Setup(struct IClass *cl, Object *obj, struct MUIP_Setup *msg)
72 struct Balance_DATA *data = INST_DATA(cl, obj);
74 if (!(DoSuperMethodA(cl, obj, (Msg) msg)))
75 return FALSE;
77 if (_parent(obj))
78 get(_parent(obj), MUIA_Group_Horiz, &data->horizgroup);
80 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR)&data->ehn);
82 return TRUE;
85 IPTR Balance__MUIM_Cleanup(struct IClass *cl, Object *obj, struct MUIP_Cleanup *msg)
87 struct Balance_DATA *data = INST_DATA(cl, obj);
89 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR)&data->ehn);
91 return DoSuperMethodA(cl, obj, (Msg) msg);
94 /**************************************************************************
95 MUIM_AskMinMax
96 **************************************************************************/
97 IPTR Balance__MUIM_AskMinMax(struct IClass *cl, Object *obj, struct MUIP_AskMinMax *msg)
99 struct Balance_DATA *data = INST_DATA(cl, obj);
101 DoSuperMethodA(cl, obj, (Msg)msg);
103 if (data->horizgroup)
105 msg->MinMaxInfo->MinWidth += 3;
106 msg->MinMaxInfo->DefWidth = msg->MinMaxInfo->MinWidth;
107 msg->MinMaxInfo->MaxWidth = msg->MinMaxInfo->DefWidth;
108 msg->MinMaxInfo->MaxHeight = MUI_MAXMAX;
110 else
112 msg->MinMaxInfo->MinHeight += 3;
113 msg->MinMaxInfo->DefHeight = msg->MinMaxInfo->MinHeight;
114 msg->MinMaxInfo->MaxHeight = msg->MinMaxInfo->DefHeight;
115 msg->MinMaxInfo->MaxWidth = MUI_MAXMAX;
118 return TRUE;
122 IPTR Balance__MUIM_Draw(struct IClass *cl, Object *obj, struct MUIP_Draw *msg)
124 struct Balance_DATA *data = INST_DATA(cl, obj);
125 struct MUI_RenderInfo *mri;
126 LONG col1, col2;
128 DoSuperMethodA(cl, obj, (Msg)msg);
130 if (!(msg->flags & MADF_DRAWOBJECT))
131 return 0;
133 if (_mwidth(obj) < 1 || _mheight(obj) < 1)
134 return TRUE;
136 mri = muiRenderInfo(obj);
138 if (data->state == NOT_CLICKED)
140 col1 = MPEN_TEXT;
141 col2 = MPEN_SHINE;
143 else
145 col2 = MPEN_TEXT;
146 col1 = MPEN_SHINE;
149 if (data->horizgroup)
151 SetAPen(_rp(obj), _pens(obj)[col1]);
152 Move(_rp(obj), _mleft(obj), _mtop(obj));
153 Draw(_rp(obj), _mleft(obj), _mbottom(obj));
154 SetAPen(_rp(obj), _pens(obj)[MPEN_FILL]);
155 Move(_rp(obj), _mleft(obj) + 1, _mtop(obj));
156 Draw(_rp(obj), _mleft(obj) + 1, _mbottom(obj));
157 SetAPen(_rp(obj), _pens(obj)[col2]);
158 Move(_rp(obj), _mleft(obj) + 2, _mtop(obj));
159 Draw(_rp(obj), _mleft(obj) + 2, _mbottom(obj));
161 else
163 SetAPen(_rp(obj), _pens(obj)[col1]);
164 Move(_rp(obj), _mleft(obj), _mtop(obj));
165 Draw(_rp(obj), _mright(obj), _mtop(obj));
166 SetAPen(_rp(obj), _pens(obj)[MPEN_FILL]);
167 Move(_rp(obj), _mleft(obj), _mtop(obj) + 1);
168 Draw(_rp(obj), _mright(obj), _mtop(obj) + 1);
169 SetAPen(_rp(obj), _pens(obj)[col2]);
170 Move(_rp(obj), _mleft(obj), _mtop(obj) + 2);
171 Draw(_rp(obj), _mright(obj), _mtop(obj) + 2);
174 return TRUE;
177 static void draw_object_frame (Object *obj, Object *o, BOOL fixed)
179 SetAPen(_rp(obj), _pens(obj)[MPEN_TEXT]);
180 Move(_rp(obj), _left(o), _top(o));
181 Draw(_rp(obj), _left(o), _bottom(o));
182 Draw(_rp(obj), _right(o), _bottom(o));
183 Draw(_rp(obj), _right(o), _top(o));
184 Draw(_rp(obj), _left(o), _top(o));
185 if (!fixed)
187 Draw(_rp(obj), _right(o), _bottom(o));
188 Move(_rp(obj), _right(o), _top(o));
189 Draw(_rp(obj), _left(o), _bottom(o));
193 static LONG get_first_bound (struct Balance_DATA *data, Object *obj)
195 ULONG spacing;
197 if (data->horizgroup)
199 get(_parent(obj), MUIA_Group_HorizSpacing, &spacing);
200 return _left(obj) + _minwidth(obj) + _subwidth(obj) + spacing;
202 else
204 get(_parent(obj), MUIA_Group_VertSpacing, &spacing);
205 return _top(obj) + _minheight(obj) + _subheight(obj) + spacing;
209 static LONG get_second_bound (struct Balance_DATA *data, Object *obj)
211 ULONG spacing;
213 if (data->horizgroup)
215 get(_parent(obj), MUIA_Group_HorizSpacing, &spacing);
216 return _right(obj) - _minwidth(obj) - _subwidth(obj) - spacing;
218 else
220 get(_parent(obj), MUIA_Group_VertSpacing, &spacing);
221 return _bottom(obj) - _minheight(obj) - _subheight(obj) - spacing;
226 static LONG get_first_bound_multi (struct Balance_DATA *data, Object *obj)
228 if (data->horizgroup)
230 return _mleft(_parent(obj));
232 else
234 return _mtop(_parent(obj));
238 static LONG get_second_bound_multi (struct Balance_DATA *data, Object *obj)
240 if (data->horizgroup)
242 return _mright(_parent(obj));
244 else
246 return _mbottom(_parent(obj));
250 static BOOL is_fixed_size (struct Balance_DATA *data, Object *obj)
252 if (data->horizgroup)
254 return (_minwidth(obj) == _maxwidth(obj));
256 else
258 return (_minheight(obj) == _maxheight(obj));
263 static ULONG get_total_weight_2(struct Balance_DATA *data, Object *objA, Object *objB)
265 if (data->horizgroup)
267 return _hweight(objA) + _hweight(objB);
269 else
271 return _vweight(objA) + _vweight(objB);
276 static void set_weight_2 (struct Balance_DATA *data, WORD current)
278 LONG weightB;
279 LONG weightA;
281 if (current >= data->second_bound)
283 weightB = 0;
284 weightA = data->total_weight;
285 D(bug("weightB = 0\n"));
287 else if (current <= data->first_bound)
289 weightA = 0;
290 weightB = data->total_weight;
291 D(bug("weightA = 0\n"));
293 else
295 D(bug("L=%ld, mid=%ld, R=%ld || M=%d\n",
296 data->first_bound, data->clickpos, data->second_bound,
297 current));
298 weightA = (current - data->first_bound + 1) * data->total_weight
299 / (data->second_bound - data->first_bound + 1);
301 D(bug("found wA = %ld\n", weightA));
302 if (weightA > data->total_weight)
304 D(bug("*** weightA > data->total_weight\n"));
305 weightA = data->total_weight;
307 weightB = data->total_weight - weightA;
310 if (data->horizgroup)
312 _hweight(data->obj_before) = weightA;
313 _hweight(data->obj_after) = weightB;
315 else
317 _vweight(data->obj_before) = weightA;
318 _vweight(data->obj_after) = weightB;
323 static void recalc_weights_neighbours (struct IClass *cl, Object *obj, WORD mouse)
325 struct Balance_DATA *data = INST_DATA(cl, obj);
327 if ((data->total_weight == -1) || (data->first_bound == -1) || (data->second_bound == -1))
329 Object *sibling;
330 Object *object_state;
332 object_state = (Object *)data->objs->lh_Head;
333 while ((sibling = NextObject(&object_state)))
335 if (!(_flags(sibling) & MADF_SHOWME))
336 continue;
337 /* D(bug("sibling %lx\n", sibling)); */
338 if (sibling == obj)
340 while ((sibling = NextObject(&object_state)))
342 if (!(_flags(sibling) & MADF_SHOWME))
343 continue;
344 data->obj_after = sibling;
345 break;
347 break;
349 data->obj_before = sibling;
351 if (!(data->obj_before && data->obj_after))
353 D(bug("Balance(%0xlx): missing siblings; before=%lx, after=%lx\n",
354 obj, data->obj_before, data->obj_after));
355 return;
357 if (data->total_weight == -1)
358 data->total_weight = get_total_weight_2(data, data->obj_before, data->obj_after);
359 if (data->first_bound == -1)
360 data->first_bound = get_first_bound(data, data->obj_before);
361 if (data->second_bound == -1)
362 data->second_bound = get_second_bound(data, data->obj_after);
365 set_weight_2(data, mouse);
368 static LONG get_weight (struct Balance_DATA *data, Object *obj)
370 if (data->horizgroup)
372 return _hweight(obj);
374 else
376 return _vweight(obj);
380 static void set_weight (struct Balance_DATA *data, Object *obj, LONG w)
382 if (data->horizgroup)
384 _hweight(obj) = w;
386 else
388 _vweight(obj) = w;
392 static LONG get_size (struct Balance_DATA *data, Object *obj)
394 if (data->horizgroup)
396 return _width(obj);
398 else
400 return _height(obj);
404 #if 0
405 static void set_interpolated_weight (struct Balance_DATA *data, Object *obj,
406 LONG oldw, LONG neww)
408 if (data->horizgroup)
410 if (oldw)
411 _hweight(obj) = _hweight(obj) * neww / oldw;
412 else
413 _hweight(obj) = 0;
415 else
417 if (oldw)
418 _vweight(obj) = _vweight(obj) * neww / oldw;
419 else
420 _vweight(obj) = 0;
424 #endif
426 static void set_weight_all (struct Balance_DATA *data, Object *obj, WORD current)
428 LONG weightB;
429 LONG weightA;
430 LONG ldelta, rdelta, lwbygad, rwbygad, lbonus, rbonus, lneg, rneg, lzero, rzero, count;
431 int lfirst, rfirst;
434 if (data->lsize && data->rsize)
436 weightA = data->lsum
437 + ((current - data->clickpos)
438 * ((data->lsum / (double)data->lsize) + (data->rsum / (double)data->rsize))
439 / 2.0);
441 else
442 weightA = data->lsum;
444 if (weightA > data->total_weight)
446 D(bug("*** weightA > data->total_weight\n"));
447 weightA = data->total_weight;
449 if (weightA < 0)
451 D(bug("*** weightA < 0n"));
452 weightA = 0;
454 weightB = data->total_weight - weightA;
455 D(bug("normal : weights = %ld/%ld\n", weightA, weightB));
458 ldelta = weightA - data->oldWeightA;
459 rdelta = weightB - data->oldWeightB;
460 lwbygad = ldelta / data->lsiblings;
461 rwbygad = rdelta / data->rsiblings;
462 lbonus = ldelta % data->lsiblings;
463 if (lbonus < 0)
464 lbonus = -lbonus;
465 rbonus = rdelta % data->rsiblings;
466 if (rbonus < 0)
467 rbonus = -rbonus;
468 lfirst = (int) ((double)data->lsiblings*rand()/(RAND_MAX+1.0));
469 rfirst = (int) ((double)data->rsiblings*rand()/(RAND_MAX+1.0));
471 count = 0;
474 Object *sibling;
475 Object *object_state;
476 WORD left = data->lsiblings;
478 D(bug("delta=%ld/%ld; wbygad=%ld/%ld; bonus=%ld/%ld; first=%d/%d\n", ldelta, rdelta,
479 lwbygad, rwbygad, lbonus, rbonus, lfirst, rfirst));
481 if (count++ == 4)
483 D(bug("avoiding deadloop\n"));
484 break;
486 lneg = 0;
487 rneg = 0;
488 lzero = 0;
489 rzero = 0;
491 /* D(bug("left weight : from %d to %d\n", data->lsum, weightA)); */
492 /* D(bug("right weight : from %d to %d\n", data->rsum, weightB)); */
493 object_state = (Object *)data->objs->lh_Head;
494 while ((sibling = NextObject(&object_state)))
496 if (!(_flags(sibling) & MADF_SHOWME))
497 continue;
498 if (is_fixed_size(data, sibling))
499 continue;
501 /* D(bug(" B %d\n", left)); */
502 if (left > 0)
504 WORD w1, w2;
505 w1 = get_weight(data, sibling);
506 w2 = w1;
507 if (w2 || (count < 2))
509 w2 += lwbygad;
510 if ((lfirst-- <= 0) && lbonus-- > 0)
511 w2 += ((ldelta > 0) ? 1 : -1);
512 if (w2 < 0)
514 lzero++;
515 lneg += w2;
516 w2 = 0;
518 set_weight(data, sibling, w2);
519 /* D(bug(" w (left) from %d to %d (%ld -> %ld)\n", w1, w2, data->oldWeightA, weightA)); */
522 else
524 WORD w1, w2;
525 w1 = get_weight(data, sibling);
526 w2 = w1;
527 if (w2 || (count < 2))
529 w2 += rwbygad;
530 if ((rfirst-- <= 0) && rbonus-- > 0)
531 w2 += ((rdelta > 0) ? 1 : -1);
532 if (w2 < 0)
534 rzero++;
535 rneg += w2;
536 w2 = 0;
538 set_weight(data, sibling, w2);
539 /* D(bug(" w (right) from %d to %d (%ld -> %ld)\n", w1, w2, data->oldWeightB, weightB)); */
542 left--;
544 if (lzero == data->lsiblings)
545 break;
546 if (rzero == data->rsiblings)
547 break;
549 lwbygad = lneg / (data->lsiblings - lzero);
550 lbonus = -(lneg % (data->lsiblings - lzero)) + lbonus;
551 rwbygad = rneg / (data->rsiblings - rzero);
552 rbonus = -(rneg % (data->rsiblings - rzero)) + rbonus;
553 } while (lneg || rneg || (lbonus > 0) || (rbonus > 0));
555 data->oldWeightA = weightA;
556 data->oldWeightB = weightB;
560 static void recalc_weights_all (struct IClass *cl, Object *obj, WORD mouse)
562 struct Balance_DATA *data = INST_DATA(cl, obj);
563 Object *first = NULL;
564 Object *last = NULL;
566 D(bug("recalc_weights_all\n"));
568 if ((data->total_weight == -1) || (data->lsiblings == -1) || (data->rsiblings == -1)
569 || (data->first_bound == -1) || (data->second_bound == -1))
571 Object *sibling;
572 Object *object_state;
573 Object *next;
574 BOOL mid = FALSE;
576 data->lsum = 0;
577 data->rsum = 0;
578 data->lsize = 0;
579 data->rsize = 0;
581 object_state = (Object *)data->objs->lh_Head;
582 for (next = NextObject(&object_state); next ; next = NextObject(&object_state))
584 sibling = next;
585 if (!(_flags(sibling) & MADF_SHOWME))
586 continue;
588 if (sibling == obj)
590 mid = TRUE;
591 data->rsiblings = 0;
592 continue;
595 if (is_fixed_size(data, sibling))
596 continue;
598 if (!first)
600 first = sibling;
601 data->lsiblings = 1;
602 data->lsum += get_weight(data, sibling);
603 data->lsize += get_size(data, sibling);
605 else
607 if (!mid)
609 data->lsiblings++;
610 data->lsum += get_weight(data, sibling);
611 data->lsize += get_size(data, sibling);
613 else
615 data->rsiblings++;
616 data->rsum += get_weight(data, sibling);
617 data->rsize += get_size(data, sibling);
618 last = sibling;
623 if (!first || !mid || !last)
624 return;
625 if (data->total_weight == -1)
626 data->total_weight = data->lsum + data->rsum;
628 if (data->first_bound == -1)
629 data->first_bound = get_first_bound_multi(data, first);
631 if (data->second_bound == -1)
632 data->second_bound = get_second_bound_multi(data, last);
634 data->oldWeightA = data->lsum;
635 data->oldWeightB = data->rsum;
637 D(bug("Total Weight = %ld, left = %ld, right = %ld\n", data->total_weight, data->lsum, data->rsum));
638 D(bug("bound 1 = %ld, bound 2 = %ld\n", data->first_bound, data->second_bound));
639 set_weight_all(data, obj, mouse);
643 static void handle_move (struct IClass *cl, Object *obj, WORD mouse)
645 struct Balance_DATA *data = INST_DATA(cl, obj);
647 if (data->state == CLICKED)
648 recalc_weights_all(cl, obj, mouse);
649 else if (data->state == SHIFT_CLICKED)
650 recalc_weights_neighbours(cl, obj, mouse);
651 else
652 return;
654 /* relayout with new weights */
655 DoMethod(_parent(obj), MUIM_Layout);
657 /* full drawing, or sketch */
658 if (muiGlobalInfo(obj)->mgi_Prefs->balancing_look == BALANCING_SHOW_OBJECTS)
660 MUI_Redraw(_parent(obj), MADF_DRAWALL);
662 else
664 Object *sibling;
665 Object *object_state;
667 DoMethod(_parent(obj), MUIM_DrawBackground, _mleft(_parent(obj)),
668 _mtop(_parent(obj)), _mwidth(_parent(obj)), _mheight(_parent(obj)),
669 0, 0, 0);
670 /* for each child, draw a black frame */
671 object_state = (Object *)data->objs->lh_Head;
672 while ((sibling = NextObject(&object_state)))
674 if (!(_flags(sibling) & MADF_SHOWME))
675 continue;
676 /* D(bug("sibling %lx\n", sibling)); */
678 draw_object_frame(obj, sibling, is_fixed_size(data, sibling));
684 /**************************************************************************
685 MUIM_HandleEvent
686 **************************************************************************/
687 IPTR Balance__MUIM_HandleEvent(struct IClass *cl, Object *obj, struct MUIP_HandleEvent *msg)
689 struct Balance_DATA *data = INST_DATA(cl, obj);
691 if (msg->imsg)
693 switch (msg->imsg->Class)
695 case IDCMP_MOUSEBUTTONS:
696 if (msg->imsg->Code == SELECTDOWN)
698 if (_isinobject(msg->imsg->MouseX, msg->imsg->MouseY))
700 get(_parent(obj), MUIA_Group_ChildList, &data->objs);
701 data->clickpos = data->horizgroup ? msg->imsg->MouseX : msg->imsg->MouseY;
702 data->lastpos = data->clickpos;
703 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR)&data->ehn);
704 data->ehn.ehn_Events |= IDCMP_MOUSEMOVE;
705 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR)&data->ehn);
706 if (msg->imsg->Qualifier & (IEQUALIFIER_LSHIFT | IEQUALIFIER_RSHIFT))
708 data->state = SHIFT_CLICKED;
709 data->obj_before = NULL;
710 data->obj_after = NULL;
712 else
714 data->state = CLICKED;
715 data->lsiblings = -1;
716 data->rsiblings = -1;
718 data->total_weight = -1;
719 data->first_bound = -1;
720 data->second_bound = -1;
721 srand(1);
722 MUI_Redraw(obj, MADF_DRAWOBJECT);
725 else /* msg->imsg->Code != SELECTDOWN */
727 if (data->state != NOT_CLICKED)
729 DoMethod(_win(obj), MUIM_Window_RemEventHandler, (IPTR)&data->ehn);
730 data->ehn.ehn_Events &= ~IDCMP_MOUSEMOVE;
731 DoMethod(_win(obj), MUIM_Window_AddEventHandler, (IPTR)&data->ehn);
732 data->state = NOT_CLICKED;
733 if (data->total_weight != -1)
734 MUI_Redraw(_parent(obj), MADF_DRAWALL);
735 else
736 MUI_Redraw(obj, MADF_DRAWALL);
739 break;
741 case IDCMP_MOUSEMOVE:
743 if ((data->horizgroup) && (msg->imsg->MouseX == data->lastpos))
744 break;
745 if ((!data->horizgroup) && (msg->imsg->MouseY == data->lastpos))
746 break;
747 data->lazy ^= 1;
748 if (data->lazy)
749 break;
750 if (data->horizgroup)
752 handle_move(cl, obj, msg->imsg->MouseX);
753 data->lastpos = msg->imsg->MouseX;
755 else
757 handle_move(cl, obj, msg->imsg->MouseY);
758 data->lastpos = msg->imsg->MouseY;
761 break;
765 return 0;
768 #if ZUNE_BUILTIN_BALANCE
769 BOOPSI_DISPATCHER(IPTR, Balance_Dispatcher, cl, obj, msg)
771 switch (msg->MethodID)
773 case OM_NEW: return Balance__OM_NEW(cl, obj, (struct opSet *) msg);
774 case MUIM_Setup: return Balance__MUIM_Setup(cl, obj, (APTR)msg);
775 case MUIM_Cleanup: return Balance__MUIM_Cleanup(cl, obj, (APTR)msg);
776 case MUIM_AskMinMax: return Balance__MUIM_AskMinMax(cl, obj, (APTR)msg);
777 case MUIM_Draw: return Balance__MUIM_Draw(cl, obj, (APTR)msg);
778 case MUIM_HandleEvent: return Balance__MUIM_HandleEvent(cl, obj, (APTR)msg);
779 default: return DoSuperMethodA(cl, obj, msg);
782 BOOPSI_DISPATCHER_END
784 const struct __MUIBuiltinClass _MUI_Balance_desc =
786 MUIC_Balance,
787 MUIC_Area,
788 sizeof(struct Balance_DATA),
789 (void*)Balance_Dispatcher
791 #endif