]>
git.saurik.com Git - wxWidgets.git/blob - src/os2/menu.cpp
fa4a093a6bc035b5f778724c01b7127ee92824a8
1 /////////////////////////////////////////////////////////////////////////////
3 // Purpose: wxMenu, wxMenuBar, wxMenuItem
4 // Author: David Webster
8 // Copyright: (c) David Webster
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
13 #pragma implementation "menu.h"
16 // For compilers that support precompilation, includes "wx.h".
17 #include "wx/wxprec.h"
29 #include "wx/ownerdrw.h"
32 #include "wx/os2/private.h"
34 // other standard headers
37 // ----------------------------------------------------------------------------
39 // ----------------------------------------------------------------------------
41 extern wxMenu
* wxCurrentPopupMenu
;
43 // ----------------------------------------------------------------------------
45 // ----------------------------------------------------------------------------
48 // The (popup) menu title has this special id
50 static const int idMenuTitle
= -2;
53 // The unique ID for Menus
56 USHORT
wxMenu::m_nextMenuId
= 0;
58 static USHORT
wxMenu::m_nextMenuId
= 0;
61 // ----------------------------------------------------------------------------
63 // ----------------------------------------------------------------------------
65 IMPLEMENT_DYNAMIC_CLASS(wxMenu
, wxEvtHandler
)
66 IMPLEMENT_DYNAMIC_CLASS(wxMenuBar
, wxEvtHandler
)
68 // ----------------------------------------------------------------------------
69 // static function for translating menu labels
70 // ----------------------------------------------------------------------------
72 static wxString
TextToLabel(const wxString
& rTitle
)
76 for (pc
= rTitle
.c_str(); *pc
!= wxT('\0'); pc
++ )
80 if (*(pc
+1) == wxT('&'))
90 if ( *pc
== wxT('~') )
92 // tildes must be doubled to prevent them from being
93 // interpreted as accelerator character prefix by PM ???
102 // ============================================================================
104 // ============================================================================
106 // ---------------------------------------------------------------------------
107 // wxMenu construction, adding and removing menu items
108 // ---------------------------------------------------------------------------
111 // Construct a menu with optional title (then use append)
118 // Create the menu (to be used as a submenu or a popup)
120 if ((m_hMenu
= ::WinCreateWindow( HWND_DESKTOP
135 wxLogLastError("WinLoadMenu");
137 m_vMenuData
.iPosition
= 0;
138 m_vMenuData
.afStyle
= MIS_SUBMENU
| MIS_TEXT
;
139 m_vMenuData
.afAttribute
= (USHORT
)0;
140 m_vMenuData
.id
= m_nextMenuId
++;
141 m_vMenuData
.hwndSubMenu
= m_hMenu
;
142 m_vMenuData
.hItem
= NULLHANDLE
;
145 // If we have a title, insert it in the beginning of the menu
147 if (!m_title
.IsEmpty())
154 } // end of wxMenu::Init
157 // The wxWindow destructor will take care of deleting the submenus.
162 // We should free PM resources only if PM doesn't do it for us
163 // which happens if we're attached to a menubar or a submenu of another
165 if (!IsAttached() && !GetParent())
167 if (!::WinDestroyWindow((HWND
)GetHmenu()) )
169 wxLogLastError("WinDestroyWindow");
177 #if (!(defined(__VISAGECPP__) && (__IBMCPP__ < 400 || __IBMC__ < 400 )))
178 WX_CLEAR_ARRAY(m_vAccels
);
180 #endif // wxUSE_ACCEL
181 } // end of wxMenu::~wxMenu
185 // this will take effect during the next call to Append()
187 } // end of wxMenu::Break
191 int wxMenu::FindAccel(
196 size_t nCount
= m_vAccels
.GetCount();
198 for (n
= 0; n
< nCount
; n
++)
200 if (m_vAccels
[n
]->m_command
== nId
)
204 } // end of wxMenu::FindAccel
206 void wxMenu::UpdateAccel(
211 // Find the (new) accel for this item
213 wxAcceleratorEntry
* pAccel
= wxGetAccelFromString(pItem
->GetText());
216 pAccel
->m_command
= pItem
->GetId();
221 int n
= FindAccel(pItem
->GetId());
223 if (n
== wxNOT_FOUND
)
226 // No old, add new if any
229 m_vAccels
.Add(pAccel
);
231 return; // skipping RebuildAccelTable() below
236 // Replace old with new or just remove the old one if no new
241 m_vAccels
[n
] = pAccel
;
243 m_vAccels
.RemoveAt(n
);
248 m_menuBar
->RebuildAccelTable();
250 } // wxMenu::UpdateAccel
252 #endif // wxUSE_ACCEL
255 // Append a new item or submenu to the menu
257 bool wxMenu::DoInsertOrAppend(
267 #endif // wxUSE_ACCEL
270 // rItem is the member MENUITEM for the menu items and the submenu's
271 // MENUITEM for submenus as required by ::MM_INSERTITEM message API
274 wxMenu
* pSubmenu
= pItem
->GetSubMenu();
275 MENUITEM
& rItem
= (pSubmenu
!= NULL
)?pSubmenu
->m_vMenuData
:
279 wxASSERT_MSG(pSubmenu
->GetHMenu(), wxT("invalid submenu"));
280 pSubmenu
->SetParent(this);
281 rItem
.afStyle
|= MIS_SUBMENU
| MIS_TEXT
;
285 // If "Break" has just been called, insert a menu break before this item
286 // (and don't forget to reset the flag)
290 rItem
.afStyle
|= MIS_BREAK
;
294 if (pItem
->IsSeparator())
296 rItem
.afStyle
|= MIS_SEPARATOR
;
300 // Id is the numeric id for normal menu items and HMENU for submenus as
301 // required by ::MM_INSERTITEM message API
304 if (pSubmenu
!= NULL
)
306 wxASSERT_MSG(pSubmenu
->GetHMenu(), wxT("invalid submenu"));
307 pSubmenu
->SetParent(this);
309 rItem
.iPosition
= 0; // submenus have a 0 position
310 rItem
.id
= (USHORT
)pSubmenu
->GetHMenu();
311 rItem
.afStyle
|= MIS_SUBMENU
| MIS_TEXT
;
315 rItem
.id
= pItem
->GetId();
320 #if wxUSE_OWNER_DRAWN
321 if (pItem
->IsOwnerDrawn())
324 // Want to get {Measure|Draw}Item messages?
325 // item draws itself, passing pointer to data doesn't work in OS/2
326 // Will eventually need to set the image handle somewhere into vItem.hItem
328 rItem
.afStyle
|= MIS_OWNERDRAW
;
330 rItem
.hItem
= (HBITMAP
)pItem
->GetBitmap().GetHBITMAP();
331 pItem
->m_vMenuData
.afStyle
= rItem
.afStyle
;
332 pItem
->m_vMenuData
.hItem
= rItem
.hItem
;
338 // Menu is just a normal string (passed in data parameter)
340 rItem
.afStyle
|= MIS_TEXT
;
341 pData
= (char*)pItem
->GetText().c_str();
344 if (nPos
== (size_t)-1)
346 rItem
.iPosition
= MIT_END
;
350 rItem
.iPosition
= nPos
;
355 rc
= (APIRET
)::WinSendMsg( GetHmenu()
360 #if wxUSE_OWNER_DRAWN
361 if (pItem
->IsOwnerDrawn())
366 ::WinSendMsg( GetHmenu()
368 ,MPFROM2SHORT( (USHORT
)pItem
->GetId()
375 if (rc
== MIT_MEMERROR
|| rc
== MIT_ERROR
)
377 vError
= ::WinGetLastError(vHabmain
);
378 sError
= wxPMErrorToStr(vError
);
379 wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError
);
380 wxLogLastError("Insert or AppendMenu");
386 // If we're already attached to the menubar, we must update it
388 if (IsAttached() && m_menuBar
->IsAttached())
390 m_menuBar
->Refresh();
395 } // end of wxMenu::DoInsertOrAppend
397 bool wxMenu::DoAppend(
401 return wxMenuBase::DoAppend(pItem
) && DoInsertOrAppend(pItem
);
404 bool wxMenu::DoInsert(
409 return ( wxMenuBase::DoInsert( nPos
411 DoInsertOrAppend( pItem
414 } // end of wxMenu::DoInsert
416 wxMenuItem
* wxMenu::DoRemove(
421 // We need to find the items position in the child list
424 wxMenuItemList::Node
* pNode
= GetMenuItems().GetFirst();
426 for (nPos
= 0; pNode
; nPos
++)
428 if (pNode
->GetData() == pItem
)
430 pNode
= pNode
->GetNext();
434 // DoRemove() (unlike Remove) can only be called for existing item!
436 wxCHECK_MSG(pNode
, NULL
, wxT("bug in wxMenu::Remove logic"));
440 // Remove the corresponding accel from the accel table
442 int n
= FindAccel(pItem
->GetId());
444 if (n
!= wxNOT_FOUND
)
447 m_vAccels
.RemoveAt(n
);
450 #endif // wxUSE_ACCEL
452 // Remove the item from the menu
454 ::WinSendMsg( GetHmenu()
456 ,MPFROM2SHORT(pItem
->GetId(), TRUE
)
459 if (IsAttached() && m_menuBar
->IsAttached())
462 // Otherwise, the chane won't be visible
464 m_menuBar
->Refresh();
468 // And from internal data structures
470 return wxMenuBase::DoRemove(pItem
);
471 } // end of wxMenu::DoRemove
473 // ---------------------------------------------------------------------------
474 // accelerator helpers
475 // ---------------------------------------------------------------------------
480 // Create the wxAcceleratorEntries for our accels and put them into provided
481 // array - return the number of accels we have
483 size_t wxMenu::CopyAccels(
484 wxAcceleratorEntry
* pAccels
487 size_t nCount
= GetAccelCount();
489 for (size_t n
= 0; n
< nCount
; n
++)
491 *pAccels
++ = *m_vAccels
[n
];
494 } // end of wxMenu::CopyAccels
496 #endif // wxUSE_ACCEL
498 // ---------------------------------------------------------------------------
500 // ---------------------------------------------------------------------------
502 void wxMenu::SetTitle(
503 const wxString
& rLabel
506 bool bHasNoTitle
= m_title
.IsEmpty();
507 HWND hMenu
= GetHmenu();
512 if (!rLabel
.IsEmpty())
514 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
516 wxLogLastError("SetMenuTitle");
522 if (rLabel
.IsEmpty() )
524 ::WinSendMsg( GetHmenu()
526 ,MPFROM2SHORT(hMenu
, TRUE
)
535 if (!::WinSetWindowText(hMenu
, rLabel
.c_str()))
537 wxLogLastError("SetMenuTitle");
541 } // end of wxMenu::SetTitle
543 // ---------------------------------------------------------------------------
545 // ---------------------------------------------------------------------------
547 bool wxMenu::OS2Command(
548 WXUINT
WXUNUSED(uParam
)
553 // Ignore commands from the menu title
556 if (vId
!= (WXWORD
)idMenuTitle
)
559 ,(int)::WinSendMsg( GetHmenu()
567 } // end of wxMenu::OS2Command
569 // ---------------------------------------------------------------------------
571 // ---------------------------------------------------------------------------
573 wxWindow
* wxMenu::GetWindow() const
575 if (m_invokingWindow
!= NULL
)
576 return m_invokingWindow
;
577 else if ( m_menuBar
!= NULL
)
578 return m_menuBar
->GetFrame();
581 } // end of wxMenu::GetWindow
583 // recursive search for item by id
584 wxMenuItem
* wxMenu::FindItem(
587 , wxMenu
** ppItemMenu
593 wxMenuItem
* pItem
= NULL
;
595 for ( wxMenuItemList::Node
*node
= m_items
.GetFirst();
597 node
= node
->GetNext() )
599 pItem
= node
->GetData();
601 if ( pItem
->GetId() == nItemId
&& pItem
->m_vMenuData
.hItem
== hItem
)
604 *ppItemMenu
= (wxMenu
*)this;
606 else if ( pItem
->IsSubMenu() )
608 pItem
= pItem
->GetSubMenu()->FindItem( nItemId
617 // don't exit the loop
622 } // end of wxMenu::FindItem
624 // ---------------------------------------------------------------------------
626 // ---------------------------------------------------------------------------
628 void wxMenuBar::Init()
630 m_eventHandler
= this;
631 m_menuBarFrame
= NULL
;
633 } // end of wxMenuBar::Init
635 wxMenuBar::wxMenuBar()
638 } // end of wxMenuBar::wxMenuBar
640 wxMenuBar::wxMenuBar(
641 long WXUNUSED(lStyle
)
645 } // end of wxMenuBar::wxMenuBar
647 wxMenuBar::wxMenuBar(
650 , const wxString sTitles
[]
655 m_titles
.Alloc(nCount
);
656 for ( int i
= 0; i
< nCount
; i
++ )
658 m_menus
.Append(vMenus
[i
]);
659 m_titles
.Add(sTitles
[i
]);
660 vMenus
[i
]->Attach(this);
662 } // end of wxMenuBar::wxMenuBar
664 wxMenuBar::~wxMenuBar()
666 } // end of wxMenuBar::~wxMenuBar
668 // ---------------------------------------------------------------------------
670 // ---------------------------------------------------------------------------
672 void wxMenuBar::Refresh()
674 wxCHECK_RET( IsAttached(), wxT("can't refresh unatteched menubar") );
676 WinSendMsg(GetWinHwnd(m_menuBarFrame
), WM_UPDATEFRAME
, (MPARAM
)FCF_MENU
, (MPARAM
)0);
677 } // end of wxMenuBar::Refresh
679 WXHMENU
wxMenuBar::Create()
687 wxCHECK_MSG(!m_hMenu
, TRUE
, wxT("menubar already created"));
690 // Menubars should be associated with a frame otherwise they are popups
692 if (m_menuBarFrame
!= NULL
)
693 hFrame
= GetWinHwnd(m_menuBarFrame
);
695 hFrame
= HWND_DESKTOP
;
697 // Create an empty menu and then fill it with insertions
699 if ((m_hMenu
= ::WinCreateWindow( hFrame
702 ,MS_ACTIONBAR
| WS_SYNCPAINT
| WS_VISIBLE
714 wxLogLastError("WinLoadMenu");
718 size_t nCount
= GetMenuCount();
720 for (size_t i
= 0; i
< nCount
; i
++)
728 // Set the parent and owner of the submenues to be the menubar, not the desktop
730 hSubMenu
= m_menus
[i
]->m_vMenuData
.hwndSubMenu
;
731 if (!::WinSetParent(m_menus
[i
]->m_vMenuData
.hwndSubMenu
, m_hMenu
, FALSE
))
733 vError
= ::WinGetLastError(vHabmain
);
734 sError
= wxPMErrorToStr(vError
);
735 wxLogError("Error setting parent for submenu. Error: %s\n", sError
);
739 if (!::WinSetOwner(m_menus
[i
]->m_vMenuData
.hwndSubMenu
, m_hMenu
))
741 vError
= ::WinGetLastError(vHabmain
);
742 sError
= wxPMErrorToStr(vError
);
743 wxLogError("Error setting parent for submenu. Error: %s\n", sError
);
747 m_menus
[i
]->m_vMenuData
.iPosition
= i
;
749 rc
= (APIRET
)::WinSendMsg(m_hMenu
, MM_INSERTITEM
, (MPARAM
)&m_menus
[i
]->m_vMenuData
, (MPARAM
)m_titles
[i
].c_str());
750 if (rc
== MIT_MEMERROR
|| rc
== MIT_ERROR
)
752 vError
= ::WinGetLastError(vHabmain
);
753 sError
= wxPMErrorToStr(vError
);
754 wxLogError("Error inserting or appending a menuitem. Error: %s\n", sError
);
760 } // end of wxMenuBar::Create
762 // ---------------------------------------------------------------------------
763 // wxMenuBar functions to work with the top level submenus
764 // ---------------------------------------------------------------------------
767 // NB: we don't support owner drawn top level items for now, if we do these
768 // functions would have to be changed to use wxMenuItem as well
770 void wxMenuBar::EnableTop(
775 wxCHECK_RET(IsAttached(), wxT("doesn't work with unattached menubars"));
780 uFlag
= MIA_DISABLED
;
782 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
783 if (nId
== MIT_ERROR
)
785 wxLogLastError("LogLastError");
788 ::WinSendMsg((HWND
)m_hMenu
, MM_SETITEMATTR
, MPFROM2SHORT(nId
, TRUE
), MPFROM2SHORT(MIA_DISABLED
, uFlag
));
790 } // end of wxMenuBar::EnableTop
792 void wxMenuBar::SetLabelTop(
794 , const wxString
& rLabel
800 wxCHECK_RET(nPos
< GetMenuCount(), wxT("invalid menu index"));
801 m_titles
[nPos
] = rLabel
;
808 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
809 if (nId
== MIT_ERROR
)
811 wxLogLastError("LogLastError");
814 if(!::WinSendMsg( (HWND
)m_hMenu
816 ,MPFROM2SHORT(nId
, TRUE
)
820 wxLogLastError("QueryItem");
824 if (::WinSendMsg(GetHmenu(), MM_SETITEMTEXT
, MPFROMSHORT(nId
), (MPARAM
)rLabel
.c_str()));
826 wxLogLastError("ModifyMenu");
829 } // end of wxMenuBar::SetLabelTop
831 wxString
wxMenuBar::GetLabelTop(
835 wxCHECK_MSG( nPos
< GetMenuCount(), wxEmptyString
,
836 wxT("invalid menu index in wxMenuBar::GetLabelTop") );
837 return m_titles
[nPos
];
838 } // end of wxMenuBar::GetLabelTop
840 // ---------------------------------------------------------------------------
841 // wxMenuBar construction
842 // ---------------------------------------------------------------------------
844 wxMenu
* wxMenuBar::Replace(
847 , const wxString
& rTitle
851 wxString Title
= TextToLabel(rTitle
);
852 wxMenu
* pMenuOld
= wxMenuBarBase::Replace( nPos
858 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)m_hMenu
, MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
859 if (nId
== MIT_ERROR
)
861 wxLogLastError("LogLastError");
866 m_titles
[nPos
] = Title
;
869 ::WinSendMsg((HWND
)m_hMenu
, MM_REMOVEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
870 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)Title
.c_str());
873 if (pMenuOld
->HasAccels() || pMenu
->HasAccels())
876 // Need to rebuild accell table
880 #endif // wxUSE_ACCEL
884 } // end of wxMenuBar::Replace
886 bool wxMenuBar::Insert(
889 , const wxString
& rTitle
892 wxString Title
= TextToLabel(rTitle
);
893 if (!wxMenuBarBase::Insert( nPos
899 m_titles
.Insert( Title
905 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)Title
.c_str());
907 if (pMenu
->HasAccels())
909 // need to rebuild accell table
912 #endif // wxUSE_ACCEL
916 } // end of wxMenuBar::Insert
918 bool wxMenuBar::Append(
920 , const wxString
& rTitle
923 WXHMENU hSubmenu
= pMenu
? pMenu
->GetHMenu() : 0;
925 wxCHECK_MSG(hSubmenu
, FALSE
, wxT("can't append invalid menu to menubar"));
927 wxString Title
= TextToLabel(rTitle
);
928 if (!wxMenuBarBase::Append(pMenu
, Title
))
935 pMenu
->m_vMenuData
.iPosition
= MIT_END
;
936 ::WinSendMsg((HWND
)m_hMenu
, MM_INSERTITEM
, (MPARAM
)&pMenu
->m_vMenuData
, (MPARAM
)Title
.c_str());
938 if (pMenu
->HasAccels())
941 // Need to rebuild accell table
945 #endif // wxUSE_ACCEL
949 } // end of wxMenuBar::Append
951 wxMenu
* wxMenuBar::Remove(
955 wxMenu
* pMenu
= wxMenuBarBase::Remove(nPos
);
961 nId
= SHORT1FROMMR(::WinSendMsg((HWND
)GetHmenu(), MM_ITEMIDFROMPOSITION
, MPFROMSHORT(nPos
), (MPARAM
)0));
962 if (nId
== MIT_ERROR
)
964 wxLogLastError("LogLastError");
969 ::WinSendMsg((HWND
)GetHmenu(), MM_REMOVEITEM
, MPFROM2SHORT(nId
, TRUE
), (MPARAM
)0);
972 if (pMenu
->HasAccels())
975 // Need to rebuild accell table
979 #endif // wxUSE_ACCEL
982 m_titles
.Remove(nPos
);
984 } // end of wxMenuBar::Remove
988 void wxMenuBar::RebuildAccelTable()
991 // Merge the accelerators of all menus into one accel table
993 size_t nAccelCount
= 0;
995 size_t nCount
= GetMenuCount();
997 for (i
= 0; i
< nCount
; i
++)
999 nAccelCount
+= m_menus
[i
]->GetAccelCount();
1004 wxAcceleratorEntry
* pAccelEntries
= new wxAcceleratorEntry
[nAccelCount
];
1007 for (i
= 0; i
< nCount
; i
++)
1009 nAccelCount
+= m_menus
[i
]->CopyAccels(&pAccelEntries
[nAccelCount
]);
1011 m_vAccelTable
= wxAcceleratorTable( nAccelCount
1014 delete [] pAccelEntries
;
1016 } // end of wxMenuBar::RebuildAccelTable
1018 #endif // wxUSE_ACCEL
1020 void wxMenuBar::Attach(
1024 wxASSERT_MSG( !IsAttached(), wxT("menubar already attached!") );
1027 RebuildAccelTable();
1029 // Ensure the accelerator table is set to the frame (not the client!)
1031 if (!::WinSetAccelTable( vHabmain
1032 ,(HWND
)pFrame
->GetHWND()
1033 ,m_vAccelTable
.GetHACCEL()
1035 wxLogLastError("WinSetAccelTable");
1036 #endif // wxUSE_ACCEL
1037 } // end of wxMenuBar::Attach
1039 void wxMenuBar::Detach()
1041 ::WinDestroyWindow((HWND
)m_hMenu
);
1042 m_hMenu
= (WXHMENU
)NULL
;
1043 m_menuBarFrame
= NULL
;
1044 } // end of wxMenuBar::Detach
1046 // ---------------------------------------------------------------------------
1047 // wxMenuBar searching for menu items
1048 // ---------------------------------------------------------------------------
1051 // Find the itemString in menuString, and return the item id or wxNOT_FOUND
1053 int wxMenuBar::FindMenuItem(
1054 const wxString
& rMenuString
1055 , const wxString
& rItemString
1058 wxString sMenuLabel
= wxStripMenuCodes(rMenuString
);
1059 size_t nCount
= GetMenuCount();
1061 for (size_t i
= 0; i
< nCount
; i
++)
1063 wxString sTitle
= wxStripMenuCodes(m_titles
[i
]);
1065 if (rMenuString
== sTitle
)
1066 return m_menus
[i
]->FindItem(rItemString
);
1069 } // end of wxMenuBar::FindMenuItem
1071 wxMenuItem
* wxMenuBar::FindItem(
1073 , wxMenu
** ppItemMenu
1079 wxMenuItem
* pItem
= NULL
;
1080 size_t nCount
= GetMenuCount();
1082 for (size_t i
= 0; !pItem
&& (i
< nCount
); i
++)
1084 pItem
= m_menus
[i
]->FindItem( nId
1089 } // end of wxMenuBar::FindItem
1091 wxMenuItem
* wxMenuBar::FindItem(
1094 , wxMenu
** ppItemMenu
1100 wxMenuItem
* pItem
= NULL
;
1101 size_t nCount
= GetMenuCount();
1103 for (size_t i
= 0; !pItem
&& (i
< nCount
); i
++)
1105 pItem
= m_menus
[i
]->FindItem( nId
1111 } // end of wxMenuBar::FindItem