2 * Copyright (c) 1999-2000, Eric Moon.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions, and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions, and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
16 * 3. The name of the author may not be used to endorse or promote products
17 * derived from this software without specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
20 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
21 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
26 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
27 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #include "ValControl.h"
35 #include "ValControlSegment.h"
37 #include "TextControlFloater.h"
49 __USE_CORTEX_NAMESPACE
52 const float ValControl::fSegmentPadding
= 2.0;
54 // the decimal point covers one more pixel x and y-ward:
55 const float ValControl::fDecimalPointWidth
= 2.0;
56 const float ValControl::fDecimalPointHeight
= 2.0;
60 ValControl::ValControl(BRect frame
, const char* name
, const char* label
,
61 BMessage
* message
, align_mode alignMode
, align_flags alignFlags
,
62 update_mode updateMode
, bool backBuffer
)
63 : BControl(frame
, name
, label
, message
, B_FOLLOW_TOP
|B_FOLLOW_LEFT
,
64 B_WILL_DRAW
|B_FRAME_EVENTS
),
66 fUpdateMode(updateMode
),
67 fLabelFont(be_bold_font
),
68 fValueFont(be_bold_font
),
69 fAlignMode(alignMode
),
70 fAlignFlags(alignFlags
),
71 fOrigBounds(Bounds()),
72 fHaveBackBuffer(backBuffer
),
77 _AllocBackBuffer(frame
.Width(), frame
.Height());
79 // m_font.SetSize(13.0);
80 // rgb_color red = {255,0,0,255};
85 ValControl::~ValControl()
91 ValControl::update_mode
92 ValControl::updateMode() const
99 ValControl::setUpdateMode(update_mode mode
)
106 ValControl::labelFont() const
113 ValControl::setLabelFont(const BFont
* labelFont
)
115 fLabelFont
= labelFont
;
116 // inform label segments
122 ValControl::valueFont() const
129 ValControl::setValueFont(const BFont
* valueFont
)
131 fValueFont
= valueFont
;
133 // inform value segments
134 for (int n
= CountEntries(); n
> 0; --n
) {
135 const ValCtrlLayoutEntry
& e
= _EntryAt(n
-1);
136 if (e
.type
!= ValCtrlLayoutEntry::SEGMENT_ENTRY
)
139 ValControlSegment
* s
= dynamic_cast<ValControlSegment
*>(e
.pView
);
141 s
->SetFont(&fValueFont
);
142 s
->fontChanged(&fValueFont
);
148 ValControl::baselineOffset() const
151 be_plain_font
->GetHeight(&h
);
152 return ceil(h
.ascent
);
157 ValControl::segmentPadding() const
159 return fSegmentPadding
;
164 ValControl::backBufferView() const
166 return fBackBufferView
;
171 ValControl::backBuffer() const
184 "*** ValControl::dump():\n"
185 " FRAME (%.1f,%.1f)-(%.1f,%.1f)\n"
187 f
.left
, f
.top
, f
.right
, f
.bottom
));
189 for (layout_set::const_iterator it
= fLayoutSet
.begin();
190 it
!= fLayoutSet
.end(); ++it
) {
191 const ValCtrlLayoutEntry
& e
= *it
;
193 case ValCtrlLayoutEntry::SEGMENT_ENTRY
:
194 PRINT((" Segment "));
197 case ValCtrlLayoutEntry::VIEW_ENTRY
:
201 case ValCtrlLayoutEntry::DECIMAL_POINT_ENTRY
:
202 PRINT((" Decimal Point "));
210 PRINT(("\n cached frame (%.1f,%.1f)-(%.1f,%.1f) + pad(%.1f)\n",
211 e
.frame
.left
, e
.frame
.top
, e
.frame
.right
, e
.frame
.bottom
,
214 if (e
.type
== ValCtrlLayoutEntry::SEGMENT_ENTRY
215 || e
.type
== ValCtrlLayoutEntry::VIEW_ENTRY
) {
217 PRINT((" real frame (%.1f,%.1f)-(%.1f,%.1f)\n\n",
218 e
.pView
->Frame().left
, e
.pView
->Frame().top
,
219 e
.pView
->Frame().right
, e
.pView
->Frame().bottom
));
221 PRINT((" (no view!)\n\n"));
230 ValControl::SetEnabled(bool enabled
)
232 // redraw if enabled-state changes
233 _Inherited::SetEnabled(enabled
);
240 ValControl::_InvalidateAll()
243 int c
= CountChildren();
244 for (int n
= 0; n
< c
; ++n
)
245 ChildAt(n
)->Invalidate();
250 ValControl::AttachedToWindow()
252 // adopt parent view's color
254 SetViewColor(Parent()->ViewColor());
259 ValControl::AllAttached()
261 // move children to requested positions
262 BWindow
* pWnd
= Window();
263 pWnd
->BeginViewTransaction();
265 for_each(fLayoutSet
.begin(), fLayoutSet
.end(),
266 ptr_fun(&ValCtrlLayoutEntry::InitChildFrame
)); // +++++?
268 pWnd
->EndViewTransaction();
272 //! Paint decorations (& decimal point)
274 ValControl::Draw(BRect updateRect
)
276 // draw lightweight entries:
277 for (unsigned int n
= 0; n
< fLayoutSet
.size(); n
++) {
278 if (fLayoutSet
[n
].type
== ValCtrlLayoutEntry::DECIMAL_POINT_ENTRY
)
279 drawDecimalPoint(fLayoutSet
[n
]);
285 ValControl::drawDecimalPoint(ValCtrlLayoutEntry
& e
)
287 rgb_color dark
= {0, 0, 0, 255};
288 rgb_color med
= {200, 200, 200, 255};
289 // rgb_color light = {244,244,244,255};
292 center
.x
= e
.frame
.left
+ 1;
293 center
.y
= baselineOffset() - 1;
296 StrokeLine(center
, center
);
298 StrokeLine(center
- BPoint(0, 1), center
+ BPoint(1, 0));
299 StrokeLine(center
- BPoint(1, 0), center
+ BPoint(0, 1));
301 // SetHighColor(light);
302 // StrokeLine(center+BPoint(-1,1), center+BPoint(-1,1));
303 // StrokeLine(center+BPoint(1,1), center+BPoint(1,1));
304 // StrokeLine(center+BPoint(-1,-1), center+BPoint(-1,-1));
305 // StrokeLine(center+BPoint(1,-1), center+BPoint(1,-1));
310 ValControl::FrameResized(float width
, float height
)
312 _Inherited::FrameResized(width
,height
);
314 _AllocBackBuffer(width
, height
);
317 // "# ValControl::FrameResized(): %.1f, %.1f\n",
323 ValControl::GetPreferredSize(float* outWidth
, float* outHeight
)
325 ASSERT(fLayoutSet
.size() > 0);
328 fLayoutSet
.back().frame
.right
-
329 fLayoutSet
.front().frame
.left
;
332 for(layout_set::const_iterator it
= fLayoutSet
.begin();
333 it
!= fLayoutSet
.end(); ++it
) {
334 if((*it
).frame
.Height() > *outHeight
)
335 *outHeight
= (*it
).frame
.Height();
339 // "# ValControl::GetPreferredSize(): %.1f, %.1f\n",
340 // *outWidth, *outHeight));
345 ValControl::MakeFocus(bool focused
)
347 _Inherited::MakeFocus(focused
);
349 // +++++ only the underline needs to be redrawn
355 ValControl::MouseDown(BPoint where
)
362 ValControl::MessageReceived(BMessage
* message
)
365 const char* stringValue
;
368 // "ValControl::MessageReceived():\n"));
369 // message->PrintToStream();
371 switch (message
->what
) {
373 err
= message
->FindString("_value", &stringValue
);
376 "! ValControl::MessageReceived(): no _value found!\n"));
381 err
= setValueFrom(stringValue
);
384 "! ValControl::MessageReceived(): setValueFrom('%s'):\n"
390 // +++++ broadcast new value +++++ [23aug99]
393 case M_GET_VALUE
: // +++++
397 _Inherited::MessageReceived(message
);
402 // -------------------------------------------------------- //
403 // archiving/instantiation
404 // -------------------------------------------------------- //
406 ValControl::ValControl(BMessage
* archive
)
411 archive
->FindInt32("updateMode", (int32
*)&fUpdateMode
);
412 archive
->FindInt32("alignMode", (int32
*)&fAlignMode
);
413 archive
->FindInt32("alignFlags", (int32
*)&fAlignFlags
);
416 archive
->FindRect("origBounds", &fOrigBounds
);
421 ValControl::Archive(BMessage
* archive
, bool deep
) const
423 status_t err
= _Inherited::Archive(archive
, deep
);
427 err
= archive
->AddInt32("updateMode", (int32
)fUpdateMode
);
429 err
= archive
->AddInt32("alignMode", (int32
)fAlignMode
);
431 err
= archive
->AddInt32("alignFlags", (int32
)fAlignFlags
);
433 err
= archive
->AddRect("origBounds", fOrigBounds
);
442 for (layout_set::const_iterator it
= fLayoutSet
.begin();
443 it
!= fLayoutSet
.end(); it
++) {
448 err
= (*it
).pView
->Archive(&layoutSet
, true);
452 archive
->AddMessage("layoutSet", &layoutSet
);
459 // -------------------------------------------------------- //
460 // internal operations
461 // -------------------------------------------------------- //
463 // add segment view (which is responsible for generating its
464 // own ValCtrlLayoutEntry)
466 ValControl::_Add(ValControlSegment
* segment
, entry_location from
,
469 BWindow
* pWnd
= Window();
471 pWnd
->BeginViewTransaction();
475 segment
->SetFont(&fValueFont
);
476 segment
->fontChanged(&fValueFont
);
478 uint16 nIndex
= _LocationToIndex(from
, distance
);
479 ValCtrlLayoutEntry entry
= segment
->makeLayoutEntry();
480 _InsertEntry(entry
, nIndex
);
481 // linkSegment(segment, nIndex);
484 pWnd
->EndViewTransaction();
488 // add general view (manipulator, label, etc.)
489 // the entry's frame rectangle will be filled in
491 ValControl::_Add(ValCtrlLayoutEntry
& entry
, entry_location from
)
493 BWindow
* window
= Window();
495 window
->BeginViewTransaction();
498 AddChild(entry
.pView
);
500 uint16 index
= _LocationToIndex(from
, 0);
501 _InsertEntry(entry
, index
);
504 window
->EndViewTransaction();
508 // access child-view ValCtrlLayoutEntry
509 // (_IndexOf returns index from left)
510 const ValCtrlLayoutEntry
&
511 ValControl::_EntryAt(entry_location from
, uint16 distance
) const
513 uint16 nIndex
= _LocationToIndex(from
, distance
);
514 ASSERT(nIndex
< fLayoutSet
.size());
515 return fLayoutSet
[nIndex
];
519 const ValCtrlLayoutEntry
&
520 ValControl::_EntryAt(uint16 offset
) const
522 uint16 nIndex
= _LocationToIndex(FROM_LEFT
, offset
);
523 ASSERT(nIndex
< fLayoutSet
.size());
524 return fLayoutSet
[nIndex
];
529 ValControl::_IndexOf(BView
* child
) const
531 for (uint16 n
= 0; n
< fLayoutSet
.size(); n
++) {
532 if (fLayoutSet
[n
].pView
== child
)
536 ASSERT(!"shouldn't be here");
542 ValControl::CountEntries() const
544 return fLayoutSet
.size();
548 // pop up keyboard input field +++++
550 ValControl::showEditField()
555 status_t err
= getString(valueString
);
559 BRect f
= Bounds().OffsetByCopy(4.0, -4.0);
562 //"# ValControl::showEditField(): base bounds (%.1f, %.1f)-(%.1f,%.1f)\n",
563 //f.left, f.top, f.right, f.bottom));
565 new TextControlFloater(f
, B_ALIGN_RIGHT
, &fValueFont
, valueString
.String(),
566 BMessenger(this), new BMessage(M_SET_VALUE
));
567 // TextControlFloater embeds new value
568 // in message: _value (string) +++++ DO NOT HARDCODE
572 //! (Re-)initialize backbuffer
574 ValControl::_AllocBackBuffer(float width
, float height
)
576 ASSERT(fHaveBackBuffer
);
577 if (fBackBuffer
&& fBackBuffer
->Bounds().Width() >= width
578 && fBackBuffer
->Bounds().Height() >= height
)
584 fBackBufferView
= NULL
;
587 BRect
bounds(0, 0, width
, height
);
588 fBackBuffer
= new BBitmap(bounds
, B_RGB32
, true);
589 fBackBufferView
= new BView(bounds
, "back", B_FOLLOW_NONE
, B_WILL_DRAW
);
590 fBackBuffer
->AddChild(fBackBufferView
);
594 // ref'd view must already be a child +++++
595 // (due to GetPreferredSize implementation in segment views)
597 ValControl::_InsertEntry(ValCtrlLayoutEntry
& entry
, uint16 index
)
599 // view ptr must be 0, or a ValControlSegment that's already a child
600 ValControlSegment
* pSeg
= dynamic_cast<ValControlSegment
*>(entry
.pView
);
604 ASSERT(this == pSeg
->Parent());
606 // entry must be at one side or the other:
607 ASSERT(!index
|| index
== fLayoutSet
.size());
611 !(entry
.flags
& ValCtrlLayoutEntry::LAYOUT_NO_PADDING
||
613 fLayoutSet
[index
- 1].flags
& ValCtrlLayoutEntry::LAYOUT_NO_PADDING
)) ||
614 ((index
+ 1 < static_cast<uint16
>(fLayoutSet
.size()) &&
615 fLayoutSet
[index
+ 1].flags
& ValCtrlLayoutEntry::LAYOUT_NO_PADDING
)));
617 entry
.fPadding
= (bNeedsPadding
) ? fSegmentPadding
: 0.0;
619 // fetch (and grant) requested frame size
620 BRect
frame(0, 0, 0, 0);
622 pSeg
->GetPreferredSize(&frame
.right
, &frame
.bottom
);
624 _GetDefaultEntrySize(entry
.type
, &frame
.right
, &frame
.bottom
);
626 // figure amount this entry will displace:
627 float fDisplacement
= frame
.Width() + entry
.fPadding
+ 1;
629 // set entry's top-left position:
630 if (!fLayoutSet
.size()) {
632 if (fAlignMode
== ALIGN_FLUSH_RIGHT
)
633 frame
.OffsetBy(Bounds().right
- frame
.Width(), 0.0);
635 // insert at right side
636 if (fAlignMode
== ALIGN_FLUSH_LEFT
)
637 frame
.OffsetBy(fLayoutSet
.back().frame
.right
+ 1 + entry
.fPadding
, 0.0);
639 frame
.OffsetBy(fLayoutSet
.back().frame
.right
- frame
.Width(), 0.0); //+++++
641 // insert at left side
642 if (fAlignMode
== ALIGN_FLUSH_RIGHT
)
643 frame
.OffsetBy(fLayoutSet
.front().frame
.left
- fDisplacement
, 0.0);
649 index
? fLayoutSet
.end() : fLayoutSet
.begin(),
652 // slide following or preceding entries (depending on align mode)
654 switch (fAlignMode
) {
655 case ALIGN_FLUSH_LEFT
:
656 // following entries are shifted to the right
657 for(uint32 n
= index
+1; n
< fLayoutSet
.size(); n
++)
658 _SlideEntry(n
, fDisplacement
);
661 case ALIGN_FLUSH_RIGHT
: {
662 // preceding entries are shifted to the left
663 for(int n
= index
-1; n
>= 0; n
--)
664 _SlideEntry(n
, -fDisplacement
);
671 // "### added entry: (%.1f,%.1f)-(%.1f,%.1f)\n",
672 // frame.left, frame.top, frame.right, frame.bottom));
677 ValControl::_SlideEntry(int index
, float delta
)
679 ValCtrlLayoutEntry
& e
= fLayoutSet
[index
];
680 e
.frame
.OffsetBy(delta
, 0.0);
682 // move & possibly resize view:
684 e
.pView
->MoveTo(e
.frame
.LeftTop());
686 BRect curFrame
= e
.pView
->Frame();
687 float fWidth
= e
.frame
.Width();
688 float fHeight
= e
.frame
.Height();
689 if (curFrame
.Width() != fWidth
690 || curFrame
.Height() != fHeight
)
691 e
.pView
->ResizeTo(fWidth
+ 5.0, fHeight
);
697 ValControl::_LocationToIndex(entry_location from
, uint16 distance
) const
707 nResult
= fLayoutSet
.size() - distance
;
711 ASSERT(nResult
<= fLayoutSet
.size());
717 ValControl::_GetDefaultEntrySize(ValCtrlLayoutEntry::entry_type type
,
718 float* outWidth
, float* outHeight
)
721 case ValCtrlLayoutEntry::SEGMENT_ENTRY
:
722 case ValCtrlLayoutEntry::VIEW_ENTRY
:
727 case ValCtrlLayoutEntry::DECIMAL_POINT_ENTRY
:
728 *outWidth
= fDecimalPointWidth
;
729 *outHeight
= fDecimalPointHeight
;