Free cookie consent management tool by TermsFeed Policy Generator

Ignore:
Timestamp:
06/05/09 14:35:47 (15 years ago)
Author:
whackl
Message:

fixed some bugs #663

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/sources/HeuristicLab.Hive.Client.Console/3.2/HiveClientConsole.cs

    r2023 r2024  
    3838{
    3939
    40   #region Delegates
    41 
    42   public delegate void AppendTextDelegate(String message);
    43   public delegate void RemoveTextDelegate(int newLength, int maxChars);
    44   public delegate void OnDialogClosedDelegate(RecurrentEvent e);
    45 
    46   #endregion
    47 
    48   public partial class HiveClientConsole : Form
    49   {
    50 
    51     #region Declarations
    52 
    53     private const string ENDPOINTADRESS = "net.tcp://127.0.0.1:8000/ClientConsole/ClientConsoleCommunicator";
    54     //private const string EVENTLOGNAME = "Hive Client";
    55 
    56     //private EventLog HiveClientEventLog;
    57     private LogFileReader logFileReader;
    58     private ClientConsoleCommunicatorClient cccc;
    59     private System.Windows.Forms.Timer refreshTimer;
    60     //private ListViewColumnSorterDate lvwColumnSorter;
    61 
    62     [XmlArray("Appointments")]
    63     [XmlArrayItem("Appointment",typeof(Appointment))]
    64     public List<Appointment> onlineTimes = new List<Appointment>();
    65 
    66     public OnDialogClosedDelegate dialogClosedDelegate;
     40    #region Delegates
     41
     42    public delegate void AppendTextDelegate(String message);
     43    public delegate void RemoveTextDelegate(int newLength, int maxChars);
     44    public delegate void OnDialogClosedDelegate(RecurrentEvent e);
    6745
    6846    #endregion
    6947
    70     #region Constructor
    71 
    72     public HiveClientConsole()
     48    public partial class HiveClientConsole : Form
    7349    {
    74       InitializeComponent();
    75       InitTimer();
    76       ConnectToClient();
    77       RefreshGui();
    78       InitCalender();
    79       InitLogFileReader();
     50
     51        #region Declarations
     52
     53        private const string ENDPOINTADRESS = "net.tcp://127.0.0.1:8000/ClientConsole/ClientConsoleCommunicator";
     54        //private const string EVENTLOGNAME = "Hive Client";
     55
     56        //private EventLog HiveClientEventLog;
     57        private LogFileReader logFileReader;
     58        private ClientConsoleCommunicatorClient cccc;
     59        private System.Windows.Forms.Timer refreshTimer;
     60        //private ListViewColumnSorterDate lvwColumnSorter;
     61
     62        [XmlArray("Appointments")]
     63        [XmlArrayItem("Appointment", typeof(Appointment))]
     64        public List<Appointment> onlineTimes = new List<Appointment>();
     65
     66        public OnDialogClosedDelegate dialogClosedDelegate;
     67
     68        #endregion
     69
     70        #region Constructor
     71
     72        public HiveClientConsole()
     73        {
     74            InitializeComponent();
     75            InitTimer();
     76            ConnectToClient();
     77            RefreshGui();
     78            InitCalender();
     79            InitLogFileReader();
     80        }
     81
     82        private void InitTestCalenderEntries()
     83        {
     84            DateTime date = DateTime.Now;
     85            while (date.Year == 2009)
     86            {
     87
     88                onlineTimes.Add(CreateAppointment(date.AddHours(1), date.AddHours(3), false));
     89                date = date.AddDays(1);
     90            }
     91        }
     92
     93        #endregion
     94
     95        #region Methods
     96
     97        private void InitLogFileReader()
     98        {
     99            logFileReader = new LogFileReader(Environment.CurrentDirectory + @"/Hive.log");
     100            logFileReader.MoreData += new LogFileReader.MoreDataHandler(logFileReader_MoreData);
     101            logFileReader.Start();
     102        }
     103
     104        private void logFileReader_MoreData(object sender, string newData)
     105        {
     106            int maxChars = txtLog.MaxLength;
     107            if (newData.Length > maxChars)
     108            {
     109                newData = newData.Remove(0, newData.Length - maxChars);
     110            }
     111            int newLength = this.txtLog.Text.Length + newData.Length;
     112            if (newLength > maxChars)
     113            {
     114                RemoveText(newLength, maxChars);
     115            }
     116            AppendText(newData);
     117        }
     118
     119        private void RemoveText(int newLength, int maxChars)
     120        {
     121            if (this.txtLog.InvokeRequired)
     122            {
     123                this.txtLog.Invoke(new
     124                  RemoveTextDelegate(RemoveText), new object[] { newLength, maxChars });
     125            }
     126            else
     127            {
     128                this.txtLog.Text = this.txtLog.Text.Remove(0, newLength - (int)maxChars);
     129            }
     130        }
     131
     132        private void InitTimer()
     133        {
     134            refreshTimer = new System.Windows.Forms.Timer();
     135            refreshTimer.Interval = 1000;
     136            refreshTimer.Tick += new EventHandler(refreshTimer_Tick);
     137            refreshTimer.Start();
     138        }
     139
     140        private void RefreshGui()
     141        {
     142            try
     143            {
     144                cccc.GetStatusInfosAsync();
     145            }
     146            catch (Exception ex)
     147            {
     148                refreshTimer.Stop();
     149                DialogResult res = MessageBox.Show("Connection Error, check if Hive Client is running!", "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
     150                if (res == DialogResult.OK)
     151                    this.Close();
     152            }
     153        }
     154
     155        private void ConnectToClient()
     156        {
     157            try
     158            {
     159                //changed by MB, 16.04.09
     160                //cccc = new ClientConsoleCommunicatorClient(new NetTcpBinding(), new EndpointAddress(ENDPOINTADRESS));
     161                cccc = new ClientConsoleCommunicatorClient(WcfSettings.GetBinding(), new EndpointAddress(ENDPOINTADRESS));
     162                cccc.GetStatusInfosCompleted += new EventHandler<GetStatusInfosCompletedEventArgs>(cccc_GetStatusInfosCompleted);
     163                cccc.GetCurrentConnectionCompleted += new EventHandler<GetCurrentConnectionCompletedEventArgs>(cccc_GetCurrentConnectionCompleted);
     164                cccc.GetUptimeCalendarCompleted += new EventHandler<GetUptimeCalendarCompletedEventArgs>(cccc_GetUptimeCalendarCompleted);
     165                cccc.SetUptimeCalendarCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(cccc_SetUptimeCalendarCompleted);
     166            }
     167            catch (Exception)
     168            {
     169                refreshTimer.Stop();
     170                DialogResult res = MessageBox.Show("Connection Error, check if Hive Client is running!", "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
     171                if (res == DialogResult.OK)
     172                    this.Close();
     173            }
     174        }
     175
     176        void cccc_SetUptimeCalendarCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
     177        {
     178            if (e.Error == null)
     179            {
     180                MessageBox.Show("Calendar successfully saved!", "Calender", MessageBoxButtons.OK, MessageBoxIcon.Information);
     181            }
     182            else
     183            {
     184                MessageBox.Show("Error saving calender \n" + e.Error.ToString(), "Calender", MessageBoxButtons.OK, MessageBoxIcon.Error);
     185            }
     186        }
     187
     188        void cccc_GetUptimeCalendarCompleted(object sender, GetUptimeCalendarCompletedEventArgs e)
     189        {
     190            if (e.Error == null)
     191            {
     192                if (e.Result != null)
     193                {
     194                    onlineTimes = e.Result.ToList<Appointment>();
     195                    onlineTimes.ForEach(a => a.BorderColor = Color.Red);
     196                }
     197                else
     198                {
     199                    onlineTimes = new List<Appointment>();
     200                }
     201            }
     202            //InitTestCalenderEntries();
     203        }
     204
     205        private void UpdateGraph(JobStatus[] jobs)
     206        {
     207            ZedGraphControl zgc = new ZedGraphControl();
     208            GraphPane myPane = zgc.GraphPane;
     209            myPane.GraphObjList.Clear();
     210
     211            myPane.Title.IsVisible = false;  // no title
     212            myPane.Border.IsVisible = false; // no border
     213            myPane.Chart.Border.IsVisible = false; // no border around the chart
     214            myPane.XAxis.IsVisible = false;  // no x-axis
     215            myPane.YAxis.IsVisible = false;  // no y-axis
     216            myPane.Legend.IsVisible = false; // no legend
     217
     218            myPane.Fill.Color = this.BackColor;
     219
     220            myPane.Chart.Fill.Type = FillType.None;
     221            myPane.Fill.Type = FillType.Solid;
     222
     223            double allProgress = 0;
     224            double done = 0;
     225
     226            if (jobs.Length == 0)
     227            {
     228                myPane.AddPieSlice(100, Color.Green, 0.1, "");
     229            }
     230            else
     231            {
     232                for (int i = 0; i < jobs.Length; i++)
     233                {
     234                    allProgress += jobs[i].Progress;
     235                }
     236
     237                done = allProgress / jobs.Length;
     238
     239                myPane.AddPieSlice(done, Color.Green, 0, "");
     240                myPane.AddPieSlice(1 - done, Color.Red, 0, "");
     241            }
     242            //Hides the slice labels
     243            PieItem.Default.LabelType = PieLabelType.None;
     244
     245            myPane.AxisChange();
     246
     247            pbGraph.Image = zgc.GetImage();
     248        }
     249
     250        private void InitCalender()
     251        {
     252            dvOnline.StartDate = DateTime.Now;
     253            dvOnline.OnNewAppointment += new EventHandler<NewAppointmentEventArgs>(DvOnline_OnNewAppointment);
     254            dvOnline.OnResolveAppointments += new EventHandler<ResolveAppointmentsEventArgs>(DvOnline_OnResolveAppointments);
     255
     256            //get calender from client
     257            cccc.GetUptimeCalendarAsync();
     258        }
     259
     260        private Appointment CreateAppointment(DateTime startDate, DateTime endDate, bool allDay)
     261        {
     262            Appointment App = new Appointment();
     263            App.StartDate = startDate;
     264            App.EndDate = endDate;
     265            App.AllDayEvent = allDay;
     266            App.BorderColor = Color.Red;
     267            App.Locked = true;
     268            App.Subject = "Online";
     269            App.Recurring = false;
     270            return App;
     271        }
     272
     273        private Appointment CreateAppointment(DateTime startDate, DateTime endDate, bool allDay, bool recurring, Guid recurringId)
     274        {
     275            Appointment App = new Appointment();
     276            App.StartDate = startDate;
     277            App.EndDate = endDate;
     278            App.AllDayEvent = allDay;
     279            App.BorderColor = Color.Red;
     280            App.Locked = true;
     281            App.Subject = "Online";
     282            App.Recurring = recurring;
     283            App.RecurringId = recurringId;
     284            return App;
     285        }
     286
     287        private void DeleteAppointment()
     288        {
     289            onlineTimes.Remove(dvOnline.SelectedAppointment);
     290        }
     291
     292        private void DeleteRecurringAppointment(Guid recurringId)
     293        {
     294            onlineTimes.RemoveAll(a => a.RecurringId.ToString() == dvOnline.SelectedAppointment.RecurringId.ToString());
     295        }
     296
     297        #endregion
     298
     299        #region Events
     300
     301        private void refreshTimer_Tick(object sender, EventArgs e)
     302        {
     303            RefreshGui();
     304        }
     305
     306        private void cccc_GetCurrentConnectionCompleted(object sender, GetCurrentConnectionCompletedEventArgs e)
     307        {
     308            if (e.Error == null)
     309            {
     310                ConnectionContainer curConnection = e.Result;
     311                tbIPAdress.Text = curConnection.IPAdress;
     312                tbPort.Text = curConnection.Port.ToString();
     313            }
     314            else
     315            {
     316                refreshTimer.Stop();
     317                DialogResult res = MessageBox.Show("Connection Error, check if Hive Client is running! - " + e.Error.Message, "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
     318                if (res == DialogResult.OK)
     319                    this.Close();
     320            }
     321        }
     322
     323        private void cccc_GetStatusInfosCompleted(object sender, GetStatusInfosCompletedEventArgs e)
     324        {
     325
     326            if (e.Error == null)
     327            {
     328                StatusCommons sc = e.Result;
     329
     330                lbGuid.Text = sc.ClientGuid.ToString();
     331                lbConnectionStatus.Text = sc.Status.ToString();
     332                lbJobdone.Text = sc.JobsDone.ToString();
     333                lbJobsAborted.Text = sc.JobsAborted.ToString();
     334                lbJobsFetched.Text = sc.JobsFetched.ToString();
     335
     336                this.Text = "Client Console (" + sc.Status.ToString() + ")";
     337
     338                ListViewItem curJobStatusItem;
     339
     340                if (sc.Jobs != null)
     341                {
     342                    lvJobDetail.Items.Clear();
     343                    double progress;
     344                    foreach (JobStatus curJob in sc.Jobs)
     345                    {
     346                        curJobStatusItem = new ListViewItem(curJob.JobId.ToString());
     347                        curJobStatusItem.SubItems.Add(curJob.Since.ToString());
     348                        progress = curJob.Progress * 100;
     349                        curJobStatusItem.SubItems.Add(progress.ToString());
     350                        lvJobDetail.Items.Add(curJobStatusItem);
     351                    }
     352                    lvJobDetail.Sort();
     353                }
     354
     355                UpdateGraph(sc.Jobs);
     356
     357                if (sc.Status == NetworkEnumWcfConnState.Connected || sc.Status == NetworkEnumWcfConnState.Loggedin)
     358                {
     359                    btConnect.Enabled = false;
     360                    btnDisconnect.Enabled = true;
     361                    lbCs.Text = sc.ConnectedSince.ToString();
     362                    cccc.GetCurrentConnectionAsync();
     363                }
     364                else if (sc.Status == NetworkEnumWcfConnState.Disconnected)
     365                {
     366                    btConnect.Enabled = true;
     367                    btnDisconnect.Enabled = false;
     368                    lbCs.Text = String.Empty;
     369                }
     370                else if (sc.Status == NetworkEnumWcfConnState.Failed)
     371                {
     372                    btConnect.Enabled = true;
     373                    btnDisconnect.Enabled = false;
     374                    lbCs.Text = String.Empty;
     375                }
     376
     377                cccc.GetCurrentConnection();
     378            }
     379            else
     380            {
     381                refreshTimer.Stop();
     382                DialogResult res = MessageBox.Show("Connection Error, check if Hive Client is running! - " + e.Error.Message, "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
     383                if (res == DialogResult.OK)
     384                    this.Close();
     385            }
     386        }
     387
     388        private void HiveClientConsole_Load(object sender, EventArgs e)
     389        {
     390            //nothing to do
     391        }
     392
     393        private void AppendText(string message)
     394        {
     395            if (this.txtLog.InvokeRequired)
     396            {
     397                this.txtLog.Invoke(new
     398                  AppendTextDelegate(AppendText), new object[] { message });
     399            }
     400            else
     401            {
     402                this.txtLog.AppendText(message);
     403            }
     404        }
     405
     406        private void btConnect_Click(object sender, EventArgs e)
     407        {
     408            IPAddress ipAdress;
     409            int port;
     410            ConnectionContainer cc = new ConnectionContainer();
     411            if (IPAddress.TryParse(tbIPAdress.Text, out ipAdress) && int.TryParse(tbPort.Text, out port))
     412            {
     413                cc.IPAdress = tbIPAdress.Text;
     414                cc.Port = port;
     415                cccc.SetConnectionAsync(cc);
     416            }
     417            else
     418            {
     419                MessageBox.Show("IP Adress and/or Port Error", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
     420            }
     421        }
     422
     423        private void btnDisconnect_Click(object sender, EventArgs e)
     424        {
     425            cccc.DisconnectAsync();
     426        }
     427
     428        private void btn_clientShutdown_Click(object sender, EventArgs e)
     429        {
     430            DialogResult res = MessageBox.Show("Do you really want to shutdown the Hive Client?", "Hive Client Console", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
     431            if (res == DialogResult.Yes)
     432            {
     433                logFileReader.Stop();
     434                cccc.ShutdownClient();
     435                this.Close();
     436            }
     437        }
     438
     439        private void btbDelete_Click(object sender, EventArgs e)
     440        {
     441            Appointment selectedAppointment = dvOnline.SelectedAppointment;
     442            if (dvOnline.SelectedAppointment != null)
     443            {
     444                if (!selectedAppointment.Recurring)
     445                    DeleteAppointment();
     446                else
     447                {
     448                    DialogResult res = MessageBox.Show("Delete all events in this series?", "Delete recurrences", MessageBoxButtons.YesNo);
     449                    if (res != DialogResult.Yes)
     450                        DeleteAppointment();
     451                    else
     452                        DeleteRecurringAppointment(selectedAppointment.RecurringId);
     453                }
     454            }
     455            dvOnline.Invalidate();
     456        }
     457
     458        private void chbade_CheckedChanged(object sender, EventArgs e)
     459        {
     460            txttimeFrom.Visible = !chbade.Checked;
     461            txttimeTo.Visible = !chbade.Checked;
     462        }
     463
     464        private void dvOnline_OnSelectionChanged(object sender, EventArgs e)
     465        {
     466            //btCreate.Enabled = true;
     467            if (dvOnline.Selection == SelectionType.DateRange)
     468            {
     469                dtpFrom.Text = dvOnline.SelectionStart.ToShortDateString();
     470                dtpTo.Text = dvOnline.SelectionEnd.Date.ToShortDateString();
     471                txttimeFrom.Text = dvOnline.SelectionStart.ToShortTimeString();
     472                txttimeTo.Text = dvOnline.SelectionEnd.ToShortTimeString();
     473
     474                btCreate.Text = "Save";
     475            }
     476
     477            if (dvOnline.Selection == SelectionType.Appointment)
     478            {
     479
     480                dtpFrom.Text = dvOnline.SelectedAppointment.StartDate.ToShortDateString();
     481                dtpTo.Text = dvOnline.SelectedAppointment.EndDate.ToShortDateString();
     482                txttimeFrom.Text = dvOnline.SelectedAppointment.StartDate.ToShortTimeString();
     483                txttimeTo.Text = dvOnline.SelectedAppointment.EndDate.ToShortTimeString();
     484
     485                if (dvOnline.SelectedAppointment.Recurring)
     486                    //btCreate.Enabled = false;
     487                    //also change the caption of the save button
     488                    btCreate.Text = "Save changes";
     489            }
     490
     491            if (dvOnline.Selection == SelectionType.None)
     492            {
     493                //also change the caption of the save button
     494                btCreate.Text = "Save";
     495            }
     496
     497        }
     498
     499        private void Connection_KeyPress(object sender, KeyPressEventArgs e)
     500        {
     501            if (e.KeyChar == (char)Keys.Return)
     502                btConnect_Click(null, null);
     503        }
     504
     505        private void mcOnline_DateChanged(object sender, DateRangeEventArgs e)
     506        {
     507            dvOnline.StartDate = mcOnline.SelectionStart;
     508        }
     509
     510        private bool CreateAppointment()
     511        {
     512            DateTime from, to;
     513
     514            if (!string.IsNullOrEmpty(dtpFrom.Text) && !string.IsNullOrEmpty(dtpTo.Text))
     515            {
     516                if (chbade.Checked)
     517                {
     518                    //whole day appointment, only dates are visible
     519                    if (DateTime.TryParse(dtpFrom.Text, out from) && DateTime.TryParse(dtpTo.Text, out to) && from <= to)
     520                        onlineTimes.Add(CreateAppointment(from, to.AddDays(1), true));
     521                    else
     522                        MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
     523                }
     524                else if (!string.IsNullOrEmpty(txttimeFrom.Text) && !string.IsNullOrEmpty(txttimeTo.Text))
     525                {
     526                    //Timeframe appointment
     527                    if (DateTime.TryParse(dtpFrom.Text + " " + txttimeFrom.Text, out from) && DateTime.TryParse(dtpTo.Text + " " + txttimeTo.Text, out to) && from < to)
     528                    {
     529                        if (from.Date == to.Date)
     530                            onlineTimes.Add(CreateAppointment(from, to, false));
     531                        else
     532                        {
     533                            //more than 1 day selected
     534                            while (from.Date != to.Date)
     535                            {
     536                                onlineTimes.Add(CreateAppointment(from, new DateTime(from.Year, from.Month, from.Day, to.Hour, to.Minute, 0, 0), false));
     537                                from = from.AddDays(1);
     538                            }
     539                            onlineTimes.Add(CreateAppointment(from, new DateTime(from.Year, from.Month, from.Day, to.Hour, to.Minute, 0, 0), false));
     540                        }
     541                    }
     542                    else
     543                        MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
     544                }
     545                dvOnline.Invalidate();
     546                return true;
     547            }
     548            else
     549            {
     550                MessageBox.Show("Error in create appointment, please fill out all textboxes!", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
     551                return false;
     552            }
     553        }
     554
     555        private void btCreate_Click(object sender, EventArgs e)
     556        {
     557            if (dvOnline.Selection != SelectionType.Appointment)
     558            {
     559                CreateAppointment();
     560            }
     561            else
     562            {
     563                //now we want to change an existing appointment
     564                if (!dvOnline.SelectedAppointment.Recurring)
     565                {
     566                    if (CreateAppointment())
     567                        DeleteAppointment();
     568                }
     569                else
     570                {
     571                    //change recurring appointment
     572                    //check, if only selected appointment has to change or whole recurrence
     573                    DialogResult res = MessageBox.Show("Change all events in this series?", "Change recurrences", MessageBoxButtons.YesNo);
     574                    if (res != DialogResult.Yes)
     575                    {
     576                        if (CreateAppointment())
     577                            DeleteAppointment();
     578                    }
     579                    else
     580                        ChangeRecurrenceAppointment(dvOnline.SelectedAppointment.RecurringId);
     581                }
     582            }
     583            dvOnline.Invalidate();
     584        }
     585
     586        private void ChangeRecurrenceAppointment(Guid recurringId)
     587        {
     588            int hourfrom = int.Parse(txttimeFrom.Text.Substring(0, txttimeFrom.Text.IndexOf(':')));
     589            int hourTo = int.Parse(txttimeTo.Text.Substring(0, txttimeTo.Text.IndexOf(':')));
     590            List<Appointment> recurringAppointments = onlineTimes.Where(appointment => appointment.RecurringId == recurringId).ToList();
     591            recurringAppointments.ForEach(appointment => appointment.StartDate = new DateTime(appointment.StartDate.Year, appointment.StartDate.Month, appointment.StartDate.Day, hourfrom, 0, 0));
     592            recurringAppointments.ForEach(appointment => appointment.EndDate = new DateTime(appointment.EndDate.Year, appointment.EndDate.Month, appointment.EndDate.Day, hourTo, 0, 0));
     593
     594            DeleteRecurringAppointment(recurringId);
     595            onlineTimes.AddRange(recurringAppointments);
     596        }
     597
     598        private void DvOnline_OnResolveAppointments(object sender, ResolveAppointmentsEventArgs e)
     599        {
     600            List<Appointment> Apps = new List<Appointment>();
     601
     602            foreach (Appointment m_App in onlineTimes)
     603                if ((m_App.StartDate >= e.StartDate) &&
     604                    (m_App.StartDate <= e.EndDate))
     605                    Apps.Add(m_App);
     606            e.Appointments = Apps;
     607        }
     608
     609        private void DvOnline_OnNewAppointment(object sender, NewAppointmentEventArgs e)
     610        {
     611            Appointment Appointment = new Appointment();
     612
     613            Appointment.StartDate = e.StartDate;
     614            Appointment.EndDate = e.EndDate;
     615
     616            onlineTimes.Add(Appointment);
     617        }
     618
     619        private void btnRecurrence_Click(object sender, EventArgs e)
     620        {
     621            Recurrence recurrence = new Recurrence();
     622            recurrence.dialogClosedDelegate = new OnDialogClosedDelegate(this.DialogClosed);
     623            recurrence.Show();
     624        }
     625
     626        public void DialogClosed(RecurrentEvent e)
     627        {
     628            CreateDailyRecurrenceAppointments(e.DateFrom, e.DateTo, e.AllDay, e.IncWeeks, e.WeekDays);
     629        }
     630
     631        private void CreateDailyRecurrenceAppointments(DateTime dateFrom, DateTime dateTo, bool allDay, int incWeek, HashSet<DayOfWeek> daysOfWeek)
     632        {
     633            DateTime incDate = dateFrom;
     634            Guid guid = Guid.NewGuid();
     635
     636            while (incDate.Date <= dateTo.Date)
     637            {
     638                if (daysOfWeek.Contains(incDate.Date.DayOfWeek))
     639                    onlineTimes.Add(CreateAppointment(incDate, new DateTime(incDate.Year, incDate.Month, incDate.Day, dateTo.Hour, dateTo.Minute, 0), allDay, true, guid));
     640                incDate = incDate.AddDays(1);
     641            }
     642
     643            dvOnline.Invalidate();
     644        }
     645
     646        private void btnSaveCal_Click(object sender, EventArgs e)
     647        {
     648            cccc.SetUptimeCalendarAsync(onlineTimes.ToArray());
     649        }
     650
     651        #endregion
     652
    80653    }
    81 
    82     private void InitTestCalenderEntries()
    83     {
    84         DateTime date = DateTime.Now;
    85         while (date.Year == 2009)
    86         {
    87            
    88             onlineTimes.Add(CreateAppointment(date.AddHours(1), date.AddHours(3), false));
    89             date = date.AddDays(1);
    90         }
    91     }
    92 
    93     #endregion
    94 
    95     #region Methods
    96 
    97     private void InitLogFileReader() {
    98       logFileReader = new LogFileReader(Environment.CurrentDirectory + @"/Hive.log");
    99       logFileReader.MoreData += new LogFileReader.MoreDataHandler(logFileReader_MoreData);
    100       logFileReader.Start();
    101     }
    102 
    103     private void logFileReader_MoreData(object sender, string newData) {
    104       int maxChars = txtLog.MaxLength;
    105       if (newData.Length > maxChars) {
    106         newData = newData.Remove(0, newData.Length - maxChars);
    107       }
    108       int newLength = this.txtLog.Text.Length + newData.Length;
    109       if (newLength > maxChars) {
    110           RemoveText(newLength, maxChars);
    111       }
    112       AppendText(newData);
    113     }
    114 
    115     private void RemoveText(int newLength, int maxChars)
    116     {
    117         if (this.txtLog.InvokeRequired)
    118         {
    119             this.txtLog.Invoke(new
    120               RemoveTextDelegate(RemoveText), new object[] { newLength, maxChars });
    121         }
    122         else
    123         {
    124             this.txtLog.Text = this.txtLog.Text.Remove(0, newLength - (int)maxChars);
    125         }
    126     }
    127 
    128     private void InitTimer()
    129     {
    130       refreshTimer = new System.Windows.Forms.Timer();
    131       refreshTimer.Interval = 1000;
    132       refreshTimer.Tick += new EventHandler(refreshTimer_Tick);
    133       refreshTimer.Start();
    134     }
    135 
    136     private void RefreshGui()
    137     {
    138       try
    139       {
    140         cccc.GetStatusInfosAsync();
    141       }
    142       catch (Exception ex)
    143       {
    144         refreshTimer.Stop();
    145         DialogResult res = MessageBox.Show("Connection Error, check if Hive Client is running!", "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    146         if (res == DialogResult.OK)
    147           this.Close();
    148       }
    149     }
    150 
    151     private void ConnectToClient()
    152     {
    153       try
    154       {
    155         //changed by MB, 16.04.09
    156         //cccc = new ClientConsoleCommunicatorClient(new NetTcpBinding(), new EndpointAddress(ENDPOINTADRESS));
    157         cccc = new ClientConsoleCommunicatorClient(WcfSettings.GetBinding(), new EndpointAddress(ENDPOINTADRESS));
    158         cccc.GetStatusInfosCompleted += new EventHandler<GetStatusInfosCompletedEventArgs>(cccc_GetStatusInfosCompleted);
    159         cccc.GetCurrentConnectionCompleted += new EventHandler<GetCurrentConnectionCompletedEventArgs>(cccc_GetCurrentConnectionCompleted);
    160         cccc.GetUptimeCalendarCompleted += new EventHandler<GetUptimeCalendarCompletedEventArgs>(cccc_GetUptimeCalendarCompleted);
    161         cccc.SetUptimeCalendarCompleted += new EventHandler<System.ComponentModel.AsyncCompletedEventArgs>(cccc_SetUptimeCalendarCompleted);
    162       }
    163       catch (Exception)
    164       {
    165         refreshTimer.Stop();
    166         DialogResult res = MessageBox.Show("Connection Error, check if Hive Client is running!", "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    167         if (res == DialogResult.OK)
    168           this.Close();
    169       }
    170     }
    171 
    172     void cccc_SetUptimeCalendarCompleted(object sender, System.ComponentModel.AsyncCompletedEventArgs e)
    173     {
    174         if (e.Error == null)
    175         {
    176             MessageBox.Show("Calendar successfully saved!", "Calender", MessageBoxButtons.OK, MessageBoxIcon.Information);
    177         }
    178         else
    179         {
    180             MessageBox.Show("Error saving calender \n" + e.Error.ToString(), "Calender", MessageBoxButtons.OK, MessageBoxIcon.Error);
    181         }
    182     }
    183 
    184     void cccc_GetUptimeCalendarCompleted(object sender, GetUptimeCalendarCompletedEventArgs e)
    185     {
    186         if (e.Error == null)
    187         {
    188             if (e.Result != null)
    189             {
    190                 onlineTimes = e.Result.ToList<Appointment>();
    191                 onlineTimes.ForEach(a => a.BorderColor = Color.Red);
    192             }
    193             else
    194             {
    195                 onlineTimes = new List<Appointment>();
    196             }
    197         }
    198         //InitTestCalenderEntries();
    199     }
    200 
    201     private void UpdateGraph(JobStatus[] jobs)
    202     {
    203       ZedGraphControl zgc = new ZedGraphControl();
    204       GraphPane myPane = zgc.GraphPane;
    205       myPane.GraphObjList.Clear();
    206 
    207       myPane.Title.IsVisible = false;  // no title
    208       myPane.Border.IsVisible = false; // no border
    209       myPane.Chart.Border.IsVisible = false; // no border around the chart
    210       myPane.XAxis.IsVisible = false;  // no x-axis
    211       myPane.YAxis.IsVisible = false;  // no y-axis
    212       myPane.Legend.IsVisible = false; // no legend
    213 
    214       myPane.Fill.Color = this.BackColor;
    215 
    216       myPane.Chart.Fill.Type = FillType.None;
    217       myPane.Fill.Type = FillType.Solid;
    218 
    219       double allProgress = 0;
    220       double done = 0;
    221 
    222       if (jobs.Length == 0)
    223       {
    224         myPane.AddPieSlice(100, Color.Green, 0.1, "");
    225       }
    226       else
    227       {
    228         for (int i = 0; i < jobs.Length; i++)
    229         {
    230           allProgress += jobs[i].Progress;
    231         }
    232 
    233         done = allProgress / jobs.Length;
    234 
    235         myPane.AddPieSlice(done, Color.Green, 0, "");
    236         myPane.AddPieSlice(1 - done, Color.Red, 0, "");
    237       }
    238       //Hides the slice labels
    239       PieItem.Default.LabelType = PieLabelType.None;
    240 
    241       myPane.AxisChange();
    242 
    243       pbGraph.Image = zgc.GetImage();
    244     }
    245 
    246     private void InitCalender()
    247     {
    248       dvOnline.StartDate = DateTime.Now;
    249       dvOnline.OnNewAppointment += new EventHandler<NewAppointmentEventArgs>(DvOnline_OnNewAppointment);
    250       dvOnline.OnResolveAppointments += new EventHandler<ResolveAppointmentsEventArgs>(DvOnline_OnResolveAppointments);
    251 
    252         //get calender from client
    253       cccc.GetUptimeCalendarAsync();
    254     }
    255 
    256     private Appointment CreateAppointment(DateTime startDate, DateTime endDate, bool allDay)
    257     {
    258       Appointment App = new Appointment();
    259       App.StartDate = startDate;
    260       App.EndDate = endDate;
    261       App.AllDayEvent = allDay;
    262       App.BorderColor = Color.Red;
    263       App.Locked = true;
    264       App.Subject = "Online";
    265       App.Recurring = false;
    266       return App;
    267     }
    268 
    269     private Appointment CreateAppointment(DateTime startDate, DateTime endDate, bool allDay, bool recurring, Guid recurringId)
    270     {
    271       Appointment App = new Appointment();
    272       App.StartDate = startDate;
    273       App.EndDate = endDate;
    274       App.AllDayEvent = allDay;
    275       App.BorderColor = Color.Red;
    276       App.Locked = true;
    277       App.Subject = "Online";
    278       App.Recurring = recurring;
    279       App.RecurringId = recurringId;
    280       return App;
    281     }
    282 
    283     private void DeleteAppointment()
    284     {
    285       onlineTimes.Remove(dvOnline.SelectedAppointment);
    286     }
    287 
    288     private void DeleteRecurringAppointment(Guid recurringId)
    289     {
    290       onlineTimes.RemoveAll(a => a.RecurringId.ToString() == dvOnline.SelectedAppointment.RecurringId.ToString());
    291     }
    292 
    293     #endregion
    294 
    295     #region Events
    296 
    297     private void refreshTimer_Tick(object sender, EventArgs e)
    298     {
    299       RefreshGui();
    300     }
    301 
    302     private void cccc_GetCurrentConnectionCompleted(object sender, GetCurrentConnectionCompletedEventArgs e)
    303     {
    304       if (e.Error == null)
    305       {
    306         ConnectionContainer curConnection = e.Result;
    307         tbIPAdress.Text = curConnection.IPAdress;
    308         tbPort.Text = curConnection.Port.ToString();
    309       }
    310       else
    311       {
    312         refreshTimer.Stop();
    313         DialogResult res = MessageBox.Show("Connection Error, check if Hive Client is running! - " + e.Error.Message, "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    314         if (res == DialogResult.OK)
    315           this.Close();
    316       }
    317     }
    318 
    319     private void cccc_GetStatusInfosCompleted(object sender, GetStatusInfosCompletedEventArgs e)
    320     {
    321 
    322       if (e.Error == null)
    323       {
    324         StatusCommons sc = e.Result;
    325 
    326         lbGuid.Text = sc.ClientGuid.ToString();
    327         lbConnectionStatus.Text = sc.Status.ToString();
    328         lbJobdone.Text = sc.JobsDone.ToString();
    329         lbJobsAborted.Text = sc.JobsAborted.ToString();
    330         lbJobsFetched.Text = sc.JobsFetched.ToString();
    331 
    332         this.Text = "Client Console (" + sc.Status.ToString() + ")";
    333 
    334         ListViewItem curJobStatusItem;
    335 
    336         if (sc.Jobs != null)
    337         {
    338           lvJobDetail.Items.Clear();
    339           double progress;
    340           foreach (JobStatus curJob in sc.Jobs)
    341           {
    342             curJobStatusItem = new ListViewItem(curJob.JobId.ToString());
    343             curJobStatusItem.SubItems.Add(curJob.Since.ToString());
    344             progress = curJob.Progress * 100;
    345             curJobStatusItem.SubItems.Add(progress.ToString());
    346             lvJobDetail.Items.Add(curJobStatusItem);
    347           }
    348           lvJobDetail.Sort();
    349         }
    350 
    351         UpdateGraph(sc.Jobs);
    352 
    353         if (sc.Status == NetworkEnumWcfConnState.Connected || sc.Status == NetworkEnumWcfConnState.Loggedin)
    354         {
    355           btConnect.Enabled = false;
    356           btnDisconnect.Enabled = true;
    357           lbCs.Text = sc.ConnectedSince.ToString();
    358           cccc.GetCurrentConnectionAsync();
    359         }
    360         else if (sc.Status == NetworkEnumWcfConnState.Disconnected)
    361         {
    362           btConnect.Enabled = true;
    363           btnDisconnect.Enabled = false;
    364           lbCs.Text = String.Empty;
    365         }
    366         else if (sc.Status == NetworkEnumWcfConnState.Failed)
    367         {
    368           btConnect.Enabled = true;
    369           btnDisconnect.Enabled = false;
    370           lbCs.Text = String.Empty;
    371         }
    372 
    373         cccc.GetCurrentConnection();
    374       }
    375       else
    376       {
    377         refreshTimer.Stop();
    378         DialogResult res = MessageBox.Show("Connection Error, check if Hive Client is running! - " + e.Error.Message, "Connection Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    379         if (res == DialogResult.OK)
    380           this.Close();
    381       }
    382     }
    383 
    384     private void HiveClientConsole_Load(object sender, EventArgs e)
    385     {
    386       //nothing to do
    387     }
    388 
    389     private void AppendText(string message) {
    390       if (this.txtLog.InvokeRequired) {
    391         this.txtLog.Invoke(new
    392           AppendTextDelegate(AppendText), new object[] { message });
    393       } else {
    394         this.txtLog.AppendText(message);
    395       }
    396     }
    397 
    398     private void btConnect_Click(object sender, EventArgs e)
    399     {
    400       IPAddress ipAdress;
    401       int port;
    402       ConnectionContainer cc = new ConnectionContainer();
    403       if (IPAddress.TryParse(tbIPAdress.Text, out ipAdress) && int.TryParse(tbPort.Text, out port))
    404       {
    405         cc.IPAdress = tbIPAdress.Text;
    406         cc.Port = port;
    407         cccc.SetConnectionAsync(cc);
    408       }
    409       else
    410       {
    411         MessageBox.Show("IP Adress and/or Port Error", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    412       }
    413     }
    414 
    415     private void btnDisconnect_Click(object sender, EventArgs e)
    416     {
    417       cccc.DisconnectAsync();
    418     }
    419 
    420     private void btn_clientShutdown_Click(object sender, EventArgs e)
    421     {
    422       DialogResult res = MessageBox.Show("Do you really want to shutdown the Hive Client?", "Hive Client Console", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
    423       if (res == DialogResult.Yes)
    424       {
    425           logFileReader.Stop();
    426         cccc.ShutdownClient();
    427         this.Close();
    428       }
    429     }
    430 
    431     private void btbDelete_Click(object sender, EventArgs e)
    432     {
    433       Appointment selectedAppointment = dvOnline.SelectedAppointment;
    434       if (dvOnline.SelectedAppointment != null)
    435       {
    436         if (!selectedAppointment.Recurring)
    437           DeleteAppointment();
    438         else
    439         {
    440           DialogResult res = MessageBox.Show("Delete all events in this series?", "Delete recurrences", MessageBoxButtons.YesNo);
    441           if (res != DialogResult.Yes)
    442             DeleteAppointment();
    443           else
    444             DeleteRecurringAppointment(selectedAppointment.RecurringId);
    445         }
    446       }
    447       dvOnline.Invalidate();
    448     }
    449 
    450     private void chbade_CheckedChanged(object sender, EventArgs e)
    451     {
    452       txttimeFrom.Visible = !chbade.Checked;
    453       txttimeTo.Visible = !chbade.Checked;
    454     }
    455 
    456     private void dvOnline_OnSelectionChanged(object sender, EventArgs e)
    457     {
    458       //btCreate.Enabled = true;
    459       if (dvOnline.Selection == SelectionType.DateRange)
    460       {
    461         dtpFrom.Text = dvOnline.SelectionStart.ToShortDateString();
    462         dtpTo.Text = dvOnline.SelectionEnd.Date.ToShortDateString();
    463         txttimeFrom.Text = dvOnline.SelectionStart.ToShortTimeString();
    464         txttimeTo.Text = dvOnline.SelectionEnd.ToShortTimeString();
    465 
    466         btCreate.Text = "Save";
    467       }
    468 
    469       if (dvOnline.Selection == SelectionType.Appointment)
    470       {
    471 
    472         dtpFrom.Text = dvOnline.SelectedAppointment.StartDate.ToShortDateString();
    473         dtpTo.Text = dvOnline.SelectedAppointment.EndDate.ToShortDateString();
    474         txttimeFrom.Text = dvOnline.SelectedAppointment.StartDate.ToShortTimeString();
    475         txttimeTo.Text = dvOnline.SelectedAppointment.EndDate.ToShortTimeString();
    476 
    477         if (dvOnline.SelectedAppointment.Recurring)
    478           //btCreate.Enabled = false;
    479         //also change the caption of the save button
    480         btCreate.Text = "Save changes";
    481       }
    482 
    483       if (dvOnline.Selection == SelectionType.None)
    484       {
    485         //also change the caption of the save button
    486         btCreate.Text = "Save";
    487       }
    488 
    489     }
    490 
    491     private void Connection_KeyPress(object sender, KeyPressEventArgs e)
    492     {
    493       if (e.KeyChar == (char)Keys.Return)
    494         btConnect_Click(null, null);
    495     }
    496 
    497     private void mcOnline_DateChanged(object sender, DateRangeEventArgs e)
    498     {
    499       dvOnline.StartDate = mcOnline.SelectionStart;
    500     }
    501 
    502     private bool CreateAppointment()
    503     {
    504       DateTime from, to;
    505 
    506       if (!string.IsNullOrEmpty(dtpFrom.Text) && !string.IsNullOrEmpty(dtpTo.Text))
    507       {
    508         if (chbade.Checked)
    509         {
    510           //whole day appointment, only dates are visible
    511           if (DateTime.TryParse(dtpFrom.Text + " " + txttimeFrom.Text, out from) && DateTime.TryParse(dtpTo.Text + " " + txttimeTo.Text, out to) && from < to)
    512             onlineTimes.Add(CreateAppointment(from, to.AddDays(1), true));
    513           else
    514             MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    515         }
    516         else if (!string.IsNullOrEmpty(txttimeFrom.Text) && !string.IsNullOrEmpty(txttimeTo.Text))
    517         {
    518           //Timeframe appointment
    519           if (DateTime.TryParse(dtpFrom.Text + " " + txttimeFrom.Text, out from) && DateTime.TryParse(dtpTo.Text + " " + txttimeTo.Text, out to) && from < to)
    520           {
    521             if (from.Date == to.Date)
    522               onlineTimes.Add(CreateAppointment(from, to, false));
    523             else
    524             {
    525               //more than 1 day selected
    526               while (from.Date != to.Date)
    527               {
    528                 onlineTimes.Add(CreateAppointment(from, new DateTime(from.Year, from.Month, from.Day, to.Hour, to.Minute, 0, 0), false));
    529                 from = from.AddDays(1);
    530               }
    531               onlineTimes.Add(CreateAppointment(from, new DateTime(from.Year, from.Month, from.Day, to.Hour, to.Minute, 0, 0), false));
    532             }
    533           }
    534           else
    535             MessageBox.Show("Incorrect date format", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    536         }
    537         dvOnline.Invalidate();
    538         return true;
    539       }
    540       else
    541       {
    542         MessageBox.Show("Error in create appointment, please fill out all textboxes!", "Schedule Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
    543         return false;
    544       }
    545     }
    546 
    547     private void btCreate_Click(object sender, EventArgs e)
    548     {
    549       if (dvOnline.Selection != SelectionType.Appointment)
    550       {
    551         CreateAppointment();
    552       }
    553       else
    554       {
    555         //now we want to change an existing appointment
    556         if (!dvOnline.SelectedAppointment.Recurring)
    557         {
    558           if (CreateAppointment())
    559             DeleteAppointment();
    560         }
    561         else
    562         {
    563           //change recurring appointment
    564           //check, if only selected appointment has to change or whole recurrence
    565           DialogResult res = MessageBox.Show("Change all events in this series?", "Change recurrences", MessageBoxButtons.YesNo);
    566           if (res != DialogResult.Yes)
    567           {
    568             if (CreateAppointment())
    569               DeleteAppointment();
    570           }
    571           else
    572             ChangeRecurrenceAppointment(dvOnline.SelectedAppointment.RecurringId);
    573         }
    574       }
    575     }
    576 
    577     private void ChangeRecurrenceAppointment(Guid recurringId)
    578     {
    579       int hourfrom = int.Parse(txttimeFrom.Text.Substring(0, txttimeFrom.Text.IndexOf(':')));
    580       int hourTo = int.Parse(txttimeTo.Text.Substring(0, txttimeTo.Text.IndexOf(':')));
    581       List<Appointment> recurringAppointments = onlineTimes.Where(appointment => appointment.RecurringId == recurringId).ToList();     
    582       recurringAppointments.ForEach(appointment => appointment.StartDate = new DateTime(appointment.StartDate.Year, appointment.StartDate.Month, appointment.StartDate.Day, hourfrom, 0, 0 ));
    583       recurringAppointments.ForEach(appointment => appointment.EndDate = new DateTime(appointment.EndDate.Year, appointment.EndDate.Month, appointment.EndDate.Day, hourTo, 0, 0));
    584 
    585       DeleteRecurringAppointment(recurringId);
    586       onlineTimes.AddRange(recurringAppointments);
    587     }
    588 
    589     private void DvOnline_OnResolveAppointments(object sender, ResolveAppointmentsEventArgs e)
    590     {
    591       List<Appointment> Apps = new List<Appointment>();
    592 
    593       foreach (Appointment m_App in onlineTimes)
    594         if ((m_App.StartDate >= e.StartDate) &&
    595             (m_App.StartDate <= e.EndDate))
    596           Apps.Add(m_App);
    597       e.Appointments = Apps;
    598     }
    599 
    600     private void DvOnline_OnNewAppointment(object sender, NewAppointmentEventArgs e)
    601     {
    602       Appointment Appointment = new Appointment();
    603 
    604       Appointment.StartDate = e.StartDate;
    605       Appointment.EndDate = e.EndDate;
    606 
    607       onlineTimes.Add(Appointment);
    608     }
    609 
    610     private void btnRecurrence_Click(object sender, EventArgs e)
    611     {
    612       Recurrence recurrence = new Recurrence();
    613       recurrence.dialogClosedDelegate = new OnDialogClosedDelegate(this.DialogClosed);
    614       recurrence.Show();
    615     }
    616 
    617     public void DialogClosed(RecurrentEvent e)
    618     {
    619       CreateDailyRecurrenceAppointments(e.DateFrom, e.DateTo, e.AllDay, e.IncWeeks, e.WeekDays);
    620     }
    621 
    622     private void CreateDailyRecurrenceAppointments(DateTime dateFrom, DateTime dateTo, bool allDay, int incWeek, HashSet<DayOfWeek> daysOfWeek) {
    623       DateTime incDate = dateFrom;
    624       Guid guid = Guid.NewGuid();
    625 
    626       while (incDate.Date <= dateTo.Date) {
    627         if (daysOfWeek.Contains(incDate.Date.DayOfWeek))
    628           onlineTimes.Add(CreateAppointment(incDate, new DateTime(incDate.Year, incDate.Month, incDate.Day, dateTo.Hour, dateTo.Minute, 0), allDay, true, guid));
    629         incDate = incDate.AddDays(1);
    630       }
    631 
    632       dvOnline.Invalidate();
    633     }
    634 
    635 
    636    //private void SerializeCalender() {
    637      
    638    //    XmlSerializer s = new XmlSerializer(typeof(List<Appointment>));
    639    //    TextWriter w = new StreamWriter(@"c:\temp\apptest.xml");
    640    //    s.Serialize(w, onlineTimes);
    641    //    w.Close();
    642    //}
    643 
    644    //private void DeSerializeCalender()
    645    //{
    646    //    XmlSerializer s = new XmlSerializer(typeof(List<Appointment>));
    647    //    TextReader r = new StreamReader(@"c:\temp\apptest.xml");
    648    //    onlineTimes = (List<Appointment>)s.Deserialize(r);
    649    //    onlineTimes.ForEach(a => a.BorderColor = Color.Red);
    650    //    r.Close();
    651 
    652    //}
    653 
    654    private void btnSaveCal_Click(object sender, EventArgs e)
    655    {
    656        cccc.SetUptimeCalendarAsync(onlineTimes.ToArray());
    657    }
    658 
    659     #endregion
    660 
    661   }
    662654}
Note: See TracChangeset for help on using the changeset viewer.