/*
 * Copyright (c) 1999-2000, Eric Moon.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions, and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions, and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
 
 
// DormantNodeView.cpp
 
#include "DormantNodeView.h"
// DormantNodeView
#include "DormantNodeWindow.h"
#include "DormantNodeListItem.h"
// InfoWindow
#include "InfoWindowManager.h"
 
// Interface Kit
#include <Deskbar.h>
#include <Region.h>
#include <Screen.h>
#include <ScrollBar.h>
// Media Kit
#include <MediaRoster.h>
// Storage Kit
#include <Mime.h>
 
__USE_CORTEX_NAMESPACE
 
#include <Debug.h>
#define D_ALLOC(x) //PRINT (x)		// ctor/dtor
#define D_HOOK(x) //PRINT (x)		// BListView impl.
#define D_MESSAGE(x) //PRINT (x)	// MessageReceived()
#define D_INTERNAL(x) //PRINT (x)	// internal operations
 
// -------------------------------------------------------- //
// ctor/dtor (public)
// -------------------------------------------------------- //
 
DormantNodeView::DormantNodeView(
	BRect frame,
	const char *name,
	uint32 resizeMode)
	: BListView(frame, name, B_SINGLE_SELECTION_LIST, resizeMode),
	  m_lastItemUnder(0) {
	D_ALLOC(("DormantNodeView::DormantNodeView()\n"));
 
}
 
DormantNodeView::~DormantNodeView() {
	D_ALLOC(("DormantNodeView::~DormantNodeView()\n"));
 
}
 
// -------------------------------------------------------- //
// BListView impl. (public)
// -------------------------------------------------------- //
 
void DormantNodeView::AttachedToWindow() {
	D_HOOK(("DormantNodeView::AttachedToWindow()\n"));
 
	// populate the list
	_populateList();
 
	// Start watching the MediaRoster for flavor changes
	BMediaRoster *roster = BMediaRoster::CurrentRoster();
	if (roster) {
		BMessenger messenger(this, Window());
		roster->StartWatching(messenger, B_MEDIA_FLAVORS_CHANGED);
	}
}
 
void DormantNodeView::DetachedFromWindow() {
	D_HOOK(("DormantNodeView::DetachedFromWindow()\n"));
 
	// delete the lists contents
	_freeList();
 
	// Stop watching the MediaRoster for flavor changes
	BMediaRoster *roster = BMediaRoster::CurrentRoster();
	if (roster) {
		BMessenger messenger(this, Window());
		roster->StopWatching(messenger, B_MEDIA_FLAVORS_CHANGED);
	}
}
 
void DormantNodeView::GetPreferredSize(
	float* width,
	float* height) {
	D_HOOK(("DormantNodeView::GetPreferredSize()\n"));
 
	// calculate the accumulated size of all list items
	*width = 0;
	*height = 0;
	for (int32 i = 0; i < CountItems(); i++) {
		DormantNodeListItem *item;
		item = dynamic_cast<DormantNodeListItem *>(ItemAt(i));
		if (item) {
			BRect r = item->getRealFrame(be_plain_font);
			if (r.Width() > *width) {
				*width = r.Width();
			}
			*height += r.Height() + 1.0;
		}
	}
}
 
void DormantNodeView::MessageReceived(
	BMessage *message) {
	D_MESSAGE(("DormantNodeView::MessageReceived()\n"));
 
	switch (message->what) {
		case B_MEDIA_FLAVORS_CHANGED: {
			D_MESSAGE((" -> B_MEDIA_FLAVORS_CHANGED\n"));
 
			// init & re-populate the list
			int32 addOnID = 0;
			if (message->FindInt32("be:addon_id", &addOnID) != B_OK) {
				D_MESSAGE((" -> messages doesn't contain 'be:addon_id'!\n"));
				return;
			}
			_updateList(addOnID);
			break;
		}
		case InfoWindowManager::M_INFO_WINDOW_REQUESTED: {
			D_MESSAGE((" -> InfoWindowManager::M_INFO_WINDOW_REQUESTED)\n"));
 
			DormantNodeListItem *item;
			item = dynamic_cast<DormantNodeListItem *>(ItemAt(CurrentSelection()));
			if (item) {
				InfoWindowManager *manager = InfoWindowManager::Instance();
				if (manager && manager->Lock()) {
					manager->openWindowFor(item->info());
					manager->Unlock();
				}
			}
			break;
		}
		default: {
			_inherited::MessageReceived(message);
		}
	}
}
 
void DormantNodeView::MouseDown(
	BPoint point) {
	D_HOOK(("DormantNodeView::MouseDown()\n"));
 
	BMessage* message = Window()->CurrentMessage();
	int32 buttons = message->FindInt32("buttons");
	if (buttons == B_SECONDARY_MOUSE_BUTTON) {
		int32 index;
		if ((index = IndexOf(point)) >= 0) {
			DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(index));
			if (item) {
				Select(index);
				BRect r = item->getRealFrame(be_plain_font);
				if (r.Contains(point)) {
					item->showContextMenu(point, this);
				}
			}
		}
	}
 
	_inherited::MouseDown(point);
}
 
void DormantNodeView::MouseMoved(
	BPoint point,
	uint32 transit,
	const BMessage *message) {
	D_HOOK(("DormantNodeView::MouseMoved()\n"));
 
	int32 index;
	if (!message && ((index = IndexOf(point)) >= 0)) {
		DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(index));
		DormantNodeListItem *last = dynamic_cast<DormantNodeListItem *>(m_lastItemUnder);
		BRect r = item->getRealFrame(be_plain_font);
		if (item && r.Contains(point)) {
			if (item != last) {
				if (last)
					last->MouseOver(this, point, B_EXITED_VIEW);
				item->MouseOver(this, point, B_ENTERED_VIEW);
				m_lastItemUnder = item;
			}
			else {
				item->MouseOver(this, point, B_INSIDE_VIEW);
			}
		}
		else if (last) {
			last->MouseOver(this, point, B_EXITED_VIEW);
		}
	}
	
	_inherited::MouseMoved(point, transit, message);
}
 
bool DormantNodeView::InitiateDrag(
	BPoint point,
	int32 index,
	bool wasSelected) {
	D_HOOK(("DormantNodeView::InitiateDrag()\n"));
 
	DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(CurrentSelection()));
	if (item) {
		BMessage dragMsg(M_INSTANTIATE_NODE);
		dragMsg.AddData("which", B_RAW_TYPE,
						reinterpret_cast<const void *>(&item->info()),
						sizeof(item->info()));
		point -= ItemFrame(index).LeftTop();
		DragMessage(&dragMsg, item->getDragBitmap(), B_OP_ALPHA, point);
		return true;
	}
	return false;
}
 
// -------------------------------------------------------- //
// internal operations (private)
// -------------------------------------------------------- //
 
void DormantNodeView::_populateList() {
	D_INTERNAL(("DormantNodeView::_populateList()\n"));
 
	// init the resizable node-info buffer
	BMediaRoster *roster = BMediaRoster::CurrentRoster();
	const int32 bufferInc = 64;
	int32 bufferSize = bufferInc;
	dormant_node_info *infoBuffer = new dormant_node_info[bufferSize];
	int32 numNodes;
	
	// fill the buffer
	while (true) {
		numNodes = bufferSize;
		status_t error = roster->GetDormantNodes(infoBuffer, &numNodes);
		if (error) {
			return;
		}
		if (numNodes < bufferSize) {
			break;
		}
			
		// reallocate buffer & try again
		delete [] infoBuffer;
		bufferSize += bufferInc;
		infoBuffer = new dormant_node_info[bufferSize];
	}
	
	// populate the list
	for (int32 i = 0; i < numNodes; i++) {
		DormantNodeListItem *item = new DormantNodeListItem(infoBuffer[i]);
		AddItem(item);
	}
	SortItems(compareName);
}
 
void DormantNodeView::_freeList() {
	D_HOOK(("DormantNodeView::_freeList()\n"));
 
	// remove and delete all items in the list
	while (CountItems() > 0) {
		BListItem *item = ItemAt(0);
		if (RemoveItem(item)) {
			delete item;
		}
	}
}
 
void DormantNodeView::_updateList(
	int32 addOnID) {
	D_INTERNAL(("DormantNodeView::_updateList(%ld)\n", addOnID));
 
	// init the resizable node-info buffer
	BMediaRoster *roster = BMediaRoster::CurrentRoster();
	const int32 bufferInc = 64;
	int32 bufferSize = bufferInc;
	dormant_node_info *infoBuffer = new dormant_node_info[bufferSize];
	int32 numNodes;
	
	// fill the buffer
	while (true) {
		numNodes = bufferSize;
		status_t error = roster->GetDormantNodes(infoBuffer, &numNodes);
		if (error) {
			return;
		}
		if (numNodes < bufferSize) {
			break;
		}
			
		// reallocate buffer & try again
		delete [] infoBuffer;
		bufferSize += bufferInc;
		infoBuffer = new dormant_node_info[bufferSize];
	}
 
	// sort the list by add-on id to avoid multiple searches through
	// the list
	SortItems(compareAddOnID);
 
	// Remove all nodes managed by this add-on
	int32 start;
	for (start = 0; start < CountItems(); start++) {
		DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(start));
		if (item && (item->info().addon == addOnID)) {
			break;
		}
	}
	int32 count = 0;
	for (int32 i = start; start < CountItems(); i++) {
		DormantNodeListItem *item = dynamic_cast<DormantNodeListItem *>(ItemAt(i));
		if (!item || (item->info().addon != addOnID)) {
			break;
		}
		count++;
	}
	RemoveItems(start, count);
 
	// add the items
	for (int32 i = 0; i < numNodes; i++) {
		if (infoBuffer[i].addon != addOnID) {
			continue;
		}
		AddItem(new DormantNodeListItem(infoBuffer[i]));
	}
 
	SortItems(compareName);
}
 
// END -- DormantNodeView.cpp --

V595 The 'item' pointer was utilized before it was verified against nullptr. Check lines: 201, 202.