vanilla-wow-addons – Rev 1

Subversion Repositories:
Rev:
gCalendarEventEditor_ShowScheduleEditor = false;
gCalendarEventEditor_Active = false;

gCalendarEventEditor_Database = nil;
gCalendarEventEditor_Event = nil;
gCalendarEventEditor_IsNewEvent = false;
gCalendarEventEditor_NewClassLimits = nil;

gCalendarEventEditor_PreviousEventType = nil;

gCalendarEventEditor_CurrentPanel = 1;
gCalendarEventEditor_UsingLocalTime = false;
gCalendarEventEditor_EventDate = nil;
gCalendarEventEditor_EventTime = nil;
gCalendarEventEditor_EventDateIsLocal = false;

gGroupCalendar_cNumAttendanceItems = 12;
gGroupCalendar_cNumAutoConfirmAttendanceItems = 11;
gGroupCalendar_cNumPlainAttendanceItems = 16;
gGroupCalendar_cAttendanceItemHeight = 16;

gCalendarEventEditor_PanelFrames =
{
        "CalendarEventEditorEventFrame",
        "CalendarEventEditorAttendanceFrame",
};

StaticPopupDialogs["CONFIRM_CALENDAR_EVENT_DELETE"] = {
        text = TEXT(CalendarEventEditor_cConfirmDeleteMsg),
        button1 = TEXT(CalendarEventEditor_cDelete),
        button2 = TEXT(CANCEL),
        OnAccept = function() CalendarEventEditor_DeleteEvent(); end,
        timeout = 0,
        whileDead = 1,
        hideOnEscape = 1
};

StaticPopupDialogs["CONFIRM_CALENDAR_RSVP_DELETE"] = {
        text = TEXT(GroupCalendar_cConfrimDeleteRSVP),
        button1 = TEXT(CalendarEventEditor_cDelete),
        button2 = TEXT(CANCEL),
        OnAccept = function() CalendarEventEditor_DeleteRSVP(); end,
        timeout = 0,
        whileDead = 1,
        hideOnEscape = 1
};

StaticPopupDialogs["CONFIRM_CALENDAR_CLEAR_WHISPERS"] = {
        text = TEXT(GroupCalendar_cConfirmClearWhispers),
        button1 = TEXT(GroupCalendar_cClear),
        button2 = TEXT(CANCEL),
        OnAccept = function() CalendarWhisperLog_Clear(); end,
        timeout = 0,
        whileDead = 1,
        hideOnEscape = 1
};

function CalendarEventEditor_EditEvent(pDatabase, pEvent, pIsNewEvent)
        gCalendarEventEditor_Database = pDatabase;
        gCalendarEventEditor_Event = pEvent;
        gCalendarEventEditor_IsNewEvent = pIsNewEvent;
        gCalendarEventEditor_UsingLocalTime = gGroupCalendar_Settings.ShowEventsInLocalTime;
        gCalendarEventEditor_NewClassLimits = nil;
        
        CalendarAttendanceList_SetEvent(CalendarEventEditorAttendance, pDatabase, pEvent);
        
        gCalendarEventEditor_EventDateIsLocal = gCalendarEventEditor_UsingLocalTime;
        
        if pIsNewEvent then
                -- New events have their date in whatever time zone is
                -- selected for display
                
                gCalendarEventEditor_EventDate = pEvent.mDate;
                gCalendarEventEditor_EventTime = pEvent.mTime;
        else
                -- Existing events have their date in server time so
                -- convert to a local date if necessary
                
                if gCalendarEventEditor_EventDateIsLocal then
                        gCalendarEventEditor_EventDate, gCalendarEventEditor_EventTime = Calendar_GetLocalDateTimeFromServerDateTime(pEvent.mDate, pEvent.mTime);
                else
                        gCalendarEventEditor_EventDate = pEvent.mDate;
                        gCalendarEventEditor_EventTime = pEvent.mTime;
                end
        end
        
        CalendarEventEditorFrameSubTitle:SetText(Calendar_GetLongDateString(gCalendarEventEditor_EventDate));
        
        CalendarEventEditor_UpdateControlsFromEvent(pEvent);
        
        ShowUIPanel(CalendarEventEditorFrame);
        
        if pIsNewEvent then
                CalendarEventTitle:SetFocus();
                CalendarEventTitle:HighlightText();
        else
                CalendarEventTitle:HighlightText(0, 0);
        end
        
        gCalendarEventEditor_ShowScheduleEditor = false;
        gCalendarEventEditor_Active = true;
end

function CalendarEventEditor_MajorDatabaseChange()
        if not gCalendarEventEditor_Active then
                return;
        end
        
        if gCalendarEventEditor_UsingLocalTime ~= gGroupCalendar_Settings.ShowEventsInLocalTime then
                gCalendarEventEditor_UsingLocalTime = gGroupCalendar_Settings.ShowEventsInLocalTime;
                
                gCalendarEventEditor_EventTime = CalendarEventEditor_GetTimeControlValue(CalendarEventEditorTime);
                
                if gCalendarEventEditor_UsingLocalTime then
                        gCalendarEventEditor_EventDate, gCalendarEventEditor_EventTime = Calendar_GetLocalDateTimeFromServerDateTime(gCalendarEventEditor_EventDate, gCalendarEventEditor_EventTime);
                else
                        gCalendarEventEditor_EventDate, gCalendarEventEditor_EventTime = Calendar_GetServerDateTimeFromLocalDateTime(gCalendarEventEditor_EventDate, gCalendarEventEditor_EventTime);
                end
                
                CalendarEventEditor_SetTimeControlValue(CalendarEventEditorTime, gCalendarEventEditor_EventTime);
        end
end

function CalendarEventEditor_EventChanged(pEvent)
        if pEvent == gCalendarEventEditor_Event then
                CalendarAttendanceList_EventChanged(CalendarEventEditorAttendance, gCalendarEventEditor_Database, pEvent);
                
                -- Don't update the control values since that'll overwrite whatever
                -- is currently being set up
                -- CalendarEventEditor_UpdateControlsFromEvent(pEvent);
                
                CalendarGroupInvites_EventChanged(pEvent);
        end
end

function CalendarEventEditor_OnLoad()
        -- Tabs
        
        PanelTemplates_SetNumTabs(this, table.getn(gCalendarEventEditor_PanelFrames));
        CalendarEventEditorFrame.selectedTab = gCalendarEventEditor_CurrentPanel;
        PanelTemplates_UpdateTabs(this);
end

function CalendarEventEditor_OnShow()
        PlaySound("igCharacterInfoOpen");
        
        CalendarEventTypeEventType.ChangedValueFunc = CalendarEventEditor_EventTypeChanged;
        
        CalendarEventEditor_ShowPanel(1); -- Always switch to the event view when showing the window
end

function CalendarEventEditor_OnHide()
        PlaySound("igCharacterInfoClose");
        
        CalendarAddPlayer_Cancel(); -- Force the dialog to close if it's open
        
        if not gCalendarEventEditor_ShowScheduleEditor then
                CalendarEventEditor_SaveEvent();
                HideUIPanel(CalendarEditorFrame);
        end
        
        gCalendarEventEditor_Database = nil;
        gCalendarEventEditor_Event = nil;
        gCalendarEventEditor_Active = false;
end

function CalendarEventEditor_DoneEditing()
        if not gCalendarEventEditor_Active then
                return;
        end
        
        CalendarEventEditor_SaveEvent();
        CalendarEventEditor_CloseEditor(true);
end

function CalendarEventEditor_UpdateControlsFromEvent(pEvent, pExistingValuesOnly)
        if not pExistingValuesOnly
        or pEvent.mType then
                CalendarDropDown_SetSelectedValue(CalendarEventTypeEventType, pEvent.mType);
        end

        if pEvent.mTitle and pEvent.mTitle ~= "" then
                CalendarEventTitle:SetText(Calendar_UnescapeString(pEvent.mTitle));
        elseif not pExistingValuesOnly
        or pEvent.mType then
                local   vEventName = EventDatabase_GetEventNameByID(pEvent.mType);
                
                if vEventName ~= nil then
                        CalendarEventTitle:SetText(vEventName);
                else
                        CalendarEventTitle:SetText("");
                end
        end
        
        if not pExistingValuesOnly
        or gCalendarEventEditor_EventTime then
                CalendarEventEditor_SetTimeControlValue(CalendarEventEditorTime, gCalendarEventEditor_EventTime);
        end
        
        if not pExistingValuesOnly
        or pEvent.mDuration then
                CalendarDropDown_SetSelectedValue(CalendarEventEditorDurationDuration, pEvent.mDuration);
        end

        if pEvent.mDescription then
                CalendarEventDescription:SetText(Calendar_UnescapeString(pEvent.mDescription));
        elseif not pExistingValuesOnly then
                CalendarEventDescription:SetText("");
        end

        if pEvent.mMinLevel then
                CalendarEventMinLevel:SetText(pEvent.mMinLevel);
        elseif not pExistingValuesOnly then
                CalendarEventMinLevel:SetText("");
        end

        if pEvent.mMaxLevel then
                CalendarEventMaxLevel:SetText(pEvent.mMaxLevel);
        elseif not pExistingValuesOnly then
                CalendarEventMaxLevel:SetText("");
        end
        
        if not pExistingValuesOnly
        or pEvent.mType then
                gCalendarEventEditor_PreviousEventType = pEvent.mType;
                CalendarEventEditor_SetEventType(pEvent.mType);
        end
end

function CalendarEventEditor_SaveClassLimits(pLimits)
        gCalendarEventEditor_NewClassLimits = pLimits;
end

