Release 960606
Wed Jun 5 20:13:54 1996 Alexandre Julliard <julliard@lrc.epfl.ch>
* [controls/button.c] [controls/listbox.c]
Fixed wParam of WM_DRAWITEM message.
* [if1632/Makefile.in] [loader/builtin.c]
Remove WPROCS32 DLL, as the relay code can call Wine routines
directly.
* [loader/module.c] [loader/ne_image.c]
Fixed initial stack layout for self-loading modules.
* [tools/build.c]
Fixed data segment building for Win16 modules.
* [windows/defdlg.c]
Implemented Win32 versions of DefDlgProc().
* [windows/dialog.c]
Merged Win16 and Win32 dialog code.
Added support for control extra data in dialog item template.
* [windows/win.c]
Unified Win16 and Win32 versions of CreateWindow().
Implemented Win32 version of GetWindowLong().
* [windows/winproc.c]
Changed the implementation of window procedures, so that 16-bit
winprocs are valid segmented pointers.
Sun Jun 2 16:39:46 1996 Marcus Meissner <msmeissn@cip.informatik.uni-erlangen.de>
* [misc/registry.c]
Fixed another bug in the w95 loader. Quietened some debug output.
Sun Jun 2 10:00:22 1996 Ulrich Schmid <uschmid@mail.hh.provi.de>
* [windows/winproc.c]
Bug fix: WM_PARENTNOTIFY: don't fall through to WM_SETTEXT.
Sat Jun 1 12:37:22 1996 Tristan Tarrant <tst@sthinc.demon.co.uk>
* [resources/TODO] [resources/sysres_It.rc]
Updated font dialog box.
Thu May 30 21:05:19 1996 Albrecht Kleine <kleine@ak.sax.de>
* [include/commdlg.h] [misc/commdlg.c]
ChooseFont() and ChooseColor():
Bugfixes and added more support for some CF_* and CC_* flags:
dialog templates and font size control.
Bugfix in structure definition of CHOOSECOLOR definition.
* [ipc/dde_proc.c] [windows/event.c]
Replaced SendMessage with SendMessage16 and added inclusion of
dde_proc.h for error-free compilation of ipc module.
Thu May 30 19:00:00 1996 Alex Korobka <alex@phm30.pharm.sunysb.edu>
* [windows/scroll.c]
Made ScrollDC to save/restore current clipping region.
* [misc/clipboard.c] [windows/event.c]
Implemented most of the previously missing features (not tested),
improved text pasting from/to X.
* [if1632/user.spec] [if1632/gdi.spec] [objects/dc.c]
[objects/gdiobj.c] [objects/clipping.c] [windows/dce.c]
[windows/winpos.c] [windows/painting.c]
Updated DCE code, implemented dynamic invalidation of owned DCs.
This fixes a lot of problems with scrolling in WinWord. Not
sure about the effect on -desktop.
Wed May 29 23:35:44 1996 Jukka Iivonen <iivonen@cc.helsinki.fi>
* [win32/time.c] [if1632/kernel32.spec]
Added SetSystemTime and SetTimeZoneInformation.
* [if1632/kernel32.spec]
Added lstrcat, lstrcatA, lstrcmp, lstrcmpA, lstrcpy, lstrlen.
* [include/windows.h]
Added SYSTEM_POWER_STATUS structure and prototypes for
GetSystemPowerStatus, SetSystemPowerState, SetSystemTime.
* [include/kernel32.h]
Added a prototype for SetTimeZoneInformation.
* [win32/environment.c] [if1632/kernel32.spec]
Added GetSystemPowerStatus and SetSystemPowerState stubs.
diff --git a/objects/bitblt.c b/objects/bitblt.c
index 2805a10..9db1ceb 100644
--- a/objects/bitblt.c
+++ b/objects/bitblt.c
@@ -35,6 +35,8 @@
#define MAX_OP_LEN 6 /* Longest opcode + 1 for the terminating 0 */
+extern void CLIPPING_UpdateGCRegion(DC* );
+
static const unsigned char BITBLT_Opcodes[256][MAX_OP_LEN] =
{
{ OP(PAT,DST,GXclear) }, /* 0x00 0 */
@@ -1031,6 +1033,9 @@
useDst = (((rop >> 1) & 0x550000) != (rop & 0x550000));
if (!dcSrc && useSrc) return FALSE;
+ if (dcDst->w.flags & DC_DIRTY) CLIPPING_UpdateGCRegion( dcDst );
+ if (dcSrc && (dcSrc->w.flags & DC_DIRTY)) CLIPPING_UpdateGCRegion( dcSrc );
+
/* Map the coordinates to device coords */
xDst = dcDst->w.DCOrgX + XLPTODP( dcDst, xDst );
@@ -1270,7 +1275,7 @@
dcSrc = (DC *) GDI_GetObjPtr( hdcSrc, DC_MAGIC );
dprintf_bitblt(stddeb,
- "BitBlt: %04x %d,%d %d bpp -> %04x %d,%d %dx%dx%d rop=%06lx\n",
+ "BitBlt: hdcSrc=%04x %d,%d %d bpp -> hdcDest=%04x %d,%d %dx%dx%d rop=%06lx\n",
hdcSrc, xSrc, ySrc, dcSrc ? dcSrc->w.bitsPerPixel : 0,
hdcDst, xDst, yDst, width, height, dcDst->w.bitsPerPixel, rop);
diff --git a/objects/bitmap.c b/objects/bitmap.c
index a028713..870667e 100644
--- a/objects/bitmap.c
+++ b/objects/bitmap.c
@@ -122,11 +122,17 @@
*/
HBITMAP CreateCompatibleBitmap( HDC hdc, INT width, INT height )
{
- DC * dc;
- dprintf_gdi(stddeb, "CreateCompatibleBitmap: %04x %dx%d\n",
+ HBITMAP hbmpRet = 0;
+ DC * dc;
+ dprintf_gdi(stddeb, "CreateCompatibleBitmap(%04x,%d,%d) = \n",
hdc, width, height );
if (!(dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC ))) return 0;
- return CreateBitmap( width, height, 1, dc->w.bitsPerPixel, NULL );
+
+ hbmpRet = CreateBitmap( width, height, 1, dc->w.bitsPerPixel, NULL );
+
+ dprintf_gdi(stddeb,"\t\t%04x\n", hbmpRet);
+
+ return hbmpRet;
}
@@ -278,7 +284,7 @@
/***********************************************************************
* BITMAP_SelectObject
*/
-HBITMAP BITMAP_SelectObject( HDC hdc, DC * dc, HBITMAP hbitmap,
+HBITMAP BITMAP_SelectObject( DC * dc, HBITMAP hbitmap,
BITMAPOBJ * bmp )
{
HRGN hrgn;
@@ -300,7 +306,7 @@
XFreeGC( display, dc->u.x.gc );
dc->u.x.gc = XCreateGC( display, dc->u.x.drawable, 0, NULL );
dc->w.bitsPerPixel = bmp->bitmap.bmBitsPixel;
- DC_InitDC( hdc );
+ DC_InitDC( dc );
}
else CLIPPING_UpdateGCRegion( dc ); /* Just update GC clip region */
return prevHandle;
diff --git a/objects/brush.c b/objects/brush.c
index e5ee9b0..a1780ba 100644
--- a/objects/brush.c
+++ b/objects/brush.c
@@ -334,14 +334,14 @@
/***********************************************************************
* BRUSH_SelectObject
*/
-HBRUSH BRUSH_SelectObject( HDC hdc, DC * dc, HBRUSH hbrush, BRUSHOBJ * brush )
+HBRUSH BRUSH_SelectObject( DC * dc, HBRUSH hbrush, BRUSHOBJ * brush )
{
HBITMAP hBitmap;
BITMAPINFO * bmpInfo;
HBRUSH prevHandle = dc->w.hBrush;
dprintf_gdi(stddeb, "Brush_SelectObject: hdc=%04x hbrush=%04x\n",
- hdc,hbrush);
+ dc->hSelf,hbrush);
if (dc->header.wMagic == METAFILE_DC_MAGIC)
{
switch (brush->logbrush.lbStyle)
@@ -400,7 +400,7 @@
if ((bmpInfo = (BITMAPINFO *) GlobalLock16( (HANDLE)brush->logbrush.lbHatch )))
{
int size = DIB_BitmapInfoSize( bmpInfo, brush->logbrush.lbColor );
- hBitmap = CreateDIBitmap( hdc, &bmpInfo->bmiHeader, CBM_INIT,
+ hBitmap = CreateDIBitmap( dc->hSelf, &bmpInfo->bmiHeader, CBM_INIT,
((char *)bmpInfo) + size, bmpInfo,
(WORD) brush->logbrush.lbColor );
BRUSH_SelectPatternBrush( dc, hBitmap );
diff --git a/objects/clipping.c b/objects/clipping.c
index 7064d03..3683d58 100644
--- a/objects/clipping.c
+++ b/objects/clipping.c
@@ -50,6 +50,13 @@
fprintf( stderr, "UpdateGCRegion: hVisRgn is zero. Please report this.\n" );
exit(1);
}
+
+ if (dc->w.flags & DC_DIRTY)
+ {
+ UpdateDirtyDC(dc);
+ dc->w.flags &= ~DC_DIRTY;
+ }
+
if (!dc->w.hClipRgn)
CombineRgn( dc->w.hGCClipRgn, dc->w.hVisRgn, 0, RGN_COPY );
else
@@ -80,6 +87,7 @@
dc->w.hClipRgn = 0;
retval = SIMPLEREGION; /* Clip region == whole DC */
}
+
CLIPPING_UpdateGCRegion( dc );
return retval;
}
@@ -96,6 +104,8 @@
dprintf_clipping(stddeb, "SelectVisRgn: %04x %04x\n", hdc, hrgn );
+ dc->w.flags &= ~DC_DIRTY;
+
retval = CombineRgn( dc->w.hVisRgn, hrgn, 0, RGN_COPY );
CLIPPING_UpdateGCRegion( dc );
return retval;
@@ -271,6 +281,7 @@
if (!dc) return ERROR;
dprintf_clipping(stddeb, "ExcludeVisRect: %04x %dx%d,%dx%d\n",
hdc, left, top, right, bottom );
+
return CLIPPING_IntersectVisRect( dc, left, top, right, bottom, TRUE );
}
@@ -285,6 +296,7 @@
if (!dc) return ERROR;
dprintf_clipping(stddeb, "IntersectVisRect: %04x %dx%d,%dx%d\n",
hdc, left, top, right, bottom );
+
return CLIPPING_IntersectVisRect( dc, left, top, right, bottom, FALSE );
}
@@ -299,6 +311,10 @@
dprintf_clipping(stddeb, "PtVisible: %04x %d,%d\n", hdc, x, y );
if (!dc->w.hGCClipRgn) return FALSE;
+
+ if( dc->w.flags & DC_DIRTY ) UpdateDirtyDC(dc);
+ dc->w.flags &= ~DC_DIRTY;
+
return PtInRegion( dc->w.hGCClipRgn, XLPTODP(dc,x), YLPTODP(dc,y) );
}
@@ -375,6 +391,9 @@
fprintf( stderr, "SaveVisRgn: hVisRgn is zero. Please report this.\n" );
exit(1);
}
+ if( dc->w.flags & DC_DIRTY ) UpdateDirtyDC(dc);
+ dc->w.flags &= ~DC_DIRTY;
+
if (!(obj = (RGNOBJ *) GDI_GetObjPtr( dc->w.hVisRgn, REGION_MAGIC )))
return 0;
if (!(copy = CreateRectRgn( 0, 0, 0, 0 ))) return 0;
diff --git a/objects/dc.c b/objects/dc.c
index 8094756..6349560 100644
--- a/objects/dc.c
+++ b/objects/dc.c
@@ -14,11 +14,13 @@
#include "color.h"
#include "debug.h"
#include "font.h"
+#include "callback.h"
#include "xmalloc.h"
static DeviceCaps * displayDevCaps = NULL;
extern void CLIPPING_UpdateGCRegion( DC * dc ); /* objects/clipping.c */
+extern BOOL DCHook( HDC, WORD, DWORD, DWORD ); /* windows/dce.c */
/* Default DC values */
static const WIN_DC_INFO DC_defaultValues =
@@ -142,15 +144,14 @@
*
* Setup device-specific DC values for a newly created DC.
*/
-void DC_InitDC( HDC hdc )
+void DC_InitDC( DC* dc )
{
- DC * dc = (DC *) GDI_GetObjPtr( hdc, DC_MAGIC );
- RealizeDefaultPalette( hdc );
- SetTextColor( hdc, dc->w.textColor );
- SetBkColor( hdc, dc->w.backgroundColor );
- SelectObject( hdc, dc->w.hPen );
- SelectObject( hdc, dc->w.hBrush );
- SelectObject( hdc, dc->w.hFont );
+ RealizeDefaultPalette( dc->hSelf );
+ SetTextColor( dc->hSelf, dc->w.textColor );
+ SetBkColor( dc->hSelf, dc->w.backgroundColor );
+ SelectObject( dc->hSelf, dc->w.hPen );
+ SelectObject( dc->hSelf, dc->w.hBrush );
+ SelectObject( dc->hSelf, dc->w.hFont );
XSetGraphicsExposures( display, dc->u.x.gc, False );
XSetSubwindowMode( display, dc->u.x.gc, IncludeInferiors );
CLIPPING_UpdateGCRegion( dc );
@@ -191,6 +192,8 @@
val.background = COLOR_PixelToPalette[val.background];
}
+ if (dc->w.flags & DC_DIRTY) CLIPPING_UpdateGCRegion(dc);
+
val.function = DC_XROPfunction[dc->w.ROPmode-1];
val.fill_style = dc->u.x.brush.fillStyle;
switch(val.fill_style)
@@ -262,6 +265,8 @@
if (dc->u.x.pen.style == PS_NULL) return FALSE;
+ if (dc->w.flags & DC_DIRTY) CLIPPING_UpdateGCRegion(dc);
+
if ((screenDepth <= 8) && /* FIXME: Should check for palette instead */
((dc->w.ROPmode == R2_BLACK) || (dc->w.ROPmode == R2_WHITE)))
{
@@ -309,6 +314,9 @@
fprintf( stderr, "DC_SetupGCForText: fstruct is NULL. Please report this\n" );
return FALSE;
}
+
+ if (dc->w.flags & DC_DIRTY) CLIPPING_UpdateGCRegion(dc);
+
val.function = GXcopy; /* Text is always GXcopy */
val.foreground = dc->w.textPixel;
val.background = dc->w.backgroundPixel;
@@ -322,6 +330,26 @@
/***********************************************************************
+ * DC_CallHookProc
+ */
+BOOL DC_CallHookProc(DC* dc, WORD code, LPARAM lParam)
+{
+ BOOL bRet = 0;
+ FARPROC ptr = GDI_GetDefDCHook();
+
+ dprintf_dc(stddeb,"CallDCHook: code %04x\n", code);
+
+ /* if 16-bit callback is, in fact, a thunk to DCHook simply call DCHook */
+
+ if( dc->hookProc && !(dc->w.flags & (DC_SAVED | DC_MEMORY)) )
+ bRet = (dc->hookProc == ptr) ?
+ DCHook(dc->hSelf, code, dc->dwHookData, lParam):
+ CallDCHookProc(dc->hookProc, dc->hSelf, code, dc->dwHookData, lParam);
+
+ return bRet;
+}
+
+/***********************************************************************
* GetDCState (GDI.179)
*/
HDC GetDCState( HDC hdc )
@@ -338,6 +366,8 @@
memset( &newdc->u.x, 0, sizeof(newdc->u.x) );
memcpy( &newdc->w, &dc->w, sizeof(dc->w) );
memcpy( &newdc->u.x.pen, &dc->u.x.pen, sizeof(dc->u.x.pen) );
+
+ newdc->hSelf = (HDC)handle;
newdc->saveLevel = 0;
newdc->w.flags |= DC_SAVED;
@@ -474,7 +504,11 @@
DC_FillDevCaps( displayDevCaps );
}
+ dc->hSelf = (HDC)handle;
dc->saveLevel = 0;
+ dc->dwHookData = 0L;
+ dc->hookProc = (SEGPTR)NULL;
+
memcpy( &dc->w, &DC_defaultValues, sizeof(DC_defaultValues) );
memset( &dc->u.x, 0, sizeof(dc->u.x) );
@@ -491,7 +525,7 @@
return 0;
}
- DC_InitDC( handle );
+ DC_InitDC( dc );
return handle;
}
@@ -530,8 +564,12 @@
return 0;
}
bmp = (BITMAPOBJ *) GDI_GetObjPtr( hbitmap, BITMAP_MAGIC );
-
+
+ dc->hSelf = (HDC)handle;
dc->saveLevel = 0;
+ dc->dwHookData = 0L;
+ dc->hookProc = (SEGPTR)NULL;
+
memcpy( &dc->w, &DC_defaultValues, sizeof(DC_defaultValues) );
memset( &dc->u.x, 0, sizeof(dc->u.x) );
@@ -551,7 +589,7 @@
return 0;
}
- DC_InitDC( handle );
+ DC_InitDC( dc );
return handle;
}
@@ -713,3 +751,60 @@
dc->w.DCOrgY = y;
return prevOrg;
}
+
+
+/***********************************************************************
+ * SetDCHook (GDI.190)
+ */
+BOOL SetDCHook( HDC hDC, FARPROC16 hookProc, DWORD dwHookData )
+{
+ DC *dc = (DC *)GDI_GetObjPtr( hDC, DC_MAGIC );
+
+ dprintf_dc( stddeb, "SetDCHook: hookProc %08x, default is %08x\n",
+ (unsigned)hookProc,(unsigned)GDI_GetDefDCHook() );
+
+ if (!dc) return FALSE;
+ dc->hookProc = hookProc;
+ dc->dwHookData = dwHookData;
+ return TRUE;
+}
+
+
+/***********************************************************************
+ * GetDCHook (GDI.191)
+ */
+DWORD GetDCHook( HDC hDC, FARPROC16 *phookProc )
+{
+ DC *dc = (DC *)GDI_GetObjPtr( hDC, DC_MAGIC );
+ if (!dc) return 0;
+ *phookProc = dc->hookProc;
+ return dc->dwHookData;
+}
+
+
+/***********************************************************************
+ * SetHookFlags (GDI.192)
+ */
+WORD SetHookFlags(HDC hDC, WORD flags)
+{
+ DC* dc = (DC*)GDI_GetObjPtr( hDC, DC_MAGIC );
+
+ if( dc )
+ {
+ WORD wRet = dc->w.flags & DC_DIRTY;
+
+ /* "Undocumented Windows" info is slightly
+ * confusing
+ */
+
+ dprintf_dc(stddeb,"SetHookFlags: hDC %04x, flags %04x\n",hDC,flags);
+
+ if( flags & DCHF_INVALIDATEVISRGN )
+ dc->w.flags |= DC_DIRTY;
+ else if( flags & DCHF_VALIDATEVISRGN || !flags )
+ dc->w.flags &= ~DC_DIRTY;
+ return wRet;
+ }
+ return 0;
+}
+
diff --git a/objects/dib.c b/objects/dib.c
index 26ef98e..becf185 100644
--- a/objects/dib.c
+++ b/objects/dib.c
@@ -18,6 +18,7 @@
#include "debug.h"
#include "xmalloc.h"
+extern void CLIPPING_UpdateGCRegion(DC* );
/***********************************************************************
* DIB_GetImageWidthBytes
@@ -575,6 +576,8 @@
if (!(colorMapping = DIB_BuildColorMap( dc, coloruse, depth, info )))
return 0;
+ if( dc->w.flags & DC_DIRTY ) CLIPPING_UpdateGCRegion(dc);
+
/* Transfer the pixels */
XCREATEIMAGE(bmpImage, infoWidth, lines, depth );
diff --git a/objects/font.c b/objects/font.c
index ff7c0c6..8f3b4a1 100644
--- a/objects/font.c
+++ b/objects/font.c
@@ -785,7 +785,12 @@
*/
BOOL GetCharABCWidths(HDC hdc, UINT wFirstChar, UINT wLastChar, LPABC lpABC)
{
- /* No TrueType fonts in Wine */
+
+ /* No TrueType fonts in Wine so far */
+
+ fprintf(stdnimp,"STUB: GetCharABCWidths(%04x,%04x,%04x,%08x)\n",
+ hdc,wFirstChar,wLastChar,(unsigned)lpABC);
+
return FALSE;
}
diff --git a/objects/gdiobj.c b/objects/gdiobj.c
index 6af4d94..81c1c16 100644
--- a/objects/gdiobj.c
+++ b/objects/gdiobj.c
@@ -12,6 +12,7 @@
#include "brush.h"
#include "font.h"
#include "heap.h"
+#include "module.h"
#include "palette.h"
#include "pen.h"
#include "region.h"
@@ -152,16 +153,23 @@
(GDIOBJHDR *) &SystemFixedFont
};
+static FARPROC16 defDCHookCallback;
+
/***********************************************************************
* GDI_Init
*
- * GDI initialisation.
+ * GDI initialization.
*/
BOOL GDI_Init(void)
{
HPALETTE16 hpalette;
+ defDCHookCallback = (FARPROC16)MODULE_GetEntryPoint(GetModuleHandle("USER"),
+ 362 /* DCHook */ );
+ dprintf_gdi( stddeb, "DCHook: 16-bit callback is %08x\n",
+ (unsigned)defDCHookCallback );
+
/* Create default palette */
if (!(hpalette = COLOR_Init())) return FALSE;
@@ -171,11 +179,11 @@
if (!BITMAP_Init()) return FALSE;
- /* Initialise brush dithering */
+ /* Initialize brush dithering */
if (!BRUSH_Init()) return FALSE;
- /* Initialise fonts */
+ /* Initialize fonts */
if (!FONT_Init()) return FALSE;
@@ -184,6 +192,15 @@
/***********************************************************************
+ * GDI_GetDefDCHook
+ */
+FARPROC16 GDI_GetDefDCHook(void)
+{
+ return defDCHookCallback;
+}
+
+
+/***********************************************************************
* GDI_AllocObject
*/
HANDLE GDI_AllocObject( WORD size, WORD magic )
@@ -321,7 +338,7 @@
GDIOBJHDR * ptr = NULL;
DC * dc;
- dprintf_gdi(stddeb, "SelectObject: %04x %04x\n", hdc, handle );
+ dprintf_gdi(stddeb, "SelectObject: hdc=%04x %04x\n", hdc, handle );
if ((handle >= FIRST_STOCK_HANDLE) && (handle <= LAST_STOCK_HANDLE))
ptr = StockObjects[handle - FIRST_STOCK_HANDLE];
else
@@ -340,9 +357,9 @@
case PEN_MAGIC:
return PEN_SelectObject( dc, handle, (PENOBJ *)ptr );
case BRUSH_MAGIC:
- return BRUSH_SelectObject( hdc, dc, handle, (BRUSHOBJ *)ptr );
+ return BRUSH_SelectObject( dc, handle, (BRUSHOBJ *)ptr );
case BITMAP_MAGIC:
- return BITMAP_SelectObject( hdc, dc, handle, (BITMAPOBJ *)ptr );
+ return BITMAP_SelectObject( dc, handle, (BITMAPOBJ *)ptr );
case FONT_MAGIC:
return FONT_SelectObject( dc, handle, (FONTOBJ *)ptr );
case REGION_MAGIC:
diff --git a/objects/text.c b/objects/text.c
index 10ef8b4..1d5a5e1 100644
--- a/objects/text.c
+++ b/objects/text.c
@@ -338,9 +338,9 @@
if (!DC_SetupGCForText( dc )) return TRUE;
font = dc->u.x.font.fstruct;
- dprintf_text(stddeb,"ExtTextOut: %d,%d '%*.*s', %d flags=%d\n",
- x, y, count, count, str, count, flags);
- if (lprect != NULL) dprintf_text(stddeb, "rect %d %d %d %d\n",
+ dprintf_text(stddeb,"ExtTextOut: hdc=%04x %d,%d '%*.*s', %d flags=%d\n",
+ hdc, x, y, count, count, str, count, flags);
+ if (lprect != NULL) dprintf_text(stddeb, "\trect=(%d,%d- %d,%d)\n",
lprect->left, lprect->top,
lprect->right, lprect->bottom );
@@ -363,6 +363,9 @@
if (rect.bottom < rect.top) SWAP_INT( rect.top, rect.bottom );
}
+ dprintf_text(stddeb,"\treal coord: x=%i, y=%i, rect=(%d,%d-%d,%d)\n",
+ x, y, rect.left, rect.top, rect.right, rect.bottom);
+
/* Draw the rectangle */
if (flags & ETO_OPAQUE)
@@ -394,6 +397,7 @@
x -= info.width / 2;
break;
}
+
switch( dc->w.textAlign & (TA_TOP | TA_BOTTOM | TA_BASELINE) )
{
case TA_TOP: