mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-23 07:00:46 +00:00
New file
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@12600 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
849bf40175
commit
386aadd1e6
2 changed files with 513 additions and 0 deletions
65
Headers/gnustep/gui/GSMemoryPanel.h
Normal file
65
Headers/gnustep/gui/GSMemoryPanel.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/* GSMemoryPanel.h -*-objc-*-
|
||||
|
||||
A GNUstep panel for tracking memory leaks.
|
||||
|
||||
Copyright (C) 2000, 2002 Free Software Foundation, Inc.
|
||||
|
||||
Author: Nicola Pero <nicola@brainstorm.co.uk>
|
||||
Date: 2000, 2002
|
||||
|
||||
This file is part of the GNUstep GUI Library.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; see the file COPYING.LIB.
|
||||
If not, write to the Free Software Foundation,
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Class displaying a panel showing object allocation statistics.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _GNUstep_H_GSMEMORY_PANEL_
|
||||
#define _GNUstep_H_GSMEMORY_PANEL_
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
#include <AppKit/AppKit.h>
|
||||
|
||||
@interface GSMemoryPanel: NSPanel
|
||||
{
|
||||
NSTableView *table;
|
||||
NSMutableArray *classArray;
|
||||
NSMutableArray *countArray;
|
||||
NSMutableArray *totalArray;
|
||||
NSMutableArray *peakArray;
|
||||
/* Are we ordering by class name, or by count or total or peak number
|
||||
of instances ? */
|
||||
int orderingBy;
|
||||
}
|
||||
+ (id) sharedMemoryPanel;
|
||||
|
||||
/* Updates the statistics */
|
||||
+ (void) update: (id)sender;
|
||||
- (void) update: (id)sender;
|
||||
@end
|
||||
|
||||
@interface NSApplication (GSMemoryPanel)
|
||||
- (void) orderFrontSharedMemoryPanel: (id)sender;
|
||||
@end
|
||||
|
||||
#endif /* _GNUstep_H_GSMEMORY_PANEL_ */
|
||||
|
||||
|
||||
|
||||
|
448
Source/GSMemoryPanel.m
Normal file
448
Source/GSMemoryPanel.m
Normal file
|
@ -0,0 +1,448 @@
|
|||
/* GSMemoryPanel.m -*-objc-*-
|
||||
|
||||
A GNUstep panel for tracking memory leaks.
|
||||
|
||||
Copyright (C) 2000, 2002 Free Software Foundation, Inc.
|
||||
|
||||
Author: Nicola Pero <nicola@brainstorm.co.uk>
|
||||
Date: 2000, 2002
|
||||
|
||||
This file is part of the GNUstep GUI Library.
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Library General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2 of the License, or (at your option) any later version.
|
||||
|
||||
This library is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Library General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Library General Public
|
||||
License along with this library; see the file COPYING.LIB.
|
||||
If not, write to the Free Software Foundation,
|
||||
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include <AppKit/GSMemoryPanel.h>
|
||||
#include <AppKit/GSHbox.h>
|
||||
#include <AppKit/GSVbox.h>
|
||||
|
||||
enum {
|
||||
OrderByClassName,
|
||||
OrderByCount,
|
||||
OrderByTotal,
|
||||
OrderByPeak
|
||||
};
|
||||
|
||||
static inline NSComparisonResult
|
||||
invertComparison (NSComparisonResult comparison)
|
||||
{
|
||||
/* invert comparison */
|
||||
if (comparison == NSOrderedAscending)
|
||||
{
|
||||
comparison = NSOrderedDescending;
|
||||
}
|
||||
else if (comparison == NSOrderedDescending)
|
||||
{
|
||||
comparison = NSOrderedAscending;
|
||||
}
|
||||
return comparison;
|
||||
}
|
||||
|
||||
/*
|
||||
* Internal private class used in reordering entries.
|
||||
*
|
||||
*/
|
||||
@interface GSMemoryPanelEntry : NSObject
|
||||
{
|
||||
NSString *string;
|
||||
NSNumber *count;
|
||||
NSNumber *total;
|
||||
NSNumber *peak;
|
||||
}
|
||||
- (id) initWithString: (NSString *)aString
|
||||
count: (int)aCount
|
||||
total: (int)aTotal
|
||||
peak: (int)aPeak;
|
||||
- (NSString *) string;
|
||||
- (NSNumber *) count;
|
||||
- (NSNumber *) total;
|
||||
- (NSNumber *) peak;
|
||||
- (NSComparisonResult) compareByTotal: (GSMemoryPanelEntry *)aEntry;
|
||||
- (NSComparisonResult) compareByCount: (GSMemoryPanelEntry *)aEntry;
|
||||
- (NSComparisonResult) compareByPeak: (GSMemoryPanelEntry *)aEntry;
|
||||
- (NSComparisonResult) compareByClassName: (GSMemoryPanelEntry *)aEntry;
|
||||
@end
|
||||
|
||||
@implementation GSMemoryPanelEntry
|
||||
|
||||
- (id) initWithString: (NSString *)aString
|
||||
count: (int)aCount
|
||||
total: (int)aTotal
|
||||
peak: (int)aPeak
|
||||
{
|
||||
ASSIGN (string, aString);
|
||||
ASSIGN (count, [NSNumber numberWithInt: aCount]);
|
||||
ASSIGN (total, [NSNumber numberWithInt: aTotal]);
|
||||
ASSIGN (peak, [NSNumber numberWithInt: aPeak]);
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) dealloc
|
||||
{
|
||||
RELEASE (string);
|
||||
RELEASE (count);
|
||||
RELEASE (total);
|
||||
RELEASE (peak);
|
||||
[super dealloc];
|
||||
}
|
||||
|
||||
- (NSString *) string
|
||||
{
|
||||
return string;
|
||||
}
|
||||
|
||||
- (NSNumber *) count
|
||||
{
|
||||
return count;
|
||||
}
|
||||
|
||||
- (NSNumber *) total
|
||||
{
|
||||
return total;
|
||||
}
|
||||
|
||||
- (NSNumber *) peak
|
||||
{
|
||||
return peak;
|
||||
}
|
||||
|
||||
- (NSComparisonResult) compareByCount: (GSMemoryPanelEntry *)aEntry
|
||||
{
|
||||
NSComparisonResult comparison = [count compare: aEntry->count];
|
||||
|
||||
return invertComparison (comparison);
|
||||
}
|
||||
|
||||
- (NSComparisonResult) compareByTotal: (GSMemoryPanelEntry *)aEntry
|
||||
{
|
||||
NSComparisonResult comparison = [total compare: aEntry->total];
|
||||
|
||||
return invertComparison (comparison);
|
||||
}
|
||||
|
||||
- (NSComparisonResult) compareByPeak: (GSMemoryPanelEntry *)aEntry
|
||||
{
|
||||
NSComparisonResult comparison = [peak compare: aEntry->peak];
|
||||
|
||||
return invertComparison (comparison);
|
||||
}
|
||||
|
||||
- (NSComparisonResult) compareByClassName: (GSMemoryPanelEntry *)aEntry
|
||||
{
|
||||
return [string compare: aEntry->string];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
/*
|
||||
* The Memory Panel code
|
||||
*/
|
||||
|
||||
static GSMemoryPanel *sharedGSMemoryPanel = nil;
|
||||
|
||||
@implementation GSMemoryPanel
|
||||
+ (id) sharedMemoryPanel
|
||||
{
|
||||
if (sharedGSMemoryPanel == nil)
|
||||
{
|
||||
sharedGSMemoryPanel = [GSMemoryPanel new];
|
||||
}
|
||||
|
||||
return sharedGSMemoryPanel;
|
||||
}
|
||||
|
||||
+ (void) update: (id)sender
|
||||
{
|
||||
[[self sharedMemoryPanel] update: sender];
|
||||
}
|
||||
|
||||
- (id) init
|
||||
{
|
||||
NSRect winFrame;
|
||||
NSTableColumn *classColumn;
|
||||
NSTableColumn *countColumn;
|
||||
NSTableColumn *totalColumn;
|
||||
NSTableColumn *peakColumn;
|
||||
NSScrollView *scrollView;
|
||||
GSVbox *vbox;
|
||||
GSHbox *hbox;
|
||||
NSButton *button;
|
||||
|
||||
/* Activate debugging of allocation. */
|
||||
GSDebugAllocationActive (YES);
|
||||
|
||||
/* Button updating the table. */
|
||||
button = [NSButton new];
|
||||
[button setBordered: YES];
|
||||
[button setButtonType: NSMomentaryPushButton];
|
||||
[button setTitle: @"Update"];
|
||||
[button setImagePosition: NSNoImage];
|
||||
[button setTarget: self];
|
||||
[button setAction: @selector(update:)];
|
||||
[button setAutoresizingMask: NSViewMinXMargin];
|
||||
[button sizeToFit];
|
||||
[button setFrameSize: NSMakeSize ([button frame].size.width + 6,
|
||||
[button frame].size.height + 4)];
|
||||
|
||||
hbox = [GSHbox new];
|
||||
[hbox setDefaultMinXMargin: 5];
|
||||
[hbox setBorder: 5];
|
||||
[hbox setAutoresizingMask: NSViewWidthSizable];
|
||||
[hbox addView: button];
|
||||
RELEASE (button);
|
||||
|
||||
/* Ordering by number of objects by default */
|
||||
orderingBy = OrderByCount;
|
||||
|
||||
classColumn = [[NSTableColumn alloc] initWithIdentifier: @"Class"];
|
||||
[classColumn setEditable: NO];
|
||||
[[classColumn headerCell] setStringValue: @"Class Name"];
|
||||
[classColumn setMinWidth: 200];
|
||||
|
||||
countColumn = [[NSTableColumn alloc] initWithIdentifier: @"Count"];
|
||||
[countColumn setEditable: NO];
|
||||
[[countColumn headerCell] setStringValue: @"Current"];
|
||||
[countColumn setMinWidth: 50];
|
||||
|
||||
totalColumn = [[NSTableColumn alloc] initWithIdentifier: @"Total"];
|
||||
[totalColumn setEditable: NO];
|
||||
[[totalColumn headerCell] setStringValue: @"Total"];
|
||||
[totalColumn setMinWidth: 50];
|
||||
|
||||
peakColumn = [[NSTableColumn alloc] initWithIdentifier: @"Peak"];
|
||||
[peakColumn setEditable: NO];
|
||||
[[peakColumn headerCell] setStringValue: @"Peak"];
|
||||
[peakColumn setMinWidth: 50];
|
||||
|
||||
table = [[NSTableView alloc] initWithFrame: NSMakeRect (0, 0, 300, 300)];
|
||||
[table addTableColumn: classColumn];
|
||||
RELEASE (classColumn);
|
||||
[table addTableColumn: countColumn];
|
||||
RELEASE (countColumn);
|
||||
[table addTableColumn: totalColumn];
|
||||
RELEASE (totalColumn);
|
||||
[table addTableColumn: peakColumn];
|
||||
RELEASE (peakColumn);
|
||||
[table setDataSource: self];
|
||||
[table setDelegate: self];
|
||||
[table setDoubleAction: @selector (reorder:)];
|
||||
|
||||
scrollView = [[NSScrollView alloc]
|
||||
initWithFrame: NSMakeRect (0, 0, 350, 300)];
|
||||
[scrollView setDocumentView: table];
|
||||
RELEASE (table);
|
||||
[scrollView setHasHorizontalScroller: YES];
|
||||
[scrollView setHasVerticalScroller: YES];
|
||||
[scrollView setBorderType: NSBezelBorder];
|
||||
[scrollView setAutoresizingMask: (NSViewWidthSizable | NSViewHeightSizable)];
|
||||
|
||||
[table sizeToFit];
|
||||
|
||||
vbox = [GSVbox new];
|
||||
[vbox setDefaultMinYMargin: 5];
|
||||
[vbox setBorder: 5];
|
||||
[vbox addView: hbox enablingYResizing: NO];
|
||||
RELEASE (hbox);
|
||||
[vbox addView: scrollView];
|
||||
RELEASE (scrollView);
|
||||
|
||||
/* FIXME - should actually autosave the memory panel position and frame ! */
|
||||
winFrame.size = [vbox frame].size;
|
||||
winFrame.origin = NSMakePoint (100, 200);
|
||||
|
||||
self = [super initWithContentRect: winFrame
|
||||
styleMask: (NSTitledWindowMask
|
||||
| NSClosableWindowMask
|
||||
| NSMiniaturizableWindowMask
|
||||
| NSResizableWindowMask)
|
||||
backing: NSBackingStoreBuffered
|
||||
defer: NO];
|
||||
|
||||
[self setReleasedWhenClosed: NO];
|
||||
[self setContentView: vbox];
|
||||
RELEASE (vbox);
|
||||
[self setTitle: @"Memory Panel"];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (int) numberOfRowsInTableView: (NSTableView *)aTableView
|
||||
{
|
||||
return [countArray count];
|
||||
}
|
||||
|
||||
- (id) tableView: (NSTableView *)aTableView
|
||||
objectValueForTableColumn: (NSTableColumn *)aTableColumn
|
||||
row:(int)rowIndex
|
||||
{
|
||||
id identifier = [aTableColumn identifier];
|
||||
|
||||
if ([identifier isEqual: @"Class"])
|
||||
{
|
||||
return [classArray objectAtIndex: rowIndex];
|
||||
}
|
||||
else if ([identifier isEqual: @"Count"])
|
||||
{
|
||||
return [countArray objectAtIndex: rowIndex];
|
||||
}
|
||||
else if ([identifier isEqual: @"Total"])
|
||||
{
|
||||
return [totalArray objectAtIndex: rowIndex];
|
||||
}
|
||||
else if ([identifier isEqual: @"Peak"])
|
||||
{
|
||||
return [peakArray objectAtIndex: rowIndex];
|
||||
}
|
||||
|
||||
NSLog (@"Hi, I am a bug in your table view");
|
||||
|
||||
return @"";
|
||||
}
|
||||
|
||||
- (void) update: (id)sender
|
||||
{
|
||||
Class *classList = GSDebugAllocationClassList ();
|
||||
Class *pointer;
|
||||
NSMutableArray *array = [NSMutableArray new];
|
||||
NSArray *array_imm;
|
||||
SEL orderSel = NULL;
|
||||
GSMemoryPanelEntry *entry;
|
||||
int i, count, total, peak;
|
||||
NSString *className;
|
||||
NSMutableArray *classes = [NSMutableArray new];
|
||||
NSMutableArray *counts = [NSMutableArray new];
|
||||
NSMutableArray *totals = [NSMutableArray new];
|
||||
NSMutableArray *peaks = [NSMutableArray new];
|
||||
|
||||
pointer = classList;
|
||||
i = 0;
|
||||
|
||||
while (pointer[i] != NULL)
|
||||
{
|
||||
className = NSStringFromClass (pointer[i]);
|
||||
count = GSDebugAllocationCount (pointer[i]);
|
||||
total = GSDebugAllocationTotal (pointer[i]);
|
||||
peak = GSDebugAllocationPeak (pointer[i]);
|
||||
|
||||
/* Insert into array */
|
||||
entry = [GSMemoryPanelEntry alloc];
|
||||
[entry initWithString: className count: count
|
||||
total: total peak: peak];
|
||||
[array addObject: entry];
|
||||
RELEASE (entry);
|
||||
i++;
|
||||
}
|
||||
|
||||
switch (orderingBy)
|
||||
{
|
||||
case (OrderByClassName):
|
||||
orderSel = @selector(compareByClassName:);
|
||||
break;
|
||||
case (OrderByCount):
|
||||
orderSel = @selector(compareByCount:);
|
||||
break;
|
||||
case (OrderByTotal):
|
||||
orderSel = @selector(compareByTotal:);
|
||||
break;
|
||||
case (OrderByPeak):
|
||||
orderSel = @selector(compareByPeak:);
|
||||
break;
|
||||
}
|
||||
|
||||
array_imm = [array sortedArrayUsingSelector: orderSel];
|
||||
RELEASE (array);
|
||||
|
||||
count = [array_imm count];
|
||||
for (i = 0; i < count; i++)
|
||||
{
|
||||
entry = [array_imm objectAtIndex: i];
|
||||
[counts addObject: [entry count]];
|
||||
[totals addObject: [entry total]];
|
||||
[peaks addObject: [entry peak]];
|
||||
[classes addObject: [entry string]];
|
||||
}
|
||||
|
||||
ASSIGN (classArray, classes);
|
||||
RELEASE (classes);
|
||||
|
||||
ASSIGN (countArray, counts);
|
||||
RELEASE (counts);
|
||||
|
||||
ASSIGN (totalArray, totals);
|
||||
RELEASE (totals);
|
||||
|
||||
ASSIGN (peakArray, peaks);
|
||||
RELEASE (peaks);
|
||||
|
||||
[table reloadData];
|
||||
}
|
||||
|
||||
- (void) reorder: (id)sender
|
||||
{
|
||||
int selectedColumn = [table clickedColumn];
|
||||
NSArray *tableColumns = [table tableColumns];
|
||||
id identifier;
|
||||
int newOrderingBy = 0;
|
||||
|
||||
if (selectedColumn == -1)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
identifier = [[tableColumns objectAtIndex: selectedColumn] identifier];
|
||||
|
||||
if ([identifier isEqual: @"Class"])
|
||||
{
|
||||
newOrderingBy = OrderByClassName;
|
||||
}
|
||||
else if ([identifier isEqual: @"Count"])
|
||||
{
|
||||
newOrderingBy = OrderByCount;
|
||||
}
|
||||
else if ([identifier isEqual: @"Total"])
|
||||
{
|
||||
newOrderingBy = OrderByTotal;
|
||||
}
|
||||
else if ([identifier isEqual: @"Peak"])
|
||||
{
|
||||
newOrderingBy = OrderByPeak;
|
||||
}
|
||||
|
||||
if (newOrderingBy == orderingBy)
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
orderingBy = newOrderingBy;
|
||||
[self update: self];
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation NSApplication (memoryPanel)
|
||||
|
||||
- (void) orderFrontSharedMemoryPanel: (id)sender
|
||||
{
|
||||
GSMemoryPanel *memoryPanel;
|
||||
|
||||
memoryPanel = [GSMemoryPanel sharedMemoryPanel];
|
||||
[memoryPanel update: self];
|
||||
[memoryPanel orderFront: self];
|
||||
}
|
||||
|
||||
@end
|
Loading…
Reference in a new issue