function CalendarEventEditor_UpdateEventFromControls(rEvent, rChangedFields)
        local   vChanged = false;
        local   vValue;
        
        -- Type
        
        vValue = UIDropDownMenu_GetSelectedValue(CalendarEventTypeEventType);
        
        if rEvent.mType ~= vValue then
                rEvent.mType = vValue;
                rChangedFields.mType = {op = "UPD", val = vValue};
                vChanged = true;
        end
        
        -- Title
        
        vValue = CalendarEventTitle:GetText();
        
        if vValue == EventDatabase_GetEventNameByID(rEvent.mType)
        or vValue == "" then
                vValue = nil;
        else
                vValue = Calendar_EscapeString(vValue);
        end
        
        if rEvent.mTitle ~= vValue then
                rEvent.mTitle = vValue;
                rChangedFields.mTitle = "UPD";
                vChanged = true;
        end
        
        -- Time
        
        local   vDate, vTime = nil, nil;
        
        if EventDatabase_EventTypeUsesTime(rEvent.mType) then
                vDate, vTime = gCalendarEventEditor_EventDate, CalendarEventEditor_GetTimeControlValue(CalendarEventEditorTime);
                
                if gCalendarEventEditor_UsingLocalTime then
                        vDate, vTime = Calendar_GetServerDateTimeFromLocalDateTime(vDate, vTime);
                end
        else
                vDate = gCalendarEventEditor_EventDate;
        end
        
        if rEvent.mDate ~= vDate then
                rEvent.mDate = vDate;
                rChangedFields.mDate = "UPD";
                vChanged = true;
        end
        
        if rEvent.mTime ~= vTime then
                rEvent.mTime = vTime;
                rChangedFields.mTime = "UPD";
                vChanged = true;
        end
        
        -- Duration
        
        if EventDatabase_EventTypeUsesTime(rEvent.mType) then
                vValue = UIDropDownMenu_GetSelectedValue(CalendarEventEditorDurationDuration);
        else
                vValue = nil;
        end
        
        if vValue == 0 then
                vValue = nil;
        end
        
        if rEvent.mDuration ~= vValue then
                rEvent.mDuration = vValue;
                rChangedFields.mDuration = "UPD";
                vChanged = true;
        end
        
        -- Description
        
        vValue = CalendarEventDescription:GetText();
        
        if vValue == "" then
                vValue = nil;
        else
                vValue = Calendar_EscapeString(vValue);
        end
        
        if rEvent.mDescription ~= vValue then
                rEvent.mDescription = vValue;
                rChangedFields.mDescription = "UPD";
                vChanged = true;
        end
        
        -- MinLevel
        
        if EventDatabase_EventTypeUsesLevelLimits(rEvent.mType) then
                vValue = CalendarEventMinLevel:GetText();
        else
                vValue = nil;
        end
        
        if vValue and vValue ~= "" then
                vValue = tonumber(vValue);
                
                if vValue == 0 then
                        vValue = nil;
                end
        else
                vValue = nil;
        end
        
        if rEvent.mMinLevel ~= vValue then
                rEvent.mMinLevel = vValue;
                rChangedFields.mMinLevel = "UPD";
                vChanged = true;
        end
        
        -- MaxLevel
        
        if EventDatabase_EventTypeUsesTime(rEvent.mType) then
                vValue = CalendarEventMaxLevel:GetText();
        else
                vValue = nil;
        end
        
        if vValue and vValue ~= "" then
                vValue = tonumber(vValue);
                
                if vValue == 0 then
                        vValue = nil;
                end
        else
                vValue = nil;
        end
        
        if rEvent.mMaxLevel ~= vValue then
                rEvent.mMaxLevel = vValue;
                rChangedFields.mMaxLevel = "UPD";
                vChanged = true;
        end
        
        -- Automatic confirmations
        
        local   vManualConfirm;
        
        if EventDatabase_IsQuestingEventType(rEvent.mType) then
                vManualConfirm = CalendarAttendanceList_GetManualConfirmEnable(CalendarEventEditorAttendance);
        else
                -- Non-questing events are always auto-confirm (ie meetings)
                
                vManualConfirm = nil;
        end
        
        if vManualConfirm ~= rEvent.mManualConfirm then
                rEvent.mManualConfirm = vManualConfirm;
                rChangedFields.mManualConfirm = {op = "UPD", val = vManualConfirm};
        end
        
        if EventDatabase_IsQuestingEventType(rEvent.mType) then
                if gCalendarEventEditor_NewClassLimits
                and CalendarClassLimits_ClassLimitsChanged(rEvent.mLimits, gCalendarEventEditor_NewClassLimits) then
                        rEvent.mLimits = gCalendarEventEditor_NewClassLimits;
                        rChangedFields.mLimits = {op = "UPD", val = rEvent.mLimits};
                end
        else
                -- No limits on non-questing event types (ie, meetings)
                
                if rEvent.mLimits then
                        rEvent.mLimits = nil;
                        rChangedFields.mLimits = {op = "UPD", val = rEvent.mLimits};
                end
        end
        
        -- Done
        
        return vChanged;
end

function CalendarEventEditor_SetEventType(pEventType)
        -- CalendarEventTypeEventType:Show();
        -- CalendarEventTitle:Show();
        -- CalendarEventDescription:Show();
        
        local   vTitleText = CalendarEventTitle:GetText();
        local   vEventName = EventDatabase_GetEventNameByID(gCalendarEventEditor_PreviousEventType);
        local   vTitleWasEventName = vTitleText == vEventName;
        
        if EventDatabase_EventTypeUsesTime(pEventType) then
                CalendarEventEditorTime:Show();
                CalendarEventEditorDuration:Show();
        else
                CalendarEventEditorTime:Hide();
                CalendarEventEditorDuration:Hide();
        end
        
        if EventDatabase_EventTypeUsesLevelLimits(pEventType) then
                CalendarEventMinLevel:Show();
                CalendarEventMaxLevel:Show();
        else
                CalendarEventMinLevel:Hide();
                CalendarEventMaxLevel:Hide();
        end
        
        if EventDatabase_EventTypeUsesAttendance(pEventType) then
                CalendarEventEditorFrameTab2:Show();
                CalendarEventEditorSelfAttend:Show();
        else
                CalendarEventEditorFrameTab2:Hide();
                CalendarEventEditorSelfAttend:Hide();
        end
        
        if vTitleWasEventName then
                CalendarEventTitle:SetText(EventDatabase_GetEventNameByID(pEventType));
                CalendarEventTitle:SetFocus();
                CalendarEventTitle:HighlightText();
        end
        
        -- Update the sample icon
        
        CalendarEventEditorEventBackground:SetTexture(Calendar_GetEventTypeIconPath(pEventType));
        if pEventType == "Birth" then
                CalendarEventEditorEventBackground:SetVertexColor(1, 1, 1, 0.4);
        else
                CalendarEventEditorEventBackground:SetVertexColor(1, 1, 1, 0.19);
        end

        --
        
        gCalendarEventEditor_PreviousEventType = pEventType;
end

function CalendarEventEditor_DeleteEvent()
        if not gCalendarEventEditor_IsNewEvent then
                EventDatabase_DeleteEvent(gCalendarEventEditor_Database, gCalendarEventEditor_Event);
        end
        
        CalendarEventEditor_CloseEditor(true);
end

function CalendarEventEditor_DeleteRSVP()
        if not gGroupCalendar_RSVPToDelete then
                return;
        end
        
        gGroupCalendar_RSVPToDelete.mStatus = "-";
        
        EventDatabase_AddEventRSVP(
                        gCalendarEventEditor_Database,
                        gCalendarEventEditor_Event,
                        gGroupCalendar_RSVPToDelete.mName,
                        gGroupCalendar_RSVPToDelete);
        
        gGroupCalendar_RSVPToDelete = nil;
end

function CalendarEventEditor_CloseEditor(pShowScheduleEditor)
        gCalendarEventEditor_Active = false;
        
        gCalendarEventEditor_ShowScheduleEditor = pShowScheduleEditor;
        HideUIPanel(CalendarEventEditorFrame);
end

function CalendarEventEditor_CopyTemplateFields(pFromEvent, rToEvent)
        rToEvent.mTitle = pFromEvent.mTitle;
        rToEvent.mDescription = pFromEvent.mDescription;
        rToEvent.mTime = pFromEvent.mTime;
        rToEvent.mDuration = pFromEvent.mDuration;
        rToEvent.mMinLevel = pFromEvent.mMinLevel;
        rToEvent.mMaxLevel = pFromEvent.mMaxLevel;

        rToEvent.mManualConfirm = pFromEvent.mManualConfirm;
        rToEvent.mLimits = pFromEvent.mLimits;
end

function CalendarEventEditor_SaveEvent()
        -- Update the event
        
        local   vChangedFields = {};
        
        CalendarEventEditor_UpdateEventFromControls(gCalendarEventEditor_Event, vChangedFields);
        
        if Calendar_ArrayIsEmpty(vChangedFields) then
                return;
        end
        
        -- Save the event if it's new
        
        if gCalendarEventEditor_IsNewEvent then
                if (gCalendarEventEditor_Event.mTitle ~= nil and gCalendarEventEditor_Event.mTitle ~= "")
                or gCalendarEventEditor_Event.mType ~= nil then
                        EventDatabase_AddEvent(gCalendarEventEditor_Database, gCalendarEventEditor_Event);
                end
        else
                EventDatabase_EventChanged(gCalendarEventEditor_Database, gCalendarEventEditor_Event, vChangedFields);
        end
        
        -- Save a template for the event
        
        if gCalendarEventEditor_Event.mType
        and gCalendarEventEditor_Event.mType ~= "Birth" then
                if not gGroupCalendar_PlayerSettings.EventTemplates then
                        gGroupCalendar_PlayerSettings.EventTemplates = {};
                end
                
                local   vEventTemplate = {};
                
                CalendarEventEditor_CopyTemplateFields(gCalendarEventEditor_Event, vEventTemplate);
                vEventTemplate.mSelfAttend = CalendarEventEditor_GetSelfAttend();
                
                gGroupCalendar_PlayerSettings.EventTemplates[gCalendarEventEditor_Event.mType] = vEventTemplate;
        end
end

function CalendarEventEditor_SetTimeControlValue(pFrame, pTime)
        if pTime == nil then
                return;
        end
        
        local   vFrameName = pFrame:GetName();
        
        local   vHourFrame = getglobal(vFrameName.."Hour");
        local   vMinuteFrame = getglobal(vFrameName.."Minute");
        local   vAMPMFrame = getglobal(vFrameName.."AMPM");
        
        if TwentyFourHourTime then
                local   vHour, vMinute = Calendar_ConvertTimeToHM(pTime);
                
                CalendarDropDown_SetSelectedValue(vHourFrame, vHour);
                CalendarDropDown_SetSelectedValue(vMinuteFrame, vMinute);
                vAMPMFrame:Hide();
        else
                local   vHour, vMinute, vAMPM = Calendar_ConvertTimeToHMAMPM(pTime);
                
                CalendarDropDown_SetSelectedValue(vHourFrame, vHour);
                CalendarDropDown_SetSelectedValue(vMinuteFrame, vMinute);
                
                vAMPMFrame:Show();
                CalendarDropDown_SetSelectedValue(vAMPMFrame, vAMPM);
        end
end

function CalendarEventEditor_GetTimeControlValue(pFrame)
        local   vFrameName = pFrame:GetName();
        
        local   vHourFrame = getglobal(vFrameName.."Hour");
        local   vMinuteFrame = getglobal(vFrameName.."Minute");
        local   vAMPMFrame = getglobal(vFrameName.."AMPM");
        
        local   vHour = UIDropDownMenu_GetSelectedValue(vHourFrame);
        local   vMinute = UIDropDownMenu_GetSelectedValue(vMinuteFrame);
        
        if TwentyFourHourTime then
                return Calendar_ConvertHMToTime(vHour, vMinute);
        else
                local   vAMPM = UIDropDownMenu_GetSelectedValue(vAMPMFrame);
                
                return Calendar_ConvertHMAMPMToTime(vHour, vMinute, vAMPM);
        end
