// swad_timetable.c: timetables /* SWAD (Shared Workspace At a Distance), is a web platform developed at the University of Granada (Spain), and used to support university teaching. This file is part of SWAD core. Copyright (C) 1999-2017 Antonio Caņas Vargas This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ /*****************************************************************************/ /*********************************** Headers *********************************/ /*****************************************************************************/ #include // For NULL #include // For fprintf, etc. #include // For string functions #include "swad_calendar.h" #include "swad_database.h" #include "swad_global.h" #include "swad_parameter.h" #include "swad_text.h" #include "swad_timetable.h" /*****************************************************************************/ /*************************** External constants ******************************/ /*****************************************************************************/ /*****************************************************************************/ /************** External global variables from others modules ****************/ /*****************************************************************************/ extern struct Globals Gbl; /*****************************************************************************/ /*************************** Internal constants ******************************/ /*****************************************************************************/ #define TT_START_HOUR 6 // Day starts at 6:00 #define TT_END_HOUR 24 // Day ends at 24:00 #define TT_HOURS_PER_DAY (TT_END_HOUR - TT_START_HOUR) // From 6:00 to 24:00 #define TT_DAYS 7 // Seven days per week #define TT_MAX_COLUMNS_PER_CELL 3 // Maximum number of items (i.e. classes) in a timetable cell (1, 2, 3 or 4) #define TT_NUM_MINICOLUMNS_PER_DAY 6 // Least common multiple of 1,2,3,...,TT_MAX_COLUMNS_PER_CELL #define TT_PERCENT_WIDTH_OF_A_MINICOLUMN 2 // Width (%) of each minicolumn #define TT_PERCENT_WIDTH_OF_A_DAY (TT_PERCENT_WIDTH_OF_A_MINICOLUMN * TT_NUM_MINICOLUMNS_PER_DAY) // Width (%) of each day #define TT_PERCENT_WIDTH_OF_ALL_DAYS (TT_PERCENT_WIDTH_OF_A_DAY * TT_DAYS) // Width (%) of all days #define TT_PERCENT_WIDTH_OF_A_SEPARATION_COLUMN 1 // Width (%) of left and right columns (frame) #define TT_PERCENT_WIDTH_OF_AN_HOUR_COLUMN ((100 - TT_PERCENT_WIDTH_OF_ALL_DAYS - TT_PERCENT_WIDTH_OF_A_SEPARATION_COLUMN * 2) / 2) // Width (%) of the separation columns #define TT_MAX_BYTES_STR_CLASS_TYPE 256 #define TT_MAX_BYTES_STR_DURATION 32 // "hh:mm h" /*****************************************************************************/ /******************************* Internal types ******************************/ /*****************************************************************************/ /*****************************************************************************/ /************************* Internal global variables *************************/ /*****************************************************************************/ char *TimeTableStrsClassTypeDB[TT_NUM_CLASS_TYPES] = { "free", "lecture", "practical", "tutoring", }; char *TimeTableStrsClassTypeOldDB[TT_NUM_CLASS_TYPES] = // TODO: Remove { "libre", "teoria", "practicas", "tutorias", }; char TimeTableCharsDaysOld[TT_DAYS] = // TODO: Remove { 'L', // Monday 'M', // Tuesday 'X', // Wednesday 'J', // Thursday 'V', // Friday 'S', // Saturday 'D', // Sunday }; struct TimeTableColumn { long CrsCod; // Course code (-1 if no course selected) long GrpCod; // Group code (-1 if no group selected) TT_HourType_t HourType; TT_ClassType_t ClassType; unsigned DurationMinutes; unsigned DurationOld; // TODO: Remove char Place[TT_MAX_BYTES_PLACE + 1]; char Group[Grp_MAX_BYTES_GROUP_NAME + 1]; }; struct { unsigned NumColumns; struct TimeTableColumn Columns[TT_MAX_COLUMNS_PER_CELL]; } TimeTableOld[TT_DAYS][TT_HOURS_PER_DAY * 12]; struct { unsigned NumColumns; struct TimeTableColumn Columns[TT_MAX_COLUMNS_PER_CELL]; } TT_TimeTable[TT_DAYS][TT_HOURS_PER_DAY * 12]; bool TimeTableHoursChecked[TT_HOURS_PER_DAY * 12]; /*****************************************************************************/ /***************************** Internal prototypes **************************/ /*****************************************************************************/ static void TT_ShowTimeTableGrpsSelected (void); static void TT_GetParamsTimeTable (void); static void TT_PutContextualIcons (void); static void TT_PutFormToSelectWhichGroupsToShow (void); static void TT_PutIconToViewCrsTT (void); static void TT_PutIconToViewMyTT (void); static void TT_WriteCrsTimeTableIntoDB (long CrsCod); static void TT_WriteTutTimeTableIntoDB (long UsrCod); static void TT_CreatTimeTableFromDB (long UsrCod); static void TT_ModifTimeTable (void); static void TT_DrawTimeTable (void); static void TT_TimeTableDrawAdjustRow (void); static void TT_TimeTableDrawDaysCells (void); static void TT_TimeTableDrawHourCell (unsigned HourOld,unsigned Min,const char *Align); static unsigned TT_TimeTableCalculateColsToDraw (unsigned Day,unsigned HourOld); static void TT_DrawCellAlignTimeTable (void); static void TT_TimeTableDrawCell (unsigned Day,unsigned HourOld,unsigned Column,unsigned ColSpan, long CrsCod,TT_HourType_t HourType,TT_ClassType_t ClassType, unsigned DurationOld,char *Group,long GrpCod,char *Place); /*****************************************************************************/ /*********** Show whether only my groups or all groups are shown *************/ /*****************************************************************************/ static void TT_ShowTimeTableGrpsSelected (void) { extern const char *Txt_Groups_OF_A_USER; extern const char *Txt_All_groups; fprintf (Gbl.F.Out,"
"); switch (Gbl.CurrentCrs.Grps.WhichGrps) { case Grp_ONLY_MY_GROUPS: fprintf (Gbl.F.Out,Txt_Groups_OF_A_USER, Gbl.Usrs.Me.UsrDat.FullName); break; case Grp_ALL_GROUPS: fprintf (Gbl.F.Out,"%s",Txt_All_groups); break; } fprintf (Gbl.F.Out,"
"); } /*****************************************************************************/ /******************** Get paramaters for timetable editing *******************/ /*****************************************************************************/ static void TT_GetParamsTimeTable (void) { char StrClassTypeOld[TT_MAX_BYTES_STR_CLASS_TYPE + 1]; char StrDuration[TT_MAX_BYTES_STR_DURATION + 1]; unsigned Hours; unsigned Minutes; /***** Get day (0: monday, 1: tuesday,..., 6: sunday *****/ Gbl.TimeTable.Day = (unsigned) Par_GetParToUnsignedLong ("ModTTDay", 0, TT_DAYS - 1, 0); /***** Get hour *****/ Gbl.TimeTable.HourOld = (unsigned) Par_GetParToUnsignedLong ("ModTTHour", 0, TT_HOURS_PER_DAY * 2 - 1, 0); /***** Get number of column *****/ Gbl.TimeTable.Column = (unsigned) Par_GetParToUnsignedLong ("ModTTCol", 0, TT_MAX_COLUMNS_PER_CELL - 1, 0); /***** Get class type *****/ Par_GetParToText ("ModTTClassTypeOld",StrClassTypeOld,TT_MAX_BYTES_STR_CLASS_TYPE); for (Gbl.TimeTable.ClassType = (TT_ClassType_t) 0; Gbl.TimeTable.ClassType < (TT_ClassType_t) TT_NUM_CLASS_TYPES; Gbl.TimeTable.ClassType++) if (!strcmp (StrClassTypeOld,TimeTableStrsClassTypeOldDB[Gbl.TimeTable.ClassType])) break; if (Gbl.TimeTable.ClassType == (TT_ClassType_t) TT_NUM_CLASS_TYPES) Lay_ShowErrorAndExit ("Type of timetable cell is missing."); /***** Get class duration *****/ Par_GetParToText ("ModTTDur",StrDuration,TT_MAX_BYTES_STR_DURATION); if (sscanf (StrDuration,"%u:%u",&Hours,&Minutes) != 2) Lay_ShowErrorAndExit ("Duration is missing."); Gbl.TimeTable.DurationOld = Hours * 2 + Minutes / 30; /***** Get group code *****/ Gbl.TimeTable.GrpCod = Par_GetParToLong ("ModTTGrpCod"); /***** Get place *****/ Par_GetParToText ("ModTTPlace",Gbl.TimeTable.Place,TT_MAX_BYTES_PLACE); } /*****************************************************************************/ /**************************** Show class timetable ***************************/ /*****************************************************************************/ void TT_ShowClassTimeTable (void) { extern const char *Hlp_COURSE_Timetable; extern const char *Hlp_PROFILE_Timetable; extern const char *Txt_TIMETABLE_TYPES[TT_NUM_TIMETABLE_TYPES]; const char *Help[TT_NUM_TIMETABLE_TYPES] = { Hlp_COURSE_Timetable, // TT_COURSE_TIMETABLE Hlp_PROFILE_Timetable, // TT_MY_TIMETABLE NULL, // TT_TUTORING_TIMETABLE }; Act_Action_t ActChgTT1stDay[TT_NUM_TIMETABLE_TYPES] = { ActChgCrsTT1stDay,// TT_COURSE_TIMETABLE ActChgMyTT1stDay, // TT_MY_TIMETABLE ActUnk, // TT_TUTORING_TIMETABLE }; bool PrintView = (Gbl.Action.Act == ActPrnCrsTT || Gbl.Action.Act == ActPrnMyTT);; /***** Initializations *****/ switch (Gbl.Action.Act) { case ActSeeCrsTT: case ActPrnCrsTT: case ActChgCrsTT1stDay: Gbl.TimeTable.Type = TT_COURSE_TIMETABLE; break; case ActSeeMyTT: case ActPrnMyTT: case ActChgMyTT1stDay: Gbl.TimeTable.Type = TT_MY_TIMETABLE; break; default: Lay_ShowErrorAndExit ("Wrong action."); } Gbl.TimeTable.ContextualIcons.PutIconEditCrsTT = (Gbl.TimeTable.Type == TT_COURSE_TIMETABLE && !PrintView && Gbl.Usrs.Me.LoggedRole >= Rol_TEACHER); Gbl.TimeTable.ContextualIcons.PutIconEditOfficeHours = (Gbl.TimeTable.Type == TT_MY_TIMETABLE && !PrintView && (Gbl.Usrs.Me.AvailableRoles & (1 << Rol_TEACHER))); Gbl.TimeTable.ContextualIcons.PutIconPrint = !PrintView; /***** Get whether to show only my groups or all groups *****/ Grp_GetParamWhichGrps (); /***** Start frame *****/ Lay_StartRoundFrame ("100%",Txt_TIMETABLE_TYPES[Gbl.TimeTable.Type], (Gbl.TimeTable.ContextualIcons.PutIconEditCrsTT || Gbl.TimeTable.ContextualIcons.PutIconEditOfficeHours || Gbl.TimeTable.ContextualIcons.PutIconPrint) ? TT_PutContextualIcons : NULL, Help[Gbl.TimeTable.Type]); /***** Start time table drawing *****/ if (Gbl.TimeTable.Type == TT_COURSE_TIMETABLE) Lay_WriteHeaderClassPhoto (PrintView,false, Gbl.CurrentIns.Ins.InsCod,Gbl.CurrentDeg.Deg.DegCod,Gbl.CurrentCrs.Crs.CrsCod); if (PrintView) /***** Show whether only my groups or all groups are selected *****/ TT_ShowTimeTableGrpsSelected (); else { /***** Select whether show only my groups or all groups *****/ if ( Gbl.TimeTable.Type == TT_MY_TIMETABLE || (Gbl.TimeTable.Type == TT_COURSE_TIMETABLE && Gbl.CurrentCrs.Grps.NumGrps)) TT_PutFormToSelectWhichGroupsToShow (); /***** Show form to change first day of week *****/ Cal_ShowFormToSelFirstDayOfWeek (ActChgTT1stDay[Gbl.TimeTable.Type], Grp_PutParamWhichGrps, "ICO25x25"); } /***** Show the time table *****/ TT_ShowTimeTable (Gbl.Usrs.Me.UsrDat.UsrCod); /***** End frame *****/ Lay_EndRoundFrame (); } /*****************************************************************************/ /***************** Put contextual icons above the time table *****************/ /*****************************************************************************/ static void TT_PutContextualIcons (void) { extern const char *Txt_Edit; extern const char *Txt_Edit_office_hours; extern const char *Txt_Print; if (Gbl.TimeTable.ContextualIcons.PutIconEditCrsTT) Lay_PutContextualLink (ActEdiCrsTT,NULL,Grp_PutParamWhichGrps, "edit64x64.png", Txt_Edit,NULL, NULL); if (Gbl.TimeTable.ContextualIcons.PutIconEditOfficeHours) Lay_PutContextualLink (ActEdiTut,NULL,NULL, "edit64x64.png", Txt_Edit_office_hours,NULL, NULL); if (Gbl.TimeTable.ContextualIcons.PutIconPrint) Lay_PutContextualLink (Gbl.TimeTable.Type == TT_COURSE_TIMETABLE ? ActPrnCrsTT : ActPrnMyTT, NULL,Grp_PutParamWhichGrps, "print64x64.png", Txt_Print,NULL, NULL); } /*****************************************************************************/ /***************** Put form to select which groups to show *******************/ /*****************************************************************************/ static void TT_PutFormToSelectWhichGroupsToShow (void) { Act_Action_t ActSeeTT[TT_NUM_TIMETABLE_TYPES] = { ActSeeCrsTT, // TT_COURSE_TIMETABLE ActSeeMyTT, // TT_MY_TIMETABLE ActUnk, // TT_TUTORING_TIMETABLE }; Grp_ShowFormToSelWhichGrps (ActSeeTT[Gbl.TimeTable.Type],NULL); } /*****************************************************************************/ /********************** Show course timetable for edition ********************/ /*****************************************************************************/ void TT_EditCrsTimeTable (void) { extern const char *Hlp_COURSE_Timetable; extern const char *Txt_TIMETABLE_TYPES[TT_NUM_TIMETABLE_TYPES]; /***** Editable time table *****/ Gbl.TimeTable.Type = TT_COURSE_TIMETABLE; Lay_StartRoundFrame ("100%",Txt_TIMETABLE_TYPES[Gbl.TimeTable.Type], TT_PutIconToViewCrsTT,Hlp_COURSE_Timetable); TT_ShowTimeTable (Gbl.Usrs.Me.UsrDat.UsrCod); Lay_EndRoundFrame (); } /*****************************************************************************/ /********************* Show tutor timetable for edition **********************/ /*****************************************************************************/ void TT_EditMyTutTimeTable (void) { extern const char *Hlp_PROFILE_Timetable; extern const char *Txt_TIMETABLE_TYPES[TT_NUM_TIMETABLE_TYPES]; /***** Time table *****/ Gbl.TimeTable.Type = TT_TUTORING_TIMETABLE; Lay_StartRoundFrame ("100%",Txt_TIMETABLE_TYPES[Gbl.TimeTable.Type], TT_PutIconToViewMyTT,Hlp_PROFILE_Timetable); TT_ShowTimeTable (Gbl.Usrs.Me.UsrDat.UsrCod); Lay_EndRoundFrame (); } /*****************************************************************************/ /********************** Put icon to view course timetable ********************/ /*****************************************************************************/ static void TT_PutIconToViewCrsTT (void) { extern const char *Txt_Show_timetable; /***** Link (form) to see course timetable *****/ Lay_PutContextualLink (ActSeeCrsTT,NULL,NULL, "eye-on64x64.png", Txt_Show_timetable,NULL, NULL); } /*****************************************************************************/ /************************ Put icon to view my timetable **********************/ /*****************************************************************************/ static void TT_PutIconToViewMyTT (void) { extern const char *Txt_Show_timetable; /***** Link (form) to see my timetable *****/ Lay_PutContextualLink (ActSeeMyTT,NULL,NULL, "eye-on64x64.png", Txt_Show_timetable,NULL, NULL); } /*****************************************************************************/ /*********** Show course timetable or tutor timetable of a teacher ***********/ /*****************************************************************************/ void TT_ShowTimeTable (long UsrCod) { /***** Create an internal table with the timetable from database *****/ TT_CreatTimeTableFromDB (UsrCod); /***** If timetable must be modified... *****/ if (Gbl.Action.Act == ActChgCrsTT || Gbl.Action.Act == ActChgTut) { /* Get parameters for time table editing */ TT_GetParamsTimeTable (); /* Modify timetable in memory */ TT_ModifTimeTable (); /* Write a new timetable in database */ switch (Gbl.TimeTable.Type) { case TT_COURSE_TIMETABLE: TT_WriteCrsTimeTableIntoDB (Gbl.CurrentCrs.Crs.CrsCod); break; case TT_TUTORING_TIMETABLE: TT_WriteTutTimeTableIntoDB (UsrCod); break; default: break; } /* Get a new table from database */ TT_CreatTimeTableFromDB (UsrCod); } /***** Draw timetable *****/ TT_DrawTimeTable (); } /*****************************************************************************/ /******************* Write course timetable into database ********************/ /*****************************************************************************/ static void TT_WriteCrsTimeTableIntoDB (long CrsCod) { char Query[512 + TT_MAX_BYTES_PLACE + Grp_MAX_BYTES_GROUP_NAME]; unsigned Interval5Minutes; unsigned Day; unsigned Hour; unsigned Min; unsigned Column; /***** Remove former timetable *****/ sprintf (Query,"DELETE FROM timetable_crs WHERE CrsCod=%ld", CrsCod); DB_QueryDELETE (Query,"can not remove former timetable"); /***** Go across the timetable inserting classes into database *****/ for (Day = 0; Day < TT_DAYS; Day++) for (Interval5Minutes = 0, Hour = TT_START_HOUR, Min = 0; Hour < TT_END_HOUR; Interval5Minutes++, Hour += (Min + 5) / 60, Min = (Min + 5) % 60) for (Column = 0; Column < TT_MAX_COLUMNS_PER_CELL; Column++) if (TT_TimeTable[Day][Interval5Minutes].Columns[Column].HourType == TT_FIRST_HOUR && TT_TimeTable[Day][Interval5Minutes].Columns[Column].DurationOld > 0) { sprintf (Query,"INSERT INTO timetable_crs" " (CrsCod,GrpCod,Weekday,StartTime,Duration," "ClassType,Place,GroupName)" " VALUES" " (%ld,%ld,%u,'%02u:%02u:00',SEC_TO_TIME(%u)," "'%s','%s','%s')", CrsCod, TT_TimeTable[Day][Interval5Minutes].Columns[Column].GrpCod, Day, Hour,Min, TT_TimeTable[Day][Interval5Minutes].Columns[Column].DurationMinutes * 60, TimeTableStrsClassTypeOldDB[TT_TimeTable[Day][Interval5Minutes].Columns[Column].ClassType], TT_TimeTable[Day][Interval5Minutes].Columns[Column].Place, TT_TimeTable[Day][Interval5Minutes].Columns[Column].Group); DB_QueryINSERT (Query,"can not create course timetable"); } } /*****************************************************************************/ /********************* Write tutor timetable into database *******************/ /*****************************************************************************/ static void TT_WriteTutTimeTableIntoDB (long UsrCod) { char Query[512 + TT_MAX_BYTES_PLACE]; unsigned HourOld; unsigned Day; unsigned Column; /***** Remove former timetable *****/ sprintf (Query,"DELETE FROM timetable_tut WHERE UsrCod=%ld", UsrCod); DB_QueryDELETE (Query,"can not remove former timetable"); /***** Loop over timetable *****/ for (Day = 0; Day < TT_DAYS; Day++) for (HourOld = 0; HourOld < TT_HOURS_PER_DAY * 2; HourOld++) for (Column = 0; Column < TT_MAX_COLUMNS_PER_CELL; Column++) if (TimeTableOld[Day][HourOld].Columns[Column].HourType == TT_FIRST_HOUR && TimeTableOld[Day][HourOld].Columns[Column].DurationOld > 0) { sprintf (Query,"INSERT INTO timetable_tut" " (UsrCod,DayOld,HourOld,DurationOld,Place)" " VALUES" " (%ld,'%c',%u,%d,'%s')", UsrCod,TimeTableCharsDaysOld[Day],HourOld, TimeTableOld[Day][HourOld].Columns[Column].DurationOld, TimeTableOld[Day][HourOld].Columns[Column].Place); DB_QueryINSERT (Query,"can not create office timetable"); } } /*****************************************************************************/ /********** Create an internal table with timetable from database ************/ /*****************************************************************************/ static void TT_CreatTimeTableFromDB (long UsrCod) { extern const char *Txt_Incomplete_timetable_for_lack_of_space; char Query[4096]; MYSQL_RES *mysql_res; MYSQL_ROW row; unsigned long NumRow; unsigned long NumRows; unsigned HourOld; unsigned Day; unsigned Column; unsigned FirstFreeColumn; unsigned DurationOld; unsigned H; long GrpCod; TT_ClassType_t ClassType = TT_FREE; // Initialized to avoid warning bool TimeTableIsIncomplete = false; bool TimeTableHasSpaceForThisClass; bool Found; /***** Initialize timetable to all free *****/ for (Day = 0; Day < TT_DAYS; Day++) for (HourOld = 0; HourOld < TT_HOURS_PER_DAY * 2; HourOld++) { TimeTableOld[Day][HourOld].NumColumns = 0; for (Column = 0; Column < TT_MAX_COLUMNS_PER_CELL; Column++) { TimeTableOld[Day][HourOld].Columns[Column].CrsCod = -1L; TimeTableOld[Day][HourOld].Columns[Column].GrpCod = -1L; TimeTableOld[Day][HourOld].Columns[Column].HourType = TT_FREE_HOUR; TimeTableOld[Day][HourOld].Columns[Column].ClassType = TT_FREE; TimeTableOld[Day][HourOld].Columns[Column].DurationOld = 0; TimeTableOld[Day][HourOld].Columns[Column].Group[0] = '\0'; TimeTableOld[Day][HourOld].Columns[Column].Place[0] = '\0'; } } /***** Get timetable from database *****/ switch (Gbl.TimeTable.Type) { case TT_MY_TIMETABLE: switch (Gbl.CurrentCrs.Grps.WhichGrps) { case Grp_ONLY_MY_GROUPS: sprintf (Query,"SELECT timetable_crs.DayOld,timetable_crs.HourOld,timetable_crs.DurationOld,timetable_crs.Place," "timetable_crs.ClassTypeOld,timetable_crs.GroupName,timetable_crs.GrpCod,timetable_crs.CrsCod" " FROM timetable_crs,crs_usr" " WHERE crs_usr.UsrCod=%ld" " AND timetable_crs.GrpCod=-1" " AND timetable_crs.CrsCod=crs_usr.CrsCod" " UNION DISTINCT " "SELECT timetable_crs.DayOld,timetable_crs.HourOld,timetable_crs.DurationOld,timetable_crs.Place," "timetable_crs.ClassTypeOld,timetable_crs.GroupName,timetable_crs.GrpCod,timetable_crs.CrsCod" " FROM timetable_crs,crs_grp_usr" " WHERE crs_grp_usr.UsrCod=%ld" " AND timetable_crs.GrpCod=crs_grp_usr.GrpCod" " UNION " "SELECT DayOld,HourOld,DurationOld,Place," "'tutorias' AS ClassTypeOld,'' AS GroupName," "-1 AS GrpCod,-1 AS CrsCod" " FROM timetable_tut" " WHERE UsrCod=%ld" " ORDER BY DayOld,HourOld,ClassTypeOld,GroupName,GrpCod,Place,DurationOld DESC,CrsCod", UsrCod,UsrCod,UsrCod); break; case Grp_ALL_GROUPS: sprintf (Query,"SELECT timetable_crs.DayOld,timetable_crs.HourOld,timetable_crs.DurationOld,timetable_crs.Place," "timetable_crs.ClassTypeOld,timetable_crs.GroupName,timetable_crs.GrpCod,timetable_crs.CrsCod" " FROM timetable_crs,crs_usr" " WHERE crs_usr.UsrCod=%ld" " AND timetable_crs.CrsCod=crs_usr.CrsCod" " UNION " "SELECT DayOld,HourOld,DurationOld,Place," "'tutorias' AS ClassTypeOld,'' AS GroupName," "-1 AS GrpCod,-1 AS CrsCod" " FROM timetable_tut" " WHERE UsrCod=%ld" " ORDER BY DayOld,HourOld,ClassTypeOld," "GroupName,GrpCod,Place,DurationOld DESC,CrsCod", UsrCod,UsrCod); break; } break; case TT_COURSE_TIMETABLE: if (Gbl.CurrentCrs.Grps.WhichGrps == Grp_ALL_GROUPS || Gbl.Action.Act == ActEdiCrsTT || Gbl.Action.Act == ActChgCrsTT) // If we are editing, all groups are shown sprintf (Query,"SELECT DayOld,HourOld,DurationOld,Place,ClassTypeOld,GroupName,GrpCod" " FROM timetable_crs" " WHERE CrsCod=%ld" " ORDER BY DayOld,HourOld,ClassTypeOld,GroupName,GrpCod,Place,DurationOld DESC", Gbl.CurrentCrs.Crs.CrsCod); else sprintf (Query,"SELECT timetable_crs.DayOld,timetable_crs.HourOld,timetable_crs.DurationOld,timetable_crs.Place,timetable_crs.ClassTypeOld,timetable_crs.GroupName,timetable_crs.GrpCod" " FROM timetable_crs,crs_usr" " WHERE timetable_crs.CrsCod=%ld" " AND timetable_crs.GrpCod=-1 AND crs_usr.UsrCod=%ld" " AND timetable_crs.CrsCod=crs_usr.CrsCod" " UNION DISTINCT " "SELECT timetable_crs.DayOld,timetable_crs.HourOld,timetable_crs.DurationOld,timetable_crs.Place," "timetable_crs.ClassTypeOld,timetable_crs.GroupName,timetable_crs.GrpCod" " FROM timetable_crs,crs_grp_usr" " WHERE timetable_crs.CrsCod=%ld" " AND crs_grp_usr.UsrCod=%ld" " AND timetable_crs.GrpCod=crs_grp_usr.GrpCod" " ORDER BY DayOld,HourOld,ClassTypeOld,GroupName,GrpCod,Place,DurationOld DESC", Gbl.CurrentCrs.Crs.CrsCod,UsrCod, Gbl.CurrentCrs.Crs.CrsCod,UsrCod); break; case TT_TUTORING_TIMETABLE: sprintf (Query,"SELECT DayOld,HourOld,DurationOld,Place" " FROM timetable_tut" " WHERE UsrCod=%ld" " ORDER BY DayOld,HourOld,Place,DurationOld DESC", UsrCod); break; } NumRows = DB_QuerySELECT (Query,&mysql_res,"can not get timetable"); /***** Build the table depending on the number of rows of the timetable *****/ for (NumRow = 0; NumRow < NumRows; NumRow++) { row = mysql_fetch_row (mysql_res); if (Gbl.TimeTable.Type == TT_MY_TIMETABLE || Gbl.TimeTable.Type == TT_COURSE_TIMETABLE) /* Group code */ if (sscanf (row[6],"%ld",&GrpCod) != 1) GrpCod = -1; /* Day of week (row[0]) */ for (Day = 0; Day < TT_DAYS; Day++) if (Str_ConvertToUpperLetter (row[0][0]) == TimeTableCharsDaysOld[Day]) break; if (Day == TT_DAYS) Lay_ShowErrorAndExit ("Wrong day of week in timetable."); /* HourOld (row[1]) */ if (sscanf (row[1],"%u",&HourOld) != 1) Lay_ShowErrorAndExit ("Wrong hour in timetable."); /* DurationOld (row[2]) */ if (sscanf (row[2],"%u",&DurationOld) != 1) Lay_ShowErrorAndExit ("Wrong duration in timetable."); /* Type of class */ switch (Gbl.TimeTable.Type) { case TT_COURSE_TIMETABLE: case TT_MY_TIMETABLE: for (ClassType = TT_LECTURE, Found = false; ClassType <= TT_TUTORING; ClassType++) if (!strcmp (row[4],TimeTableStrsClassTypeOldDB[ClassType])) { Found = true; break; } if (!Found) Lay_ShowErrorAndExit ("Wrong type of class in timetable."); break; case TT_TUTORING_TIMETABLE: ClassType = TT_TUTORING; break; } /* Cell has been read without errors. */ if (TimeTableOld[Day][HourOld].NumColumns < TT_MAX_COLUMNS_PER_CELL) // If there's place for another column in this cell... { /* Find the first free column for this day-hour */ FirstFreeColumn = TT_MAX_COLUMNS_PER_CELL; for (Column = 0; Column < TT_MAX_COLUMNS_PER_CELL; Column++) if (TimeTableOld[Day][HourOld].Columns[Column].HourType == TT_FREE_HOUR) { FirstFreeColumn = Column; break; } if (FirstFreeColumn < TT_MAX_COLUMNS_PER_CELL) // If there's place for another column in this cell { /* Check if there's place for all the rows of this class */ TimeTableHasSpaceForThisClass = true; for (H = HourOld + 1; H < HourOld + DurationOld && H < TT_HOURS_PER_DAY * 2; H++) if (TimeTableOld[Day][H].Columns[FirstFreeColumn].HourType != TT_FREE_HOUR) { TimeTableIsIncomplete = true; TimeTableHasSpaceForThisClass = false; break; } if (TimeTableHasSpaceForThisClass) { TimeTableOld[Day][HourOld].Columns[FirstFreeColumn].ClassType = ClassType; TimeTableOld[Day][HourOld].Columns[FirstFreeColumn].DurationOld = DurationOld; TimeTableOld[Day][HourOld].Columns[FirstFreeColumn].HourType = TT_FIRST_HOUR; for (H = HourOld + 1; H < HourOld + DurationOld && H < TT_HOURS_PER_DAY * 2; H++) { TimeTableOld[Day][H].Columns[FirstFreeColumn].HourType = TT_NEXT_HOUR; TimeTableOld[Day][H].NumColumns++; } /* Group and place */ switch (Gbl.TimeTable.Type) { case TT_MY_TIMETABLE: case TT_COURSE_TIMETABLE: TimeTableOld[Day][HourOld].Columns[FirstFreeColumn].CrsCod = (Gbl.TimeTable.Type == TT_MY_TIMETABLE ? Str_ConvertStrCodToLongCod (row[7]) : Gbl.CurrentCrs.Crs.CrsCod); Str_Copy (TimeTableOld[Day][HourOld].Columns[FirstFreeColumn].Group, row[5], Grp_MAX_BYTES_GROUP_NAME); TimeTableOld[Day][HourOld].Columns[FirstFreeColumn].GrpCod = GrpCod; // no break; case TT_TUTORING_TIMETABLE: Str_Copy (TimeTableOld[Day][HourOld].Columns[FirstFreeColumn].Place, row[3], TT_MAX_BYTES_PLACE); break; } /* Increment number of items in this cell */ TimeTableOld[Day][HourOld].NumColumns++; } } else TimeTableIsIncomplete = true; } else TimeTableIsIncomplete = true; } /***** Free structure that stores the query result *****/ DB_FreeMySQLResult (&mysql_res); if (TimeTableIsIncomplete) Lay_ShowAlert (Lay_INFO,Txt_Incomplete_timetable_for_lack_of_space); } /*****************************************************************************/ /*********************** Modify a class in timetable *************************/ /*****************************************************************************/ static void TT_ModifTimeTable (void) { if (TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].HourType == TT_FIRST_HOUR) { /***** Free this cell *****/ TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].GrpCod = -1; TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].HourType = TT_FREE_HOUR; TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].ClassType = TT_FREE; TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].DurationOld = 0; TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].Group[0] = '\0'; TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].Place[0] = '\0'; TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].NumColumns--; } if (Gbl.TimeTable.ClassType != TT_FREE && Gbl.TimeTable.DurationOld > 0 && TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].NumColumns < TT_MAX_COLUMNS_PER_CELL) { /***** Change this cell *****/ TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].NumColumns++; TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].GrpCod = Gbl.TimeTable.GrpCod; TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].HourType = TT_FIRST_HOUR; TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].ClassType = Gbl.TimeTable.ClassType; TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].DurationOld = Gbl.TimeTable.DurationOld; Str_Copy (TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].Group, Gbl.TimeTable.Group, Grp_MAX_BYTES_GROUP_NAME); Str_Copy (TimeTableOld[Gbl.TimeTable.Day][Gbl.TimeTable.HourOld].Columns[Gbl.TimeTable.Column].Place, Gbl.TimeTable.Place, TT_MAX_BYTES_PLACE); } } /*****************************************************************************/ /********************* Draw timetable using internal table *******************/ /*****************************************************************************/ static void TT_DrawTimeTable (void) { bool Editing = false; unsigned DayColumn; // Column from left (0) to right (6) unsigned Day; // Day of week unsigned HourOld; unsigned Min; unsigned H; unsigned Column; unsigned ColumnsToDraw; unsigned ColumnsToDrawIncludingExtraColumn; unsigned ContinuousFreeMinicolumns; switch (Gbl.Action.Act) { case ActSeeCrsTT: case ActPrnCrsTT: case ActChgCrsTT1stDay: case ActSeeMyTT: case ActPrnMyTT: case ActChgMyTT1stDay: case ActSeeRecOneTch: case ActSeeRecSevTch: Editing = false; break; case ActEdiCrsTT: case ActChgCrsTT: case ActEdiTut: case ActChgTut: // If editing and there's place for more columns, a potential new column is added at the end of each day Editing = true; break; } /***** Table start *****/ fprintf (Gbl.F.Out,""); /***** Top row used for column adjustement *****/ TT_TimeTableDrawAdjustRow (); /***** Row with day names *****/ fprintf (Gbl.F.Out,"" "", TT_PERCENT_WIDTH_OF_AN_HOUR_COLUMN,TT_START_HOUR); TT_DrawCellAlignTimeTable (); TT_TimeTableDrawDaysCells (); TT_DrawCellAlignTimeTable (); fprintf (Gbl.F.Out,"" "", TT_PERCENT_WIDTH_OF_AN_HOUR_COLUMN, TT_START_HOUR); /***** Get list of groups types and groups in this course *****/ if (Gbl.Action.Act == ActEdiCrsTT || Gbl.Action.Act == ActChgCrsTT) Grp_GetListGrpTypesAndGrpsInThisCrs (Grp_ONLY_GROUP_TYPES_WITH_GROUPS); /***** Write the table row by row *****/ for (HourOld = 0, Min = 5; HourOld < TT_HOURS_PER_DAY * 12; HourOld++, Min = (Min + 5) % 60) { fprintf (Gbl.F.Out,""); /* HourOld */ if (HourOld % 2) TT_TimeTableDrawHourCell (TT_START_HOUR + (HourOld + 2) / 12,Min,"RIGHT_MIDDLE"); /* Empty column used to adjust height */ TT_DrawCellAlignTimeTable (); /* Row for this hour */ for (DayColumn = 0; DayColumn < TT_DAYS; DayColumn++) { /* Day == 0 ==> monday, ... Day == 6 ==> sunday */ Day = (DayColumn + Gbl.Prefs.FirstDayOfWeek) % 7; /* Check how many colums are needed. For each item (class) in this hour from left to right, we must check the maximum of columns */ for (H = 0; H < TT_HOURS_PER_DAY * 12; H++) TimeTableHoursChecked[H] = false; ColumnsToDraw = TT_TimeTableCalculateColsToDraw (Day,HourOld); if (!Editing && ColumnsToDraw == 0) ColumnsToDraw = 1; ColumnsToDrawIncludingExtraColumn = ColumnsToDraw; if (Editing && ColumnsToDraw < TT_MAX_COLUMNS_PER_CELL) ColumnsToDrawIncludingExtraColumn++; /* Draw cells */ for (Column = 0, ContinuousFreeMinicolumns = 0; Column < ColumnsToDrawIncludingExtraColumn; Column++) if (TimeTableOld[Day][HourOld].Columns[Column].HourType == TT_FREE_HOUR) ContinuousFreeMinicolumns += TT_NUM_MINICOLUMNS_PER_DAY / ColumnsToDrawIncludingExtraColumn; else { if (ContinuousFreeMinicolumns) { TT_TimeTableDrawCell (Day,HourOld,Column-1,ContinuousFreeMinicolumns, -1L,TT_FREE_HOUR,TT_FREE,0,NULL,-1,NULL); ContinuousFreeMinicolumns = 0; } TT_TimeTableDrawCell (Day,HourOld,Column,TT_NUM_MINICOLUMNS_PER_DAY/ColumnsToDrawIncludingExtraColumn, TimeTableOld[Day][HourOld].Columns[Column].CrsCod, TimeTableOld[Day][HourOld].Columns[Column].HourType, TimeTableOld[Day][HourOld].Columns[Column].ClassType, TimeTableOld[Day][HourOld].Columns[Column].DurationOld, TimeTableOld[Day][HourOld].Columns[Column].Group, TimeTableOld[Day][HourOld].Columns[Column].GrpCod, TimeTableOld[Day][HourOld].Columns[Column].Place); } if (ContinuousFreeMinicolumns) TT_TimeTableDrawCell (Day,HourOld,Column-1,ContinuousFreeMinicolumns, -1L,TT_FREE_HOUR,TT_FREE,0,NULL,-1L,NULL); } /* Empty column used to adjust height */ TT_DrawCellAlignTimeTable (); /* HourOld */ if (HourOld % 2) TT_TimeTableDrawHourCell (TT_START_HOUR + (HourOld + 2) / 12,Min,"LEFT_MIDDLE"); fprintf (Gbl.F.Out,""); } /***** Free list of groups types and groups in this course *****/ if (Gbl.Action.Act == ActEdiCrsTT || Gbl.Action.Act == ActChgCrsTT) Grp_FreeListGrpTypesAndGrps (); /***** Row with day names *****/ fprintf (Gbl.F.Out,""); TT_DrawCellAlignTimeTable (); TT_TimeTableDrawDaysCells (); TT_DrawCellAlignTimeTable (); fprintf (Gbl.F.Out,""); /***** Bottom row used for column adjustement *****/ TT_TimeTableDrawAdjustRow (); /***** End of the table *****/ fprintf (Gbl.F.Out,"
" "%02u" "" "%02u" "
"); } /*****************************************************************************/ /********** Draw a row used for column adjustement in a time table ***********/ /*****************************************************************************/ static void TT_TimeTableDrawAdjustRow (void) { unsigned Day; unsigned Minicolumn; fprintf (Gbl.F.Out,"" "" " " "", TT_PERCENT_WIDTH_OF_AN_HOUR_COLUMN); TT_DrawCellAlignTimeTable (); for (Day = 0; Day < TT_DAYS; Day++) for (Minicolumn = 0; Minicolumn < TT_NUM_MINICOLUMNS_PER_DAY; Minicolumn++) fprintf (Gbl.F.Out,"" " " "", TT_PERCENT_WIDTH_OF_A_MINICOLUMN); TT_DrawCellAlignTimeTable (); fprintf (Gbl.F.Out,"" " " "" "", TT_PERCENT_WIDTH_OF_AN_HOUR_COLUMN); } /*****************************************************************************/ /****************** Draw cells with day names in a time table ****************/ /*****************************************************************************/ static void TT_TimeTableDrawDaysCells (void) { extern const char *Txt_DAYS_CAPS[7]; unsigned DayColumn; unsigned Day; for (DayColumn = 0; DayColumn < TT_DAYS; DayColumn++) { Day = (DayColumn + Gbl.Prefs.FirstDayOfWeek) % 7; fprintf (Gbl.F.Out,"" "%s" "", TT_NUM_MINICOLUMNS_PER_DAY, Day == 6 ? "TT_SUNDAY" : // Sunday drawn in red "TT_DAY", // Monday to Saturday TT_PERCENT_WIDTH_OF_A_DAY, Txt_DAYS_CAPS[Day]); } } /*****************************************************************************/ /****************** Draw cells with day names in a time table ****************/ /*****************************************************************************/ static void TT_TimeTableDrawHourCell (unsigned HourOld,unsigned Min,const char *Align) { fprintf (Gbl.F.Out,"", Min ? "TT_HOUR_SMALL" : "TT_HOUR_BIG", Align, TT_PERCENT_WIDTH_OF_AN_HOUR_COLUMN); fprintf (Gbl.F.Out,"%02u",HourOld); if (Min) fprintf (Gbl.F.Out,":%02u",Min); fprintf (Gbl.F.Out,""); } /*****************************************************************************/ /**** Calculate recursively number of columns to draw for a day and hour *****/ /*****************************************************************************/ static unsigned TT_TimeTableCalculateColsToDraw (unsigned Day,unsigned HourOld) { unsigned ColumnsToDraw; unsigned Column; unsigned H; unsigned FirstHour; unsigned Cols; ColumnsToDraw = TimeTableOld[Day][HourOld].NumColumns; if (!TimeTableHoursChecked[HourOld]) { TimeTableHoursChecked[HourOld] = true; for (Column = 0; Column < TT_MAX_COLUMNS_PER_CELL; Column++) { switch (TimeTableOld[Day][HourOld].Columns[Column].HourType) { case TT_FREE_HOUR: break; case TT_FIRST_HOUR: /* Check from first hour (this one) to last hour searching maximum number of columns */ for (H = HourOld + 1; H < HourOld + TimeTableOld[Day][HourOld].Columns[Column].DurationOld; H++) if (!TimeTableHoursChecked[H]) { Cols = TT_TimeTableCalculateColsToDraw (Day,H); if (Cols > ColumnsToDraw) ColumnsToDraw = Cols; } break; case TT_NEXT_HOUR: /* Find first hour for this item (class) */ for (FirstHour = HourOld; TimeTableOld[Day][FirstHour].Columns[Column].HourType == TT_NEXT_HOUR; FirstHour--); /* Check from first hour to last hour searching maximum number of columns */ for (H = FirstHour; H < FirstHour + TimeTableOld[Day][FirstHour].Columns[Column].DurationOld; H++) if (!TimeTableHoursChecked[H]) { Cols = TT_TimeTableCalculateColsToDraw (Day,H); if (Cols > ColumnsToDraw) ColumnsToDraw = Cols; } break; } } } return ColumnsToDraw; } /*****************************************************************************/ /******************** Write empty cell for alignment *************************/ /*****************************************************************************/ static void TT_DrawCellAlignTimeTable (void) { fprintf (Gbl.F.Out,"" "", TT_PERCENT_WIDTH_OF_A_SEPARATION_COLUMN); } /*****************************************************************************/ /*************************** Write a timetable cell **************************/ /*****************************************************************************/ static void TT_TimeTableDrawCell (unsigned Day,unsigned HourOld,unsigned Column,unsigned ColSpan, long CrsCod,TT_HourType_t HourType,TT_ClassType_t ClassType, unsigned DurationOld,char *Group,long GrpCod,char *Place) { extern const char *Txt_unknown_removed_course; extern const char *Txt_TIMETABLE_CLASS_TYPES[TT_NUM_CLASS_TYPES]; extern const char *Txt_Group; extern const char *Txt_All_groups; extern const char *Txt_Classroom; extern const char *Txt_Place; static const char *TimeTableClasses[TT_NUM_CLASS_TYPES] = { "TT_FREE", // Free hour "TT_THEO", // Theoretical class "TT_PRAC", // Practical class "TT_TUTO" // Tutorials }; enum { TT_CRS_SHOW, TT_CRS_EDIT, TT_TUT_SHOW, TT_TUT_EDIT, } TimeTableView = TT_CRS_SHOW; struct GroupData GrpDat; unsigned NumGrpTyp; unsigned NumGrp; unsigned H; unsigned Dur; unsigned MaxDuration; unsigned RowSpan = 0; TT_ClassType_t CT; struct Course Crs; /***** Compute row span and background color depending on hour type *****/ switch (HourType) { case TT_FREE_HOUR: // Free cell written RowSpan = 1; break; case TT_FIRST_HOUR: // Normal cell written RowSpan = DurationOld; break; case TT_NEXT_HOUR: // Nothing written break; } /***** If there's nothing to do... *****/ if (RowSpan == 0) return; /***** Set type of view depending on current action *****/ switch (Gbl.Action.Act) { case ActSeeCrsTT: case ActPrnCrsTT: case ActChgCrsTT1stDay: case ActSeeMyTT: case ActPrnMyTT: case ActChgMyTT1stDay: TimeTableView = TT_CRS_SHOW; break; case ActEdiCrsTT: case ActChgCrsTT: TimeTableView = TT_CRS_EDIT; break; case ActSeeRecOneTch: case ActSeeRecSevTch: TimeTableView = TT_TUT_SHOW; break; case ActEdiTut: case ActChgTut: TimeTableView = TT_TUT_EDIT; break; } /***** If group code > 0, a group is selected ==> get group type and name *****/ if (HourType == TT_FIRST_HOUR && (TimeTableView == TT_CRS_SHOW || TimeTableView == TT_CRS_EDIT) && GrpCod > 0) { /* Get group type and name */ GrpDat.GrpCod = GrpCod; Grp_GetDataOfGroupByCod (&GrpDat); } /***** Cell start *****/ fprintf (Gbl.F.Out,""); /***** Form to modify this cell *****/ if (TimeTableView == TT_CRS_EDIT) Act_FormStart (ActChgCrsTT); else if (TimeTableView == TT_TUT_EDIT) Act_FormStart (ActChgTut); /***** Draw cell depending on type of view *****/ switch (TimeTableView) { case TT_CRS_SHOW: case TT_TUT_SHOW: if (HourType != TT_FREE_HOUR) // If cell is not empty... { fprintf (Gbl.F.Out,"
"); if (Gbl.TimeTable.Type == TT_MY_TIMETABLE) { Crs.CrsCod = CrsCod; Crs_GetDataOfCourseByCod (&Crs); if (ClassType == TT_LECTURE || ClassType == TT_PRACTICAL) fprintf (Gbl.F.Out,"%s
", Crs.ShrtName[0] ? Crs.ShrtName : Txt_unknown_removed_course); } fprintf (Gbl.F.Out,"%s (%dh%s)", Txt_TIMETABLE_CLASS_TYPES[ClassType], DurationOld / 2, DurationOld % 2 ? "30'" : ""); if (TimeTableView == TT_CRS_SHOW) { if (GrpCod == -1) { if (Group[0]) fprintf (Gbl.F.Out,"
%s",Group); } else fprintf (Gbl.F.Out,"
%s %s", GrpDat.GrpTypName,GrpDat.GrpName); if (Place[0]) fprintf (Gbl.F.Out,"
%s",Place); } else // TimeTableView == TT_TUT_SHOW if (Place[0]) fprintf (Gbl.F.Out,"
%s",Place); fprintf (Gbl.F.Out,"
"); } break; case TT_CRS_EDIT: case TT_TUT_EDIT: Par_PutHiddenParamUnsigned ("ModTTDay",Day); Par_PutHiddenParamUnsigned ("ModTTHour",HourOld); Par_PutHiddenParamUnsigned ("ModTTCol",Column); /***** Class type *****/ fprintf (Gbl.F.Out,""); if (HourType == TT_FREE_HOUR) { fprintf (Gbl.F.Out," 1) fprintf (Gbl.F.Out,"1:00"); else fprintf (Gbl.F.Out,"0:30"); fprintf (Gbl.F.Out," h\" />"); } else { /***** Class duration *****/ fprintf (Gbl.F.Out,""); if (TimeTableView == TT_CRS_EDIT) { /***** Group *****/ fprintf (Gbl.F.Out,"
" ""); /***** Class room *****/ fprintf (Gbl.F.Out,"
" "", Txt_Classroom,TT_MAX_CHARS_PLACE,Place,Gbl.Form.Id); } else // TimeTableView == TT_TUT_EDIT { /***** Place *****/ fprintf (Gbl.F.Out,"
" "", Txt_Place,TT_MAX_CHARS_PLACE,Place,Gbl.Form.Id); } } break; } /***** End of form *****/ if (TimeTableView == TT_CRS_EDIT || TimeTableView == TT_TUT_EDIT) Act_FormEnd (); /***** End of cell *****/ fprintf (Gbl.F.Out,""); }