]> Creatis software - creaImageIO.git/blob - src/creaImageIOWxGimmick.cpp
start!
[creaImageIO.git] / src / creaImageIOWxGimmick.cpp
1
2 #include <creaImageIOWxGimmick.h>
3 #include <creaImageIODicomNodeComparators.h>
4
5 #include <creaMessageManager.h>
6
7 #include "icons/database.xpm"
8 #include "icons/folder.xpm"
9 #include "icons/dicomdir.xpm"
10 #include "icons/patient.xpm"
11 #include "icons/study.xpm"
12 #include "icons/series.xpm"
13 #include "icons/image.xpm"
14 #include "icons/root.xpm"
15 #include <wx/filedlg.h>
16 #include <wx/dirdlg.h>
17
18 #include <fstream>
19 #include <vtkCamera.h>
20 #include <vtkRenderer.h>
21
22 #include<wx/filefn.h>
23
24 using namespace crea;
25
26 #include <boost/filesystem.hpp>
27 #include <boost/algorithm/string.hpp>
28
29 namespace creaImageIO
30 {
31   //================================================================
32   const int WxGimmick::UserMenuFirstId = 1000;
33   //================================================================
34
35   //================================================================
36   typedef enum
37     {
38       Icon_Root,
39       Icon_Database,
40       Icon_Folder,
41       Icon_DicomDir,
42       Icon_Patient,
43       Icon_Study,
44       Icon_Series,
45       Icon_Image
46     }
47     icon_id;
48   //================================================================
49
50   //================================================================
51   enum 
52     {
53       PopUp_NewCollection = 100,
54       PopUp_OpenCollection = 101,
55       PopUp_CloseCollection = 102,
56       PopUp_DeleteCollection = 103,
57       PopUp_AddDirectory = 110,
58       PopUp_AddFile = 111,
59       PopUp_AddRawFile = 112,
60       PopUp_Remove = 120,
61       PopUp_Sort = 200,
62       PopUp_Settings = 501,
63       PopUp_About    = 502,
64       PopUp_User = WxGimmick::UserMenuFirstId,
65     };
66   //================================================================
67
68   //================================================================
69 #define TreeListCtrlId 10000
70   //================================================================
71
72   //================================================================
73   const icon_id Icon[5] = { Icon_Database,  
74                             Icon_Patient,
75                             Icon_Study,
76                             Icon_Series,
77                             Icon_Image };
78   //================================================================
79  
80
81   //================================================================
82   class WxGimmickDicomNodeData : public DicomNodeData
83   {
84   public:
85     WxGimmickDicomNodeData
86     (WxGimmickTreeItemData* d = 0) :
87       mTreeItemData(d),
88       mLoaded(false)
89     {}
90     ~WxGimmickDicomNodeData();
91
92     WxGimmickTreeItemData* GetTreeItemData()
93     { return mTreeItemData; }
94     void SetTreeItemData(   WxGimmickTreeItemData* d)
95     { mTreeItemData = d; }
96     inline bool IsLoaded() { return mLoaded; }
97     inline void SetLoaded(bool v) { mLoaded = v; }
98
99   private:    
100     WxGimmickTreeItemData* mTreeItemData;
101     bool mLoaded;
102   };
103   //================================================================
104
105
106   //================================================================
107   class WxGimmickTreeItemData : public wxTreeItemData
108   {
109   public:
110     WxGimmickTreeItemData(DicomNode* node) 
111       : 
112       mType(0),
113       mDicomNode(node), 
114       //        mLoaded(false),
115       mUpdateTime(0),
116       mUserFlags(0)
117     { 
118       if (node) 
119         {
120           WxGimmickDicomNodeData* data =
121             node->GetData<WxGimmickDicomNodeData*>();
122           if (data!=0)
123             {
124               if (data->GetTreeItemData()!=0) 
125                 {
126                   std::cout << "WxGimmickTreeItemData ERROR ****"
127                             << std::endl;
128                   return;
129                 }
130               data->SetTreeItemData(this);
131               return;
132             }
133           node->SetData( new WxGimmickDicomNodeData(this) );
134           if (node->GetType()==DicomNode::Database)
135             {
136               mType = 2;
137             }
138           else 
139             {
140               mType = 1;
141             }
142         }
143     }
144     ~WxGimmickTreeItemData()
145     {
146       if (mDicomNode) 
147         {
148           WxGimmickDicomNodeData* data =
149             mDicomNode->GetData<WxGimmickDicomNodeData*>();
150           if (data) data->SetTreeItemData(0);
151
152         }
153     }
154     inline void ResetDicomNode() 
155     {
156       mDicomNode = 0;
157     }
158
159     inline void SetItemId ( const wxTreeItemId& item ) { mItemId = item; }
160     inline const wxTreeItemId& GetItemId() const { return mItemId; }
161
162     inline bool IsDefault() const { return (mType == 0); }
163     inline bool IsDatabase() const { return (mType == 2); }
164     inline bool IsDicomNode() const { return (mType == 1); }
165     
166     inline DicomNode* GetDicomNode() { return mDicomNode; }
167     inline long& UpdateTime() { return mUpdateTime; }
168     inline bool IsLoaded() 
169     { 
170       mDicomNode->GetData<WxGimmickDicomNodeData*>()->IsLoaded();
171     }
172     inline void SetLoaded(bool v) 
173     { 
174       mDicomNode->GetData<WxGimmickDicomNodeData*>()->SetLoaded(v);
175     }
176
177     inline int GetUserFlags() const { return mUserFlags; }
178     inline void SetUserFlags(int f) { mUserFlags = f; }
179
180   private:
181     // The type of item : 
182     // 0 = Default
183     // 1 = DicomNode
184     // 2 = DicomNode of type Database
185     int mType;
186     wxTreeItemId mItemId;
187     DicomNode* mDicomNode;
188     //    bool mLoaded;
189     long mUpdateTime;
190     int mUserFlags;
191   };
192   //================================================================
193  
194
195   //================================================================
196   WxGimmickDicomNodeData::~WxGimmickDicomNodeData()
197   {
198     if (mTreeItemData) 
199       {
200         mTreeItemData->ResetDicomNode();
201       }
202   }
203   //================================================================
204
205
206  
207
208
209
210
211
212   //================================================================
213   class WxGimmickSettingsDialog : public wxDialog
214   {
215   public:
216     WxGimmickSettingsDialog(wxWindow *parent);
217     ~WxGimmickSettingsDialog();
218     
219     
220   };
221   //================================================================
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250   //================================================================
251   //================================================================
252   //================================================================
253   //================================================================
254   //================================================================
255   //================================================================
256   //================================================================
257   WxGimmick::WxGimmick(wxWindow *parent, 
258                                                    wxWindowID id,
259                                                    const wxPoint& pos,
260                                                    const wxSize& size,
261                                                    int threads)
262     : wxPanel(parent,id,pos,size),
263       mSaveConfigurationOnClose(true),
264       mReader(threads)
265   {
266     // Start the threads ...
267     mReader.Start();
268
269     //
270     SetDatabaseExtension("sqlite3");
271     // Create the UserSettings dir if does not exist
272     CreateUserSettingsDirectory();
273     // Sets the current directory to the Setting dir
274     mCurrentDirectory =  std2wx(GetUserSettingsDirectory());
275
276     // Window layout creation
277     wxBoxSizer *topsizer = new wxBoxSizer(wxVERTICAL);
278
279     // Left/Right split
280     mSplitter1 = new wxSplitterWindow( this, -1);
281
282     // TreeCtrl on the left
283     long style = 
284       wxTR_HIDE_ROOT 
285       | wxTR_HAS_BUTTONS 
286       | wxTR_NO_LINES
287       //| wxTR_LINES_AT_ROOT 
288       | wxTR_FULL_ROW_HIGHLIGHT
289       //      | wxTR_SINGLE 
290       | wxTR_MULTIPLE
291       | wxTR_EDIT_LABELS ;
292  
293     /*
294       style = style 
295       | wxTR_EDIT_LABELS        //Use this style if you wish the user to be able to edit labels in the tree list control.
296       //wxTR_NO_BUTTONS         For convenience to document that no buttons are to be drawn.
297       | wxTR_HAS_BUTTONS        //Use this style to show + and - buttons to the left of parent items.
298       | wxTR_TWIST_BUTTONS      //Use this style to show Mac-style twister buttons to the left of parent items. If both wxTR_HAS_BUTTONS and wxTR_TWIST_BUTTONS are given, twister buttons are generated.
299       //wxTR_NO_LINES   Use this style to hide vertical level connectors.
300       | wxTR_FULL_ROW_HIGHLIGHT         //Use this style to have the background colour and the selection highlight extend over the entire horizontal row of the tree list control window. (This flag is ignored under Windows unless you specify wxTR_NO_LINES as well.)
301       | wxTR_LINES_AT_ROOT      //Use this style to show lines between root nodes. Only applicable if wxTR_HIDE_ROOT is set and wxTR_NO_LINES is not set.
302       | wxTR_HIDE_ROOT  //Use this style to suppress the display of the root node, effectively causing the first-level nodes to appear as a series of root nodes.
303       //   wxTR_ROW_LINES //    Use this style to draw a contrasting border between displayed rows.
304       //      wxTR_HAS_VARIABLE_ROW_HEIGHT//    Use this style to cause row heights to be just big enough to fit the content. If not set, all rows use the largest row height. The default is that this flag is unset.
305       // wxTR_SINGLE    For convenience to document that only one item may be selected at a time. Selecting another item causes the current selection, if any, to be deselected. This is the default.
306       | wxTR_MULTIPLE //        Use this style to allow a range of items to be selected. If a second range is selected, the current range, if any, is deselected.
307       | wxTR_EXTENDED //        Use this style to allow disjoint items to be selected. (Only partially implemented; may not work in all cases.)
308       //wxTR_DEFAULT_STYLE      The set of flags that are closest to the defaults for the native control for a particular toolkit.
309       //| wxTR_VIRTUAL //The application provides items text on demand.
310       */
311   
312     mTreeListCtrl = new wxTreeListCtrl(mSplitter1,
313                                        TreeListCtrlId,
314                                        wxDefaultPosition,
315                                        wxDefaultSize,
316                                        style);
317
318     mTreeListCtrl->SetIndent(0);
319     mTreeListCtrl->SetLineSpacing(5); 
320
321     CreateImageList();
322
323     // Right panel
324     wxPanel *rpanel = new wxPanel( mSplitter1, -1 ); 
325     // Right sizer
326     wxBoxSizer  *rsizer = new wxBoxSizer(wxHORIZONTAL);
327     // Right panel top/bottom split
328     mSplitter2 = new wxSplitterWindow( rpanel , -1);
329
330     // Image panel (top)
331     mPanelImage = new wxPanel(mSplitter2,-1);
332     mPanelImage->SetBackgroundColour( wxColour(0,0,0) );
333     // Image sizer
334     wxBoxSizer *isizer = new wxBoxSizer(wxHORIZONTAL  );
335     mPanelImage->SetSizer( isizer ); 
336   
337     // Fields view (bottom)
338     mFieldsView = new WxGimmickFieldsView(mSplitter2,-1,
339                                             wxDefaultPosition,
340                                             wxDefaultSize,0);
341
342     mFieldsView->SetColors
343       ( GetSettings().Colour(DicomNode::Database),
344         GetSettings().BgColour(DicomNode::Database),
345         GetSettings().Colour(DicomNode::Patient),
346         GetSettings().BgColour(DicomNode::Patient),
347         GetSettings().Colour(DicomNode::Study),
348         GetSettings().BgColour(DicomNode::Study),
349         GetSettings().Colour(DicomNode::Series),
350         GetSettings().BgColour(DicomNode::Series),
351         GetSettings().Colour(DicomNode::Image),
352         GetSettings().BgColour(DicomNode::Image));
353     
354     
355
356     int wsize = size.GetWidth();
357     int hsize = size.GetHeight();
358     int previewhsize = 150;
359     int previewwsize = 400;
360
361     mSplitter2->SetMinimumPaneSize( previewhsize );
362     mSplitter2->SplitHorizontally( mPanelImage, mFieldsView, 
363                                    hsize - previewhsize);
364   
365     rsizer->Add( mSplitter2,1,wxGROW  ,0);
366   
367     rpanel->SetAutoLayout(true);
368     rpanel->SetSizer( rsizer );     
369     rpanel->Layout(); 
370   
371     // previewer
372     mInteractor = new wxVTKRenderWindowInteractor(mPanelImage,-1);
373     mInteractor->UseCaptureMouseOn();   
374   
375     mViewer     = vtkImageViewer2::New();
376     mViewer->SetupInteractor ( mInteractor );
377     mViewer->SetInput(mReader.GetImage(""));
378
379     isizer-> Add( mInteractor ,1,wxGROW  ,0);
380
381     topsizer->Add( mSplitter1,1,wxGROW  ,0);
382
383     // Left/right split 
384     mSplitter1->SetMinimumPaneSize( 200 );
385     mSplitter1->SplitVertically(mTreeListCtrl, 
386                                 rpanel, 
387                                 wsize - previewwsize );
388
389   
390     //    ProcessImageEvents();
391     SetSizer( topsizer );     
392     SetAutoLayout(true);
393  
394     mDatabaseListFile = GetUserSettingsDirectory();
395     mDatabaseListFile += "collections.txt";
396
397     LoadConfiguration();
398     Layout();
399
400     ShowImage(mReader.GetImage(""));
401
402   }
403   //================================================================
404     
405 // file separator
406 #if defined(_WIN32)
407 #define VALID_FILE_SEPARATOR "\\"
408 #define INVALID_FILE_SEPARATOR "/"
409 #else
410 #define INVALID_FILE_SEPARATOR "\\"
411 #define VALID_FILE_SEPARATOR "/"
412 #endif
413
414   //================================================================
415   const std::string& WxGimmick::GetUserSettingsDirectory()
416   {
417     if (mUserSettingsDirectory.size()==0) 
418       {
419 #if defined(__GNUC__)
420         mUserSettingsDirectory = getenv("HOME");
421 #elif defined(_WIN32)
422         mUserSettingsDirectory = getenv("USERPROFILE");
423 #endif
424         mUserSettingsDirectory += "/.gimmick/";
425         boost::algorithm::replace_all( mUserSettingsDirectory, 
426                                        INVALID_FILE_SEPARATOR , 
427                                        VALID_FILE_SEPARATOR);
428       }
429     return mUserSettingsDirectory;
430   }
431   //================================================================
432
433   //========================================================================
434   void WxGimmick::CreateUserSettingsDirectory()
435   {
436     if (! boost::filesystem::is_directory( GetUserSettingsDirectory() ) )
437       {
438         creaMessage("Gimmick!",1,"==> Directory '"<<GetUserSettingsDirectory()<<"' "
439                     << "does not exist : creating it"<<std::endl);
440
441         if ( ! boost::filesystem::create_directory( GetUserSettingsDirectory() ) )
442           {
443             creaMessage("Gimmick!",1,"!! ERROR CREATING '"<<GetUserSettingsDirectory()<<"'");
444           }
445       }
446   }
447   //========================================================================
448
449   //================================================================
450   WxGimmick::~WxGimmick()
451   {
452 //    std::cout << "WxGimmick::~WxGimmick()" <<std::endl;
453     if (mSaveConfigurationOnClose) SaveConfiguration();
454     
455         mReader.Stop();
456 //      std::cout << "Reader stopped"<<std::endl;
457     
458         DicomDatabaseListType::iterator i;
459     for (i =GetDicomDatabaseList().begin(); 
460          i!=GetDicomDatabaseList().end(); 
461          ++i)
462       {
463         delete *i;
464       }
465     mViewer->Delete();
466     delete mInteractor;  
467   }
468   //================================================================
469
470
471   //================================================================
472   void WxGimmick::RebuildView()
473   {
474     wxBusyCursor busy;
475
476     mTreeListCtrl->DeleteRoot(); //DeleteAllItems();
477     //    mTreeDefaultItemId = wxTreeItemId();
478
479   
480     int nbattr = mSettings.GetMaxNumberOfColumns();
481
482     for (int j=0;j<nbattr; j++)
483       {
484         mTreeListCtrl->AddColumn (_T(""),
485                                   200, //DEFAULT_COL_WIDTH,
486                                   wxALIGN_LEFT,
487                                   -1,
488                                   true,
489                                   false);
490       }
491     mTreeListCtrl->SetMainColumn (0);
492     mTreeListCtrl->SetColumnEditable (0, true);
493
494     mTreeRootId = mTreeListCtrl->AddRoot( _T(""),Icon_Root,Icon_Root);
495
496     // The collections columns legends
497     mCollectionsTitlesItemId = 
498       CreateChildrenColumnsTitles(mTreeRootId,DicomNode::Database);
499
500     DicomDatabaseListType::iterator i;
501     for (i =GetDicomDatabaseList().begin(); 
502          i!=GetDicomDatabaseList().end(); 
503          ++i)
504       {  
505         UpdateDicomDatabaseView(*i);
506       }
507
508     mTreeListCtrl->Expand(mTreeRootId);
509     // LG : test
510     //    mTreeListCtrl->ExpandAll(mTreeRootId);
511     //    std::cout << "EO RebuildAll"<<std::endl;
512   }
513   //================================================================
514
515   
516
517   //================================================================
518   void WxGimmick::UpdateDicomDatabaseView(DicomDatabase* db)
519   {
520     wxBusyCursor busy;
521     wxTreeItemId dbid;
522     TreeItemData *data;
523
524     // Does the db exist ?
525     wxTreeItemIdValue cookie;
526     for (dbid = mTreeListCtrl->GetFirstChild(mTreeRootId,cookie);
527          dbid.IsOk();
528          dbid = mTreeListCtrl->GetNextChild(mTreeRootId,cookie))
529       {    
530         data = (TreeItemData *)mTreeListCtrl->GetItemData(dbid);
531         if ((data->IsDatabase())&&(data->GetDicomNode()==db)) break;
532       }
533
534     // Not found : create
535     if (!dbid.IsOk())
536       {
537         // Icon
538         int iconid = Icon[DicomNode::Database];
539
540         // Creation
541         //      std::cout << " -> Creating item for '"<<db->GetLabel()<<"'"<<std::endl;
542         data = new TreeItemData(db);
543         dbid = mTreeListCtrl->AppendItem( mTreeRootId,
544                                           std2wx(db->GetLabel()),
545                                           iconid, 
546                                           iconid, 
547                                           data );
548         data->SetItemId(dbid);
549         mTreeListCtrl->SetItemTextColour
550           (dbid,mSettings.Colour(DicomNode::Database));
551         mTreeListCtrl->SetItemBackgroundColour
552           (dbid,mSettings.BgColour(DicomNode::Database));
553         UpdateColumns(dbid);
554         // The patients columns legends
555         CreateChildrenColumnsTitles(dbid,DicomNode::Patient);
556
557       }
558     // Increase UpdateTime to detect obsolete items after 
559     // tree traversal
560     data->UpdateTime()++;
561     // Recurse
562     DicomNode::ChildrenListType::iterator j;
563     for (j= db->GetChildrenList().begin();
564          j!=db->GetChildrenList().end();
565          j++)
566       {
567         UpdateDicomNodeView(*j,dbid);
568       }
569     
570     DeleteObsoleteChildren(dbid);
571
572     mTreeListCtrl->EnsureVisible(dbid);
573
574   }
575   //================================================================
576
577   //================================================================
578   void WxGimmick::UpdateDicomNodeView(DicomNode* n, 
579                                                     const wxTreeItemId& parent)
580   {
581   
582     wxBusyCursor busy;
583     //     std::cout << "* UpdateDicomNodeView("<<n->GetLabel()<<")"<<std::endl;
584
585     wxTreeItemId newparent = parent; 
586     TreeItemData *data;
587     // Skip study level
588     if ((!mSettings.MergeStudySeries()) ||
589         (n->GetType() != DicomNode::Study))
590       {
591         // Does the item exist ?
592         wxTreeItemIdValue cookie;
593         for (newparent = mTreeListCtrl->GetFirstChild(parent,cookie);
594              newparent.IsOk();
595              newparent = mTreeListCtrl->GetNextChild(parent,cookie))
596           {    
597             data = (TreeItemData *)mTreeListCtrl->GetItemData(newparent);
598             if (data->GetDicomNode() == n) break;
599           }
600         // Not found : create
601         if (!newparent.IsOk())
602           {
603             int image(Icon[n->GetType()]);
604             wxColour *colour(&mSettings.Colour(n->GetType()));
605             wxColour *bgcolour(&mSettings.BgColour(n->GetType()));
606
607             if (n->GetType()==DicomNode::Image)
608               {
609                 //              std::cout << "!!!Image"<<std::endl;
610                 if (n->GetData<NodeData*>()!=0)
611                   {
612                     //              std::cout << ">> n->GetData<NodeData*>()!=0" << std::endl;
613                     if (n->GetData<NodeData*>()->IsLoaded())
614                       {
615                         colour = &mSettings.LoadedImageColour();
616                       }
617                     //              std::cout << "<< n->GetData<NodeData*>()!=0" << std::endl;
618                   }
619               }
620
621             data = new TreeItemData(n);
622             newparent = mTreeListCtrl->AppendItem(parent, 
623                                                   _T(""),
624                                                   image, image,
625                                                   data);
626             data->SetItemId(newparent);
627             mTreeListCtrl->SetItemTextColour(newparent,*colour);
628             mTreeListCtrl->SetItemBackgroundColour(newparent,*bgcolour);
629  
630             UpdateColumns(newparent);
631
632             
633             if (n->GetType()!=DicomNode::Image)
634               {
635                 CreateChildrenColumnsTitles(newparent,n->GetType()+1);
636               }
637             
638           }
639         else 
640           {
641             UpdateColumns(newparent,true);
642
643           }
644         // synchonise update time with parent
645         TreeItemData * parent_data = 
646           (TreeItemData *)mTreeListCtrl->GetItemData(parent);
647         data->UpdateTime() = parent_data->UpdateTime();
648       }
649   
650     DicomNode::ChildrenListType::iterator i;
651     for (i=n->GetChildrenList().begin();
652          i!=n->GetChildrenList().end();
653          i++)
654       {
655         UpdateDicomNodeView(*i,newparent);
656       }
657
658     if (n->GetType() != DicomNode::Image) 
659       DeleteObsoleteChildren(newparent);
660
661   }
662   //================================================================
663
664   //================================================================
665   void WxGimmick::UpdateColumns(wxTreeItemId& item,
666                                               bool only_first)
667   {
668     TreeItemData *data = 
669       (TreeItemData *)mTreeListCtrl->GetItemData(item);
670     DicomNode* node = data->GetDicomNode();
671     
672     if (only_first) 
673       {
674         // Update only the first field (for #children update)
675         DicomNode* node2 = node;
676         // If Study and Series level are merged and node type is Series
677         // then have to get back to the Study level
678         if ((mSettings.MergeStudySeries())&& 
679             (node->GetType() == DicomNode::Series))
680           node2 = node->GetParent();
681     
682         std::string lab;
683         lab += node2->GetFieldValueMap()
684           [ mSettings.GetColumnList(node2->GetType())[0].Key ];
685
686         if (node->GetType() != DicomNode::Image) 
687           {
688             if (node->GetChildrenList().size()>0)
689               {
690                 char sz[100];
691                 sprintf(sz,"  [%d]",node->GetNumberOfChildren());
692                 lab += sz;
693               }
694           }
695         mTreeListCtrl->SetItemText(item,std2wx(lab));
696       }
697     else 
698       {
699         
700         int c = 0;
701         Settings::ColumnListType::iterator col;
702         // If Study and Series level are merged and node type is Series
703         // then have to fill the Study level cols first
704         if ((mSettings.MergeStudySeries())&& 
705             (node->GetType() == DicomNode::Series))
706           {
707             DicomNode* node2 = node->GetParent();
708             for (col  = mSettings.GetColumnList(node2->GetType()).begin();
709                  col != mSettings.GetColumnList(node2->GetType()).end();
710                  ++col)
711               {
712                 std::string s = node2->GetFieldValueMap()[col->Key];
713                 
714                 if (c==0) 
715                   {
716                     char sz[100];
717                     sprintf(sz,"  [%d]",node->GetNumberOfChildren());
718                     s += sz;
719                   }
720                 mTreeListCtrl->SetItemText (item, c,  std2wx(s));
721                 c++;
722               }   
723           }
724         
725         for (col  = mSettings.GetColumnList(node->GetType()).begin();
726              col != mSettings.GetColumnList(node->GetType()).end();
727              ++col)
728           {
729             std::string s = node->GetFieldValueMap()[col->Key];
730             if ((c==0)&&(node->GetType() != DicomNode::Image))
731               {
732                 char sz[100];
733                 sprintf(sz,"  [%d]",node->GetNumberOfChildren());
734                 s += sz;
735               }
736             mTreeListCtrl->SetItemText (item, c,  std2wx(s));
737             c++;
738           }
739       }
740
741   }
742   //================================================================
743
744   //================================================================
745   wxTreeItemId WxGimmick::CreateChildrenColumnsTitles
746   (wxTreeItemId& item,
747    DicomNode::Type t)
748   {
749     // Creates the sub-level columns titles
750     TreeItemData* data 
751       = new TreeItemData(0);
752     wxTreeItemId id = mTreeListCtrl->AppendItem( item,
753                                                  _T(""),
754                                                  -1,
755                                                  -1,
756                                                  data );
757     data->SetItemId(id);
758     mTreeListCtrl->SetItemFont(id, *wxITALIC_FONT);
759     mTreeListCtrl->SetItemTextColour(id, mSettings.Colour(t));
760     mTreeListCtrl->SetItemBackgroundColour(id, mSettings.BgColour(t));
761     UpdateColumnsTitles(id,t);  
762     return id;
763   }
764   //================================================================
765
766   //================================================================
767   void WxGimmick::UpdateColumnsTitles(wxTreeItemId& item,
768                                                     DicomNode::Type t)
769   {
770     //    std::cout << "Update columns titles "<<t<<std::endl;
771     int c = 0;
772     Settings::ColumnListType::iterator col;
773     for (col  = mSettings.GetColumnList(t).begin();
774          col != mSettings.GetColumnList(t).end();
775          ++col)
776       {
777         //      std::cout << col->Name << std::endl;
778         mTreeListCtrl->SetItemText (item, c,  std2wx(col->Name));
779         c++;
780       }
781   }
782   //================================================================
783
784
785   //================================================================
786   void WxGimmick::DeleteObsoleteChildren(wxTreeItemId& id)
787     
788   {
789     TreeItemData * parent_data = 
790       (TreeItemData *)mTreeListCtrl->GetItemData(id);
791     
792     wxTreeItemId child;
793     wxTreeItemIdValue cookie;
794     std::vector<wxTreeItemId> children;
795     for (child = mTreeListCtrl->GetFirstChild(id,cookie);
796          child.IsOk();
797          child = mTreeListCtrl->GetNextChild(id,cookie))
798       {    
799         children.push_back(child);
800       }
801     std::vector<wxTreeItemId>::iterator i;
802     for (i=children.begin();i!=children.end();++i)
803       {
804         TreeItemData *data = 
805           (TreeItemData *)mTreeListCtrl->GetItemData(*i);
806         if (
807             ((data->GetDicomNode()>0) && 
808              ((data->UpdateTime() != parent_data->UpdateTime()))) || 
809             ((data->IsDicomNode()) &&
810              (data->GetDicomNode()==0))
811             ) 
812           {
813             //      std::cout << "DOBSC="<<mTreeListCtrl->GetItemText(*i)<<std::endl;
814             mTreeListCtrl->Delete(*i);
815           }
816       }
817   }
818   //================================================================
819
820   //================================================================
821   void WxGimmick::OpenOrNewDatabase(bool open)
822   {
823     wxBusyCursor busy;
824
825     long style = wxFD_SAVE | wxFD_OVERWRITE_PROMPT;
826     if (open) style = wxOPEN | wxFILE_MUST_EXIST;
827     std::string wc("*.");
828     wc += GetDatabaseExtension();
829
830     // TO DO : Handler give their wildcards
831     wxFileDialog* FD = new wxFileDialog( 0, 
832                                          _T("Select file"),
833                                          mCurrentDirectory,
834                                          _T(""),
835                                          std2wx(wc),
836                                          style,
837                                          wxDefaultPosition);
838
839         if (FD->ShowModal()!=wxID_OK) return;
840
841     std::string filename = wx2std (FD->GetPath());
842         mCurrentDirectory = FD->GetDirectory();
843
844     if (!open)
845       {
846         boost::filesystem::path filepath(filename);
847         boost::filesystem::change_extension(filepath,GetDatabaseExtension());
848         if ( boost::filesystem::exists(filepath) )
849           {
850             if ( ! boost::filesystem::remove(filepath) )
851               {
852                 wxMessageBox(_T("Could not overwrite ")
853                              +std2wx(filepath.string()),
854                              _T("Error"),
855                              wxOK,this);
856                 return;
857         
858               }
859           }
860       }
861     
862     DicomDatabase* db = new DicomDatabase(filename);
863     bool r;
864     if (open) 
865       {
866         r = db->Open();
867         if (!r) 
868           {
869             wxMessageBox(_T("An error occured while opening ")
870                          +std2wx(filename),
871                          _T("Error"),
872                          wxOK,this);
873             return;
874           }
875       }
876     else 
877       {
878         wxString collname = 
879           wxGetTextFromUser(_T("Enter collection name"),_T("New collection"),
880                             _T(""),this);
881         db->SetName(wx2std(collname));
882
883         r = db->New();
884         if (!r) 
885           {
886             wxMessageBox(_T("An error occured while creating ")
887                          +std2wx(filename),
888                          _T("Error"),
889                          wxOK,this);
890             return;
891           }
892       }
893
894     GetDicomDatabaseList().push_back(db);
895     UpdateDicomDatabaseView(db);
896
897   }
898   //================================================================
899
900
901   //================================================================
902   void WxGimmick::LoadConfiguration()
903   {
904     wxBusyCursor busy;
905     //    std::cout << "WxGimmick : Reading config"<<std::endl;
906
907     //    std::cout << "==> Loading collections from '"<<mDatabaseListFile
908     //        <<"'"<<std::endl;
909
910     std::ifstream s;
911     s.open(mDatabaseListFile.c_str());
912     if (s.good())
913         {
914         while (!s.eof()) 
915           {
916             std::string str;
917             std::getline(s,str);
918             if (str.size()==0) continue;
919
920             std::vector<std::string> tokens;        
921             boost::split( tokens, str, boost::is_any_of("\t") );
922
923             DicomDatabase* db = new DicomDatabase(tokens[0]);
924             
925             //  std::cout << "  -> Loading collection '"<<tokens[0]<<"'"<<std::endl;
926             
927             if (tokens.size()==2) 
928               {
929                 db->SetName(tokens[1]);
930               }
931             
932             if (db->Open()) 
933               {
934                 GetDicomDatabaseList().push_back(db);
935                 db->DBLoadChildren(db,DicomNode::Patient);
936                 if (mSettings.HasActiveComparator(DicomNode::Patient))
937                   {
938                     db->SortChildren
939                       ( mSettings.GetActiveComparator(DicomNode::Patient)  );
940                   }
941               }
942             else 
943               {
944                 //          std::cout << "    ... ERROR !"<<std::endl;
945                 delete db;
946               }
947           }
948         s.close();
949         }
950         else 
951         {
952                 std::cout << "ERROR opening "<<mDatabaseListFile<<std::endl;
953         }
954
955         
956     mTreeListCtrl->SetBackgroundColour(mSettings.BgColour(DicomNode::Database));
957     if (GetDicomDatabaseList().begin() != 
958         GetDicomDatabaseList().end() )
959       {
960         mFieldsView->UpdateFields(*GetDicomDatabaseList().begin());
961       }
962
963     RebuildView();
964       
965   }
966   //================================================================
967   
968   //================================================================
969   void WxGimmick::SaveConfiguration()
970   {
971     wxBusyCursor busy;
972     creaMessage("Gimmick!",1,"Gimmick! : Saving configuration..."<<std::endl);
973     
974     creaMessage("Gimmick!",1,"Gimmick! : ==> Saving collections in '"
975                 <<mDatabaseListFile<<"'"<<std::endl);
976     
977     std::ofstream s;
978     s.open(mDatabaseListFile.c_str());
979     if (!s.good())
980       {
981         creaError("Gimmick! : error opening '"<<mDatabaseListFile<<"'"); 
982       }
983     
984     DicomDatabaseListType::iterator i;
985     for (i =GetDicomDatabaseList().begin();
986          i!=GetDicomDatabaseList().end();
987          ++i)
988       {  
989         s << (*i)->GetFileName() << "\t";
990         s << (*i)->GetName() << std::endl;
991       }
992     
993     s.close();
994
995   }
996   //================================================================
997
998   /*
999   //================================================================
1000   void  WxGimmick::OnClose(wxCloseEvent& event)
1001   {
1002   if (mSaveConfigurationOnClose) SaveConfiguration();
1003   }
1004   //================================================================
1005   */
1006   //================================================================
1007   void WxGimmick::OnItemActivated(wxTreeEvent& event)
1008   {  
1009     event.Skip();
1010     return;
1011
1012     wxBusyCursor busy;
1013     //    std::cout << "OnItemActivated" <<std::endl;
1014     wxTreeItemId itemId = event.GetItem();
1015     if (mTreeListCtrl->IsExpanded(itemId))
1016       {
1017         mTreeListCtrl->Collapse(itemId);
1018       }
1019     else 
1020       {
1021         mTreeListCtrl->Expand(itemId);
1022       }
1023   }
1024   //================================================================
1025
1026   //================================================================
1027   void WxGimmick::OnItemExpanded(wxTreeEvent& event)                   
1028   {
1029
1030     //    std::cout << "* Expanded *"<<std::endl;
1031     //  }
1032
1033     wxBusyCursor busy;
1034     
1035     wxTreeItemId itemId = event.GetItem();
1036  
1037     // expand if collapsed and collapse if expanded ...
1038     TreeItemData *item = 
1039       (TreeItemData *)mTreeListCtrl->GetItemData(itemId);
1040     if (item)
1041       {
1042         if ( ( item->IsDicomNode() || item->IsDatabase() ) &&
1043              ( ! item->GetDicomNode()->ChildrenLoaded() ) )
1044           {
1045                 
1046             // If children not already loaded : do it 
1047             if (
1048                 item->GetDicomNode()->GetDicomDatabase()->DBLoadChildren
1049                 (item->GetDicomNode(),item->GetDicomNode()->GetType()+1)
1050                 > 0 )
1051               {
1052                     
1053                 // Some new children loaded
1054                 // Sort them
1055                 if (mSettings.HasActiveComparator
1056                     (item->GetDicomNode()->GetType()+1))
1057                   {
1058                     /*                  std::cout << "Sorting using '"
1059                       << mSettings.GetActiveComparator
1060                       (item->GetDicomNode()->GetType()+1).GetName() 
1061                       << "' ... ";
1062                     */
1063                     item->GetDicomNode()->SortChildren
1064                       ( mSettings.GetActiveComparator
1065                         (item->GetDicomNode()->GetType()+1)
1066                         );
1067                     //                  std::cout << "ok"<<std::endl;
1068                   }
1069                 /*              
1070                 // If images : sort them 
1071                 if (item->IsDicomNode())
1072                 {
1073                 if (item->GetDicomNode()->GetType()==DicomNode::Series)
1074                 {  
1075                 // SORT
1076                    
1077                 LexicographicalDicomNodeComparator compare;
1078                 // DicomNodeImageImageNumberComparator c1;
1079                     
1080                 DicomNodeImageSliceLocationComparator c1;
1081                 DicomNodeImageImageNumberComparator c2;
1082                 DicomNodeImageFileNameComparator cn;
1083                 compare.Add(c1);
1084                 compare.Add(c2);
1085                 compare.Add(cn);
1086                 //              std::cout << "SORT"<<std::endl;
1087                 item->GetDicomNode()->SortChildren(compare);
1088                 //              std::cout << "EO SORT"<<std::endl;
1089                 //
1090                     
1091                 }
1092                 }
1093                 */
1094                     
1095                 // Update tree
1096                 DicomNode::ChildrenListType::iterator i;
1097                 for (i=item->GetDicomNode()->GetChildrenList().begin();
1098                      i!=item->GetDicomNode()->GetChildrenList().end();
1099                      i++)
1100                   {
1101                     UpdateDicomNodeView(*i,itemId);
1102                   }
1103               }
1104             // EO If children not already loaded 
1105           }
1106       }
1107     //  mTreeListCtrl->Expand(itemId);
1108      
1109   }
1110   //================================================================
1111
1112
1113   /*
1114   //=====================================================================
1115   void WxGimmick::InsertRoot(wxTreeItemId& id, Root* r)
1116   {
1117   wxBusyCursor busy;
1118   TreeItemData *data = (TreeItemData *)mTreeListCtrl->GetItemData(id);
1119   if (data)
1120   { 
1121         
1122   wxStopWatch sw;
1123   data->GetDicomNode()->GetDicomDatabase()->LoadAll();
1124   printf(">>>>>> Time to load all = %ldms \n",sw.Time());
1125
1126   UpdateRootView(data->GetDicomNode()->GetDicomDatabase());
1127     
1128
1129   if (data->IsDicomNode())
1130   {
1131   wxStopWatch sw1;
1132   r->Insert(data->GetDicomNode());
1133   printf(">>>>>> Time to insert = %ldms \n",sw1.Time());
1134   UpdateRootView(r);
1135   }
1136   else if (data->IsDatabase())
1137   {     
1138   wxStopWatch sw1;
1139   DicomNode::ChildrenListType::iterator j;
1140   for (j= data->GetDicomNode()->GetChildrenList().begin();
1141   j!=data->GetDicomNode()->GetChildrenList().end();
1142   j++)
1143   {
1144   r->Insert(*j);
1145   }
1146   printf(">>>>>> Time to insert = %ldms \n",sw1.Time());
1147   UpdateRootView(r);    
1148   }
1149
1150   }
1151   }
1152   //=====================================================================
1153   */
1154
1155   
1156   //=================================================
1157   void WxGimmick::DeleteDicomDatabase(wxTreeItemId& id, 
1158                                                     DicomDatabase* db)
1159   {
1160     wxBusyCursor busy;
1161     DicomDatabaseListType::iterator i = find(GetDicomDatabaseList().begin(),
1162                                              GetDicomDatabaseList().end(),
1163                                              db);
1164     delete (*i);
1165     GetDicomDatabaseList().erase(i);
1166     mTreeListCtrl->Delete(id);
1167   }
1168   //=================================================
1169
1170
1171
1172   //=====================================================================
1173   
1174   void WxGimmick::OnItemRightClick(wxTreeEvent& event)
1175   {
1176
1177     wxTreeItemId itemId = event.GetItem();
1178     if (itemId.IsOk())
1179       {
1180         wxPoint clientpt = event.GetPoint();
1181         wxPoint screenpt = ClientToScreen(clientpt);
1182         ShowMenu(itemId, clientpt);
1183       }
1184     event.Skip();
1185   }
1186   //=====================================================================
1187   
1188
1189
1190   //=====================================================================
1191   void WxGimmick::ShowMenu(wxTreeItemId id, const wxPoint& pt)
1192   { 
1193
1194     //  std::cout << "ShowMenu" <<std::endl;
1195     mItemOfMenu = id;
1196     TreeItemData *data = 
1197       (TreeItemData *)mTreeListCtrl->GetItemData(id);
1198   
1199     /*
1200       wxString title;
1201       if ( id.IsOk() )
1202       {
1203       title << wxT("Menu for ") << mTreeListCtrl->GetItemText(id);
1204       }
1205       else
1206       {
1207       title = wxT("Menu for no particular item");
1208       }
1209     */
1210
1211 #if wxUSE_MENUS
1212     wxMenu menu;
1213
1214     if (id==mCollectionsTitlesItemId)
1215       {
1216         menu.Append(PopUp_NewCollection, _T("&New collection"));
1217         menu.Append(PopUp_OpenCollection, _T("&Open collection"));
1218       }
1219     if (data)
1220       {
1221         if (data->IsDatabase())
1222           {
1223             wxMenu* addmenu = new wxMenu;
1224             addmenu->Append(PopUp_AddDirectory, _T("Scan &Directory"));
1225             addmenu->Append(PopUp_AddFile, _T("Select &File(s)"));
1226             //      addmenu->Append(PopUp_AddRawFile, _T("Add &Raw image"));
1227             menu.AppendSubMenu(addmenu, _T("&Add image(s) to collection..."));
1228             menu.Append(PopUp_CloseCollection, _T("&Close collection"));
1229             menu.Append(PopUp_DeleteCollection, _T("&Delete collection"));
1230           }
1231         if (data->IsDicomNode())
1232           {
1233             /*
1234             //      LG : BUGGY
1235             std::string str("&Remove ");
1236             str += data->GetDicomNode()->GetTypeName();
1237             menu.Append(PopUp_Remove, std2wx(str));
1238             */
1239           }
1240       
1241         if ((data->GetDicomNode()>0)&&
1242             ( data->GetDicomNode()->GetType()<DicomNode::Image))
1243           {
1244             int ctype = data->GetDicomNode()->GetType()+1;
1245             if (mSettings.HasActiveComparator(ctype))
1246               {
1247                 wxMenu* sortmenu = new wxMenu;
1248                 int n = 0;
1249                 Settings::ComparatorsList::iterator i;
1250                 for (i =mSettings.GetComparatorsList(ctype).begin();
1251                      i !=mSettings.GetComparatorsList(ctype).end();
1252                      ++i)
1253                   {
1254                     sortmenu->AppendRadioItem(PopUp_Sort+n, std2wx(i->GetName()));
1255                     n++;
1256                   }    
1257               
1258                 sortmenu->Check(PopUp_Sort+
1259                                 mSettings.GetActiveComparatorIndex(ctype)
1260                                 ,true);
1261               
1262                 std::string sortmenustr("&Sort ");
1263                 sortmenustr += DicomNode::GetPluralTypeName(ctype);
1264                 sortmenustr += " by...";
1265                 if (menu.GetMenuItemCount()>0) menu.AppendSeparator();
1266                 menu.AppendSubMenu(sortmenu,std2wx(sortmenustr));
1267               
1268                 /*
1269                   item->GetDicomNode()->SortChildren
1270                   ( mSettings.GetActiveComparator
1271                   (item->GetDicomNode()->GetType()+1)
1272                   );
1273                   std::cout << "ok"<<std::endl;
1274                 */
1275               }
1276           }
1277       }
1278     // Event : user can customize the menu 
1279     WxGimmickEvent 
1280       ev(wxEVT_COMMAND_TREEVIEWLIST_CONTEXTUAL_MENU,this,id);
1281     ev.SetMenu(&menu);
1282     if (data)
1283       {
1284         ev.SetDicomNode(data->GetDicomNode());
1285       }
1286     GetEventHandler()->ProcessEvent(ev);
1287     //
1288   
1289     if (menu.GetMenuItemCount()>0) menu.AppendSeparator();
1290     menu.Append(PopUp_Settings, wxT("&Settings..."));
1291     menu.Append(PopUp_About, wxT("&About..."));
1292   
1293
1294   
1295     /*
1296       wxMenu* newmenu = new wxMenu;
1297       wxMenu* openmenu = new wxMenu;
1298       Tree::RootHandlerListType::iterator h;
1299       int i=0;
1300       for (h= Tree::GetRootHandlerList().begin();
1301       h!=Tree::GetRootHandlerList().end();
1302       h++)
1303       {
1304       if ((*h)->SupportsNew()) 
1305       {
1306       newmenu->Append(PopUp_New+i, std2wx((*h)->GetName()));
1307       }
1308       if ((*h)->SupportsOpen()) 
1309       openmenu->Append(PopUp_Open+i, std2wx((*h)->GetName()));
1310       i++;
1311       }
1312     
1313       menu.AppendSubMenu(openmenu, _T("&Open"));
1314       menu.AppendSubMenu(newmenu, _T("&New"));
1315
1316       if (data)
1317       { 
1318       if ((data->IsDatabase())||(data->IsDicomNode()))
1319       {
1320       Root* itroot = data->GetDicomNode()->GetDicomDatabase();
1321       //        if (!itroot) itroot = data->GetDicomNode()->GetRoot();
1322       wxMenu* insertmenu = new wxMenu;
1323       bool hasone = false;
1324       i = 0;
1325       Tree::RootListType::iterator j;
1326       for (j  = mTree->GetDatabaseList().begin();
1327       j != mTree->GetDatabaseList().end();
1328       j++)
1329       {
1330       //            std::cout << (*j)->GetName() << " " 
1331       //                      <<  (*j)->GetTypeName()
1332       //                      << " i="<<(*j)->SupportsInsert()<<std::endl;
1333       if ( ((*j)!=itroot) && ((*j)->SupportsInsert()) ) 
1334       {
1335       insertmenu->Append(PopUp_Insert+i, 
1336       std2wx((*j)->GetName()));
1337       hasone = true;
1338       }
1339       i++;
1340       }
1341             
1342       if (hasone) menu.AppendSubMenu(insertmenu, _T("&Insert into"));
1343       }
1344       if (data->IsDatabase())
1345       {
1346       menu.Append(PopUp_Close, wxT("&Close"));
1347       }
1348       if (data->IsDicomNode() && data->GetDicomNode()->GetDicomDatabase()->SupportsRemove())
1349       {
1350       menu.Append(PopUp_Remove, wxT("&Remove"));
1351       }
1352       }
1353     */
1354
1355  
1356
1357  
1358     PopupMenu(&menu, pt);
1359 #endif // wxUSE_MENUS
1360
1361     //    std::cout << "EO ShowMenu" <<std::endl;
1362   }
1363   //=====================================================================
1364
1365   //=====================================================================
1366   // Pop up menu callbacks
1367   void  WxGimmick::OnPopUpAbout(wxCommandEvent& event)
1368   {
1369     wxMessageBox( _T("Give me my medical images quick ! \n\n  (c) CREATIS-LRMN 2008\n"),
1370                   _T("Gimmick!"),
1371                   wxOK | wxICON_INFORMATION, this);
1372   }
1373   //=====================================================================
1374
1375   //=====================================================================
1376   void  WxGimmick::OnPopUpSettings(wxCommandEvent& event)
1377   {
1378     WxGimmickSettingsDialog* s = 
1379       new WxGimmickSettingsDialog(this);
1380     s->ShowModal();
1381     delete s;
1382   }
1383   //=====================================================================
1384
1385   //=====================================================================
1386   void  WxGimmick::OnPopUpNewCollection(wxCommandEvent& event)
1387   {
1388     wxBusyCursor busy;
1389     OpenOrNewDatabase(false);
1390   }
1391   //=====================================================================
1392   
1393
1394  
1395   //=====================================================================
1396   void  WxGimmick::OnPopUpOpenCollection(wxCommandEvent& event)
1397   {
1398     wxBusyCursor busy;
1399     OpenOrNewDatabase(true);
1400   }
1401   //=====================================================================
1402
1403
1404   //=====================================================================
1405   void  WxGimmick::OnPopUpCloseCollection(wxCommandEvent& event)
1406   {
1407     if (wxMessageBox(_T("This will remove this collection from your list of collections but will not delete the collection's file on disk. Proceed ?"),_T("Confirm"),wxYES_NO,this)==wxNO) return;
1408    
1409     wxBusyCursor busy;
1410     //   std::cout << "OnPopUpClose"<<std::endl;
1411     //  wxTreeItemId id = event.GetId();
1412     TreeItemData *data = (TreeItemData *)mTreeListCtrl->GetItemData(mItemOfMenu);
1413     DicomDatabase* r = data->GetDicomNode()->GetDicomDatabase();
1414     //   std::cout << "OnPopUpClose '"<<r->GetName()<<"'"<<std::endl;
1415     DeleteDicomDatabase(mItemOfMenu,r);
1416   }
1417   //=====================================================================
1418
1419   //=====================================================================
1420   void  WxGimmick::OnPopUpDeleteCollection(wxCommandEvent& event)
1421   {
1422     if (wxMessageBox(_T("This will physically delete the collection's file on disk and cannot be reverted. Proceed ?"),_T("Confirm"),wxYES_NO,this)==wxNO) return;
1423     
1424     wxBusyCursor busy;
1425
1426    
1427     //   std::cout << "OnPopUpClose"<<std::endl;
1428     //  wxTreeItemId id = event.GetId();
1429     TreeItemData *data = (TreeItemData *)mTreeListCtrl->GetItemData(mItemOfMenu);
1430     DicomDatabase* r = data->GetDicomNode()->GetDicomDatabase();
1431
1432     wxRemoveFile(std2wx(r->GetFileName()));
1433     //   std::cout << "OnPopUpClose '"<<r->GetName()<<"'"<<std::endl;
1434     DeleteDicomDatabase(mItemOfMenu,r);
1435     
1436   }
1437   //=====================================================================
1438   
1439   void DisplayUpdateSummary( DicomDatabase::UpdateSummary& summary,
1440                              wxWindow* parent )
1441   {
1442     std::stringstream mess;
1443     mess << "Dirs\tscanned\t\t\t: " << summary.scanned_dirs << "\n";
1444     mess << "Files\tscanned\t\t\t: " << summary.scanned_files << "\n";
1445     mess << "Files\thandled\t\t\t: " << summary.handled_images << "\n\n";
1446     mess << "Patients\tadded\t\t: " << summary.added_patients<< "\n";
1447     mess << "Studies\tadded\t\t: " << summary.added_studies<< "\n";
1448     mess << "Series\tadded\t\t: " << summary.added_series<< "\n";
1449     mess << "Images\tadded\t\t: " << summary.added_images<< "\n\n";
1450     char times[500];
1451     sprintf(times,"Time to parse dir \t\t: %ld ms \t%d°/o\nTime to read files info \t: %ld ms \t%d°/o\nTime to update structs \t: %ld ms \t%d°/o\nTime to update database \t: %ld ms \t%d°/o\nTotal time \t\t\t: %ld ms",
1452             summary.parse_time,
1453             (int)( summary.parse_time*100./summary.total_time),
1454             summary.file_scan_time,
1455             (int)(summary.file_scan_time*100./summary.total_time),
1456             summary.update_structs_time,
1457             (int)(summary.update_structs_time*100./summary.total_time),
1458             summary.update_database_time,
1459             (int)(summary.update_database_time*100./summary.total_time),
1460             summary.total_time );
1461     
1462     mess << times;
1463     
1464     wxMessageBox(std2wx(mess.str()),_T("Update summary"),wxOK,parent);
1465   }
1466   
1467   
1468   //=====================================================================
1469   void  WxGimmick::OnPopUpAddFile(wxCommandEvent& event)
1470   {
1471     long style = wxOPEN | wxFILE_MUST_EXIST | wxFD_MULTIPLE;
1472     std::string wc("*.*");
1473     wxFileDialog* FD = new wxFileDialog( 0, 
1474                                          _T("Select file"),
1475                                          mCurrentDirectory,
1476                                          _T(""),
1477                                          std2wx(wc),
1478                                          style,
1479                                          wxDefaultPosition);
1480     
1481     if (FD->ShowModal()==wxID_OK)
1482       {
1483         wxBusyCursor busy;
1484
1485         mCurrentDirectory = FD->GetDirectory();
1486         wxArrayString files;
1487         FD->GetPaths(files);
1488         unsigned int i;
1489         std::vector<std::string> filenames;
1490         for (i=0;i<files.GetCount();++i)
1491           filenames.push_back(wx2std(files[i]));
1492
1493        
1494         TreeItemData *data = 
1495           (TreeItemData *)
1496           mTreeListCtrl->GetItemData(mItemOfMenu);
1497         DicomDatabase* db = data->GetDicomNode()->GetDicomDatabase();
1498         DicomDatabase::UpdateSummary summary;
1499         wxProgressDialog* progress = 
1500           new wxProgressDialog(_T("Adding file(s)"),
1501                                _T(""),
1502                                1000,
1503                                this,
1504                                wxPD_ELAPSED_TIME |
1505                                wxPD_ESTIMATED_TIME | 
1506                                wxPD_REMAINING_TIME |
1507                                wxPD_CAN_ABORT );
1508        
1509         db->AddFiles(filenames,progress,summary);
1510         
1511         progress->Pulse(_T("Updating view..."));
1512         UpdateDicomDatabaseView(db);
1513         delete progress;
1514         DisplayUpdateSummary(summary,this);
1515       }
1516   
1517   }
1518   //=====================================================================
1519
1520   //=====================================================================
1521   void  WxGimmick::OnPopUpAddRawFile(wxCommandEvent& event)
1522   {
1523     wxMessageBox(_T("Not yet implemented !"),_T("Sorry"),wxOK,this);
1524   }
1525   //=====================================================================
1526
1527   //=====================================================================
1528   void  WxGimmick::OnPopUpAddDirectory(wxCommandEvent& event)
1529   {
1530     long style = wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST;
1531     wxDirDialog* FD = 
1532       new wxDirDialog( 0, 
1533                        _T("Select directory"),
1534                        mCurrentDirectory,
1535                        style);
1536    
1537     if (FD->ShowModal()==wxID_OK)
1538       {
1539
1540         bool recurse = false;
1541         if (wxMessageBox(_T("Recurse into sub-directories ?"),
1542                          _T("Scan directory"),
1543                          wxYES_NO,this ) == wxYES)
1544           {
1545             recurse = true;
1546           }
1547        
1548         wxBusyCursor busy;
1549         wxProgressDialog* progress = 
1550           new wxProgressDialog(_T("Scanning directory"),
1551                                _T("Parsing directory"),
1552                                1000,
1553                                this,
1554                                wxPD_ELAPSED_TIME |
1555                                wxPD_ESTIMATED_TIME | 
1556                                wxPD_REMAINING_TIME |
1557                                wxPD_CAN_ABORT );
1558         DicomDatabase::UpdateSummary summary;
1559
1560         std::string dirname = wx2std (FD->GetPath()) ;
1561         mCurrentDirectory = FD->GetPath();
1562         TreeItemData *data = 
1563           (TreeItemData *)
1564           mTreeListCtrl->GetItemData(mItemOfMenu);
1565         DicomDatabase* db = data->GetDicomNode()->GetDicomDatabase();
1566         db->AddDirectory(dirname,recurse,progress,summary);
1567
1568         progress->Pulse(_T("Updating view..."));
1569         UpdateDicomDatabaseView(db);
1570
1571         delete progress;    
1572         DisplayUpdateSummary(summary,this);
1573         /*    
1574               if (summary.cancelled_by_user)
1575               {
1576               std::cout << "!! Cancelled by user !!"<<std::endl;
1577               }
1578         */
1579
1580       }
1581   }
1582   //=====================================================================
1583
1584   //=====================================================================
1585   void  WxGimmick::OnPopUpRemove(wxCommandEvent& event)
1586   {
1587
1588     /*
1589       wxMessageBox(_T("Not yet implemented"),_T("Sorry !"),wxOK);
1590       return;
1591     */
1592
1593     //  wxTreeItemId id = event.GetId();
1594     TreeItemData *data = (TreeItemData *)mTreeListCtrl->GetItemData(mItemOfMenu);
1595
1596     std::string mess("Remove ");
1597     mess += data->GetDicomNode()->GetTypeName();
1598     mess += " from collection ?";
1599     int answer = wxMessageBox(std2wx(mess), _T("Confirm"), wxYES_NO);
1600     if (answer == wxNO) return;
1601    
1602     if ( mTreeListCtrl->IsSelected(mItemOfMenu) )
1603       {
1604         wxTreeItemId next = mTreeListCtrl->GetNextSibling(mItemOfMenu);
1605         if (next.IsOk()) 
1606           {
1607             mTreeListCtrl->SelectItem(next);
1608           }
1609         else 
1610           {
1611             return;
1612           }
1613       }
1614
1615     DicomDatabase* db = data->GetDicomNode()->GetDicomDatabase();
1616     db->Remove(data->GetDicomNode());
1617     //   std::cout << "OnPopUpClose '"<<r->GetName()<<"'"<<std::endl;
1618     // TODO : Optimize update only parent's branch
1619     UpdateDicomDatabaseView(db);
1620     //   DeleteDicomDatabase(mItemOfMenu,r);
1621   }
1622   //=====================================================================
1623
1624   //=====================================================================
1625   void  WxGimmick::OnPopUpSort(wxCommandEvent& event)
1626   {
1627     wxBusyCursor busy;
1628     //    std::cout << "OnPopUpSort"<<std::endl;
1629     TreeItemData *data = (TreeItemData *)mTreeListCtrl->GetItemData(mItemOfMenu);
1630     int index = event.GetId() - PopUp_Sort;
1631     DicomNode* node = data->GetDicomNode();
1632     DicomNode::Type ctype = node->GetType()+1;
1633     mSettings.SetActiveComparatorIndex(ctype,index);
1634
1635     if (node->ChildrenLoaded())
1636       {
1637         // Remove children
1638         mTreeListCtrl->DeleteChildren(mItemOfMenu);
1639
1640         /*      std::cout << "Sorting using '"
1641           << mSettings.GetActiveComparator(ctype).GetName() 
1642           << "' ... ";
1643         */
1644         node->SortChildren ( mSettings.GetActiveComparator(ctype) );
1645         //      std::cout << "ok"<<std::endl;
1646
1647         
1648         // Update tree
1649         CreateChildrenColumnsTitles(mItemOfMenu,ctype);
1650         DicomNode::ChildrenListType::iterator i;
1651         for (i=node->GetChildrenList().begin();
1652              i!=node->GetChildrenList().end();
1653              i++)
1654           {
1655             UpdateDicomNodeView(*i,mItemOfMenu);
1656           }
1657       }
1658   }
1659   //=====================================================================
1660
1661
1662   //=====================================================================
1663   void  WxGimmick::OnPopUpUser(wxCommandEvent& event)
1664   {
1665     //    std::cout << "OnPopUpUser"<<std::endl;
1666     event.Skip();
1667   }
1668   //=====================================================================
1669
1670   //=================================================
1671   void WxGimmick::CreateImageList(int size)
1672   {
1673     if ( size == -1 )
1674       {
1675         mTreeListCtrl->SetImageList(NULL);
1676         return;
1677       }
1678     if ( size == 0 )
1679       size = m_imageSize;
1680     else
1681       m_imageSize = size;
1682     
1683     wxIcon icons[20];
1684     // should correspond to Icon_xxx enum
1685     icons[Icon_Patient] = wxIcon(patient_xpm);
1686     icons[Icon_Study] = wxIcon(study_xpm);
1687     icons[Icon_Series] = wxIcon(series_xpm);
1688     icons[Icon_Image] = wxIcon(image_xpm);
1689     icons[Icon_Database] = wxIcon(database_xpm);
1690     icons[Icon_Folder] = wxIcon(folder_xpm);
1691     icons[Icon_DicomDir] = wxIcon(dicomdir_xpm);
1692     icons[Icon_Root] = wxIcon(root_xpm);
1693
1694
1695     //    mFirstRootIconIndex = 8;
1696     //    int i=0;
1697     /*
1698       Tree::RootHandlerListType::iterator h;
1699
1700       for (h= Tree::GetDatabaseHandlerList().begin();
1701       h!=Tree::GetDatabaseHandlerList().end();
1702       h++)
1703       {
1704       icons[mFirstRootIconIndex+i] = (*h)->GetIcon();
1705       i++;
1706       }
1707     */
1708     unsigned int NbIcons = 8;//mFirstRootIconIndex + i;
1709     // Make an image list containing small icons
1710     wxImageList *images = new wxImageList(size, size, true);
1711     
1712     int sizeOrig = icons[0].GetWidth();
1713     for ( size_t i = 0; i < NbIcons; i++ )
1714       {
1715         if ( size == sizeOrig )
1716           {
1717             images->Add(icons[i]);
1718           }
1719         else
1720           {
1721             images->Add(wxBitmap(wxBitmap(icons[i]).ConvertToImage().Rescale(size, size)));
1722           }
1723       }
1724     mTreeListCtrl->AssignImageList(images);
1725   }
1726   //=================================================
1727
1728   //================================================================
1729   void WxGimmick::OnSelChanging(wxTreeEvent& event)                   
1730   {
1731     wxTreeItemId id = event.GetItem();
1732     if (!id.IsOk()) 
1733       {
1734         std::cout << "ERROR : ID NOT OK"<<std::endl;
1735         return;
1736       }
1737
1738     TreeItemData *data = (TreeItemData *)mTreeListCtrl->GetItemData(id);
1739
1740     event.Veto();
1741     if (data->IsDicomNode())
1742       {
1743         if ((data->GetDicomNode()>0)&&
1744             ( data->GetDicomNode()->GetType()==DicomNode::Image))
1745           {
1746             event.Allow();
1747           }
1748       }
1749   }
1750   //================================================================
1751
1752   //================================================================
1753   void WxGimmick::OnSelChanged(wxTreeEvent& event)                   
1754   {
1755     //    wxBusyCursor busy;
1756     //    std::vector<wxTreeItemId> items;
1757     //    GetSelectedItems(items);
1758     /*
1759       std::vector<DicomNode*>::iterator i;
1760       for (i=nodes.begin();i!=nodes.end();++i)
1761       {
1762       std::cout << "'" << (*i)->GetFieldValue("FullFileName") 
1763       << "'" << std::endl;
1764       }
1765       std::cout << "++++++++++++++++++++" << std::endl;
1766     */
1767     //    ShowImage(mReader.GetImage(""));
1768
1769      bool no_image = true;
1770
1771     static int max = 1000;
1772
1773     
1774     //    if (items.size()>0) 
1775     //      {
1776
1777         // Update image preview : send requests to the MTImageReader
1778     //  bool first = true;
1779     //  std::vector<wxTreeItemId>::iterator i;
1780     //  for (i=items.begin();i!=items.end();++i)
1781     //    {
1782
1783             /*
1784             if (first)
1785               {
1786                 DicomNode* node = GetDicomNodeOfItem(items[0]);
1787                 if (!node) return;
1788                 // Update dicom fields panel
1789                 mFieldsView->UpdateValues(node);
1790               }
1791             */
1792
1793     wxTreeItemId item =  mTreeListCtrl->GetCurrent();
1794     
1795     DicomNode* n = GetDicomNodeOfItem(item);
1796
1797     if (n) mFieldsView->UpdateValues(n);    
1798
1799     if ( (n!=0) &&
1800          (n->GetType()==DicomNode::Image) )
1801       {
1802
1803         //                
1804         no_image = false;
1805         //if (i==items.begin()) 
1806         mCurImageItemToShow = item;
1807         
1808         int maxprio = mReader.GetMaximalPriority();
1809         int prio = maxprio + 1000;
1810         wxTreeItemId sib = item; //GetTreeListCtrl()->GetNextSibling(*i);
1811         while (sib.IsOk())
1812           {
1813             DicomNode* nsib = GetDicomNodeOfItem(sib);
1814             if (nsib>0) 
1815               {
1816                 //                      std::cout << "-- Request '"
1817                 //                                << nsib->GetFieldValue("FullFileName")
1818                 //                                << "' prio="<<prio<<std::endl;
1819                 mReader.Request(this,
1820                                 nsib->GetFieldValue("FullFileName"), 
1821                                 prio);
1822                 mImageFileNameToNode[nsib->GetFieldValue("FullFileName")] =
1823                   nsib;
1824                 prio--;
1825               }
1826             sib = GetTreeListCtrl()->GetNextSibling(sib);
1827           }
1828         prio = maxprio + 1000;
1829         sib = GetTreeListCtrl()->GetPrevSibling(item);
1830         while (sib.IsOk())
1831           {
1832             DicomNode* nsib = GetDicomNodeOfItem(sib);
1833             if (nsib>0) 
1834               {
1835                 //                      std::cout << "-- Request '"
1836                 //                                << nsib->GetFieldValue("FullFileName")
1837                 //                                << "' prio="<<prio<<std::endl;
1838                 mReader.Request(this,
1839                                 nsib->GetFieldValue("FullFileName"), 
1840                                 prio);
1841                 mImageFileNameToNode[nsib->GetFieldValue("FullFileName")] =
1842                   nsib;
1843                 prio--;
1844               }
1845             sib = GetTreeListCtrl()->GetPrevSibling(sib);
1846           }
1847         //              mImageFileNameToNode[n->GetFieldValue("FullFileName")] = n;
1848         max += 1000;
1849         
1850         ProcessImageEvents();
1851       }
1852     //  std::cout << "* Selection changed * (im)"<<std::endl;
1853
1854     //---------------------------------------------------------------------
1855     // Send event
1856     WxGimmickEvent ev(wxEVT_COMMAND_TREEVIEWLIST_SEL_CHANGED,
1857                                     this,
1858                                     event.GetItem());
1859     
1860     GetEventHandler()->ProcessEvent(ev);
1861
1862     if (no_image) ShowImage(mReader.GetImage(""));
1863     
1864   }
1865   //================================================================
1866
1867   //================================================================
1868   void WxGimmick::ShowImage(vtkImageData* im)
1869   {
1870     //  wxBusyCursor busy;
1871     
1872     int x1,x2,y1,y2,z1,z2;
1873     double spx,spy,spz;
1874     im->GetSpacing(spx,spy,spz);
1875     im->GetExtent (x1,x2,y1,y2,z1,z2);
1876     
1877     /*
1878       std::cout << x1 << "-"<<x2<<std::endl; 
1879       std::cout << y1 << "-"<<y2<<std::endl; 
1880       std::cout << z1 << "-"<<z2<<std::endl; 
1881       std::cout << spx << "-"<<spy<<"-"<<spz<<std::endl; 
1882     */
1883     
1884     if ((x1!=mx1) ||
1885         (x2!=mx2) ||
1886         (y1!=my1) ||
1887         (y2!=my2) ||
1888         (z1!=mz1) ||
1889         (z2!=mz2) ||
1890         (spx!=mspx) ||
1891         (spy!=mspy) ||
1892         (spz!=mspz) 
1893         )
1894       {
1895         mx1 = x1;
1896         mx2 = x2;
1897         my1 = y1;
1898         my2 = y2;
1899         mz1 = z1;
1900         mz2 = z2;
1901         mspx = spx;
1902         mspy = spy;
1903         mspz = spz;
1904         
1905         vtkCamera *camera = mViewer->GetRenderer()->GetActiveCamera();
1906         
1907         camera->SetViewUp ( spx*0, -spy*1, spz*0);
1908         camera->SetPosition( spx*(x1+x2)/2, spy*(y1+y2)/2, -spz*10000000); 
1909         camera->SetFocalPoint   ( spx*(x1+x2)/2 , spy*(y1+y2)/2 , spz*0); 
1910         
1911         camera->ComputeViewPlaneNormal();
1912         camera->SetParallelScale(  spx*(x2-x1)/2.0 );
1913         
1914       }
1915     
1916     mViewer->SetInput( im );
1917     mViewer->SetSlice( 0 );
1918     mInteractor->Render();
1919   } 
1920   //================================================================
1921   
1922
1923
1924
1925
1926
1927   //================================================================
1928   void WxGimmick::
1929   OnMultiThreadImageReaderEvent(const std::string& filename,
1930                                 MultiThreadImageReaderUser::EventType e,
1931                                 vtkImageData* image)
1932   {
1933     if (filename.size()==0)
1934       {
1935         mImageEventQueue.push_back(ImageEventType(image));
1936         return;
1937       }
1938     std::map<std::string,DicomNode*>::iterator i;
1939     i = mImageFileNameToNode.find(filename);
1940     if (i!=mImageFileNameToNode.end())
1941       {
1942         wxTreeItemId id = i->second->GetData<NodeData*>()->GetTreeItemData()->GetItemId();
1943         mImageEventQueue.push_back(ImageEventType(id,image));
1944       }
1945   }
1946   //================================================================
1947
1948   //================================================================
1949   // Processes the queue of image events 
1950   void WxGimmick::ProcessImageEvents()
1951   {
1952     //    std::cout << "++++++++++ ProcessImageEvents " << std::endl;
1953     MultiThreadImageReaderEventLock();
1954
1955
1956     while (!mImageEventQueue.empty())
1957       {
1958         ImageEventType e = mImageEventQueue.front();
1959         mImageEventQueue.pop_front();
1960         if( e.image!=0 ) 
1961           {
1962             if (e.item.IsOk()) 
1963               {
1964                 mTreeListCtrl->SetItemTextColour(e.item,
1965                                                  mSettings.LoadedImageColour());//wxImageLoadedColour);
1966                 TreeItemData *data = 
1967                   (TreeItemData *)mTreeListCtrl->GetItemData(e.item);
1968                 data->SetLoaded(true);
1969
1970                 if (mCurImageItemToShow == e.item)
1971                   {
1972                     ShowImage(e.image);
1973                   }
1974               }
1975             else if (!mCurImageItemToShow.IsOk())
1976               {
1977                 ShowImage(e.image);
1978               }
1979           }
1980         else if (e.item.IsOk())
1981           {
1982             mTreeListCtrl->SetItemTextColour(e.item,mSettings.Colour(DicomNode::Image)); //.wxImageUnloadedColour);
1983             TreeItemData *data = 
1984               (TreeItemData *)mTreeListCtrl->GetItemData(e.item);
1985             data->SetLoaded(false);
1986           }
1987       }
1988     mImageEventQueue.clear();
1989     MultiThreadImageReaderEventUnlock();
1990     //    std::cout << "++++++++++ END ProcessImageEvents " << std::endl;
1991   }
1992   //================================================================
1993
1994   //================================================================
1995   void  WxGimmick::OnInternalIdle()
1996   {
1997     ProcessImageEvents();
1998   }
1999   //================================================================
2000  
2001
2002
2003   //================================================================
2004   void WxGimmick::GetSelectedImages(std::vector<std::string>& f)
2005   {
2006     wxArrayTreeItemIds id;
2007     // TO DO : TEST THAT STYLE IS MULTIPLE 
2008     unsigned int nb = mTreeListCtrl->GetSelections(id);
2009     f.clear();
2010     for (unsigned int i=0; i<nb; ++i)
2011       {
2012         TreeItemData *data = 
2013           (TreeItemData *)mTreeListCtrl->GetItemData(id[i]);
2014         if ((data) && (data->IsDicomNode()))
2015           {
2016             if (data->GetDicomNode()->GetType()==DicomNode::Image)
2017               {
2018                 f.push_back ( data->GetDicomNode()->GetFieldValue("FullFileName") );
2019               }
2020             else if (data->GetDicomNode()->GetType()==DicomNode::Series)
2021               {
2022                 DicomNode::ChildrenListType::iterator j;
2023                 for (j =data->GetDicomNode()->GetChildrenList().begin();
2024                      j!=data->GetDicomNode()->GetChildrenList().end();
2025                      j++) 
2026                   {
2027                     f.push_back((*j)->GetFieldValue("FullFileName"));
2028                   }
2029               }
2030           }
2031       }
2032   }
2033   //================================================================
2034
2035
2036   //================================================================
2037   void WxGimmick::GetSelectedDicomNodes(std::vector<DicomNode*>& f)
2038   {
2039     wxArrayTreeItemIds id;
2040     // TO DO : TEST THAT STYLE IS MULTIPLE 
2041     unsigned int nb = mTreeListCtrl->GetSelections(id);
2042     f.clear();
2043     for (unsigned int i=0; i<nb; ++i)
2044       {
2045         TreeItemData *data = 
2046           (TreeItemData *)mTreeListCtrl->GetItemData(id[i]);
2047         if ((data) && (data->IsDicomNode()))
2048           {
2049             f.push_back ( data->GetDicomNode() );
2050           }
2051         /*
2052
2053         if (data->GetDicomNode()->GetType()==DicomNode::Image)
2054         {
2055         f.push_back ( data->GetDicomNode() ); //->GetFieldValue("FullFileName") );
2056         }  
2057         else if (data->GetDicomNode()->GetType()==DicomNode::Series)
2058         {
2059         DicomNode::ChildrenListType::iterator j;
2060         for (j =data->GetDicomNode()->GetChildrenList().begin();
2061         j!=data->GetDicomNode()->GetChildrenList().end();
2062         j++) 
2063         {
2064         f.push_back((*j)); //->GetFieldValue("FullFileName"));
2065         }
2066         }
2067         }
2068         */
2069       }
2070   }
2071   //================================================================
2072
2073   //================================================================
2074   void WxGimmick::GetSelectedItems(std::vector<wxTreeItemId>& f)
2075   {
2076     wxArrayTreeItemIds id;
2077     // TO DO : TEST THAT STYLE IS MULTIPLE 
2078     unsigned int nb = mTreeListCtrl->GetSelections(id);
2079     f.clear();
2080     for (unsigned int i=0; i<nb; ++i)
2081       {
2082         f.push_back(id[i]);
2083       }
2084   }
2085   //================================================================
2086
2087   //================================================================
2088   DicomNode* WxGimmick::GetDicomNodeOfItem(const wxTreeItemId& i)
2089   {
2090     TreeItemData *data = 
2091       (TreeItemData *)mTreeListCtrl->GetItemData(i);
2092     if (data) return ( data->GetDicomNode() );
2093     return 0;
2094   }
2095   //================================================================
2096
2097   //================================================================
2098   //================================================================
2099   //================================================================
2100   //================================================================
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116   //================================================================
2117   //================================================================
2118   //================================================================
2119   //================================================================
2120   
2121   BEGIN_EVENT_TABLE(WxGimmick, wxPanel)
2122   // POP UP MENU
2123     EVT_MENU(PopUp_NewCollection,WxGimmick::OnPopUpNewCollection)
2124     EVT_MENU(PopUp_OpenCollection,WxGimmick::OnPopUpOpenCollection)
2125     EVT_MENU(PopUp_CloseCollection,WxGimmick::OnPopUpCloseCollection)
2126     EVT_MENU(PopUp_DeleteCollection,WxGimmick::OnPopUpDeleteCollection)
2127     EVT_MENU(PopUp_AddFile, WxGimmick::OnPopUpAddFile)
2128     EVT_MENU(PopUp_AddRawFile, WxGimmick::OnPopUpAddRawFile)
2129     EVT_MENU(PopUp_AddDirectory, WxGimmick::OnPopUpAddDirectory)
2130     EVT_MENU(PopUp_Remove, WxGimmick::OnPopUpRemove)
2131     EVT_MENU(PopUp_About, WxGimmick::OnPopUpAbout)
2132     EVT_MENU(PopUp_Settings, WxGimmick::OnPopUpSettings)
2133     
2134     EVT_MENU_RANGE(PopUp_Sort, PopUp_Sort+99,  WxGimmick::OnPopUpSort)
2135     EVT_MENU_RANGE(PopUp_User, PopUp_User+99,  WxGimmick::OnPopUpUser)
2136     
2137     
2138     
2139   // DRAG
2140     EVT_TREE_BEGIN_DRAG(TreeListCtrlId, WxGimmick::OnBeginDrag)
2141     EVT_TREE_BEGIN_RDRAG(TreeListCtrlId, WxGimmick::OnBeginRDrag)
2142     EVT_TREE_END_DRAG(TreeListCtrlId, WxGimmick::OnEndDrag)
2143
2144   // LABEL
2145     EVT_TREE_BEGIN_LABEL_EDIT(TreeListCtrlId, WxGimmick::OnBeginLabelEdit)
2146     EVT_TREE_END_LABEL_EDIT(TreeListCtrlId, WxGimmick::OnEndLabelEdit)
2147
2148   //DELETE : UNUSED
2149     EVT_TREE_DELETE_ITEM(TreeListCtrlId, WxGimmick::OnDeleteItem)
2150 #if 0       // there are so many of those that logging them causes flicker
2151     EVT_TREE_GET_INFO(TreeListCtrlId, WxGimmick::OnGetInfo)
2152 #endif
2153   // UNUSED
2154     EVT_TREE_SET_INFO(TreeListCtrlId, WxGimmick::OnSetInfo)
2155
2156   // EXPAND/COLLAPSE
2157     EVT_TREE_ITEM_EXPANDED(TreeListCtrlId, WxGimmick::OnItemExpanded)
2158     EVT_TREE_ITEM_EXPANDING(TreeListCtrlId, WxGimmick::OnItemExpanding)
2159     EVT_TREE_ITEM_COLLAPSED(TreeListCtrlId, WxGimmick::OnItemCollapsed)
2160     EVT_TREE_ITEM_COLLAPSING(TreeListCtrlId, WxGimmick::OnItemCollapsing)
2161
2162   // SELECTION
2163     EVT_TREE_SEL_CHANGED(TreeListCtrlId, WxGimmick::OnSelChanged)
2164     EVT_TREE_SEL_CHANGING(TreeListCtrlId, WxGimmick::OnSelChanging)
2165   // KEY
2166     EVT_TREE_KEY_DOWN(TreeListCtrlId, WxGimmick::OnTreeKeyDown)
2167   // ACTIVATION = DOUBLE CLICK OR ENTER ON SELECTED
2168     EVT_TREE_ITEM_ACTIVATED(TreeListCtrlId, WxGimmick::OnItemActivated)
2169
2170   // so many differents ways to handle right mouse button clicks...
2171   //    EVT_CONTEXT_MENU(WxGimmick::OnContextMenu)
2172   // EVT_TREE_ITEM_MENU is the preferred event for creating context menus
2173   // on a tree control, because it includes the point of the click or item,
2174   // meaning that no additional placement calculations are required.
2175   //    EVT_TREE_ITEM_MENU(TreeListCtrlId, WxGimmick::OnItemMenu)
2176     
2177     EVT_TREE_ITEM_RIGHT_CLICK(TreeListCtrlId, WxGimmick::OnItemRightClick)
2178
2179   // UNUSED
2180   //    EVT_RIGHT_DOWN(WxGimmick::OnRMouseDown)
2181   //    EVT_RIGHT_UP(WxGimmick::OnRMouseUp)
2182   //    EVT_RIGHT_DCLICK(WxGimmick::OnRMouseDClick)
2183     END_EVENT_TABLE()
2184
2185   //IMPLEMENT_DYNAMIC_CLASS(WxGimmick, wxTreeListCtrl)
2186
2187   /*
2188     wxTree::wxTree(wxWindow *parent, const wxWindowID id,
2189     const wxPoint& pos, const wxSize& size,
2190     long style)
2191     : wxTreeListCtrl(parent, id, pos, size, style)
2192     {
2193     m_reverseSort = false;
2194
2195     CreateImageList();
2196
2197     // Add some items to the tree
2198     AddTestItemsToTree(5, 2);
2199     }
2200   */
2201
2202
2203
2204 #if USE_GENERIC_TREECTRL || !defined(__WXMSW__)
2205     void WxGimmick::CreateButtonsImageList(int size)
2206   {
2207     /*
2208       if ( size == -1 )
2209       {
2210       mTreeListCtrl->SetButtonsImageList(NULL);
2211       return;
2212       }
2213
2214       // Make an image list containing small icons
2215       wxImageList *images = new wxImageList(size, size, true);
2216
2217       // should correspond to TreeListCtrlIcon_xxx enum
2218       wxBusyCursor wait;
2219       wxIcon icons[4];
2220       icons[0] = wxIcon(icon3_xpm);   // closed
2221       icons[1] = wxIcon(icon3_xpm);   // closed, selected
2222       icons[2] = wxIcon(icon5_xpm);   // open
2223       icons[3] = wxIcon(icon5_xpm);   // open, selected
2224
2225       for ( size_t i = 0; i < WXSIZEOF(icons); i++ )
2226       {
2227       int sizeOrig = icons[i].GetWidth();
2228       if ( size == sizeOrig )
2229       {
2230       images->Add(icons[i]);
2231       }
2232       else
2233       {
2234       images->Add(wxBitmap(wxBitmap(icons[i]).ConvertToImage().Rescale(size, size)));
2235       }
2236       }
2237
2238       mTreeListCtrl->AssignButtonsImageList(images);
2239     */
2240 #else
2241     void WxGimmick::CreateButtonsImageList(int WXUNUSED(size))
2242     {
2243 #endif
2244     }
2245
2246     /*
2247       int WxGimmick::OnCompareItems(const wxTreeItemId& item1,
2248       const wxTreeItemId& item2)
2249       {
2250       if ( m_reverseSort )
2251       {
2252       // just exchange 1st and 2nd items
2253       return mTreeListCtrl->OnCompareItems(item2, item1);
2254       }
2255       else
2256       {
2257       return mTreeListCtrl->OnCompareItems(item1, item2);
2258       }
2259       }
2260
2261
2262       void WxGimmick::DoToggleIcon(const wxTreeItemId& item)
2263       {
2264   
2265       int image = (mTreeListCtrl->GetItemImage(item) == TreeIcon_Folder)
2266       ? TreeIcon_File
2267       : TreeIcon_Folder;
2268       mTreeListCtrl->SetItemImage(item, image, wxTreeItemIcon_Normal);
2269
2270       image = (mTreeListCtrl->GetItemImage(item) == TreeIcon_FolderSelected)
2271       ? TreeIcon_FileSelected
2272       : TreeIcon_FolderSelected;
2273       mTreeListCtrl->SetItemImage(item, image, wxTreeItemIcon_Selected);
2274       }
2275
2276       void WxGimmick::LogEvent(const wxChar *name, const wxTreeEvent& event)
2277       {
2278       wxTreeItemId item = event.GetItem();
2279       wxString text;
2280       if ( item.IsOk() )
2281       text << _T('"') << mTreeListCtrl->GetItemText(item).c_str() << _T('"');
2282       else
2283       text = _T("invalid item");
2284       //    wxLogMessage(wxT("%s(%s)"), name, text.c_str());
2285       }
2286
2287     */
2288     // avoid repetition
2289 #define TREE_EVENT_HANDLER(name)                                \
2290     void WxGimmick::name(wxTreeEvent& event)    \
2291     {                                                           \
2292       /*    LogEvent(_T(#name), event); */                      \
2293       /*    SetLastItem(mTreeListCtrl->wxTreeItemId()) *;*/     \
2294       event.Skip();                                             \
2295     }
2296
2297     TREE_EVENT_HANDLER(OnBeginRDrag)
2298       TREE_EVENT_HANDLER(OnDeleteItem)
2299       TREE_EVENT_HANDLER(OnGetInfo)
2300       TREE_EVENT_HANDLER(OnSetInfo)
2301       //TREE_EVENT_HANDLER(OnItemExpanded)
2302       TREE_EVENT_HANDLER(OnItemExpanding)
2303       //TREE_EVENT_HANDLER(OnItemCollapsed)
2304       //TREE_EVENT_HANDLER(OnSelChanged)
2305       //      TREE_EVENT_HANDLER(OnSelChanging)
2306
2307
2308       void WxGimmick::OnItemCollapsed(wxTreeEvent& event)                   
2309       {
2310         //    std::cout << "* Collapsed *"<<std::endl;
2311       }
2312
2313 #undef TREE_EVENT_HANDLER
2314
2315     void WxGimmick::OnTreeKeyDown(wxTreeEvent& event)
2316     {
2317       /*
2318       //  LogKeyEvent(wxT("Tree key down "), event.GetKeyEvent());
2319       std::cout << "* Key down *"<<std::endl;
2320       if (event.GetKeyCode()==WXK_RIGHT)
2321         {
2322           std::cout << "Right"<<std::endl;
2323           wxTreeItemId itemId =  mTreeListCtrl->GetSelection();
2324           if (itemId.IsOk())
2325             {
2326               std::cout << "item is ok"<<std::endl;
2327               wxPoint clientpt = event.GetPoint();
2328               wxPoint screenpt = ClientToScreen(clientpt);
2329               ShowMenu(itemId, clientpt);
2330             }     
2331           event.Veto();
2332           return;
2333         }
2334       std::cout << "NOT Right"<<std::endl;
2335       */
2336       event.Skip();       
2337     }
2338
2339     void WxGimmick::OnBeginDrag(wxTreeEvent& event)
2340     {
2341       wxTreeItemId id = event.GetItem();
2342       TreeItemData *data = (TreeItemData *)mTreeListCtrl->GetItemData(id);
2343       //    std::cout << "OnBeginDrag("<<id<<")"<<std::endl;
2344       if (data->IsDatabase())
2345         {
2346           //      std::cout << "-- IS ROOT"<<std::endl;
2347           //  event.Allow();
2348         }
2349       else if (data->IsDicomNode())
2350         {
2351           //      std::cout << "-- IS NODE"<<std::endl;
2352         }
2353       /*
2354       // need to explicitly allow drag
2355       if ( event.GetItem() != GetDatabaseItem() )
2356       {
2357       m_draggedItem = event.GetItem();
2358
2359       wxPoint clientpt = event.GetPoint();
2360       wxPoint screenpt = ClientToScreen(clientpt);
2361
2362       wxLogMessage(wxT("OnBeginDrag: started dragging %s at screen coords (%i,%i)"),
2363       GetItemText(m_draggedItem).c_str(),
2364       screenpt.x, screenpt.y);
2365
2366       event.Allow();
2367       }
2368       else
2369       {
2370       wxLogMessage(wxT("OnBeginDrag: this item can't be dragged."));
2371       }
2372       */
2373     }
2374
2375     void WxGimmick::OnEndDrag(wxTreeEvent& event)
2376     {
2377       wxTreeItemId id = event.GetItem();
2378       //      std::cout << "OnEndDrag("<<id<<")"<<std::endl;
2379       if (!id.IsOk()) return;
2380       TreeItemData *data = (TreeItemData *)mTreeListCtrl->GetItemData(id);
2381       if (data->IsDatabase())
2382         {
2383           //      std::cout << "-- IS ROOT"<<std::endl;
2384         }
2385       else if (data->IsDicomNode())
2386         {
2387           //      std::cout << "-- IS NODE"<<std::endl;
2388         }
2389
2390       /*
2391         wxTreeItemId itemSrc = m_draggedItem,
2392         itemDst = event.GetItem();
2393         m_draggedItem = (wxTreeItemId)0l;
2394
2395         // where to copy the item?
2396         if ( itemDst.IsOk() && !ItemHasChildren(itemDst) )
2397         {
2398         // copy to the parent then
2399         itemDst = GetItemParent(itemDst);
2400         }
2401
2402         if ( !itemDst.IsOk() )
2403         {
2404         wxLogMessage(wxT("OnEndDrag: can't drop here."));
2405
2406         return;
2407         }
2408
2409         wxString text = GetItemText(itemSrc);
2410         wxLogMessage(wxT("OnEndDrag: '%s' copied to '%s'."),
2411         text.c_str(), GetItemText(itemDst).c_str());
2412
2413         // just do append here - we could also insert it just before/after the item
2414         // on which it was dropped, but this requires slightly more work... we also
2415         // completely ignore the client data and icon of the old item but could
2416         // copy them as well.
2417         //
2418         // Finally, we only copy one item here but we might copy the entire tree if
2419         // we were dragging a folder.
2420         int image = wxGetApp().ShowImages() ? TreeIcon_File : -1;
2421         AppendItem(itemDst, text, image);
2422       */
2423     }
2424
2425
2426     //====================================================================
2427     void WxGimmick::OnBeginLabelEdit(wxTreeEvent& event)
2428     {
2429       //      std::cout << "OnBeginLabelEdit"<<std::endl;
2430       wxTreeItemId id = event.GetItem();
2431       TreeItemData *data = (TreeItemData *)mTreeListCtrl->GetItemData(id);
2432       // If not a root : veto
2433       if (data->IsDatabase()) 
2434         { 
2435           event.Allow();
2436           return;
2437         }
2438       event.Veto();
2439     }
2440     //====================================================================
2441
2442     //====================================================================
2443     void WxGimmick::OnEndLabelEdit(wxTreeEvent& event)
2444     {
2445       //      std::cout << "OnEndLabelEdit"<<std::endl;
2446       wxTreeItemId id = event.GetItem();
2447       TreeItemData *data = GetItemData(id);
2448       // If not a database : bug !
2449       if (data->IsDatabase()) 
2450         { 
2451           data->GetDicomNode()->GetDicomDatabase()->SetName(wx2std(event.GetLabel()));
2452           mFieldsView->UpdateValues(data->GetDicomNode());
2453         }
2454       else
2455         {
2456           std::cerr<< "!!!! Internal error : send bug report !!!!"<<std::endl;
2457         }
2458     }
2459     //====================================================================
2460
2461
2462     void WxGimmick::OnItemCollapsing(wxTreeEvent& event)
2463     {
2464       //    wxLogMessage(wxT("OnItemCollapsing"));
2465
2466       // for testing, prevent the user from collapsing the first child folder
2467       wxTreeItemId itemId = event.GetItem();
2468
2469       /*
2470         if ( IsTestItem(itemId) )
2471         {
2472         wxMessageBox(wxT("You can't collapse this item."));
2473
2474         event.Veto();
2475         }
2476       */
2477     }
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489     //================================================================
2490     //================================================================
2491     //================================================================
2492     //================================================================
2493     //================================================================
2494     // WxGimmickEvent
2495     //================================================================
2496     //================================================================
2497     //================================================================
2498     //================================================================
2499     //================================================================
2500
2501
2502
2503
2504
2505     // ----------------------------------------------------------------------------
2506     // events
2507     // ----------------------------------------------------------------------------
2508     /*
2509       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_BEGIN_DRAG)
2510       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_BEGIN_RDRAG)
2511       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_DELETE_ITEM)
2512       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_GET_INFO)
2513       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_SET_INFO)
2514       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_ITEM_EXPANDED)
2515       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_ITEM_EXPANDING)
2516       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_ITEM_COLLAPSED)
2517       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_ITEM_COLLAPSING)
2518     */
2519
2520     DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_BEGIN_LABEL_EDIT)
2521       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_END_LABEL_EDIT)
2522
2523
2524       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREEVIEWLIST_SEL_CHANGED)
2525       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREEVIEWLIST_SEL_CHANGING)
2526       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREEVIEWLIST_KEY_DOWN)
2527       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREEVIEWLIST_CONTEXTUAL_MENU)
2528       DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREEVIEWLIST_ITEM_STYLE_CHANGED)
2529       /*
2530         DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_ITEM_ACTIVATED)
2531         DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_ITEM_RIGHT_CLICK)
2532         DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_ITEM_MIDDLE_CLICK)
2533         DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_END_DRAG)
2534         DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_STATE_IMAGE_CLICK)
2535         DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_ITEM_GETTOOLTIP)
2536         DEFINE_EVENT_TYPE(wxEVT_COMMAND_TREE_ITEM_MENU)
2537       */
2538       // ----------------------------------------------------------------------------
2539       // Tree event
2540       // ----------------------------------------------------------------------------
2541
2542       IMPLEMENT_ABSTRACT_CLASS(WxGimmickEvent, wxNotifyEvent)
2543   
2544   
2545       WxGimmickEvent::WxGimmickEvent(wxEventType commandType,
2546                                                                  WxGimmick *tree,
2547                                                                  const wxTreeItemId& item)
2548       : 
2549       wxNotifyEvent(commandType, tree->GetId()),
2550       m_item(item),
2551       mDicomNode(0)
2552         {
2553           //      m_editCancelled = false;
2554       
2555           SetEventObject(tree);
2556       
2557           if ( item.IsOk() )
2558             SetClientObject(tree->mTreeListCtrl->GetItemData(item));
2559         }
2560   
2561       WxGimmickEvent::WxGimmickEvent(wxEventType commandType, int id)
2562         : 
2563         wxNotifyEvent(commandType, id),
2564         mDicomNode(0)
2565           {
2566             m_itemOld = 0l;
2567             //      m_editCancelled = false;
2568           }
2569     
2570         WxGimmickEvent::WxGimmickEvent(const WxGimmickEvent & event)
2571           : 
2572           wxNotifyEvent(event),
2573           mDicomNode(0)
2574
2575             {
2576               m_evtKey = event.m_evtKey;
2577               m_item = event.m_item;
2578               m_itemOld = event.m_itemOld;
2579               mColor = event.mColor;
2580               mUserData = event.mUserData;
2581               //    m_pointDrag = event.m_pointDrag;
2582               //    m_label = event.m_label;
2583               //    m_editCancelled = event.m_editCancelled;
2584             }
2585       
2586       
2587
2588
2589
2590
2591
2592
2593           //================================================================
2594           //================================================================
2595           //================================================================
2596           //================================================================
2597           //================================================================
2598           WxGimmickFrame::WxGimmickFrame( wxWindow *parent, 
2599                                                                       wxString title, 
2600                                                                       wxSize size)
2601             : wxFrame((wxFrame *)parent, -1, title, wxDefaultPosition, size)
2602             {   
2603               wxBoxSizer *sizer = new wxBoxSizer(wxVERTICAL);
2604               long style = wxTR_DEFAULT_STYLE |
2605 #ifndef NO_VARIABLE_HEIGHT
2606                 wxTR_HAS_VARIABLE_ROW_HEIGHT |
2607 #endif
2608                 wxTR_EDIT_LABELS | wxSUNKEN_BORDER;
2609               mWxGimmick = new WxGimmick(this,-1,wxDefaultPosition,wxDefaultSize,style);
2610               sizer->Add(mWxGimmick,1,wxGROW);
2611               SetSizer(sizer);
2612               SetAutoLayout(true);
2613               Layout();
2614             }
2615             //================================================================
2616
2617             //================================================================
2618             WxGimmickFrame::~WxGimmickFrame()
2619               {
2620               }
2621             //================================================================
2622
2623             //================================================================
2624             void WxGimmickFrame::OnSelChanged(WxGimmickEvent& event)
2625             {
2626               //    std::cout << "+++++ WxGimmickFrame::OnSelChanged ++++++++++"
2627               //              <<std::endl;
2628               std::vector<std::string> file;
2629               //    mWxGimmick->GetSelectedImages(file);
2630               /*
2631                 std::vector<std::string>::iterator i;
2632                 for (i=file.begin();i!=file.end();++i)
2633                 {
2634                 std::cout << "'" << *i << "'" << std::endl;
2635                 }
2636                 std::cout << "++++++++++++++++++++" << std::endl;
2637               */
2638             }
2639             //================================================================
2640
2641             //================================================================
2642             BEGIN_EVENT_TABLE(WxGimmickFrame, wxFrame)
2643               EVT_TREEVIEWLIST_SEL_CHANGED(-1,WxGimmickFrame::OnSelChanged)
2644               END_EVENT_TABLE()
2645               //================================================================
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668               //================================================================
2669               //================================================================
2670               //================================================================
2671               //================================================================
2672
2673               //================================================================
2674               WxGimmickSettingsDialog::WxGimmickSettingsDialog(wxWindow *parent)
2675               : 
2676               wxDialog( parent, 
2677                         -1, 
2678                         _T("Settings"),
2679                         wxDefaultPosition,
2680                         wxSize(400,400),
2681                         wxRESIZE_BORDER | 
2682                         wxSYSTEM_MENU  |
2683                         wxCLOSE_BOX |
2684                         wxMAXIMIZE_BOX | 
2685                         wxMINIMIZE_BOX | 
2686                         wxCAPTION  
2687                         )
2688               {
2689               } 
2690               //================================================================
2691     
2692               //================================================================
2693               WxGimmickSettingsDialog::~WxGimmickSettingsDialog()
2694                 {
2695                 }
2696               //================================================================
2697
2698     
2699
2700
2701
2702
2703   }