end

function CalendarEventEditor_EventTypeChanged(pMenuFrame, pValue)
        CalendarEventEditor_SetEventType(pValue);
        
        -- Set the templated field values if available
        
        if gCalendarEventEditor_IsNewEvent then
                -- Set the default limits for this dungeon (the template will
                -- override this if it's present)
                
                gCalendarEventEditor_Event.mLimits = EventDatabase_GetStandardLimitsByID(pValue);
                
                -- Copy of template values
                
                if gGroupCalendar_PlayerSettings.EventTemplates then
                        local   vEventTemplate = gGroupCalendar_PlayerSettings.EventTemplates[pValue];
                        
                        if vEventTemplate then
                                CalendarEventEditor_CopyTemplateFields(vEventTemplate, gCalendarEventEditor_Event);
                                
                                CalendarEventEditor_UpdateControlsFromEvent(gCalendarEventEditor_Event, true);
                                
                                if vEventTemplate.mSelfAttend then
                                        CalendarEventEditor_SetSelfAttend(vEventTemplate.mSelfAttend);
                                end
                        end
                end
        end
end

function CalendarEventEditor_ShowPanel(pPanelIndex)
        CalendarAddPlayer_Cancel(); -- Force the dialog to close if it's open
        
        if gCalendarEventEditor_CurrentPanel > 0
        and gCalendarEventEditor_CurrentPanel ~= pPanelIndex then
                CalendarEventEditor_HidePanel(gCalendarEventEditor_CurrentPanel);
        end
        
        -- NOTE: Don't check for redundant calls since this function
        -- will be called to reset the field values as well as to 
        -- actuall show the panel when it's hidden
        
        gCalendarEventEditor_CurrentPanel = pPanelIndex;
        
        -- Update the control values
        
        if pPanelIndex == 1 then
                -- Event panel
                
                CalendarEventEditorSelfAttend:SetChecked(CalendarEventEditor_GetSelfAttend());
                CalendarEventEditorSelfAttendText:SetText(string.format(GroupCalendar_cSelfWillAttend, gGroupCalendar_PlayerName));
                
        elseif pPanelIndex == 2 then
                -- Attendance panel
                
                if EventDatabase_IsQuestingEventType(gCalendarEventEditor_PreviousEventType) then
                        CalendarAttendanceList_SetClassTotalsVisible(CalendarEventEditorAttendance, true, true);
                else
                        CalendarAttendanceList_SetClassTotalsVisible(CalendarEventEditorAttendance, false, false);
                end
        else
                Calendar_DebugMessage("CalendarEventEditor_ShowPanel: Unknown index ("..pPanelIndex..")");
        end
        
        getglobal(gCalendarEventEditor_PanelFrames[pPanelIndex]):Show();
        
        PanelTemplates_SetTab(CalendarEventEditorFrame, pPanelIndex);
        
        CalendarEventEditor_Update();
end

function CalendarEventEditor_HidePanel(pFrameIndex)
        if gCalendarEventEditor_CurrentPanel ~= pFrameIndex then
                return;
        end
        
        getglobal(gCalendarEventEditor_PanelFrames[pFrameIndex]):Hide();
        gCalendarEventEditor_CurrentPanel = 0;
end

function CalendarEventEditor_Update()
        -- Event panel
        
        if gCalendarEventEditor_CurrentPanel == 1 then
        
        -- Attendance panel
        
        elseif gCalendarEventEditor_CurrentPanel == 2 then
                CalendarAttendanceList_Update(CalendarEventEditorAttendance);
        end
end

Calendar_cStatusCodeStrings =
{
        N = CalendarEventEditor_cNotAttending,
        Y = CalendarEventEditor_cConfirmed,
        D = CalendarEventEditor_cDeclined,
        S = CalendarEventEditor_cStandby,
        P = CalendarEventEditor_cPending,
        Q = CalendarEventEditor_cQueued,
};

function CalendarEventEditor_GetStatusString(pStatus)
        local   vStatus1 = string.sub(pStatus, 1, 1);
        local   vString = Calendar_cStatusCodeStrings[vStatus1];
        
        if vString then 
                return vString;
        else
                return format(CalendarEventEditor_cUnknownStatus, pStatus);
        end
end

function CalendarEventEditor_GetSelfAttend()
        return EventDatabase_FindEventRSVPString(gCalendarEventEditor_Event, gGroupCalendar_PlayerName) ~= nil;
end

function CalendarEventEditor_SetSelfAttend(pWillAttend)
        if pWillAttend then
                local vRSVP = EventDatabase_CreatePlayerRSVP(
                                                                gCalendarEventEditor_Database,
                                                                gCalendarEventEditor_Event,
                                                                gGroupCalendar_PlayerName,
                                                                EventDatabase_GetRaceCodeByRace(UnitRace("PLAYER")),
                                                                EventDatabase_GetClassCodeByClass(UnitClass("PLAYER")),
                                                                gGroupCalendar_PlayerLevel,
                                                                "Y",
                                                                nil,
                                                                gGroupCalendar_PlayerGuild,
                                                                gGroupCalendar_PlayerGuildRank,
                                                                gGroupCalendar_PlayerCharacters);
                
                EventDatabase_AddEventRSVP(
                                gCalendarEventEditor_Database,
                                gCalendarEventEditor_Event,
                                gGroupCalendar_PlayerName,
                                vRSVP)
        else
                EventDatabase_RemoveEventRSVP(
                                gCalendarEventEditor_Database,
                                gCalendarEventEditor_Event,
                                gGroupCalendar_PlayerName)
        end
end

function CalendarEventEditor_AskDeleteEvent()
        -- If it's new just kill it without asking
        
        if gCalendarEventEditor_IsNewEvent then
                CalendarEventEditor_DeleteEvent();
        else
                -- Update an event record so we can display a meaningful name
                
                local   vChangedFields = {};
                local   vEvent = {};
                
                CalendarEventEditor_UpdateEventFromControls(vEvent, vChangedFields);
                
                StaticPopup_Show("CONFIRM_CALENDAR_EVENT_DELETE", EventDatabase_GetEventDisplayName(vEvent));
        end
end

function CalendarAttendanceList_AnyItemsCollapsed(pAttendanceList)
        if not pAttendanceList.CollapsedCategories then
                return false;
        end
        
        for vCategory, vCollapsed in pAttendanceList.CollapsedCategories do
                return true;
        end
        
        return false;
end

local   gCalendar_WhisperLog = {mPlayers = {}, mEvent = nil};

function CalendarWhisperLog_AddWhisper(pPlayerName, pMessage)
        -- If no event is active then just ignore all whispers
        -- NOTE: Disabling this for now, it seems like it's better to have too
        -- many than too few whispers.  With this feature enabled, the organizer
        -- must remember to open the event before receiving whispers or else
        -- they'll be lost.  That can be a bit of a pain.
        
        --[[
        if not gCalendar_WhisperLog.mEvent then
                return;
        end
        ]]
        
        -- Ignore whispers which appear to be data from other addons
        
        local   vFirstChar = string.sub(pMessage, 1, 1);
        
        if vFirstChar == "<"
        or vFirstChar == "["
        or vFirstChar == "{" then
                return;
        end
        
        -- Filter if necessary
        
        if gCalendar_WhisperLog.mWhisperFilterFunc
        and not gCalendar_WhisperLog.mWhisperFilterFunc(gCalendar_WhisperLog.mWhisperFilterParam, pPlayerName) then
                return;
        end
        
        --
        
        local   vPlayerLog = gCalendar_WhisperLog.mPlayers[pPlayerName];
        
        if not vPlayerLog then
                vPlayerLog = {};
                
                vPlayerLog.mName = pPlayerName;
                vPlayerLog.mDate, vPlayerLog.mTime = EventDatabase_GetServerDateTime60Stamp();
                vPlayerLog.mWhispers = {};
                
                gCalendar_WhisperLog.mPlayers[pPlayerName] = vPlayerLog;
        end
        
        local   vLength = table.getn(vPlayerLog.mWhispers);
        
        if vLength > 3 then
                table.remove(vPlayerLog.mWhispers, 1);
                vLength = vLength - 1;
        end
        
        vPlayerLog.mWhispers[vLength + 1] = pMessage;

        -- Notify
        
        if gCalendar_WhisperLog.mNotificationFunc then
                gCalendar_WhisperLog.mNotificationFunc(gCalendar_WhisperLog.mNotifcationParam);
        end
end

function CalendarWhisperLog_AskClear()
        StaticPopup_Show("CONFIRM_CALENDAR_CLEAR_WHISPERS");
end

function CalendarWhisperLog_Clear()
        gCalendar_WhisperLog.mPlayers = {};
        
        if gCalendar_WhisperLog.mNotificationFunc then
                gCalendar_WhisperLog.mNotificationFunc(gCalendar_WhisperLog.mNotifcationParam);
        end
end

function CalendarWhisperLog_GetPlayerWhispers(pEvent)
        if gCalendar_WhisperLog.mEvent ~= pEvent then
                gCalendar_WhisperLog.mEvent = pEvent;
                CalendarWhisperLog_Clear();
        end
        
        return gCalendar_WhisperLog.mPlayers;
end

function CalendarWhisperLog_SetNotificationFunc(pFunc, pParam)
        gCalendar_WhisperLog.mNotificationFunc = pFunc;
        gCalendar_WhisperLog.mNotifcationParam = pParam;
end

function CalendarWhisperLog_SetWhisperFilterFunc(pFunc, pParam)
        gCalendar_WhisperLog.mWhisperFilterFunc = pFunc;
        gCalendar_WhisperLog.mWhisperFilterParam = pParam;
end

function CalendarWhisperLog_RemovePlayer(pPlayerName)
        gCalendar_WhisperLog.mPlayers[pPlayerName] = nil;

        -- Notify
        
        if gCalendar_WhisperLog.mNotificationFunc then
                gCalendar_WhisperLog.mNotificationFunc(gCalendar_WhisperLog.mNotifcationParam);
        end
end

function CalendarWhisperLog_GetNextWhisper(pPlayerName)
        -- Make an indexed list of the whispers
        
        local   vWhispers = {};
        
        for vName, vWhisper in gCalendar_WhisperLog.mPlayers do
                table.insert(vWhispers, vWhisper);
        end
        
        -- Sort by time
        
        table.sort(vWhispers, EventDatabase_CompareRSVPsByDate);
        
        --
        
        local   vLowerName = strlower(pPlayerName);
        local   vUseNext = false;
        
        for vIndex, vWhisper in vWhispers do
                if vUseNext then
                        return vWhisper;
                end
                
                if vLowerName == strlower(vWhisper.mName) then
                        vUseNext = true;
                end
        end
        
        return nil;
end

function CalendarAttendanceList_GetManualConfirmEnable(pAttendanceList)
        local   vAutoConfirmEnable = getglobal(pAttendanceList:GetName().."MainViewAutoConfirmEnable");
        local   vManualConfirm = not vAutoConfirmEnable:GetChecked();
        
        if not vManualConfirm then
                vManualConfirm = nil;
        end
        
        return vManualConfirm;
end

gCalendarAttendanceList_VerticalScrollList = nil;

function CalendarAttendanceList_OnLoad()
        local   vListName = this:GetName();
        
        this.NumItems = gGroupCalendar_cNumAttendanceItems;
        this.CollapsedCategories = {};
        this.ListViewMode = "Date";
        
        -- Set the text color for the class totals
        
        for vClassID, vClassInfo in gGroupCalendar_ClassInfoByClassCode do
                local   vLabel = getglobal(vListName.."MainViewTotals"..vClassInfo.element.."sLabel");
                local   vCount = getglobal(vListName.."MainViewTotals"..vClassInfo.element.."s");
                local   vColor = RAID_CLASS_COLORS[vClassInfo.color];
                
                vLabel:SetTextColor(vColor.r, vColor.g, vColor.b);
                vCount:SetTextColor(vColor.r, vColor.g, vColor.b);
        end
        
        CalendarAttendanceList_SetClassTotalsVisible(this, true, true);
end

function CalendarAttendanceList_OnShow()
        CalendarAttendanceList_ShowPanel(this, "MainView");
        
        CalendarWhisperLog_SetNotificationFunc(CalendarAttendanceList_UpdateWhispers, this);
        CalendarWhisperLog_SetWhisperFilterFunc(CalendarAttendanceList_FilterWhispers, this);
end

function CalendarAttendanceList_OnHide()
        CalendarWhisperLog_SetNotificationFunc(nil, nil, nil);
        
        if GetNumPartyMembers() == 0
        and GetNumRaidMembers() == 0 then
                CalendarGroupInvites_EndEvent(this.Event);
        else
                CalendarGroupInvites_SetChangedFunc(nil, nil);
        end
end

function CalendarAttendanceList_SetEvent(pAttendanceList, pDatabase, pEvent)
        pAttendanceList.Database = pDatabase;
        pAttendanceList.Event = pEvent;
        pAttendanceList.Group = nil;
        
        pAttendanceList.ListViewMode = "Date";
        
        pAttendanceList.EventListInfo = {};
        pAttendanceList.CollapsedCategories = {};

        -- Update the auto confirm enable
        
        local   vAttendanceListName = pAttendanceList:GetName();
        local   vMainViewName = vAttendanceListName.."MainView";
        
        local   vAutoConfirmEnable = getglobal(vMainViewName.."AutoConfirmEnable");
        local   vAutoConfirmOptions = getglobal(vMainViewName.."AutoConfirmOptions");
        
        vAutoConfirmEnable:SetChecked(not pAttendanceList.Event.mManualConfirm);
        Calendar_SetButtonEnable(vAutoConfirmOptions, not pAttendanceList.Event.mManualConfirm);
        
        --
        
        CalendarAttendanceList_UpdateAttendanceCounts(pAttendanceList);
end

function CalendarAttendanceList_SetGroup(pAttendanceList, pGroup)
        pAttendanceList.Group = pGroup;
end

function CalendarAttendanceList_EventChanged(pAttendanceList, pDatabase, pEvent)
        pAttendanceList.Database = pDatabase;
        pAttendanceList.Event = pEvent;
        
        CalendarAttendanceList_UpdateAttendanceCounts(pAttendanceList);
        CalendarGroupInvites_EventChanged(pDatabase, pEvent);
        
        CalendarAttendanceList_Update(pAttendanceList);
end

function CalendarAttendanceList_UpdateAttendanceCounts(pAttendanceList)
        local   vUseClassCategories = EventDatabase_IsQuestingEventType(pAttendanceList.Event.mType);
        local   vHadWhispers = pAttendanceList.EventListInfo.AttendanceCounts
                                   and pAttendanceList.EventListInfo.AttendanceCounts.Categories["WHISPERS"] ~= nil;
        
        pAttendanceList.EventListInfo.AttendanceCounts =
                        CalendarEvent_GetAttendanceCounts(
                                        pAttendanceList.Database,
                                        pAttendanceList.Event,
                                        vUseClassCategories);
        
        -- Add in the recent whispers, but only if they're not in the attendance list
        -- and only if the event is editable
        
        if not vHadWhispers then
                pAttendanceList.CollapsedCategories["WHISPERS"] = true;
        end
        
        local   vAttendanceList = pAttendanceList.EventListInfo.AttendanceCounts;
        local   vGroupList = gGroupCalendar_Invites.Group;
        
        if pAttendanceList.mCanEdit then
                local   vPlayerWhispers = CalendarWhisperLog_GetPlayerWhispers(pAttendanceList.Event);
                
                for vPlayerName, vWhispers in vPlayerWhispers do
                        CalendarAttendanceList_AddWhisper(
                                        vAttendanceList,
                                        vPlayerName,
                                        vWhispers);
                end
        end
        
        CalendarEvent_SortAttendanceCounts(pAttendanceList.EventListInfo.AttendanceCounts, pAttendanceList.ListViewMode);
end

function CalendarAttendanceList_FilterWhispers(pAttendanceList, pPlayerName)
        local   vAttendanceList = pAttendanceList.EventListInfo.AttendanceCounts;
        
        if vAttendanceList.Items
        and vAttendanceList.Items[pPlayerName] then
                return false;
        end
        
        if gGroupCalendar_Invites.Group
        and gGroupCalendar_Invites.Group.Items[pPlayerName] then
                return false;
        end
        
        return true;
end

function CalendarAttendanceList_UpdateWhispers(pAttendanceList)
        if not CalendarAttendanceList_RemoveCategory(pAttendanceList.EventListInfo.AttendanceCounts, "WHISPERS") then
                -- Collapse the category if this is the first one
                
                pAttendanceList.CollapsedCategories["WHISPERS"] = true;
        end
        
        local   vAttendanceList = pAttendanceList.EventListInfo.AttendanceCounts;
        local   vGroupList = gGroupCalendar_Invites.Group;
        
        local   vPlayerWhispers = CalendarWhisperLog_GetPlayerWhispers(pAttendanceList.Event);
        
        for vPlayerName, vWhispers in vPlayerWhispers do
                CalendarAttendanceList_AddWhisper(
                                vAttendanceList,
                                vPlayerName,
                                vWhispers);
        end
        
        CalendarEvent_SortAttendanceCounts(pAttendanceList.EventListInfo.AttendanceCounts, pAttendanceList.ListViewMode);
        
        CalendarAttendanceList_Update(pAttendanceList);
end

function CalendarAttendanceList_SetCanEdit(pAttendanceList, pCanEdit)
        pAttendanceList.mCanEdit = pCanEdit;
end

function CalendarScrollbarTrench_SizeChanged(pScrollbarTrench)
        local   vScrollbarTrenchName = pScrollbarTrench:GetName();
        local   vScrollbarTrenchMiddle = getglobal(vScrollbarTrenchName.."Middle");
        
        local   vMiddleHeight= pScrollbarTrench:GetHeight() - 51;
        vScrollbarTrenchMiddle:SetHeight(vMiddleHeight);
end

function CalendarAttendanceList_SetClassTotalsVisible(pAttendanceList, pVisible, pShowAutoConfirm)
        pAttendanceList.ShowTotals = pVisible;
        pAttendanceList.ShowAutoConfirm = pShowAutoConfirm;
        
        CalendarAttendanceList_AdjustHeight(pAttendanceList);
end

function CalendarAttendanceList_AdjustHeight(pAttendanceList)
        local   vListName = pAttendanceList:GetName();
        local   vTotals = getglobal(vListName.."MainViewTotals");
        local   vAutoConfirm = getglobal(vListName.."MainViewAutoConfirm");
        local   vScrollFrame = getglobal(vListName.."ScrollFrame");
        local   vScrollTrench = getglobal(vListName.."ScrollbarTrench");
        local   vScrollFrameBaseHeight = 254;
        local   vScrollTrenchBaseHeight = 261;
        
        if pAttendanceList.CurrentPanel == "GroupView"
        or pAttendanceList.ShowTotals then
                local   vFooterHeight;
                
                vTotals:Show();
                
                if pAttendanceList.CurrentPanel ~= "GroupView"
                and pAttendanceList.ShowAutoConfirm then
                        vAutoConfirm:Show();
                        pAttendanceList.NumItems = gGroupCalendar_cNumAutoConfirmAttendanceItems;
                        vFooterHeight = vAutoConfirm:GetHeight() + vTotals:GetHeight();
                else
                        vAutoConfirm:Hide();
                        pAttendanceList.NumItems = gGroupCalendar_cNumAttendanceItems;
                        vFooterHeight = vTotals:GetHeight();
                end
                
                vScrollFrame:SetHeight(vScrollFrameBaseHeight - vFooterHeight);
                vScrollTrench:SetHeight(vScrollTrenchBaseHeight - vFooterHeight);
        else
                vTotals:Hide();
                vAutoConfirm:Hide();
                pAttendanceList.NumItems = gGroupCalendar_cNumPlainAttendanceItems;
                vScrollFrame:SetHeight(vScrollFrameBaseHeight);
                vScrollTrench:SetHeight(vScrollTrenchBaseHeight);
        end
end

function CalendarAttendanceList_Update(pAttendanceList)
        if not pAttendanceList:IsVisible() then
                return;
        end
        
        local   vAttendanceListName = pAttendanceList:GetName();
        local   vMainViewName = vAttendanceListName.."MainView";
        
        -- Update the view menu
        
        local   vListViewMenu = getglobal(vAttendanceListName.."ViewMenu");
        
        CalendarDropDown_SetSelectedValue(vListViewMenu, pAttendanceList.ListViewMode);
        
        if pAttendanceList.CurrentPanel == "MainView" then
                -- Update the scroll frame
                
                local           vTotalCount = 0;
                
                if pAttendanceList.EventListInfo.AttendanceCounts then
                        for vCategory, vClassAttendanceInfo in pAttendanceList.EventListInfo.AttendanceCounts.Categories do
                                vTotalCount = vTotalCount + 1;
                                
                                if not pAttendanceList.CollapsedCategories[vCategory] then
                                        vTotalCount = vTotalCount + vClassAttendanceInfo.mCount;
                                end
                        end
                end
                
                FauxScrollFrame_Update(
                                getglobal(vAttendanceListName.."ScrollFrame"),
                                vTotalCount,
                                pAttendanceList.NumItems,
                                gGroupCalendar_cAttendanceItemHeight,
                                nil, nil, nil,
                                nil,
                                293, 316);
                
                CalendarAttendanceList_UpdateAttendanceList(pAttendanceList);
                
                -- Update the expand/collapse all button
                
                local   vExpandAllButton = getglobal(vAttendanceListName.."ExpandAllButton")
                
                if CalendarAttendanceList_AnyItemsCollapsed(pAttendanceList) then
                        vExpandAllButton:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up");
                else
                        vExpandAllButton:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up"); 
                end
                
                CalendarAttendanceList_UpdateClassTotals(pAttendanceList);
                
                -- Show or hide the add button
                
                local           vAddButton = getglobal(vMainViewName.."AddButton");
                
                if pAttendanceList.mCanEdit then
                        vAddButton:Show();
                else
                        vAddButton:Hide();
                end
                
        elseif pAttendanceList.CurrentPanel == "GroupView" then
                local   vGroupViewName = vAttendanceListName.."GroupView";
                local   vSelectionInfoText = getglobal(vGroupViewName.."SelectionInfo");
                local   vStatusText = getglobal(vGroupViewName.."Status");
                local   vStatusMessage = GroupCalendar_cInviteStatusMessages[gGroupCalendar_Invites.Status];
                local   vInviteButton = getglobal(vGroupViewName.."Invite");
                
                if vStatusMessage then
                        vStatusText:SetText(vStatusMessage);
                elseif gGroupCalendar_Invites.Status then
                        vStatusText:SetText("Unknown "..gGroupCalendar_Invites.Status);
                else
                        vStatusText:SetText("Unknown (nil)");
                end
                
                local   vNumSelected = gGroupCalendar_Invites.NumSelected;
                
                if not vNumSelected
                or vNumSelected == 0 then
                        vSelectionInfoText:SetText(GroupCalendar_cNoSelection);
                elseif vNumSelected == 1 then
                        vSelectionInfoText:SetText(GroupCalendar_cSingleSelection);
                else
                        vSelectionInfoText:SetText(format(GroupCalendar_cMultiSelection, vNumSelected));
                end
                
                -- Update the scroll frame
                
                local           vTotalCount = 0;
                
                if pAttendanceList.EventListInfo.AttendanceCounts then
                        for vCategory, vClassAttendanceInfo in gGroupCalendar_Invites.Group.Categories do
                                vTotalCount = vTotalCount + 1;
                                
                                if not pAttendanceList.CollapsedCategories[vCategory] then
                                        vTotalCount = vTotalCount + vClassAttendanceInfo.mCount;
                                end
                        end
                end
                
                FauxScrollFrame_Update(
                                getglobal(vAttendanceListName.."ScrollFrame"),
                                vTotalCount,
                                pAttendanceList.NumItems,
                                gGroupCalendar_cAttendanceItemHeight,
                                nil, nil, nil,
                                nil,
                                293, 316);
                
                CalendarAttendanceList_UpdateAttendanceList(pAttendanceList);
                
                -- Enable the invite button if the player is the raid or party leader
                -- or is not in a raid or pary
                
                local           vEnableInvites = false;
                local           vInParty = GetNumPartyMembers() > 0 or GetNumRaidMembers() > 0;
                
                if not vInParty
                or IsPartyLeader()
                or IsRaidLeader()
                or IsRaidOfficer() then
                        vEnableInvites = true;
                end
                
                Calendar_SetButtonEnable(vInviteButton, vEnableInvites);
        else
                Calendar_ErrorMessage("CalendarAttendanceList_Update: Unknown panel "..pAttendanceList.CurrentPanel);
        end
end

function CalendarAttendanceList_ToggleExpandAll(pAttendanceList)
        -- Get the group
        
        local   vGroup;
        
        if pAttendanceList.CurrentPanel == "MainView" then
                vGroup = pAttendanceList.EventListInfo.AttendanceCounts;
        elseif pAttendanceList.CurrentPanel == "GroupView" then
                vGroup = gGroupCalendar_Invites.Group;
        else
                Calendar_ErrorMessage("CalendarAttendanceList_ToggleExpandAll: Unknown view "..pAttendanceList.CurrentPanel);
                return;
        end
        
        -- Just return if there are no attendance items to toggle
        
        if not pAttendanceList
        or not vGroup then
                return;
        end
        
        if CalendarAttendanceList_AnyItemsCollapsed(pAttendanceList) then
                -- Expand all
                
                pAttendanceList.CollapsedCategories = {};
        else
                -- Collapse all
                
                pAttendanceList.CollapsedCategories = {};
                
                for vCategory, vClassAttendanceInfo in vGroup.Categories do
                        pAttendanceList.CollapsedCategories[vCategory] = true;
                end
        end
        
        CalendarAttendanceList_Update(pAttendanceList);
end

function CalendarAttendanceList_UpdateClassTotals(pAttendanceList)
        local   vListName = pAttendanceList:GetName();
        
        for vClassCode, vClassInfo in gGroupCalendar_ClassInfoByClassCode do
                local   vClassTotal;
                
                if pAttendanceList.EventListInfo.AttendanceCounts then
                        vClassTotal = pAttendanceList.EventListInfo.AttendanceCounts.ClassTotals[vClassCode];
                else
                        vClassTotal = nil;
                end
                
                local   vCountTextItem = getglobal(vListName.."MainViewTotals"..vClassInfo.element.."s");
                
                if not vClassTotal then
                        vCountTextItem:SetText("0");
                        vCountTextItem:SetTextColor(0.9, 0.2, 0.2);
                else
                        local   vColor = RAID_CLASS_COLORS[vClassInfo.color];
                        
                        vCountTextItem:SetText(vClassTotal);
                        vCountTextItem:SetTextColor(vColor.r, vColor.g, vColor.b);
                end
        end
end

gGroupCalendar_cAttendanceCategories =
{
        NO = GroupCalendar_cNotAttending,
        YES = GroupCalendar_cAttending,
        PENDING = GroupCalendar_cPendingApproval,
        STANDBY = GroupCalendar_cStandby,
        QUEUED = GroupCalendar_Queued,
        WHISPERS = GroupCalendar_cWhispers,
};

function CalendarAttendanceList_GetClassNameColor(pClassName)
        if not pClassName then
                return NORMAL_FONT_COLOR;
        end
        
        local   vClassColorName = getglobal("GroupCalendar_c"..pClassName.."ClassColorName");
        
        return RAID_CLASS_COLORS[vClassColorName];
end
        
function CalendarAttendanceList_SetItem(
                                        pAttendanceList,
                                        pIndex,
                                        pItemInfo,
                                        pLeftFormat,
                                        pRightFormat,
                                        pShowMenu,
                                        pMenuValue,
                                        pColor,
                                        pOffline,
                                        pIconPath,
                                        pChecked,
                                        pTooltipName,
                                        pTooltipText,
                                        pTooltipTextColor)
        local   vListName = pAttendanceList:GetName();
        local   vItemName = vListName.."Item"..pIndex;
        local   vItem = getglobal(vItemName);
        local   vItemHighlight = getglobal(vItemName.."Highlight");
        
        local   vItemCategory = getglobal(vItemName.."Category");
        local   vItemLeftText = getglobal(vItemName.."Name");
        local   vItemRightText = getglobal(vItemName.."Status");
        local   vItemMenu = getglobal(vItemName.."Menu");
        local   vItemActionButton = getglobal(vItemName.."Action");
        
        --
        
        vItemCategory:Hide();
        vItemLeftText:Show();
        vItemRightText:Show();
        vItemActionButton:Hide();
        
        if pShowMenu then
                CalendarDropDown_SetSelectedValue2(vItemMenu, pMenuValue);
                vItemMenu:Show();
                vItemRightText:SetWidth(112);
        else
                vItemMenu:Hide();
                vItemRightText:SetWidth(130);
        end
        
        vItemLeftText:SetText(string.gsub(pLeftFormat, "%$(%w+)", function (pField) return pItemInfo[pField]; end));
        vItemRightText:SetText(string.gsub(pRightFormat, "%$(%w+)", function (pField) return pItemInfo[pField]; end));
        
        if pColor then
                if pOffline then
                        vItemLeftText:SetTextColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b);
                else
                        vItemLeftText:SetTextColor(pColor.r, pColor.g, pColor.b);
                end
                
                vItemRightText:SetTextColor(pColor.r, pColor.g, pColor.b);
        else
                if pOffline then
                        vItemLeftText:SetTextColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b);
                else
                        vItemLeftText:SetTextColor(1, 1, 1);
                end
                vItemRightText:SetTextColor(1, 1, 1);
        end
        
        if pChecked ~= nil then
                vItem.Checkable = true;
                vItem:SetNormalTexture("Interface\\Buttons\\UI-CheckBox-Up");
                vItem:SetPushedTexture("Interface\\Buttons\\UI-CheckBox-Down");
                vItemHighlight:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight");
                vItem:SetCheckedTexture("Interface\\Buttons\\UI-CheckBox-Check");
                vItem:SetChecked(pChecked);
        else
                vItem.Checkable = false;
                if pIconPath then
                        vItem:SetNormalTexture(pIconPath);
                else
                        vItem:SetNormalTexture("");
                end
                vItem:SetPushedTexture("");
                vItem:SetCheckedTexture("");
                vItemHighlight:SetTexture("");
                vItem:SetChecked(false);
        end
        
        vItem.IsCategoryItem = false;
        vItem:Show();
        
        vItem.TooltipName = pTooltipName;
        vItem.TooltipText = pTooltipText;
        vItem.TooltipTextColor = pTooltipTextColor;
        
        return vItem;
