00001 using Foodolini.BusinessLogic;
00002 using System;
00003 using Gtk;
00004 using System.Collections.Generic;
00005 using Foodolini.Activities.FoodRegistration;
00006 using Foodolini.Activities.FoodRegistration.Devices;
00007 using ZBar;
00008 using Foodolini.Activities.Ingredients;
00009
00010
00011 namespace Foodolini.Activities.FoodRegistration
00012 {
00019 [FoodoliniActivity("Food registration", false, "FoodRegistration.cart.png")]
00020 [System.ComponentModel.ToolboxItem(true)]
00021 public partial class FoodRegistrationActivity : Gtk.Bin, IActivity
00022 {
00023
00024 private NodeStore foodList = new NodeStore(typeof(FoodItemAdapter));
00025 private IOwner owner;
00026
00027 public FoodRegistrationActivity(){
00028
00029 this.Build();
00030
00031
00032 this.Destroyed += HandleDestroyed;
00033 this.Hidden += HandleHidden;
00034 this.Shown += HandleShown;
00035
00036
00037 this.scanner.Stopped += HandleStopped;
00038 this.scanner.Error += HandleError;
00039
00040
00041 bool flipped = Foodolini.BusinessLogic.Settings.Instance.GetValue("FlipVideo", "True") == "True";
00042 this.scanner.Flip = flipped;
00043 this.FlipButton.Active = flipped;
00044
00045
00046 this.PopulateVideoSourceComboBox();
00047
00048
00049 this.scanner.Mute = Foodolini.BusinessLogic.Settings.Instance.GetValue("MuteScanner", "False") == "True";
00050
00051
00052 this.UpdateMuteButton();
00053
00054 this.FoodView.NodeStore = this.foodList;
00055 this.FoodView.AppendColumn("Description", new CellRendererText(), "text", 0);
00056 this.FoodView.AppendColumn("Quantity", new CellRendererText(), delegate(TreeViewColumn tree_column, CellRenderer cell, ITreeNode node){
00057 FoodItemAdapter n = (FoodItemAdapter)node;
00058 CellRendererText c = (CellRendererText)cell;
00059 c.Text = n.Quantity.ToString();
00060 });
00061 this.FoodView.AppendColumn("Expiration date", new CellRendererText(), delegate(TreeViewColumn tree_column, CellRenderer cell, ITreeNode node){
00062 FoodItemAdapter n = (FoodItemAdapter)node;
00063 CellRendererText c = (CellRendererText)cell;
00064 c.Text = n.ExpirationDate.ToLongDateString();
00065 });
00066
00067 this.FoodView.NodeSelection.Mode = SelectionMode.Single;
00068
00069 this.SelectIngredientButton.Sensitive = false;
00070 this.QuantitySpinbutton.Sensitive = false;
00071 this.DuplicateButton.Sensitive = false;
00072 this.RemoveButton.Sensitive = false;
00073 this.SaveButton.Sensitive = false;
00074
00075 }
00076
00077 void HandleHidden(object sender, EventArgs e){
00078 this.scanner.Close();
00079 }
00080
00081 void HandleDestroyed(object sender, EventArgs e){
00082
00083 Foodolini.BusinessLogic.Settings.Instance["FlipVideo"] = this.scanner.Flip.ToString();
00084
00085 Foodolini.BusinessLogic.Settings.Instance["MuteScanner"] = this.scanner.Mute.ToString();
00086
00087 Foodolini.BusinessLogic.Settings.Instance.Save();
00088
00089 this.scanner.Destroy();
00090 this.muteImage.Destroy();
00091 this.unmuteImage.Destroy();
00092
00093 CaptureDeviceMonitor.Instance.Dispose();
00094 }
00095
00096 void HandleShown (object sender, EventArgs e){
00097 if(currentDevice != null)
00098 this.scanner.Open(currentDevice);
00099
00100
00101 if(CaptureDeviceMonitor.Instance.SupportsAutoRefresh)
00102 this.RefreshButton.Hide();
00103 }
00104
00105 #region Scanner control
00106
00107 private string currentDevice = null;
00108
00112 void HandleStopped(object sender, EventArgs e){
00113
00114 this.VideoSourceComboBox.SetActiveIter(this.NoVideoIter);
00115 }
00116
00120 void HandleError(object sender, ErrorEventArgs e){
00121
00122 Console.WriteLine("ZBar error i: " + e.Message);
00123
00124 this.VideoSourceComboBox.SetActiveIter(this.NoVideoIter);
00125 }
00126
00127 private ListStore VideoSources = new ListStore(typeof(string), typeof(string));
00128 private TreeIter NoVideoIter;
00129 private void PopulateVideoSourceComboBox(){
00130
00131 this.VideoSourceComboBox.Model = this.VideoSources;
00132 this.NoVideoIter = this.VideoSources.AppendValues("No video", null);
00133 Nullable<TreeIter> source = null;
00134 foreach(var device in CaptureDeviceMonitor.Instance.Devices){
00135 var iter = this.VideoSources.AppendValues(device.Name, device.DevicePath);
00136 if(source == null)
00137 source = iter;
00138 }
00139
00140 if(source != null){
00141 this.IgnoreVideoSourceComboBoxChanged = true;
00142 this.VideoSourceComboBox.SetActiveIter(source.Value);
00143 this.IgnoreVideoSourceComboBoxChanged = false;
00144 var device = (string)this.VideoSources.GetValue(source.Value, 1);
00145 this.currentDevice = device;
00146 }else
00147 this.VideoSourceComboBox.SetActiveIter(this.NoVideoIter);
00148
00149
00150 CaptureDeviceMonitor.Instance.DeviceAdded += HandleDeviceAdded;
00151 CaptureDeviceMonitor.Instance.DeviceRemoved += HandleDeviceRemoved;
00152 }
00153
00154 protected virtual void OnRefreshButtonClicked(object sender, System.EventArgs e){
00155
00156 List<string> listedDevices = new List<string>();
00157
00158 ICollection<IDeviceInfo> devs = CaptureDeviceMonitor.Instance.Devices;
00159
00160 TreeIter iter;
00161 if(this.VideoSourceComboBox.GetActiveIter(out iter)){
00162 string name = (string)this.VideoSources.GetValue(iter, 0);
00163 string devpath = (string)this.VideoSources.GetValue(iter, 1);
00164
00165 bool exists = false;
00166 foreach(var dev in devs){
00167 if(name != null && name == dev.Name &&
00168 devpath != null && devpath == dev.DevicePath)
00169 exists = true;
00170 }
00171 if(!exists)
00172 this.VideoSourceComboBox.SetActiveIter(this.NoVideoIter);
00173 }
00174
00175 TreeIter i;
00176 bool hasNext = this.VideoSources.GetIterFirst(out i);
00177 while(hasNext){
00178 string name = (string)this.VideoSources.GetValue(i, 0);
00179 string devpath = (string)this.VideoSources.GetValue(i, 1);
00180
00181 bool exists = false;
00182 foreach(var dev in devs){
00183 if(name != null && name == dev.Name &&
00184 devpath != null && devpath == dev.DevicePath)
00185 exists = true;
00186 }
00187
00188 if(devpath != null && !listedDevices.Contains(devpath))
00189 listedDevices.Add(devpath);
00190
00191 if(!exists){
00192 this.VideoSources.Remove(ref i);
00193
00194 hasNext = this.VideoSources.GetIterFirst(out i);
00195 }else
00196 hasNext = this.VideoSources.IterNext(ref i);
00197 }
00198 foreach(var dev in devs){
00199 if(!listedDevices.Contains(dev.DevicePath)){
00200
00201 TreeIter device = this.VideoSources.AppendValues(dev.Name, dev.DevicePath);
00202
00203 if(this.VideoSourceComboBox.GetActiveIter(out iter)){
00204 string devpath = (string)this.VideoSources.GetValue(iter, 1);
00205
00206 if(devpath == null)
00207 this.VideoSourceComboBox.SetActiveIter(device);
00208 }
00209 }
00210 }
00211 }
00212
00216 void HandleDeviceAdded(object sender, DeviceEventArgs e){
00217
00218 TreeIter device = this.VideoSources.AppendValues(e.Name, e.DevicePath);
00219
00220 TreeIter iter;
00221 if(this.VideoSourceComboBox.GetActiveIter(out iter)){
00222 string dev = (string)this.VideoSources.GetValue(iter, 1);
00223
00224 if(dev == null)
00225 this.VideoSourceComboBox.SetActiveIter(device);
00226 }
00227 }
00228
00232 void HandleDeviceRemoved(object sender, DeviceEventArgs e){
00233
00234 TreeIter iter;
00235 if(this.VideoSourceComboBox.GetActiveIter(out iter)){
00236 string name = (string)this.VideoSources.GetValue(iter, 0);
00237 string devpath = (string)this.VideoSources.GetValue(iter, 1);
00238 if(name != null && name == e.Name &&
00239 devpath != null && devpath == e.DevicePath)
00240 this.VideoSourceComboBox.SetActiveIter(this.NoVideoIter);
00241 }
00242
00243 TreeIter i;
00244 bool hasNext = this.VideoSources.GetIterFirst(out i);
00245 while(hasNext){
00246 string name = (string)this.VideoSources.GetValue(i, 0);
00247 string devpath = (string)this.VideoSources.GetValue(i, 1);
00248
00249 if(name != null && name == e.Name &&
00250 devpath != null && devpath == e.DevicePath){
00251 this.VideoSources.Remove(ref i);
00252
00253 hasNext = this.VideoSources.GetIterFirst(out i);
00254 }else
00255 hasNext = this.VideoSources.IterNext(ref i);
00256 }
00257 }
00258
00259 private bool IgnoreVideoSourceComboBoxChanged = false;
00260 private void OnVideoSourceComboBoxChanged (object sender, System.EventArgs e){
00261 if(this.IgnoreVideoSourceComboBoxChanged)
00262 return;
00263
00264 TreeIter iter;
00265 if(this.VideoSourceComboBox.GetActiveIter(out iter)){
00266 string device = (string)this.VideoSources.GetValue(iter, 1);
00267 if(device != null){
00268 this.currentDevice = device;
00269 this.scanner.Open(device);
00270 }else{
00271 this.currentDevice = null;
00272 this.scanner.Close();
00273 }
00274 }else{
00275 this.currentDevice = null;
00276 this.scanner.Close();
00277 }
00278 }
00279
00280 private void OnMuteButtonClicked (object sender, System.EventArgs e){
00281 this.scanner.Mute = !this.scanner.Mute;
00282 this.UpdateMuteButton();
00283 }
00284
00285 private Gtk.Image muteImage = new Gtk.Image(null, "FoodRegistration.muted.png");
00286 private Gtk.Image unmuteImage = new Gtk.Image(null, "FoodRegistration.audio.png");
00287
00291 private void UpdateMuteButton(){
00292 if(this.scanner.Mute)
00293 this.MuteButton.Image = this.muteImage;
00294 else
00295 this.MuteButton.Image = this.unmuteImage;
00296 }
00297
00301 protected virtual void OnFlipButtonToggled (object sender, System.EventArgs e){
00302 this.scanner.Flip = this.FlipButton.Active;
00303 }
00304
00305 #endregion
00306
00307 #region IActivity implementation
00308
00309 public Widget Widget{
00310 get{return this;}
00311 }
00312
00313 public void Register(IOwner owner){
00314 this.owner = owner;
00315 this.owner.ActivityUnloading += OwnerhandleActivityUnloading;
00316 }
00317
00318 public void Unregister(){
00319 this.owner.ActivityUnloading -= OwnerhandleActivityUnloading;
00320 this.owner = null;
00321 }
00322
00323 #endregion
00324
00325 #region Save, cancel, unloading
00326
00327 private bool cancelled = true;
00328
00338 void OnSaveButtonClicked (object sender, System.EventArgs e){
00339 this.cancelled = true;
00340 foreach (var node in this.foodList){
00341 FoodItemAdapter food = (FoodItemAdapter)node;
00342 food.FoodItem.Save();
00343 }
00344 this.foodList.Clear();
00345 Foodolini.BusinessLogic.Settings.Instance.Save();
00346 this.owner.PopActivity();
00347
00348 }
00349
00362 void OnCancelButtonClicked (object sender, System.EventArgs e){
00363 this.cancelled = !this.foodList.GetEnumerator().MoveNext();
00364 this.owner.PopActivity();
00365 }
00366
00377 void OwnerhandleActivityUnloading (object sender, ActivityUnloadedArgs e){
00378 if(!this.cancelled && e.Activity == this ){
00379 SaveDialog dlg = new SaveDialog();
00380 var result = (Gtk.ResponseType)dlg.Run();
00381 if(result == Gtk.ResponseType.Apply)
00382 this.SaveButton.Click();
00383 else if(result == ResponseType.Cancel)
00384 e.Unload = false;
00385
00386 dlg.Hide();
00387 dlg.Dispose();
00388 }
00389 }
00390
00391 #endregion
00392
00393 #region Editor logic
00394
00398 private FoodItemAdapter current = null;
00399
00403 void OnRemoveButtonClicked (object sender, System.EventArgs e){
00404 if(current != null)
00405 this.foodList.RemoveNode(current);
00406 current = null;
00407 UpdateEditor();
00408 }
00409
00413 void OnAddButtonClicked (object sender, System.EventArgs e){
00414
00415 IngredientSelector dialog = new IngredientSelector(false);
00416 if (dialog.Run() == (int)ResponseType.Ok){
00417
00418 OnClearBarCodeButtonClicked(this, e);
00419
00420
00421 var ing = dialog.SelectedIngredient;
00422
00423 current = new FoodItemAdapter(new FoodItem(100, ing, DateTime.Now + ing.ShelfLife, false));
00424 foodList.AddNode(current);
00425 this.FoodView.NodeSelection.SelectNode(current);
00426 this.UpdateEditor();
00427 }
00428 dialog.Destroy();
00429 }
00430
00434 void OnSelectIngredientButtonClicked (object sender, System.EventArgs e){
00435 if(current != null){
00436 IngredientSelector dialog = new IngredientSelector(current.FoodItem.Ingredient, false);
00437 if (dialog.Run() == (int)ResponseType.Ok){
00438 current.FoodItem.Ingredient = dialog.SelectedIngredient;
00439 current.FoodItem.ExpirationDate = DateTime.Now + dialog.SelectedIngredient.ShelfLife;
00440 this.IngredientLabel.Text = current.FoodItem.Ingredient.ShortDescription;
00441 this.ExpirationCalendar.Date = current.FoodItem.ExpirationDate;
00442 }
00443 dialog.Destroy();
00444 }else
00445 OnAddButtonClicked(this, e);
00446 }
00447
00451 private BarCode code = null;
00452
00456 void OnScannerBarScanned (object sender, BarScannedArgs e){
00457
00458 BarCodeType type = FromZBarType(e.Symbol.Type);
00459 this.code = BarCode.Find(e.Symbol.Data,type);
00460 if (this.code != null){
00461 current = new FoodItemAdapter(new FoodItem(code.Quantity, code.Ingredient, DateTime.Now + code.Ingredient.ShelfLife, false));
00462 foodList.AddNode(current);
00463 this.FoodView.NodeSelection.SelectNode(current);
00464 }else{
00465
00466 IngredientSelector dialog = new IngredientSelector(false);
00467 if (dialog.Run() == (int)ResponseType.Ok){
00468 var ing = dialog.SelectedIngredient;
00469
00470 code = new BarCode(e.Symbol.Data.ToString(), type);
00471 code.ProductName = ing.LongDescription;
00472 code.Quantity = 100;
00473 code.Ingredient = ing;
00474 code.Save();
00475
00476
00477 current = new FoodItemAdapter(new FoodItem(code.Quantity, code.Ingredient, DateTime.Now + code.Ingredient.ShelfLife, false));
00478 foodList.AddNode(current);
00479 this.FoodView.NodeSelection.SelectNode(current);
00480 }
00481 dialog.Destroy();
00482 }
00483 UpdateEditor();
00484 }
00485
00489 protected virtual void OnClearBarCodeButtonClicked (object sender, System.EventArgs e){
00490 this.code = null;
00491 this.ClearBarCodeButton.Sensitive = false;
00492 this.BarCodeLabel.Text = "-";
00493 this.scanner.ResetLastItemScanned();
00494 }
00495
00499 private void UpdateEditor(){
00500
00501 this.SelectIngredientButton.Sensitive = current != null;
00502 this.ExpirationCalendar.Sensitive = current != null;
00503 this.RemoveButton.Sensitive = current != null;
00504 this.DuplicateButton.Sensitive = current != null;
00505 this.QuantitySpinbutton.Sensitive = current != null;
00506 this.ClearBarCodeButton.Sensitive = current != null && code != null;
00507
00508 if(current != null){
00509 this.ExpirationCalendar.Date = current.FoodItem.ExpirationDate;
00510 this.QuantitySpinbutton.Value = current.FoodItem.Quantity;
00511 this.IngredientLabel.Text = current.FoodItem.Ingredient.LongDescription;
00512 }else{
00513 this.ExpirationCalendar.Date = DateTime.Now;
00514 this.QuantitySpinbutton.Value = 0;
00515 this.IngredientLabel.Text = "-";
00516 }
00517 if(current != null && code != null){
00518 this.BarCodeLabel.Markup = "<i>" + GLib.Markup.EscapeText(code.ToString()) + "</i>";
00519 }else
00520 this.BarCodeLabel.Text = "-";
00521
00522 SaveButton.Sensitive = false;
00523 this.cancelled = true;
00524 if(foodList.GetEnumerator().MoveNext()) {
00525 this.cancelled = false;
00526 SaveButton.Sensitive = true;
00527 }
00528 }
00529
00533 protected virtual void OnFoodViewCursorChanged (object sender, System.EventArgs e){
00534 this.current = (FoodItemAdapter)this.FoodView.NodeSelection.SelectedNode;
00535 this.UpdateEditor();
00536 }
00537
00541 protected virtual void OnQuantitySpinbuttonValueChanged (object sender, System.EventArgs e){
00542 if(current != null){
00543 current.FoodItem.Quantity = QuantitySpinbutton.Value;
00544 current.ReportChange();
00545 }
00546 if(code != null){
00547 code.Quantity = QuantitySpinbutton.Value;
00548 code.Save();
00549 }
00550 }
00551
00555 protected virtual void OnDuplicateButtonClicked (object sender, System.EventArgs e){
00556 current = new FoodItemAdapter(new FoodItem(current.FoodItem.Quantity, current.FoodItem.Ingredient, current.ExpirationDate, false));
00557 foodList.AddNode(current);
00558 this.FoodView.NodeSelection.SelectNode(current);
00559 this.UpdateEditor();
00560 }
00561
00565 protected virtual void CalendarChanged (object sender, System.EventArgs e)
00566 {
00567 if(current != null){
00568 current.FoodItem.ExpirationDate = this.ExpirationCalendar.Date;
00569 current.ReportChange();
00570 }
00571 }
00572
00573 #endregion
00574
00578 private BarCodeType FromZBarType(SymbolType symbol){
00579 switch(symbol){
00580 case SymbolType.UPCE:
00581 return BarCodeType.UPCE;
00582 case SymbolType.UPCA:
00583 return BarCodeType.UPCA;
00584 case SymbolType.CODE128:
00585 return BarCodeType.CODE128;
00586 case SymbolType.CODE39:
00587 return BarCodeType.CODE39;
00588 case SymbolType.EAN13:
00589 return BarCodeType.EAN13;
00590 case SymbolType.EAN8:
00591 return BarCodeType.EAN8;
00592 case SymbolType.I25:
00593 return BarCodeType.I25;
00594 case SymbolType.ISBN10:
00595 return BarCodeType.ISBN10;
00596 case SymbolType.ISBN13:
00597 return BarCodeType.ISBN13;
00598 case SymbolType.PDF417:
00599 return BarCodeType.PDF417;
00600 }
00601 throw new Exception("Unknown barcode type...");
00602 }
00603 }
00604 }