vfs: check userland buffers before reading them.
[haiku.git] / src / apps / cortex / RouteApp / RouteWindow.cpp
blobae5fd26ca31a2b639d59d692abf768d510f0647a
1 /*
2 * Copyright (c) 1999-2000, Eric Moon.
3 * All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
32 // RouteWindow.cpp
33 // e.moon 14may99
35 #include "RouteApp.h"
36 #include "RouteWindow.h"
37 #include "MediaRoutingView.h"
38 #include "StatusView.h"
40 #include "DormantNodeWindow.h"
41 #include "TransportWindow.h"
43 #include "RouteAppNodeManager.h"
44 #include "NodeGroup.h"
45 #include "TipManager.h"
47 #include <Alert.h>
48 #include <Autolock.h>
49 #include <Debug.h>
50 #include <Font.h>
51 #include <MenuBar.h>
52 #include <Menu.h>
53 #include <MenuItem.h>
54 #include <Message.h>
55 #include <Messenger.h>
56 #include <Roster.h>
57 #include <Screen.h>
58 #include <ScrollView.h>
59 #include <StringView.h>
61 #include <algorithm>
63 #include "debug_tools.h"
64 #define D_HOOK(x) //PRINT (x)
65 #define D_INTERNAL(x) //PRINT (x)
67 __USE_CORTEX_NAMESPACE
70 const char* const RouteWindow::s_windowName = "Cortex";
72 const BRect RouteWindow::s_initFrame(100,100,700,550);
74 const char* const g_aboutText =
75 "Cortex/Route 2.1.2\n\n"
76 "Copyright 1999-2000 Eric Moon\n"
77 "All rights reserved.\n\n"
78 "The Cortex Team:\n\n"
79 "Christopher Lenz: UI\n"
80 "Eric Moon: UI, back-end\n\n"
81 "Thanks to:\nJohn Ashmun\nJon Watte\nDoug Wright\n<your name here>\n\n"
82 "Certain icons used herein are the property of\n"
83 "Be, Inc. and are used by permission.";
86 RouteWindow::~RouteWindow()
91 RouteWindow::RouteWindow(RouteAppNodeManager* manager)
93 BWindow(s_initFrame, s_windowName, B_DOCUMENT_WINDOW, 0),
94 m_hScrollBar(0),
95 m_vScrollBar(0),
96 m_transportWindow(0),
97 m_dormantNodeWindow(0),
98 m_selectedGroupID(0),
99 m_zoomed(false),
100 m_zooming(false)
102 BRect b = Bounds();
104 // initialize the menu bar: add all menus that target this window
105 BMenuBar* pMenuBar = new BMenuBar(b, "menuBar");
106 BMenu* pFileMenu = new BMenu("File");
107 BMenuItem* item = new BMenuItem("Open" B_UTF8_ELLIPSIS,
108 new BMessage(RouteApp::M_SHOW_OPEN_PANEL), 'O');
109 item->SetTarget(be_app);
110 pFileMenu->AddItem(item);
111 pFileMenu->AddItem(new BSeparatorItem());
112 item = new BMenuItem("Save nodes" B_UTF8_ELLIPSIS,
113 new BMessage(RouteApp::M_SHOW_SAVE_PANEL), 'S');
114 item->SetTarget(be_app);
115 pFileMenu->AddItem(item);
116 pFileMenu->AddItem(new BSeparatorItem());
117 pFileMenu->AddItem(new BMenuItem("About Cortex/Route" B_UTF8_ELLIPSIS,
118 new BMessage(B_ABOUT_REQUESTED)));
119 pFileMenu->AddItem(new BSeparatorItem());
120 pFileMenu->AddItem(new BMenuItem("Quit", new BMessage(B_QUIT_REQUESTED)));
121 pMenuBar->AddItem(pFileMenu);
122 AddChild(pMenuBar);
124 // build the routing view
125 BRect rvBounds = b;
126 rvBounds.top = pMenuBar->Frame().bottom+1;
127 rvBounds.right -= B_V_SCROLL_BAR_WIDTH;
128 rvBounds.bottom -= B_H_SCROLL_BAR_HEIGHT;
129 m_routingView = new MediaRoutingView(manager, rvBounds, "routingView");
131 BRect hsBounds = rvBounds;
132 hsBounds.left = rvBounds.left + 199;
133 hsBounds.top = hsBounds.bottom + 1;
134 hsBounds.right++;
135 hsBounds.bottom = b.bottom + 1;
137 m_hScrollBar = new BScrollBar(hsBounds, "hScrollBar", m_routingView,
138 0, 0, B_HORIZONTAL);
139 AddChild(m_hScrollBar);
141 BRect vsBounds = rvBounds;
142 vsBounds.left = vsBounds.right + 1;
143 vsBounds.top--;
144 vsBounds.right = b.right + 1;
145 vsBounds.bottom++;
147 m_vScrollBar = new BScrollBar(vsBounds, "vScrollBar", m_routingView,
148 0, 0, B_VERTICAL);
149 AddChild(m_vScrollBar);
151 BRect svBounds = rvBounds;
152 svBounds.left -= 1;
153 svBounds.right = hsBounds.left - 1;
154 svBounds.top = svBounds.bottom + 1;
155 svBounds.bottom = b.bottom + 1;
157 m_statusView = new StatusView(svBounds, manager, m_hScrollBar);
158 AddChild(m_statusView);
160 AddChild(m_routingView);
162 float minWidth, maxWidth, minHeight, maxHeight;
163 GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
164 minWidth = m_statusView->Frame().Width() + 6 * B_V_SCROLL_BAR_WIDTH;
165 minHeight = 6 * B_H_SCROLL_BAR_HEIGHT;
166 SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
168 // construct the Window menu
169 BMenu* windowMenu = new BMenu("Window");
170 m_transportWindowItem = new BMenuItem(
171 "Show transport",
172 new BMessage(M_TOGGLE_TRANSPORT_WINDOW));
173 windowMenu->AddItem(m_transportWindowItem);
175 m_dormantNodeWindowItem = new BMenuItem(
176 "Show add-ons",
177 new BMessage(M_TOGGLE_DORMANT_NODE_WINDOW));
178 windowMenu->AddItem(m_dormantNodeWindowItem);
180 windowMenu->AddItem(new BSeparatorItem());
182 m_pullPalettesItem = new BMenuItem(
183 "Pull palettes",
184 new BMessage(M_TOGGLE_PULLING_PALETTES));
185 windowMenu->AddItem(m_pullPalettesItem);
187 pMenuBar->AddItem(windowMenu);
189 // create the dormant-nodes palette
190 _toggleDormantNodeWindow();
192 // display group inspector
193 _toggleTransportWindow();
197 // #pragma mark - operations
200 /*! Enable/disable palette position-locking (when the main
201 window is moved, all palettes follow).
203 bool
204 RouteWindow::isPullPalettes() const
206 return m_pullPalettesItem->IsMarked();
210 void
211 RouteWindow::setPullPalettes(bool enabled)
213 m_pullPalettesItem->SetMarked(enabled);
217 void
218 RouteWindow::constrainToScreen()
220 BScreen screen(this);
222 const BRect sr = screen.Frame();
224 // [c.lenz 1mar2000] this should be handled by every window
225 // itself. will probably change soon ;-)
226 _constrainToScreen();
227 /* // main window
228 BRect r = Frame();
229 BPoint offset(0.0, 0.0);
230 if(r.left < 0.0)
231 offset.x = -r.left;
232 if(r.top < 0.0)
233 offset.y = -r.top;
234 if(r.left >= (sr.right - 20.0))
235 offset.x -= (r.left - (sr.Width()/2));
236 if(r.top >= (sr.bottom - 20.0))
237 offset.y -= (r.top - (sr.Height()/2));
238 if(offset.x != 0.0 || offset.y != 0.0) {
239 setPullPalettes(false);
240 MoveBy(offset.x, offset.y);
243 // transport window
244 BPoint offset = BPoint(0.0, 0.0);
245 BRect r = (m_transportWindow) ?
246 m_transportWindow->Frame() :
247 m_transportWindowFrame;
248 if(r.left < 0.0)
249 offset.x = (sr.Width()*.75) - r.left;
250 if(r.top < 0.0)
251 offset.y = (sr.Height()*.25) - r.top;
252 if(r.left >= (sr.right - 20.0))
253 offset.x -= (r.left - (sr.Width()/2));
254 if(r.top >= (sr.bottom - 20.0))
255 offset.y -= (r.top - (sr.Height()/2));
257 if(offset.x != 0.0 || offset.y != 0.0) {
258 if(m_transportWindow)
259 m_transportWindow->MoveBy(offset.x, offset.y);
260 else
261 m_transportWindowFrame.OffsetBy(offset.x, offset.y);
264 // addon palette
265 offset = BPoint(0.0, 0.0);
266 r = (m_dormantNodeWindow) ?
267 m_dormantNodeWindow->Frame() :
268 m_dormantNodeWindowFrame;
269 if(r.left < 0.0)
270 offset.x = (sr.Width()*.25) - r.left;
271 if(r.top < 0.0)
272 offset.y = (sr.Height()*.125) - r.top;
273 if(r.left >= (sr.right - 20.0))
274 offset.x -= (r.left - (sr.Width()/2));
275 if(r.top >= (sr.bottom - 20.0))
276 offset.y -= (r.top - (sr.Height()/2));
278 if(offset.x != 0.0 || offset.y != 0.0) {
279 if(m_dormantNodeWindow)
280 m_dormantNodeWindow->MoveBy(offset.x, offset.y);
281 else
282 m_dormantNodeWindowFrame.OffsetBy(offset.x, offset.y);
288 // #pragma mark - BWindow implementation
291 void
292 RouteWindow::FrameMoved(BPoint point)
294 // ignore notification if the window isn't yet visible
295 if(IsHidden())
296 return;
298 BPoint delta = point - m_lastFramePosition;
299 m_lastFramePosition = point;
302 if (m_pullPalettesItem->IsMarked())
303 _movePalettesBy(delta.x, delta.y);
307 void
308 RouteWindow::FrameResized(float width, float height)
310 D_HOOK(("RouteWindow::FrameResized()\n"));
312 if (!m_zooming) {
313 m_zoomed = false;
315 else {
316 m_zooming = false;
321 bool
322 RouteWindow::QuitRequested()
324 be_app->PostMessage(B_QUIT_REQUESTED);
325 return false; // [e.moon 20oct99] app now quits window
329 void
330 RouteWindow::Zoom(BPoint origin, float width, float height)
332 D_HOOK(("RouteWindow::Zoom()\n"));
334 m_zooming = true;
336 BScreen screen(this);
337 if (!screen.Frame().Contains(Frame())) {
338 m_zoomed = false;
341 if (!m_zoomed) {
342 // resize to the ideal size
343 m_manualSize = Bounds();
344 float width, height;
345 m_routingView->GetPreferredSize(&width, &height);
346 width += B_V_SCROLL_BAR_WIDTH;
347 height += B_H_SCROLL_BAR_HEIGHT;
348 if (KeyMenuBar()) {
349 height += KeyMenuBar()->Frame().Height();
351 ResizeTo(width, height);
352 _constrainToScreen();
353 m_zoomed = true;
355 else {
356 // resize to the most recent manual size
357 ResizeTo(m_manualSize.Width(), m_manualSize.Height());
358 m_zoomed = false;
363 // #pragma mark - BHandler implemenation
366 void
367 RouteWindow::MessageReceived(BMessage* pMsg)
369 // PRINT((
370 // "RouteWindow::MessageReceived()\n"));
371 // pMsg->PrintToStream();
373 switch (pMsg->what) {
374 case B_ABOUT_REQUESTED:
376 BAlert* alert = new BAlert("About", g_aboutText, "OK");
377 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
378 alert->Go();
379 break;
381 case MediaRoutingView::M_GROUP_SELECTED:
382 _handleGroupSelected(pMsg);
383 break;
385 case MediaRoutingView::M_SHOW_ERROR_MESSAGE:
386 _handleShowErrorMessage(pMsg);
387 break;
389 case M_TOGGLE_TRANSPORT_WINDOW:
390 _toggleTransportWindow();
391 break;
393 case M_REFRESH_TRANSPORT_SETTINGS:
394 _refreshTransportSettings(pMsg);
395 break;
397 case M_TOGGLE_PULLING_PALETTES:
398 _togglePullPalettes();
399 break;
401 case M_TOGGLE_DORMANT_NODE_WINDOW:
402 _toggleDormantNodeWindow();
403 break;
405 case M_TOGGLE_GROUP_ROLLING:
406 _toggleGroupRolling();
407 break;
409 default:
410 _inherited::MessageReceived(pMsg);
411 break;
416 // #pragma mark - IStateArchivable
419 status_t
420 RouteWindow::importState(const BMessage* archive)
422 status_t err;
424 // frame rect
425 BRect r;
426 err = archive->FindRect("frame", &r);
427 if(err == B_OK) {
428 MoveTo(r.LeftTop());
429 ResizeTo(r.Width(), r.Height());
430 m_lastFramePosition = r.LeftTop();
433 // status view width
434 int32 i;
435 err = archive->FindInt32("statusViewWidth", &i);
436 if (err == B_OK) {
437 float diff = i - m_statusView->Bounds().IntegerWidth();
438 m_statusView->ResizeBy(diff, 0.0);
439 m_hScrollBar->ResizeBy(-diff, 0.0);
440 m_hScrollBar->MoveBy(diff, 0.0);
443 // settings
444 bool b;
445 err = archive->FindBool("pullPalettes", &b);
446 if(err == B_OK)
447 m_pullPalettesItem->SetMarked(b);
449 // const char* p;
450 // err = archive->FindString("saveDir", &p);
451 // if(err == B_OK) {
452 // m_openPanel.SetPanelDirectory(p);
453 // m_savePanel.SetPanelDirectory(p);
454 // }
456 // dormant-node window
457 err = archive->FindRect("addonPaletteFrame", &r);
458 if (err == B_OK)
459 m_dormantNodeWindowFrame = r;
460 err = archive->FindBool("addonPaletteVisible", &b);
461 if (err == B_OK && (b != (m_dormantNodeWindow != 0))) {
462 _toggleDormantNodeWindow();
463 if(!m_dormantNodeWindow)
464 m_dormantNodeWindowFrame = r;
467 if (m_dormantNodeWindow) {
468 m_dormantNodeWindow->MoveTo(m_dormantNodeWindowFrame.LeftTop());
469 m_dormantNodeWindow->ResizeTo(
470 m_dormantNodeWindowFrame.Width(),
471 m_dormantNodeWindowFrame.Height());
474 // transport window
475 err = archive->FindRect("transportFrame", &r);
476 if (err == B_OK)
477 m_transportWindowFrame = r;
478 err = archive->FindBool("transportVisible", &b);
479 if (err == B_OK && (b != (m_transportWindow != 0))) {
480 _toggleTransportWindow();
481 if (!m_transportWindow)
482 m_transportWindowFrame = r;
485 if (m_transportWindow) {
486 m_transportWindow->MoveTo(m_transportWindowFrame.LeftTop());
487 m_transportWindow->ResizeTo(
488 m_transportWindowFrame.Width(),
489 m_transportWindowFrame.Height());
492 return B_OK;
496 status_t
497 RouteWindow::exportState(BMessage* archive) const
499 BRect r = Frame();
500 archive->AddRect("frame", r);
501 archive->AddBool("pullPalettes", m_pullPalettesItem->IsMarked());
503 bool b = (m_dormantNodeWindow != 0);
504 r = b ? m_dormantNodeWindow->Frame() : m_dormantNodeWindowFrame;
505 archive->AddRect("addonPaletteFrame", r);
506 archive->AddBool("addonPaletteVisible", b);
508 b = (m_transportWindow != 0);
509 r = b ? m_transportWindow->Frame() : m_transportWindowFrame;
511 archive->AddRect("transportFrame", r);
512 archive->AddBool("transportVisible", b);
514 // [c.lenz 23may00] remember status view width
515 int i = m_statusView->Bounds().IntegerWidth();
516 archive->AddInt32("statusViewWidth", i);
518 // entry_ref saveRef;
519 // m_savePanel.GetPanelDirectory(&saveRef);
520 // BEntry saveEntry(&saveRef);
521 // if(saveEntry.InitCheck() == B_OK) {
522 // BPath p;
523 // saveEntry.GetPath(&p);
524 // archive->AddString("saveDir", p.Path());
525 // }
527 return B_OK;
531 // #pragma mark - implementation
534 void
535 RouteWindow::_constrainToScreen()
537 D_INTERNAL(("RouteWindow::_constrainToScreen()\n"));
539 BScreen screen(this);
540 BRect screenRect = screen.Frame();
541 BRect windowRect = Frame();
543 // if the window is outside the screen rect
544 // move it to the default position
545 if (!screenRect.Intersects(windowRect)) {
546 windowRect.OffsetTo(screenRect.LeftTop());
547 MoveTo(windowRect.LeftTop());
548 windowRect = Frame();
551 // if the window is larger than the screen rect
552 // resize it to fit at each side
553 if (!screenRect.Contains(windowRect)) {
554 if (windowRect.left < screenRect.left) {
555 windowRect.left = screenRect.left + 5.0;
556 MoveTo(windowRect.LeftTop());
557 windowRect = Frame();
559 if (windowRect.top < screenRect.top) {
560 windowRect.top = screenRect.top + 5.0;
561 MoveTo(windowRect.LeftTop());
562 windowRect = Frame();
564 if (windowRect.right > screenRect.right) {
565 windowRect.right = screenRect.right - 5.0;
567 if (windowRect.bottom > screenRect.bottom) {
568 windowRect.bottom = screenRect.bottom - 5.0;
570 ResizeTo(windowRect.Width(), windowRect.Height());
575 void
576 RouteWindow::_toggleTransportWindow()
578 if (m_transportWindow) {
579 m_transportWindowFrame = m_transportWindow->Frame();
580 m_transportWindow->Lock();
581 m_transportWindow->Quit();
582 m_transportWindow = 0;
583 m_transportWindowItem->SetMarked(false);
584 } else {
585 m_transportWindow = new TransportWindow(m_routingView->manager,
586 this, "Transport");
588 // ask for a selection update
589 BMessenger(m_routingView).SendMessage(
590 MediaRoutingView::M_BROADCAST_SELECTION);
592 // place & display the window
593 if (m_transportWindowFrame.IsValid()) {
594 m_transportWindow->MoveTo(m_transportWindowFrame.LeftTop());
595 m_transportWindow->ResizeTo(m_transportWindowFrame.Width(),
596 m_transportWindowFrame.Height());
599 m_transportWindow->Show();
600 m_transportWindowItem->SetMarked(true);
605 void
606 RouteWindow::_togglePullPalettes()
608 m_pullPalettesItem->SetMarked(!m_pullPalettesItem->IsMarked());
612 void
613 RouteWindow::_toggleDormantNodeWindow()
615 if (m_dormantNodeWindow) {
616 m_dormantNodeWindowFrame = m_dormantNodeWindow->Frame();
617 m_dormantNodeWindow->Lock();
618 m_dormantNodeWindow->Quit();
619 m_dormantNodeWindow = 0;
620 m_dormantNodeWindowItem->SetMarked(false);
621 } else {
622 m_dormantNodeWindow = new DormantNodeWindow(this);
623 if (m_dormantNodeWindowFrame.IsValid()) {
624 m_dormantNodeWindow->MoveTo(m_dormantNodeWindowFrame.LeftTop());
625 m_dormantNodeWindow->ResizeTo(m_dormantNodeWindowFrame.Width(),
626 m_dormantNodeWindowFrame.Height());
628 m_dormantNodeWindow->Show();
629 m_dormantNodeWindowItem->SetMarked(true);
634 void
635 RouteWindow::_handleGroupSelected(BMessage* message)
637 status_t err;
638 uint32 groupID;
640 err = message->FindInt32("groupID", (int32*)&groupID);
641 if (err < B_OK) {
642 PRINT((
643 "! RouteWindow::_handleGroupSelected(): no groupID in message!\n"));
644 return;
647 if (!m_transportWindow)
648 return;
650 BMessage m(TransportWindow::M_SELECT_GROUP);
651 m.AddInt32("groupID", groupID);
652 BMessenger(m_transportWindow).SendMessage(&m);
654 m_selectedGroupID = groupID;
658 void
659 RouteWindow::_handleShowErrorMessage(BMessage* message)
661 status_t err;
662 BString text;
664 err = message->FindString("text", &text);
665 if (err < B_OK) {
666 PRINT((
667 "! RouteWindow::_handleShowErrorMessage(): no text in message!\n"));
668 return;
671 m_statusView->setErrorMessage(text.String(), message->HasBool("error"));
675 //! Refresh the transport window for the given group, if any
676 void
677 RouteWindow::_refreshTransportSettings(BMessage* message)
679 status_t err;
680 uint32 groupID;
682 err = message->FindInt32("groupID", (int32*)&groupID);
683 if (err < B_OK) {
684 PRINT((
685 "! RouteWindow::_refreshTransportSettings(): no groupID in message!\n"));
686 return;
689 if(m_transportWindow) {
690 // relay the message
691 BMessenger(m_transportWindow).SendMessage(message);
696 void
697 RouteWindow::_closePalettes()
699 BAutolock _l(this);
701 if (m_transportWindow) {
702 m_transportWindow->Lock();
703 m_transportWindow->Quit();
704 m_transportWindow = 0;
709 //! Move all palette windows by the specified amounts
710 void RouteWindow::_movePalettesBy(float xDelta, float yDelta)
712 if (m_transportWindow)
713 m_transportWindow->MoveBy(xDelta, yDelta);
715 if (m_dormantNodeWindow)
716 m_dormantNodeWindow->MoveBy(xDelta, yDelta);
720 //! Toggle group playback
721 void
722 RouteWindow::_toggleGroupRolling()
724 if (!m_selectedGroupID)
725 return;
727 NodeGroup* g;
728 status_t err = m_routingView->manager->findGroup(m_selectedGroupID, &g);
729 if (err < B_OK)
730 return;
732 Autolock _l(g);
733 uint32 startAction = (g->runMode() == BMediaNode::B_OFFLINE)
734 ? NodeGroup::M_ROLL : NodeGroup::M_START;
736 BMessenger m(g);
737 switch (g->transportState()) {
738 case NodeGroup::TRANSPORT_STOPPED:
739 m.SendMessage(startAction);
740 break;
742 case NodeGroup::TRANSPORT_RUNNING:
743 case NodeGroup::TRANSPORT_ROLLING:
744 m.SendMessage(NodeGroup::M_STOP);
745 break;
747 default:
748 break;