end

function CalendarAttendanceList_SetItemToRSVP(pAttendanceList, pIndex, pCategory, pRSVP, pNameFormat, pShowRank)
        local   vItemInfo =
        {
                name = pRSVP.mName,
                class = EventDatabase_GetClassByClassCode(pRSVP.mClassCode),
                race = EventDatabase_GetRaceByRaceCode(pRSVP.mRaceCode),
                level = tostring(pRSVP.mLevel),
                status = "",
        };
        
        -- Set the status
        
        local   vShowStatusString = false;
        
        if vShowStatusString then
                vItemInfo.status = CalendarEventEditor_GetStatusString(pRSVP.mStatus);
        elseif pShowRank then
                local   vRank = EventDatabase_MapGuildRank(pRSVP.mGuild, pRSVP.mGuildRank);
                
                if vRank then
                        vItemInfo.status = GuildControlGetRankName(vRank + 1);
                else
                        vItemInfo.status = GroupCalendar_cUnknown;
                end
                
        else
                local   vDate, vTime = EventDatabase_GetRSVPOriginalDateTime(pRSVP);
                
                if vDate and vTime then
                        vTime = vTime / 60; -- Convert from seconds to minutes
                        
                        if gGroupCalendar_Settings.ShowEventsInLocalTime then
                                vDate, vTime = Calendar_GetLocalDateTimeFromServerDateTime(vDate, vTime);
                        end
                        
                        local   vShortDateString = Calendar_GetShortDateString(vDate);
                        local   vShortTimeString = Calendar_GetShortTimeString(vTime);
                        
                        vItemInfo.status = vShortDateString.." "..vShortTimeString;
                end
        end
        
        local   vColor = CalendarAttendanceList_GetClassNameColor(vItemInfo.class);
        
        -- Get the icon path
        
        local   vIconPath;
        local   vTooltipName = nil;
        local   vTooltipText = nil;
        local   vTooltipTextColor = nil;
        
        if pRSVP.mComment and pRSVP.mComment ~= "" then
                vIconPath = "Interface\\Addons\\GroupCalendar\\Textures\\AttendanceNoteIcon";
                vTooltipName = pRSVP.mName;
                vTooltipText = Calendar_UnescapeString(pRSVP.mComment);
        elseif pRSVP.mType == "Whisper" then
                vIconPath = "Interface\\Addons\\GroupCalendar\\Textures\\AttendanceNoteIcon";
                
                vTooltipName = pRSVP.mName;
                vTooltipText = pRSVP.mWhispers;
                vTooltipTextColor = ChatTypeInfo["WHISPER"];
                vColor = vTooltipTextColor;
        else
                vIconPath = "";
        end
        
        -- Set the item
        
        local   vItem = CalendarAttendanceList_SetItem(
                                                        pAttendanceList,
                                                        pIndex,
                                                        vItemInfo,
                                                        pNameFormat,
                                                        "$status",
                                                        pAttendanceList.mCanEdit,
                                                        pRSVP.mStatus,
                                                        vColor,
                                                        false,
                                                        vIconPath,
                                                        nil,
                                                        vTooltipName,
                                                        vTooltipText,
                                                        vTooltipTextColor);
        
        vItem.Item = pRSVP;
end

function CalendarAttendanceList_SetItemToCategory(pAttendanceList, pIndex, pCategory, pActionFunc, pActionParam)
        local   vItemName = pAttendanceList:GetName().."Item"..pIndex;
        local   vItem = getglobal(vItemName);
        
        local   vItemHighlight = getglobal(vItemName.."Highlight");
        local   vItemCategory = getglobal(vItemName.."Category");
        local   vItemPlayerName = getglobal(vItemName.."Name");
        local   vItemPlayerStatus = getglobal(vItemName.."Status");
        local   vItemMenu = getglobal(vItemName.."Menu");
        local   vItemActionButton = getglobal(vItemName.."Action");

        local   vCategoryName;
        
        if type(pCategory) == "number" then
                vCategoryName = GuildControlGetRankName(pCategory + 1);
        else
                vCategoryName = gGroupCalendar_cAttendanceCategories[pCategory];
                
                if not vCategoryName then
                        vCategoryName = EventDatabase_GetClassByClassCode(pCategory);
                end
        end
        
        vItemCategory:SetText(vCategoryName);
        vItemCategory:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b);
        vItemCategory:Show();
        vItemPlayerName:Hide();
        vItemPlayerStatus:Hide();
        
        if pActionFunc then
                vItemActionButton.ActionFunc = pActionFunc;
                vItemActionButton.ActionParam = pActionParam;
                vItemActionButton:Show();
        else
                vItemActionButton:Hide();
        end
        
        vItemMenu:Hide();
        vItemPlayerStatus:SetWidth(130);
        
        if pAttendanceList.CollapsedCategories[pCategory] then
                vItem:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up");
                vItem:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down");
        else
                vItem:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up"); 
                vItem:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down"); 
        end
        
        vItemHighlight:SetTexture("Interface\\Buttons\\UI-PlusButton-Hilight");
        vItem:SetChecked(false);
        
        vItem.Item = nil;
        vItem.IsCategoryItem = true;
        vItem.Category = pCategory;
        
        vItem.TooltipName = nil;
        vItem.TooltipText = nil;
        vItem.TooltipTextColor = nil;
        
        vItem:Show();
end

function CalendarAttendanceList_HideUnusedAttendanceItems(pAttendanceList, pFirstItem)
        local   vListName = pAttendanceList:GetName();
        
        for vIndex = pFirstItem, gGroupCalendar_cNumPlainAttendanceItems - 1 do
                local   vItemName = vListName.."Item"..vIndex;
                getglobal(vItemName):Hide();
        end
end

function CalendarAttendanceList_UpdateAttendanceList(pAttendanceList)
        if pAttendanceList.CurrentPanel == "MainView" then
                CalendarAttendanceList_UpdateEventAttendance(pAttendanceList);
        elseif pAttendanceList.CurrentPanel == "GroupView" then
                CalendarAttendanceList_UpdateGroupAttendance(pAttendanceList);
        else
                Calendar_ErrorMessage("Unknown attendance panel "..pAttendanceList.CurrentPanel);
        end
end

function CalendarAttendanceList_UpdateGroupAttendance(pAttendanceList)
        local   vSortByFlags = GroupCalendar_cSortByFlags[pAttendanceList.ListViewMode];
        
        pAttendanceList.ViewType = "Class";
        pAttendanceList.ShowRank = vSortByFlags.Rank;
        
        CalendarAttendanceList_UpdateAttendanceItems(pAttendanceList, gGroupCalendar_Invites.Group, CalendarAttendanceList_SetGroupItem, GroupCalendar_cNumPlayersFormat);
end

function CalendarAttendanceList_UpdateEventAttendance(pAttendanceList)
        local   vSetItemFunction;
        
        if EventDatabase_IsQuestingEventType(pAttendanceList.Event.mType) then
                pAttendanceList.ViewType = "Class";
        else
                pAttendanceList.ViewType = "Plain";
        end
        
        local   vSortByFlags = GroupCalendar_cSortByFlags[pAttendanceList.ListViewMode];
        
        pAttendanceList.ShowRank = vSortByFlags.Class and vSortByFlags.Rank;
        
        CalendarAttendanceList_UpdateAttendanceItems(pAttendanceList, pAttendanceList.EventListInfo.AttendanceCounts, CalendarAttendanceList_SetAttendanceItem, GroupCalendar_cNumAttendeesFormat);
end

function CalendarAttendanceList_SetGroupItem(pAttendanceList, pItemIndex, pCategory, pItem)
        local   vItemInfo = {};
        local   vToolTipName, vTooltipText;
        local   vIconPath;
        
        vItemInfo.name = pItem.mName;
        vItemInfo.status = GroupCalendar_cGroupStatusMessages[pItem.mGroupStatus];
        vItemInfo.class = EventDatabase_GetClassByClassCode(pItem.mClassCode);
        vItemInfo.race = EventDatabase_GetRaceByRaceCode(pItem.mRaceCode);
        vItemInfo.level = tostring(pItem.mLevel);
        vItemInfo.offline = pItem.mOffline;
        
        -- Set the status
        
        local   vStatusColumnFormat = "";
        
        if pAttendanceList.ListViewMode == "Rank" then
                local   vRank = EventDatabase_MapGuildRank(pItem.mGuild, pItem.mGuildRank);
                
                if vRank then
                        vItemInfo.rank = GuildControlGetRankName(vRank + 1);
                else
                        vItemInfo.rank = GroupCalendar_cUnknown;
                end
                
                vStatusColumnFormat = "$rank";
        elseif pAttendanceList.ListViewMode == "Date"
        or pAttendanceList.ListViewMode == "Name" then
                local   vDate, vTime = EventDatabase_GetRSVPOriginalDateTime(pItem);
                
                if vDate and vTime then
                        vTime = vTime / 60; -- Convert from seconds to minutes
                        
                        if gGroupCalendar_Settings.ShowEventsInLocalTime then
                                vDate, vTime = Calendar_GetLocalDateTimeFromServerDateTime(vDate, vTime);
                        end
                        
                        local   vShortDateString = Calendar_GetShortDateString(vDate);
                        local   vShortTimeString = Calendar_GetShortTimeString(vTime);
                        
                        vItemInfo.date = vShortDateString.." "..vShortTimeString;
                        
                        vStatusColumnFormat = "$date";
                end
        end
        
        local   vNameColumnFormat;
        
        if pCategory == "STANDBY" then
                vNameColumnFormat = GroupCalendar_cMeetingAttendanceNameFormat;
        else
                vNameColumnFormat = GroupCalendar_cGroupAttendanceNameFormat;
        end
        
        local   vSelected;
        
        if pItem.mGroupStatus ~= "Joined" then
                vSelected = pItem.mSelected == true;
        end
        
        local   vItem = CalendarAttendanceList_SetItem(
                                                        pAttendanceList,
                                                        pItemIndex,
                                                        vItemInfo,
                                                        vNameColumnFormat,
                                                        vStatusColumnFormat,
                                                        false,
                                                        false,
                                                        CalendarAttendanceList_GetClassNameColor(vItemInfo.class),
                                                        pItem.mOffline,
                                                        vIconPath,
                                                        vSelected,
                                                        vTooltipName,
                                                        vTooltipText);
        
        vItem.Item = pItem;
end

function CalendarAttendanceList_SetAttendanceItem(pAttendanceList, pItemIndex, pCategory, pItem)
        if pItem.mType == "Whisper" then
                CalendarAttendanceList_SetItemToRSVP(pAttendanceList, pItemIndex, pCategory, pItem, GroupCalendar_cMeetingAttendanceNameFormat, pAttendanceList.ShowRank);
        elseif pAttendanceList.ViewType == "Questing" then
                local   vNameFormat;

                if pCategory ~= "STANDBY" then
                        vNameFormat = GroupCalendar_cQuestAttendanceNameFormat;
                else
                        vNameFormat = GroupCalendar_cMeetingAttendanceNameFormat;
                end
                
                CalendarAttendanceList_SetItemToRSVP(pAttendanceList, pItemIndex, pCategory, pItem, vNameFormat, pAttendanceList.ShowRank);
        else
                CalendarAttendanceList_SetItemToRSVP(pAttendanceList, pItemIndex, pCategory, pItem, GroupCalendar_cMeetingAttendanceNameFormat, pAttendanceList.ShowRank);
        end
end

function CalendarAttendanceList_UpdateAttendanceItems(pAttendanceList, pAttendanceCounts, pSetItemFunction, pTotalAttendanceFormat)
        local   vTotalAttendeesItem = getglobal(pAttendanceList:GetName().."Total");
        
        local   vFirstItemIndex = FauxScrollFrame_GetOffset(getglobal(pAttendanceList:GetName().."ScrollFrame"));
        local   vItemIndex = 0;
        
        if not pAttendanceCounts then
                CalendarAttendanceList_HideUnusedAttendanceItems(pAttendanceList, vItemIndex);
                vTotalAttendeesItem:SetText(string.format(pTotalAttendanceFormat, 0));
                return;
        end
        
        vTotalAttendeesItem:SetText(string.format(pTotalAttendanceFormat, pAttendanceCounts.NumAttendees));
        
        for vIndex, vCategory in pAttendanceCounts.SortedCategories do
                local   vClassAttendanceInfo = pAttendanceCounts.Categories[vCategory];
                
                if vFirstItemIndex == 0 then
                        local   vActionFunc = nil;
                        
                        if vCategory == "WHISPERS" then
                                vActionFunc = CalendarWhisperLog_AskClear;
                        end
                        
                        CalendarAttendanceList_SetItemToCategory(pAttendanceList, vItemIndex, vCategory, vActionFunc, nil);
                        
                        vItemIndex = vItemIndex + 1;
                        
                        if vItemIndex >= pAttendanceList.NumItems then
                                CalendarAttendanceList_HideUnusedAttendanceItems(pAttendanceList, vItemIndex);
                                return;
                        end
                else
                        vFirstItemIndex = vFirstItemIndex - 1;
                end
                
                if not pAttendanceList.CollapsedCategories[vCategory] then
                        if vFirstItemIndex >= vClassAttendanceInfo.mCount then
                                vFirstItemIndex = vFirstItemIndex - vClassAttendanceInfo.mCount;
                        else
                                for vIndex, vRSVP in vClassAttendanceInfo.mAttendees do
                                        if vFirstItemIndex == 0 then
                                                pSetItemFunction(pAttendanceList, vItemIndex, vCategory, vRSVP);
                                                
                                                vItemIndex = vItemIndex + 1;
                                                
                                                if vItemIndex >= pAttendanceList.NumItems then
                                                        CalendarAttendanceList_HideUnusedAttendanceItems(pAttendanceList, vItemIndex);
                                                        return;
                                                end
                                        else
                                                vFirstItemIndex = vFirstItemIndex - 1;
                                        end
                                end
                        end
                end
        end
        
        CalendarAttendanceList_HideUnusedAttendanceItems(pAttendanceList, vItemIndex);
end

function CalendarAttendanceList_GetIndexedItem(pAttendanceList, pIndex)
        local   vIndex = pIndex + FauxScrollFrame_GetOffset(getglobal(pAttendanceList:GetName().."ScrollFrame"));
        
        for vCategoryIndex, vCategory in pAttendanceList.EventListInfo.AttendanceCounts.SortedCategories do
                local   vClassAttendanceInfo = pAttendanceList.EventListInfo.AttendanceCounts.Categories[vCategory];
                
                if vIndex == 0 then
                        Calendar_DebugMessage("CalendarAttendanceList_GetIndexedItem: Index specifies a header");
                        return nil;
                end
                
                vIndex = vIndex - 1;
                
                if not pAttendanceList.CollapsedCategories[vCategory] then
                        if vIndex < vClassAttendanceInfo.mCount then
                                return vClassAttendanceInfo.mAttendees[vIndex + 1];
                        end
                        
                        vIndex = vIndex - vClassAttendanceInfo.mCount;
                end
        end
        
        Calendar_DebugMessage("CalendarAttendanceList_GetIndexedItem: Index too high");
        return nil;
end

function CalendarAttendanceList_OnVerticalScroll()
        CalendarAttendanceList_Update(gCalendarAttendanceList_VerticalScrollList);
end

function CalendarAttendanceList_OnClick(pButton)
        local   vAttendanceList = pButton:GetParent();
        
        if pButton.IsCategoryItem then
                if not vAttendanceList.CollapsedCategories[pButton.Category] then
                        vAttendanceList.CollapsedCategories[pButton.Category] = true;
                else
                        vAttendanceList.CollapsedCategories[pButton.Category] = nil;
                end
                
                CalendarAttendanceList_Update(vAttendanceList);
                return;
        end
        
        if not pButton.Checkable then
                pButton:SetChecked(false);
                return;
        end
        
        if pButton.Item then
                if vAttendanceList.CurrentPanel == "GroupView" then
                        CalendarGroupInvites_SetItemSelected(pButton.Item, not pButton.Item.mSelected);
                end
        end
end

function CalendarAttendanceList_ShowMessageTooltip(pOwner, pName, pMessages, pColor)
        if not pName then
                return;
        end
        
        GameTooltip:SetOwner(pOwner, "ANCHOR_LEFT");
        GameTooltip:AddLine(pName);
        
        local   vColor = {r = 1, g = 1, b = 1};
        
        if pColor then
                vColor = pColor;
        end;
        
        if type(pMessages) == "table" then
                for vIndex, vText in pMessages do
                        GameTooltip:AddLine(vText, vColor.r, vColor.g, vColor.b, 1);
                end
        else
                GameTooltip:AddLine(pMessages, vColor.r, vColor.g, vColor.b, 1);
        end
        
        GameTooltip:Show();
end

function CalendarAttendanceList_OnEnter()
        local   vAttendanceList = this:GetParent();
        
        CalendarAttendanceList_ShowMessageTooltip(this, this.TooltipName, this.TooltipText, this.TooltipTextColor);
end

function CalendarAttendanceList_OnLeave()
        GameTooltip:Hide();
end

function CalendarAttendanceList_ListViewItemSelected(pMenu, pItem)
        local   vAttendanceList = pMenu.AttendanceList;
        
        CalendarAttendanceList_SetListView(vAttendanceList, pItem);
end

function CalendarAttendanceList_SetListView(pAttendanceList, pViewMode)
        pAttendanceList.ListViewMode = pViewMode;
        
        CalendarAttendanceList_EventChanged(pAttendanceList, pAttendanceList.Database, pAttendanceList.Event);
        CalendarGroupInvites_SetSortMode(pViewMode);
end

CalendarAttendanceList_cPanelNames =
{
        ExpandAll = "MainView",
        GroupTab = "GroupView",
};

function CalendarAttendanceList_GroupListChanged(pAttendanceList)
        CalendarAttendanceList_Update(pAttendanceList);
end

function CalendarAttendanceList_ShowPanel(pAttendanceList, pPanelName)
        local   vAttendanceListName = pAttendanceList:GetName();
        
        for vTabName, vPanelName in CalendarAttendanceList_cPanelNames do
                local   vPanel = getglobal(vAttendanceListName..vPanelName);
                local   vTab = getglobal(vAttendanceListName..vTabName);
                
                if vPanelName == pPanelName then
                        vPanel:Show();
                        PanelTemplates_SelectTab(vTab);
                        
                        -- Handle panel-specific show code
                        
                        if pPanelName == "GroupView" then
                                local   vGroup = CalendarGroupInvites_BeginEvent(pAttendanceList.Database, pAttendanceList.Event);
                                
                                CalendarAttendanceList_SetGroup(pAttendanceList, vGroup);
                                CalendarGroupInvites_SetChangedFunc(CalendarAttendanceList_GroupListChanged, pAttendanceList);
                        end
                else
                        if vPanelName == "GroupView" then
                                CalendarGroupInvites_SetChangedFunc(nil, nil);
                        end
                        
                        vPanel:Hide();
                        PanelTemplates_DeselectTab(vTab);
                end
        end
        
        pAttendanceList.CurrentPanel = pPanelName;
        
        CalendarAttendanceList_AdjustHeight(pAttendanceList);
        CalendarAttendanceList_Update(pAttendanceList);
end

function CalendarAddPlayer_Open()
        CalendarAddPlayer_Reset();
        
        CalendarAddPlayerFrame:Show();
        CalendarAddPlayerFrameName:SetFocus();
end

function CalendarAddPlayer_Reset()
        CalendarAddPlayerFrame.IsWhisper = false;
        CalendarAddPlayerFrame.Guild = nil;
        
        CalendarAddPlayerFrameName:SetText("");
        CalendarAddPlayerFrameLevel:SetText("");
        CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameGuildRankMenu, nil);
        CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameStatusMenu, "Y");
        CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameClassMenu, "?");
        CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameRaceMenu, "?");
        CalendarAddPlayerFrameComment:SetText("");
        
        if IsInGuild() then
                CalendarAddPlayerFrameGuildRankMenu:Show();
        else
                CalendarAddPlayerFrameGuildRankMenu:Hide();
        end
        
        CalendarAddPlayerFrameDeleteButton:Hide();
        
        CalendarAddPlayerFrameWhisper:Hide();
        CalendarAddPlayerFrame:SetHeight(CalendarAddPlayerFrame.NormalHeight);
end

function CalendarAddPlayer_OpenWhisper(pName, pDate, pTime, pWhispers)
        CalendarAddPlayer_Reset();
        
        CalendarAddPlayerFrame.IsWhisper = true;
        CalendarAddPlayerFrame.Name = pName;
        CalendarAddPlayerFrame.Date = pDate;
        CalendarAddPlayerFrame.Time = pTime;
        CalendarAddPlayerFrame.Whispers = pWhispers;
        
        CalendarAddPlayerFrameName:SetText(pName);
        CalendarAddPlayer_AutoCompletePlayerInfo();
        
        CalendarAddPlayerFrameDeleteButton:Show();
        
        CalendarAddPlayerFrameWhisper:Show();
        CalendarAddPlayerFrameWhisperRecent:SetText(pWhispers[table.getn(pWhispers)]);
        
        if gGroupCalendar_PlayerSettings.LastWhisperConfirmMessage then
                CalendarAddPlayerFrameWhisperReply:SetText(gGroupCalendar_PlayerSettings.LastWhisperConfirmMessage);
        end
        
        if gGroupCalendar_PlayerSettings.LastWhisperStatus then
                CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameStatusMenu, gGroupCalendar_PlayerSettings.LastWhisperStatus);
        end
        
        local   vColor = ChatTypeInfo["WHISPER"];
        
        CalendarAddPlayerFrameWhisperRecent:SetTextColor(vColor.r, vColor.g, vColor.b);
        CalendarAddPlayerFrameWhisperReply:SetTextColor(vColor.r, vColor.g, vColor.b);
        
        CalendarAddPlayerFrame:SetHeight(CalendarAddPlayerFrame.NormalHeight + CalendarAddPlayerFrameWhisper:GetHeight());
        
        CalendarAddPlayerFrame:Show();
        CalendarAddPlayerFrameName:SetFocus();
end

function CalendarAddPlayer_SaveNext()
        if CalendarAddPlayerFrame.IsWhisper then
                local   vNextWhisper = CalendarWhisperLog_GetNextWhisper(CalendarAddPlayerFrame.Name);
                
                CalendarAddPlayer_Save();
                
                if vNextWhisper then
                        CalendarAddPlayer_OpenWhisper(vNextWhisper.mName, vNextWhisper.mDate, vNextWhisper.mTime, vNextWhisper.mWhispers);
                else
                        CalendarAddPlayer_Cancel();
                end
        else
                CalendarAddPlayer_Save();
                CalendarAddPlayer_Open();
        end
end

function CalendarAddPlayer_Delete()
        if not CalendarAddPlayerFrame.IsWhisper then
                return;
        end
        
        local   vNextWhisper = CalendarWhisperLog_GetNextWhisper(CalendarAddPlayerFrame.Name);
        
        CalendarWhisperLog_RemovePlayer(CalendarAddPlayerFrame.Name);
        
        if vNextWhisper then
                CalendarAddPlayer_OpenWhisper(vNextWhisper.mName, vNextWhisper.mDate, vNextWhisper.mTime, vNextWhisper.mWhispers);
        else
                CalendarAddPlayer_Cancel();
        end
end

function CalendarAddPlayer_EditRSVP(pRSVP)
        CalendarAddPlayer_Reset();
        
        CalendarAddPlayerFrame.RSVP = pRSVP;
        CalendarAddPlayerFrameName:SetText(pRSVP.mName);
        CalendarAddPlayerFrameLevel:SetText(pRSVP.mLevel);
        CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameStatusMenu, pRSVP.mStatus);
        CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameClassMenu, pRSVP.mClassCode);
        CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameRaceMenu, pRSVP.mRaceCode);
        
        local   vGuildRank = EventDatabase_MapGuildRank(pRSVP.mGuild, pRSVP.mGuildRank);
        
        CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameGuildRankMenu, vGuildRank);
        
        if pRSVP.mComment then
                CalendarAddPlayerFrameComment:SetText(pRSVP.mComment);
        end
        
        CalendarAddPlayerFrame:Show();
        CalendarAddPlayerFrameName:SetFocus();
end

function CalendarAddPlayer_Done()
        CalendarAddPlayer_Save();
        CalendarAddPlayerFrame:Hide();
end

function CalendarAddPlayer_Cancel()
        CalendarAddPlayerFrame:Hide();
end

function CalendarAddPlayer_Save()
        local   vName = CalendarAddPlayerFrameName:GetText();
        
        if vName == "" then
                return;
        end
        
        local   vStatusCode = UIDropDownMenu_GetSelectedValue(CalendarAddPlayerFrameStatusMenu);
        local   vClassCode = UIDropDownMenu_GetSelectedValue(CalendarAddPlayerFrameClassMenu);
        local   vRaceCode = UIDropDownMenu_GetSelectedValue(CalendarAddPlayerFrameRaceMenu);
        local   vLevel = tonumber(CalendarAddPlayerFrameLevel:GetText());
        local   vComment = CalendarAddPlayerFrameComment:GetText();
        local   vGuild = CalendarAddPlayerFrame.Guild;
        local   vGuildRank = UIDropDownMenu_GetSelectedValue(CalendarAddPlayerFrameGuildRankMenu);
        
        if not vGuild then
                vGuild = gGroupCalendar_PlayerGuild;
        end
        
        if not vGuildRank then
                vGuild = nil;
        end
        
        local vRSVP = EventDatabase_CreatePlayerRSVP(
                                                        gCalendarEventEditor_Database,
                                                        gCalendarEventEditor_Event,
                                                        vName,
                                                        vRaceCode,
                                                        vClassCode,
                                                        vLevel,
                                                        vStatusCode,
                                                        vComment,
                                                        vGuild,
                                                        vGuildRank,
                                                        nil);
        
        if CalendarAddPlayerFrame.RSVP then
                -- if CalendarAddPlayerFrame.RSVP.mGuild then
                --      vRSVP.mGuild = CalendarAddPlayerFrame.RSVP.mGuild;
                --      vRSVP.mGuildRank = CalendarAddPlayerFrame.RSVP.mGuildRank;
                -- end
                
                vRSVP.mDate = CalendarAddPlayerFrame.RSVP.mDate;
                vRSVP.mTime = CalendarAddPlayerFrame.RSVP.mTime;
                vRSVP.mAlts = CalendarAddPlayerFrame.RSVP.mAlts;
                
                -- Save the guild rank mapping
                
                EventDatabase_SetGuildRankMapping(
                                CalendarAddPlayerFrame.RSVP.mGuild,
                                CalendarAddPlayerFrame.RSVP.mGuildRank,
                                vGuildRank);
        end
        
        --
        
        EventDatabase_AddEventRSVP(
                        gCalendarEventEditor_Database,
                        gCalendarEventEditor_Event,
                        vName,
                        vRSVP)
        
        if CalendarAddPlayerFrame.IsWhisper then
                CalendarWhisperLog_RemovePlayer(CalendarAddPlayerFrame.Name);
        end
        
        -- Send the reply /w if there is one
        
        if CalendarAddPlayerFrameWhisper:IsVisible() then
                local   vReplyWhisper = CalendarAddPlayerFrameWhisperReply:GetText();
                
                if vReplyWhisper and vReplyWhisper ~= "" then
                        gGroupCalendar_PlayerSettings.LastWhisperConfirmMessage = vReplyWhisper;
                        SendChatMessage(vReplyWhisper, "WHISPER", nil, CalendarAddPlayerFrame.Name);
                end
                
                -- Remember what status was used
                
                gGroupCalendar_PlayerSettings.LastWhisperStatus = UIDropDownMenu_GetSelectedValue(CalendarAddPlayerFrameStatusMenu);
        end
end

function CalendarAddPlayer_AutoCompletePlayerInfo()
        local   vName = CalendarAddPlayerFrameName:GetText();
        local   vUpperName = strupper(vName);
        
        local   vGuildMemberIndex = CalendarNetwork_GetGuildMemberIndex(vName);
        
        if vGuildMemberIndex then
                local   vMemberName, vRank, vRankIndex, vLevel, vClass, vZone, vNote, vOfficerNote, vOnline = GetGuildRosterInfo(vGuildMemberIndex);
                
                CalendarAddPlayerFrameName:SetText(vMemberName);
                CalendarAddPlayerFrameLevel:SetText(vLevel);
                CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameClassMenu, EventDatabase_GetClassCodeByClass(vClass));
                -- CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameRaceMenu, EventDatabase_GetRaceCodeByRace(vRace));
                CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameGuildRankMenu, vRankIndex);
                
                CalendarAddPlayerFrame.Guild = gGroupCalendar_PlayerGuild;
                
                return true;
        end
        
        local   vNumFriends = GetNumFriends();
        
        for vFriendIndex = 1, vNumFriends do
                local   vFriendName, vLevel, vClass, vArea, vConnected = GetFriendInfo(vFriendIndex);
                
                if strupper(vFriendName) == vUpperName
                and vConnected then
                        CalendarAddPlayerFrameName:SetText(vFriendName);
                        CalendarAddPlayerFrameLevel:SetText(vLevel);
                        CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameClassMenu, EventDatabase_GetClassCodeByClass(vClass));
                        -- CalendarDropDown_SetSelectedValue(CalendarAddPlayerFrameRaceMenu, EventDatabase_GetRaceCodeByRace(vRace));
                end
        end
        
        return false;
end

function CalendarAddPlayerWhisper_OnEnter()
        if not CalendarAddPlayerFrame.IsWhisper then
                return;
        end
        
        CalendarAttendanceList_ShowMessageTooltip(this, CalendarAddPlayerFrame.Name, CalendarAddPlayerFrame.Whispers, ChatTypeInfo["WHISPER"]);
end

function CalendarAddPlayerWhisper_OnLeave()
        GameTooltip:Hide();
end

function CalendarAddPlayerWhisper_Reply()
        local   vName;
        
        if CalendarAddPlayerFrame.Name then
                vName = CalendarAddPlayerFrame.Name;
        else
                vName = CalendarAddPlayerFrameName:GetText()
        end

        if not vName or vName == "" then
                return;
        end
        
        DEFAULT_CHAT_FRAME.editBox.chatType = "WHISPER";
        DEFAULT_CHAT_FRAME.editBox.tellTarget = vName;
        
        ChatEdit_UpdateHeader(DEFAULT_CHAT_FRAME.editBox);
        
        if not DEFAULT_CHAT_FRAME.editBox:IsVisible() then
                ChatFrame_OpenChat("", DEFAULT_CHAT_FRAME);
        end
end

function CalendarEventEditor_AttendanceMenuItemSelected(pOwner, pValue)
        local   vAttendanceItem = pOwner:GetParent();
        local   vLineIndex = vAttendanceItem:GetID();
        local   vAttendanceList = vAttendanceItem:GetParent();
        
        local   vItem = CalendarAttendanceList_GetIndexedItem(vAttendanceList, vLineIndex);
        
        if pValue == "DELETE" then
                if vItem.mType == "Whisper" then
                        CalendarWhisperLog_RemovePlayer(vItem.mName);
                else
                        gGroupCalendar_RSVPToDelete = vItem;
                        StaticPopup_Show("CONFIRM_CALENDAR_RSVP_DELETE", vItem.mName);
                end
        elseif pValue == "ADD" then
                if vItem.mType == "Whisper" then
                        CalendarAddPlayer_OpenWhisper(vItem.mName, vItem.mDate, vItem.mTime, vItem.mWhispers);
                end
        elseif pValue == "EDIT" then
                CalendarAddPlayer_EditRSVP(vItem);
        elseif pValue == "INVITE" then
                InviteByName(vItem.mName);
        else
                vItem.mStatus = pValue;
                
                EventDatabase_AddEventRSVP(
                                gCalendarEventEditor_Database,
                                gCalendarEventEditor_Event,
                                vItem.mName,
                                vItem);
        end
end

function CalendarClassLimitItem_SetClassName(pItem, pClassName)
        local   vItemName = pItem:GetName();
        local   vLabelItem = getglobal(vItemName.."Label");
        local   vSeparatorItem = getglobal(vItemName.."Separator");
        
        local   vLabel = getglobal("GroupCalendar_c"..pClassName.."sLabel");
        vLabelItem:SetText(vLabel);

        local   vColor = RAID_CLASS_COLORS[getglobal("GroupCalendar_c"..pClassName.."ClassColorName")];
        
        vLabelItem:SetTextColor(vColor.r, vColor.g, vColor.b);
        vSeparatorItem:SetTextColor(vColor.r, vColor.g, vColor.b);
end

function CalendarAttendanceTotals_OnShow(pTotalsFrame)
        local   vFrameName = this:GetName();
        local   vPaladinsFrame = getglobal(vFrameName.."Paladins");
        local   vPaladinsLabelFrame = getglobal(vFrameName.."PaladinsLabel");
        local   vShamansFrame = getglobal(vFrameName.."Shamans");
        local   vShamansLabelFrame = getglobal(vFrameName.."ShamansLabel");
        
        if gGroupCalendar_PlayerFactionGroup == "Alliance" then
                vPaladinsFrame:Show();
                vPaladinsLabelFrame:Show();
                vShamansFrame:Hide();
                vShamansLabelFrame:Hide();
        else
                vPaladinsFrame:Hide();
                vPaladinsLabelFrame:Hide();
                vShamansFrame:Show();
                vShamansLabelFrame:Show();
        end
end