' This is a PlodWare pwScripter script. ' https://RipeTech.com/PlodWare-pc-applications ' ------------------------------------------------------------------------------------------- ' This program was developed to create and manage metadata for the (~27000) digital images that I (currently) have, ' so that if and when an image viewer is available that can display embedded metadata, ' then I shall have the metadata ready to insert in to the images. ' Although the motivation was metadata for image files, there is nothing to prevent this program handling ' metadata (i.e. tag groups and tags) for any other type of file. ' Although metadata fields are defined for image types (e.g. Exif, IPTC, XMP) I wanted a flexible and open '(i.e. not committed to a specific format) metadata tool that can serve as the basis for future efforts ' to embed metadata into files. ' ------------------------------------------------------------------------------------------- dbclose () ' ------------------------------------------------------------------------------------------- dbExtension ("bk64tree",1) dbExtension ("popcount",1) dbExtension ("eval",1) ' these extensions will be loaded for each DB that is opened ' BUT: ' the **contents** of a virtual table do not survive ' a DB close/open or a DB save/saveAs ' but the table name does! ' ------------------------------------------------------------------------------------------- ' these are APPLICATION specific dim AppVersion = "2024-03-18 Tagger for Files" dim appName = "Metadata Tagger for Files" dim maxHD = 12 ' ------------------------------------------------------------------------------------------- ' these are APPLICATION specific defines that should not really be altered dim EOLdelim = chr ( 167 ) ' "§" dim nonPrintableDelim = chr ( 0x01e ) ' RS = Record Separator dim tagDelim = nonPrintableDelim & EOLdelim ' "§" dim FreeTxtId = "TagTxt" ' appended to each Free text field name, must NOT end in "Tag" to avoid aliasing with TagGroups dim TagGroupId = "Tag" ' appended to each tag group field name dim NoTag = " none" ' leading space is needed to prevent aliasing dim ImageFileExtensions = "('jpg', 'jpeg', 'png', 'gif', 'bmp')" ' ------------------------------------------------------------------------------------------- ' these are APPLICATION specific defines that are a matter of builder's preference dim allowRectMeta = 1 ' set this to allow/disable RectMeta when creating a NEW database dim allowRectPicasa = 0 ' set this to allow/disable RectPicasa when creating a NEW database dim NbrBulkExportRows = 7 ' nbr of BLOCKS in the File Tagging panel ' ------------------------------------------------------------------------------------------- ' these are APPLICATION specific defines that are general purpose AND should not be changed dbFastQuery = 1 sob ("RGB","DEFAULT") dim EOL = chr(0x0a) ' ------------------------------------------------------------------------------------------- ' create the IMAGE window ' ------------------------------------------------------------------------------------------- ' create a window to contain some SOBs IF ( 0 ) then dim ImageWindowSOB = sob ("application", "NEW", appName & "- Image") sob ( ImageWindowSOB,"SET" ,"SHOW",0) ' Define a callback to handle a click on the TOP Right "X" FUNCTION cbImageApplication ( sobid ) sob ( ImageWindowSOB,"SET" ,"SHOW",0) END function sob ( ImageWindowSOB,"ON","CLICK", "cbImageApplication") END if ' ------------------------------------------------------------------------------------------- ' create the HELP window ' ------------------------------------------------------------------------------------------- dim HelpPageSob , HelpTextsob dim HelpFixedHTMLSOB , HelpFloatHTMLSOB ' create a window to contain some SOBs dim HelpFloatOVl = sob ("application", "NEW", appName & "- HELP") sob ( HelpFloatOVl,"SET" ,"SHOW",0) ' Define a callback to handle a click on the TOP Right "X" FUNCTION cbHelpApplication ( sobid ) sob ( HelpFloatOVl,"SET" ,"SHOW",0) END function sob ( HelpFloatOVl,"ON","CLICK", "cbHelpApplication") dim HelpMenuBarSOB = sob (HelpFloatOVl , "add" , "menu" , "bar" ) dim HelpMenuOptionsSOB = sob (HelpMenuBarSOB , "add" , "menu" , "Horizontal" , "Options" ) dim HelpMenuHideSOB = sob (HelpMenuOptionsSOB , "add" , "menu" , "Vertical" , "Hide" ) dim HelpMenuHelp = sob (HelpMenuBarSOB , "add" , "menu" , "Horizontal" , "Help" ) dim HelpMenuAbout = sob (HelpMenuBarSOB , "add" , "menu" , "Horizontal" , "About" ) sob ( HelpMenuAbout , "add" , "menu" , "Vertical" , "Use cases" ) sob ( -1 ,"ON","CLICK", "HelpUseCases") sob ( HelpMenuAbout , "add" , "menu" , "Vertical" , "Techie Talk" ) sob ( -1 ,"ON","CLICK", "cbTechieTalk") sob ( HelpMenuAbout , "add" , "menu" , "Vertical", "The author" ) sob ( -1 , "ON" , "CLICK" , "AboutTheAuthor" ) sob ( HelpMenuAbout , "add" , "menu" , "Vertical" , "Contact" ) sob ( -1 ,"ON","CLICK", "ContactHTML") FUNCTION CBHelpMenuHide ( sobid ) sob ( HelpFloatOVl,"SET" ,"SHOW",0) END Function sob ( -1 , "ON" , "CLICK" , "CBHelpMenuHide") HelpFloatHTMLSOB =sob ( HelpFloatOVl , "ADD" , "web" ) sob ( HelpFloatHTMLSOB, "SET", "SILENT" , 1) ' ----------------------------------------------------- FUNCTION HelpTagging ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

Tagging

>>

Tagging does not alter any file except the database.

>>The standard way to assign/add a tag to a file is: >>
(assuming a tag Mum in the tag group who) >>
    >>
  1. Drag the target file on to the bottom right panel (blueish)
  2. >>
  3. Select the menu option Tools/File Tagging. >>
    The resulting display shows 6 columns. >> >>
  4. >>
  5. Pick one, any one, of the rows, and in it's action column >> select the add option. >>
  6. >>
  7. Tag Group column, same row: select the tag group of the >> tag you want to add (e.g. who) >>
  8. >>
  9. The goal is to get the Tag Name box to display the tag that we want to add! >>
    The Filter and Tag Name are interconnected! >>
    If the Filter box is empty, then the Tag Name contains all >> of the tags in the currently selected Tag group box. >>
    If the Filter box contained say jones, then the Tag Name contains all >> of the tags which contain the string jones (case insensitive). >>
    So between setting the Filter box and the selecting that wanted Tag Name you >> must pick your tag (e.g. Mum) >>
  10. >> >>
  11. Press the button (right hand side of the action definitiion) for this row, and the file is tagged! >>
  12. >>
>>You could drag'n'drop a selection of files (and/or folder(s)) on to these buttons and the >> tagging will be applied to them! >>

>>Actions >> >> Filter >> sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function ' ----------------------------------------------------- FUNCTION HelpExpertMode ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

Expert Mode

>> The Expert Mode gives you access to the underlying BASIC like interpreter and to >> a command line interface to the SQLite database. >>

I will not attempt explaining the Expert Mode here. >>

But I will highlight some of the database tables used by this application. >>

TagTable contains one row for each tag. A row contains: >> >>
>>Master contains one row for each file. A row contains: >> >>
>>SavedEnv contains application internal data that enables some configuration information to survive over sessions. >>

>>The SQLite module was compiled so that the function like is case insensitive. >>
If you start writing your own queries on the tables then I recommend using >>like and not the operator = so that on queries you do not have to be >> so carefuly with upper/lower case typing. >>
This is especially true if you use update and insert statements with case errors, if >> you use like in select statements and where sub-statements you will avoid one source of errors. >>

>>There are some videos on youtube about the general SQLite / BASIC capabilites (search for PlodWare). sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function FUNCTION HelpBackUp ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

Backup

>> You can back up the files, and the folder structure, referenced in a database to another disk/folder. >>

Additionally when creating a backup >> a copy of the database file is exported to the backup folder, suitably modified so that the >> backed up database is useable with the backed up files! >>

p.s. if I have understood correctly, a backup is a thing, whilst back up is an action. >>

Or more formally expressed: >> >>I could of course be wrong! sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function ' ----------------------------------------------------- FUNCTION HelpFreeText ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

File Text

>> Tag groups and tags are common to all of the files in the database. >> But you might want to assign a unique block of text to a specific file. >>
This is where file text is useful. >>
>>
In another help page I mentioned that I have images of my Great Uncle Bill >>
He appears in a (regretably small) number of pictures, some taken in the 1950s together with peope >> that I can not identify. I know which one is Great Uncle Bill and I can (and have) >> defined a region on the images and assigned them to the Great Uncle Bill tag. >>

But I have also added to one of the images a file text >> saying something like: >>

>> "Uncle" Bill (with cap), probably Taunton Somerset, probably before 1957 >>

>>To set the file text you must drag'n'drop the file on to the bottom right panel (blueish). >>
Then find the text File Text Tool slightly above what was the blueish panel. >>
>>
Set the Action to be edit. >>
Enter the text that you want in to the edit area to the right of the text File Text Tool field >>
Press the (red?) edit button. >>

Thats it! >>

The contents of the File Text edit area can be assigned to other files >> by drag'n'dropping the file(s) (and/or folders) on to the (red?) edit button. sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function ' ----------------------------------------------------- FUNCTION HelpCurrentFile ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

Delete Current File

>>When a file is drag'n'dropped on to the lower right (blueish) panel, then this program >> will identify all files in the database with the same MD5 hash, i.e. to a very very high likelihood, identical. >>
The full file name (path/name/ext) of all of the files (including the drag'n'dropped file) will be displayed >> in a combo box near the top of the screen, to the right of a text string File(s):. >>
>>
To the right of this combobox are the buttons Open folder and Delete file. >>
>>
Pressing the Delete file button causes the file currently selected in the combobox >> to be removed from the database, from the file system and sent to the recycling bin. >>
>>
Use with care! sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function FUNCTION HelpTipsNTricks ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

Tips and Tricks

>>

keeping track

>>If you have lots of files to tag, it is almost guaranteed that you will lose track of which files >> have been fully tagged, and which not, or which have say all of their who tags but not all of their where tags. >>
There is a built-in tag group called Tagged, which can only hold the names of the other tag groups. >> I recommend that you use it, if at all, in the following (positive) way: >>

Assuming tag groups of say who what where then when you haved applied all of say the who tags >> that you set the Tagged group for that file to include the tag who. >>
This will enable you to filter in/out the files that have all of their who tags. >>
If you decide that no more tagging is needed for a file, then set the Tagged group tag Tagged. >>
Sorry, that sounds a bit weird, but it works. >>
Do not use it negatively i.e. to hold the names of unhandled tag groups. Why! Because newly added files >> default to an empty Tagged group! >>
>>
>>

finding the original folder of an exported file

>>If you want to find the original folder of an exported file, then >> drag'n'drop the exported file on to the reload button just above the lower right (blueish) panel. >>
That will cause the original folder to be opened in a file explorer. >>
Exported files are given unique names of the form: >> >>
>>
>>

Tag group, tag name and free text field name

>>Within its "type" these must be unique. >>
e.g. a tag name fred can only be used once in say the tag group who but >> it can also appear once as the name of a tag group or as a tag in another tag group, and once as a free text name. >> >>
>>
>>

Internal (database names) for Tag groups and free text fields

>> sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function FUNCTION HelpDelFiles ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

Delete Files

>>There is a bulk delete capability based on the file subset function. >>

The menu option Delete / Delete Files will display a panel with which you can select >> the file subset to be deleted, and a Delete Files in Subset button to >> trigger the action. sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function FUNCTION HelpDelExtDups ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

Delete External Duplicates

>> The goal is to be able to scan a folder (and its sub-folders) and delete those files that are in >> the database. Files that are known are deleted (to the recycle bin). >>

Why! >>

I have images backed-up on to various storage devices. >>
When an opportunity came to consolidate the various images on to a single storage device I wanted >> a capability to look at the various storage devices and remove those files already consolidated. >>

To access the capability use the menu option Delete / Delete External Duplicates sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function FUNCTION HelpFileSubSet (sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >> >> >> >> >>

Create the file subset

>>The file subset is the basis for the following bulk actions: >> >>Initially the subset is empty, but files can be added (and removed) on the basis of >> their tags or of their file system characteristics (i.e. path, name , extension). >>

>> Since the file subset is only cleared when you press the New list button then >> it is possible to : >> >> In the upper half of the panels for Power Tagging , Files to Folder and Delete Files, >> under the grey row ( Action    Tag Group    Filter    Tag Name ) area there are 8 rows >> each of which defines selection critera for the inclusion, or removal, of files in a subset of the database. >>
>>
The Action column defines the main part of the inclusion or removal activity. >>

The following actions cause files to be added to the subset. >> >>>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >>
ActionAdd rows to the file sublist if:
 add only >>                  >>                >> has only got the specified tag in the specified group. (Tags in other groups are allowed.) >>
 add == has atleast the specified group & tag
 add <> has not got the specified group & ta
 add any has any tag in the specified group
 add non has no tag in the specified group
 add == has any tag with the partial string (Filter column) in the specified group
 add <> has no tag with the partial string (Filter column) in the specified group
 tag == tags in specified group are identical with those of current file (blueish panel)
 tag <> tags in specified group are not identical with those of current file (blueish panel)
 tags == tags in all groups are identical with those of current file (blueish panel)
 tags <> tags in all groups are not identical with those of current file (blueish panel)
 duplicate has a non-unique hash value
>>
The following actions cause files to be removed from the subset. >> >>>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >>
ActionRemove rows from file sublist if:
 remove == >>                  >>            >> has atleastthe specified group & tag
 remove <> has not got any tag in the specified group & tag
 remove any has any tag in the specified group
 remove non has no tag in the specified group
 remove ==?  has any tag with the partial string (Filter column) in the specified group
 remove <>? has no tag with the partial string (Filter column) in the specified group >>                  >>           >>
 -unchanged  remove rows that were unchanged in the last rescan
 -new  remove rows that were added in the last rescan
 -changed  remove rows that changed in the last rescan
>>
Rather than create an even larger set of filter actions I have implemented >> two low level filters: >> >>In each case the user provides the XX. >>

The following table shows examples of XX which could be placed >> in the edit fields of the Insert where and Delete where low level >> filter options. But note: it is entirely possible to access tags using low level filters but since the >> tag delimiter includes a non-printable character then the syntax is a bit complicated. Hopefully the tag related >> requirements are covered in the two tables above. >>

>> >>>> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >> >>
Where clausewhat it selects
 path like '%fred%' path name of row contains the substring fred >>
 name like '%fred%' file name of row contains the substring fred
 ext in ('jpg','bmp')  ext must be either jpg or bmp. Warning in is case sensitive   >>                  >>              >>            >>
 ext like 'jpg' or ext like 'bmp'  ext must be either jpg or bmp. Note: is case insensitive
 size = 0 file has no contents
 size < 1000 and ext like 'jpg'   smallish jpg images
>>
To force the subset to be empty, click on the New List button. >>

>>Filter actions will be executed when the Apply Filters button is pressed. >> >> sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function ' ----------------------------------------------------- FUNCTION HelpPowerTagging ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

Power Tagging

>>Power tagging is where you select a subset of the files in the database, >> and then apply a set of tag operations to those files. >> >>
>>
For details on creating the file subset, please refer to the help section File Subset >>
>>

Define the tagging action

>> In the lower half of the screen, under the grey row ( Action Tag Group Filter Tag Name ) area there are 8 rows >> with which you can define tag operations on the file subset. >>
Define the tag operations in the usual way (menu Help / Tagging). >>
Note: that a tag filter is only activated when you press on the appropriate >> button at the right hand end of the filter row. sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function ' ----------------------------------------------------- FUNCTION HelpInherit ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

Inherit

>>This is a easy way to copy the tagging information from a reference file >> to one or more other files. >>
>>
    >>
  1. Drag and drop the reference file to the bottom right panel (blueish). >>
    If that file is an image file, then it will be displayed. >>

    >>
  2. >>
  3. Drag and drop one or more files or folders on to the drop to inherit field >>
    and the reference tags will be applied to the dropped files. >>
    This also copies the file text but not any region(s). >>
  4. sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function FUNCTION HelpSingleFileOptions ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

    Single File Options

    >>A Microsoft Windows installation includes some capabilites to extract some metadata from some >> file types. >>

    As a convenience, this program can access two of these capabilities and send the >> found metadata to the display. >>

    >> sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function ' ----------------------------------------------------- FUNCTION HelpTagTextHTML ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

    Tag Text

    >>I have a who group, in which I have tag for a relative, my Great Uncle Bill. >>

    >>I wanted to include in the database some information about Great Uncle Bill >> without making the tag ridiculously long. >>
    This is where tag text is useful. >>
    >>
    For each and every tag you can have a unique text block, that is not associated >> directly with a file, only with the tag. >>

    >>You can create / modify / delete tag text under the menu option Tools / Tag Management >>

    >>In a side table, where I keep copies of the tag groups and tags there is a free text >> field, in which the who tag Great Uncle Bill has text like: >>

    >>
    DOB: 1896-02-24 >>
    DOD: 1976-02-21 >>
    Born in ... >>

    >>
      >>
    1. It seems to me to be a good place to capture information about the target tag
    2. >>
    3. Specifically for image files, you can define a region on an image that, >> if clicked upon, when displayed by this program, will display the tag text
    4. >> sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function ' ----------------------------------------------------- FUNCTION AboutTheAuthor( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

      The author

      >> First program written in 1971/72. >>
      >>
      Still learning. >>
      >>
      Still trying to get things right. >>
      >>
      Still here (2023). >>
      >>
      In my own words still plodding along. >>
      Hence the web site www.PlodWare.com (actually a sub-domain of www.RipeTech.com). >>
      >>
      www.RipeTech.com hosts a collection of things that interest me. Originally >> created to host my programming efforts around a FORTH like interpretive language. >>

      RipeTech, my abbreviation for Rapid Interactive Programming Environment Technology >> because that is how I think of FORTH. >>

      www.PlodWare.com a sub-domain of www.RipeTech.com, focussing on stand-alone applications >> based on my FORTH like interpreter. >>
      >>
      No one ever said that it should all make sense. >>
      >>
      To quote from the HHGTTG share and enjoy sob ( HelpTextsob , "ADD" , "ROW", freestring ) END Function FUNCTION HelpOnline ( sobid ) ShellExecute ( "https://www.RipeTech.com/PWMetaTagger" ) END Function FUNCTION aboutHTML ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

      Introduction

      >> This program was developed to create and manage metadata for the (~27000) >> digital images that I (currently) have, so that if and when an image viewer is available >> that can display embedded metadata, then I shall have the metadata ready to insert in to the >> images. >>

      Although the motivation was metadata for image files, there is nothing to >> prevent this program handling metadata (i.e. tag groups and tags) for any other type of file. >>

      >> Although metadata fields are defined for image types (e.g. Exif, IPTC, XML) >> I wanted a flexible and open (i.e. not committed to a specific format) metadata tool that can serve as the basis for future efforts to >> embed metadata into files. >>

      >> So I developed this program to: >> sob ( HelpTextsob , "ADD" , "ROW", freestring ) END Function FUNCTION ContactHTML ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

      Contact

      >> E-Mail: George.Salisbury@RipeTech.com >>

      >>(Auch auf Deutsch wenn Sie wollen.) sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function ' ----------------------------------------------------- FUNCTION HelpVersion ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

      Version

      >> Application version: freestring = freestring & " " & AppVersion >>

      Underlying database engine is SQLite version: freestring = freestring & " " & sqlVersion() sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function FUNCTION HelpUseCases ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

      Use Cases

      >> There are use cases for this program even if you do not apply any tagging. >>
      In a new database, scan the required source folders, do not bother with any tagging: >>
        >>
      1. Back up: save the files to a back up storage device / folder.
      2. >>
      3. Find adds & changes: save the database, do a rescan some time later and the program will identify new and changed files.
      4. >>
      5. Remove empty files: Delete / Delete files use the empty action to find >>empty (size = 0) files and delete them.
      6. >>
      7. Remove duplicate files: Export / Export to folder use the duplicate >> action to find duplicate files.
        Export the files to a folder. >> Then drag'n'drop individual files on to the bottom right (blueish) panel, >> this will cause the duplicate files to be listed in a cobobox at the top of the screen. >> Select from the combobox the upwanted duplicate(s) and delete them (Delete file >> button to the right of the combobox.
      8. >>
      9. Locate files: Export / Export to folder use the path, file, >> and ext actions to filter the database down to the wanted file(s) and export a copy >> to the export folder.
      10. >>
      >>
      If you apply tagging then the following use cases are added: >>
        >>
      1. Locate files: Export / Export to folder you have the file-system actions and all of the tag related >> actions to filter the database down to the wanted file(s) and export a copy >> to your export folder. >>
        My sister recently asked me for a copy of all image files with her daughter in them.
      2. >> In just a few minutes I had a zip file for my sister with almost 350 pictures. >>
      3. Who is that: I use the Regions / Rubberbanding tool to define a region (rectangle) over people >> in image files, and associate the region with a tag (such as a who tag group, with the >> tag Great Uncle Bill, with tag text holding some details about Great Uncle Bill). >> When browsing the images, with other people sitting next to me, I can >> click on a region and provide those people next to me with the name of the person under the region, and >> if the tag text is populated, information about the person in the region.
      4. >>
      5. What is that: Again using the Regions / Rubberbanding tool. >>
        Define a region (rectangle) over objects in an image: cars, dogs, food (recipe in the tag text?),... >>
      6. >>
      7. Lest we forget: Use the File text field of a specific file to hold >> a general statement/description of the file. >>
        e.g. I have a picture showing about half of the landing craft of MV Llangibby Castle on D-Day 1945 >> , the file text for this image is a good place to mention that William Alfred Salisbury (my father) was a >> Royal Marine on board one of MV Llangibby Castle's landing craft, possibly one of the craft in the image! >>
      8. >>
      >>
      Yes I know that regions, file text etc, only make sense to this >> metadata tagging program, BUT the information is in an open format, and >> in a standard (SQLite) database. >>

      You can either access the database with some other tool, or write >> a script, using the Expert Mode to export the wanted content in the format that you >> want. >>
      Or just export it all to Microsoft Excel (if you have it on the same machine as this program) >> and write a script in Microsoft VBA, or whatever! >>

      My position, as the author of this program is: >>

      The really important issue (especially regarding images) is capturing the metadata >> in an open, plain text, reusable format.

      >>Writing on the back of a paper picture is just not enough, and it damages the photo. sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function FUNCTION cbTechieTalk ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

      Techie Talk

      >>A single (portable) executable file (okay, it does create a small database file in the folder where >> the executable is located, this database is used to hold configuration data that persists over sessions). >>
      >>
      Freeware, closed source. >>
      Only available for Microsoft Windows. >>

      >>This is a tri-language program (4 if you include SQLite). >>
        >>
      1. The application that you see is written in a BASIC like script that has been encoded and embedded in to this executable.
      2. >>
      3. The BASIC script is interpreted at runtime via a FORTH like system into calls to native code.
      4. >>
      5. The native code was compliled from a program written in plain old "C".
      6. >>
      >>No MFC or WPF or any other framework was used, GUI management was done at the Windows API (Win32) message level. >>
      Why be awkward, when, with a little effort you can be impossible >>

      The BASIC like environment supports: >> >> More about the capabilites of this program is available online at: >> www.plodware.com (actually a sub-domain of www.ripetech.com) >>
      Application version: freestring = freestring & " " & AppVersion >>

      Underlying database engine is SQLite version: freestring = freestring & " " & sqlVersion() >>
      For help with the SQLite syntax please look at the many online sites. sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function FUNCTION HelpFacialRecognition ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

      Facial Recognition

      >> No, this program can not do any facial recognition. >>

      But here is what I did! >>

      By late 2021 I had a amassed a collection of ~27000 pictures. >> About a third were analogue: 35mm slides, 6x6 slides, 35mm negatives, 6x6 negatives and simple paper pictures >>
      A pitiful small number of the pictures were old paper pictures of family members dating back to about 1908. >>
      I felt motivated to preserve some knowledge (e.g. metadata) of the people and places in those pictures, and I wanted >> it to be in a computer file, not on the back of the paper picture. >>
      A digital file, such as a database would allow me search, find and display the images and metadata. >>
      The first step was to digitise the analogue images. So I bought: >> >> With these 3 scanners I was able to digitise all of my analogue images. >>
      Even the panoramic picture from my university days which was too long to scan in one go. >>So I scanned as much as I could from each end, and then used a freeware program >> to stitch the two pieces together (I can not remember which program it was, sorry). >>

      Now, there was one issue when digitising the images. Having dust free slides and film strips. It takes time! >>
      My recommendation. Do not clean (yet)! >>
      Scan everything. See which images are worth keeping, and which of these need cleaning. >>
      Those that are interesting, clean them and scan to the highest resolution. >>

      >> Now for the Facial recognition bit. >>

      >> I tried using Microsoft Photos. Let us just say that I failed to achieve my goal! >>
      >>
      I then heard that the Picasa program from Google, although no longer supported, has some facial recognition abilities. >>
      Copies of Picasa can still be found in the Internet. >>
      Downloaded it, installed it, ran it. >>
      Within 2 hours I was applying names to facial groups that Picasa had identified. >>
      Then another 2 days to sort out the problem faces (one baby can look a lot like another). >>
      >>
      Of the ~27000 files, Picasa provided me with facial recognition on 7300 images. >>
      About 5200 images with a single person >>
      About 2100 images with more than one (identified)person >>

      I then used the Region capabilites of this metadata tagging program and provided names by hand (easy, and with acceptable speed) >>
      About 2300 images with a single person >>
      About 700 images with more than one (identified) person >>

      In total I now have 9500 images where someone is identified. >>

      In some cases Picasa did not identify a face because the face was turned away from the camera, or >> was partially obsured. >>
      All in all I am well pleased with the result. >>
      >>
      Picasa saves, in plain text, facial recognition information in a .picasa.ini file which it writes in to the >> folder where the image is. >>
      I have about 850 folders with images, so 850 .picasa.ini files. >>
      I wrote a script of less than 300 lines, using the BASIC like language in which this application was written, to >> scan the .picasa.ini files, create a who tag for each face group(about 200 named face groups) and insert >> that tag in to the appropriate file's database entry. >>

      >>Summary: facial recognition was much easier than I expected: Now I wonder if other recognition solutions are >> available (animals, flower,...) i.e. generic objects. >>
      >>

      The .picasa.ini files include the position of the face on the image. >>
      This was my motivation to implement Regions, see a separate help topic! >>

      sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function FUNCTION HelpRegions ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

      Regions

      >> This only applies to individual image files that are drag'n'dropped on to the >> bottom right panel (blueish). >>

      A region is a rectangular area on a image that is linked to a tag. >>
      If that image is drag'n'dropped on to the >> bottom right panel (blueish), then the region(s) associated with that image will be displayed >> on the image (only in the bottom right panel, not actually in the image). >>

      You can then click on a region and the tag information associated with that region >> will be displayed. >>

      You can define your own regions, and assign each region its own (single) tag. The tag can be in any >> tag group. >>
      I have some images of my musical instruments, and on some images I have a region >> with the tag bouzouki (nice for playing Irish folk songs). >>
      >>
      To assign a region you must activate the menu option Regions / Rubberbanding. >>
      Draw a rectangle over the region with the mouse, let go, and a tool-page will appear on the >> left hand side, enabling you to: >> >>There are three types of region: >> >>You can customise the colour of these regions' edges so that you can >> better identify which regions are which. >>
      >>
      This tool-page also allows you to edit the tag text that belongs to a tag that >> is assigned to a region. >>
      Yes, this could be done under the menu Tools / Tag Management but experience suggests to me >> that it is useful to have an edit option here as well. sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function FUNCTION myTagGroups ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

      Tag Groups that I use

      >> >> >> Currently I think that I need another tag group, location. Whilst the database knows where the digital file is, >> it currently does not have any information about the physical location of the referenced analogue object. >>
      i.e. for a scan of a paper picture, where is that paper picture located. Likewise for scans of formal documents! sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function FUNCTION WelcomeMessage ( sobid ) sob ( HelpPageSob ,"SET","SHOW",1) sob ( HelpTextSOB, "empty" ) > >>

      Welcome

      >>There are some on-line help pages for this application, see the menu option >> Help / Go to online help. >>
      >>
      https://RipeTech.com/PWMetaTagger >>
      >>
      >>There are also some help pages embedded in to this program. >>
      >>
      Try the menu Help. sob ( HelpTextsob, "ADD", "ROW" , freestring ) END Function ' ------------------------------------------------------------------------------------------- ' ------------------------------------------------------------------------------------------- ' ------------------------------------------------------------------------------------------- ' ------------------------------------------------------------------------------------------- dim escChr = 0x01b dim defActionComboText = " " dim defTypeComboText = " " dim haveRectMeta = 0 dim haveRectPicasa = 0 dim DbName = "" dim copyTagGroup = "" dim copyTagName = "" dim RegionRectWeight , RegionRectShow , reDrawRegionsID dim ImageLoadedFlag = 0 dim leftRB , topRB , rightRB, bottomRB dim HaveRubberBandRect dim FirstFileMenuItem dim CommonSobTitle dim CommonSobInfo dim CommonSobBtn , CommonSobBtnB , CommonSobCombo dim CommonSobResult FUNCTION CommonSobClear ( mytitle ) sob ( CommonSobTitle , "SET" , "TITLE" , mytitle) sob ( CommonSobInfo , "EMPTY" ) sob ( CommonSobResult , "EMPTY" ) sob ( CommonSobBtn , "set" , "show" , 0) sob ( CommonSobBtnB , "set" , "show" , 0) sob ( CommonSobCombo , "set" , "show" , 0) sob ( CommonSobCombo , "EMPTY" ) noErrorReport ("") END FUNCTION dim TransparentRGB = rgb (1,1,1) ' do NOT change this, is hardcodeed in the underlying interpreter! dim defBtnCol = rgb ( 0xd4, 0xd0, 0xc8 ) dim bgBoxCol = rgb ( 0x7f, 0xff, 0x7f ) dim defBoxTxtCol = rgb ( 0xeb, 0xeb, 0xeb ) dim defLabelCol = rgb ( 0xeb, 0xeb, 0xeb ) dim ErrorCol = rgb ( 0xff, 0x60, 0x60 ) dim WarningCol = rgb ( 0xff, 0xbf, 0xbf ) dim bgStatusCol = rgb ( 0xeb, 0xeb, 0xeb ) dim defStatusCol = rgb ( 0x00, 0xff, 0x00 ) dim bgTypeCol = rgb ( 0x7f, 0xff, 0x7f ) dim defTypeCol = bgTypeCol dim altTypeCol = rgb ( 0xff, 0xff, 0xff ) dim defNameCol = rgb ( 0x97, 0xd5, 0xff ) dim altNameCol = rgb ( 0xff, 0xff, 0xff ) dim defTextCol = rgb ( 0xff, 0xff, 0x7f ) dim bgROeditCol = bgStatusCol dim tagArray , uBoundTagArray dim tagTxtArray , uBoundTagTxtArray dim tagFlag, FreeTextFlag dim SavedTagTypeComboArray ( 128 ) as handle dim SavedTagTypeComboIndex = 0 dim TagTypeComboArray ( 128 ) as handle dim TagTypeComboIndex = 0 ' ------------------------------------------------------------------------------------------- ' ------------------------------------------------------------------------------------------- ' ------------------------------------------------------------------------------------------- ' ------------------------------------------------------------------------------------------- ' ------------------------------------------------------------------------------------------- FUNCTION UpdateUsedFileList () dim i , fName, pos = 0 , rid FOR I = 0 to 10 > select txt from parameters where type like 'file' order by val desc limit 1 offset freestring = freestring & " " & sqlString ( "" & i ) fName = db_arg_text ( freestring ) IF ( isFile ( fName ) == 1 ) then sob ( sob(FirstFileMenuItem,"SIBLING",pos++) , "SET" , "TITLE" , fName ) END IF NEXT i > select count (*) from parameters where type like 'file' pos = db_arg_int ( freestring ) i = 11 DO while i < pos > select rowid from parameters where type like 'file' order by val desc limit 1 offset 11 rid = db_arg_int (freestring) > delete from parameters where type like 'file' and rowid = freestring = freestring & rid db_arg_set(freestring) i ++ LOOP END function FUNCTION AddFileToUsedList ( useFile, accesstype ) dim fileNbr fileNbr = db_arg_int ( " select count(*) from parameters where type like 'file' " ) IF fileNbr then > select max ( val) from parameters where type like 'file' fileNbr = db_arg_int( freestring ) + 1 ELSE fileNbr = 1 END if > Delete from parameters where type like 'file' and txt like freestring = freestring & " " & sqlString ( useFile ) db_arg_set ( freestring ) > insert into parameters values ('file', '', freestring = freestring & " " & sqlString ( fileNbr ) >> , freestring = freestring & " " & sqlString ( useFile ) >> , '' ) db_arg_set ( freestring ) UpdateUsedFileList () END Function FUNCTION OpenMessage ( ) dim onArea = CommonSobInfo sob ( onArea , "ADD" , "ROW" , "" & EOL ) sob ( onArea , "ADD" , "ROW" , "You can now start working on the database." & EOL ) sob ( onArea , "ADD" , "ROW" , "e.g." & EOL ) sob ( onArea , "ADD" , "ROW" , " To get details about a file drag and drop a " & EOL ) sob ( onArea , "ADD" , "ROW" , " file on to the blue area." & EOL ) sob ( sobImageHolder , "SET" , "RGB", RGB( 100,200,255) ) END FUNCTION ' ---------------------------------------------------------- FUNCTION deReferenceTagInRegions ( TagGroup , Tagname ) IF ( haveRectMeta ) then > update Master set RectMeta = replace( RectMeta, freestring = freestring & " " & sqlString ( ")," & TagGroup & TagDelim & Tagname & ";" ) >> , freestring = freestring & " " & sqlString ( ")fffffffffffffff;" ) >> ) sql ( freestring ) END IF IF ( haveRectPicasa ) then > update Master set RectPicasa = replace( RectPicasa , freestring = freestring & " " & sqlString ( ")," & TagGroup & TagDelim & Tagname & ";" ) >> , freestring = freestring & " " & sqlString ( ")fffffffffffffff;" ) >> ) sql ( freestring ) END IF END Function FUNCTION deReferenceTagInRegionsInHash ( hashVal , TagGroup , Tagname ) ' remove a tag group/tag name from the RECT columns for a specific HASH value ' we leave the RECT part in the table, and set its group/name part to the default > update Master set RectMeta = replace( RectMeta, freestring = freestring & " " & sqlString ( ")," & TagGroup & TagDelim & Tagname & ";" ) >> , freestring = freestring & " " & sqlString ( ")fffffffffffffff;" ) >> ) where hash like freestring = freestring & " " & sqlString ( hashVal ) sql ( freestring ) > update Master set RectPicasa = replace( RectPicasa , freestring = freestring & " " & sqlString ( ")," & TagGroup & TagDelim & Tagname & ";" ) >> , freestring = freestring & " " & sqlString ( ")fffffffffffffff;" ) >> ) where hash like freestring = freestring & " " & sqlString ( hashVal ) sql ( freestring ) END Function FUNCTION ReReferenceTagInRegions ( TagGroup , OldTagname , NewTagname ) > update Master set RectMeta = replace( RectMeta, freestring = freestring & " " & sqlString ( ")," & TagGroup & TagDelim & OldTagname & ";" ) >> , freestring = freestring & " " & sqlString ( ")," & TagGroup & TagDelim & NewTagname & ";" ) >> ) sql( freestring ) > update Master set RectPicasa = replace( RectPicasa, freestring = freestring & " " & sqlString ( ")," & OldTagname & ";" ) >> , freestring = freestring & " " & sqlString ( ")," & NewTagname & ";" ) >> ) sql( freestring ) END Function ' ---------------------------------------------------------- FUNCTION CBUsedFileList ( sobid ) ' sobid = selected UsedFileList menu shortcut ' open a previous database using the UsedFileList shortcuts dim dbFIle = sob ( sobid, "get","title") currentFile = "" CommonSobClear ( "Open database from used file list" ) >Open a database file, in memory§ >>§ >>Changes are ONLY saved when you explicitly trigger a save.§ sob ( CommonOvl ,"set","show",1) dim proceed = 1 IF ( proceed ) then TRY dbopen("memory" ,dbFIle) CATCH ErrorReport ("Open failed on file : " & dbFIle ) sob ( CommonSobResult , "add" , "row" , "Open failed on file : " & EOL & dbFIle & EOL ) proceed = 0 END try END IF copyTagName = "" IF ( proceed ) then proceed = dbValidityCheck () END IF IF ( proceed ) then > select comment from parameters where type like 'file' and txt = freestring = freestring & " " & sqlString ( dbFIle ) currentFile = db_arg_text ( freestring) AddFileToUsedList ( dbFIle, "rw" ) sob ( statusDBFile, "SET" ,"TITLE" , dbFIle ) IF ( ! isTable ( , "master") ) then sob ( CommonSobResult , "add" , "row" , "No master table in database" & EOL ) dbclose() proceed = 0 END if END IF IF ( proceed ) then NoErrorReport ("Open OK " ) update master set key = hash || path || name sob ( CommonSobResult , "add" , "row" , "Database file OK: " & EOL & dbFIle & EOL & EOL ) OnOpenPrepareMasterTagTable (CommonSobResult) OpenMessage ( ) cbsingleFile ( currentFile ) END IF END function FUNCTION create_usedFileList () FirstFileMenuItem = sob ( myMenuFile , "add" , "menu" , "Vertical" , "0 " ) sob ( -1 , "ON" , "CLICK" , "CBUsedFileList") dim i FOR I = 1 to 10 sob ( myMenuFile , "add" , "menu" , "Vertical" ,"" ) sob ( -1 , "ON" , "CLICK" , "CBUsedFileList") NEXT i END Function FUNCTION enableUsedFileSelection ( flag ) dim i FOR I = 0 to 10 SOB (sob(FirstFileMenuItem,"SIBLING",i) , "SET" , "MENU.GREY" ,flag ) NEXT i END function FUNCTION getFirstFileDir () dim defDir = sob ( FirstFileMenuItem , "GET" , "TITLE" ) IF ( isFIle ( defDir ) ) then IF ( defDir ) then DO WHILE ( right ( defDir , 1 ) <> "\\" ) defDir = right ( defDir , -1 ) LOOP getFirstFileDir = defDir END IF ELSE getFirstFileDir = dir END IF IF ( isFIle ( getFirstFileDir ) == -1 ) then ELSE getFirstFileDir = app@cwd END IF FileSelDir ( getFirstFileDir) END function ' ------------------------------------------------------------------------------------------- FUNCTION toggleWarnColour ( sobid ) IF ( sob (sobId , "GET", "RGB" ) == WarningCol ) then sob ( sobId , "SET", "RGB" , RGB ( 0xff ,0,0 ) ) ELSE sob ( sobId , "SET", "RGB" , WarningCol ) END IF END FUNCTION FUNCTION toggleOKColour ( sobid ) IF ( sob (sobId , "GET", "RGB" ) == bgTypeCol ) then sob ( sobId , "SET", "RGB" , RGB ( 0 ,0xff ,0 ) ) ELSE sob ( sobId , "SET", "RGB" , bgTypeCol ) END IF END FUNCTION dim MarkRowid = "0" FUNCTION CbdeleteFileListDoit ( sobid ) IF ( ! isDBopen ) then sob ( CommonSobResult , "ADD" , "ROW" , "No database is open!" & EOL ) toggleWarnColour ( sobid) EXIT function END IF SELECT count (*) from temp.SubList where type = 'file' dim fileCount = qrSingleValue IF ( ! fileCount ) then sob ( CommonSobResult , "ADD" , "ROW" , "No files selected!" & EOL ) toggleWarnColour ( sobid) EXIT function END IF IF ( msgBox ( "Delete the files?" , 0x04 , "WARNING" ) ) THEN EXIT function END if cancelClear () cancelText( "DELETE Files" , 1 ) cancelText( "Files to delete: " & FileCount , 2 ) cancelShow(1) WITHQUERY ( " select path , name , rowid from temp.SubList where type = 'file' ") TRY DeleteFileNow ( wqtext(1) & wqtext(2) ) > delete from master where path like $pth and name like $nme freestring = replace ( freestring , "$pth" , sqlString (wqText(1)) ) freestring = replace ( freestring , "$nme" , sqlString (wqText(2)) ) sql (freestring) > delete from temp.SubList where rowid = $rid freestring = replace ( freestring , "$rid" , sqlString (wqText(3)) ) sql (freestring) CATCH IF ( isFile (wqtext(1) & wqtext(2)) ) then sob ( CommonSobResult , "add" , "row" , "delete file failure: " & EOL & wqtext(1) & wqtext(2) & EOL & dstPoint & EOL ) END IF END try cancelToggle () fileCount-- IF ( fileCount BAND 0x0f ) then ELSE cancelText( "Files to delete: " & FileCount , 2 ) END IF END withquery cancelShow(0) sob ( CommonSobResult , "add" , "row" , "Delete finished! " & EOL ) toggleWarnColour ( sobid) END FUNCTION FUNCTION CbdeleteFileList ( sobid ) CommonSobClear ( "Delete files" ) > Use this to DELETE the files in the file list§ >> - The files will be sent to the Recycle bin§ >> The files will be deleted from the file system and removed from the database§ sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn , "SET" , "TITLE" , "Delete") sob ( CommonSobBtn , "ON" , "PRESS" , "CbdeleteFileListDoIt") sob ( CommonSobBtn , "SET" , "rgb" , defBtnCol) sob ( CommonOvl ,"set","show",1) END Function FUNCTION CbExportFileListDoIt ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if exportFilter ( "export" , "" , GetPath & "\\" ) END FUNCTION FUNCTION CbReuseTargetFolder ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if exportFilter ( "export" , "" , ExportFolder ) END FUNCTION dim TargetFolder , ExportFolder = "" FUNCTION CbOpenExportFolder (sobid ) sob ( sobid, "set", "show", 0 ) IF ( ! isdbopen ) THEN ErrorReport("No database open") EXIT function END IF ShellExecute ( TargetFolder ) END function FUNCTION cbExportFileList ( sobid ) CommonSobClear ( "Export files" ) > Use this to export(copy) the files in the file list to a folder§ >> - The files are given a unique name, and placed under the specified folder§ >> - The files will be placed in a single "flat" folder sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn , "SET" , "TITLE" , "Export") sob ( CommonSobBtn , "ON" , "PRESS", "CbExportFileListDoIt") sob ( CommonSobBtn , "SET" , "rgb" , defBtnCol) sob ( CommonSobBtn , "set" , "show",1) IF ( ExportFolder <> "") then sob ( CommonSobBtnB , "SET", "TITLE" , "Export to previous folder" ) sob ( CommonSobBtnB , "ON" , "PRESS", "CbReuseTargetFolder") END if sob ( CommonOvl , "set" , "show",1) END Function FUNCTION FilterOnTagPlus ( TagGroup ) dim FilterTags , uBoundFilterTags , i > insert into temp.SubList SELECT rowid , * from master where sql ( "select " & sqlIdentifier ( TagGroup & TagGroupId ) & " from temp.masterfile limit 1" ) set FilterTags = split ( replace ( qrSingleValue , TagDelim , nonPrintableDelim ) , nonPrintableDelim ) uBoundFilterTags = uBOuND ( FilterTags ) IF ( uBoundFilterTags > -1 ) then FOR i = 0 to uBoundFilterTags freestring = freestring & " " & sqlIdentifier ( TagGroup & TagGroupId ) >> like freestring = freestring & " " & sqlString ( "%" & TagDelim & FilterTags(i) & TagDelim & "%" ) >> and NEXT i END IF >> 1 sql(freestring) END function FUNCTION FilterOnTagOnly ( TagGroup , TagName) dim MustHaveTags , uBoundMustHaveTags , i drop table if exists temp.SubListTemp create table temp.SubListTemp as select rowid as rid , * from master limit 0 > insert into temp.SubListTemp SELECT rowid , * from master where freestring = freestring & " " & sqlIdentifier ( TagGroup & TagGroupId ) >> like freestring = freestring & " " & sqlstring("%" & TagDelim & TagName & TagDelim & "%" ) sql(freestring) dim tmpStr > delete from temp.SubListTemp where tmpStr = " ( (length (§§) - length(replace(§§, § , '' ) ) ) / length (§) ) " tmpStr = replace ( tmpStr , "§§" , sqlidentifier( TagGroup & TagGroupId ) ) freestring = freestring & " " & replace ( tmpStr , "§" , sqlstring(TagDelim) ) >> > 2 sql(freestring) report ( freestring ) insert into temp.SubList select * from temp.SubListTemp END Function FUNCTION FilterOnTagIdentical ( TagGroup ) dim MustHaveTags , uBoundMustHaveTags , i drop table if exists temp.SubListTemp create table temp.SubListTemp as select rowid as rid , * from master limit 0 > insert into temp.SubListTemp SELECT rowid , * from master where sql ( "select " & sqlIdentifier ( TagGroup & TagGroupId ) & " from temp.masterfile limit 1" ) set MustHaveTags = split ( replace ( qrSingleValue , TagDelim , nonPrintableDelim ) , nonPrintableDelim ) uBoundMustHaveTags = uBOuND ( MustHaveTags ) IF ( uBoundMustHaveTags > -1 ) then FOR i = 0 to uBoundMustHaveTags freestring = freestring & " " & sqlIdentifier ( TagGroup & TagGroupId ) >> like freestring = freestring & " " & sqlString ( "%" & TagDelim & MustHaveTags(i) & TagDelim & "%" ) >> and NEXT i END IF >> 1 sql(freestring) dim tmpStr > delete from temp.SubListTemp where tmpStr = " not ( ( length (§§) - length(replace(§§, § , '' ) )/ length ( §) ) " tmpStr = replace ( tmpStr , "§§" , sqlidentifier( TagGroup & TagGroupId ) ) freestring = freestring & " " & replace ( tmpStr , "§" , sqlstring(TagDelim) ) >> = ( select tmpStr = " ( length (§§) - length(replace(§§, § , '' ) )/ length ( §) ) " tmpStr = replace ( tmpStr , "§§" , sqlidentifier( TagGroup & TagGroupId ) ) freestring = freestring & " " & replace ( tmpStr , "§" , sqlstring(TagDelim) ) >> from temp.masterFile limit 1 ) ) >> or >> 0 sql(freestring) report ( freestring ) insert into temp.SubList select * from temp.SubListTemp END Function FUNCTION FilterOnTagsPlus ( ) dim FilterTags , uBoundFilterTags , i > insert into temp.SubList SELECT rowid , * from master where WITHQUERY ( "select distinct ttGroup from tagTable ") sql ( "select " & sqlIdentifier ( wqText ( 1 ) ) & " from temp.masterfile limit 1" ) set FilterTags = split ( replace ( qrSingleValue , TagDelim , nonPrintableDelim ) , nonPrintableDelim ) uBoundFilterTags = uBOuND ( FilterTags ) IF ( uBoundFilterTags > -1 ) then FOR i = 0 to uBoundFilterTags freestring = freestring & " " & sqlIdentifier ( wqText ( 1 ) ) >> like freestring = freestring & " " & sqlString ( "%" & TagDelim &FilterTags(i) & TagDelim & "%" ) >> and NEXT i END IF END withquery >> 1 sql(freestring) END function FUNCTION FilterOnTagsIdentical ( ) dim MustHaveTags , uBoundMustHaveTags , i drop table if exists temp.SubListTemp create table temp.SubListTemp as select rowid as rid , * from master limit 0 > insert into temp.SubListTemp SELECT rowid , * from master where WITHQUERY ( "select distinct ttGroup from tagTable ") sql ( "select " & sqlIdentifier ( wqText ( 1 ) ) & " from temp.masterfile limit 1" ) set MustHaveTags = split ( replace ( qrSingleValue , TagDelim , nonPrintableDelim ) , nonPrintableDelim ) uBoundMustHaveTags = uBOuND ( MustHaveTags ) IF ( uBoundMustHaveTags > -1 ) then FOR i = 0 to uBoundMustHaveTags freestring = freestring & " " & sqlIdentifier ( wqText ( 1 ) ) >> like freestring = freestring & " " & sqlString ( "%" & TagDelim & MustHaveTags (i) & TagDelim & "%" ) >> and NEXT i END IF END withquery >> 1 sql(freestring) dim tmpStr > delete from temp.SubListTemp where WITHQUERY ( "select distinct ttGroup from tagTable ") tmpStr = " not ( ( length (§§) - length(replace(§§, § , '' ) )/ length ( §) ) " tmpStr = replace ( tmpStr , "§§" , sqlidentifier(wqtext(1)) ) freestring = freestring & " " & replace ( tmpStr , "§" , sqlstring(TagDelim) ) >> = ( select tmpStr = " ( length (§§) - length(replace(§§, § , '' ) )/ length ( §) ) " tmpStr = replace ( tmpStr , "§§" , sqlidentifier(wqtext(1)) ) freestring = freestring & " " & replace ( tmpStr , "§" , sqlstring(TagDelim) ) >> from temp.masterFile limit 1 ) ) >> or END withquery >> 0 sql(freestring) report ( freestring ) insert into temp.SubList select * from temp.SubListTemp END Function FUNCTION exportFilter ( actionTxt , optional tagtype = "" , optional TagName = "" ) dim filecount IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if sob ( CommonSobBtnB ,"set","show",0) IF ( tagtype == noTag ) then ErrorReport (" no Tag Group selected" ) EXIT function END IF IF ( TagName == "" ) then IF ( actionTxt == noTag ) then ELSEIF ( actionTxt == "new" ) then ELSEIF ( actionTxt == "statistic" ) then ELSEIF ( actionTxt == "export" ) then ELSEIF ( actionTxt == "lock" ) then ELSEIF ( actionTxt == "+duplicate" ) then ELSEIF ( actionTxt == "+empty" ) then ELSEIF ( actionTxt == "Tags ==" ) then ELSE ErrorReport (" no Tag Name selected" ) EXIT function END IF END IF IF ( actionTxt == "new" ) then MarkRowid = 0 drop table if exists temp.SubList create table temp.SubList as select rowid as rid , * from master limit 0 EXIT function ELSEIF ( actionTxt == "none" ) THEN EXIT function END IF IF ( ! isDBTable ( "temp", "SubList" ) ) THEN EXIT function END IF IF ( actionTxt == "Tags ==" ) then FilterOnTagsIdentical () ELSEIF ( actionTxt == "Tags =+" ) then FilterOnTagsPlus () ELSEIF ( actionTxt == "Tag ==" ) then FilterOnTagIdentical (TagType) ELSEIF ( actionTxt == "Tag =+" ) then FilterOnTagPlus ( TagType) ELSEIF ( actionTxt == "Tag only" ) then FilterOnTagOnly ( TagType , TagName) ELSEIF ( actionTxt == "add ==" ) then > insert into temp.SubList SELECT rowid , * from master where >> $TagType like freestring = freestring & " " & sqlstring ( "%" & TagDelim ) >> || $TagName || freestring = freestring & " " & sqlstring ( TagDelim & "%" ) >> and type like 'file' freestring = replace ( freestring , "$TagType" , sqlIdentifier ( TagType & TagGroupId ) ) freestring = replace ( freestring , "$TagName" , sqlString ( TagName ) ) sql ( freestring ) freestring = "" ELSEIF ( actionTxt == "add <>" ) then > insert into temp.SubList SELECT rowid , * from master where >> not ( $TagType like freestring = freestring & " " & sqlstring ( "%" & TagDelim ) >> || $TagName || freestring = freestring & " " & sqlstring ( TagDelim & "%" ) >> ) and type like 'file' freestring = replace ( freestring , "$TagType" , sqlIdentifier ( TagType & TagGroupId ) ) freestring = replace ( freestring , "$TagName" , sqlString ( TagName ) ) sql ( freestring ) freestring = "" ELSEIF ( actionTxt == "add any" ) then > insert into temp.SubList SELECT rowid , * from master where >> not ( $TagType is null or $TagType in ( '' , freestring = freestring & " " & sqlstring ( TagDelim ) >> , freestring = freestring & " " & sqlstring ( TagDelim & TagDelim ) >> ) ) >> and type like 'file' freestring = replace ( freestring , "$TagType" , sqlIdentifier ( TagType & TagGroupId ) ) sql ( freestring ) ELSEIF ( actionTxt == "add ==?" ) then > insert into temp.SubList SELECT rowid , * from master where >> $TagType like '%' || $TagName || '%' >> and type like 'file' freestring = replace ( freestring , "$TagType" , sqlIdentifier ( TagType & TagGroupId ) ) freestring = replace ( freestring , "$TagName" , sqlString ( TagName ) ) sql ( freestring ) ELSEIF ( actionTxt == "add <>?" ) then > insert into temp.SubList SELECT rowid , * from master where >> not $TagType like '%' || $TagName || '%' >> and type like 'file' freestring = replace ( freestring , "$TagType" , sqlIdentifier ( TagType & TagGroupId ) ) freestring = replace ( freestring , "$TagName" , sqlString ( TagName ) ) sql ( freestring ) ELSEIF ( actionTxt == "add non" ) then > insert into temp.SubList SELECT rowid , * from master where >> ( $TagType is null or $TagType in ( '' , freestring = freestring & " " & sqlstring ( TagDelim ) >> , freestring = freestring & " " & sqlstring ( TagDelim & TagDelim ) >> ) ) >> and type like 'file' freestring = replace ( freestring , "$TagType" , sqlIdentifier ( TagType & TagGroupId ) ) sql ( freestring ) freestring = "" ELSEIF ( actionTxt == "remove <>" ) then > delete from temp.SubList where >> not $TagType like freestring = freestring & " " & sqlString ( "%" & TagDelim ) >> || $TagName || freestring = freestring & " " & sqlString ( TagDelim & "%" ) >> and rowid > $mark freestring = replace ( freestring , "$TagType" , sqlIdentifier ( TagType & TagGroupId ) ) freestring = replace ( freestring , "$TagName" , sqlString ( TagName ) ) freestring = replace ( freestring , "$mark" , cstr(MarkRowid ) ) sql ( freestring ) ELSEIF ( actionTxt == "remove ==" ) then > delete from temp.SubList where >> $TagType like freestring = freestring & " " & sqlString ( "%" & TagDelim ) >> || $TagName || freestring = freestring & " " & sqlString ( TagDelim & "%" ) >> and rowid > $mark freestring = replace ( freestring , "$TagType" , sqlIdentifier ( TagType & TagGroupId ) ) freestring = replace ( freestring , "$TagName" , sqlString ( TagName ) ) freestring = replace ( freestring , "$mark" , cstr(MarkRowid ) ) sql ( freestring ) ELSEIF ( actionTxt == "remove non" ) then > delete from temp.SubList where >> ( $TagType is null or $TagType in ( '' , freestring = freestring & " " & sqlstring ( TagDelim ) >> , freestring = freestring & " " & sqlstring ( TagDelim & TagDelim ) >> ) ) >> and rowid > $mark freestring = replace ( freestring , "$TagType" , sqlIdentifier ( TagType & TagGroupId ) ) freestring = replace ( freestring , "$TagName" , sqlString ( TagName ) ) freestring = replace ( freestring , "$mark" , cstr(MarkRowid ) ) sql ( freestring ) ELSEIF ( actionTxt == "remove any" ) then > delete from temp.SubList where >> not ( $TagType is null or $TagType in ( '' , freestring = freestring & " " & sqlstring ( TagDelim ) >> , freestring = freestring & " " & sqlstring ( TagDelim & TagDelim ) >> ) ) >> and rowid > $mark freestring = replace ( freestring , "$TagType" , sqlIdentifier ( TagType & TagGroupId ) ) freestring = replace ( freestring , "$TagName" , sqlString ( TagName ) ) freestring = replace ( freestring , "$mark" , cstr(MarkRowid ) ) sql ( freestring ) ELSEIF ( actionTxt == "remove <>?" ) then > delete from temp.SubList where >> not ( $TagType like '%' || $TagName || '%' ) >> and rowid > $mark freestring = replace ( freestring , "$TagType" , sqlIdentifier ( TagType & TagGroupId ) ) freestring = replace ( freestring , "$TagName" , sqlString ( TagName ) ) freestring = replace ( freestring , "$mark" , cstr(MarkRowid ) ) sql ( freestring ) ELSEIF ( actionTxt == "remove ==?" ) then > delete from temp.SubList where >> ( $TagType like '%' || $TagName || '%' ) >> and rowid > $mark freestring = replace ( freestring , "$TagType" , sqlIdentifier ( TagType & TagGroupId ) ) freestring = replace ( freestring , "$TagName" , sqlString ( TagName ) ) freestring = replace ( freestring , "$mark" , cstr(MarkRowid ) ) sql ( freestring ) ELSEIF ( actionTxt == "export" ) then dim TargetPath = TagName IF ( targetPath = "\\" ) then sob ( CommonSobResult, "ADD" , "row" , "Cancelled by user" & EOL ) EXIT function END IF IF ( ! isFile ( targetPath) ) then sob ( CommonSobResult, "ADD" , "row" , "Destination folder does not exist: " & EOL & TargetPath & EOL ) EXIT function ELSE sob ( CommonSobResult, "ADD" , "row" , "Destination folder: " & EOL & TargetPath & EOL ) END IF ExportFolder = TargetPath dim dstPoint dim fso = createobject ("Scripting.FileSystemObject") IF ( ! fso ) THEN sob ( CommonSobResult , "Add" , "row", "Could not create the File System Object " & EOL ) EXIT function END IF cancelClear () cancelText( "Exporting Files" , 1 ) SELECT count (*) from temp.SubList where type = 'file' fileCount = qrSingleValue sob ( CommonSobResult , "Add" , "row", "Nr files to export: " & fileCount & EOL ) cancelText( "Files to export: " & FileCount , 2 ) cancelShow(1) TRY fso.DeleteFile ( TargetPath & "E0*" ) CATCH END try WITHQUERY ( " select path , name , rid from temp.SubList where type = 'file' ") dstPoint = TargetPath & "E" & Right ( "0000000000" & wqtext(3) , 7 ) & "_" & wqtext(2) TRY fso.copyFile ( wqtext(1) & wqtext(2) , dstPoint ) CATCH IF ( isFile (wqtext(1) & wqtext(2)) ) then report ( "copy file failure: " & EOL & wqtext(1) & wqtext(2) & EOL & dstPoint & EOL ) END IF END try cancelToggle () fileCount-- IF ( fileCount BAND 0x0f ) then ELSE cancelText( "Files to export: " & FileCount , 2 ) END IF END withquery cancelShow(0) sob ( CommonSobBtnB , "SET" , "TITLE", "Open Export folder" ) sob ( CommonSobBtnB , "ON" , "PRESS", "CbOpenExportFolder") TargetFolder = ExportFolder sob ( CommonSobBtnB ,"set","show",1) ELSEIF ( actionTxt == "statistic" ) then SELECT count (*) from temp.SubList exportFilter = qrsingleValue ELSEIF ( actionTxt == "lock" ) then SELECT max (rowid) from temp.SubList MarkRowid = qrsingleValue ELSEIF ( actionTxt == "+duplicate" ) then > insert into temp.SubList SELECT rowid ,* from master where type like 'file' and >> hash in ( select hash from master group by hash having count (*)> 1 ) sql ( freestring ) ELSEIF ( actionTxt == "+empty" ) then > insert into temp.SubList SELECT rowid ,* from master where type like 'file' and >> size = 0 sql ( freestring ) ELSEIF ( actionTxt == "like" ) then > insert into temp.SubList SELECT rowid ,* from master where type like 'file' and freestring = freestring & " " & sqlIdentifier ( tagtype ) >> like freestring = freestring & " " & sqlstring ( TagName ) sql ( freestring ) ELSEIF ( actionTxt == "-like" ) then > delete from temp.SubList where type like 'file' and freestring = freestring & " " & sqlIdentifier ( tagtype ) >> like freestring = freestring & " " & sqlstring ( TagName ) sql ( freestring ) END IF IF ( isDBTable ( "temp" , "SubList" ) ) THEN removeDuplicates ( "temp" , "SubList" ) END if END FUNCTION FUNCTION clearAllFolderTags () WITHQUERY ( "select name from temp.masterColumns" ) > update master SET freestring = freestring & " " & sqlidentifier ( wqText(1) ) >> = "" >> where type = 'folder' sql ( freestring ) END withquery END function FUNCTION normaliseTags () WITHQUERY ( "select name from temp.masterColumns" ) > update master SET freestring = freestring & " " & sqlidentifier ( wqText(1) ) >> = freestring = freestring & " " & sqlstring ( TagDelim ) >> where type = 'file' and ( freestring = freestring & " " & sqlidentifier ( wqText(1) ) >> is NULL >> or freestring = freestring & " " & sqlidentifier ( wqText(1) ) >> = freestring = freestring & " " & sqlstring ( TagDelim & TagDelim) >> or freestring = freestring & " " & sqlidentifier ( wqText(1) ) >> = "" >> ) report ( freestring ) END withquery END function FUNCTION upDateMaster () ' delete the ORIGINAL changed file ' Note: changed means that the hash has changed update master set rescanstate = 'old' where not type like 'folder' update Master set key = path || name update temp.changedFiles set key = path || name delete from master where key in ( select key from temp.changedFiles ) ' now we can copy the changed to MASTER update temp.changedFiles set rescanstate = 'changed' where not type like 'folder' insert into master select * from temp.changedFiles ' -------------------------- ' give NEW files META data from MASTER ' since we are not doing any DROP or CREATE commands, we can use a WITHQUERY ' for each META column, copy the original META data across WITHQUERY ( "select name from temp.masterColumns" ) > update temp.newFiles SET >> $col = frmTbl. $col >> FROM >> ( select $col , hash from master ) as frmTbl >> WHERE >> newFiles.hash = frmTbl.hash freestring = replace ( freestring , "$col" , sqlIdentifier(wqText(1)) ) sql ( freestring ) END withquery ' now we can copy the NEW to MASTER update temp.newFiles set rescanstate = 'new' where not type like 'folder' insert into master select * from temp.newFiles ' -------------------------- ' NOW and only NOW, we can remove the LOST files from MASTER ' WHY? in case their HASH was also in the NEW files list ' so we needed their META info to copy across! update temp.lostFiles set key = path || name update master set key = path || name delete from master where key in ( select key from temp.lostFiles ) update master set key = hash || path || name END FUNCTION ' ---------------------------------------------------------- ' functions to manage the set of COMBO boxes ' that offer the user a choice of TagGroup ' ---------------------------------------------------------- FUNCTION isTagTypeCombo( sobid ) TagTypeComboArray (TagTypeComboIndex++ ) = sobid END Function FUNCTION updateTagTypeCombos ( ) ' we have a LIST of all COMBOs that offer the user a choice of ' TagGroup ' this code will update those COMBOs so that they all offer the ' full range of available TAG GROUPS dim i = 0 , j , sobId IF ( ! tagFlag ) then EXIT function END IF IF ( isDBopen ) then DO while i < TagTypeComboIndex sobId = TagTypeComboArray (i) sob( sobId , "empty" ) sob( sobid , "add" , "row" , NoTag ) FOR j = 0 to uBoundTagArray sob( sobid , "add" , "row" , right (tagArray(j) , -3)) NEXT j sob( sobId , "SET", "SELECTION" , "ROW" , 1 ) i++ LOOP ELSE DO while i < TagTypeComboIndex sobId = TagTypeComboArray (i) sob( sobId , "empty" ) sob( sobid , "add" , "row" , NoTag) i++ LOOP END IF END Function ' -------------------------- FUNCTION resetTagBoxes () ' we have a LIST of all COMBOs that offer the user as choice of ' TagGroup ' this code will update those COMBOs so that they all offer the ' DEFAULT TAG GROUP row = 1 fo the COMBO dim i = 0 dim sobid DO while i < TagTypeComboIndex sobid = TagTypeComboArray (i) sob ( sobid , "set" , "selection", "row" , 1 ) i++ LOOP END FUNCTION ' -------------------------- ' as a CONVENIENCE we can save/restore across a DB save/open ' the state of those COMBO boxes ' that offer the user a choice of TagGroup ' The saving/recovery DB part is found under the functions ' saveEnvironment ' restoreEnvironment FUNCTION isSavedTagTypeCombo( sobid ) ' records the target SOBID as one to be saved/restored SavedTagTypeComboArray (SavedTagTypeComboIndex++ ) = sobid END Function FUNCTION resetSavedBoxes () ' writes the saved environment back to the various COMBOs.. ' BEWARNED: ' hardcoded Screen Object SIBLING sequence ' Action Combo -1 ' Tag Group Combo 0 ' Filter edit field 1 ' Tag Name Combo 2 ' Action Button 3 dim i = 0 dim sib , sobid , txt DO while i < SavedTagTypeComboIndex sobid = SavedTagTypeComboArray (i) sib = SOB ( sobid , "sibling" , -1 ) txt = sob ( sib , "set", "title" , "#################" ) sob ( sib , "set" , "selection", "row" , 1 ) txt = sob ( sib , "get", "title" ) sib = SOB ( sobid , "sibling" , 0 ) sob ( sib , "set" , "selection", "row" , 1 ) sib = SOB ( sobid , "sibling" , 1 ) sob ( sib , "empty" ) sib = SOB ( sobid , "sibling" , 2 ) sob ( sib , "empty" ) sib = SOB ( sobid , "sibling" , 3 ) sob ( sib , "set" , "title" , txt ) sob ( sib , "set" , "rgb" , defBtnCol ) ' CB_action_select (tbid , 1) i++ LOOP END FUNCTION ' ---------------------------------------------------------- ' ' ' ---------------------------------------------------------- dim defaultText = " " dim Currenthash = "" dim CurrentFile = "" dim CurrentRowid = 0 ' ------------------------------------------------------------------------------------------- FUNCTION ExtractTags ( frmTbl , frmCol ) ' do NOT call from inside a WITHQUERY ' a WITHQUERY blocks the DB from doing a CREATE or a DROP drop table if exists temp.ExtractTbl > create temp table ExtractTbl as select $frmCol as haveTag from $frmTbl freestring = replace ( freestring , "$frmTbl" , sqlIdentifier(frmTbl) ) freestring = replace ( freestring , "$frmCol" , sqlIdentifier(frmCol)) sql ( freestring ) drop table if exists temp.ResultTbl > create temp table ResultTbl as >> with recursive list ( element , remainder ) as >> ( >> select distinct null as element , haveTag as remainder from ExtractTbl where not ( haveTag = '' or haveTag = freestring = freestring & " " & SQLstring ( TagDelim) >> ) >> UNION ALL >> SELECT >> CASE '>> WHEN INSTR( remainder, '§' )>0 THEN >> WHEN INSTR( remainder, freestring = freestring & " " & SQLstring ( TagDelim) >> )>0 THEN ' >> SUBSTR( remainder, 0, INSTR( remainder, '§' ) ) >> SUBSTR( remainder, 0, INSTR( remainder, freestring = freestring & " " & SQLstring ( TagDelim) >> ) ) >> ELSE >> remainder >> END AS element, >> CASE '>> WHEN INSTR( remainder, '§' )>0 THEN >> WHEN INSTR( remainder, freestring = freestring & " " & SQLstring ( TagDelim) >> ) > 0 then '>> SUBSTR( remainder, INSTR( remainder, '§' )+1 ) >> SUBSTR( remainder, INSTR( remainder, freestring = freestring & " " & SQLstring ( TagDelim) >> ) + freestring = freestring & " " & length ( TagDelim) >> ) >> ELSE >> NULL >> END AS remainder >> FROM list >> WHERE remainder IS NOT NULL >> ) >> SELECT distinct element as haveTag FROM list WHERE element IS NOT NULL and not element = '' sql ( freestring ) drop table if exists temp.ExtractTbl END Function FUNCTION updateFreeTextCombos ( ) dim i sob ( FreeTextComboSob , "EMPTY") sob ( FreeTextToolCombo , "EMPTY") IF ( ! isDBopen ) then FreeTextFlag = 0 IF ( FreeTextFlag ) then sob ( FreeTextComboSob , "add" , "row" , " all" ) FOR i = 0 to uBoundTagTxtArray sob ( FreeTextComboSob , "add" , "row" , right(tagTxtArray(i), -1*length(FreeTxtId)) ) sob ( FreeTextToolCombo , "add" , "row" , right(tagTxtArray(i), -1*length(FreeTxtId)) ) NEXT i sob ( FreeTextToolCombo , "SET" , "Selection", "row", 1 ) IF ( uBoundTagTxtArray == 0 ) then sob ( FreeTextComboSob , "SET" , "Selection", "row", 2 ) ELSE sob ( FreeTextComboSob , "SET" , "Selection", "row", 1 ) END IF ELSE sob ( FreeTextComboSob , "add" , "row" , NoTag ) sob ( FreeTextComboSob , "SET" , "Selection", "row", 1 ) sob ( FreeTextToolCombo , "add" , "row" , NoTag ) sob ( FreeTextToolCombo , "SET" , "Selection", "row", 1 ) END IF END FUNCTION FUNCTION CreateUsersTagTableAndColumnsList () ' the idea is that MasterColumns lists the USER defined columns ' and that tagTable lists the USER defined values that ' can be assigned to USER defined columns ' these two tables are created / modified / deleted via the ' menu Tag Tools / Tag Management ' and via the Free Text Fields panel ' and when providing contents for REGIONS ' want to create a table, temp.MasterColumns of the USER defined columns ' i.e. those of the form ' '%tag' and '%TagTxt' Drop table if exists temp.masterColumns create temp table masterColumns as select name , 1 as flag from pragma_table_info ( 'master' ) where name like '%tag' or name like '%TagTxt' ' ----------------------------------- ' handle the Free text fields SELECT group_concat ( name) from temp.mastercolumns where name like '%tagTxt' order by name asc IF ( (qrsingleValue == "") OR (istype(qrsingleValue) == "empty") OR ( qrsingleValue == 0) ) THEN FreeTextFlag = 0 IF ( sob ( FreeTextPopUpid , "GET" , "CHECK" ) ) then sob ( FreeTextPopUpid , "SET" , "CHECK" , 0 ) sob ( RegionPopUpid , "SET" , "CHECK" , 1 ) END IF sob ( FreeTextPopUpid , "SET" , "MENU.GREY", 1 ) ELSE set TagTxtArray = split ( qrsingleValue , "," ) uBoundTagTxtArray = uBound ( TagTxtArray ) FreeTextFlag = 1 sob ( FreeTextPopUpid , "SET" , "MENU.GREY", 0 ) END IF dim i updateFreeTextCombos () ' ----------------------------------- ' handle the Tags SELECT group_concat ( name) from temp.mastercolumns where name like '%tag' order by name asc IF ( (qrsingleValue == "") OR (istype(qrsingleValue) == "empty") OR ( qrsingleValue == 0) ) THEN tagFlag = 0 ELSE set TagArray = split ( qrsingleValue , "," ) uBoundTagArray = uBound ( TagArray ) tagFlag = 1 END IF drop table if exists tagTable create table tagTable ( ttGroup , ttName , ttComment ) IF ( tagFlag ) then FOR i = 0 to uBoundTagArray ExtractTags ( "master", tagArray (i)) > insert into tagTable select $tagName, haveTag, '' from ResultTbl freestring = replace ( freestring, "$tagName" , sqlIdentifier ( tagArray (i) ) ) sql (freestring ) NEXT i END IF insert into tagTable select 'TaggedTag', substr ( name ,1, length(name) - 3 ) , '' from mastercolumns ' remove duplicates > delete from tagTable >> WHERE exists ( >> select 1 from tagTable as TT2 >> where tagTable.ttGroup = tt2.ttGroup >> and tagTable.ttName = tt2.ttName >> and tagTable.rowid > tt2.rowid >> ) sql (freestring) END function ' ----------------------------------------------------- ' create a window to contain some SOBs dim appWindow = sob ("application", "NEW", appName) ' this is IMPORTANT ' always always always place POPUP definitions right after their ' parent WINDOW dim CopyPastePopUp = sob ( appWindow, "add", "Menu", "popup") dim OnImagePopUpSimilar = sob ( appWindow, "add", "Menu", "popup") dim OnImagePopUpGroup = sob ( appWindow, "add", "Menu", "popup") dim OnImageMngmtPopUp = sob ( appWindow, "add", "Menu", "popup") ' to prevent screen flashing whilst we define all of the SOBS ' hide the application windows sob ( appWindow,"SET" ,"SHOW",0) ' Define a callback to handle a click on the TOP Right "X" FUNCTION cbCloseApplication ( sobid ) IF ( isDbOPen ) then IF ( ! MSGBOX ( "Close without saving database?", 1, "Warning") ) then CBFileMenuClose (sobid) quit END IF ELSE quit END IF END function sob ( appWindow,"ON","CLICK", "cbCloseApplication") ' ----------------------------------------------------- dim myMenuBar = sob (appWindow , "add" , "menu" , "bar" ) ' add a menu bar myWindow ' ----------------------------------------------------- ' add horizonal menus to myMenuBar ' ----------------------------------------------------- dim myMenuFile = sob (myMenuBar , "add" , "menu" , "Horizontal" , "File" ) dim OptionsMenu = sob (myMenuBar , "add" , "menu" , "Horizontal" , "Options" ) dim myMenuTools = sob (myMenuBar , "add" , "menu" , "Horizontal" , "Tag Tools" ) dim myMenuExport = sob (myMenuBar , "add" , "menu" , "Horizontal" , "Export" ) dim myMenuImport = sob (myMenuBar , "add" , "menu" , "Horizontal" , "Import" ) dim myMenuDelete = sob (myMenuBar , "add" , "menu" , "Horizontal" , "Delete" ) ' ----------------------------------------------------- ' CALLBACK when the OPTIONS menu is ABOUT to be displayed. ' This is the only CALLBACK implemented for a Horizontal MENU ' This is to ensure that the menu option Expert Mode always ' has the correct check state. The Expert window is automatically ' displayed if there is some programming error. dim ExpertFlag = 0 FUNCTION ClickExpertMode ( sobid ) ExpertFlag = sob( pwsWindow, "GET", "SHOW") sob(optionsMenuExpert, "SET", "CHECK" ,ExpertFlag) END function sob ( OptionsMenu , "ON" , "PRESS" , "ClickExpertMode" ) ' -------------- dim haveWIA = 0 dim haveShell = 0 FUNCTION getHaves ( ) haveWIA = 0 haveShell = 0 dim objIF = CreateObject("WIA.ImageFile") IF ( istype(objIF) == "DISPATCH" ) then haveWIA = 1 dim objV = CreateObject("WIA.Vector") dim objShell = CreateObject ("Shell.Application") IF ( istype(objShell) == "DISPATCH") then haveShell = 1 ' the termination of a function automatically release the ' locally defined DISPATCHs END FUNCTION getHaves ( ) dim myMenuSingleFile = 0 IF ( haveWIA + haveShell) then myMenuSingleFile = sob (myMenuBar , "add" , "menu" , "Horizontal" , "Single File Options" ) END IF ' -------------- dim myMenuImageMngmt = sob (myMenuBar , "add" , "menu" , "Horizontal" , "Image Management" ) dim myMenuRegions = sob (myMenuBar , "add" , "menu" , "Horizontal" , "Regions" ) dim myMenuHelp = sob (myMenuBar , "add" , "menu" , "Horizontal" , "Help" ) dim myMenuAbout = sob (myMenuBar , "add" , "menu" , "Horizontal" , "About" ) ' ----------------------------------------------------- ' add menu items to the FILE menu ' each meni item has 3 parts ' add the item in to its parent menu ' define a call back action for the new item ' link the new item to the call back ' Note: callbacks can be shared with other menu items ' -------------- ' ----------------------------------------------------- ' ##################################################### ' NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW NEW ' ----------------------------------------------------- dim idMenuFileNew = sob ( myMenuFile , "add" , "menu" , "Vertical" , "New" ) FUNCTION CBFileMenuNewORIG ( sobid ) sob ( CommonSobResult , "empty") IF ( isDBopen ) then ErrorReport ( "Database already open!") toggleWarnColour ( sobid) EXIT function END IF dim TargetPath = GetPath & "\\" IF ( targetPath = "\\" ) then ErrorReport ( "Cancelled by user" ) toggleWarnColour ( sobid) EXIT function END IF dbopen( "memory" ) IF ( isDBopen ) then cancelClear () cancelText( "Reading in new folder" , 0 ) cancelText( "This can take a short while" , 3 ) cancelShow(1) cancelText( TargetPath , 1 ) ' -------------------------------------------------------------------------- ' get number of files in the target so that the user can follow the progress of ' the slower dbFileList with checksum generation ' -------------------------------------------------------------------------- dbFileList ( , "master", TargetPath , -1 ) SELECT count (*) from master where type like 'file' cancelText( "Total files " & qrsinglevalue , 2 ) ' -------------------------------------------------------------------------- ' create a table of the target contents ' -------------------------------------------------------------------------- sob ( CommonSobResult , "ADD" , "ROW", "Start: " & time & EOL ) TRY dbFileList ( , "master", TargetPath , -1 , , "md5" ) CATCH sob ( CommonSobResult , "ADD" , "ROW", "WARNING: Action cancelled by User. File scan incomplete." & EOL ) END TRY sob ( CommonSobResult , "ADD" , "ROW", "Stop: " & time & EOL ) ' -------------------------------------------------------------------------- update master set key = hash || path || name sob ( CommonSobResult , "ADD" , "ROW", "Root folder: " & TargetPath & EOL ) SELECT count (*) from master where type like 'file' sob ( CommonSobResult , "ADD" , "ROW", "Files read : " & qrsingleValue & EOL ) sob ( CommonSobResult , "ADD" , "ROW", "Next action: " & "Create Tag Group(s) and Tags!"& EOL ) sob ( statusDBFile, "SET" ,"TITLE" , "memory" ) NoErrorReport ("Open OK " ) ' -------------------------------------------------------------------------- ' we need some extra DEFAULT columns that dbFileList does not provide ' -------------------------------------------------------------------------- IF ( ! isDBcolumn ( "master", "fsHash" ) ) then alter table master add column fsHash update master set fsHash = hash END IF IF ( allowRectPicasa ) then alter table master add column RectPicasa text update master set RectPicasa = "" END if IF ( allowRectMeta ) then alter table master add column RectMeta text update master set RectMeta = "" END IF alter table master add column IgnoreFile update master set IgnoreFile = 0 alter table master add column RescanState update master set RescanState = "new" alter table master add column TaggedTag update master set TaggedTag = "" ' ----------------------------------------------------------------------------------------- ' add the IMAGE related columns alter table master add column pHash alter table master add column dHash alter table master add column ImageGroup alter table master add column ImageGroupType alter table master add column ImageNearness ' ----------------------------------------------------------------------------------------- ' the following is SLOW ' get the perceptual hash and difference hash for the image files ' ----------------------------------------------------------------------------------------- > select count (*) from master where ext in freestring = freestring & ImageFileExtensions sql (freestring ) dim imgCnt = qrSingleValue cancelText( "Get pHash / dHash from: " & imgCnt & " image files" , 1 ) dim pHash = 0 , dHash > select path||name , fsHash from master where ext in freestring = freestring & ImageFileExtensions WITHQUERY (freestring ) phash = canvas("new", "phash.from.file", wqText(1) , byref dHash ) > update master set >> pHash = freestring = freestring & " " & pHash >> , dHash = freestring = freestring & " " & dHash >> , ImageGroup = 0 , IgnoreFile = 0 , ImageGroupType = 'unknown' , ImageNearness = '0000' >> where fsHash = freestring = freestring & " " & sqlidentifier (wqText (2)) sql(freestring) imgCnt-- IF ( ! ( imgCnt BAND 0x0f ) ) THEN cancelText( imgCnt , 2 ) END IF END WithQuery > update master >> set >> ImageGroup = num2Hex(pHash) || num2Hex(dHash) >> , hash = num2Hex(pHash) || num2Hex(dHash) >> where >> not pHash = 0 sql(freestring) drop table if exists temp.xHashTbl MakeBkTree ( ) ' ----------------------------------------------------------------------------------------- cancelShow(0) sob ( sobid , "SET" , "SHOW", 0 ) sob ( CommonSobBtnB , "SET" , "TITLE", "Open root folder" ) sob ( CommonSobBtnB , "SET" , "SHOW", 1 ) TargetFolder = TargetPath sob ( CommonSobBtnB , "ON" , "PRESS", "CbOpenExportFolder") OnOpenPrepareMasterTagTable ( CommonSobBtnB ) ' NEW database OpenMessage ( ) ELSE sob ( CommonSobResult , "ADD" , "ROW", "Open failed on file : " & EOL & dbName & EOL)) ErrorReport ("Open failed on file : " & dbName ) toggleWarnColour ( sobid) END IF END Function FUNCTION CBFileMenuNew ( sobid ) CommonSobClear ( "Create new database" ) > Use this to select a root folder.§ >> All of the root folder's file & subfolders will be scanned and an entry made in a new database.§ >> The use the menu Tools / Tag management to add new TAG GROUPS.§ >> - each tag group will have its own column in the database§ >> - Typically, for images, I have TAG GROUPS: who , what , when , where§ >> Then add specific tags to the TAG GROUPS. (same menu option) § >> - e.g. for the WHO tag group: MUM , DAD , King Arthur , Jane Doe (nee Smith), ... § >> You can add new TAG GROUPS, and TAGs at any time to a database§ >> You can add more folders at any time to a database. sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn , "SET" , "TITLE" , "Create") sob ( CommonSobBtn , "ON" , "PRESS", "CBFileMenuNewORIG") sob ( CommonSobBtn , "SET" , "rgb" , rgb(0,255,0) ) sob ( CommonSobBtn ,"set","show",1) sob ( CommonSobResult , "empty") sob ( CommonOvl ,"set","show",1) END Function sob ( -1 , "ON" , "CLICK" , "CBFileMenuNew") ' ----------------------------------------------------- ' ##################################################### ' SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE SAVE ' ----------------------------------------------------- FUNCTION restoreEnvironment () IF ( ! isDBTable ( , "savedEnv" ) ) then EXIT function END IF dim rid , i , sib , sobid , txt , row i = 0 WITHQUERY ( "select * from savedEnv " ) sobid = SavedTagTypeComboArray(i) IF ( sobid == wqInt ( 1 ) ) then ' set the filter text sib = sob ( SavedTagTypeComboArray(i) , "SIBLING" , 1 ) sob ( sib , "Set", "Title" , wqText(4) ) ' action sib = sob ( SavedTagTypeComboArray(i) , "SIBLING" , -1 ) row = wqInt(2) IF ( ! row ) then row = 1 sob ( sib , "Set", "SELECTION" , "ROW" , row ) sob ( sib , "trigger" ) ' group sib = sob ( SavedTagTypeComboArray(i) , "SIBLING" , 0 ) row = wqInt(3) IF ( ! row ) then row = 1 sob ( sib , "Set", "SELECTION" , "ROW" , row ) sob ( sib , "trigger" ) ELSE END If i = i + 1 END withQuery END function FUNCTION saveEnvironment () IF ( ! isDBTable ( , "savedEnv" ) ) then create table savedEnv ( sobid int , TagAction int , TagGroup int , TagFilter text ) END IF delete from savedEnv dim txt , i , sib , rid FOR i = 0 to SavedTagTypeComboIndex - 1 > insert into savedEnv values ( freestring = freestring & " " & SavedTagTypeComboArray(i) >> , sib = sob ( SavedTagTypeComboArray(i) , "SIBLING" , -1 ) rid = sob ( sib , "get", "Selection" , "row" ) freestring = freestring & " " & rid >> , sib = sob ( SavedTagTypeComboArray(i) , "SIBLING" , 0 ) rid = sob ( sib , "get", "Selection" , "row" ) freestring = freestring & " " & rid >> , sib = sob ( SavedTagTypeComboArray(i) , "SIBLING" , 1 ) txt = sob ( sib , "get", "title" ) freestring = freestring & " " & sqlString (txt) >> ) sql ( freestring ) NEXT i END function sob ( myMenuFile , "add" , "menu" , "SPACER" ) dim idMenuFileSave = sob ( myMenuFile , "add" , "menu" , "Vertical" , "Save" ) FUNCTION CBFileMenuSave ( sobid ) ' following is simply to minimise the disk space update master set key = '' saveEnvironment () vacuum dbSave () ' recover the key contents update master set key = hash || path || name END Function sob ( -1 , "ON" , "CLICK" , "CBFileMenuSave") dim idMenuFileSaveNClose = sob ( myMenuFile , "add" , "menu" , "Vertical" , "Save & Close" ) dim idMenuFileSaveNClosenExit = sob ( myMenuFile , "add" , "menu" , "Vertical" , "Save & Close & Exit" ) ' ----------------------------------------------------- ' ##################################################### ' SAVEAS SAVEAS SAVEAS SAVEAS SAVEAS SAVEAS SAVEAS ' ----------------------------------------------------- dim idMenuFileSaveAs = sob ( myMenuFile , "add" , "menu" , "Vertical" , "SaveAs" ) FUNCTION CBFileMenuSaveAS ( sobid ) ' following is simply to minimise the disk space update master set key = '' saveEnvironment () vacuum dim dbFIle , defDir TRY getFirstFileDir () dbFIle = DBSaveAs ( ) IF ( dbFIle == "" ) then noErrorReport ("SaveAs cancelled" ) ELSE IF ( toUpper ( right(dbFIle,4)) == ".DB3" ) then ELSE dbFIle = dbFIle & ".db3" END IF sob ( statusDBFile, "SET" ,"TITLE" , dbFIle ) noErrorReport ("Saved as " & dbFIle ) AddFileToUsedList ( dbFIle , "rw" ) END IF CATCH ErrorReport ("SaveAs failed " & ErrorText(CaughtError) ) END try ' recover the key contents update master set key = hash || path || name END Function sob ( -1 , "ON" , "CLICK" , "CBFileMenuSaveAs") ' ----------------------------------------------------- ' ##################################################### ' SAVECOPY SAVECOPY SAVECOPY SAVECOPY SAVECOPY SAVECOPY ' ----------------------------------------------------- dim idMenuFileSaveCopyAs = sob ( myMenuFile , "add" , "menu" , "Vertical" , "SaveCopyAs" ) FUNCTION CBFileMenuSaveCopyAs ( sobid ) ' following is simply to minimise the disk space update master set key = '' vacuum TRY noErrorReport ("Saved copy " & dbSaveCopy ( ,1 )) CATCH ErrorReport ("SaveAs failed " & ErrorText(CaughtError) ) END try ' recover the key contents update master set key = hash || path || name END Function sob ( -1 , "ON" , "CLICK" , "CBFileMenuSaveCopyAs") ' ----------------------------------------------------- ' ##################################################### ' OPEN OPEN OPEN OPEN OPEN OPEN OPEN OPEN OPEN OPEN ' ----------------------------------------------------- dim idMenuFileOpen =sob ( myMenuFile , "add" , "menu" , "Vertical" , "Open" ) FUNCTION dbValidityCheck () dbValidityCheck = 0 IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) ELSEIF ( ! isDBtable ( , "tagTable" ) ) then ErrorReport ("Invalid database: No TagTable." ) ELSEIF ( ! isDBcolumn ( "tagTable", "ttComment" ) ) then ErrorReport ("Invalid TagTable: No TagTable." ) ELSEIF ( ! isDBtable ( , "master" ) ) then ErrorReport ("Invalid database: No Master table." ) ELSE dbValidityCheck = 1 EXIT function END IF dbclose () END FUNCTION FUNCTION OnOpenPrepareMasterTagTable ( sobid ) ' called when we open a NEW or SAVED database IF ( isDBtable ( , "tagTable" ) ) then create temp table TagTableOld as select * from TagTable END IF exportFolder = "" IF ( isDBcolumn ( "master", "ImageGroup" ) ) then update master set Hash = ImageGroup where not ImageGroup = 0 END IF CreateUsersTagTableAndColumnsList () IF ( isDBtable ( "temp" , "TagTableOld" ) ) then > update tagTable >> set ttComment = frmTbl.ttcomment >> from ( select * from temp.TagTableOld ) as frmTbl >> WHERE >> tagTable.ttGroup = frmTbl.ttGroup >> AND tagTable.ttName = frmTbl.ttName sql ( freestring ) END IF ' resetTagBoxes () ' irrelevant since updateTagTypeCombos does it too updateTagTypeCombos ( ) haveRectMeta = isDBcolumn ( "master" , "RectMeta" ) haveRectPicasa = isDBcolumn ( "master" , "RectPicasa" ) IF ( ! ( haveRectMeta or haveRectPicasa ) ) then sob ( copyPopupId , "SET" , "MENU.GREY" , 1 ) sob ( pastePopupId , "SET" , "MENU.GREY" , 1 ) sob ( DeletePopUpid , "SET" , "MENU.GREY" , 1 ) sob ( RegionPopUpid , "SET" , "MENU.GREY" , 1 ) sob ( myMenuRegions , "SET" , "MENU.GREY" , 1 ) ELSE sob ( copyPopupId , "SET" , "MENU.GREY" , 0 ) sob ( pastePopupId , "SET" , "MENU.GREY" , 1 ) sob ( DeletePopUpid , "SET" , "MENU.GREY" , 1 ) sob ( RegionPopUpid , "SET" , "MENU.GREY" , 0 ) sob ( myMenuRegions , "SET" , "MENU.GREY" , 0 ) END if IF ( ! isDBcolumn ( "master" , "RescanState" ) ) then alter table master add column rescanState update master set rescanstate = 'new' where not type like 'folder' END IF SOB (idMenuFileNew , "SET" , "MENU.GREY" ,1 ) SOB (idMenuFileSave , "SET" , "MENU.GREY" ,0) SOB (idMenuFileSaveAs, "SET" , "MENU.GREY" ,0 ) SOB (idMenuFileSaveCopyAs, "SET" , "MENU.GREY" ,0 ) SOB (idMenuFileOpen , "SET" , "MENU.GREY" ,1) enableUsedFileSelection ( 1 ) SOB (idMenuFileClose , "SET" , "MENU.GREY" ,0 ) SOB (idMenuFileReload , "SET" , "MENU.GREY" ,0 ) SOB (idMenuFileExit , "SET" , "MENU.GREY" ,1 ) getrootFolders ( CommonSobResult ,CommonSobCombo ) SELECT count (*) from master where type like 'file' sob ( CommonSobResult , "add" , "row" , EOL & "Nr of files: " & qrSingleValue & EOL ) SELECT count (*) from temp.masterColumns where name like '%Tag' sob ( CommonSobResult , "add" , "row" , "Nr of Tag Groups: " & qrSingleValue & EOL ) create temp table tblRectList ( rlSrc , rlTagGroup , rlTagName, rlRect , rlLeft , rlTop, rlRight, rlBottom , rlHit , rlId ) restoreEnvironment () MakeBkTree() END function FUNCTION cbOpenDBfromFile ( sobid ) dim proceed = 1 sob( CommonSobResult , "empty") dim dbFIle = selectDBfile( getFirstFileDir () ) IF ( dbFile == "" ) then sob ( CommonSobResult , "add" , "row" , "cancelled by user" & EOL ) proceed = 0 END IF IF ( proceed ) then TRY dbopen("memory" ,dbFIle) CATCH ErrorReport ("Open failed on file : " & dbFIle ) sob ( CommonSobResult , "add" , "row" , "Open failed on file : " & EOL & dbFIle & EOL ) proceed = 0 END try END IF IF ( proceed ) then proceed = dbValidityCheck () END IF IF ( proceed) then IF ( proceed ) then AddFileToUsedList ( dbFIle, "rw" ) sob ( statusDBFile, "SET" ,"TITLE" , dbFIle ) IF ( ! isTable ( , "master") ) then ErrorReport ("No master table in database") dbclose() proceed = 0 END if END IF IF ( proceed ) then sob ( CommonSobBtn ,"set","show",0) update master set key = hash || path || name sob ( CommonSobResult , "add" , "row" , "Database file OK: " & EOL & dbFIle & EOL & EOL ) OnOpenPrepareMasterTagTable (CommonSobResult) OpenMessage ( ) END IF toggleWarnColour ( sobid) END if END FUNCTION FUNCTION CBFileMenuOpen ( sobid ) CommonSobClear ( "Open database from file" ) >Open a database file, in memory§ >>§ >>Changes are ONLY saved when you explicitly trigger a save.§ sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn , "SET" , "TITLE" , "Open") sob ( CommonSobBtn , "ON" , "PRESS", "cbOpenDBfromFile") sob ( CommonSobBtn , "SET" , "rgb" , defBtnCol) sob ( CommonSobInfo ,"set","show",1) sob ( CommonOvl ,"set","show",1) cbOpenDBfromFile ( CommonSobBtn ) END Function sob ( -1 , "ON" , "CLICK" , "CBFileMenuOpen") ' ----------------------------------------------------- ' ##################################################### ' RELOAD RELOAD RELOAD RELOAD RELOAD RELOAD RELOAD ' ----------------------------------------------------- dim idMenuFileReload = sob ( myMenuFile , "add" , "menu" , "Vertical" , "Reload" ) FUNCTION CBFileMenuReload ( sobid ) CBFileMenuClose ( sobid ) CBUsedFileList ( FirstFileMenuItem ) END FUNCTION sob ( -1 , "ON" , "CLICK" , "CBFileMenuReload") ' ----------------------------------------------------- ' ##################################################### ' CLOSE CLOSE CLOSE CLOSE CLOSE CLOSE CLOSE CLOSE CLOSE ' ----------------------------------------------------- dim idMenuFileClose = sob ( myMenuFile , "add" , "menu" , "Vertical" , "Close" ) FUNCTION EmptyThings () ' sob ( TagRectCombo , "empty") sob ( TagCommentEdit , "empty") sob ( CommonSobResult , "empty") sob ( PathFile , "empty") sob ( CommonSobInfo , "empty") sob ( TagInfo , "empty") sob ( TagTxtField , "empty") sob ( FreeTextComboSob , "empty") sob ( TagTxtEdit , "empty") sob ( FileListSob , "empty") SetLblRegionRGB ( defBtnCol ) removeAllThumbNails ( ) removeThumbNails ( siImageMngmtMatrix ) CommonSobClear ( "" ) END function ' --------------------------------------------------- ' https://www.codeproject.com/Articles/739/Rendering-GIF-JPEG-Icon-or-Bitmap-Files-with-OleLo FUNCTION ReleaseImage (sobId) dim CanvasHandle = SOB( sobId , "GET" , "CANVAS" ) IF ( CanvasHandle ) then canvas( CanvasHandle , "DELETE" ) END IF sob(sobId ,"SET","IMAGE","" ) ImageLoadedFlag = 0 END Function FUNCTION AssignImage ( sobid, fname ) ReleaseImage (sobId) ImageLoadedFlag = 1 TRY IF ( sob ( sobid , "SET", "IMAGE" , fname) ) then dim w = CANVAS ( "IMAGE.FILE", "GET" , "WIDTH" , fname) dim h = CANVAS ( "IMAGE.FILE", "GET" , "HEIGHT" , fname) dim canvId = CANVAS ( "NEW" , "BLANK" , w , h ) canvas ( canvId , "flood", TransparentRGB ) sob ( sobid , "SET", "CANVAS" , canvId) ELSE ImageLoadedFlag = 0 sob ( sobid , "SET", "IMAGE" , "" ) dim canvId = CANVAS ( "NEW" , "BLANK" , 100 , 100 ) canvas ( canvId , "flood", TransparentRGB ) sob ( sobid , "SET", "CANVAS" , canvId) canvas ( canvid , "icon.ext" , fname ) END IF CATCH ImageLoadedFlag = 0 END TRY END Function ' --------------------------------------------------- FUNCTION CBFileMenuClose ( sobid ) IF ( isDBopen ) then > update parameters set comment = freestring = freestring & " " & sqlString ( CurrentFile ) >> where txt like freestring = freestring & " " & sqlString ( sob(statusDBFile, "GET" ,"TITLE" ) ) db_arg_set ( freestring) dbClose () NoErrorReport ( dbName ) ReleaseImage (sobImageHolder) ELSE NoErrorReport ( "" ) END IF EmptyThings () sob ( statusDBFile, "SET" ,"TITLE" , "" ) ' resetTagBoxes () ' irrelevant since updateTagTypeCombos does it too updateTagTypeCombos ( ) updateFreeTextCombos () sob ( StatusTxt, "empty") sob ( StatusTxt, "SET" , "RGB", -1 ) haveRectMeta = 0 haveRectPicasa = 0 DbName = "" copyTagGroup = "" copyTagName = "" ImageLoadedFlag = 0 CommonSobClear ( "" ) SavedTagTypeComboIndex = 0 ' ---------------------------------------- ' set the GREY state of the menu File items to reflect NO OPEN DB SOB (idMenuFileNew , "SET" , "MENU.GREY" ,0 ) SOB (idMenuFileSave , "SET" , "MENU.GREY" ,1 ) SOB (idMenuFileSaveAs , "SET" , "MENU.GREY" ,1 ) SOB (idMenuFileSaveCopyAs, "SET" , "MENU.GREY" ,1 ) SOB (idMenuFileReload , "SET" , "MENU.GREY" ,1 ) SOB (idMenuFileOpen , "SET" , "MENU.GREY" ,0 ) enableUsedFileSelection ( 0 ) SOB (idMenuFileClose , "SET" , "MENU.GREY" ,1 ) SOB (idMenuFileExit , "SET" , "MENU.GREY" ,0 ) END Function sob ( -1 , "ON" , "CLICK" , "CBFileMenuClose") FUNCTION cbMenuFileSaveNClose ( sobid ) CBFileMenuSave ( sobid ) CBFileMenuClose ( sobid) END Function sob ( idMenuFileSaveNClose , "ON" , "CLICK" , "cbMenuFileSaveNClose") FUNCTION cbMenuFileSaveNClosenExit ( sobid ) CBFileMenuSave ( sobid ) CBFileMenuClose ( sobid) quit END Function sob ( idMenuFileSaveNClosenExit , "ON" , "CLICK" , "cbMenuFileSaveNClosenExit") ' --------------------------------------------------- dim idMenuFileExit = sob ( myMenuFile , "add" , "menu" , "Vertical" , "Exit" ) FUNCTION CBFileMenuExit ( sobid ) ' would be better to check if exists a DB ' ask if it should be saved,... CBFileMenuClose (sobid) quit END Function sob ( -1 , "ON" , "CLICK" , "CBFileMenuExit") ' ----------------------------------------------------- sob ( myMenuFile , "add" , "menu" , "SPACER" ) ' ----------------------------------------------------- create_usedFileList () SOB (idMenuFileNew , "SET" , "MENU.GREY" ,0 ) SOB (idMenuFileSave , "SET" , "MENU.GREY" ,1) SOB (idMenuFileSaveAs, "SET" , "MENU.GREY" ,1 ) SOB (idMenuFileSaveCopyAs, "SET" , "MENU.GREY" ,1 ) SOB (idMenuFileOpen , "SET" , "MENU.GREY" ,0) enableUsedFileSelection ( 0 ) SOB (idMenuFileClose , "SET" , "MENU.GREY" ,1 ) SOB (idMenuFileReload , "SET" , "MENU.GREY" ,1 ) SOB (idMenuFileExit , "SET" , "MENU.GREY" ,0 ) ' ----------------------------------------------------- ' myMenuRegions ' ----------------------------------------------------- sob ( myMenuRegions , "add" , "menu" , "Vertical" , "Visible" ) FUNCTION cbRegionsVisible ( sobid ) sob(sobid, "SET", "CHECK" , ! sob(sobid, "GET", "CHECK" ) ) RegionRectShow = sob(sobid, "GET", "CHECK" ) IF ( ImageLoadedFlag ) then sob ( reDrawRegionsID , "trigger") END IF END FUNCTION sob ( -1 , "ON" , "CLICK" , "cbRegionsVisible" ) sob ( -1 , "TRIGGER") ' ----------------------------------------------------- dim myMenuRegionsRGBn = sob ( myMenuRegions , "add" , "menu" , "Horizontal" , "RGB (named)" ) dim RegionRectRGBn FUNCTION cbRegionsRGBn ( sobid ) sob( sobid , "SET", "CHECKS.OFF" ) sob( sobid , "SET", "CHECK" , 1 ) RegionRectRGBn = sob ( sobid , "get", "data") IF ( ImageLoadedFlag ) then sob ( reDrawRegionsID , "trigger") END IF END FUNCTION FUNCTION MakeRegionsRGBn( name , colour ) sob ( myMenuRegionsRGBn , "add" , "menu" , "Vertical" , name ) sob ( -1 , "SET" , "Data" , colour ) sob ( -1 , "ON" , "CLICK" , "cbRegionsRGBn" ) END FUNCTION MakeRegionsRGBn( "Black" , rgb(000,000,000) ) sob ( -1 , "TRIGGER") MakeRegionsRGBn( "Red" , rgb(255,000,000) ) MakeRegionsRGBn( "Yellow" , rgb(255,255,000) ) MakeRegionsRGBn( "Green" , rgb(000,255,000) ) MakeRegionsRGBn( "Blue" , rgb(000,000,255) ) MakeRegionsRGBn( "White" , rgb(255,255,255) ) ' ----------------------------------------------------- dim myMenuRegionsRGBu = sob ( myMenuRegions , "add" , "menu" , "Horizontal" , "RGB (unnamed)" ) dim RegionRectRGBu FUNCTION cbRegionsRGBu ( sobid ) sob( sobid , "SET", "CHECKS.OFF" ) sob( sobid , "SET", "CHECK" , 1 ) RegionRectRGBu = sob ( sobid , "get", "data") IF ( ImageLoadedFlag ) then sob ( reDrawRegionsID , "trigger") END IF END FUNCTION FUNCTION MakeRegionsRGBu( name , colour ) sob ( myMenuRegionsRGBu , "add" , "menu" , "Vertical" , name ) sob ( -1 , "SET" , "Data" , colour ) sob ( -1 , "ON" , "CLICK" , "cbRegionsRGBu" ) END FUNCTION MakeRegionsRGBn( "Black" , rgb(000,000,000) ) MakeRegionsRGBu( "Red" , rgb(255,000,000) ) MakeRegionsRGBu( "Yellow" , rgb(255,255,000) ) MakeRegionsRGBu( "Green" , rgb(000,255,000) ) MakeRegionsRGBu( "Blue" , rgb(000,000,255) ) sob ( -1 , "TRIGGER") MakeRegionsRGBu( "White" , rgb(255,255,255) ) ' ----------------------------------------------------- dim myMenuRegionsRGBa = sob ( myMenuRegions , "add" , "menu" , "Horizontal" , "RGB (active)" ) dim RegionRectRGBa FUNCTION cbRegionsRGBa ( sobid ) sob( sobid , "SET", "CHECKS.OFF" ) sob( sobid , "SET", "CHECK" , 1 ) RegionRectRGBa = sob ( sobid , "get", "data") IF ( ImageLoadedFlag ) then sob ( reDrawRegionsID , "trigger") END IF END FUNCTION FUNCTION MakeRegionsRGBa( name , colour ) sob ( myMenuRegionsRGBa , "add" , "menu" , "Vertical" , name ) sob ( -1 , "SET" , "Data" , colour ) sob ( -1 , "ON" , "CLICK" , "cbRegionsRGBa" ) END FUNCTION MakeRegionsRGBa( "Black" , rgb(000,000,000) ) MakeRegionsRGBa( "Red" , rgb(255,000,000) ) sob ( -1 , "TRIGGER") MakeRegionsRGBa( "Yellow" , rgb(255,255,000) ) MakeRegionsRGBa( "Green" , rgb(000,255,000) ) MakeRegionsRGBa( "Blue" , rgb(000,000,255) ) MakeRegionsRGBa( "White" , rgb(255,255,255) ) ' ----------------------------------------------------- dim myMenuRegionsWeigth = sob ( myMenuRegions , "add" , "menu" , "Horizontal" , "Width" ) FUNCTION cbRegionsWeight ( sobid ) sob( sobid , "SET" , "CHECKS.OFF" ) sob( sobid , "SET" , "CHECK" , 1 ) RegionRectWeight = sob ( sobid , "get", "data") IF ( ImageLoadedFlag ) then sob ( reDrawRegionsID , "trigger") END IF END FUNCTION FUNCTION MakeRegionsWeight( weigth ) sob ( myMenuRegionsWeigth , "add" , "menu" , "Vertical" , "" & weigth ) sob ( -1 , "SET" , "Data" , weigth ) sob ( -1 , "ON" , "CLICK" , "cbRegionsWeight" ) END FUNCTION MakeRegionsWeight( 1 ) MakeRegionsWeight( 2 ) MakeRegionsWeight( 4 ) MakeRegionsWeight( 8 ) sob ( -1 , "TRIGGER") MakeRegionsWeight( 16 ) MakeRegionsWeight( 32 ) ' ----------------------------------------------------- ' dim myMenuRegionRubberbanding = sob ( myMenuRegions , "add" , "menu" , "Vertical" , "Rubberbanding" ) FUNCTION NoOpPress ( sobid,a,b,c,d ) END function FUNCTION cbRegionsRubberbanding ( sobid ) dim val = sob( myMenuRegionRubberbanding , "GET", "CHECK" ) IF ( val ) then ' we were rubberbanding sob( sobid , "SET", "CHECK" , 0 ) sob ( sobImageHolder , "ON" , "RUBBERBAND", "cbImageRubberBand" , "OFF") ' remove rubberbanding Rect from view IF ( ImageLoadedFlag ) then sob ( reDrawRegionsID , "trigger") END IF ELSE sob( sobid , "SET", "CHECK" , 1 ) sob ( sobImageHolder , "ON" , "RUBBERBAND", "cbImageRubberBand") END IF END FUNCTION sob ( -1 , "ON" , "CLICK" , "cbRegionsRubberbanding" ) ' ----------------------------------------------------- FUNCTION sink () sob ( HelpFloatOVl , "SET", "SHOW" , 0 ) HelpPageSOB = HelpFixedOVl HelpTextSob = HelpFixedHTMLSOB END FUNCTION FUNCTION HelpFloat ( sobid ) dim txt dim chk = ! sob ( sobid, "get","check" ) sob( Fixed2Float, "SET", "CHECK" , chk ) sob( Float2Fixed, "SET", "CHECK" , chk ) txt = sob ( HelpTextSob , "GET", "HTML" , "OUTER" ) sob ( HelpTextSob , "empty") IF ( chk ) then sob ( HelpFloatOVl , "SET", "SHOW" , 1 ) HelpPageSOB = HelpFloatOVl HelpTextSob = HelpFloatHTMLSOB ELSE sob ( HelpFloatOVl , "SET", "SHOW" , 0 ) HelpPageSOB = HelpFixedOVl HelpTextSob = HelpFixedHTMLSOB END IF sob ( HelpTextSob , "empty") sob ( HelpTextSob ,"add","row" , txt ) END FUNCTION FUNCTION createHelpMenuContent ( parentMenu ) ' ----------------------------------------------------- sob ( parentMenu , "add" , "menu" , "Vertical", "Introduction" ) sob ( -1 , "ON" , "CLICK" , "aboutHTML" ) sob ( parentMenu , "add" , "menu" , "Vertical", "Tagging" ) sob ( -1 , "ON" , "CLICK" , "HelpTagging" ) sob ( parentMenu , "add" , "menu" , "Vertical" , "Tag Groups that I use" ) sob ( -1 ,"ON","CLICK", "myTagGroups") sob ( parentMenu , "add" , "menu" , "Vertical", "Tag Text" ) sob ( -1 , "ON" , "CLICK" , "HelpTagTextHTML" ) sob ( parentMenu , "add" , "menu" , "Vertical", "File Text" ) sob ( -1 , "ON" , "CLICK" , "HelpFreeText" ) sob ( parentMenu , "add" , "menu" , "Vertical", "File Subset" ) sob ( -1 , "ON" , "CLICK" , "HelpFileSubSet" ) sob ( parentMenu , "add" , "menu" , "Vertical", "Delete External Duplicates" ) sob ( -1 , "ON" , "CLICK" , "HelpDelExtDups" ) sob ( parentMenu , "add" , "menu" , "Vertical", "Delete Files" ) sob ( -1 , "ON" , "CLICK" , "HelpDelFiles" ) sob ( parentMenu , "add" , "menu" , "Vertical", "Delete Current File" ) sob ( -1 , "ON" , "CLICK" , "HelpCurrentFile" ) sob ( parentMenu , "add" , "menu" , "Vertical", "Power Tagging" ) sob ( -1 , "ON" , "CLICK" , "HelpPowerTagging" ) sob ( parentMenu , "add" , "menu" , "Vertical", "Inherit" ) sob ( -1 , "ON" , "CLICK" , "HelpInherit" ) IF ( haveWIA + haveShell) then sob ( parentMenu , "add" , "menu" , "Vertical", "Single File Options" ) sob ( -1 , "ON" , "CLICK" , "HelpSingleFileOptions" ) END if sob ( parentMenu , "add" , "menu" , "Vertical" , "Facial Recognition" ) sob ( -1 ,"ON","CLICK", "HelpFacialRecognition") sob ( parentMenu , "add" , "menu" , "Vertical" , "Regions" ) sob ( -1 ,"ON","CLICK", "HelpRegions") sob ( parentMenu , "add" , "menu" , "Vertical" , "Backup" ) sob ( -1 ,"ON","CLICK", "HelpBackUp") sob ( parentMenu , "add" , "menu" , "Vertical" , "Expert Mode" ) sob ( -1 ,"ON","CLICK", "HelpExpertMode") sob ( parentMenu , "add" , "menu" , "Vertical" , "Tips'n'Tricks" ) sob ( -1 ,"ON","CLICK", "HelpTipsNTricks") sob ( parentMenu , "add" , "menu" , "Vertical", "Go to online help" ) sob ( -1 , "ON" , "CLICK" , "HelpOnline" ) END function dim Fixed2Float = sob ( myMenuHelp , "add" , "menu" , "Vertical", "Float the help panel" ) sob ( -1 , "ON" , "CLICK" , "HelpFloat" ) createHelpMenuContent ( myMenuHelp ) dim Float2Fixed = sob ( HelpMenuHelp , "add" , "menu" , "Vertical", "Float the help panel" ) sob ( -1 , "ON" , "CLICK" , "HelpFloat" ) createHelpMenuContent ( HelpMenuHelp ) ' ----------------------------------------------------- sob ( myMenuAbout , "add" , "menu" , "Vertical" , "Version" ) sob ( -1 ,"ON","CLICK", "HelpVersion") sob ( myMenuAbout , "add" , "menu" , "Vertical" , "Use cases" ) sob ( -1 ,"ON","CLICK", "HelpUseCases") sob ( myMenuAbout , "add" , "menu" , "Vertical" , "Techie Talk" ) sob ( -1 ,"ON","CLICK", "cbTechieTalk") sob ( myMenuAbout , "add" , "menu" , "Vertical", "The author" ) sob ( -1 , "ON" , "CLICK" , "AboutTheAuthor" ) sob ( myMenuAbout , "add" , "menu" , "Vertical" , "Contact" ) sob ( -1 ,"ON","CLICK", "ContactHTML") ' ----------------------------------------------------- ' OptionsMenu ' ----------------------------------------------------- dim optionsMenuExpert = sob ( OptionsMenu , "add" , "menu" , "Vertical" , "Expert Mode" ) FUNCTION cbOptionsMenuExpert ( sobid ) ExpertFlag = ! ExpertFlag sob(sobid, "SET", "CHECK" ,ExpertFlag) sob ( pwsWindow, "SET", "SHOW", ExpertFlag ) IF ( ExpertFlag ) then dbDisplayRefresh sob ( pwsWindow, "set", "shrink") end if END Function sob ( -1 ,"ON","CLICK", "cbOptionsMenuExpert") ' -------------- dim optionsMenuRescan = sob ( OptionsMenu , "add" , "menu" , "Vertical" , "Rescan Master" ) FUNCTION cbOptionsMenuRescan ( sobid ) CommonSobClear ( "Rescan the root folders" ) > Use this to rescan the root folder(s) (those previously read via the commands NEW and SCAN NEW FOLDER§ >> Identify changes in terms of: § >> - new location ( path|filename ) with a new hash§ >> will start with no TAG values§ >> - new location ( path|filename ) with a hash known to the database§ >> will start with the TAG values applicable for that hash§ >> - existing location ( path|filename ) with different hash to that previously held§ >> will continue with its previous TAGs§ >> - existing location ( path|filename ) not found.§ >> will be removed from the database sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn , "SET" , "TITLE" , "Rescan") sob ( CommonSobBtn , "ON" , "PRESS", "CbRescanDatabase") sob ( CommonSobBtn , "SET" , "rgb" , defBtnCol) sob ( CommonSobBtn , "SET" , "SHOW" , 1) getrootFolders (CommonSobResult ,CommonSobCombo) sob ( CommonOvl ,"set","show",1) END Function sob ( -1 ,"ON","CLICK", "cbOptionsMenuRescan") ' -------------- dim optionsMenuNewScan = sob ( OptionsMenu , "add" , "menu" , "Vertical" , "Scan new folder" ) FUNCTION cboptionsMenuNewScan ( sobid ) getrootFolders (CommonSobResult,CommonSobCombo) CommonSobClear ( "Scan new folder into database" ) > Use this to scan a new root folder(s) and add its contents to the current database§ >> - new location ( path & filename ) with a new hash§ >> will start with no TAG values§ >> - new location ( path & filename ) with a hash known to the database§ >> will start with the TAG values applicable for that hash§ >> § >> If the new folder structure overlaps a current root folder, then the overlapping area will NOT reread!§ sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn , "SET" , "TITLE" , " Scan ") sob ( CommonSobBtn , "ON" , "PRESS", "CbScanNewFolder") sob ( CommonSobBtn , "SET" , "rgb" , defBtnCol) sob ( CommonSobBtn ,"set","show",1) sob ( CommonOvl ,"set","show",1) END Function sob ( -1 ,"ON","CLICK", "cbOptionsMenuNewScan") ' -------------- FUNCTION singleFileInfo ( sobid ) SOB ( sobid , "SET" , "CHECKS.OFF" ) sob(sobid, "SET", "CHECK" ,1) IF ( imageLoadedFlag ) then sob ( TagInfo , "SET", "SHOW", 0) DisplayTagsInFile () IF ( haveWIA ) then IF ( sob ( SingleFileMenuWIA , "GET" , "CHECK" ) ) then handleFileWIA ( TagInfo , CurrentFile ) END IF IF ( haveShell ) then IF ( sob ( SingleFileMenuGetDetailsOf , "GET" , "CHECK" ) ) then handleFileGetDetailsOf ( TagInfo ,CurrentFile ) END IF sob ( TagInfo , "SET", "SHOW", 1) END if END Function IF ( haveWIA + haveShell) then dim SingleFileMenuNoInfo= sob ( myMenuSingleFile , "add" , "menu" , "Vertical" , "No extra info" ) sob ( -1 ,"ON","CLICK", "singleFileInfo") IF ( haveWIA) then dim SingleFileMenuWIA= sob ( myMenuSingleFile , "add" , "menu" , "Vertical" , "Info from WIA" ) sob ( -1 ,"ON","CLICK", "singleFileInfo") END IF IF ( haveShell) then dim SingleFileMenuGetDetailsOf= sob ( myMenuSingleFile , "add" , "menu" , "Vertical" , "Info from SHELL" ) sob ( -1 ,"ON","CLICK", "singleFileInfo") END IF sob ( -1 , "TRIGGER") END IF ' -------------- dim ImageMngmtDisplayRows = 7 dim ImageMngmtBase FUNCTION CreateImageMngmtTblGroup ( type ) drop table if exists temp.ImageMngmtTbl > create temp table ImageMngmtTbl as >> select rowid as selfA ,* from master >> WHERE >> ImageGroupType like freestring = freestring & " " & sqlString ( type ) >> AND IgnoreFile = 0 >> AND not pHash = 0 >> group by ImageGroup having count (*) > 1 >> order by size desc sql ( freestring ) ImageMngmtBase = 0 SELECT count (*) from ImageMngmtTbl sob ( ImageMngmtOF, "SET" ,"TITLE", cstr( qrSingleValue) ) END function FUNCTION CreateImageMngmtTbl ( type ) drop table if exists temp.ImageMngmtTbl > create temp table ImageMngmtTbl as >> select rowid as selfA , * from master >> WHERE >> ImageGroupType like freestring = freestring & " " & sqlString ( type ) >> AND IgnoreFile = 0 >> AND not pHash = 0 >> order by size desc sql ( freestring ) ImageMngmtBase = 0 SELECT count (*) from ImageMngmtTbl sob ( ImageMngmtOF, "SET" ,"TITLE", cstr( qrSingleValue) ) END function FUNCTION ImageMngmtDisplay () removeThumbNails ( siImageMngmtMatrix ) dim row = 1 , col dim ImgGroup dim siTmp sob ( ImageMngmtFrom, "SET" ,"TITLE", cstr(ImageMngmtBase)) > select path||name , ImageGroup, SelfA, * from ImageMngmtTbl >> limit freestring = freestring & " " & sqlString (ImageMngmtDisplayRows ) >> offset freestring = freestring & " " & ImageMngmtBase WITHQUERY ( freestring ) siTmp = sob ( siImageMngmtMatrix , "CHILD", "HANDLE" , row ) sob ( siTmp ,"SET","IMAGE", wqText(1) ) sob ( siTmp ,"SET","Data", wqInt(3) ) col = 1 > select path||name , ImageGroup, rowid, * from master >> where >> ImageGroup like freestring = freestring & " " & SqlString ( wqText(2) ) >> and NOT rowid = freestring = freestring & " " & wqInt (3) >> AND IgnoreFile = 0 >> AND not pHash = 0 >> order by size desc >> limit 5 WITHQUERY ( freestring ) siTmp = sob ( siImageMngmtMatrix , "CHILD", "HANDLE" , row + col ) sob ( siTmp ,"SET","IMAGE", wqText(1) ) sob ( siTmp ,"SET","Data", wqInt(3) ) col = col + 1 END WithQuery row = row + 5 END withquery ImageMngmtBase = ImageMngmtBase + ImageMngmtDisplayRows sob ( ImageMngmtTo, "SET" ,"TITLE", cstr(ImageMngmtBase) ) END function FUNCTION cbImageMngmtReset( sobid ) ResetImageGroups () END function FUNCTION cbImageMngmtDuplicates( sobid ) sob ( ImageManagementOvl , "SET", "SHOW", 1 ) CreateImageMngmtTblGroup ("duplicate" ) ImageMngmtDisplay ( ) END function FUNCTION cbImageMngmtDifferent( sobid ) sob ( ImageManagementOvl , "SET", "SHOW", 1 ) CreateImageMngmtTbl ("different" ) ImageMngmtDisplay () END function FUNCTION cbImageMngmtNearidentical( sobid ) sob ( ImageManagementOvl , "SET", "SHOW", 1 ) CreateImageMngmtTblGroup ("Near-Identical" ) ImageMngmtDisplay () END function sob ( myMenuImageMngmt , "add" , "menu" , "Vertical" , "Duplicates" ) sob ( -1 ,"ON","CLICK", "cbImageMngmtDuplicates") sob ( myMenuImageMngmt , "add" , "menu" , "Vertical" , "Near-Identical" ) sob ( -1 ,"ON","CLICK", "cbImageMngmtNearidentical") sob ( myMenuImageMngmt , "add" , "menu" , "Vertical" , "Different" ) sob ( -1 ,"ON","CLICK", "cbImageMngmtDifferent") sob ( myMenuImageMngmt , "add" , "menu" , "Vertical" , "Reset" ) sob ( -1 ,"ON","CLICK", "cbImageMngmtReset") ' -------------- dim optionsMenuRemoveExternalDups= sob ( myMenuDelete , "add" , "menu" , "Vertical" , "Delete External Duplicates" ) FUNCTION CBoptionsMenuRemoveExternalDups ( sobid ) CommonSobClear ( "Delete known files/hash in external folder" ) >Be warned, this deletes files (to the recycle bin)§ >>It scans a user defined folder structure and DELETES any file whose hash is in the database.§ >> § >>I use this when I think that the target folder has files that I want to bring (later) in to § >>the database, and I want to exclude from that folder structure any file that is already known!§ >> § >>It will NOT delete ANYTHING in the database!§ >>It does NOT bring the not deleted files in to the database, do that using the -Scan new folder- option. sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn , "SET" , "TITLE" , "Delete") sob ( CommonSobBtn , "ON" , "PRESS", "CbRemoveExternalDuplicates") sob ( CommonSobBtn , "SET" , "rgb" , defBtnCol) sob ( CommonSobBtn ,"set","show",1) sob ( CommonOvl ,"set","show",1) END Function sob ( -1 ,"ON","CLICK", "CBoptionsMenuRemoveExternalDups") ' -------------- dim toolsMenuDelete = sob ( myMenuDelete , "add" , "menu" , "Vertical" , "Delete Files" ) FUNCTION cbtoolsMenuDelete ( sobid ) sob ( FileExportOvl , "set" , "show" , 1) sob ( NbrFilesInFilter , "SET", "TITLE" , "" & exportFilter ( "statistic" , "" , "" ) ) sob ( SubListTitleSob , "set" , "title", "Create a file sublist for deleting from database and disk" ) sob ( BtnExportFiles , "set" , "show" , 0) sob ( BtnDeleteFiles , "set" , "show" , 1) sob ( PowerTaggingSob , "set" , "show" , 0) END Function sob ( -1 ,"ON","CLICK", "cbtoolsMenuDelete") ' -------------- dim optionsMenuRootFolders= sob ( OptionsMenu , "add" , "menu" , "Vertical" , "Root Folders" ) FUNCTION CbShowRootFolders ( soibid ) END function FUNCTION onRootFolderSelection ( sobid , rowid ) IF ( ! isdbopen ) THEN ErrorReport("No database open") EXIT function END IF ShellExecute ( sob ( sobid , "get", "title" ) ) END Function FUNCTION CBoptionsMenuRootFolders ( sobid ) CommonSobClear ( "Root Folders" ) getrootFolders(CommonSobResult,CommonSobCombo) >select a root folder from the combo box below and the folder will be >> opened in a file explorer. sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn , "ON" , "PRESS", "CbShowRootFolders") sob ( CommonSobBtn , "SET" , "rgb" , defBtnCol) sob ( CommonOvl , "set" , "show",1) sob ( CommonSobCombo , "set" , "show",1) sob ( CommonSobCombo , "ON" , "SELECTION" , "onRootFolderSelection") END Function sob ( -1 ,"ON","CLICK", "CBoptionsMenuRootFolders") ' -------------- dim optionsMenuAutoSesion= sob ( OptionsMenu , "add" , "menu" , "Vertical" , "Auto reload last session" ) FUNCTION autoStartChk ( ) sob(optionsMenuAutoSesion , "SET", "CHECK" , 0 ) IF ( db_arg_int ( "select count (*) from parameters where type like 'autoStart'" ) ) then IF ( db_arg_int ( "select val from parameters where type like 'autoStart'" ) ) then CBUsedFileList ( FirstFileMenuItem ) sob(optionsMenuAutoSesion , "SET", "CHECK" , 1 ) END IF END if END FUNCTION FUNCTION optionsMenuAutoSession ( sobid ) sob(sobid, "SET", "CHECK" , ! sob(sobid, "GET", "CHECK" ) ) dim AutoState = sob(sobid, "GET", "CHECK" ) IF ( ! db_arg_int ( "select count (*) from parameters where type like 'autoStart'" ) ) then db_arg_set ( "insert into parameters values ('autoStart', '', 0 , '' , '' ) " ) END if > update parameters set val = freestring = freestring & " " & AutoState >> where type like 'autoStart' db_arg_set ( freestring ) END FUNCTION sob ( -1 ,"ON","CLICK", "optionsMenuAutoSession") ' -------------- dim optionsMenuResetFields= sob ( OptionsMenu , "add" , "menu" , "Vertical" , "Reset all fields" ) FUNCTION cboptionsMenuResetFields ( sobid ) resetSavedBoxes () sob ( CommonOvl ,"set","show",1) CommonSobClear ( "" ) END FUNCTION sob ( -1 ,"ON","CLICK", "cboptionsMenuResetFields") ' ----------------------------------------------------- dim toolsMenuTagManage = sob ( myMenuTools , "add" , "menu" , "Vertical" , "Tag Management" ) FUNCTION cbtoolsMenuTagManage ( sobid ) sob ( TagManageOvl ,"set","show",1) END Function sob ( -1 ,"ON","CLICK", "cbtoolsMenuTagManage") ' -------------- dim toolsMenuTagFilesB= sob ( myMenuTools , "add" , "menu" , "Vertical" , "File Tagging" ) FUNCTION cbtoolsMenuTagFilesB ( sobid ) sob ( FileTaggingOvl , "set", "show" , 1) END Function sob ( -1 ,"ON","CLICK", "cbtoolsMenuTagFilesB") ' -------------- dim toolsMenuTagPower= sob ( myMenuTools , "add" , "menu" , "Vertical" , "Power Tagging" ) FUNCTION cbtoolsMenuTagPower ( sobid ) sob ( FileExportOvl , "set" , "show" , 1) sob ( SubListTitleSob , "set" , "title", "Create a file sublist for Power Tagging" ) sob ( NbrFilesInFilter , "SET", "TITLE" , "" & exportFilter ( "statistic" , "" , "" ) ) sob ( PowerTaggingSob , "set" , "show" , 1) sob ( BtnExportFiles , "set" , "show" , 0) sob ( BtnDeleteFiles , "set" , "show" , 0) END Function sob ( -1 ,"ON","CLICK", "cbtoolsMenuTagPower") ' -------------- FUNCTION cbImportMenuExcel ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if CommonSobClear ( "Import database from Excel" ) >Recreate a database from a previous saved Excel file.§ >>You will be asked for a file name, from which the databse will be recreated.§ sob ( CommonSobBtn ,"set","show",1) sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn , "SET" , "TITLE" , "Import") sob ( CommonSobBtn , "ON" , "PRESS", "CbImportMasterToExcel") sob ( CommonSobBtn , "SET" , "rgb" , defBtnCol) sob ( CommonOvl ,"set","show",1) END Function IF ( XLallowed ) then dim ImportMenuExcel= sob ( myMenuImport , "add" , "menu" , "Vertical" , "Database from Excel" ) sob ( -1 ,"ON","CLICK", "cbImportMenuExcel") END IF ' -------------- ' -------------- FUNCTION cbImportMenuCSV ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if CommonSobClear ( "Import database from CSV" ) >Import a copy of the database master table to CSV >>§ >>It actually try to read in two CSV files: one for the MASTER table, and one for the Tag table.§ >> (When you provided a name for the CVS export, it MUST end in .csv § >> So if you provided the name xyz.csv then that will be used for the MASTER table§ >> And xyzTT.csv will be used for the Tag Table.)§ >>§ >>The Tag Table is only needed to provide the free text associated with the individual tags.§ >>Should the Tag Table be lost, then the fundamental tagging capability is NOT affected.§ sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn , "SET" , "TITLE" , "Import") sob ( CommonSobBtn , "ON" , "PRESS", "CbImportMasterToCSV") sob ( CommonSobBtn , "SET" , "rgb" , defBtnCol) sob ( CommonOvl ,"set","show",1) END Function dim ImportMenuCSV= sob ( myMenuImport , "add" , "menu" , "Vertical" , "Database from CSV" ) sob ( -1 ,"ON","CLICK", "cbImportMenuCSV") ' -------------- ' -------------- FUNCTION cbExportMenuExcel ( sobid ) CommonSobClear ( "Export database to Excel" ) >Export a copy of the database master table to Excel§ >>§ >>It does NOT create an EXCEL compatible file, it "talks" to Excel and gets Excel to create the file.§ >>If Excel is not running, it will be started.§ >>If Excel is already running, then the exported data will be placed in a NEW workbook§ >>You will be asked for a file name under which the workbook will be saved.§ IF ( xlactive ) then >>Excel is already running§ ELSE >>Excel will be started if/when you press the export button§ END IF sob ( CommonSobBtn ,"set","show",1) sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn , "SET" , "TITLE" , "Export") sob ( CommonSobBtn , "ON" , "PRESS", "CbExportMasterToExcel") sob ( CommonSobBtn , "SET" , "rgb" , defBtnCol) sob ( CommonOvl ,"set","show",1) END Function IF ( XLallowed ) then dim ExportMenuExcel= sob ( myMenuExport , "add" , "menu" , "Vertical" , "Database to Excel" ) sob ( -1 ,"ON","CLICK", "cbExportMenuExcel") END IF ' -------------- ' -------------- ' -------------- FUNCTION cbExportMenuCSV ( sobid ) CommonSobClear ( "Export database to CSV" ) >Export a copy of the database master table to CSV >>§ >>It actually creates two CSV files: one for the MASTER table, and one for the Tag table.§ >>When you provide a name for the CVS export, it MUST end in .csv § >>So if you provide the name xyz.csv then that will be used for the MASTER table§ >>And xyzTT.csv will be used for the Tag Table§ >>§ >>The Tag Table is only needed to provide the free text associated with the individual tags.§ >>Should the Tag Table be lost, then the fundamental tagging capability is NOT affected.§ sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn , "set" , "show" , 1 ) sob ( CommonSobBtn , "SET" , "TITLE" , "Export") sob ( CommonSobBtn , "ON" , "PRESS", "CbExportMasterToCSV") sob ( CommonSobBtn , "SET" , "rgb" , defBtnCol) sob ( CommonOvl ,"set","show",1) END Function dim ExportMenuCSV= sob ( myMenuExport , "add" , "menu" , "Vertical" , "Database to CSV" ) sob ( -1 ,"ON","CLICK", "cbExportMenuCSV") ' -------------- dim myMenuBackup = sob (myMenuExport , "add" , "menu" , "Vertical" , "Database to Backup" ) FUNCTION cbExportMenuBackup ( sobid ) CommonSobClear ( "Back up whole database" ) >Back-up the FILES referenced in the database: >> you specify a destination folder.§ >> Then the folders & files held in the database will be COPIED to that destination.§ >> § >> If the destination already has the same folder structure then any files therein § >> will be DELETED if they are not also in the database.§ >> § >> If the destination has folders not in the database folder structure then they § >> will be DELETED .§ >> § >> If the destination already has a folder/file that is in the database, AND they have § >> the same checksum, the destination will not be overwritten.§ >> The backup is therefore an INCREMENTAL backup (for performance reasons),.§ sob ( CommonSobInfo , "ADD" , "ROW" , replace ( freestring , EOLdelim , EOL ) ) sob ( CommonSobBtn ,"set","show",1) sob ( CommonSobBtn , "SET" , "TITLE" , "Backup") sob ( CommonSobBtn , "ON" , "PRESS", "CbBackUpDatabase") sob ( CommonSobBtn , "SET" , "rgb" , defBtnCol) sob ( CommonOvl ,"set","show",1) END Function sob ( -1 ,"ON","CLICK", "cbExportMenuBackup") ' -------------- sob ( myMenuExport , "add" , "menu" , "SPACER" ) ' -------------- dim toolsMenuDBExportExcel= sob ( myMenuExport , "add" , "menu" , "Vertical" , "Sublist to Excel" ) FUNCTION cbtoolsMenuDBExportExcel ( sobid ) sob ( FileExportOvl , "set" , "show" , 1) sob ( NbrFilesInFilter , "SET", "TITLE" , "" & exportFilter ( "statistic" , "" , "" ) ) sob ( SubListTitleSob, "set" , "title", "Create a file sublist for Excel" ) sob ( BtnExportFiles , "set", "Title" , "Export sublist" ) sob ( -1 , "ON" , "PRESS" , "cbExportFileListToExcel") sob ( BtnExportFiles , "set" , "show" , 1 ) sob ( BtnDeleteFiles , "set" , "show" , 0 ) sob ( PowerTaggingSob , "set" , "show" , 0 ) END Function sob ( -1 ,"ON","CLICK", "cbtoolsMenuDBExportExcel") ' -------------- dim toolsMenuDBExport= sob ( myMenuExport , "add" , "menu" , "Vertical" , "Sublist to CSV" ) FUNCTION cbtoolsMenuDBExportCSV ( sobid ) sob ( FileExportOvl , "set" , "show" , 1) sob ( NbrFilesInFilter , "SET", "TITLE" , "" & exportFilter ( "statistic" , "" , "" ) ) sob ( SubListTitleSob, "set" , "title", "Create a file sublist for CSV" ) sob ( BtnExportFiles , "set", "Title" , "Export sublist" ) sob ( -1 , "ON" , "PRESS" , "CbExportSubListToCSV") sob ( BtnExportFiles , "set" , "show" , 1 ) sob ( BtnDeleteFiles , "set" , "show" , 0 ) sob ( PowerTaggingSob , "set" , "show" , 0 ) END Function sob ( -1 ,"ON","CLICK", "cbtoolsMenuDBExportCSV") ' -------------- dim toolsMenuExportSubLIst= sob ( myMenuExport , "add" , "menu" , "Vertical" , "SubList to Backup" ) FUNCTION cbtoolsMenuExportSubList ( sobid ) sob ( FileExportOvl , "set" , "show" , 1) sob ( NbrFilesInFilter , "SET" , "TITLE" , "" & exportFilter ( "statistic" , "" , "" ) ) sob ( SubListTitleSob , "set" , "title", "Create a backup of the files in a file sublist" ) sob ( BtnExportFiles , "set", "Title" , "Backup sublist" ) sob ( -1 , "ON" , "PRESS" , "CbBackUpSubList") sob ( BtnExportFiles , "set" , "show" , 1) sob ( BtnDeleteFiles , "set" , "show" , 0) sob ( PowerTaggingSob , "set" , "show" , 0) END Function sob ( -1 ,"ON","CLICK", "cbtoolsMenuExportSubList") ' -------------- sob ( myMenuExport , "add" , "menu" , "SPACER" ) ' -------------- dim toolsMenuExport= sob ( myMenuExport , "add" , "menu" , "Vertical" , "Sublist to flat folder" ) FUNCTION cbtoolsMenuExport ( sobid ) sob ( FileExportOvl , "set" , "show" , 1) sob ( NbrFilesInFilter , "SET" , "TITLE" , "" & exportFilter ( "statistic" , "" , "" ) ) sob ( SubListTitleSob , "set" , "title", "Create a file sublist to export files to a flat folder" ) sob ( BtnExportFiles , "set", "Title" , "Export files" ) sob ( -1 , "ON" , "PRESS" , "cbExportFileList") sob ( BtnExportFiles , "set" , "show" , 1) sob ( BtnDeleteFiles , "set" , "show" , 0) sob ( PowerTaggingSob , "set" , "show" , 0) END Function sob ( -1 ,"ON","CLICK", "cbtoolsMenuExport") ' -------------- ' -------------- FUNCTION IsPPTAvailable ( ) ' return 0 = no PowerPoint ' 1 = have PPT but no trusted access ' -1 = have PPT AND trusted access IsPPTAvailable = 0 set IsPPTAvailable = createObject ("powerPoint.application") IF ( istype ( IsPPTAvailable) == "DISPATCH" ) then ' test for access to PPT Trusted mode TRY IsPPTAvailable.VBE.CommandBars.Count IsPPTAvailable.quit set IsPPTAvailable = -1 sob ( myMenuExport , "add" , "menu" , "SPACER" ) sob ( myMenuExport , "add" , "menu" , "Vertical" , "PowerPoint, with trusted access" ) CATCH IsPPTAvailable.quit IsPPTAvailable=1 sob ( myMenuExport , "add" , "menu" , "SPACER" ) sob ( myMenuExport , "add" , "menu" , "Vertical" , "PowerPoint, no trusted access" ) END TRY END IF END function ' -------------- if ( IsPPTAvailable() ) then sob ( myMenuExport , "add" , "menu" , "Vertical" , "Sublist to Thumbnails" ) sob ( -1 ,"ON","CLICK", "cbtoolsMenuDBExportThumbnails") end if FUNCTION ExportThumbNails ( sobid ) IF ( ! isDbOpen ) then ErrorReport("no database open") cancelShow(0) toggleWarnColour ( sobid) EXIT function END if dim baseFolder = GetPath & "\\" ' start PPT dim myPPt = CreateObject( "powerpoint.application" ) IF ( ! myPPt ) THEN Message ="Could not create object" STOP END IF ' myPPt.visible = 0 ' does not work in later versions of PowerPoint / Excel..., only .visible = 1 dim TargetPPt = "Images.ppt" dim myPres , presPath myPres = myPPt.presentations.add(true) dim row = 0 , col = 0 , imgNbr = 1 dim atPage = 1 dim oPic, myTxtBox , iName ' we are going to place the thumbnails in a grid/matrix ' default: 8 across by 5 down SELECT count (*) from SubList where EXT in ( 'jpg' , 'jpeg' ) dim nbrImages = qrSingleValue dim matrixAcross dim matrixDown IF ( nbrImages < 5 ) then matrixAcross = 2 matrixDown = 2 ELSEIF ( nbrImages < 10 ) then matrixAcross = 3 matrixDown = 3 ELSEIF ( nbrImages < 17 ) then matrixAcross = 4 matrixDown = 4 ELSEIF ( nbrImages < 26 ) then matrixAcross = 5 matrixDown = 5 ELSE matrixAcross = 8 matrixDown = 5 END if ' to get the size of a given grid cell, create a blank slide now ' and use its size as the reference. ' We will also use this slide as the first thumbnail slide. dim mySlide = myPres.slides.add (atPage,12) ' 12 = blank slide dim cellwidth = myPres.PageSetup.SlideWidth / matrixAcross dim cellheight = ( myPres.PageSetup.SlideHeight - 20) / matrixDown ' I wanted the images to have some whitespace between them ' so each image is slightly smaller than the size of a grid cell. dim imagewidth = myPres.PageSetup.SlideWidth / (matrixAcross +1) dim imageheight = myPres.PageSetup.SlideHeight / (matrixDown+1) dim objWIA = CreateObject("WIA.ImageFile") dim objIP = CreateObject("WIA.ImageProcess") objIP.Filters.Add ( objIP.FilterInfos("Scale").FilterID ) ' max size of resized image = 300*300 pixels which is < 10 k bytes objIP.Filters(1).Properties("MaximumWidth").value = 300 objIP.Filters(1).Properties("MaximumHeight").value = 300 dim offsetHeight = (cellheight - imageheight) / 1 dim offsetWidth = (cellWidth - imagewidth ) / 2 myTxtBox = mySlide.Shapes.AddTextbox( 1 , offsetWidth , cellheight * matrixDown , cellwidth*matrixAcross ,offsetHeight ) myTxtBox.TextFrame.TextRange.Text (baseFolder) myTxtBox.TextFrame.TextRange.Font.Size = 8 cancelShow(1) canceltext ( nbrImages , 1 ) > select path , name , rid from SubList where ext in freestring = freestring & ImageFileExtensions >> order by name asc WITHQUERY (freestring ) WITHQUERY ( freestring ) IF ! ( imgNbr rem 10 ) then canceltext ( nbrImages - imgNbr , 2 ) END if IF ( col == matrixAcross ) then row = row + 1 IF ( row == matrixDown ) then row = 0 atPage = atPage + 1 mySlide = myPres.slides.add (atPage,12) ' 12 = blank slide myTxtBox = mySlide.Shapes.AddTextbox( 1 , offsetWidth , cellheight * matrixDown , cellwidth*matrixAcross ,offsetHeight ) myTxtBox.TextFrame .TextRange.Text (baseFolder) myTxtBox.TextFrame.TextRange.Font.Size = 8 END if col = 0 END If TRY deletefile (baseFolder & "pptimg.jpg" ) END try objWIA.LoadFile ( wqtext(1) & wqtext(2) ) myTxtBox = mySlide.Shapes.AddTextbox( 1 , cellwidth * col , cellheight * row , cellwidth ,offsetHeight ) iName ="E" & Right ( "0000000000" & wqtext(3) , 7 ) & "_" & wqtext(2) myTxtBox.TextFrame .TextRange.Text (iName) myTxtBox.TextFrame.TextRange.Font.Size = 8 objWIA = objIP.Apply(objWIA) objWIA.SaveFile ( baseFolder & "pptimg.jpg" ) oPic = mySlide.Shapes.AddPicture(baseFolder & "pptimg.jpg", False, True, offsetWidth + (cellwidth * col ), offsetHeight + (cellheight * row) ) oPic.LockAspectRatio = -1 IF ( ( oPic.rotation = 90 ) or ( oPic.Rotation = 270 ) ) then IF opic.height > imageWidth then opic.height = imagewidth if ( opic.width > imageHeight) then opic.width = imageHeight opic.left = opic.left - imagewidth opic.left = offsetWidth + (cellwidth * col ) ELSE opic.width = imageheight opic.left = opic.left - cellwidth if ( opic.height > imageWidth) then opic.height = imageWidth opic.left = opic.left + imagewidth END IF ELSEIF ( oPic.rotation = 180 ) then IF opic.height > opic.width then opic.height = imageheight if ( opic.width > imageWidth ) then opic.width = imageWidth ELSE opic.width = imagewidth if ( opic.height > imageheight ) then opic.height = imageheight END IF opic.top = offsetHeight + (cellheight * row) opic.left = offsetWidth + (cellwidth * col ) ELSE IF opic.height > imageheight then opic.height = imageheight if ( opic.width > imageWidth ) then opic.width = imageWidth ELSE opic.width = imagewidth if ( opic.height > imageheight ) then opic.height = imageheight END IF opic.top = offsetHeight + (cellheight * row) opic.left = offsetWidth + (cellwidth * col ) END if col = col + 1 imgNbr = imgNbr + 1 END withQuery TRY deletefile (baseFolder & "pptimg.jpg" ) END try myPres.saveas ( baseFolder & "ThumbNails.ppt") myPres.close myPPt.quit cancelShow(0) END Function FUNCTION cbtoolsMenuDBExportThumbnails ( sobid ) sob ( FileExportOvl , "set" , "show" , 1) sob ( NbrFilesInFilter , "SET", "TITLE" , "" & exportFilter ( "statistic" , "" , "" ) ) sob ( SubListTitleSob, "set" , "title", "Create a file sublist for Thumbnails" ) sob ( BtnExportFiles , "set", "Title" , "Export Thumbnails" ) sob ( -1 , "ON" , "PRESS" , "ExportThumbNails") sob ( BtnExportFiles , "set" , "show" , 1 ) sob ( BtnDeleteFiles , "set" , "show" , 0 ) sob ( PowerTaggingSob , "set" , "show" , 0 ) END Function sob ( -1 ,"ON","CLICK", "cbtoolsMenuDBExportThumbnails") ' -------------- FUNCTION cbExportFileListToExcel ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if dim ExlFile sob ( CommonSobResult, "empty" ) dim InitialExcelState IF ( isTable ( , "master") ) then SetFileFilter ( "EXCEL Files(*.xlsx)\0*.xl*\0All Files (*.*)\0*.*\0\0" ) ExlFile = getFile cancelClear () cancelText( "Export a sublist to Excel" , 0 ) cancelText( "Creating link to Excel" , 1 ) cancelText( " " , 3 ) cancelShow(1) InitialExcelState = xlActive IF ( ! xlRunning ) then xlopen () ELSE dim myWb = exl.Workbooks.Add END IF cancelText( "Exporting SubList table, should be less than 60 seconds" , 1 ) exl.ActiveSheet.name ="SubList" qrWriteToXL ( "select * from SubList") cancelText( "Formating SubList table, should be less than 60 seconds" , 1 ) xlTableFormat () exl.range(exl.cells(1,1),exl.cells(1,QRcols)).autofilter ' ----------------------------------------------------------- cancelText( "Creating Admin sheet" , 1 ) exl.worksheets.add().name = "Admin" exl.cells(1,1).value = "Content:" exl.Cells(2,1).value = "Source:" exl.Cells(3,1).value = "Generated:" exl.cells(1,2).value = "SubList table" exl.Cells(2,2).value = sob ( statusDBFile, "GET" ,"TITLE" ) exl.Cells(3,2).value = "'" & date & " " & time exl.Cells(9,1).value = "Generated using " & appName & " " & AppVersion exl.Cells(10,1).value = "Freeware, no installation, single file, < 5 Mbyte" exl.Cells(11,1).value = "www.PlodWare.com" exl.Cells(9,1).font.size = 8 exl.Cells(10,1).font.size = 8 exl.Cells(11,1).font.size = 8 exl.cells.columns.autofit exl.cells.VerticalAlignment = -4108 ' ----------------------------------------------------------- cancelText( "Saving workbook to file" ,1 ) exl.Application.DisplayAlerts = 0 exl.ActiveWorkBook.saveas ( ExlFile , 50 ) ' 50 = xlExcel12 (Excel Binary Workbook in 2007-2013 with or without macro's, xlsb) ' 51 = xlOpenXMLWorkbook (without macro's in 2007-2013, xlsx) ' 52 = xlOpenXMLWorkbookMacroEnabled (with or without macro's in 2007-2013, xlsm) ' 56 = xlExcel8 (97-2003 format in Excel 2007-2013, xls) ' ActiveWorkBook.close exl.Application.DisplayAlerts = 1 exl.ActiveWorkbook.Close IF ( ! InitialExcelState ) then exl.quit set exl = nothing END IF sob ( CommonSobResult, "ADD" , "ROW" , "Database saved to: " & EOL & ExlFile ) ELSE sob ( CommonSobResult, "SET" , "TITLE" , "No open database" ) END IF cancelShow(0) toggleWarnColour ( sobid) END function FUNCTION onPushOpenFolder ( sobid ) dim filename = sob ( FileListSob , "get", "title") if ( filename == "" ) then exit function END IF ' FileName field starts with "Internal" if file is in datatase currentFile = left ( filename, -10 ) IF ( left ( filename , 2 ) == "In" ) then > SELECT path from master where path ||name like freestring = freestring & " " & sqlstring ( currentFile) sql(freestring ) ShellExecute ( qrSingleValue) ELSE SELECT path from temp.inputFile ShellExecute ( qrSingleValue) END if END function ' ----------------------------------------------------- ' define the AREAs on the display dim applicationArea = sob(appWindow,"ADD", "CONTAINER","COLUMN.W") dim tmpSob, InsertWhere , DeleteWhere , InsertDeleteWhereSob tmpSob = sob ( applicationArea , "ADD", "CONTAINER" , "ROW" ) sob ( -1 , "add" , "LABEL" , "DB File " ) dim statusDBFile = sob(-1,"add","EDIT" , " " ) sob ( statusDBFile , "SET" ,"rw" , 0 ) tmpSob = sob ( applicationArea , "add" , "container" , "row" ) sob ( tmpSob , "ADD" , "LABEL" , "File(s): " ) dim FileListSob = sob ( tmpSob , "add", "COMBO" ,"dropdown", " " ) sob ( FileListSob , "set" , "stretch" , 1 ) sob ( tmpSob , "ADD" , "Button" , "push" , "Open folder" ) sob ( -1 , "ON" , "CLICK" , "onPushOpenFolder" ) sob ( tmpSob , "ADD" , "Button" , "push" , "Delete file" ) sob ( -1 , "ON" , "CLICK" , "onPushOfferFileDelete" ) ' dim mainArea= sob(applicationArea,"ADD", "CONTAINER","ROW.H") dim mainArea= sob ( applicationArea ,"ADD", "CONTAINER","PANEL.v", " " ,1 ) dim mainLP = sob( mainArea, "GET","PANEL.F") dim mainRP = sob( mainArea, "GET","PANEL.S") sob ( mainRP ,"ADD", "CONTAINER","PANEL.h", " " ,1 ) dim upperP = sob( -1, "GET","PANEL.F") dim lowerP = sob( -1, "GET","PANEL.S") sob ( lowerP,"ADD", "CONTAINER","PANEL.v", " " , 1 ) dim lowMidArea = sob( -1, "GET","PANEL.F") dim lowRightArea = sob( -1, "GET","PANEL.S") ' ----------------------------------------------------- ' now assign CONTENT to these areas ' ----------------------------------------------------- dim TagToolsOverlay = sob ( mainLP, "ADD", "container", "OVERLAY") ' each direct child of an OVERLAY container overlaps one another dim TagManageOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN.w") dim FileExportOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN.w") dim FileTaggingOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") dim CommonOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") dim HelpFixedOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") dim RemoveExternalDupsOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") dim RescanDatabaseOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") dim ScanNewFolderOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") dim DeleteOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") dim ExportOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") dim TagCommentOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") dim RubberBandOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") dim ImageGroupOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") dim ImageManagementOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") dim TagTxtOvl = sob ( TagToolsOverlay ,"ADD", "container", "COLUMN") ' ----------------------------------------------------- dim TagToolsArea = sob ( TagManageOvl , "ADD", "CONTAINER", "COLUMN.W" ) dim ExtensionArea = sob ( TagToolsArea , "ADD", "CONTAINER", "COLUMN.W" ) dim TagGroupContent = sob ( TagToolsArea , "ADD", "CONTAINER", "COLUMN.W" ) dim TagTxtContent = sob ( TagToolsArea , "ADD", "CONTAINER", "COLUMN.W" ) dim TagFtxtContent = sob ( TagToolsArea , "ADD", "CONTAINER", "COLUMN.W" ) ' ----------------------------------------------------- ' populate: StatusArea ' ----------------------------------------------------- dim StatusArea = sob ( upperP , "ADD", "CONTAINER" , "COLUMN.W" ) sob ("RGB","DEFAULT") dim StatusCols = sob ( StatusArea , "ADD", "CONTAINER" , "COLUMN.W" ) sob ( StatusCols , "ADD", "CONTAINER" , "ROW" ) sob ( -1,"add","LABEL" , "Status " ) dim StatusTxt = sob(-1,"add","LABEL" , " " ) FUNCTION ErrorReport ( txt ) sob (StatusTxt,"SET" ,"TITLE" , "ERROR: " & txt & EOL ) IF ( sob (StatusTxt,"GET", "RGB" ) = ErrorCol ) then sob (StatusTxt,"SET", "RGB" , WarningCol ) ELSE sob (StatusTxt,"SET", "RGB" , ErrorCol ) END IF END Function FUNCTION NoErrorReport ( txt) sob (StatusTxt, "SET" , "TITLE" , txt & EOL ) sob (StatusTxt, "SET" , "RGB" , defStatusCol ) END Function dim FileInfoSob = sob ( StatusArea , "add" , "container" , "column.h" ) ' tmpSob = sob ( FileInfoSob , "add" , "container" , "row" ) ' sob ( tmpSob , "ADD" , "LABEL" , "Hash " ) ' dim hashSob = sob ( tmpSob , "ADD" , "LABEL" , " " ) dim PathFile = sob(StatusArea,"add","Edit.rows" , " ", 5 ) FUNCTION mcPathFile ( sobid, clickValue ) report ( "click " & clickvalue ) END function ' sob ( pathFile, "on" , "MOUSECLICKS" , "mcPathFile" ) ' ----------------------------------------------------- ' populate: Tag Group Tools ' ----------------------------------------------------- sob ("RGB","DEFAULT") FUNCTION UpdateTagNameList ( sobid , TagType ) sob( sobid , "empty" ) > select ttName from tagTable where ttGroup like $tag order by ttName asc freestring = replace ( freestring , "$tag" , sqlstring ( TagType & TagGroupId) ) WITHQUERY ( freestring ) sob( TagNameComboSOB , "add" , "row" , wqText(1) ) END withQuery sob( sobid , "SET", "SELECTION" , "ROW", 1 ) > select ttComment from tagTable where ttGroup like $tag order by ttName asc limit 1 freestring = replace ( freestring , "$tag" , sqlstring ( TagType & TagGroupId) ) sql (freestring) IF ( istype ( qrSingleValue) == "string" ) then sob( TagTxtField , "SeT", "TITLE", qrSingleValue ) ELSE sob( TagTxtField , "SeT", "TITLE", "" ) END IF END FUNCTION FUNCTION onTagGroupComboSel ( sobid , rowid ) dim useRGB dim selTxt = sob ( sobid, "GET", "TITLE") sob ( TagGroupDoItSOB , "SET", "RGB" , defBtnCol ) sob ( TagNameDoItSOB , "SET", "RGB" , defBtnCol ) sob ( TagTextDoItSOB , "SET", "RGB" , defBtnCol ) IF ( selTxt == NoTag ) then sob ( TagNameComboSob , "set", "title" , "" ) sob ( TagTxtField , "set", "title" , "" ) sob ( TagNameNewNameSOB , "set", "title" , "" ) ELSE UpdateTagNameList ( TagNameComboSob , selTxt ) dim actionTxt = sob ( TagGroupActionComboSob, "GET", "TITLE") IF ( actionTxt <> noTag ) then sob ( TagGroupDoItSOB , "SET", "RGB" , rgb ( 0,255,0) ) END IF actionTxt = sob ( TagNameActionComboSob, "GET", "TITLE") IF ( actionTxt <> noTag ) then sob ( TagNameDoItSOB , "SET", "RGB" , rgb ( 0,255,0) ) sob ( TagNameNewNameSOB ,"SET", "RGB" , -1 ) IF ( actionTxt <> "delete" ) theN sob ( TagNameNewNameSOB ,"SET", "RGB" , rgb ( 0x0ff, 0xa0, 0xa0 ) ) END IF END IF actionTxt = sob ( TagTextActionComboSob, "GET", "TITLE") IF ( actionTxt <> noTag ) then sob ( TagTextDoItSOB , "SET", "RGB" , rgb ( 0,255,0) ) END IF onEditedTagNameFilter ( TagNameFilterSOB , " " ) IF ( sob ( TagNameActionComboSOB , "GET", "TITLE") == "derive") then sob ( TagNameNewNameSOB , "set", "title" , sob( TagNameComboSOB , "GET", "title") ) END IF END IF END FUNCTION ' ---------------------------------------------------------------------------- FUNCTION cbSelectFreeTextToolAction ( sobid, rowid ) dim actionTxt = sob ( sobid , "GET", "TITLE" ) dim TextTxt = sob ( FreeTextToolCombo , "GET", "TITLE") Sob ( FreeTextToolDoItSob , "SET", "TITLE" , actionTxt ) IF ( TextTxt == noTag ) then sob ( FreeTextToolDoItSob , "SET", "RGB" , defbtnCol ) ELSE sob ( FreeTextToolDoItSob , "SET", "RGB" , rgb ( 0 , 255,0) ) END if END FUNCTION FUNCTION cbFreeTextToolDoIt ( sobid ) dim actionTxt = sob ( sobid , "GET", "TITLE" ) dim newCol SELECT CASE actionTxt CASE noTag EXIT function CASE "new" ' use SQLite TRIM function to remove spaces and tabs > select trim ( freestring = freestring & " " & sqlidentifier ( sob ( FreeTextToolEditSob , "GET", "TITLE") ) >> , char(9) || ' ' ) sql ( freestring ) newCol = qrSingleValue & FreeTxtId > select count (*) from pragma_table_info ( 'master' ) where name like freestring = freestring & " " & sqlidentifier ( newCol ) sql ( freestring ) IF ( qrSingleValue == 0 ) then > alter table master add column freestring = freestring & " " & sqlidentifier (newCol ) sql ( freestring ) > update master set freestring = freestring & " " & sqlidentifier (newCol ) >> = '' sql ( freestring ) ELSE ToggleWarnColour ( sobid ) EXIT function END IF CASE "delete" > alter table master drop column freestring = freestring & " " & sqlidentifier ( sob ( FreeTextToolCombo , "GET", "TITLE") & FreeTxtId ) sql ( freestring ) CASE else ToggleWarnColour ( sobid ) EXIT function END SELECT CreateUsersTagTableAndColumnsList () ToggleOKColour ( sobid ) END FUNCTION tmpSob = sob ( TagGroupContent , "ADD", "CONTAINER", "COLUMN.W") sob ( tmpSob ,"add","LABEL" , "Free Text Tool" ) sob ( -1 , "SET" , "RGB" , 0x0ff00 ) tmpSob = sob ( tmpSob , "ADD", "CONTAINER", "ROW") sob ( tmpSob , "add" , "COMBO" , "DROPDOWN" , " " ) sob ( -1 , "add" , "ROW" , noTag ) sob ( -1 , "add" , "ROW" , "New" ) sob ( -1 , "add" , "ROW" , "Delete" ) sob ( -1 , "SET" , "SELECTION" , "ROW" , 1 ) sob ( -1 , "ON" , "SELECTION" , "cbSelectFreeTextToolAction" ) dim FreeTextToolCombo = sob ( tmpSob , "add" , "COMBO" , "DROPDOWN", defTypeComboText ) sob ( -1, "set", "stretch", 1 ) dim FreeTextToolDoItSob = sob ( tmpSob , "add" , "BUTTON" , "PUSH", noTag ) sob ( -1 , "set" , "title" , noTag ) sob ( -1 , "ON" , "press" , "cbFreeTextToolDoIt") ' -------------------- sob ( TagGroupContent , "ADD", "CONTAINER" , "ROW.h" ) sob ( -1 , "add" , "LABEL" , "New " ) dim FreeTextToolEditSob = sob ( -1 , "add","edit" , " " ) sob ( -1 , "empty") ' ------------------------------- sob( TagGroupContent ,"add" ,"LABEL" , "Tag Group Tools" ) sob ( -1 , "SET" , "RGB" , rgb ( 0 , 255 , 0 ) ) ' ------------------------------- dim TagGroupToolsAreaTop = sob ( TagGroupContent , "ADD", "CONTAINER" , "ROW.h" ) dim TagGroupToolsArea = sob ( TagGroupToolsAreaTop , "ADD", "CONTAINER" , "COLUMN.hw" ) sob ( -1 , "SET", "STRETCH" , 1 ) ' ------------------------------- sob ( TagGroupToolsArea , "ADD", "CONTAINER" , "ROW.h" ) sob ( -1,"add","LABEL", "Tag Group" ) dim TagGroupComboSob = sob(-1,"add","COMBO" , "dropdown" , " " ) sob ( TagGroupComboSob , "ON", "SELECTION" , "onTagGroupComboSel") ' to automatically populate with Tag Group changes isTagTypeCombo ( TagGroupComboSob ) FUNCTION OnTagGroupActionComboSelect ( sobid , rowid) dim useRGB = defBtnCol sob ( TagNameDoItSOB ,"SET", "rgb" , useRGB ) sob ( TagNameNewNameSOB ,"SET", "RGB" , -1 ) dim actionTxt = sob ( TagGroupActionComboSob , "GET", "TITLE" ) sob ( TagGroupDoItSob , "set", "title" , actionTxt ) IF ( sob ( TagGroupComboSob , "GET", "TITLE" ) == noTag ) then EXIT function END IF IF ( actionTxt <> noTag ) then sob ( TagTextDoItSob ,"SET", "rgb" , rgb ( 0,255,0) ) IF ( actionTxt <> "delete" ) theN sob ( TagNameNewNameSOB ,"SET", "RGB" , rgb ( 0x0ff, 0xa0, 0xa0 ) ) ELSE sob ( TagNameNewNameSOB ,"SET", "RGB" , -1 ) END IF END IF END Function ' ------------------------------- sob ( TagGroupToolsArea , "ADD", "CONTAINER" , "ROW.h" ) sob ( -1,"add","LABEL", "Action " ) dim TagGroupActionComboSob = sob(-1,"add","COMBO" , "dropdown" , defActionComboText ) sob ( -1 , "add" , "row" , NoTag ) sob ( -1 , "add" , "row" , "new" ) sob ( -1 , "add" , "row" , "replace" ) sob ( -1 , "add" , "row" , "delete" ) sob ( -1 , "add" , "row" , "move" ) sob ( -1 , "SET" , "SELECTION", "row" , 1 ) sob ( TagGroupActionComboSob , "ON" , "SELECTION" , "OnTagGroupActionComboSelect" ) ' ------------------------------- sob ( TagGroupToolsArea , "ADD", "CONTAINER" , "ROW.h" ) sob(-1,"add","LABEL", "New " ) dim TagGroupNewNameSOB = sob(-1,"add","edit" , " " ) sob (-1, "empty") ' ------------------------------- sob ( TagGroupToolsAreaTop , "ADD", "CONTAINER" , "COLUMN" ) dim TagGroupDoItSob = sob(-1,"add","BUTTON","PUSH", NoTag ) ' ----------------------------------------------------- ' populate: Tag Name Tools ' ----------------------------------------------------- FUNCTION updateNewTagNameField () dim actionTxt = sob ( TagNameActionComboSOB , "GET", "TITLE" ) sob ( TagNameDoItSOB ,"SET", "TITLE" , actionTxt ) IF ( sob ( TagGroupComboSob , "GET", "TITLE" ) == noTag ) then EXIT function END IF sob ( TagNameDoItSOB ,"SET", "RGB" , rgb (0,255,0) ) IF ( actionTxt == noTag ) then sob ( TagNameDoItSOB ,"SET", "RGB" , defbtnCol ) ELSE sob ( TagNameDoItSOB ,"SET", "RGB" , rgb (0,255,0) ) END IF IF ( actionTxt == "derive" ) then sob ( TagNameNewNameSOB , "set", "title" , sob( TagNameComboSOB , "GET", "title") ) END IF END Function FUNCTION onEditTagNameFilter ( sobid , editChr ) IF ( editChr == escChr) then sob( sobid , "EMPTY") sob( sob(sobid ,"SIBLING", 1) , "EMPTY") END IF END FUNCTION FUNCTION onEditedTagNameFilter ( sobid , editChr ) ' for every character entered in to the filter box ' trigger an update of the related tag Txt combo sob( TagTxtField , "SEt", "TITLE", "" ) IF ( editChr == escChr) then sob( sobid , "EMPTY") sob( sob(sobid ,"SIBLING", 1) , "EMPTY") ELSE dim useGroupCombo = TagGroupComboSob dim useTextCombo = TagNameComboSob ' set the filter's COMBO to be empty sob ( useTextCombo ,"empty") ' get the TagType dim TagGroup = sob( useGroupCombo, "GET" , "TITLE" ) dim filterTxt = sob(sobid,"GET","TITLE") ' do not include any special (non-printable) character in the filter text IF ( editChr < 0x020 ) then ELSEIF ( editChr > 0x7e ) then ELSE filterTxt = filterTxt & chr(editChr) END IF IF ( isDBopen ) then ' find the matching TAG txts (yes allow > 1 ) > select ttname from tagTable where ttGroup like $Type and ttName like '%' || $txt || '%' order by ttName asc freestring = replace ( freestring , "$Type" , sqlIdentifier ( TagGroup & TagGroupId ) ) freestring = replace ( freestring , "$txt" , sqlString ( filterTxt ) ) WITHQUERY (freestring) ' place the tag txts in the filter's COMBO sob ( useTextCombo, "add", "row" , wqText(1)) END withquery END if ' default is to select the first txt in the combo sob( useTextCombo , "SET", "SELECTION" , "ROW", 1 ) dim tagName = sob ( useTextCombo , "GET", "TITLE") IF ( sob ( TagNameActionComboSOB , "GET", "TITLE") == "derive") then sob ( TagNameNewNameSOB , "set", "title" , Tagname ) ELSEIF ( sob ( TagNameActionComboSOB , "GET", "TITLE") == NoTag) then ' sob ( TagNameNewNameSOB , "set", "title" , "" ) END IF > select ttComment from tagTable where ttGroup like $tag and ttName like $name freestring = replace ( freestring , "$tag" , sqlstring ( TagGroup & TagGroupId) ) freestring = replace ( freestring , "$name" , sqlstring ( tagName ) ) sql ( freestring ) IF ( isType ( qrsinglevalue) == "String") then sob( TagTxtField , "SEt", "TITLE", qrSingleValue ) END IF END IF ' updateNewTagNameField () END FUNCTION sob ( TagTxtContent ,"add" , "SPACE" , 0 , 10 ) sob ( -1 , "SET" , "RGB" , 0x0ffffff ) sob ( TagTxtContent ,"add" , "LABEL" , "Tag Name Tools" ) sob ( -1 , "SET" , "RGB" , rgb ( 0 , 255 , 0 ) ) dim TagNameAreaTOP = sob ( TagTxtContent , "ADD", "CONTAINER" , "ROW.h" ) dim TagNameArea = sob ( TagNameAreaTOP , "ADD", "CONTAINER" , "COLUMN.HW" ) sob ( -1 , "SET", "STRETCH" , 1 ) sob ( TagNameArea , "ADD", "CONTAINER" , "ROW.h" ) sob ( -1 , "add" ,"LABEL", "Filter " ) dim TagNameFilterSOB = sob (-1 , "Add" , "edit" , " " ) sob (-1 , "empty") sob (-1 , "ON","EDIT" , "onEditTagNameFilter" ) sob (-1 , "ON","EDITED" , "onEditedTagNameFilter" ) sob ( TagNameArea , "ADD", "CONTAINER" , "ROW.h" ) sob ( -1,"add","LABEL", "Tag Name " ) dim TagNameComboSob = sob(-1,"add","COMBO" , "dropdown" , " " ) FUNCTION TagNameComboSelect ( sobid , rowid ) updateNewTagNameField () END Function sob ( TagNameComboSob , "ON" , "SELECTION" , "TagNameComboSelect" ) sob ( TagNameArea , "ADD", "CONTAINER" , "ROW.h" ) sob(-1,"add","LABEL", "Action " ) dim TagNameActionComboSOB = sob(-1,"add","COMBO" , "dropdown" , " " ) sob ( -1 , "add" , "row" , NoTag ) sob ( -1 , "add" , "row" , "new" ) sob ( -1 , "add" , "row" , "derive" ) sob ( -1 , "add" , "row" , "rename" ) sob ( -1 , "add" , "row" , "delete" ) sob ( -1 , "add" , "row" , "move" ) sob ( -1 , "SET" , "SELECTION", "ROW" , 1) sob ( TagNameArea , "ADD", "CONTAINER" , "ROW" ) sob(-1,"add","LABEL", "New " ) dim TagNameNewNameSOB = sob(-1,"add","edit" , " " ) sob (-1, "empty") FUNCTION OnTagNameActionComboSelect ( sobid , rowid ) updateNewTagNameField () END Function sob ( TagNameActionComboSOB , "ON" , "SELECTION" , "OnTagNameActionComboSelect" ) dim TagNameDoItSOB = sob ( TagNameAreaTOP ,"add","BUTTON","PUSH", NoTag ) ' ----------------------------------------------------- ' populate: Tag Text Tools ' ----------------------------------------------------- sob( TagFtxtContent ,"add" ,"SPACE" , 0 , 10 ) sob ( -1 , "SET" , "RGB" , 0x0ffffff ) sob( TagFtxtContent ,"add","LABEL" , "Tag Text Tools" ) sob ( -1 , "SET" , "RGB" , rgb ( 0 , 255 , 0 ) ) dim TagTextAreaTop = sob ( TagFtxtContent , "ADD", "CONTAINER" , "ROW" ) dim TagTextArea = sob ( TagTextAreaTop , "ADD", "CONTAINER" , "COLUMN.HW" ) sob ( -1 , "SET", "STRETCH" , 1 ) sob ( TagTextArea , "ADD", "CONTAINER" , "ROW.h" ) sob(-1,"add","LABEL", "Action " ) FUNCTION onTagTextActionComboSob ( sobid , rowid ) dim actionTxt = sob ( TagTextActionComboSob , "get", "title") sob ( TagTextDoItSOB , "set", "title" , actionTxt) IF ( sob ( TagGroupComboSob , "get", "title") == NoTag ) then EXIT function END if IF ( actionTxt == notag ) then sob ( TagTextDoItSOB , "set", "rgb" , defBtnCol ) ELSE sob ( TagTextDoItSOB , "set", "rgb" , rgb(0,255,0) ) END if END function FUNCTION onTagTextDoit ( sobid ) IF ( ( sob(sobid,"GET" , "TITLE" ) == NoTag ) or ( sob(sobid,"GET" , "TITLE" ) == "" ) ) then EXIT function END IF IF ( ! isDBopen ) then ErrorReport ("No database selected" ) EXIT function END IF dim tagType = sob ( TagGroupComboSob , "GET" , "TITLE" ) dim tagName = sob ( TagNameComboSob , "GET" , "TITLE" ) dim tagComment = sob ( TagTxtField , "GET" , "TITLE" ) dim actionTxt = sob ( TagTextActionComboSob , "GET" , "TITLE" ) IF ( actionTxt == "show" ) then > select ttComment from tagTable WHERE ttGroup like $tagType and ttName like $tagTxt freestring = replace ( freestring , "$tagType" , sqlString ( tagType & TagGroupId ) ) freestring = replace ( freestring , "$tagTxt" , sqlString ( tagName ) ) sql ( freestring ) sob ( PathFile , "SET" , "TITLE" , qrSingleValue ) ELSE IF ( actionTxt == "clear" ) then tagComment = "" sob ( TagTxtField , "empty" ) END IF > UPDATE tagTable SET ttComment = $ftxt WHERE ttGroup like $tagType and ttName like $tagTxt freestring = replace ( freestring , "$ftxt" , sqlString ( tagComment ) ) freestring = replace ( freestring , "$tagType" , sqlString ( tagType & TagGroupId ) ) freestring = replace ( freestring , "$tagTxt" , sqlString ( tagName ) ) sql ( freestring ) END IF toggleWarnColour ( TagTextDoItSOB ) END FUNCTION FUNCTION onTagfTxtSelect ( sobid, rowid ) dim actionTxt = sob( sobid, "GET", "ROW", rowid ) sob ( TagTextDoItSOB , "SET", "TITLE" , actionTxt ) IF ( actionTxt == NoTag ) then sob ( TagTextDoItSOB , "SET", "RGB" , defTextCol ) ELSE sob ( TagTextDoItSOB , "SET", "RGB" , RGB ( 0xff ,0,0 ) ) END if END function dim TagTextActionComboSob = sob(-1,"add","COMBO" , "dropdown" , " " ) sob ( -1 , "ADD" , "ROW" , NoTag ) ' sob ( -1 , "ADD" , "ROW" ,"show" ) sob ( -1 , "ADD" , "ROW" ,"edit" ) sob ( -1 , "ADD" , "ROW" ,"clear" ) sob ( -1 , "SET" , "SELECTION", "ROW" , 1) sob ( -1 , "ON" , "SELECTION", "onTagTextActionComboSob") ' ------------------ sob ( TagTextAreaTop , "ADD", "CONTAINER" , "ROW" ) dim TagTextDoItSOB = sob ( -1 ,"add","BUTTON","PUSH", NoTag ) sob ( -1 , "on" , "press" , "OnTagTextDoit") sob ( TagFtxtContent , "ADD", "SPACE", 0,10) sob ( -1 , "SET" , "RGB" , 0x0ffffff ) ' sob ( TagTextArea , "ADD", "CONTAINER" , "ROW" ) dim TagTxtField = sob (TagFtxtContent, "ADD" , "Edit.rows", " " , 3 ) ' ----------------------------------------------------- ' populate: upper right ' ----------------------------------------------------- ' ----------------------------------------------------- ' IMAGE things ' ----------------------------------------------------- dim XY = 5 dim maxThumbNailIndex = 8 dim i ' ----------------------------------------------------- ' IMAGE MANAGEMENT ' ----------------------------------------------------- ' sob ( ImageManagementOvl , "SET", "SHOW", 1 ) dim ImageMngmtArea = sob ( ImageManagementOvl , "ADD", "CONTAINER", "COLUMN.W" ) dim ImageMngmtInfo = sob ( ImageMngmtArea, "ADD", "LABEL", "" ) tmpSob= sob ( ImageMngmtArea , "ADD", "CONTAINER", "row.W" ) ' ------------------------------------------------- sob ( tmpSob , "ADD", "BUTTON" , "PUSH" , "<<-" ) FUNCTION startImageMngnt ( sobid ) ImageMngmtBase = 0 ImageMngmtDisplay () END FUNCTION sob ( -1 , "ON" , "click" , "startImageMngnt" ) ' ------------------------------------------------- sob ( tmpSob , "ADD", "BUTTON" , "PUSH" , "<--" ) FUNCTION prevImageMngnt ( sobid ) ImageMngmtBase = ImageMngmtBase - ImageMngmtDisplayRows - ImageMngmtDisplayRows ImageMngmtDisplay () END FUNCTION sob ( -1 , "ON" , "click" , "prevImageMngnt" ) ' ------------------------------------------------- sob ( tmpSob , "ADD", "LABEL" , "from" ) sob ( -1 , "set" , "rgb" , -1 ) dim ImageMngmtFrom = sob ( tmpSob , "ADD", "LABEL" , " " ) sob ( tmpSob , "ADD", "LABEL" , "to" ) sob ( -1 , "set" , "rgb" , -1 ) dim ImageMngmtTo = sob ( tmpSob , "ADD", "LABEL" , " " ) sob ( tmpSob , "ADD", "LABEL" , "of" ) sob ( -1 , "set" , "rgb" , -1 ) dim ImageMngmtOf = sob ( tmpSob , "ADD", "LABEL" , " " ) ' ------------------------------------------------- sob ( tmpSob , "ADD", "BUTTON" , "PUSH" , "-->" ) FUNCTION nextImageMngnt ( sobid ) ImageMngmtDisplay () END FUNCTION sob ( -1 , "ON" , "click" , "nextImageMngnt" ) ' ------------------------------------------------- sob ( tmpSob , "ADD", "BUTTON" , "PUSH" , "->>" ) FUNCTION EndImageMngnt ( sobid ) SELECT max(rowid) from ImageMngmtTbl ImageMngmtBase = qrSingleValue - ImageMngmtDisplayRows ImageMngmtDisplay () END FUNCTION sob ( -1 , "ON" , "click" , "EndImageMngnt" ) ' ------------------------------------------------- ' sob ( -1 , "SET", "STRETCH" , 0 ) dim scrolledImageMngmt = sob ( ImageMngmtArea , "ADD", "CONTAINER" , "BOX.SCROLLED", " ",20) dim siImageMngmtMatrix = sob ( scrolledImageMngmt, "ADD", "CONTAINER", "matrix.c" , 5) FUNCTION cbOnImageMngmtThumbnail ( sobid ) report ( sob(sobid, "get", "data")) > select path from Master where rowid = freestring = freestring & " " & sob(sobid, "get", "data") WITHQUERY ( freestring ) ShellExecute ( wqText(1 ) ) END withquery END FUNCTION ' -------------------------------------------------------------------------------------------------- ' apply a POPUP menu to the Image Management ' -------------------------------------------------------------------------------------------------- FUNCTION CBpopupImageMngmtOpenFolder ( sobid ) dim rid = sob(siTriggeringThumbNail, "get", "data") if ( ! rid ) then exit function > select path from Master where rowid = freestring = freestring & " " & rid WITHQUERY ( freestring ) ShellExecute ( wqText(1 ) ) END withquery END FUNCTION sob (OnImageMngmtPopUp , "add", "menu","Vertical", "Open Folder") sob ( -1 , "ON" , "CLICK" , "CBpopupImageMngmtOpenFolder") ' ------------------------------------------------- FUNCTION CBpopupImageMngmtDetails ( sobid ) dim ridA = sob(siTriggeringThumbNail, "get", "data") dim SizeA, FileA , pHash , dHash > select path ||name , pHash , dHash , size from master where rowid = freestring = freestring & " " & ridA WITHQUERY ( freestring ) FileA = wqtext(1) SizeA = wqInt (4) END withquery dim txt = "" IF ( ! isFile ( FileA ) ) then txt = " File does not exist " & FileA sob ( ImageMngmtInfo , "SET" , "TITLE", txt ) EXIT function END if sob (TagInfo, "empty") dim tmptxt tmptxt = right("000000" & ridA,6) txt = txt & tmptxt tmptxt = right( "00000" & CANVAS ( "IMAGE.FILE", "GET" , "WIDTH" , FileA ) , 5 ) txt = txt & tmptxt tmptxt = right( "00000" & CANVAS ( "IMAGE.FILE", "GET" , "HEIGHT" , FileA ) , 5 ) txt = txt & "/" & tmptxt txt = txt & " " & FileA sob ( ImageMngmtInfo , "SET" , "TITLE", txt ) dim sipmatrix = SOB ( siTriggeringThumbNail ,"GET" , "PARENT" ) ' dim ChildCnt = SOB ( sipmatrix , "CHILD" , "COUNT" ) dim colNbr = SOB ( siTriggeringThumbNail , "GET" , "COLUMN.NUMBER" ) dim Col00 = sob ( siTriggeringThumbNail , "SIBLING", -1*(colNbr-1) ) dim RoC = sob ( sipmatrix , "GET", "DIMENSION") dim i dim tmpSob , rid sob (TagInfo,"add", "row" , "row: " & EOL ) sob (TagInfo,"add", "row" , "rowid width height" & EOL ) for i = 1 to abs(RoC) tmpSob = sob ( Col00 , "SIBLING", i - 1 ) rid = sob ( tmpSob , "get", "data") If ( rid ) then > select path ||name , num2Hex( pHash ) , num2Hex( dHash ) , size from master where rowid = freestring = freestring & " " & rid WITHQUERY ( freestring ) FileA = wqtext(1) pHash = wqText(2) dHash = wqText(3) END withquery tmptxt = right("000000" & rid ,6) sob (TagInfo,"add", "row" , tmptxt & " " ) tmptxt = right( "00000" & CANVAS ( "IMAGE.FILE", "GET" , "WIDTH" , FileA ) , 5 ) sob (TagInfo,"add", "row" , tmptxt ) sob (TagInfo,"add", "row" , " " ) tmptxt = right( "00000" & CANVAS ( "IMAGE.FILE", "GET" , "HEIGHT" , FileA ) , 5 ) sob (TagInfo,"add", "row" , tmptxt ) sob (TagInfo,"add", "row" , " " & pHash ) sob (TagInfo,"add", "row" , " " & dHash ) sob (TagInfo,"add", "row" , tmptxt & EOL ) end if next i END function sob (OnImageMngmtPopUp , "add", "menu","Vertical", "Details") sob ( -1 , "ON" , "CLICK" , "CBpopupImageMngmtDetails") ' ------------------------------------------------- FUNCTION CBpopupImageToTagger ( sobid ) dim rid = sob(siTriggeringThumbNail, "get", "data") > select path||name from Master where rowid = freestring = freestring & " " & rid SilentSQL ( freestring ) cbSingleFile ( qrSingleValue ) END FUNCTION sob (OnImageMngmtPopUp , "add", "menu","Vertical", "To Tagger") sob ( -1 , "ON" , "CLICK" , "CBpopupImageToTagger") ' ------------------------------------------------- FUNCTION CBpopupImageIgnore ( sobid ) dim rid = sob(siTriggeringThumbNail, "get", "data") > Update Master >> set IgnoreFile = 1 >> where rowid = freestring = freestring & " " & rid SilentSQL ( freestring ) > delete from ImageMngmtTbl >> where SelfA = freestring = freestring & " " & rid SilentSQL ( freestring ) ImageMngmtBase = ImageMngmtBase - ImageMngmtDisplayRows ImageMngmtDisplay () END FUNCTION sob (OnImageMngmtPopUp , "add", "menu","Vertical", "Ignore Image") sob ( -1 , "ON" , "CLICK" , "CBpopupImageIgnore") ' ------------------------------------------------- ' -------------------------------------------------------------------------------------------------- FUNCTION cbImageMngmtMOUSECLICKS ( sobid, clickId ) ' right mouse button clickid = 7 if ( clickid <> 7 ) then exit function dim ridA = sob ( sobid , "GET" , "DATA" ) if ( ! ridA ) then exit function siTriggeringThumbNail = sobid sob(OnImageMngmtPopUp,"SET","POSITION", sob ( appWindow , "get" , "cursor.x" ), sob ( appWindow , "get" , "cursor.y" ) ) sob ( OnImageMngmtPopUp , "SET", "SHOW" , 1 ) END function FOR i = 1 to 35 tmpSob = sob (siImageMngmtMatrix , "ADD","image.holder",0 ,50,75 ) sob ( tmpSob , "ON" , "click" , "cbOnImageMngmtThumbnail" ) sob ( tmpSob , "ON" , "MOUSECLICKS", "cbImageMngmtMOUSECLICKS") NEXT i ' sob( ImageMngmtArea ,"add" ,"SPACE" , 0 , 10 ) ' sob ( -1 , "SET" , "RGB" , 0x0ffffff ) ' ----------------------------------------------------- ' IMAGE GROUP ' ----------------------------------------------------- dim ImageGroupArea = sob ( ImageGroupOvl , "ADD", "CONTAINER", "COLUMN.W" ) dim myConA = sob ( ImageGroupArea, "ADD", "CONTAINER", "column.w") SOB ( "OVERRIDE" , "STYLE" , 0x052000300) ' force style of next LABEL to be left justified dim RefInfo = sob ( myConA , "ADD", "LABEL" , " ") SOB ( "OVERRIDE" , "STYLE" , 0x052000300) ' force style of next LABEL to be left justified dim TarInfo = sob ( myConA , "ADD", "LABEL" , " ") dim ImageGroupWH = sob ( myConA , "ADD", "CONTAINER" , "COLUMN.HW", " ",10) ' --------------------------- FUNCTION cbOnThumbNail ( sobid ) if ( ! sob ( sobid, "get" , "data") ) then exit function dim fName > select path||name from master where rowid = freestring = freestring & " " & sob ( sobid, "get" , "data") WITHQUERY ( freestring ) fName = wqText(1) END withquery cbSingleFile ( fName ) END function dim siTriggeringThumbNail ' --------------------------------------------- sob (OnImagePopUpSimilar , "add", "menu","Vertical", "Open Folder") FUNCTION CBpopupOpenFolder ( sobId ) > select path from Master where rowid = freestring = freestring & " " & sob ( siTriggeringThumbNail , "GET", "DATA") WITHQUERY ( freestring ) ShellExecute ( wqText(1 ) ) END withquery END function sob ( -1 , "ON" , "CLICK" , "CBpopupOpenFolder") sob (OnImagePopUpGroup , "add", "menu","Vertical", "Open Folder") sob ( -1 , "ON" , "CLICK" , "CBpopupOpenFolder") ' --------------------------------------------- sob (OnImagePopUpGroup , "add", "menu","Vertical", "Remove from Image Group") FUNCTION CBpopupRemoveFromGroup ( sobId ) dim xSelf = sob ( siTriggeringThumbNail , "GET", "DATA") dim zSelf = sob ( siGroup00 , "GET", "DATA") removeImageXfromGroupZ ( xSelf, zSelf ) ' ---------------------------------------------------- removeAllThumbNails ( ) GetCopies ( zSelf ) END function sob ( -1 , "ON" , "CLICK" , "CBpopupRemoveFromGroup") ' --------------------------------------------- ' --------------------------------------------- sob (OnImagePopUpSimilar , "add", "menu","Vertical", "Add to Image Group") FUNCTION CBpopupAddToGroup ( sobId ) ' get the GROUP of the reference thumbnail dim ImageGroup dim PromotedRowid = sob ( siTriggeringThumbNail , "GET", "DATA") > select hash from master where rowid = freestring = freestring & " " & PromotedRowid SilentSQL (freestring ) dim PromotedHash = qrSingleValue ' ------------------------------------------------------------------ > select ImageGroup from master where rowid = freestring = freestring & " " & sob ( siGroup00 , "GET", "DATA") WITHQUERY (freestring ) ImageGroup = wqText(1) END withquery ' ------------------------------------------------------------------ > update master set ImageGroup = freestring = freestring & " " & sqlString(ImageGroup) >> , ImageGroupType = 'near-identical' >> where rowid = freestring = freestring & " " & PromotedRowid SilentSQL (freestring ) ' by ADDING this IMAGE to a group we might have promoted a single member to a GROUP IF ( ! sob ( sob ( siGroup00 , "SIBLING", 1) , "GET", "DATA" ) ) then > update master set ImageGroup = num2hex(phash) || num2hex(dHash) >> , ImageGroupType = "unknown" >> where rowid = freestring = freestring & " " & sob ( siGroup00 , "GET", "DATA") SilentSQL (freestring ) END if WITHQUERY ( "select * from temp.masterColumns ") > update >> master >> SET >> $col = ( select $col from Master where hash = freestring = freestring & " " & sqlString ( CurrentHash) >> ) >> WHERE >> hash = freestring = freestring & " " & sqlString ( PromotedHash) sql (replace ( freestring , "$col", sqlIdentifier ( wqtext(1) ) ) ) END WithQuery removeAllThumbNails ( ) GetCopies ( currentRowid ) END function sob ( -1 , "ON" , "CLICK" , "CBpopupAddToGroup") ' --------------------------------------------- FUNCTION cbImageGroupMOUSECLICKS ( sobid, clickId ) ' right mouse button clickid = 7 if ( clickid <> 7 ) then exit function if ( ! sob ( sobid , "GET" , "DATA" ) ) then exit function dim ridA = sob ( siGroup00, "get" , "data") dim txt = "" dim distance dim FileA , pHashA , dHashA , CanvasA dim FileB , pHashB , dHashB , CanvasB > select path ||name , pHash , dHash from master where rowid = freestring = freestring & " " & ridA WITHQUERY ( freestring ) FileA = wqtext(1) pHashA = wqInt(2) dHashA = wqInt(3) END withquery dim ridB = cint(sob ( sobid , "GET" , "DATA" )) > select path||name , pHash , dHash from master where rowid = freestring = freestring & " " & ridB WITHQUERY ( freestring ) FileB = wqtext(1) pHashB = wqInt(2) dHashB = wqInt(3) END withquery IF ( ! isFile ( FileB ) ) then txt = " File does not exist " & FileB sob ( TarInfo , "SET" , "TITLE", txt ) EXIT function END if txt = right("000000" & ridB,6) txt = txt & " pHD=" & right( "00" & popCount ( pHashA bxor pHashB ) , 2 ) txt = txt & " dHD=" & right( "00" & popCount ( dHashA bxor dHashB ) , 2 ) txt = txt & " " & right( "00000" & CANVAS ( "IMAGE.FILE", "GET" , "WIDTH" , FileB ) , 5 ) txt = txt & "/" & right( "00000" & CANVAS ( "IMAGE.FILE", "GET" , "height" , FileB ) , 5 ) txt = txt & " " & FileB sob ( TarInfo , "SET" , "TITLE", txt ) siTriggeringThumbNail = sobid sob(OnImagePopUpSimilar,"SET","POSITION", sob ( appWindow , "get" , "cursor.x" ), sob ( appWindow , "get" , "cursor.y" ) ) IF ( ( siGroup00 < siMatrix00) and ( sobid < siMatrix00 ) ) then sob(OnImagePopUpGroup,"set","show",1) ELSEIF ( ( siGroup00 < siMatrix00) ) then sob(OnImagePopUpSimilar,"set","show",1) ELSEIF ( ( siMatrix00 < siGroup00 ) and ( sobid < siGroup00 ) ) then sob(OnImagePopUpSimilar,"set","show",1) ELSE sob(OnImagePopUpGroup,"set","show",1) endif END function dim siImageGroup = sob ( ImageGroupWH, "ADD", "CONTAINER", "matrix.c" , 3) ' --------------------------- FUNCTION onFileDrop ( sobid ) IF ( GetDropCount > 1 ) then EXIT function END if dim rid = 0 > select rowid as selfA , * from MASTER where (path||name) like freestring = freestring & " " & sqlstring ( getDropData ( 1 ) ) WITHQUERY ( freestring ) rid = wqInt(1 ) END withquery IF ( rid == 0 ) then EXIT function END IF dim i , siTmp dim freeCell = -1 ' ------------------------------------------ ' find empty cell in matrix of POTENTIAL matches FOR i = 0 to maxThumbNailIndex siTmp = sob ( siMatrix00 , "SIBLING", i ) IF ( ! sob ( siTmp ,"GET","data" ) ) then freeCell = siTmp EXIT FOR END IF NEXT i sob ( freeCell ,"set" , "image" , getDropData ( 1 ) ) sob ( siTmp ,"SET","data" , rid ) CreateTargetReport ( siTmp ) END function dim siImageMatrix = sob ( ImageGroupWH, "ADD", "CONTAINER", "matrix.c" , 3) dim siMatrix00 = sob (siImageMatrix , "ADD","image.holder",0 ,XY,XY ) sob ( siMatrix00 , "ON" , "MOUSECLICKS", "cbImageGroupMOUSECLICKS") sob ( siMatrix00 , "ON" , "click" , "cbOnThumbNail" ) ' sob ( siMatrix00 , "ON" , "DROP.FILE", "onFileDropXX") sob ( siMatrix00 ,"SET","data" , 0 ) FOR i = 1 to maxThumbNailIndex tmpSob = sob (siImageMatrix , "ADD","image.holder",0 ,XY,XY ) sob ( tmpSob , "ON" , "click" , "cbOnThumbNail" ) sob ( tmpSob , "ON" , "MOUSECLICKS", "cbImageGroupMOUSECLICKS") ' sob ( tmpSob , "ON" , "DROP.FILE", "onFileDrop") sob ( tmpSob ,"SET","data" , 0 ) NEXT i ' --------------------------- sob ( siImageGroup , "ON" , "DROP.FILE", "onFileDrop") dim siGroup00 = sob (siImageGroup , "ADD","image.holder",0 ,XY,XY ) sob ( -1 , "ON" , "MOUSECLICKS", "cbImageGroupMOUSECLICKS") sob ( -1 , "ON" , "click" , "cbOnThumbNail" ) FOR i = 1 to maxThumbNailIndex tmpSob = sob (siImageGroup , "ADD","image.holder",0 ,XY,XY ) sob ( tmpSob , "ON" , "click" , "cbOnThumbNail" ) sob ( tmpSob , "ON" , "MOUSECLICKS", "cbImageGroupMOUSECLICKS") NEXT i ' --------------------------- FUNCTION ResetImageGroups () drop table if exists temp.matches cancelText( "Create temporary table for image comparisons" , 1 ) > create temp table matches as >> select rowid as selfA >> , path as PathA , name as NameA >> , size >> , "" as PathB , "" as NameB , 0 as selfB >> , pHash as xHash , 99 as xHamming >> , 99 as pHamming , 99 as dHamming >> , pHash as pHashA , dHash as dHashA >> , 0 as pHashB , 0 as dHashB >> , ImageNearness , 0 as Nbr >> , (num2hex(phash) || num2hex(dHash)) as ImageGroup >> , 'unknown' as ImageGroupType >> from >> master >> where >> IgnoreFile = 0 >> and ext in freestring = freestring & " " & ImageFileExtensions sql(freestring) ' ------------------------------------------------------------------ ' now update the MATCHES table for each ROWID ' BUT note that we exclude from the results it's selfA ' SO ImageNearness talks about its neighbours excluding itself! IMPORTANT cancelText( "Run images through BK-Tree:about 1 second per 1000 images" , 1 ) ' this cost virtual nothing < 1 second drop table if exists temp.xHashTbl MakeBkTree ( ) > update matches set ImageNearness = >> eval (' select substr ( "0000" || count(*) , -4 ) , substr ( "0000" || min (distance), -4 ) , link from xHashTbl ( ' || xHash || ', freestring = freestring & " " & maxHD >> ) where not link = ' || selfa || ' >> order by distance asc limit 1' ) sql(freestring) > update >> matches >> set >> SelfB = cast(substr ( ImageNearness, -1*(length(ImageNearness) - 10 ) )as int ) >> , xHamming = cast(substr ( ImageNearness,6,4) as int ) >> , nbr = cast(substr ( ImageNearness,1,4) as int ) >> WHERE >> not ImageNearness is null sql(freestring) > update >> matches >> set >> pHashB = frmTbl.pHash >> , dHashB = frmTbl.dHash >> FROM >> ( select rowid, * from master where IgnoreFile = 0 ) as frmTbl >> WHERE >> matches.selfB = frmTbl.rowid sql(freestring) > update >> matches >> set >> pHamming = popcount ( XOR ( pHashA , pHashB) ) >> , dHamming = popcount ( XOR ( dHashA , dHashB) ) >> WHERE >> selfb > 0 sql(freestring) ' ------------------------------------------------------------------ ' has NO neighbours within a pHash distance of maxHD > update >> matches >> set >> ImageGroupType = 'unique' >> WHERE >> selfB = 0 sql(freestring) ' ------------------------------------------------------------------ ' has neighbours with a pHash distance of 0 , so are identical/duplicates ' but be careful: we might have ' root image + a duplicate + near-identicals ' or ' root image + a duplicate + 0 or more duplicates + 0 or more near-identicals ' SO only call it a duplicate if pHash distance = 0 AND NBR = 1 > update >> matches >> set >> ImageGroupType = 'duplicatE' >> WHERE >> selfB > 0 and xHamming = 0 and dHamming = 0 and nbr = 1 sql(freestring) ' ------------------------------------------------------------------ > update >> matches >> SET >> Imagegroup = frmTbl.Imagegroup >> , ImageGroupType = 'near-identicaL' >> FROM >> ( select pHashA , ImageGroup from matches where selfB > 0 and xHamming = 0 and dHamming between 1 and 17 and nbr = 1 ) as frmTbl >> WHERE >> matches.pHashA = frmTbl.pHashA >> and matches.selfB > 0 and matches.xHamming = 0 and matches.dHamming between 1 and 17 and matches.nbr = 1 silentSQL ( freestring) ' ------------------------------------------------------------------ ' even if the pHash is low, if the dHamming is > 18 then it is highly likely to be DIFFERENT ' note: only doing this if NBR = 1 > update >> matches >> set >> ImageGroupType = 'different' >> WHERE >> selfB > 0 and dHamming > 18 and nbr = 1 sql(freestring) ' --------------------------------------------------------------------------------------------- ' handle the NEAR-IDENTICALs , first do the easy case NBR = 1 cancelText( "Simple near-identical" , 1 ) > update >> matches >> SET >> Imagegroup = frmtbl.ImageGroup >> , ImageGroupType = 'near-Identical' >> FROM >> ( select selfA , selfB , ImageGroup from matches where NBR = 1 AND ImageGroupType ='unknown' ) as frmTbl >> WHERE >> matches.selfA in ( frmTbl.selfA , frmTbl.selfB ) >> AND matches. ImageGroupType ='unknown' sql ( freestring ) ' --------------------------------------------------------------------------------------------- ' handle the NEAR-IDENTICALs , do the COMPLEX case NBR > 2 cancelText( "Complex near-identical" , 1 ) dim selfA , pHashA , dHashA , ImgGroup , selfB , pHashB , dHashB dim pHamming , dHamming SilentSQL ("drop table if exists temp.singleImageGroup ") SilentSQL (" create temp table singleImageGroup ( selfB, pHashB, pHamming, dHashB, dHashA , dHamming ) ") > select selfA, pHashA , dHashA , Imagegroup from matches where ImageGroupType ='unknown' WITHQUERY ( freestring ) selfA = wqInt (1) pHashA = wqInt (2) dhashA = wqInt (3) ImgGroup = wqtext(4) ' get neighbours of target based on pHash SilentSQL (" delete from singleImageGroup ") > insert into singleImageGroup >> select link , metric , distance,0,0,0 from xHashTbl ( freestring = freestring & " " & pHashA >> , freestring = freestring & " " & 12 ' pHD >> ) WHERE not link = freestring = freestring & " " & selfa sql(freestring) > update singleImageGroup set >> dHashB = ( select dHash from master where rowid = singleImageGroup.selfB) >> , dHashA = freestring = freestring & " " & dHashA sql(freestring ) SilentSQL (" update singleImageGroup set dHamming = popcount ( xor ( dHashA , dHashb ) )") SilentSQL (" delete from singleImageGroup where dHamming > 17 ") > select selfb from singleImageGroup limit 1 WITHQUERY ( freestring) > update matches set ImageGroup = freestring = freestring & " " & sqlString(ImgGroup ) >> , ImageGroupType = 'near-iDentical' >> WHERE >> selfA in ( freestring = freestring & " " & selfA >> , freestring = freestring & " " & wqInt(1) >> ) >> AND ImageGroupType ='unknown' sql(freestring) END withquery END withquery ' --------------------------------------------------------------------------------------------- ' anything still not handled is because it has a neighbour with acceptable pHash ' BUT its dHamming distance is too big > update matches >> set ImageGroupType = 'diFferent' >> where ImageGroupType ='unknown' SilentSQL( freestring) ' --------------------------------------------------------------------------------------------- > UPDATE >> master >> SET >> ImageNearness = frmTbl.ImageNearness >> , ImageGroup = frmTbl.ImageGroup >> , ImageGroupType = frmTbl.ImageGroupType >> FROM >> ( select * from matches ) as frmTbl >> WHERE >> master.rowid = frmTbl.selfa sql ( freestring ) END function ' ----------------------------------------------------- ' populate: upper right ' ----------------------------------------------------- ' FILE EXTENSIONS sob ("RGB","DEFAULT") FUNCTION extensionCheck ( haveExt ) extensionCheck = 0 dim i IF ( extArray(0) == "*" ) then extensionCheck = 1 EXIT function END if FOR i = 0 to uBound ( extArray ) IF ( haveExt == extArray(i) ) then END IF NEXT i END function FUNCTION onExtensionSelectEdited ( sobid, txt ) dim extList = txt extList = toLower ( extList ) extList = replace ( extList, " " , "" ) set extArray = split ( extList , ",") END FUNCTION FUNCTION onExtensionSelect ( sobid, rowid ) dim extList = sob(sobid,"GET","ROW" , rowid) extList = toLower ( extList ) extList = replace ( extList, " " , "" ) set extArray = split ( extList , ",") END FUNCTION dim extArray = split ("*", ",") ' ----------------------------------------------------------------------- sob ("RGB","DEFAULT") ' ----------------------------------------------------------------------- FUNCTION populateCombo ( sobId , typeTag ) sob( soBid, "empty") IF ( isDBopen ) then > select ttname from tagTable where ttGroup like $tag order by ttname asc freestring = replace ( freestring , "$tag" , sqlstring ( typeTag) ) WITHQUERY (freestring) sob ( sobid, "add", "row" , wqText(1)) END withquery sob( sobid , "SET", "SELECTION" , "ROW", 1 ) END IF freestring = "" END FUNCTION FUNCTION populateActionCombo (sobId) ' these are the actions that can be applied to ' to the database sob( sobid , "add" , "row" , NoTag ) sob( sobid , "add" , "row" , "add" ) sob( sobid , "add" , "row" , "replace" ) sob( sobid , "add" , "row" , "remove" ) sob( sobid , "add" , "row" , "remove ALL" ) sob( sobid , "SET", "SELECTION" , "ROW", 1 ) END FUNCTION ' ----------------------------------------------------- ' populate: lower middle ' ----------------------------------------------------- dim TagInfoCol = sob ( lowMidArea, "ADD" ,"CONTAINER", "COLUMN.W" ) dim TagInfo = sob(TagInfoCol ,"add","Edit.rows" , " ", 2) font("RESET") font("SET" ,"FaceName" , "Courier New" ) dim myFont = Font("CREATE") sob ( TagInfo , "SET", "FONT" , myFont ) ' ----------------------------------------------------- ' populate: lower right ' ----------------------------------------------------- ' ----------------------------------------------------- ' File Text ' ----------------------------------------------------- FUNCTION makeExternalFileList ( UpOrDown ) ' lazy way to get PATH ' because we give it a FULL path/file name then dbFileList ' returns only 1 entry dbFileList ( "temp" , "externalFolder", currentFile) ' now use the PATH to get a full list of the FOLDER & sub folders select path from temp.externalFolder dbFileList ( "temp" , "externalFolder", qrSingleValue) > select rowid from externalFolder where path||name = freestring = freestring & sqlString ( currentFile ) sql ( freestring ) dim rowid = qrSingleValue if ( UpOrDown ) then > select (path || name ) from externalFolder where type like 'file' and rowid > freestring = freestring & " " & rowid >> order by rowid asc limit 1 sql ( freestring ) if ( istype ( qrSinglevalue) == "EMPTY" ) then > select (path || name ) from externalFolder where type like 'file' >> order by rowid asc limit 1 sql ( freestring ) END IF makeExternalFileList = qrSinglevalue ELSE > select (path || name ) from externalFolder where type like 'file' and rowid < freestring = freestring & " " & rowid >> order by rowid desc limit 1 sql ( freestring ) if ( istype ( qrSinglevalue) == "EMPTY" ) then > select (path || name ) from externalFolder where type like 'file' >> order by rowid desc limit 1 sql ( freestring ) END IF makeExternalFileList = qrSinglevalue END IF END Function FUNCTION cbOnPressPlus ( sobId ) IF ( currentFile = "" ) then EXIT function END if dim filename = sob ( FileListSob , "get", "title") if ( filename == "" ) then exit function END IF IF ( left ( filename , 2 ) == "Ex" ) then ' the currentFile is not in the DB ' so assume is in an external cbSingleFile ( makeExternalFileList ( 1 ) ) exit function END IF > select rowid from master where (path || name ) like freestring = freestring & " " & sqlstring ( currentFile) sql ( freestring ) dim rowid = qrSingleValue > select (path || name ) from master where type like 'file' and rowid > freestring = freestring & " " & rowid >> order by rowid asc limit 1 sql ( freestring ) cbSingleFile ( qrSingleValue) END Function FUNCTION cbOnPressReload ( sobid ) IF ( currentFile = "" ) then EXIT function END if cbSingleFile ( currentFile) END Function FUNCTION cbOnPressMinus ( sobId ) IF ( currentFile = "" ) then EXIT function END if dim filename = sob ( FileListSob , "get", "title") if ( filename == "" ) then exit function END IF currentFile = left ( filename, -10 ) IF ( left ( filename , 2 ) == "Ex" ) then ' the currentFile is not in the DB ' so assume is in an external cbSingleFile ( makeExternalFileList ( 0 ) ) exit function END IF > select rowid from master where (path || name ) like freestring = freestring & " " & sqlstring ( currentFile) sql ( freestring ) IF ( istype ( qrSinglevalue) == "EMPTY" ) then > select rowid from master where hash like freestring = freestring & " " & sqlstring ( currentHash) sql ( freestring ) END IF IF ( istype ( qrSinglevalue) == "EMPTY" ) then EXIT function END if dim rowid = qrSingleValue ' > select (path || name ) from master where type like 'file' and rowid < ## order by rowid desc limit 1 > select (path || name ) from master where type like 'file' and rowid < freestring = freestring & " " & rowid >> order by rowid desc limit 1 sql ( freestring ) cbSingleFile ( qrSingleValue) END Function ' ----------------------------------------------------- FUNCTION CreateRectInfoFrom ( ColName , lTagDelim ) dim loopflag, Rect, name , hit dim rLeft , rTop , rRight , rBottom IF ( ! isDBcolumn ( "master", ColName) ) then ' sob (TagRect,"add", "row" ,"fail 1 " & " " & colName & EOL ) EXIT function END if ' ------------------------------------------------------ ' get the Faces=rect64(.... text > select freestring = freestring & " " & sqlIdentifier ( ColName ) >> from master where hash = freestring = freestring & " " & sqlString ( CurrentHash ) >> limit 1 sql = freestring dim LookIn = qrSingleValue IF ( LookIn = "" ) then ' sob (TagRect,"add", "row" ,"DB failure: " & freestring & EOL ) EXIT function END if ' ------------------------------------------------------ ' get the Faces=rect64(.... text ' report ( "look in " & LookIn ) dim pos , TagGroup = "" LOOPFLAG = inStr ( Lookin , "rect64(" , 0) DO while loopFlag LOOPFLAG = inStr ( Lookin , "rect64(" , 0) Lookin = left ( lookin , -1* ( loopflag + length("rect64(") - 1 ) ) Rect = left ( Lookin , inStr ( Lookin , ")" , 0) - 1 ) LookIn = left ( LookIn , -1 * (Length(Rect)+2) ) LOOPFLAG = inStr ( Lookin , ";rect" , 0) IF ( loopFlag ) then Name = left ( Lookin , loopFlag ) ELSE name = right( Lookin , -1) END IF ' Convert a RECT string into Pixel locations of the Left,Top and Right,Bottom coordinates rBottom = CNumeric ( "0x0" & right ( Rect , 4 ) ) rRight = CNumeric ( "0x0" & left ( right( Rect , 8 ) , 4 ) ) rTop = CNumeric ( "0x0" & left ( right( Rect , 12 ) , 4 ) ) rLeft = CNumeric ( "0x0" & right ( Rect , -12 ) ) rRight = ( 100 * rRight ) / 0x010000 rBottom = ( 100 * rBottom ) / 0x010000 rLeft = ( 100 * rLeft ) / 0x010000 rTop = ( 100 * rTop ) / 0x010000 DO while ( right(name,1) == ";") name = right(name,-1) LOOP DO while ( left(name,1) == ";") name = left(name,-1) LOOP name = trim ( name ) TagGroup = "" IF ( lTagDelim == "" ) then TagGroup ="Who" ELSE ' report ( "zz " & length(lTagDelim) & " " & name ) IF ( pos := instr ( name , lTagDelim ) ) then ' expect a leading delim ' report ( "BB " & length(lTagDelim) & " " & name ) pos := instr ( name , lTagDelim ) ' report ( "CC " & pos & " " & name ) TagGroup = left ( name , pos - 1 ) name = left ( name , -1 * (pos + length(lTagDelim ) - 1) ) ' report ( "DD " & name & " " & length(name)& " " & length(TagGroup) ) END if END if > insert into tblRectList values ( freestring = freestring & sqlString ( ColName ) & " , " ' rlSrc freestring = freestring & sqlString ( TagGroup) & " , " ' rlTagGroup freestring = freestring & sqlString ( name ) & " , " ' rlTagName freestring = freestring & sqlString ( rect ) & " , " ' rlRect freestring = freestring & rLeft & " , " ' rlLeft freestring = freestring & rTop & " , " ' rlTop freestring = freestring & rRight & " , " ' rlRight freestring = freestring & rBottom & " , " ' rlBottom freestring = freestring & 0 & " , " ' rlHit freestring = freestring & 0 & " ) " ' rlId sql ( freestring ) LOOP END function FUNCTION cbImageHitStatus ( PerCentX , PerCentY ) ' does this X,Y (as percentages) lie inside a REGION ' we want to set the HIT status per REGION to 1 , else 0 ' default HIT status to 0 = X,Y not in a REGION update temp.tblRectList set rlHit = 0 ' now find the HIT(s) > update temp.tblRectList set rlHit = 1 where >> not rlLeft > freestring = freestring & PerCentx >> and not rlRight < freestring = freestring & PerCentx >> and not rlTop > freestring = freestring & PerCentY >> and not rlBottom < freestring = freestring & PerCentY sql (freestring ) ' if we have a HIT on a region, and the region is in RectMeta AND RectPicasa then ' reset the RectPicas to no hit. So RectMeta has priority > update temp.tblRectList set rlHit = 0 where rlSrc like 'RectPicasa' and rlRect In >> ( select rlRect from temp.tblRectList where not rlHit = 0 and rlSrc like 'RectMeta' ) sql ( freestring ) END FUNCTION FUNCTION cbSelectOneRect ( sobid , rowid ) ' input parameter rowid tells us which row of the COMBO has been selected ' can NOT use the row's text because it might not be unique ' The COMBO WAS populated in sequence ! IF ( ! imageLoadedFlag ) then EXIT function END IF sob ( TagCommentEdit , "empty") ' we are assuming that there is atleast 1 HIT ' and that this Function would not otherwise have been called dim HitTagGroup, HitTagName , HitRect , HitComment > select rlTagGroup , rlTagName , rlRect from tblRectList where not rlHit = 0 order by rowid asc limit 1 offset freestring = freestring & " " & (rowid-1) WITHQUERY ( freestring) ' careful, use like and not = because we have not strictly ensured the Tag Group case ! HitTagGroup = wqText(1) HitTagName = wqText(2) HitRect = wqText(3) END WITHQuery > select ttComment from tagTable where ttGroup like freestring = freestring & " " & sqlString ( HitTagGroup & TagGroupId ) >> and ttName like freestring = freestring & " " & sqlString ( HitTagName ) sql ( freestring ) sob (TagCommentEdit,"add", "row" , qrSingleValue & EOL ) sob (TagCommentEdit,"set", "row" , "top" , 0 ) sob ( lblRegionUpdateComment , "SET" , "TITLE" , "" ) sob ( lblRegionUpdateComment , "SET", "RGB", defBoxTxtCol ) ' ------------------------------------------------------------------------------ ' show all Rects in their default RGB fncShowRegions () ' ------------------------------------------------------------------------------ ' now set the HOT Rectangle to have the appropriate RGB dim CanvasHandle = SOB( sobImageHolder , "GET" , "CANVAS" ) canvas( CanvasHandle , "SET" , "pen", "rgb", RegionRectRGBa) canvas( CanvasHandle , "SET" , "pen", "width", 16) dim imW = canvas ( CanvasHandle , "get" , "width" ) dim imH = canvas ( CanvasHandle , "get" , "height" ) displayRect ( CAnvasHandle , HitRect , imw , imh ) END function FUNCTION fnOfferRectChoice () ' we are assuming that this function was called with atleast one HIT RECT '-------------------- IF ( HaveRubberBandRect ) then EXIT function END if '-------------------- ' for now set all REGION labels to green SetLblRegionRGB ( rgb(0,255,0) ) '-------------------- ' add each HIT RECT to the COMBO dim hitCount = 0 WITHQUERY ( "select rlTagName from tblRectList where not rlHit = 0 order by rowid asc ") sob ( TagRectCombo , "add" , "row" , wqText(1) ) hitCount = hitCount + 1 END withQuery '-------------------- IF ( hitCount == 0 ) then ELSEIF ( hitCount == 1 ) then sob ( lblRegionSelection ,"set", "title" , "Comment associated with a region" ) sob ( -1 , "SET" , "RGB" , 0x0ff00 ) ELSE sob ( lblRegionSelection ,"set", "title" , "Multiple Regions (select one)" ) sob ( -1 , "SET" , "RGB" , 0x0ff ) END if ' select, and trigger the FIRST hit item sob( TagRectCombo , "SET", "SELECTION" , "ROW" , 1 ) sob( TagRectCombo , "TRIGGER" ) cbSelectOneRect ( TagRectCombo , 1 ) END function FUNCTION displayRect ( CanvasHandle , Rect , imw , imh ) dim RectY , RectX , RectYY , RectXX RectYY = CNumeric ( "0x0" & right ( Rect , 4 ) ) RectXX = CNumeric ( "0x0" & left ( right( Rect , 8 ) , 4 ) ) RectY = CNumeric ( "0x0" & left ( right( Rect , 12 ) , 4 ) ) RectX = CNumeric ( "0x0" & right ( Rect , -12 ) ) RectX = cint ( ( RectX * imW ) / 0x010000 ) RectY = cint ( ( RectY * imH ) / 0x010000 ) RectXX = cint ( ( RectXX * imW ) / 0x010000 - RectX) RectYY = cint ( ( RectYY * imH ) / 0x010000 - RectY) canvas ( CanvasHandle ,"RECTANGLE" , RectX , RectY , RectXX, RectYY) END FUNCTION FUNCTION fncShowRegions () IF ( ! imageLoadedFlag ) then EXIT function END IF '-------------------- ' erase any RECT on the image (including any rubberband) dim CanvasHandle = SOB( sobImageHolder , "GET" , "CANVAS" ) IF ( CanvasHandle ) then canvas ( CanvasHandle , "FLOOD" , TransparentRGB ) END if '-------------------- ' obey user selection menu option IF ( ! RegionRectShow ) then EXIT function END IF '-------------------- dim Rect dim imW = canvas ( CanvasHandle , "get" , "width" ) dim imH = canvas ( CanvasHandle , "get" , "height" ) canvas ( CanvasHandle , "SET", "BRUSH", "GLASS") canvas ( CanvasHandle , "SET", "PEN", "width" , RegionRectWeight) canvas ( CanvasHandle , "SET", "pen", "style", 0) ' solid line ' --------------------------------------------------------- ' draw RECTs which have a TAG i.e. not the default tag canvas ( CanvasHandle ,"SET", "PEN", "RGB" , RegionRectRGBn) WITHQUERY ( "select rlRect from tblRectList where not rlTagName like '%fffffffffffffff%' " ) Rect = wqText(1) displayRect ( CaNvasHandle , Rect , imW , imH ) END WithQuery ' --------------------------------------------------------- ' draw RECTs which have no TAG i.e. the default tag ' except the current selected Rect canvas ( CanvasHandle ,"SET", "PEN", "RGB" , RegionRectRGBu ) WITHQUERY ( "select rlRect from tblRectList where rlTagName like '%fffffffffffffff%' " ) Rect = wqText(1) > select count (*) from tblRectList where not rlTagName like '%fffffffffffffff%' and rlRect = freestring = freestring & " " & sqlString ( Rect ) sql(freestring ) IF ( qrSingleValue == 0 ) then displayRect ( CanVasHandle , Rect, imW , imH ) END if END WithQuery END Function FUNCTION cbImageRubberBand ( sobid , xTL , yTL , xBR , yBR ) ' WARNING ' xTL , yTL , xBR , yBR are screen co-ordinates ' and NOT CANVAS co-ordinates ' report ( xTL & " " & yTL & " " & xBR & " " & yBR ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function< END if IF ( ! imageLoadedFlag ) then EXIT function END IF ' remove any previous Rubberband Rect ' and show RECTs in their default RGB fncShowRegions () ' ---------------------------------------------------------------------- ' display the new RubberBand Rect dim CanvasHandle = SOB( sobImageHolder , "GET" , "CANVAS" ) dim Rect dim RectY , RectX , RectYY , RectXX dim sobW = sob ( sobImageHolder , "image" , "width" ) dim sobH = sob ( sobImageHolder , "image" , "height" ) IF ( xBR > sobW ) then xBR = sobW END if IF ( yBr > sobH ) then yBr = sobH END if dim imW = canvas ( CanvasHandle , "get" , "width" ) dim imH = canvas ( CanvasHandle , "get" , "height" ) RectX = cint ( ( xTL * imW) / sobW ) RectY = cint ( ( yTL * imH) / sobH ) RectXX = cint ( ( xBR * imW) / sobW ) - RectX RectYY = cint ( ( yBR * imH) / sobH ) - RectY leftRB = RectX topRB = RectY rightRB = RectXX bottomRB = RectYY sob ( RubberBandEdit , "add" , "row" , leftRB & " " & topRB & " " & rightRB & " " & bottomRB & EOL ) canvas ( CanvasHandle ,"SET", "BRUSH", "GLASS") canvas ( CanvasHandle ,"SET", "PEN", "width" , RegionRectWeight) canvas ( CanvasHandle ,"SET", "PEN", "RGB" , 0x0ff) canvas ( CanvasHandle ,"RECTANGLE" , RectX , RectY , RectXX, RectYY) ' ---------------------------------------------------------------------- ' need the Tag Comment Overlay page to be visible to allow us ' to assign a tag group / tag name to the rubberband RECT sob ( TagCommentOvl , "set" , "show" , 1) HaveRubberBandRect = 1 sob (TagRectCombo , "EMPTY") sob (TagCommentEdit,"empty") SetLblRegionRGB ( rgb(0,255,0) ) END FUNCTION FUNCTION CreateRectInfo () ' empty the table that will hold all of the rect64 information delete from temp.tblRectList if ( haveRectMeta ) then CreateRectInfoFrom ( "RectMeta" , tagDelim ) if ( haveRectPicasa ) then CreateRectInfoFrom ( "RectPicasa" , "" ) END function FUNCTION cbImagePress ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if IF ( ! imageLoadedFlag ) then EXIT function END IF HaveRubberBandRect = 0 fncShowRegions () dim imW = sob ( sobid , "image" , "width" ) dim imH = sob ( sobid , "image" , "height" ) dim posX = sob ( sobid , "get" , "cursor.x" ) dim posY = sob ( sobid , "get" , "cursor.y" ) dim PerCentX = ( posX * 100) / imW dim PerCentY = ( posY * 100) / imH ' only need PerCentX and PerCentY to go on sob (TagCommentEdit,"empty") ' sob ( TagCommentOvl , "set" , "show" , 1) cbImageHitStatus ( PerCentX , PerCentY ) sob ( TagRectCombo , "EMPTY") SELECT count (*) from tblRectList where not rlHit = 0 IF ( qrSingleValue ) then sob ( TagCommentOvl , "set" , "show" , 1) fnOfferRectChoice () ELSE SetLblRegionRGB ( defBtnCol ) END IF END FUNCTION dim ImagePanelSob = sob ( lowRightArea ,"ADD" ,"CONTAINER" , "column.w") dim ImagePanelRow = sob ( ImagePanelSob,"ADD" ,"CONTAINER" , "row") dim inheritSob = sob ( ImagePanelRow , "ADD" , "Button" , "push" , "drop to inherit") FUNCTION onInheritDrop ( sobid ) dim fileCount IF ( sob(sobid,"GET" , "TITLE" ) == NoTag ) then EXIT function END IF IF ( ! isDBopen ) then sob( PathFile , "ADD" , "ROW" , "No Database Selected." & chr(0x0a) ) EXIT function END IF dim actionType , TagType , TagTxt CreateDoppedFileList () dim i , hasHash , inDir , hasExt SELECT count (*) from temp.bulk where type like 'file' fileCount = qrSingleValue cancelText( "Tagging in progress" , 1 ) cancelText( "This can take a short while" , 3 ) cancelText( "Files to process: " & FileCount , 2 ) cancelShow(1) WITHQUERY ( "select * from temp.masterColumns ") > update >> master >> SET >> $col = frmTbl.$col >> FROM >> ( select $col from temp.masterFIle ) as frmTbl >> WHERE >> master.hash in ( select hash from temp.bulk) freestring = replace ( freestring , "$col", sqlIdentifier ( wqtext(1) ) ) sql (freestring ) END WithQuery cancelShow(0) toggleWarnColour ( sobid ) END function sob ( -1 , "ON" , "DROP.FILE", "onInheritDrop") ' ------------------------------------ FUNCTION CBdropFileMenuReload ( sobid ) IF ( ! imageLoadedFlag ) then EXIT function END if dim i cbSingleFile ( "" ) noErrorReport ("") IF ( GetDropCount > 1 ) then ErrorReport("Only one drop file allowed" ) EXIT function END if dim pathName = getDropData ( 1 ) ' extract the filename.ext dim fileName = left ( pathName , -1 * (InStrRev ( pathName , "\\" ) ) ) ' remove leading E IF ( left( filename,1) <> "E" ) then ErrorReport ("Dropped file not previously exported" ) EXIT function END if filename = left( filename,-1) ' extract the rowid dim rid = left ( filename , InStr ( filename , "_" ) -1 ) filename = left ( filename , -1 * ( InStr ( filename , "_" ) )) >select name from master where rowid = freestring = freestring & " " & rid sql(freestring ) IF ( istype ( qrsingleValue) == "String" ) then IF ( qrsingleValue == filename ) then >select path from master where rowid = freestring = freestring & " " & rid sql(freestring ) ShellExecute ( qrSingleValue) >select path||name from master where rowid = freestring = freestring & " " & rid sql(freestring ) cbSingleFile ( qrSingleValue) EXIT function END IF END IF ErrorReport ( "Dropped file not in database" ) END FUNCTION dim PlusMinusRow = sob ( ImagePanelRow , "ADD", "CONTAINER" , "ROW" ) sob ( PlusMinusRow , "ADD", "SPACE" , 10,0 ) sob ( -1 , "SET" ,"RGB" , -1 ) sob ( PlusMinusRow, "ADD", "BUTTON" , "PUSH" , "<--" ) sob( -1 , "ON", "PRESS" , "cbOnPressMinus" ) sob ( PlusMinusRow, "ADD", "BUTTON" , "PUSH" , "reload" ) sob ( -1 , "ON", "PRESS" , "cbOnPressReload" ) sob ( -1 , "SET" , "Stretch" , 1 ) sob ( -1 , "ON" , "drop.file" , "CBdropFileMenuReload") sob ( PlusMinusRow, "ADD", "BUTTON" , "PUSH" , "-->" ) sob ( -1 , "ON", "PRESS" , "cbOnPressPlus" ) sob ( PlusMinusRow , "ADD", "SPACE" , 10,0 ) sob ( -1 , "SET" ,"RGB" , -1 ) ' ------------------------------------ FUNCTION CBpopupCopy ( sobid ) SELECT rlTagGroup from tblRectList where not rlHit = 0 copyTagGroup = qrSingleValue SELECT rlTagName from tblRectList where not rlHit = 0 copyTagName = qrSingleValue END Function FUNCTION CBpopupPaste ( sobid ) ' update the current REGION with the tag group/tag name ' that we COPIED from an earlier REGION dim oldTagGroup , oldTagName , thisRect > SELECT rlTagGroup , rlTagName , rlRect from tblRectList where not rlHit = 0 WITHQUERY ( freestring ) oldTagGroup = wqText(1) oldTagName = wqText(2) thisRect = wqText(3) END WITHQUERY dim oldEntry = "rect64(" & thisRect & ")," & oldTagGroup & TagDelim & oldTagName & ";" dim newEntry = "rect64(" & thisRect & ")," & copyTagGroup & TagDelim & copyTagName & ";" IF ( haveRectMeta ) then > update Master set RectMeta = replace(RectMeta, freestring = freestring & " " & sqlString ( ":" & oldEntry) >> , freestring = freestring & " " & sqlString ( newEntry ) >> ) where hash like freestring = freestring & " " & sqlString ( currentHash ) sql ( freestring ) > update Master set RectMeta = replace(RectMeta, freestring = freestring & " " & sqlString ( oldEntry) >> , freestring = freestring & " " & sqlString ( newEntry ) >> ) where hash like freestring = freestring & " " & sqlString ( currentHash ) sql ( freestring ) oldEntry = "rect64(" & thisRect & ")," & oldTagName & ";" newEntry = "rect64(" & thisRect & ")," & copyTagName & ";" END if IF ( haveRectPicasa ) then > update Master set RectPicasa = replace(RectPicasa , freestring = freestring & " " & sqlString ( ":" & oldEntry) >> , freestring = freestring & " " & sqlString ( newEntry ) >> ) where hash like freestring = freestring & " " & sqlString ( currentHash ) sql ( freestring ) > update Master set RectPicasa = replace(RectPicasa , freestring = freestring & " " & sqlString ( oldEntry) >> , freestring = freestring & " " & sqlString ( newEntry ) >> ) where hash like freestring = freestring & " " & sqlString ( currentHash ) sql ( freestring ) END if END Function ' -------------------- 'dim CopyPastePopUp = sob ( ImageWindowSOB, "add", "Menu", "popup") dim CopyPopUpid = sob (CopyPastePopUp , "add", "menu","Vertical", "Copy Tag") sob ( -1 , "ON" , "CLICK" , "CBpopupCopy") dim PastePopUpid = sob (CopyPastePopUp , "add", "menu","Vertical", "Paste Tag") sob ( -1 , "ON" , "CLICK" , "CBpopupPaste") dim DeletePopUpid = sob (CopyPastePopUp , "add", "menu","Vertical", "Delete Region") sob ( -1 , "ON" , "CLICK" , "cbDeleteRegion") ' -------------------- ' --------------------------------------------------------------------------------------------------------- ' Image Grouping ' --------------------------------------------------------------------------------------------------------- FUNCTION CreateSimilarImagesTable ( rid ) ' rid = the ROWID into Master of the reference Image ' ------------------------------------------ ' we need the HASH value for this image dim referenceHash > select pHash from Master where rowid = freestring = freestring & " " & rid WITHQUERY (freestring ) referencehash = wqText(1) END WITHQUERY ' ------------------------------------------ SilentSQL ("drop table if exists temp.links") ' ------------------------------------------ ' create a table of LINKS to the IMAGES that are to be considered ' as being identical/near-identical to the referenceHash ' note: this uses the BK-TREE implementation > create temp table links as >> select link from xHashTbl ( freestring = freestring & " " & referenceHash >> , freestring = freestring & " " & maxHD >> ) where distance > -1 SilentSQL (freestring) ' ----------------------------------------- SilentSQL ("drop table if exists temp.MatchesSimilarTbl") > create temp table MatchesSimilarTbl as >> select rowid as selfA , * from Master >> where >> rowid in (select link from temp.links) >> and IgnoreFile = 0 >> and not ImageGroupType like 'different' SilentSQL (freestring) ' ----------------------------------------- ' do not need links table any more SilentSQL ("drop table if exists temp.links") ' ------------------------------------------ END FUNCTION FUNCTION CreateImageGroupTable ( rid ) ' rid = the ROWID into Master of the reference Image ' ------------------------------------------ ' we need the ImageGroupTbl for this image dim referenceImageGroup > select ImageGroup from Master where rowid = freestring = freestring & " " & rid WITHQUERY (freestring ) referenceImageGroup = wqText(1) END WITHQUERY ' ------------------------------------------ SilentSQL ("drop table if exists temp.ImageGroupTbl") ' ------------------------------------------ ' create a table of the IMAGES that have the same ImageGroup > create temp table ImageGroupTbl as >> select rowid as selfA , * from master >> where >> IgnoreFile = 0 >> and ImageGroup = freestring = freestring & " " & sqlString ( referenceImageGroup ) SilentSQL (freestring) END FUNCTION FUNCTION removeImageXfromGroupZ ( xSelf, zSelf ) CreateImageGroupTable ( zSelf ) ' ------------------------------------------ ' is there a group to be SPLIT a group dim cnt > select count(distinct ( phash || dhash ) ) from ImageGroupTbl WITHQUERY ( freestring ) cnt = wqInt(1) END withquery IF ( cnt < 2 ) then ' no group EXIT function END if ' ------------------------------------------ ' are we removing the owning IMAGE ' the owning image has the pHash / dHash values that ' defined the Imagegroup value dim xImageGroup , zImageGroup > select imageGroup from ImageGroupTbl where selfA = freestring = freestring & " " & zSelf WITHQUERY ( freestring ) zImageGroup = wqText(1) END withquery > select num2hex(phash) || num2hex(dHash) from ImageGroupTbl where selfA = freestring = freestring & " " & xSelf WITHQUERY ( freestring ) xImageGroup = wqText(1) END withquery IF ( zImageGroup == xImageGroup ) then ' is the owning Image ' so must change the ImageGroup to another value > select num2hex(phash) || num2hex(dHash) from ImageGroupTbl >> where not (num2hex(phash) || num2hex(dHash)) like freestring = freestring & " " & sqlString( zImageGroup ) >> limit 1 WITHQUERY ( freestring ) zImageGroup = wqText(1) END withquery > update ImageGroupTbl set ImageGroup = freestring = freestring & " " & sqlString( zImageGroup ) silentSQl(freestring) > select count(* ) from ImageGroupTbl WITHQUERY ( freestring ) cnt = wqInt(1) END withquery IF ( cnt < 3 ) then > update ImageGroupTbl set ImageGroupType = 'unknown' silentSQL ( freestring ) END if ELSE END IF ' mark the IMAGE that has been split-off as being on its own ' i.e. unknown, and with its own ImageGroup value > update master set hash = ( phash || dhash ) >> where rowid = freestring = freestring & " " & xSelf silentSQL ( freestring ) > update ImageGroupTbl set ImageGroupType = 'unknown' >> , ImageGroup = freestring = freestring & " " & sqlString ( xImageGroup ) >> where selfA = freestring = freestring & " " & xSelf silentSQL ( freestring ) ' update MASTER with info for all the IMAGES in the ImageGroupTbl > update Master >> SET >> ImageGroup = frmTbl.ImageGroup >> , ImageGroupType = frmTbl.ImageGroupType >> FROM >> (select selfa , ImageGroup , ImageGroupType from ImageGroupTbl ) as frmTbl >> WHERE >> Master.rowid = frmTbl.selfA silentSQL(freestring) END FUNCTION FUNCTION CreateTargetReport ( sobid ) dim ridA = sob ( siGroup00, "get" , "data") dim txt = "" dim FileA , pHashA , dHashA dim FileB , pHashB , dHashB > select path||name , pHash , dHash from master where rowid = freestring = freestring & " " & ridA WITHQUERY ( freestring ) FileA = wqtext(1) pHashA = wqInt(2) dHashA = wqInt(3) END withquery dim ridB = cint(sob ( sobid , "GET" , "DATA" )) > select path||name , pHash , dHash from master where rowid = freestring = freestring & " " & ridB WITHQUERY ( freestring ) FileB = wqtext(1) pHashB = wqInt(2) dHashB = wqInt(3) END withquery IF ( ! isFile ( FileB ) ) then txt = " File does not exist " & FileB sob ( TarInfo , "SET" , "TITLE", txt ) EXIT function END if txt = right("000000" & ridB,6) txt = txt & " pHD=" & right( "00" & popCount ( pHashA bxor pHashB ) , 2 ) txt = txt & " dHD=" & right( "00" & popCount ( dHashA bxor dHashB ) , 2 ) txt = txt & " " & right( "00000" & CANVAS ( "IMAGE.FILE", "GET" , "WIDTH" , FileB ) , 5 ) txt = txt & "/" & right( "00000" & CANVAS ( "IMAGE.FILE", "GET" , "height" , FileB ) , 5 ) txt = txt & " " & FileB sob ( TarInfo , "SET" , "TITLE", txt ) END function FUNCTION removeThumbNails ( siMatrix ) ' input parameter is the SOB ID of the target MATRIX dim ChildCnt = SOB ( siMatrix , "CHILD" , "COUNT" ) dim i dim siTmp , ThumbnailId FOR i = 1 to ChildCnt siTmp = sob ( siMatrix , "child", "handle" , i ) IF ( sob ( siTmp ,"GET","data" ) ) then sob( siTmp, "set" , "image" , "") ThumbnailId = sob( siTmp, "get" , "canvas" ) IF ( ThumbnailId ) then canvas( ThumbnailId , "DELETE" ) END IF sob ( siTmp ,"SET","data" , 0 ) END IF NEXT i END function FUNCTION removeAllThumbNails ( ) sob (RefInfo , "SET", "TITLE" , "" ) sob (TarInfo , "SET", "TITLE" , "" ) removeThumbNails ( siImageGroup ) removeThumbNails ( siImageMatrix ) END Function FUNCTION ImageCnts ( rid ) ' ------------------------------------------ ' create a table of images where sob ( StatusTxt , "EMPTY" ) ' ImageGroup value is the same as the reference image CreateImageGroupTable ( rid ) ' pHash / dHash values are "near-enough" CreateSimilarImagesTable ( rid ) ' ------------------------------------------ SELECT count (*) from ImageGroupTbl dim igCnt = qrSingleValue SELECT count (*) from MatchesSimilarTbl where not selfa in ( select selfa from ImageGroupTbl ) dim msCnt = qrSingleValue sob ( StatusTxt , "SET", "TITLE" , "*identical* images= " & igCnt & " *similar*= " & msCnt & " " & sob ( StatusTxt , "gET", "TITLE" ) ) END Function FUNCTION getCopies ( rid ) ' based on the specified rid ' get its xHash ' then get all xHashes within a Hamming distance of maxHD ' there are 2 reason to treat an images as identical/near-identical to the reference image ' 1) ' it's pHash / dHash are "near-enough" ' 2) ' an image has been given the same ImageGroup value as the reference image ' ------------------------------------------------------------------------------------ ' first we tidy up from any previous display! dim gridIndex = 0 dim matrixIndex = 0 dim siTmp dim CanvasHandle dim i , zFile ' ------------------------------------------------------------------------------------ ' Because this function is called from different places it ' is possible that the reference image is ' logically removed ' or physically removed i.e. no longer in the file system ' sob ( siGroup00 ,"SET","data" , rid ) > select path||name , IgnoreFile from Master where rowid = freestring = freestring & " " & rid WITHQUERY ( freestring ) zFile = wqText(1) i = wqInt (2) END withquery IF ( i ) then ' logically removed sob ( RefInfo , "SET", "TITLE" , "Logically removed " & zFile ) ' EXIT function END if IF (! isFile ( zFile) ) then ' physically removed sob ( RefInfo , "SET", "TITLE" , "Physically removed " & zFile ) ' EXIT function END if ' ------------------------------------------ ' create a table of images whose ' ImageGroup value is the same as the reference image CreateImageGroupTable ( rid ) ' pHash / dHash values are "near-enough" CreateSimilarImagesTable ( rid ) ' ------------------------------------------ ' now handle the reference image ' display it dim xHash , ImageGroup dim txt = right("000000" & rid ,6) & " " > select path|| name , pHash , ImageGroup from ImageGroupTbl where selfa = freestring = freestring & " " & rid WITHQUERY (freestring ) zFile = wqtext(1) xhash = wqtext(2) ImageGroup = wqText(3) sob ( siGroup00 ,"SET","data" , rid ) IF ( isFile ( zFile ) ) then sob ( siGroup00 ,"SET","IMAGE", zFile ) txt = txt & " " & right( "00000" & CANVAS ( "IMAGE.FILE", "GET" , "WIDTH" , zFile ) , 5 ) txt = txt & "/" & right( "00000" & CANVAS ( "IMAGE.FILE", "GET" , "height" , zFile ) , 5 ) txt = txt & " " & zFile ELSE txt = txt & " File does not exist " & zFile END if sob ( RefInfo , "SET", "TITLE" , txt ) END withquery ' ------------------------------------------ ' any IMAGEs in the ImageGroupTbl should not be handled as a POTENTIAL matches > delete from MatchesSimilarTbl where selfA in ( select selfA from ImageGroupTbl ) SilentSQL (freestring ) ' ------------------------------------------ ' do not want to handle the REFERENCE IMAGE again, so remove it > delete from ImageGroupTbl where selfA = freestring = freestring & " " & rid SilentSQL (freestring ) ' ------------------------------------------ ' any IMAGE still in the MatchesSimilarTbl can be displayed matrixIndex = 0 > select path||name , selfA from MatchesSimilarTbl limit freestring = freestring & " " & ( maxThumbNailIndex ) WITHQUERY (freestring ) siTmp = sob ( siMatrix00 , "SIBLING", matrixIndex ) sob(siTmp ,"SET","data" , wqInt(2) ) IF ( isFile ( wqtext(1) ) ) then sob(siTmp ,"SET","IMAGE", wqtext(1)) END IF matrixIndex = matrixIndex + 1 END withquery ' ------------------------------------------ ' any IMAGE still in the ImageGroupTbl can be displayed gridIndex = 1 > select path||name , selfA from ImageGroupTbl >> limit freestring = freestring & " " & ( maxThumbNailIndex + 1) WITHQUERY (freestring ) siTmp = sob ( siGroup00 , "SIBLING", gridIndex ) gridIndex = gridIndex + 1 sob(siTmp ,"SET","data" , wqInt(2) ) IF ( isFile ( wqtext(1) ) ) then sob(siTmp ,"SET","IMAGE", wqtext(1)) END IF END withquery ' ------------------------------------------ IF ( ( MatrixIndex == 1 ) and ( gridIndex == 1 ) ) then CreateTargetReport ( sob ( siMatrix00 , "SIBLING", 0 ) ) ELSEIF ( ( MatrixIndex == 0 ) and ( gridIndex == 2 ) ) then CreateTargetReport ( sob ( siGroup00 , "SIBLING", 1 ) ) END if ' ImageCnts ( rid ) END function FUNCTION MakeBkTree ( ) ' Not HAPPY ' it sees that a calls to DBsave and DBSaveAs cause ' the VIRTUAL tables that were previously existant to be ' emptied. ' But the TABLE name remains known. ' So need some code to check whether the BKTree is really present ! dim flag = 0 > select tbl_name from sqlite_temp_master where tbl_name like 'xHashTbl' WITHQUERY (freestring) > select count (*) from xHashTbl WITHQUERY (freestring) flag = qrsingleValue END withquery END withquery IF ( flag ) then EXIT function END if drop table if exists xHashTbl create virtual table temp.xHashTbl using bk64tree > insert into xHashTbl (metric , link) select phash , rowid from master >> where >> IgnoreFile = 0 >> and not phash is NULL >> and ext in freestring = freestring & " " & ImageFileExtensions sql( freestring) END Function FUNCTION cbImageGroupTools ( sobid ) sob ( ImageGroupOvl , "set", "show", 1 ) MakeBkTree () getCopies ( CurrentRowid ) END Function ' --------------------------------------------------------------------------------------------------------- ' End of Image Grouping ' --------------------------------------------------------------------------------------------------------- FUNCTION selectToolPage ( ) ' dependent on the popup Check flags ' decide which tool page should be displayed IF ( currenthash == "" ) then EXIT function END if dim rid > select rowid from master where hash = freestring = freestring & " " & sqlString (currenthash) >> limit 1 WITHQUERY ( freestring ) rid = wqInt ( 1 ) END withquery ImageCnts ( rid ) IF ( sob ( RegionPopUpid , "get", "check" ) ) then sob ( TagCommentOvl , "set" , "show" , 1) ELSEIF ( sob ( TaggingPopUpid , "get", "check" ) ) then cbtoolsMenuTagFilesB ( TaggingPopUpid ) ELSEIF ( sob ( FreeTextPopUpid , "get", "check" ) ) then cbFreeTextTools ( FreeTextPopUpid ) ELSEIF ( sob ( ImageGroupPopUpid , "get", "check" ) ) then cbImageGroupTools ( ImageGroupPopUpid ) END if END FUNCTION ' -------------------- FUNCTION cbPopupToolSelect ( sobid ) sob ( sobid , "set", "checks.off" ) sob ( sobid , "set", "check" , 1 ) selectToolPage () END function ' -------------------- dim RegionPopUpid = sob (CopyPastePopUp , "add", "menu","Vertical", "Region Tools") sob ( -1 , "ON" , "CLICK" , "cbPopupToolSelect") ' -------------------- dim TaggingPopUpid = sob (CopyPastePopUp , "add", "menu","Vertical", "Tagging Tools") sob ( -1 , "set" , "check" , 1 ) sob ( -1 , "ON" , "CLICK" , "cbPopupToolSelect") ' -------------------- dim FreeTextPopUpid = sob (CopyPastePopUp , "add", "menu","Vertical", "Free Text Tools") sob ( -1 , "ON" , "CLICK" , "cbPopupToolSelect") ' -------------------- dim ImageGroupPopUpid = sob (CopyPastePopUp , "add", "menu","Vertical", "Image Grouping") sob ( -1 , "ON" , "CLICK" , "cbPopupToolSelect") ' -------------------- FUNCTION cbImageMOUSECLICKS ( sobid, clickId ) ' right mouse button clickid = 7 if ( clickid <> 7 ) then exit function IF ( ! ImageLoadedFlag ) then EXIT function END if ' treat as a left mouse button cbImagePress ( sobid ) ' -------------------- ' decide which popup menu options are relevant SELECT count (*) from tblRectList where not rlHit = 0 IF ( qrSingleValue ) then IF ( copyTagName == "" ) then sob ( PastePopUpid , "SET" , "MENU.GREY" , 1 ) ELSE sob ( PastePopUpid , "SET" , "MENU.GREY" , 0 ) END if sob ( CopyPopUpid , "SET" , "MENU.GREY" , 0 ) sob ( DeletePopUpid , "SET" , "MENU.GREY" , 0 ) ELSE sob ( CopyPopUpid , "SET" , "MENU.GREY" , 1 ) sob ( PastePopUpid , "SET" , "MENU.GREY" , 1 ) sob ( DeletePopUpid , "SET" , "MENU.GREY" , 1 ) END IF ' -------------------- sob(CopyPastePopUp,"SET","POSITION", sob ( appWindow , "get" , "cursor.x" ), sob ( appWindow , "get" , "cursor.y" ) ) sob(CopyPastePopUp,"set","show",1) END function ' image takes size of SOB ' minimum SOB size 10 x 10 Pixels dim sobImageHolder = sob (ImagePanelSob , "ADD","image.holder",0 ,10,10 ) dim sobImageHolderFixed = sobImageHolder sob ( sobImageHolder , "ON" , "PRESS", "cbImagePress") sob ( sobImageHolder , "ON" , "MOUSECLICKS", "cbImageMOUSECLICKS") sob ( sobImageHolder , "SET" , "RGB", RGB( 175 , 225, 255) ) ' ----------------------------------------------------- sob ("RGB","DEFAULT") ' ----------------------------------------------------- FUNCTION getFileHash ( fullName ) getFilehash = 0 IF ( isFile ( fullName ) == 1 ) then getFilehash = HashFile ( "MD5", fullName ) END IF END FUNCTION ' ------------------------------------------------------- ' tag boxes ' ------------------------------------------------------- FUNCTION CB_FIND_tag ( sobID ,editChr ) ' based on the txt in a filter box ( sobid ) ' look for matching tag txts in the appropriate tag column ' set the filter's COMBO to be empty dim FilterCombo = sob(sobid ,"SIBLING", 1) sob ( FilterCombo,"empty") ' get sobId for the related COMBO that defines the TAG Group dim typeSobComo = sob(sobid ,"SIBLING", -1) ' get the TagType dim typeTag = sob( typeSobComo, "GET" , "TITLE" ) dim filterTxt = sob(sobid,"GET","TITLE") ' do not include any special (non-printable) character in the filter text IF ( editChr < 0x020 ) then ELSEIF ( editChr > 0x7e ) then ELSE filterTxt = filterTxt & chr(editChr) END IF IF ( isDBopen ) then ' find the matching TAG txts (yes allow > 1 ) > select ttName from tagTable where ttGroup like $Type and ttName like '%' || $txt || '%' order by ttName asc freestring = replace ( freestring , "$Type" , sqlIdentifier ( typeTag & TagGroupId ) ) freestring = replace ( freestring , "$txt" , sqlString ( filterTxt ) ) WITHQUERY (freestring) ' place the tag txts in the filter's COMBO sob ( FilterCombo, "add", "row" , wqText(1)) END withquery END if ' default is to select the first txt in the combo sob( FilterCombo , "SET", "SELECTION" , "ROW", 1 ) END Function FUNCTION onEditFilter ( sobid , editChr ) IF ( editChr == escChr) then sob( sobid , "EMPTY") sob( sob(sobid ,"SIBLING", 1) , "EMPTY") END IF END FUNCTION FUNCTION CB_action_select ( sobid , row ) dim actionBtnId = sob(sobid,"GET","UNK") dim ActionTxt = sob(sobid,"GET","TITLE") sob ( actionBtnId , "SET" , "TITLE" ,ActionTxt) IF ( ActionTxt == NoTag ) then sob( actionBtnId, "SET" , "RGB" , defBoxTxtCol) ELSE sob( actionBtnId, "SET" , "RGB" , 0x0ff ) ' red END IF END FUNCTION ' ------------------------------------------------------- FUNCTION updateSingleFile ( onhash, actionType, tagGroup , tagTxt ) IF ( actionType == "remove ALL" ) then > update master SET freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> = freestring = freestring & " " & sqlString ( TagDelim ) >> WHERE hash like freestring = freestring & " " & sqlString ( onHash ) sql ( freestring ) EXIT function END IF IF ( tagTxt == "" ) then EXIT function END if IF ( actionType == "add" ) then > UPDATE master SET freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> = freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> || freestring = freestring & " " & sqlString ( tagTxt & tagDelim ) >> where freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> NOT LIKE '%' || freestring = freestring & " " & sqlString ( tagDelim & tagTxt & tagDelim) >> || '%' AND >> hash like freestring = freestring & " " & sqlString ( onHash ) sql ( freestring ) ELSEIF ( actionType == "replace" ) then ' remove any Region reference for this Tag group > select ttName from tagTable where ttGroup like freestring = freestring & " " & sqlString ( TagGroup ) WITHQUERY ( freestring ) deReferenceTagInRegions ( TagGroup , wqText(1) ) END withquery > UPDATE master SET freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> = freestring = freestring & " " & sqlString ( tagDelim & tagTxt & tagDelim) >> where >> hash like freestring = freestring & " " & sqlString ( onHash ) sql ( freestring ) ELSEIF ( actionType == "remove" ) then > UPDATE master SET freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> = replace ( freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> , freestring = freestring & " " & sqlString ( TagDelim & tagTxt & TagDelim ) >> , freestring = freestring & " " & sqlString ( TagDelim ) >> ) WHERE freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> LIKE '%' || freestring = freestring & " " & sqlString ( TagDelim & tagTxt & TagDelim ) >> || '%' and >> hash like freestring = freestring & " " & sqlString ( onHash ) sql ( freestring ) ' but the TAG could also be in RectMeta deReferenceTagInRegionsInHash ( onHash , tagGroup , tagTxt ) ELSE ' treat as NoTag END IF END function FUNCTION cbBulkActionSelect ( sobid , rowid ) dim actionTxt = sob( sobid, "GET" , "TITLE" ) dim ActionTagGroup= sob( sob(sobid,"sibling",1) , "GET" , "TITLE" ) IF ( ( actionTxt == noTag ) or ( ActionTagGroup == noTag ) ) then sob ( sob(sobid ,"SIBLING", 4 ) , "SET" ,"RGB", defLabelCol ) sob ( sob(sobid ,"SIBLING", 4 ) , "SET" ,"TITLE", NoTag ) sob ( sob(sobid ,"SIBLING", 3 ) , "empty" ) ELSE sob ( sob(sobid ,"SIBLING", 4 ) , "SET" ,"RGB", 0x0ff ) sob ( sob(sobid ,"SIBLING", 4 ) , "SET" ,"TITLE", actionTxt ) END IF END FUNCTION FUNCTION cbBulkDoOnePress ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if dim actionType , TagType , TagTxt dim BaseSob = sob( sobid ,"SIBLING", -4 ) actionType = sob( BaseSob , "GET" , "TITLE" ) TagType = sob( sob( BaseSob ,"SIBLING", 1 ), "GET" , "TITLE" ) TagTxt = sob( sob( BaseSob ,"SIBLING", 3 ), "GET" , "TITLE" ) updateSingleFile ( currenthash, actionType, TagType , TagTxt ) toggleWarnColour ( sobid ) DisplayTagsInFile () END function FUNCTION cbBulkDoOneDrop ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if dim fileCount IF ( sob(sobid,"GET" , "TITLE" ) == NoTag ) then EXIT function END IF IF ( ! isDBopen ) then ErrorReport ("No database selected" ) EXIT function END IF dim actionType , TagType , TagTxt dim BaseSob = sob( sobid ,"SIBLING", -4 ) actionType = sob( BaseSob , "GET" , "TITLE" ) TagType = sob( sob( BaseSob ,"SIBLING", 1 ), "GET" , "TITLE" ) TagTxt = sob( sob( BaseSob ,"SIBLING", 3 ), "GET" , "TITLE" ) CreateDroppedFileList () dim i , hasHash , inDir , hasExt SELECT count (*) from temp.bulk where type like 'file' fileCount = qrSingleValue cancelText( "Tagging in progress" , 1 ) cancelText( "This can take a short while" , 3 ) cancelText( "Files to process: " & FileCount , 2 ) cancelShow(1) WITHQUERY ( "select hash , ( path|| name ) , ext from temp.bulk where type like 'file' ") IF ( extensionCheck ( wqText(3) ) ) then sob( PathFile , "ADD" , "ROW" , wqText(2) & EOL ) updateSingleFile ( wqText(1) , actionType, TagType , TagTxt ) END IF cancelToggle () fileCount-- IF ( fileCount BAND 0x0f ) then ELSE cancelText( "Files to process: " & FileCount , 2 ) END IF END WithQuery cancelShow(0) toggleWarnColour ( sobid ) END FUNCTION FUNCTION cbBulkDoAllPress ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if cbBulkActionOnHash ( CurrentHash ) cbSingleFile ( CurrentFile ) END function FUNCTION cbBulkDoAllDrop ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if dim fileCount IF ( sob(sobid,"GET" , "TITLE" ) == NoTag ) then EXIT function END IF IF ( ! isDBopen ) then ErrorReport ("No database selected" ) EXIT function END IF CreateDroppedFileList () dim i , hasHash , inDir , hasExt SELECT count (*) from temp.bulk where type like 'file' fileCount = qrSingleValue cancelText( "Tagging in progress" , 1 ) cancelText( "This can take a short while" , 3 ) cancelText( "Files to process: " & FileCount , 2 ) cancelShow(1) WITHQUERY ( "select hash , ( path|| name ) , ext from temp.bulk where type like 'file' ") IF ( extensionCheck ( wqText(3) ) ) then sob( PathFile , "ADD" , "ROW" , wqText(2) & EOL ) cbBulkActionOnHash ( hasHash ) END IF cancelToggle () fileCount-- IF ( fileCount BAND 0x0f ) then ELSE cancelText( "Files to process: " & FileCount , 2 ) END IF END WithQuery cancelShow(0) toggleWarnColour ( sobid ) END FUNCTION ' ------------------------------------------------------- FUNCTION cbDoBlock ( sobid , hasHash) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if dim blockOfRows = sob ( sobid, "get", "unk" ) ' a COLUMN CONTAINER dim rowNbr = 1 dim HandledCnt = 0 dim actionType , TagType , TagTxt dim ColID dim rowID = sob ( blockOfRows , "GET" , "handle" , 0 , rowNbr ) DO while ( rowid ) ' is a ROW CONTAINER ColID = sob ( rowid , "GET" ,"handle" , 0 , 1 ) actionType = sob( sob ( colID , "SIBLING", 0 ) , "GET", "TITLE" ) TagType = sob( sob ( colID , "SIBLING", 1 ) , "GET", "TITLE" ) TagTxt = sob( sob ( colID , "SIBLING", 3 ) , "GET", "TITLE" ) IF ( actionType == noTag ) then ELSEIF ( TagType == noTag ) then ELSE HandledCnt = HandledCnt + 1 updateSingleFile ( hasHash, actionType, TagType , TagTxt ) END IF rowNbr++ rowID = sob ( blockOfRows , "GET" , "handle" , 0 , rowNbr ) LOOP ' want HANDLE of the DO button if ( HandledCnt ) then toggleWarnColour ( sobid ) END function FUNCTION cbDoBlockOnPress ( sobid ) cbDoBlock ( sobid , currentHash) DisplayTagsInFile () END FUNCTION FUNCTION CreateDroppedFileList ( ) dim i , inDir drop table if exists temp.bulkSum cancelClear () cancelText( "Identifing target files" , 1 ) cancelText( "This can take a short while" , 3 ) cancelShow(1) FOR i = 1 to GetDropCount IF ( isFile ( getDropData ( i ) ) == 1 ) then dbFileList ( "temp", "inputBulkFile" , getDropData ( i ) , , "*","md5") ELSEIF ( isFile ( getDropData ( i ) ) == -1 ) then inDir= getDropData ( i ) & "\\" dbFileList ( "temp", "inputBulkFile" , inDir, -1 , ,"MD5" ) END IF IF ( i == 1 ) then alter table temp.inputBulkFile rename to bulkSum ELSE insert into temp.bulkSum select * from temp.inputBulkFile END IF cancelToggle () NEXT I cancelShow(0) delete from temp.bulkSum where not type like 'file' drop table if exists temp.Bulk create temp table bulk as select * from master where fsHash in ( select hash from bulkSum ) END FUNCTION FUNCTION cbDoBlockOnDrop ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if dim fileCount IF ( sob(sobid,"GET" , "TITLE" ) == NoTag ) then EXIT function END IF IF ( ! isDBopen ) then ErrorReport ("No database selected" ) EXIT function END IF CreateDroppedFileList () dim i , hasHash , inDir , hasExt SELECT count (*) from temp.bulk where type like 'file' fileCount = qrSingleValue cancelText( "Tagging in progress" , 1 ) cancelText( "This can take a short while" , 3 ) cancelText( "Files to process: " & FileCount , 2 ) cancelShow(1) WITHQUERY ( "select hash , ( path|| name ) , ext from temp.bulk where type like 'file' ") IF ( extensionCheck ( wqText(3) ) ) then sob( PathFile , "ADD" , "ROW" , wqText(2) & EOL ) cbDoBlock ( sobid , wqText(1) ) END IF cancelToggle () fileCount-- IF ( fileCount BAND 0x0f ) then ELSE cancelText( "Files to process: " & FileCount , 2 ) END IF END WithQuery cancelShow(0) toggleWarnColour ( sobid ) END FUNCTION FUNCTION makeOneofBlockOfRows (sobidParent) dim thisRowSob = sob ( sobidParent , "ADD", "CONTAINER", "ROW" ) sob ( -1 , "ADD" , "COMBO" , "dropDown" , defActionComboText ) sob ( -1 , "add" , "row" , NoTag ) sob ( -1 , "add" , "row" , "add" ) sob ( -1 , "add" , "row" , "replace" ) sob ( -1 , "add" , "row" , "remove" ) sob ( -1 , "add" , "row" , "remove all" ) sob ( -1 , "SET" , "SELECTION" , "ROW", 1 ) sob ( -1 , "ON" , "SELECTION" , "cbBulkActionSelect" ) dim comboSOb = sob ( -1 , "ADD" , "COMBO" , "dropdown" , defTypeComboText ) sob ( -1 , "add" , "row" , NoTag ) sob ( -1 , "SET" , "SELECTION" , "ROW", 1 ) sob ( -1 , "ON" , "SELECTION" , "cbFilterTagSelect" ) ' to automatically populate with Tag Group changes isTagTypeCombo ( comboSOb ) isSavedTagTypeCombo (comboSOb ) sob ( thisRowSob , "Add" , "edit" , " " ) sob ( -1 , "empty" ) sob ( -1 , "ON","EDIT" , "onEditFile " ) sob ( -1 , "ON","EDITED" , "onEditedFile" ) sob ( -1 , "ON","ENTER", "CB_FIND_tag") sob ( -1 , "add","COMBO" , "dropdown" , defaultText ) sob ( -1 , "empty") sob ( -1 , "set" ,"stretch" , 1 ) sob ( thisRowSob , "add" , "Button" , "PUSH", " " ) sob ( -1 , "set" , "title" , noTag ) sob ( -1 , "ON", "CLICK", "cbBulkDoOnePress") sob ( -1 , "ON", "DROP.FILE", "cbBulkDoOneDrop") END FUNCTION FUNCTION makeBlockOfRows ( sobid , nbrRows , BlkId) dim blockRow = sob ( sobid , "ADD", "CONTAINER" , "ROW.h") dim actionBtn = sob ( blockRow , "add" , "Button" , "PUSH", " " ) sob ( actionBtn , "ON" , "PRESS", "cbDoBlockOnPress") sob ( -1 , "ON" , "DROP.FILE", "cbDoBlockOnDrop") sob ( -1 , "set" , "title" , "Do (" & BlkId & ")" ) dim BlockOfRows = sob ( blockRow , "ADD", "CONTAINER" , "COLUMN.w") dim i FOR i = 1 to nbrRows makeOneofBlockOfRows (BlockOfRows ) NEXT I sob ( actionBtn , "set" , "unk" , BlockOfRows ) sob ( sobid , "add" , "space" , 0,4 ) END FUNCTION FUNCTION makeBlocksB ( ) dim BlockOfRows = sob ( FileTaggingOvl , "ADD", "CONTAINER" , "COLUMN.w") sob ( -1 , "ADD", "LABEL" ,"File Tagging" ) sob ( -1 , "SET", "RGB" , -1 ) sob ( BlockOfRows , "ADD", "CONTAINER" , "ROW") sob ( -1 , "add", "LABEL" , " Action Tag Group Filter Tag Name " ) sob ( -1 , "add", "LABEL" , " " ) dim scrolledSob = sob ( BlockOfRows , "ADD", "CONTAINER" , "BOX.SCROLLED", " ",10) dim rowHolder = sob ( scrolledSob , "ADD", "CONTAINER" , "COLUMN.W") makeBlockOfRows ( rowHolder , 6 , "A" ) makeBlockOfRows ( rowHolder , 6 , "B" ) makeBlockOfRows ( rowHolder , 6 , "C" ) makeBlockOfRows ( rowHolder , 6 , "D" ) makeBlockOfRows ( rowHolder , 6 , "E" ) makeBlockOfRows ( rowHolder , 6 , "F" ) makeBlockOfRows ( rowHolder , 6 , "G" ) makeBlockOfRows ( rowHolder , 6 , "H" ) END function makeBlocksb () ' ------------------------------------------------------- ' FileExportOvl FUNCTION onEditFile ( sobid , editChr ) IF ( editChr == escChr) then sob( sobid , "EMPTY") sob( sob(sobid , "SIBLING", 1) , "EMPTY") END IF END FUNCTION FUNCTION updateSpecificTagNameCombo ( sobid , typeTag ,FilterTxt ) dim cnt = 0 sob( sobid , "empty" ) IF ( isDBopen ) then EXIT function END IF ' find the matching TAG txts (yes allow > 1 ) > select ttName from tagTable where ttGroup like $Type and ttName like '%' || $txt || '%' order by ttname asc freestring = replace ( freestring , "$Type" , sqlIdentifier ( typeTag & TagGroupId ) ) freestring = replace ( freestring , "$txt" , sqlString ( filterTxt ) ) WITHQUERY (freestring) ' place the tag txts in the filter's COMBO sob ( sobid, "add", "row" , wqText(1)) cnt++ END withquery IF ( cnt == 0 ) then > select ttName from tagTable where ttGroup like $Type order by ttName asc freestring = replace ( freestring , "$Type" , sqlIdentifier ( typeTag & TagGroupId ) ) WITHQUERY (freestring) ' place the tag txts in the filter's COMBO sob ( sobid, "add", "row" , wqText(1)) cnt++ END withquery END if ' default is to select the first txt in the combo sob( sobid , "SET", "SELECTION" , "ROW", 1 ) END FUNCTION FUNCTION GetFilteredChoice ( sobid ) dim TagGroupSob = sobid dim FilterSob = sob(sobid ,"SIBLING", 1 ) dim TagListSob = sob(sobid ,"SIBLING", 2 ) dim TagGroup = sob( TagGroupSob ,"GET" , "TITLE" ) dim filterTxt = sob( FilterSob ,"GET" , "TITLE" ) sob ( TagListSob , "empty") IF ( isDBopen ) then ' find the matching TAG txts (yes allow > 1 ) > select ttName from tagTable where ttGroup like $Type and ttName like '%' || $txt || '%' order by ttname asc freestring = replace ( freestring , "$Type" , sqlIdentifier ( TagGroup & TagGroupId ) ) freestring = replace ( freestring , "$txt" , sqlString ( filterTxt ) ) WITHQUERY (freestring) ' place the tag txts in the filter's COMBO sob ( TagListSob, "add", "row" , wqText(1)) END withquery END if ' default is to select the first ttName in the combo sob( TagListSob , "SET", "SELECTION" , "ROW", 1 ) END FUNCTION FUNCTION onEditedFile ( sobid , editChr ) ' for every character entered in to the filter box ' trigger an update of the related tag Txt combo IF ( editChr == escChr) then sob( sobid , "EMPTY") sob( sob(sobid ,"SIBLING", 1) , "EMPTY") ELSE IF ( editChr > 0x01f ) then dim useTypeCombo = sob(sobid ,"SIBLING", -1 ) dim useTxtCombo = sob(sobid ,"SIBLING", 1 ) ' set the filter's COMBO to be empty sob ( useTxtCombo ,"empty") ' get the TagType dim typeTag = sob( useTypeCombo, "GET" , "TITLE" ) dim filterTxt = sob(sobid,"GET","TITLE") ' do not include any special (non-printable) character in the filter text IF ( editChr < 0x020 ) then ELSEIF ( editChr > 0x7e ) then ELSE filterTxt = filterTxt & chr(editChr) END IF IF ( isDBopen ) then ' find the matching TAG txts (yes allow > 1 ) > select ttName from tagTable where ttGroup like $Type and ttName like '%' || $txt || '%' order by ttname asc freestring = replace ( freestring , "$Type" , sqlIdentifier ( typeTag & TagGroupId ) ) freestring = replace ( freestring , "$txt" , sqlString ( filterTxt ) ) WITHQUERY (freestring) ' place the tag txts in the filter's COMBO sob ( useTxtCombo, "add", "row" , wqText(1)) END withquery END if ' default is to select the first ttName in the combo sob( useTxtCombo , "SET", "SELECTION" , "ROW", 1 ) END IF END IF updateNewTagNameField () END FUNCTION FUNCTION cbExportActionSelect ( sobid , rowid ) dim TagAction = sob ( sobid , "GET", "TITLE") dim TagGroup = sob(sobid ,"SIBLING", 1 ) dim TagFilter = sob(sobid ,"SIBLING", 2 ) dim Tagname = sob(sobid ,"SIBLING", 3 ) IF ( instr ( Tagaction , "path" ) ) Then ELSEIF ( instr ( Tagaction , "file" )) Then ELSEIF ( instr ( Tagaction , "ext" ) )Then ELSEIF ( instr ( Tagaction , "lock" ) )Then ELSEIF ( instr ( Tagaction , "+duplicate" ) )Then ELSEIF ( instr ( Tagaction , "+empty" ) )Then ELSE ' is a TAG action EXIT function END IF ' is a file system action sob ( TagGroup , "set" , "SELECTION" , "ROW" , 1 ) sob ( TagName , "empty" ) END function FUNCTION cbFilterTagSelect ( sobid, rowid ) dim TagGroup = sob ( sobid , "GET", "TITLE") dim TagTxt = sob ( sob(sobid ,"SIBLING", 1 ) , "GET", "TITLE") dim ActionTxt = sob ( sob(sobid ,"SIBLING", -1 ) , "GET", "TITLE") sob ( sob(sobid ,"SIBLING", 2 ) , "empty" ) IF ( ( actionTxt == noTag ) or ( TagGroup == noTag ) ) then sob ( sob(sobid ,"SIBLING", 3 ) , "SET" ,"RGB", defLabelCol ) sob ( sob(sobid ,"SIBLING", 3 ) , "SET" ,"TITLE", NoTag ) EXIT function ELSE sob ( sob(sobid ,"SIBLING", 3 ) , "SET" ,"RGB", 0x0ff ) sob ( sob(sobid ,"SIBLING", 3 ) , "SET" ,"TITLE", actionTxt ) END IF IF ( tagTxt == "" ) then tagTxt == "%" END IF updateSpecificTagNameCombo ( sob(sobid ,"SIBLING", 2 ) , TagGroup ,tagTxt ) ' trigger the FILTER to be re-evaluated onEditedFile ( sob ( sobid , "SIBLING", 1 ) , "" ) END FUNCTION FUNCTION makeFileListSectionBoxes ( sobid ) dim AreaSob = sob ( sobid , "ADD", "CONTAINER", "ROW") sob ( -1 , "ADD" , "COMBO" , "dropdown" , defActionComboText ) sob ( -1 , "add" , "row" , NoTag ) sob ( -1 , "add" , "row" , "add ==" ) sob ( -1 , "add" , "row" , "add <>" ) sob ( -1 , "add" , "row" , "add any" ) sob ( -1 , "add" , "row" , "add non" ) sob ( -1 , "add" , "row" , "add ==?" ) sob ( -1 , "add" , "row" , "add <>?" ) sob ( -1 , "add" , "row" , "Tags ==" ) sob ( -1 , "add" , "row" , "Tags =+" ) sob ( -1 , "add" , "row" , "Tag ==" ) sob ( -1 , "add" , "row" , "Tag =+" ) sob ( -1 , "add" , "row" , "Tag only" ) sob ( -1 , "add" , "row" , "+duplicate" ) sob ( -1 , "add" , "row" , "+empty" ) sob ( -1 , "add" , "row" , "remove <>" ) sob ( -1 , "add" , "row" , "remove ==" ) sob ( -1 , "add" , "row" , "remove non" ) sob ( -1 , "add" , "row" , "remove any" ) sob ( -1 , "add" , "row" , "remove <>?" ) sob ( -1 , "add" , "row" , "remove ==?" ) sob ( -1 , "add" , "row" , "-Unchanged" ) sob ( -1 , "add" , "row" , "-New" ) sob ( -1 , "add" , "row" , "-Changed" ) sob ( -1 , "add" , "row" , "lock" ) sob ( -1 , "SET" , "SELECTION" , "ROW", 1 ) sob ( -1 , "ON" , "SELECTION" , "cbExportActionSelect" ) dim ComboSob = sob ( -1 , "ADD" , "COMBO" , "dropdown" , defTypeComboText ) sob ( -1 , "add" , "row" , NoTag ) sob ( -1 , "SET" , "SELECTION" , "ROW", 1 ) sob ( -1 , "ON" , "SELECTION" , "cbFilterTagSelect" ) ' to automatically populate with Tag Group changes isTagTypeCombo ( ComboSob ) isSavedTagTypeCombo ( ComboSob ) sob ( AreaSob , "Add" , "edit" , " " ) sob ( -1 , "empty" ) sob ( -1 , "ON","EDIT" , "onEditFile " ) sob ( -1 , "ON","EDITED" , "onEditedFile" ) sob ( -1 , "ON","ENTER", "CB_FIND_tag") sob ( -1 , "add","COMBO" , "dropdown" , defaultText ) sob ( -1 , "empty") END FUNCTION FUNCTION cbMakeNewFileList ( sobid ) exportFilter ( "new" , "new" , "new" ) sob ( NbrFilesInFilter , "empty") END FUNCTION dim FileFilterArraySob dim NbrFilterRows = 7 FUNCTION cbResetFilter ( sobid ) dim BaseSob , i FOR i = 0 to NbrFilterRows BaseSob = sob ( FileFilterArraySob, "CHILD" ,"HANDLE" , 1 + i ) sob ( sob ( BaseSob, "CHILD", "HANDLE" , 1 ) , "SET" , "SELECTION" , "ROW", 1 ) sob ( sob ( BaseSob, "CHILD", "HANDLE" , 2 ) , "SET" , "SELECTION" , "ROW", 1 ) sob ( sob ( BaseSob, "CHILD", "HANDLE" , 3 ) , "EMPTY") sob ( sob ( BaseSob, "CHILD", "HANDLE" , 4 ) , "EMPTY") NEXT i END FUNCTION FUNCTION cbExportProcess ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if IF ( isDBTable ( "temp", "SubList" ) ) THEN ELSE MarkRowid = 0 drop table if exists temp.SubList create table temp.SubList as select rowid as rid , * from master limit 0 END IF sob ( NbrFilesInFilter , "SET", "TITLE" , "" ) NoErrorReport ("" ) dim BaseSob dim action , TagType , TagName , filter dim txt dim i FOR i = 0 to NbrFilterRows BaseSob = sob ( FileFilterArraySob, "CHILD" ,"HANDLE" , 1 + i ) action = sob ( sob ( BaseSob, "CHILD", "HANDLE" , 1 ) , "GET" ,"TITLE") TagType = sob ( sob ( BaseSob, "CHILD", "HANDLE" , 2 ) , "GET" ,"TITLE") filter = sob ( sob ( BaseSob, "CHILD", "HANDLE" , 3 ) , "GET" ,"TITLE") TagName = sob ( sob ( BaseSob, "CHILD", "HANDLE" , 4 ) , "GET" ,"TITLE") IF ( action == NoTag ) then ELSEIF ( action == "lock" ) then exportFilter ( action , "" , "" ) ELSEIF ( action == "+duplicate" ) then exportFilter ( action , "" , "" ) ELSEIF ( action == "+empty" ) then exportFilter ( action , "" , "" ) ' handle those which use a partial TagName ELSEIF ( action == "add ==?" ) then exportFilter ( action , TagType , filter ) ELSEIF ( action == "add <>?" ) then exportFilter ( action , TagType , filter ) ELSEIF ( action == "remove <>?" ) then exportFilter ( action , TagType , filter ) ELSEIF ( action == "remove ==?" ) then exportFilter ( action , TagType , filter ) ELSEIF ( action == "-unchanged" ) then exportFilter ( "-like" , "rescanState" , "old" ) ELSEIF ( action == "-new" ) then exportFilter ( "-like" , "rescanState" , "new" ) ELSEIF ( action == "-changed" ) then exportFilter ( "-like" , "rescanState" , "changed" ) ' handle those which use a complete Tagname ELSE exportFilter ( action , TagType , TagName ) END IF NEXT i IF ( sob ( sob(inSertWhere,"sibling",-1), "GET","CHECK") ) then txt = trim ( sob ( inSertWhere , "get", "title") ) IF ( txt <> "" ) then > insert into temp.SubList select rowid , * from master where type like 'file' and freestring = freestring & " (" & txt & ")" TRY sql(freestring ) CATCH ErrorReport("Invalid Insert into statement") END try END if END IF IF ( sob (sob(DeleteWhere,"sibling",-1), "GET","CHECK") ) then txt = trim ( sob ( DeleteWhere , "get", "title") ) IF ( txt <> "" ) then > delete from temp.SubList where freestring = freestring & " (" & txt & ")" TRY sql(freestring ) CATCH ErrorReport("Invalid Insert into statement") END try END if END IF sob ( NbrFilesInFilter , "SET", "TITLE" , "" & exportFilter ( "statistic" , "" , "" ) ) END FUNCTION dim NbrFilesInFilter dim bulkExportMatrix FUNCTION toggleCheckState ( sobid ) sob ( sobid , "SET", "CHECK" , ! sob(sobid , "GET", "CHECK") ) END FUNCTION FUNCTION makeFileListSection ( ) dim ExportAreaCol = sob ( FileExportOvl , "ADD", "CONTAINER", "COLUMN.W") SubListTitleSob = sob ( -1 , "add" , "LABEL", "Create a File List (for exporting / deleting and power tagging" ) sob ( -1 , "SET" , "RGB" , 0x0ff00 ) ' --------------------- sob ( ExportAreaCol , "ADD", "CONTAINER", "ROW.w") sob( -1 , "add", "BUTTON" , "PUSH", "New List " ) sob( -1 , "ON" , "PRESS" , "cbMakeNewFileList") sob( -1 , "add", "BUTTON", "PUSH", "Apply Filters" ) sob( -1 , "ON" , "PRESS" , "cbExportProcess") sob( -1 , "add", "BUTTON" , "PUSH", "Reset Filter" ) sob( -1 , "ON" , "PRESS" , "cbResetFilter") ' --------------------- sob ( ExportAreaCol , "ADD", "CONTAINER", "ROW") sob( -1 , "add", "Label", " Number of Files" ) NbrFilesInFilter = sob( -1 , "add", "Label", " " ) ' --------------------- sob ( ExportAreaCol , "ADD", "CONTAINER", "ROW") sob ( -1 , "ADD" , "SPACE" , 0 ,5 ) sob ( -1 , "SET" , "RGB" , -1 ) ' --------------------- sob ( ExportAreaCol , "ADD", "CONTAINER", "ROW") sob ( -1 , "add", "LABEL" , "Action Tag Group Filter Tag Name" ) sob ( -1 , "add" , "SPACE", 0,0 ) ' --------------------- sob ( ExportAreaCol , "ADD", "CONTAINER", "COLUMN.W") FileFilterArraySob = sob("last") dim i FOR i = 0 to NbrFilterRows makeFileListSectionBoxes (FileFilterArraySob) NEXT i tmpSob = sob ( ExportAreaCol , "ADD" , "CONTAINER" , "COLUMN.w") sob ( -1 , "add" , "SPACE" , 0 , 5) sob ( -1 , "SET" , "RGB" , -1 ) ' ----------------------------------- InsertDeleteWhereSob = sob ( tmpSob , "ADD" , "CONTAINER" , "COlumn.w") sob ( InsertDeleteWhereSob , "ADD" , "CONTAINER" , "row") dim chkBtn = sob( -1 , "add" , "button", "check" , "Insert where" ) sob ( chkBtn, "set", "CHECK" , 0 ) sob ( chkBtn, "on", "CLICK" , "toggleCheckState" ) InsertWhere = sob ( -1, "add", "edit" , " " ) sob ( InsertDeleteWhereSob , "ADD" , "CONTAINER" , "row") chkBtn = sob( -1 , "add" , "button", "check" , "Delete where" ) sob ( chkBtn, "set", "CHECK" , 0 ) sob ( chkBtn, "on", "CLICK" , "toggleCheckState" ) DeleteWhere = sob ( -1, "add", "edit" , " " ) ' ----------------------------------- sob ( tmpSob , "add" , "SPACE" , 0 , 5) sob ( tmpSob , "SET" , "RGB" , -1 ) sob ( tmpSob , "ADD" , "CONTAINER" , "ROW.W") BtnExportFiles =sob ( -1 , "add", "BUTTON", "PUSH", "Export Files" ) sob ( -1 , "SET" , "RGB" , RGB(0x8f,0x8f,0x0ff ) ) sob ( -1 , "add" , "SPACE" , 0 , 0) sob ( -1 , "SET" , "RGB" , -1 ) BtnDeleteFiles = sob ( -1 , "add", "BUTTON", "PUSH", "Delete Files in Subset" ) sob ( -1 , "ON" , "PRESS" , "CbdeleteFileListDoit") sob ( -1 , "SET" , "RGB" , RGB(0xff,0,0 ) ) sob ( ExportAreaCol , "add" , "SPACE" , 0 , 0) sob ( -1 , "SET" , "RGB" , -1 ) END function dim BtnExportFiles dim BtnDeleteFiles dim SubListTitleSob makeFileListSection () dim TagCommentEdit , TagRect , TagRectCombo FUNCTION DisplayTagsInFIle ( ) sob (TagInfo, "empty") dim i ,j , splitLine IF ( tagFlag ) then sob (TagInfo,"add", "row" , "-- TAGS for this file --" & EOL ) FOR i = 0 to uBoundTagArray j = 1 ' > select $tag from master where hash = ### limit 1 > select freestring = freestring & " " & sqlIdentifier ( tagArray(i) ) >> from master where hash = freestring = freestring & " " & sqlString ( currenthash ) >> limit 1 sql ( freestring ) splitLine = qrSingleValue IF ( splitLine == "" ) THEN ELSEIF ( splitLine == tagDelim ) THEN ELSE IF ( j ) then sob (TagInfo,"add", "row" , "-- " & right ( tagArray(i) ,-1*length( TagGroupId ) ) & " --" & EOL ) j = 0 END IF splitLine = replace ( splitLine , tagDelim , EOL ) IF ( left ( splitline , 1 ) == EOL ) then splitline = left ( splitline , -1 ) END IF sob (TagInfo,"add", "row" , splitLine & EOL ) END IF NEXT i END IF selectToolPage() ' sob ( TagTxtOvl, "set" , "SHOW" , 1 ) cbSelectFreeTextField ( 1 , 1) ' do not care about the parameter values END FUNCTION FUNCTION CbUPdateTagCommentField ( sobid ) dim rowNr = sob ( TagRectCombo , "get", "selection" ,"row", 1 ) > select rlTagName from tblRectList where not rlHit = 0 order by rowid asc limit 1 offset freestring = freestring & " " & rowNr - 1 sql ( freestring ) dim Name = qrSingleValue TRY ' > update TagTable set ttComment = ## where ttName = ### > update TagTable set ttComment = freestring = freestring & " " & sqlString ( sob(TagCommentEdit,"get","title") ) >> where ttName like freestring = freestring & " " & sqlString ( Name ) sql = freestring toggleOKColour ( sobid ) sob ( sobid , "SET" , "TITLE" , "" ) ' sob ( sobid , "SET", "RGB", defBoxTxtCol ) sob (TagCommentEdit,"set", "row" , "top" , 0 ) CATCH toggleWarningColour ( sobid ) END TRY END FUNCTION FUNCTION cbChangeRectRubberBandTriggered ( sobid ) dim Newname = sob ( sob(sobid, "SIBLING", -1 ) , "get", "title") dim TagGroup = sob ( sob(sobid, "SIBLING", -3 ) , "get", "title") cbMakeRubberbandRegion ( sobId ) SELECT count (*) from tblRectList where rlHit = 1 IF ( qrSingleValue <> 1 ) then EXIT function END if ' initially the RECT will be made with the "name" "fffffffffffffff" sob ( TagRectCombo , "empty" ) sob ( TagRectCombo , "add" , "row" , "fffffffffffffff" ) sob ( TagRectCombo , "set" , "selection", "row" ,1 ) ' update the MASTER RectMeta and TAG column to hold this TAG name ' get Tag group and name from tblRectList dim OldName = sob( TagRectCombo,"get","TITLE") dim rowNr = sob ( TagRectCombo , "get", "selection" ,"row", 1 ) > select rlRect from tblRectList where not rlHit = 0 order by rowid asc limit 1 offset freestring = freestring & " " & rowNr - 1 sql ( freestring ) dim Rect = qrSingleValue UpDateRectMeta ( CurrentHash , Rect, OldName , NewName , TagGroup ) ' now must recreate the tblRectList list CreateRectInfo () ' but recreating it looses the HIT indicator update temp.tblRectList set rlHit = 0 > update temp.tblRectList set rlHit = 1 where rlrect like freestring = freestring & " " & sqlString ( Rect) sql (freestring ) DisplayTagsInFIle () sob ( TagRectCombo , "empty" ) sob ( TagRectCombo , "add" , "row" , NewName ) sob ( TagRectCombo , "set" , "selection", "row" ,1 ) fncShowRegions () HaveRubberBandRect = 0 cbSelectOneRect ( TagRectCombo , 1 ) toggleOKColour ( sobid ) END FUNCTION FUNCTION cbChangeRect ( sobid ) sob ( TagCommentEdit , "empty" ) IF ( sob ( sobid , "get", "title") = NoTag ) then togglewarnColour ( sobid) EXIT function END IF dim Newname = sob ( sob(sobid, "SIBLING", -1 ) , "get", "title") IF ( newName = "" ) then togglewarnColour ( sobid) EXIT function END IF dim TagGroup = sob ( sob(sobid, "SIBLING", -3 ) , "get", "title") IF ( TagGroup = NoTag ) then togglewarnColour ( sobid) EXIT function END IF IF ( HaveRubberBandRect ) then cbChangeRectRubberBandTriggered ( sobid ) EXIT function ELSEIF ( sob(TagRectCombo,"get","TITLE") == "" ) then togglewarnColour ( sobid) EXIT function END if ' we are changing the NAME associated with the current HIT object ' ---------------------------------------------------------------------------- ' update the MASTER RectMeta and TAG columns to hold this TAG name ' get Tag group and name from tblRectList dim OldName = sob ( TagRectCombo , "get","TITLE") dim rowNr = sob ( TagRectCombo , "get", "selection" ,"row", 1 ) > select rowid from tblRectList where not rlHit = 0 order by rowid asc limit 1 offset freestring = freestring & " " & rowNr - 1 sql ( freestring ) dim rowid = qrSingleValue SELECT count (*) from tblRectList where rlHit = 1 IF ( qrSingleValue <> 1 ) then EXIT function END if > select rlTagGroup from tblRectList where rowid = freestring = freestring & " " & rowid sql ( freestring ) dim OldTagGroup = qrSingleValue > select rlRect from tblRectList where rowid = freestring = freestring & " " & rowid sql ( freestring ) dim Rect = qrSingleValue IF ( ! ( oldName == newName ) ) then UpDateRectMeta ( CurrentHash , Rect, OldName , NewName , TagGroup ) ELSE > UPDATE master SET freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> = freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> || freestring = freestring & " " & sqlString ( NewName & tagDelim ) >> where freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> NOT LIKE '%' || freestring = freestring & " " & sqlString ( tagDelim & NewName & tagDelim) >> || '%' AND >> hash like freestring = freestring & " " & sqlString ( CurrentHash ) sql ( freestring ) OldName = "" END IF ' ---------------------------------------------------------------------- ' it is possible that we have changed the name of a RectPicasa object ' that would create a new entry in RectMeta ' which in turn would need a new entry in tblRectList ' recreate the tblRectList CreateRectInfo () update tblRectList set rlHit = 0 > update tblRectList set rlHit = 1 where rlsrc like 'RectMeta' and rlrect like freestring = freestring & " " & Sqlstring ( rect ) sql ( freestring ) deReferenceTagFromTagColumn ( currentHash , OldTagGroup , OldName ) DisplayTagsInFIle () sob ( TagRectCombo , "empty" ) sob ( TagRectCombo , "add" , "row" , NewName ) sob ( TagRectCombo , "set" , "selection", "row" ,1 ) fncShowRegions () fnOfferRectChoice () toggleOKColour ( sobid ) END function FUNCTION cbActionSelectRect ( sobid , rowid ) dim actionTxt = sob( sobid, "GET" , "TITLE" ) dim useCol = bgTypeCol IF ( actionTxt == noTag ) then useCol = defBtnCol ELSE GetFilteredChoice (sobid) END IF sob ( sob(sobid ,"SIBLING", 3 ) , "SET" ,"RGB", useCol ) sob ( sob(sobid ,"SIBLING", 3 ) , "SET" ,"TITLE", actionTxt ) END FUNCTION FUNCTION makeRenameRect ( sobidParent) dim thisRowSob = sob ( sobidParent , "ADD", "CONTAINER", "ROW" ) sob ( -1 , "ADD" , "COMBO" , "droPDown" , defActionComboText ) sob ( -1 , "add" , "row" , "replace by" ) sob ( -1 , "SET" , "SELECTION" , "ROW", 1 ) sob ( -1 , "ON" , "SELECTION" , "cbBulkActionSelect" ) dim comboSOb = sob ( -1 , "ADD" , "COMBO" , "dropdown" , defTypeComboText ) sob ( -1 , "add" , "row" , NoTag ) sob ( -1 , "SET" , "SELECTION" , "ROW", 1 ) sob ( -1 , "ON" , "SELECTION" , "cbActionSelectRect" ) ' to automatically populate with Tag Group changes'' isTagTypeCombo ( comboSob ) isSavedTagTypeCombo (comboSOb ) sob ( thisRowSob , "Add" , "edit" , " " ) sob ( -1 , "empty" ) sob ( -1 , "ON","EDIT" , "onEditFile" ) sob ( -1 , "ON","EDITED" , "onEditedFile" ) sob ( -1 , "ON","ENTER", "CB_FIND_tag") sob ( -1 , "add","COMBO" , "dropdown" , defaultText ) sob ( -1 , "empty") sob ( -1 , "set" , "stretch" , 1) ' on sob ( thisRowSob , "add" , "Button" , "PUSH", " " ) sob ( -1 , "set" , "title" , NoTag ) sob ( -1 , "ON", "CLICK", "cbChangeRect") END FUNCTION FUNCTION cbNewNameonEditedFile (sobid , editchr ) onEditedFile (sobid , editchr ) sob ( _ sob ( sobid, "GET" , "unk" )_ , "SET" , "TITLE" , _ sob ( sob(sobid,"SIBLING", 1 ) , "GET", "TITLE" ) _ ) END function FUNCTION InsertRectMeta ( hashVal , Rect ) IF ( ! haveRectMeta ) then EXIT function END if > select count (*) from master where RectMeta like '%(' || freestring = freestring & " " & sqlString ( rect ) >> || ')%' and hash = freestring = freestring & " " & sqlString ( hashVal ) >> limit 1 sql ( freestring ) IF ( qrsingleValue > 0 ) then EXIT function END if ' ---------------------------------------------------------- ' create initial entry "faces=" if not present > select RectMeta from master where hash = freestring = freestring & " " & sqlString ( hashVal ) >> limit 1 sql ( freestring ) IF ( qrSingleValue = "" ) then ' > update master set RectMeta = RectMeta || # where hash = ### > update master set RectMeta = RectMeta || freestring = freestring & " " & sqlString ( "faces=rect64(" & Rect & "),fffffffffffffff;" ) >> where hash = freestring = freestring & " " & sqlString ( hashVal ) sql ( freestring ) sql ( freestring ) ELSE ' > update master set RectMeta = RectMeta || # where hash = ### > update master set RectMeta = RectMeta || freestring = freestring & " " & sqlString ( ";rect64(" & Rect & "),fffffffffffffff;" ) >> where hash = freestring = freestring & " " & sqlString ( hashVal ) sql ( freestring ) END IF ' ---------------------------------------------------------- END function FUNCTION GetRectEntry ( Rect , RectDefn ) string dim pre GetRectEntry = "" ' first see if the have RECT string followed by ), dim inPos = instr ( RectDefn, Rect & ")," ) IF ( inPos ) THEN ' create a string that starts at the begining of the RectDefn and STOPS ' where our RECT starts pre = left ( RectDefn , inpos -1 ) ' go back to it' s rect( inPos = instrRev ( pre , "rect64" ) ' have the starting point GetRectEntry = left ( RectDefn, -1 * (inpos -1) ) ' now find where we STOP IF ( inPos := instr ( GetRectEntry , ";;rect64(" ) ) then ELSEIF ( inPos := instr ( GetRectEntry , ";rect64(" ) ) then ELSEIF ( inPos := instrRev ( GetRectEntry , ";" ) ) then ELSE inPos = length(GetRectEntry) END IF GetRectEntry = left ( GetRectEntry , inpos ) END IF END function FUNCTION deReferenceTagFromTagColumn ( hashVal , TagGroup , Tagname ) > select count (*) from tblRectList where rlTagGroup like freestring = freestring & " " & sqlString ( TagGroup ) >> and rlTagName like freestring = freestring & " " & sqlString ( Tagname ) >> and rlSrc like 'RectMeta' sql ( freestring ) IF ( qrSingleValue == 0 ) then ' delete Tagname from master Tag column > update master set freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> = REPLACE ( freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> , freestring = freestring & " " & sqlString ( TagDelim & Tagname & TagDelim ) >> , freestring = freestring & " " & sqlString ( TagDelim ) >> ) where freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> like freestring = freestring & " " & sqlString ( "%" & TagDelim & Tagname & TagDelim & "%" ) >> AND hash like freestring = freestring & " " & sqlString ( hashVal ) sql ( freestring ) END IF END FUNCTION FUNCTION UpDateRectMeta ( hashVal , Rect, OldName , NewName , TagGroup ) ' -------------------------------------------------------------------- ' ensure that there is no RECT defn for this RECT IF ( ! haveRectMeta ) then EXIT function END if > select RectMeta from master where hash = freestring = freestring & " " & sqlString ( hashVal ) >> limit 1 sql ( freestring ) dim oldRectEntry = GetRectEntry ( Rect , qrSingleValue ) ' > update master set RectMeta = replace ( RectMeta , # , '' ) where hash = ### > update master set RectMeta = replace ( RectMeta , freestring = freestring & " " & sqlString ( oldRectEntry ) >> , '' ) where hash = freestring = freestring & " " & sqlString ( hashVal ) sql ( freestring ) ' -------------------------------------------------------------------- ' we want an entry ' note how initially we do NOT have a leading ; ' the first entry in a Picasa RECT defn does not have a leading ; dim wantRect = "rect64(" & rect & ")," & NewName & ";" ' -------------------------------------------------------------------- IF ( haveRectPicasa ) then ' if new entry is in the ## RectPicasa ## then nothing to do > select count (*) from master where RectPicasa like freestring = freestring & " " & sqlString ( "%" & left(wantRect,-1) & "%" ) >> and hash = freestring = freestring & " " & sqlString ( hashVal ) sql ( freestring ) IF ( qrSingleValue ) then EXIT function END IF END IF ' ---------------------------------------------------------- ' create initial entry "faces=" if not present ' > select RectMeta from master where hash = ### limit 1 > select RectMeta from master where hash = freestring = freestring & " " & sqlString ( currentHash ) >> limit 1 sql ( freestring ) IF ( qrSingleValue = "" ) then > update master set RectMeta = "faces=" where hash = freestring = freestring & " " & sqlString ( currentHash ) sql ( freestring ) END IF ' ---------------------------------------------------------- wantRect = "rect64("& rect & ")," &TagGroup & TagDelim & NewName & ";" ' wantRect = "rect64(" & tagGroup & TagGroupId & TagDelim & & rect & ")," & NewName & ";" > update master set RectMeta = RectMeta || freestring = freestring & sqlString ( wantRect ) >> where hash = freestring = freestring & sqlString ( hashVal ) sql ( freestring ) ' ---------------------------------------------------------- ' UPDATE master SET §Tag = §Tag || §Txt || '§' WHERE §Tag NOT LIKE '%' || §Txt || '§%' AND hash = §hash > UPDATE master SET freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> = freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> || freestring = freestring & " " & sqlString ( NewName ) >> || freestring = freestring & " " & sqlString ( TagDelim ) >> where freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> not like '%' || freestring = freestring & " " & sqlString ( NewName ) >> || freestring = freestring & " " & sqlString ( TagDelim & "%" ) >> AND hash like freestring = freestring & " " & sqlString ( hashVal ) sql ( freestring ) ' ---------------------------------------------------------- END function FUNCTION cbNewNameAndChangeRect ( sobid ) IF ( ! isDBopen ) then ErrorReport ("No database selected" ) EXIT function END IF IF ( ! haveRectMeta ) then EXIT function END if dim rowNr = sob ( TagRectCombo , "get", "selection" ,"row", 1 ) IF ( rowNr ) then ELSEIF ( HaveRubberBandRect ) then ELSE ErrorReport ("No region selected" ) EXIT function END IF dim NewName = sob ( sob ( sobid, "GET" , "unk" ) , "GET" , "TITLE" ) NewName = trim ( NewName ) IF ( NewName = "" ) then ErrorReport ("No new name defined" ) EXIT function END if dim TagGroupSob = sob(sobid ,"SIBLING", -3 ) dim TagGroup = sob(TagGroupSob ,"get","title" ) IF ( TagGroup = NoTag ) then ErrorReport ("No Tag group selected" ) EXIT function END if dim TagFilterSob = sob(sobid ,"SIBLING", -2 ) ' --------------------------------------------------------------------- ' only proceed if this TagGroup / NewName combination is unique > select count (*) from TagTable where ttGroup like freestring = freestring & " " & sqlString ( TagGroup & TagGroupId ) >> and ttName like freestring = freestring & " " & sqlString ( NewName ) sql (freestring ) IF ( qrsinglevalue ) then ErrorReport ("Tag group/name already exists" ) ToggleWarnColour ( sobid ) EXIT function END if ' --------------------------------------------------------------------- ' create new entry in ttTagTable > INSERT into TagTable ( ttGroup , ttName , ttComment ) values ( freestring = freestring & " " & sqlString ( TagGroup & TagGroupId ) >> , freestring = freestring & " " & sqlString ( NewName ) >> , '' ) sql = freestring ' --------------------------------------------------------------------- sob ( TagFilterSob , "set" , "TITLE" , NewName ) GetFilteredChoice ( TagGroupSob ) cbChangeRect ( sobid ) END function FUNCTION makeNewNameRect ( sobidParent) dim thisRowSob = sob ( sobidParent , "ADD", "CONTAINER", "ROW" ) sob ( -1 , "ADD" , "COMBO" , "dropdOwn" , defActionComboText ) sob ( -1 , "add" , "row" , "derive" ) sob ( -1 , "SET" , "SELECTION" , "ROW", 1 ) dim combosob = sob ( -1 , "ADD" , "COMBO" , "dropdown" , defTypeComboText ) sob ( -1 , "add" , "row" , NoTag ) sob ( -1 , "SET" , "SELECTION" , "ROW", 1 ) sob ( -1 , "ON" , "SELECTION" , "cbActionSelectRect" ) ' to automatically populate with Tag Group changes'' isTagTypeCombo ( comboSob ) isSavedTagTypeCombo (comboSOb ) dim EditSob = sob ( thisRowSob , "Add" , "edit" , " " ) sob ( -1 , "empty" ) sob ( -1 , "ON","EDIT" , "onEditFile" ) sob ( -1 , "ON","EDITED" , "cbNewNameonEditedFile" ) sob ( -1 , "ON","ENTER", "CB_FIND_tag") sob ( -1 , "add","COMBO" , "dropdown" , defaultText ) sob ( -1 , "empty") sob ( -1 , "set" , "stretch" , 1) ' on dim doItSob = sob ( thisRowSob , "add" , "Button" , "PUSH", " " ) sob ( -1 , "set" , "title" , "Do it" ) sob ( -1 , "ON", "CLICK", "cbNewNameAndChangeRect") dim NewNameEdit = sob ( sobidParent , "ADD", "EDIT", " " ) sob ( -1 , "empty") sob ( EditSob , "SET", "UNK" , NewNameEdit ) sob ( doItSob , "SET", "UNK" , NewNameEdit ) END FUNCTION dim TagCommentAreaCol ' ----------------------------------------------------- ' rubber band things ' ----------------------------------------------------- dim RubberBandEdit FUNCTION cbMakeRubberbandRegion ( sobId ) IF ( ! imageLoadedFlag ) then EXIT function END IF dim sobW = sob ( sobImageHolder , "get" , "width" ) dim sobH = sob ( sobImageHolder , "get" , "height" ) dim CanvasHandle = SOB( sobImageHolder , "GET" , "CANVAS" ) dim imW = canvas ( CanvasHandle , "get" , "width" ) dim imH = canvas ( CanvasHandle , "get" , "height" ) dim leftRect , TopRect , rightRect , BottomRect ' leftRB etc are screen co-ordinates ' and NOT CANVAS co-ordinates sob ( RubberBandEdit , "add" , "row" ,"Make " & leftRB & " " & topRB & " " & rightRB & " " & bottomRB & EOL ) leftRect = cint( 0x010000 * leftRB / imW ) TopRect = cint( 0x010000 * topRB / imH ) rightRect = cint( 0x010000 * ( leftRB + rightRB ) / imW ) BottomRect = cint( 0x010000 * ( topRB + bottomRB ) / imH ) dim Rect rect = right( "0000" & left(hex(leftRect),-2) , 4 ) rect = rect & right( "0000" & left(hex(TopRect),-2) , 4 ) rect = rect & right( "0000" & left(hex(RightRect),-2) , 4 ) rect = rect & right( "0000" & left(hex(BottomRect),-2) , 4 ) sob ( RubberBandEdit , "add" , "row" ,"Rect64 = " & rect & EOL ) InsertRectMeta ( currentHash , Rect ) CreateRectInfo () dim PerCentX = ( (leftRB + rightRB /2) * 100) / imW dim PerCentY = ( (topRB + bottomRB /2) * 100) / imH cbImageHitStatus ( PerCentX , PerCentY ) END FUNCTION FUNCTION fncMakeRubberBandTools () dim RubberBandCol = sob ( RubberBandOvl , "ADD", "CONTAINER", "COLUMN.W") sob ( -1 , "add" , "LABEL", "Define a region of the object, that can be associated with a Tag." ) sob ( -1 , "SET" , "RGB" , 0x0ff00 ) RubberBandEdit = sob(RubberBandCol,"add","Edit.rows" , " ", 20 ) sob ( RubberBandCol , "add" , "SPACE" , 0 , 0) sob ( -1 , "SET" , "RGB" , -1 ) sob ( RubberBandCol, "ADD", "Button" , "PUSH", "Press to create this region") ' sob ( -1 , "ON", "click" , "cbMakeRubberbandRegion") sob ( RubberBandCol , "add" , "SPACE" , 0 , 0) sob ( -1 , "SET" , "RGB" , -1 ) END FUNCTION FUNCTION cbReDrawRegions ( sobid ) fncShowRegions () IF ( sob ( TagRectCombo , "GET", "SELECTION" , "ROW" ) ) then cbSelectOneRect ( TagRectCombo , sob ( TagRectCombo , "GET", "SELECTION" , "ROW" ) ) END IF END function FUNCTION cbDeleteRegion ( sobid ) IF ( ! isDBopen ) then ErrorReport ("No database selected" ) EXIT function END IF IF ( sob(TagRectCombo,"get","TITLE") == "" ) then EXIT function END if dim OldName = sob ( TagRectCombo,"get","TITLE") dim rowNr = sob ( TagRectCombo , "get", "selection" ,"row", 1 ) > select rlRect from tblRectList where not rlHit = 0 order by rowid asc limit 1 offset freestring = freestring & " " & rowNr - 1 sql ( freestring ) dim Rect = qrSingleValue > select rlTagGroup from tblRectList where not rlHit = 0 order by rowid asc limit 1 offset freestring = freestring & " " & rowNr - 1 sql ( freestring ) dim TagGroup = qrSingleValue sob ( TagCommentEdit , "empty" ) dim haveRect = "rect64(" & rect & ")," & TagGroup & TagDelim & OldName & ";" ' remove OLD entry ' > update master set RectMeta = replace ( RectMeta , ## , '' ) where hash = ### IF ( haveRectMeta ) then > update Master set RectMeta = replace(RectMeta, freestring = freestring & " " & sqlString ( ":" & haveRect ) >> , freestring = freestring & " " & sqlString ( "" ) >> ) where hash like freestring = freestring & " " & sqlString ( currentHash ) sql ( freestring ) > update Master set RectMeta = replace(RectMeta, freestring = freestring & " " & sqlString ( haveRect ) >> , freestring = freestring & " " & sqlString ( "" ) >> ) where hash like freestring = freestring & " " & sqlString ( currentHash ) sql ( freestring ) END if haveRect = "rect64(" & rect & ")," & OldName & ";" IF ( haveRectPicasa ) then > update Master set RectPicasa = replace(RectPicasa , freestring = freestring & " " & sqlString ( ":" & haveRect ) >> , freestring = freestring & " " & sqlString ( "" ) >> ) where hash like freestring = freestring & " " & sqlString ( currentHash ) sql ( freestring ) > update Master set RectPicasa = replace(RectPicasa , freestring = freestring & " " & sqlString ( haveRect ) >> , freestring = freestring & " " & sqlString ( "" ) >> ) where hash like freestring = freestring & " " & sqlString ( currentHash ) sql ( freestring ) END If CreateRectInfo () deReferenceTagFromTagColumn ( currentHash , TagGroup , OldName ) fncShowRegions () ' since we have deleted a REGION ' then we can not currently have a RECT to offer ' fnOfferRectChoice () displayTagsInFile () sob ( TagRectCombo,"empty") SetLblRegionRGB ( defBtnCol) END FUNCTION FUNCTION cbObjectCommentEdit ( sobid, char ) ' if the user has done and edit to the Object Comment field ' then set the ACTION to be edit sob ( lblRegionUpdateComment , "SET" , "TITLE" , "Comment edited, press to save" ) sob ( lblRegionUpdateComment , "SET", "RGB", rgb(255,0,0) ) END FUNCTION dim lblRegionSelection , lblRegionUpdateComment , lblRegionChangeName , lblRegionCreateName , lblRegionDelete FUNCTION SetLblRegionRGB ( toRgb ) sob ( lblRegionSelection , "SET" , "RGB" , toRGB ) sob ( lblRegionChangeName , "SET" , "RGB" , toRGB ) sob ( lblRegionCreateName, "SET" , "RGB" , toRGB ) sob ( lblRegionDelete, "SET" , "RGB" , toRGB ) sob ( lblRegionUpdateComment , "SET" , "RGB" , defBtnCol ) END FUNCTION FUNCTION cbSelectFreeTextField ( sobid , rowid) dim i sob ( TagTxtEdit , "EMPTY") IF ( ! FreeTextFlag ) then EXIT function END if FOR i = 0 to uBoundTagTxtArray > select freestring = freestring & " " & sqlIdentifier ( tagTxtArray(i) ) >> from master where hash = freestring = freestring & " " & sqlString ( currenthash ) >> limit 1 sql ( freestring ) IF ( sob ( FreeTextComboSob , "GET", "TITLE" ) == ( right(tagTxtArray(i), -1*length(FreeTxtId)) ) ) then sob (TagTxtEdit,"add", "row" , qrSingleValue& EOL & EOL ) ELSE ( sob ( FreeTextComboSob , "GET", "TITLE" ) == " all" ) sob (TagTxtEdit,"add", "row" , "-- " & right(tagTxtArray(i), -1*length(FreeTxtId)) & " --" & EOL ) sob (TagTxtEdit,"add", "row" , qrSingleValue& EOL & EOL ) END if NEXT i END FUNCTION dim TagTxtEdit dim FreeTextActionSob , FreeTextComboSob , FreeTextDoItSob FUNCTION cbFreeTextDoIt ( sobid ) dim actionTxt = sob ( FreeTextDoItSob , "GET", "TITLE") dim TextField = sob ( FreeTextComboSob , "GET", "TITLE") dim newText dim i IF ( actionTxt == "none" ) then EXIT function END if SELECT CASE actionTxt CASE "update" ' can only be single Free text field newText = sob ( TagTxtEdit , "GET", "TITLE") > update master set freestring = freestring & " " & sqlidentifier ( TextField & FreeTxtId ) >> = freestring = freestring & " " & sqlString ( newText ) >> where hash = freestring = freestring & " " & sqlidentifier ( currentHash ) sql(freestring ) CASE "clear" ' can be all or a single Free text field IF ( TextField == " all" ) then FOR i = 0 to uBoundTagTxtArray > update master set freestring = freestring & " " & sqlidentifier ( TagTxtArray(i) ) >> = '' >> where hash = freestring = freestring & " " & sqlidentifier ( currentHash ) sql(freestring ) NEXT i ELSE > update master set freestring = freestring & " " & sqlidentifier ( TextField & FreeTxtId ) >> = '' >> where hash = freestring = freestring & " " & sqlidentifier ( currentHash ) sql(freestring ) END IF END SELECT cbSingleFile ( currentfile ) END FUNCTION FUNCTION cbDefineFreeTextDoIt ( ) dim actionTxt = sob ( FreeTextActionSob , "GET", "TITLE") dim TextTxt = sob ( FreeTextComboSob , "GET", "TITLE") IF ( TextTxt == noTag ) then Sob ( FreeTextDoItSob , "SET", "TITLE" , noTag ) sob ( FreeTextDoItSob , "SET", "RGB" , defbtnCol ) EXIT function END if SELECT CASE actionTxt CASE "clear" sob ( FreeTextDoItSob , "SET", "TITLE" , actionTxt ) sob ( FreeTextDoItSob , "SET", "RGB" , RGB(0,255,0) ) CASE "Update" IF ( TextTxt == " all" ) then Sob ( FreeTextDoItSob , "SET", "TITLE" , noTag ) sob ( FreeTextDoItSob , "SET", "RGB" , defbtnCol ) ELSE sob ( FreeTextDoItSob , "SET", "TITLE" , actionTxt ) sob ( FreeTextDoItSob , "SET", "RGB" , RGB(0,255,0) ) END if CASE else Sob ( FreeTextDoItSob , "SET", "TITLE" , noTag ) sob ( FreeTextDoItSob , "SET", "RGB" , defbtnCol ) END SELECT END FUNCTION FUNCTION cbSelectFreeTextAction ( sobid , row ) cbDefineFreeTextDoIt () END FUNCTION FUNCTION cbSelectFreeTextItem ( sobid , row ) cbDefineFreeTextDoIt () cbSelectFreeTextField ( 1,1) END FUNCTION FUNCTION MakeFreeTxtActions () dim TagTxtAreaCol = sob ( TagTxtOvl , "ADD", "CONTAINER", "COLUMN.W") sob(TagTxtAreaCol,"add","LABEL" , "Free Text fields" ) sob ( -1 , "SET" , "RGB" , 0x0ff00 ) tmpSob = sob ( TagTxtAreaCol , "ADD", "CONTAINER", "ROW.w") FreeTextActionSob = sob ( tmpSob , "add" , "COMBO" , "DROPDOWN", defTypeComboText ) sob ( -1 , "add" , "ROW" , "Show" ) sob ( -1 , "add" , "ROW" , "Clear" ) sob ( -1 , "add" , "ROW" , "Update" ) sob ( -1 , "SET" , "SELECTION" , "ROW" , 1 ) sob ( -1 , "ON" , "SELECTION" , "cbSelectFreeTextAction" ) FreeTextComboSob = sob ( tmpSob , "add" , "COMBO" , "DROPDOWN", defTypeComboText ) sob ( -1, "set", "stretch", 1 ) FreeTextDoItSob = sob ( tmpSob , "add" , "BUTTON" , "PUSH", " " ) sob ( -1 , "set" , "title" , noTag ) sob ( -1 , "ON" , "press" , "cbFreeTextDoIt") ' -------------------- sob ( TagTxtAreaCol , "add" , "SPACE" , 0 , 20) sob ( -1 , "SET" , "RGB" , -1 ) sob ( FreeTextComboSob , "EMPTY") sob ( FreeTextComboSob , "add" , "row" , NoTag ) sob ( FreeTextComboSob , "ON", "SELECTION" , "cbSelectFreeTextItem") TagTxtEdit = sob(TagTxtAreaCol,"add", "Edit.rows" , " ", 11 ) ' sob ( -1 , "ON" , "edit" , "cbFreeTextDotIt") END Function MakeFreeTxtActions () FUNCTION cbFreeTextTools ( sobid ) sob ( TagTxtOvl , "set", "show", 1 ) END Function FUNCTION MakeTagCommentTools () TagCommentAreaCol = sob ( TagCommentOvl , "ADD", "CONTAINER", "COLUMN.W") lblRegionSelection = sob ( -1 , "add" , "LABEL", "Comment associated with a region" ) sob ( -1 , "SET" , "RGB" , 0x0ff00 ) TagRectCombo= sob(TagCommentAreaCol,"add","COMBO" , "DROPDOWN", " " ) sob ( TagRectCombo , "EMPTY") sob ( TagRectCombo , "ON", "SELECTION" , "cbSelectOneRect") TagCommentEdit = sob(TagCommentAreaCol,"add", "Edit.rows" , " ", 11 ) sob ( -1 , "ON" , "edit" , "cbObjectCommentEdit") lblRegionUpdateComment = sob(TagCommentAreaCol,"add","BUTTON" , "PUSH", " " ) sob ( -1 , "ON" , "PRESS" , "CbUPdateTagCommentField") sob ( TagCommentAreaCol , "add" , "SPACE" , 0 , 20) sob ( -1 , "SET" , "RGB" , -1 ) lblRegionChangeName = sob(TagCommentAreaCol,"add","LABEL" , "To change the name of this region:" ) sob ( -1 , "SET" , "RGB" , 0x0ff00 ) sob ( TagCommentAreaCol , "add" , "SPACE" , 0 , 10) sob ( -1 , "SET" , "RGB" , -1 ) makeRenameRect ( TagCommentAreaCol ) makeRenameRect ( TagCommentAreaCol ) makeRenameRect ( TagCommentAreaCol ) makeRenameRect ( TagCommentAreaCol ) makeRenameRect ( TagCommentAreaCol ) makeRenameRect ( TagCommentAreaCol ) makeRenameRect ( TagCommentAreaCol ) makeRenameRect ( TagCommentAreaCol ) makeRenameRect ( TagCommentAreaCol ) makeRenameRect ( TagCommentAreaCol ) sob ( TagCommentAreaCol , "add" , "SPACE" , 0 , 20) sob ( -1 , "SET" , "RGB" , -1 ) lblRegionCreateName = sob(TagCommentAreaCol,"add","LABEL" , "Create new name and apply to this region:" ) sob ( -1 , "SET" , "RGB" , 0x0ff00 ) sob ( TagCommentAreaCol , "add" , "SPACE" , 0 , 10) sob ( -1 , "SET" , "RGB" , -1 ) makeNewNameRect ( TagCommentAreaCol ) sob ( TagCommentAreaCol , "add" , "SPACE" , 0 , 10) sob ( -1 , "SET" , "RGB" , -1 ) ' ----------------------------------------------------- tmpSob = sob ( TagCommentAreaCol , "ADD", "CONTAINER", "ROW.HW" ) SOB ( "OVERRIDE" , "STYLE+" , 0x00002000) >zzDeleteZZ >>Regionzz freestring= replace ( freestring , "ZZ" , chr(0x0d) ) lblRegionDelete = sob ( tmpSob , "add" , "BUTTON" , "PUSH" ,freestring ) sob ( -1 , "ON", "press", "cbDeleteRegion" ) SOB ( "OVERRIDE" , "STYLE+" , 0x00002000) >ZZFree TextZZ >>ToolsZZ freestring= replace ( freestring , "ZZ" , EOL ) sob ( tmpSob , "add" , "BUTTON" , "PUSH" ,freestring ) sob ( -1 , "ON", "press", "cbFreeTextTools" ) sob ( -1 , "SET" , "RGB" , rgb(0,255,0) ) SOB ( "OVERRIDE" , "STYLE+" , 0x00002000) >ZZTaggingZZ >>ToolsZZ freestring= replace ( freestring , "ZZ" , EOL ) sob ( tmpSob , "add" , "BUTTON" , "PUSH" ,freestring ) sob ( -1 , "ON", "press", "cbtoolsMenuTagFilesB" ) sob ( -1 , "SET" , "RGB" , rgb(0,255,0) ) ' ----------------------------------------------------- SetLblRegionRGB ( defBtnCol ) END FUNCTION MakeTagCommentTools() fncMakeRubberBandTools () FUNCTION cbBulkExportDoAllDrop ( sobid ) IF ( sob(sobid,"GET" , "TITLE" ) == NoTag ) then EXIT function END IF IF ( ! isDBopen ) then ErrorReport ("No database selected" ) EXIT function END IF dim i , hasHash , inDir FOR i = 1 to GetDropCount IF ( isFile ( getDropData ( i ) ) == 1 ) then sob( PathFile , "ADD" , "ROW" , "Drag'n'Drop file " & getDropData ( i ) & EOL ) hashash = getFileHash ( getDropData ( i ) ) cbBulkActionOnHash ( hasHash ) ELSEIF ( isFile ( getDropData ( i ) ) == -1 ) then inDir= getDropData ( i ) & "\\" cancelClear () cancelText( "Identifing target files" , 0 ) cancelText( "This can take a short while" , 3 ) cancelShow(1) dbFileList ( "temp", "bulk" , inDir, -1 , ,"MD5" ) SELECT count (*) from temp.bulk where type like 'file' fileCount = qrSingleValue cancelText( "Tagging in progress" , 0 ) cancelText( "This can take a short while" , 3 ) cancelText( "Files to process: " & FileCount , 2 ) WITHQUERY ( "select hash , ( path|| name ) from temp.bulk where type like 'file' ") sob( PathFile , "ADD" , "ROW" , wqText(2) & EOL ) cbBulkActionOnHash ( wqtext(1) ) cancelToggle () fileCount-- IF ( fileCount BAND 0x0f ) then ELSE cancelText( "Files to process: " & FileCount , 2 ) END IF END WithQuery cancelShow(0) ELSE END IF NEXT i END function FUNCTION cbBulkExportDoOnePress ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if dim actionType , TagType , TagTxt , fileCount dim BaseSob = sob(sobid ,"SIBLING", -4) actionType = sob( BaseSob , "GET" , "TITLE" ) TagType = sob( sob(BaseSob ,"SIBLING", 1) , "GET" , "TITLE" ) TagTxt = sob( sob(BaseSob ,"SIBLING", 3) , "GET" , "TITLE" ) SELECT count(*) from temp.SubList fileCount = qrSingleValue cancelClear () cancelText( "Tagging the filtered Files" , 0 ) cancelText( "This can take a short while" , 3 ) cancelText( "Files to process: " & FileCount , 2 ) cancelShow(1) WITHQUERY ( "select hash from temp.SubList where type like 'file' " ) updateSingleFile ( wqText(1) , actionType, tagType , tagTxt ) cancelToggle () fileCount-- IF ( fileCount BAND 0x0f ) then ELSE cancelText( "Files to process: " & FileCount , 2 ) END IF END withQuery cancelShow(0) toggleWarnColour ( sobid ) END function FUNCTION makeBulkExportTaggerRows ( sobidParent ) dim thisRowSob = sob ( sobidParent , "ADD", "CONTAINER", "ROW" ) sob ( -1 , "ADD" , "COMBO" , "dropDOwn" , defActionComboText ) sob ( -1 , "add" , "row" , NoTag ) sob ( -1 , "add" , "row" , "add" ) ' sob ( -1 , "add" , "row" , "replace" ) sob ( -1 , "add" , "row" , "remove" ) sob ( -1 , "add" , "row" , "remove all" ) sob ( -1 , "SET" , "SELECTION" , "ROW", 1 ) sob ( -1 , "ON" , "SELECTION" , "cbBulkActionSelect" ) dim comboSob = sob ( -1 , "ADD" , "COMBO" , "dropdown" , defTypeComboText ) sob ( -1 , "add" , "row" , NoTag ) sob ( -1 , "SET" , "SELECTION" , "ROW", 1 ) sob ( -1 , "ON" , "SELECTION" , "cbFilterTagSelect" ) ' to automatically populate with Tag Group changes isTagTypeCombo ( ComboSob ) isSavedTagTypeCombo ( ComboSob ) sob ( thisRowSob , "Add" , "edit" , " " ) sob ( -1 , "empty" ) sob ( -1 , "ON","EDIT" , "onEditFile " ) sob ( -1 , "ON","EDITED" , "onEditedFile" ) sob ( -1 , "ON","ENTER", "CB_FIND_tag") sob ( -1 , "add","COMBO" , "dropdown" , " " ) sob ( -1 , "empty") sob ( -1 , "SET" , "STRETCH" , 1) ' on sob ( thisRowSob , "add" , "Button" , "PUSH", " " ) sob ( -1 , "set" , "title" , noTag ) sob ( -1 , "ON", "CLICK", "cbBulkExportDoOnePress") END FUNCTION dim PowerTaggingSob FUNCTION makeBulkExportTagger ( ) dim BulkArea = sob ( FileExportOvl , "ADD", "CONTAINER", "COLUMN.W") PowerTaggingSob = BulkArea sob ( BulkArea , "ADD" , "CONTAINER" , "COLUMN") ' sob ( -1 , "add" , "SPACE" , 0 , 20) ' sob ( -1 , "SET" , "RGB" , -1 ) sob ( -1 , "add" , "LABEL", "TAG the files in the sublist." ) sob ( -1 , "SET" , "RGB" , 0x0ff00 ) ' ------------------------------------- ' now define the replicated ROWS ' if needed give a common text heading for the columns sob ( BulkArea , "ADD", "CONTAINER", "ROW") sob ( -1 , "add", "LABEL" , "Action Tag Group Filter Tag Name " ) sob ( -1 , "add" , "SPACE", 0,0 ) bulkExportMatrix = sob ( BulkArea , "ADD", "CONTAINER", "COLUMN.W") dim i FOR i = 0 to NbrBulkExportRows makeBulkExportTaggerRows (bulkExportMatrix) NEXT i sob ( FileExportOvl , "add" , "SPACE", 0,0 ) sob ( -1 , "SET" , "RGB" , -1 ) END function makeBulkExportTagger () ' ------------------------------------------------------- FUNCTION onPushOfferFileDelete ( sobid ) dim filename = sob ( FileListSob , "get", "title") if ( filename == "" ) then exit function END IF dim fileToDelete = left ( filename, -10 ) > delete from master where path||name Like freestring = freestring & " " & sqlString ( fileToDelete ) sql ( freestring ) deleteFile ( fileToDelete ) dim index = sob ( FileListSob , "get", "selection", "row" ) sob ( FileListSob , "delete", "row", index ) sob ( FileListSob , "set", "selection", "row" , 1 ) END FUNCTION dim FileNameNotinDB ' -------------------------------------------------- FUNCTION getProperty ( oFolder, oFile, index ) string dim i , tmpstr , name getProperty = "" dim res = oFolder.GetDetailsOf ( oFile, index) IF ( res <> "" ) then name = oFolder.GetDetailsOf ( NULL,index) SELECT case length(name) CASE 0 to 6 name = name & chr(0x09) & chr(0x09)& chr(0x09)& chr(0x09) CASE 6 to 13 name = name & chr(0x09) & chr(0x09) & chr(0x09) CASE 12 to 20 name = name & chr(0x09) & chr(0x09) CASE else name = name & chr(0x09) END select getProperty = name FOR i = 0 to length (res) -1 tmpstr = mid(res, i ,1 ) IF ( asc ( tmpstr ) < 0x080 ) then getProperty = getProperty & tmpstr END IF NEXT i END if END function FUNCTION handleFileGetDetailsOf ( AppDisplay , filePathName ) dim fs, folder dim FilePath = Left ( filePathName , inStrRev ( filePathName , "\\" ) - 1) dim FileName = Left ( filePathName , -1 * inStrRev ( filePathName , "\\" ) ) dim objShell = CreateObject ("Shell.Application") dim objFolder = objShell.Namespace(FilePath) dim objFile = objFolder.parsename (FileName ) sob ( AppDisplay,"ADD", "ROW", EOL & EOL & "Tags from SHELL" & EOL & EOL ) dim i , txt FOR i = 0 to 330 SELECT case i CASE 0 to 6 CASE 10 , 57 , 61 CASE 164 , 169 CASE 190 to 196 CASE else txt = getProperty ( objFolder, objFile, i ) IF ( txt <> "" ) then SELECT case length(txt) CASE 0 to 7 txt = txt & chr(0x09) & chr(0x09)& chr(0x09) CASE 8 to 15 txt = txt & chr(0x09) & chr(0x09) CASE else txt = txt & chr(0x09) END select ' sob ( AppDisplay,"ADD", "ROW", right ( "0000" & i , 3) & " " & objFolder.GetDetailsOf ( NULL , i ) & " " & objFolder.GetDetailsOf ( objFile,i) & EOL ) sob ( AppDisplay,"ADD", "ROW", txt & EOL ) END if END select NEXT i SOB ( AppDisplay , "SET" , "ROW" , "TOP" , 1 ) END function ' -------------------------------------------------- ' EXIF data contains an orientation value from 1 to 8: ' 1 = 0 degrees: the correct orientation, no adjustment is required. ' 2 = 0 degrees, mirrored: image has been flipped back-to-front. ' 3 = 180 degrees: image is upside down. ' 4 = 180 degrees, mirrored: image has been flipped back-to-front and is upside down. ' 5 = 90 degrees: image has been flipped back-to-front and is on its side. ' 6 = 90 degrees, mirrored: image is on its side. ' 7 = 270 degrees: image has been flipped back-to-front and is on its far side. ' 8 = 270 degrees, mirrored: image is on its far side. FUNCTION handleFileWIA ( AppDisplay , filePathName ) dim fs, folder dim FilePath = Left ( filePathName , inStrRev ( filePathName , "\\" ) - 1) dim FileName = Left ( filePathName , -1 * inStrRev ( filePathName , "\\" ) ) dim objIF = CreateObject("WIA.ImageFile") dim objV = CreateObject("WIA.Vector") TRY objIF.LoadFile ( filePathName ) CATCH sob ( AppDisplay,"ADD", "ROW", "File error, or file not supported by WIA.IMAGEFILE" & EOL & EOL ) handleFileGetDetailsOf ( AppDisplay , filePathName ) EXIT function END try sob ( AppDisplay,"ADD", "ROW", EOL & EOL & "Tags from WIA" & EOL & EOL ) dim txt , limit , name dim prop , i = 0 ,j , oV FOR each prop in objIF.properties ' sob ( AppDisplay,"ADD", "ROW", objIF.properties.item(i).PropertyID & EOL) WITH prop IF ( ( .PropertyID <> 20507) And ( .PropertyID <> 20624) And ( .PropertyID <> 20625)) Then name = .name SELECT case length(name) CASE 0 to 6 name = name & chr(0x09) & chr(0x09)& chr(0x09)& chr(0x09) CASE 6 to 13 name = name & chr(0x09) & chr(0x09) & chr(0x09) CASE 12 to 20 name = name & chr(0x09) & chr(0x09) CASE else name = name & chr(0x09) END select sob ( AppDisplay,"ADD", "ROW", name ) i++ IF .IsVector = False Then IF .type = 1006 Then TRY sob ( AppDisplay,"ADD", "ROW", .Value & " " & .Value.Numerator & "/" & .Value.Denominator & " " & chr(0x0a) ) CATCH sob ( AppDisplay,"ADD", "ROW", "1066 " & .Value & chr(0x0a) ) END TRY ELSE TRY sob ( AppDisplay,"ADD", "ROW", .Value & " " & EOL ) CATCH sob ( AppDisplay,"ADD", "ROW", " .Value ?? " & EOL ) END try END If ELSE ' isVector oV = .Value txt = "" limit = oV.Count IF ( limit > 64 ) then limit = 64 FOR j = 1 To limit IF ( j == 1 ) then txt = txt & istype(ov.item(j) ) & " " END if TRY txt = txt & hex( oV.Item(j) ) & " " CATCH txt = txt & " ? " END try NEXT j IF ( oV.Count > 64 ) then txt = txt & "..." txt = Trim(txt) sob ( AppDisplay,"ADD", "ROW", txt & EOL ) END If END iF END with NEXT prop IF ( ! i ) then sob ( AppDisplay,"ADD", "ROW", "no WIA tags found" & EOL ) handleFileGetDetailsOf ( AppDisplay , filePathName ) END if SOB ( AppDisplay , "SET" , "ROW" , "TOP" , 1 ) END function FUNCTION removeDeadFiles ( ) > select path||name , self from temp.masterfilE WITHQUERY ( freestring ) IF ( isFile ( wqtext(1)) ) then ELSE > delete from temp.masterfilE where self = freestring = freestring & " " & wqInt(2) sql(freestring) END if END withquery delete from temp.masterfilE where not self = ( select self from temp.masterfilE limit 1 ) END function FUNCTION cbSingleFile ( targetFile ) ' ---------------------------------------------- Currenthash = "" CurrentFile = "" CurrentRowid = 0 sob (PathFile, "empty") sob (TagInfo, "empty") sob (StatusTxt, "empty") ReleaseImage (sobImageHolder) removeThumbNails ( siImageMngmtMatrix ) removeAllThumbNails () delete from tblRectList sob (TagRectCombo,"empty") NoErrorReport ( "" ) FileNameNotinDB = "" IF ( targetFile == "" ) then EXIT function END IF makebkTree () ' ---------------------------------------------- drop table if exists temp.inputFile dbFileList ( "temp", "inputFile" , targetFile, , ,"MD5" ) SELECT count (*) from temp.inputFile IF ( qrsingleValue == 1 ) then ELSE ErrorReport ( "File not found in file system" ) EXIT function END if ' ---------------------------------------------- ' display the selected file as an IMAGE (if possible) AssignImage ( sobImageHolder , targetFile ) ' --------------------------------------------------- ' check for a valid EXTENSION type dim i , j = 0 , haveExt IF ( extArray(0) == "*" ) THEN ELSE SELECT ext from temp.inputFile haveExt = qrsingleValue FOR i = 0 to uBound ( extArray ) IF ( haveExt == extArray(i) ) then j = 1 EXIT for END IF NEXT i IF ( ! j ) then ErrorReport ( "Invalid extension" ) EXIT function END if END if ' --------------------------------------------------- sob ( FileListSob , "empty") ' --------------------------------------------------- ' work out whether (and how) we know this file ' ------------------------------------ ' 1) filesystem based cryptographic hash e.g. MD5 dim FileStatus = 0 IF (! FileStatus ) then ' see if FILE is in DB ' careful use hash here (old dbFileList no fsHash) update temp.inputFile set key = Hash || path || name ' careful use fshash here update master set key = fsHash || path || name drop table if exists temp.masterfilE create temp table masterfilE as select *, rowid as self from master where key in (select key from inputFile) removeDeadFiles ( ) SELECT count (*) from temp.masterfilE IF ( qrsingleValue == 1) then FileStatus = 1 END IF END IF ' ------------------------------------ ' 2) simply by it's location i.e. path/name IF (! FileStatus ) then ' see if FILE is ' in DB by full name but not fsHash update temp.inputFile set key = path || name ' select key from inputFile limit 1 ' report ( qrSingleValue ) drop table if exists temp.MASTERfile create temp table MASTERfile as select *, rowid as self from master where (path || name) in (select key from inputFile) removeDeadFiles ( ) SELECT count (*) from temp.MASTERfile IF ( qrsingleValue == 1) then sob (StatusTxt, "SET" ,"TITLE" , "WARNING: file in database, but it's fsHASH has changed!" & EOL ) sob (StatusTxt,"SET", "RGB" , WarningCol ) FileStatus = 1 END IF END IF MakeBkTree() ' ------------------------------------ ' 3) by it's pHash/dHash (only relevant for Image files) IF (! FileStatus ) then dim pHash = 0 , dHash = 0 phash = canvas("new", "phash.from.file", targetFile , byref dHash ) ' first see if it has a pHash IF ( pHash ) then ' has a pHash/dHash so look for this pair in the database drop table if exists temp.MasTerFile > create temp table MasTerFile as select *, rowid as self from master where pHash = freestring = freestring & " " & pHash >> and dHash = freestring = freestring & " " & dHash sql(freestring) removeDeadFiles ( ) SELECT count (*) from temp.MasTerFile IF ( qrsingleValue == 1) then sob ( FileListSob , "ADD", "ROW", "External: " & targetFile ) sob (StatusTxt,"SET" ,"TITLE" , "WARNING: file NOT in database, but image hashes are known!" & EOL ) sob (StatusTxt,"SET", "RGB" , WarningCol ) FileStatus = 1 END IF END if END IF ' --------------------------------------------------- CurrentFile = targetFile IF (! FileStatus ) then sob ( FileListSob , "ADD", "ROW", "External: " & targetFile ) sob ( FileListSob , "set", "selection" , "ROW", 1 ) ErrorReport ( "File unknown in database" ) EXIT function END IF > select self, hash , path||name from temp.MasTerFile limit 1 WITHQUERY ( freestring ) CurrentRowid = wqInt (1) Currenthash = wqtext ( 2 ) CurrentFile = wqtext ( 3 ) END withquery ' --------------------------------------------------- update master set key = Hash || path || name ' ---------------------------------------------- CreateRectInfo () sob ( reDrawRegionsID , "trigger") ' ---------------------------------------------- ' show names of Files in database with same HashValue > select ( path || name ) as FullName from master where Hash = freestring = freestring & " " & sqlstring ( CurrentHash ) WITHQUERY ( freestring ) sob ( FileListSob , "ADD", "ROW", "Internal: " & wqText(1) ) END withQuery sob ( FileListSob , "set", "selection" , "ROW", 1 ) ' ---------------------------------------------- ' now display the tag info for this fsHASH sob ( TagInfo , "SET", "SHOW", 0) DisplayTagsInFile () IF ( haveWIA ) then IF ( sob ( SingleFileMenuWIA , "GET" , "CHECK" ) ) then handleFileWIA ( TagInfo , CurrentFile ) END IF IF ( haveShell ) then IF ( sob ( SingleFileMenuGetDetailsOf , "GET" , "CHECK" ) ) then handleFileGetDetailsOf ( TagInfo ,CurrentFile ) END IF sob ( TagInfo , "SET", "SHOW", 1) END Function FUNCTION CB_drop_on_Image_area ( sobid ) emptyThings () dim CanvasHandle IF ( ! isDBopen ) then ErrorReport ( "No Database selected" ) EXIT function END IF IF GetDropCount == 1 THEN IF ( isFile ( getDropData(1) ) == 1 ) then cbSingleFile ( getDropData(1) ) END IF ELSE ReleaseImage (sobImageHolder) EXIT function END if END function sob ( sobImageHolder ,"ON","DROP.FILE", "CB_drop_on_Image_area") FUNCTION on@drb ( sobid , row, col ) IF ( sob( sobid,"GET", "CELL", 0 ,1 ) == "path" ) then freestring = sob( sobid,"GET", "CELL", row ,1 ) & sob( sobid,"GET", "CELL", row ,2 ) ' cbSingleFile (freestring ) ShellExecute ( sob( sobid,"GET", "CELL", row ,1 )) END if inherit on@drb ( sobid, row, col) END function FUNCTION updateHistory ( cmd ) create table if not exists history ( cmd ) sql ( replace ( "insert into History values ( $cmd )" , "$cmd" , sqlstring (cmd) ) ) sql ( cmd ) END function FUNCTION replacedelim ( colName , oldDelim , newDelim) > update master set freestring = freestring & " " & sqlIdentifier ( colName ) >> = replace ( freestring = freestring & " " & sqlIdentifier ( colName ) >> , freestring = freestring & " " & sqlstring ( oldDelim ) >> , freestring = freestring & " " & sqlstring ( newDelim ) >> ) sql(freestring ) END Function FUNCTION rePlaceDelim () dim oldDelim = "§" dim newDelim = chr ( 0x01e ) & "§" ' RS = Record Separator IF ( haveRectPicasa ) then replacedelim ( "RectPicasa" , oldDelim , newDelim) END if IF ( haveRectMeta ) then replacedelim ( "RectMeta" , oldDelim , newDelim) END if replacedelim ( "whoTag" , oldDelim , newDelim) replacedelim ( "whatTag" , oldDelim , newDelim) replacedelim ( "whereTag" , oldDelim , newDelim) replacedelim ( "whenTag" , oldDelim , newDelim) replacedelim ( "keyWordtag" , oldDelim , newDelim) replacedelim ( "for DeletionTag" , oldDelim , newDelim) END Function FUNCTION assertTagGroupSpelling ( TagGroup ) string assertTagGroupSpelling = "" > select ttGroup from tagTable where ttGroup like $TagGroup limit 1 freestring = replace ( freestring , "$TagGroup" , sqlIdentifier ( TagGroup & TagGroupId ) ) sql ( freestring ) IF ( istype (qrSingleValue) == "EMPTY" ) then ErrorReport ("TAG group" & TagGroup & " not found TagTable " ) EXIT function END IF assertTagGroupSpelling = right ( qrSingleValue , -3 ) END FUNCTION FUNCTION assertTagSpelling ( TagGroup , name ) string assertTagSpelling = "" > select ttName from tagTable where ttGroup like $tagType and ttName like $name freestring = replace ( freestring , "$tagType" , sqlIdentifier ( TagGroup & TagGroupId ) ) freestring = replace ( freestring , "$name" , sqlstring ( name ) ) sql ( freestring ) IF ( istype (qrSingleValue) == "EMPTY" ) then ErrorReport ("TAG " & name & " not found in TagGroup " & TagGroup ) EXIT function END IF assertTagSpelling = qrSingleValue END FUNCTION FUNCTION rePlaceTag ( TagGroup , old, new ) ' ---------------------------------------------------------- ' BEWARNED ' DB replace commands are CASE SENSISTIVE TagGroup = assertTagGroupSpelling ( TagGroup) ' this forces the TagGroup to have the correct case old = assertTagSpelling ( TagGroup , old ) ' then force the OLD tag name to have correct case new = assertTagSpelling ( TagGroup , new ) ' then force the NEW tag name to have correct case ' ---------------------------------------------------------- ' update the appropriate TAG GROUP column > update master set freestring = freestring & " " & sqlIdentifier ( TagGroup & TagGroupId ) >> = replace ( freestring = freestring & " " & sqlIdentifier ( TagGroup & TagGroupId ) >> , freestring = freestring & " " & sqlString ( old ) >> , freestring = freestring & " " & sqlString ( new ) >> ) where freestring = freestring & " " & sqlIdentifier ( TagGroup & TagGroupId ) >> like freestring = freestring & " " & sqlString ( "%" & TagDelim ) >> || freestring = freestring & " " & sqlString ( old ) >> || freestring = freestring & " " & sqlString ( TagDelim & "%" ) sql ( freestring ) ' ---------------------------------------------------------- ' just in case the tag has been assigned to a RECT ' we must update the RectMeta column IF ( haveRectMeta ) then > update Master set RectMeta = replace(RectMeta, freestring = freestring & " " & sqlString ( TagDelim & oldName & ";" ) >> , freestring = freestring & " " & sqlString ( TagDelim & newName & ";" ) >> ) where RectMeta like freestring = freestring & " " & sqlString ( %" & TagDelim & oldName & ";%" ) sql ( freestring ) END if ' ---------------------------------------------------------- CreateUsersTagTableAndColumnsList () resetTagBoxes () ' ---------------------------------------------------------- IF ( imageLoadedFlag ) then DisplayTagsInFIle () fncShowRegions () fnOfferRectChoice () END IF END Function FUNCTION deleteTag ( TagGroup , old ) ' ---------------------------------------------------------- ' BEWARNED ' DB replace commands are CASE SENSISTIVE TagGroup = assertTagGroupSpelling ( TagGroup) old = assertTagSpelling ( TagGroup , old ) ' new = assertTagSpelling ( TagGroup , new ) ' ---------------------------------------------------------- > DELETE from tagTable where ttGroup like freestring = freestring & " " & sqlString ( TagGroup & TagGroupId ) >> and ttName like freestring = freestring & " " & sqlString ( old ) sql ( freestring ) > update master set freestring = freestring & " " & sqlIdentifier ( TagGroup & TagGroupId ) >> = REPLACE ( freestring = freestring & " " & sqlIdentifier ( TagGroup & TagGroupId ) >> , freestring = freestring & " " & sqlString ( TagDelim & OLD & TagDelim ) >> , freestring = freestring & " " & sqlString ( TagDelim ) >> ) sql ( freestring ) deReferenceTagInRegions ( TagGroup , old ) CreateUsersTagTableAndColumnsList () resetTagBoxes () IF ( imageLoadedFlag ) then DisplayTagsInFIle () fncShowRegions () fnOfferRectChoice () END IF END Function FUNCTION renameTag ( tagGroup , oldName , newName ) ' ---------------------------------------------------------- ' BEWARNED ' DB --replace-- commands are CASE SENSITIVE TagGroup = assertTagGroupSpelling ( TagGroup) oldName = assertTagSpelling ( TagGroup , oldName ) ' ---------------------------------------------------------- ' check that NewName is not already used > select count (*) from TagTable where ttgroup like freestring = freestring & " " & sqlIdentifier ( TagGroup & TagGroupId ) >> and ttname like freestring = freestring & " " & sqlstring ( newName ) sql ( freestring ) IF ( qrSingleValue ) then ErrorReport ("TAG " & newName & " already in TagGroup " & TagGroup ) EXIT function END IF > update TagTable set ttname = freestring = freestring & " " & sqlString (newName ) >> where ttname like freestring = freestring & " " & sqlString (oldName ) >> and ttGroup like freestring = freestring & " " & sqlString (TagGroup & TagGroupId ) sql ( freestring ) ReReferenceTagInRegions ( TagGroup , oldName , newName ) > update Master set freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> = replace( freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> , freestring = freestring & " " & sqlString ( TagDelim & oldName & TagDelim ) >> , freestring = freestring & " " & sqlString ( TagDelim & newName & TagDelim ) >> ) where freestring = freestring & " " & sqlIdentifier ( tagGroup & TagGroupId ) >> like freestring = freestring & " " & sqlString ( "%" & TagDelim & oldName & TagDelim& "%" ) sql ( freestring ) CreateUsersTagTableAndColumnsList () resetTagBoxes () IF ( imageLoadedFlag ) then DisplayTagsInFIle () fncShowRegions () fnOfferRectChoice () END IF END FUNCTION FUNCTION makeNewTagType ( TagGroup ) TRY > alter table master add column freestring = freestring & " " & sqlString ( TagGroup & TagGroupId ) sql ( freestring ) > update master set freestring = freestring & " " & sqlIdentifier ( TagGroup & TagGroupId ) >> = freestring = freestring & " " & sqlString ( TagDelim ) sql ( freestring ) CATCH ErrorReport ("Could not create column " & TagGroup & " in table MASTER" ) EXIT function END try > INSERT into tagTable values ( freestring = freestring & " " & sqlString ( TagGroup & TagGroupId ) >> , freestring = freestring & " " & sqlString ( TagDelim ) >> , '' ) sql ( freestring ) END Function ' ------------------------------------------------------------------ FUNCTION onTagNameDoItClicked ( sobid ) IF ( ! isDBopen ) then ErrorReport ("No database selected" ) EXIT function END IF dim actionTxt = sob ( TagNameActionComboSOB, "GET", "TITLE") dim tagGroup = sob ( TagGroupComboSob, "GET", "TITLE" ) dim tagName = sob ( TagNameComboSob, "GET", "TITLE" ) dim newName = sob ( TagNameNewNameSOB, "GET", "TITLE" ) IF ( TagGroup == "Tagged" ) then ErrorReport ("Actions on this tag group are not allowed" ) EXIT function END if IF ( actionTxt == NoTag ) then sob ( TagNameComboSob, "Empty") EXIT function END IF IF ( tagGroup == NoTag ) then ErrorReport ( "No Tag Group Selected" ) sob ( TagNameComboSob, "Empty") EXIT function END IF IF ( actionTxt == "edit" ) then sob ( TagNameNewNameSOB , "empty") sob ( TagNameNewNameSOB, "add" , "row" , selTxt ) sob ( TagNameNewNameSOB, "SET", "TITLE" , selTxt ) sob ( TagNameNewNameSOB, "SET", "RGB" , 0xff ) ELSEIF ( actionTxt == "new" ) then > select count(*) from tagTable where ttGroup like $tag and ttName like $nam freestring = replace ( freestring , "$tag" , sqlstring ( tagGroup & TagGroupId) ) freestring = replace ( freestring , "$nam" , sqlstring ( newName) ) sql ( freestring ) IF ( qrSingleValue > 0 ) then ErrorReport ("Tag " & newName & " already exists in tag group " & TagGroup ) ELSE > insert into tagTable values ( $tag , $nam , '' ) freestring = replace ( freestring , "$tag" , sqlstring ( tagGroup & TagGroupId) ) freestring = replace ( freestring , "$nam" , sqlstring ( newName) ) sql (freestring ) sob ( TagNameNewNameSOB, "empty") END IF ELSEIF ( actionTxt == "rename" ) then renameTag ( tagGroup , tagName , newName ) ELSEIF ( actionTxt == "delete" ) then deleteTag ( tagGroup , tagName ) ELSEIF ( actionTxt == "move" ) then ' newName must be the name of an existing TAGGroup ' and not = TagGroup newName = assertTagGroupSpelling ( newName ) IF ( newName == "" ) then ErrorReport ("Destination tag group -" & sob ( TagNameNewNameSOB, "GET", "TITLE" ) & "- does not exist") EXIT function END IF IF ( TagGroup == newName ) then ErrorReport ("Destination tag group same as source tag group") EXIT function END IF ' ----------------------------- IF ( haveRectMeta ) then > update master set RectMeta = replace ( RectMeta , freestring = freestring & " " & sqlString ( "," & TagGroup & TagDelim & TagName) >> , freestring = freestring & " " & sqlString ( "," & NewName & TagDelim & TagName) >> ) sql(freestring) END if ' ----------------------------- > update master >> SET >> dstGroup = dstGroup || TagName || TagDelim >> WHERE >> srcGroup like '%' || TagDelim || TagName || TagDelim ||'%' >> and NOT dstGroup like '%' || TagDelim || TagName || TagDelim ||'%' freestring = replace ( freestring , "TagDelim" , sqlString(TagDelim) ) freestring = replace ( freestring , "srcGroup" , sqlIdentifier (TagGroup & TagGroupId ) ) freestring = replace ( freestring , "dstGroup" , sqlIdentifier (newName & TagGroupId ) ) freestring = replace ( freestring , "TagName" , sqlIdentifier (TagName ) ) sql(freestring) > update master SET srcGroup = replace ( srcGroup , TagDelim , TagDelim || TagName || TagDelim ) freestring = replace ( freestring , "TagDelim" , sqlString(TagDelim) ) freestring = replace ( freestring , "srcGroup" , sqlIdentifier (TagGroup & TagGroupId) ) freestring = replace ( freestring , "TagName" , sqlIdentifier (TagName ) ) sql(freestring) ' ----------------------------- > update TagTable >> SET >> ttGroup = dstGroup >> WHERE >> ttGroup like srcGroup >> and ttName like TagName freestring = replace ( freestring , "srcGroup" , sqlIdentifier (TagGroup & TagGroupId ) ) freestring = replace ( freestring , "dstGroup" , sqlIdentifier (newName & TagGroupId ) ) freestring = replace ( freestring , "TagName" , sqlIdentifier (TagName ) ) sql(freestring) ELSE END IF UpdateTagNameList ( TagNameComboSob , tagGroup ) ' resetTagBoxes () IF ( imageLoadedFlag ) then cbSingleFile ( currentFile ) END IF IF ( sob(sobid , "GET" , "RGB" ) == altNameCol ) then sob(sobid , "SET" , "RGB", defNameCol ) ELSE sob(sobid , "SET" , "RGB", altNameCol ) END IF END FUNCTION sob( TagNameDoItSOB , "on" , "press" , "onTagNameDoItClicked") FUNCTION TagGroupDoItClicked ( sobid ) IF ( ! isDBopen ) then ErrorReport ("No database selected" ) EXIT function END IF dim actionTxt = sob ( TagGroupActionComboSob, "GET", "TITLE" ) dim TagGroup = sob ( TagGroupComboSob, "GET", "TITLE" ) dim newName = trim ( sob ( TagGroupNewNameSOB , "GET","TITLE" ) ) IF ( TagGroup == "Tagged" ) then ErrorReport ("Actions on this tag group are not allowed" ) EXIT function END if IF ( actionTxt == "new" ) then IF ( isDBcolumn ( "master", newName & TagGroupId) ) then ErrorReport ("Tag Group -"& newName & "- already exists." ) EXIT function END if makeNewTagType ( newName ) ELSEIF ( actionTxt == "replace" ) then IF ( ! isDBcolumn ( "master", newName) ) then ErrorReport ("Target Tag Group does not exist." ) EXIT function END if ELSEIF ( actionTxt == "delete" ) then ' remove any Region reference > select ttName from tagTable where ttGroup like freestring = freestring & " " & sqlString ( TagGroup ) WITHQUERY ( freestring ) deReferenceTagInRegions ( TagGroup , wqText(1) ) END withquery ' REMOVE FROM TagTable > delete tagTable where ttGroup like freestring = freestring & " " & sqlString ( TagGroup ) sql ( freestring ) ' REMOVE FROM MASTER dbColumnDropByFilter ( "main", "master" , TagGroup & TagGroupId , 1) ELSEIF ( actionTxt == "move" ) then ' newName must be the name of an existing TAG Group ' and not = TagGroup newName = assertTagGroupSpelling ( TagGroup ) > select count (*) from TagTable where ttGroup = freestring = freestring & " " & sqlString ( newName & TagGroupId ) sql(freestring) IF ( qrSingleValue == 0 ) then ErrorReport ("Destination tag group -" & newName & "- does not exist") EXIT function END IF IF ( TagGroup == newName ) then ErrorReport ("Destination tag group same as source tag group") EXIT function END IF ' ----------------------------- IF ( haveRectMeta ) then > update master set RectMeta = replace ( RectMeta , freestring = freestring & " " & sqlString ( "," & TagGroup & TagDelim) >> , freestring = freestring & " " & sqlString ( "," & NewName & TagDelim) >> ) sql(freestring) END IF ' ----------------------------- > delete from tagTable where ttGroup like freestring = freestring & " " & sqlString ( TagGroup & TagGroupId ) >> and ttName in ( select ttname from tagtable where ttGroup like freestring = freestring & " " & sqlString ( newName& TagGroupId ) >> ) sql(freestring) ' ----------------------------- > update master >> SET >> dstGroup = dstGroup || frmTbl.ttName || TagDelim >> FROM >> ( select ttName from TagTable where ttGroup = srcGroup ) as frmTbl >> WHERE >> srcGroup like '%' || TagDelim || frmTbl.ttName || TagDelim ||'%' >> and NOT dstGroup like '%' || TagDelim || frmTbl.ttName || TagDelim ||'%' freestring = replace ( freestring , "TagDelim" , sqlString(TagDelim) ) freestring = replace ( freestring , "srcGroup" , sqlIdentifier (TagGroup & TagGroupId) ) freestring = replace ( freestring , "dstGroup" , sqlIdentifier (newName & TagGroupId ) ) sql(freestring) ' ----------------------------- ' REMOVE FROM TagTable > delete from tagTable where ttGroup like freestring = freestring & " " & sqlString ( TagGroup ) sql ( freestring ) ' REMOVE FROM MASTER dbColumnDropByFilter ( "main", "master" , TagGroup & TagGroupId , 1) ELSE ' treat as NoTag END IF CreateUsersTagTableAndColumnsList () ' resetTagBoxes () ' irrelevant since updateTagTypeCombos does it too updateTagTypeCombos ( ) IF ( imageLoadedFlag ) then report(" reloaded " ) cbSingleFile ( currentFile ) END IF dim cnt = sob ( TagGroupComboSob , "GET", "ROWS") sob ( TagGroupComboSob , "SET" ,"SELECTION" , "ROW" , cnt ) IF ( sob(TagGroupDoItSob , "GET" , "RGB" ) == altTypeCol ) then sob(TagGroupDoItSob , "SET" , "RGB", defTypeCol ) ELSE sob(TagGroupDoItSob , "SET" , "RGB", altTypeCol ) END IF END FUNCTION sob( TagGroupDoItSob , "on" , "CLICK" , "TagGroupDoItClicked") ' ----------------------------------------------------- ' RemoveDupsOvl ' ----------------------------------------------------- FUNCTION CbRemoveExternalDuplicates ( sobid ) sob ( CommonSobResult, "empty" ) IF ( isTable ( , "master") ) then sob ( CommonSobResult, "empty" ) cancelClear () cancelText( "Reading in new folder" , 0 ) cancelText( "This can take a short while" , 3 ) cancelShow(1) dim TagArray , uBoundTagArray , i dim TargetPath = GetPath & "\\" dim src = TargetPath update master set key = hash || path || name IF ( targetPath = "\\" ) then sob ( CommonSobResult, "ADD" , "row" , "Cancelled by user" & EOL ) cancelShow(0) EXIT function END IF sql ( replace ( "select count (*) from master where path = $pth", "$pth", sqlString(TargetPath)) ) IF ( qrSingleValue > 0 ) then sob ( CommonSobResult, "ADD" , "row" , "Folder " & TargetPath & EOL & "already in Master" & EOL ) EXIT function END IF cancelText( TargetPath , 1 ) dbFileList ( "temp", "NewFolder" , TargetPath , -1 , , "md5" ) delete from temp.newFolder where (path||name) in ( select (path||name) from master) and type like 'file' SELECT count(*) from temp.newFolder where not hash in ( select hash from master) and type like 'file' dim fileCount = qrSingleValue cancelText( "Files to keep: " & FileCount , 3 ) SELECT count(*) from temp.newFolder where hash in ( select hash from master) and type like 'file' fileCount = qrSingleValue cancelText( "Files to ignore: " & FileCount , 2 ) WITHQUERY ("select path||name from temp.newFolder where hash in ( select hash from master) and type like 'file' " ) DeleteFileNow ( wqtext(1) ) cancelToggle() fileCount-- IF ( fileCount BAND 0x0f ) then ELSE cancelText( "Files to ignore: " & FileCount , 2 ) END IF END withQuery delete from temp.newFolder where hash in ( select hash from master) and type like 'file' ' update newfolder set key = path update newfolder set key = path || name || '\' where type like 'folder' update newfolder set level = level + 1 where type like 'folder' SELECT max ( level ) from newfolder FOR i = qrSingleValue to 0 step -1 freestring = replace ( "select key , path || name from newfolder where level = $i group by key having count (*) = 1 " , "$i" , ""&i ) WITHQUERY ( freestring ) freestring = replace ( "delete from newfolder where key like $pth ", "$pth" , sqlstring(wqtext(1)) ) report = freestring TRY rmdir ( wqtext(2) ) sql (freestring ) CATCH report (" failed " & wqtext(2)) END try END withquery NEXT i cancelShow(0) ELSE sob ( CommonSobResult, "SET" , "TITLE" , "No open database" ) END IF toggleWarnColour ( sobid ) END Function ' ----------------------------------------------------- ' Rescan Ovl ' ----------------------------------------------------- FUNCTION prepareUpdateForMaster () ' in this FUNCTION we do NOT alter MASTER ' we only prepare other temp tables dim TagArray , uBoundTagArray , i drop table if exists temp.shadowMaster drop table if exists temp.dummy ' -------------------------------- ' find the root folders (used in dbFileList calls ) ' repeat the dbFileList calls and merge the results cancelClear () cancelText( "Checking folder(s)" , 0 ) SELECT count (*) from master i = qrSingleValue cancelText( "This can take about " & cint( (i + 5000) / 5000 ) & " mins", 3 ) cancelShow(1) ' ------------------------------------------------------------------------------------------------ ' for each ROOT folder in MASTER ' create a file list and combine the lists into shadowMaster WITHQUERY ( "select distinct path from master where folderkey = 0 " ) cancelText( wqtext(1) , 1 ) dbFileList ( "temp", "dummy" , wqtext(1) , -1 , "*" , "MD5" ) create table if not exists temp.shadowMaster as select * from temp.dummy limit 0 insert into temp.shadowMaster select * from temp.dummy delete from temp.dummy END withQuery drop table if exists temp.dummy ' ------------------------------------------------------------------------------------------------ ' needed because the MD5 checksum is in dbFileList column hash ' but in the meta database we use fsHash for the filesstem MD5 value ' alter table temp.shadowMaster rename column hash to fsHash ' ------------------------------------------------------------------------------------------------ ' give the shadowmaster the META columns that Master has dbTableColumns ( "master" ) dbTableColumns ( "shadowMaster" ) ' identify the COLUMNS that master has that shadowMaster does not delete from temp.masterColumns where name in ( select name from temp.shadowMasterColumns) ' -------------------------------- ' temp.masterColumns lists the COLUMNS in the original MASTER that are not in shadowMaster i.e. the list ' just created by dbFileList ' These columns need to be created in shadowMaster, and the contents in MASTER copied across SELECT count (*) from temp.masterColumns uBoundTagArray = qrSingleValue IF ( uBoundTagArray ) then SELECT group_concat (name , ',') from temp.masterColumns set TagArray = split ( qrsingleValue , "," ) uBoundTagArray = uBound ( TagArray ) ' add the "missing" columns to shadowmaster FOR i = 0 to uBoundTagArray ' add the missing column > alter table temp.shadowMaster add column freestring = freestring & " " & sqlidentifier (TagArray(i) ) sql ( freestring ) ' initialise it to have only a TagDelim > update temp.shadowMaster set freestring = freestring & " " & sqlidentifier (TagArray(i) ) freestring = freestring & " = " & sqlString (TagDelim) sql ( freestring ) NEXT i END IF update temp.shadowMaster set fshash = hash ' ------------------------------------------------------------------------------------------------ ' identify LOST files and NEW files, put them in their own temp tables ' LOST = file in MASTER not in shadowMaster ' NEW = file not in MASTER in shadowMaster update Master set key = path || name update temp.shadowMaster set key = path || name drop table if exists temp.newFiles create table temp.newFiles as select * from temp.shadowMaster where not key in ( select key from master) drop table if exists temp.LostFiles create table temp.LostFiles as select * from master where not key in ( select key from temp.shadowMaster) ' -------------------------------- ' remove LOST files, and NEW files from the shadowmaster delete from temp.shadowMaster where key in ( select key from temp.LostFiles ) delete from temp.shadowMaster where key in ( select key from temp.NewFiles ) ' -------------------------------- ' remove unchanged files from shadowMaster update Master set key = path || name || fsHash update temp.shadowMaster set key = path || name || fsHash delete from temp.shadowMaster where key in ( select key from master ) ' -------------------------------- ' identify changed files, ANYTHING still in shadowMaster must be a changed file ' since we have eliminated: unchanged, lost and new files ' CHANGED = same path/name but different fsHash drop table if exists temp.changedFiles create table temp.changedFiles as select * from temp.shadowMaster delete from temp.shadowMaster where key in ( select key from temp.changedFiles ) ' -------------------------------------------------------------------- ' handle the CHANGED files ' why not just update the master fsHash ' because if we did, we would lose any lastUpdate info, not really important! ' give CHANGED files META data from MASTER ' simply give them their ORIGINAL META data update temp.changedFiles set key = path || name update master set key = path || name ' since we are not doing any DROP or CREATE commands, we can use a WITHQUERY ' for each META column, copy the original META data across WITHQUERY ( "select name from temp.masterColumns" ) > update temp.changedFiles SET >> $col = frmTbl. $col >> FROM >> ( select $col , key from master ) as frmTbl >> WHERE >> changedFiles.key = frmTbl.key freestring = replace ( freestring , "$col" , sqlidentifier(wqText(1)) ) sql ( freestring ) END withquery ' -------------------------------------------------------------------- ' tidy up update master set key = hash || path || name ' we have NOT removed LOST files, nor added NEW files ' we do that later END FUNCTION FUNCTION CbRescanDatabase ( sobid ) sob ( CommonSobResult, "empty" ) IF ( isTable ( , "master") ) then prepareUpdateForMaster () cancelShow(0) upDateMaster () sob ( CommonSobResult, "ADD" , "row" , Date & " " & time & EOL ) SELECT count (*) from temp.newFiles where type like 'file' sob ( CommonSobResult, "ADD" , "row" , "New files : " & qrsingleValue & EOL ) SELECT count (*) from temp.changedFiles where type like 'file' sob ( CommonSobResult, "ADD" , "row" , "Changed files : " & qrsingleValue & EOL ) SELECT count (*) from temp.lostFiles where type like 'file' sob ( CommonSobResult, "ADD" , "row" , "Lost files : " & qrsingleValue & EOL ) sob ( CommonSobResult, "ADD" , "row" , EOL ) sob ( CommonSobResult, "ADD" , "row" , "Update not finalised until Save/SavedAs is triggered." & EOL ) ELSE sob ( CommonSobResult, "SET" , "TITLE" , "No open database" ) END IF toggleWarnColour ( sobid) END Function ' ----------------------------------------------------- ' Scan new folderOvl ' ----------------------------------------------------- dim ScanNewFolderReportSob FUNCTION PopulateImageInfoIntoNewFiles () ' if a new Folder file is truly new, then we have no pHash / dHash / ImageGroup / IgnoreFile info for it cancelText( "Get pHash / dHash from image files" , 1 ) dim pHash = 0 , dHash = 0 > select path||name , rowid from temp.NewFolder where ext in freestring = freestring & ImageFileExtensions WITHQUERY (freestring ) phash = canvas("new", "phash.from.file", wqText(1) , byref dHash ) > update temp.NewFolder set >> pHash = freestring = freestring & " " & pHash >> , dHash = freestring = freestring & " " & dHash >> , ImageGroup = 0 , IgnoreFile = 0 >> where fsHash = freestring = freestring & " " & sqlidentifier (wqText (2)) sql(freestring) END WithQuery > update temp.newFolder >> set >> ImageGroup = num2Hex(pHash) || num2Hex(dHash) >> , hash = num2Hex(pHash) || num2Hex(dHash) >> where >> not pHash = 0 sql(freestring) END Function FUNCTION addNewFolderStuctureToMaster () cancelClear () cancelText( "Reading in new folder" , 0 ) cancelText( "This can take a short while" , 3 ) cancelShow(1) dim TagArray , uBoundTagArray , i dim TargetPath = GetPath & "\\" addNewFolderStuctureToMaster = TargetPath cancelText( TargetPath , 1 ) dbFileList ( "temp", "NewFolder" , TargetPath , -1 , , "md5" ) cancelText( "Folder(s) read" , 1 ) ' ------------------------------------------------- ' remove any files that we already have in the database update temp.NewFolder set key = path||name update master set key = path||name delete from temp.NewFolder where key in ( select key from master ) ' ------------------------------------------------- ' if no new files then exit SELECT count (*) from temp.NewFolder where type like 'file' IF ( qrSingleValue == 0 ) then ErrorReport ("Folder: " & TargetPath & " already in Master" ) EXIT function END IF ' ------------------------------------------------- ' want NewFolder to have same columns as Master dbTableColumns ( "master" ) dbTableColumns ( "NewFolder" ) ' identify the COLUMNS that master has that NewFolder does not delete from temp.masterColumns where name in ( select name from temp.NewFolderColumns) SELECT count (*) from temp.masterColumns uBoundTagArray = qrSingleValue ' would have liked to use WITHQUERY, but can not do ALTER TABLE inside it cancelText( "Adding customer columns" , 1 ) IF ( uBoundTagArray ) then SELECT group_concat (name , ',') from temp.masterColumns set TagArray = split ( qrsingleValue , "," ) uBoundTagArray = uBound ( TagArray ) FOR i = 0 to uBoundTagArray ' add the "missing" columns to NewFolder > alter table temp.NewFolder add column freestring = freestring & " " & sqlidentifier (TagArray(i) ) sql ( freestring ) ' give the column the default value > update temp.NewFolder set freestring = freestring & " " & sqlidentifier (TagArray(i) ) freestring = freestring & " = " & sqlString (TagDelim) sql ( freestring ) NEXT i END IF ' ------------------------------------------------- ' there is chance that a new folder file is direct MD5 hash identical to MASTER ' if so copy it's TAG info across cancelText( "Populating custom columns for known files" , 1 ) WITHQUERY ( "select name from temp.masterColumns " ) ' copy values from MASTER to NewFolder use fsHash > update temp.NewFolder SET >> $col = frmTbl. $col >> FROM >> ( select $col , fsHash from master ) as frmTbl >> WHERE >> NewFolder.fsHash = frmTbl.fsHash freestring = replace ( freestring , "$col" , sqlidentifier(wqText(1)) ) sql ( freestring ) > update temp.newFolder set freestring = freestring & " " & sqlidentifier (wqtext(1)) freestring = freestring & " = " & sqlString (TagDelim) >> where freestring = freestring & " " & sqlidentifier (wqtext(1)) >> = NULL or freestring = freestring & " " & sqlidentifier (wqtext(1)) >> = '' sql ( freestring ) END withquery update temp.NewFolder SET fsHash = hash , pHash = 0 , dHash = 0, ImageGroup='' , IgnoreFile = 0 , ImageGroupType ='unhandled' dim pHash = 0 , dHash = 0 > select path||name , rowid from temp.NewFolder where ext in freestring = freestring & ImageFileExtensions WITHQUERY (freestring ) phash = canvas("new", "phash.from.file", wqText(1) , byref dHash ) > update temp.NewFolder set >> pHash = freestring = freestring & " " & pHash >> , dHash = freestring = freestring & " " & dHash >> where rowid = freestring = freestring & " " & wqInt (2) sql(freestring) END WithQuery update temp.NewFolder set ImageGroup = num2Hex(pHash) || num2Hex(dHash) where not pHash = 0 cancelText( "Insert new files into Master table" , 1 ) insert into master select * from temp.NewFolder update master set key = hash || path || name PopulateImageInfoIntoNewFiles () drop table if exists temp.xHashTbl MakeBkTree ( ) END FUNCTION FUNCTION getRootFolders ( reportSob , reportCombo) IF ( ! isDBopen ) then ErrorReport ("No database selected" ) EXIT function END IF sob ( reportSob, "add" , "ROW" , "The root folders of the database are:" & EOL ) drop table if exists temp.baseFolders create temp table baseFolders as select path from master group by path drop table if exists shortest create temp table shortest ( path text ) dim shortest DO SELECT path from baseFolders order by length(path) asc limit 1 shortest = qrSingleValue sob ( reportSob , "add" , "ROW" , shortest & EOL ) sob ( reportCombo , "add" , "ROW" , shortest ) > SELECT count (*) from master where type like 'file' and path like freestring = freestring & " " & sqlString ( shortest & "%" ) sql ( freestring ) sob ( reportSob , "add" , "ROW" , " Number of files: " & qrSingleValue & EOL ) insert into shortest select path from baseFolders order by length(path) asc limit 1 delete from basefolders where path like ( select path from baseFolders order by length(path) asc limit 1 ) || '%' SELECT count (*) from basefolders LOOP while qrsingleValue > 0 sob ( reportCombo, "set" , "selection" , "row" , 1 ) END FUNCTION FUNCTION CbScanNewFolder ( sobid ) sob ( CommonSobResult, "empty" ) IF ( isTable ( , "master") ) then dim TargetPath = addNewFolderStuctureToMaster () CancelShow(0) IF ( targetPath = "\\" ) then sob ( StatusTxt, "ADD" , "row" , "Cancelled by user" & EOL ) EXIT function END IF sob ( CommonSobResult, "ADD" , "row" , Date & " " & time & EOL ) sob ( CommonSobResult, "ADD" , "row" , targetPath & EOL ) SELECT count (*) from temp.newFolder where type not like 'file' sob ( CommonSobResult, "ADD" , "row" , "New Folders : " & qrsingleValue & EOL ) SELECT count (*) from temp.newFolder where type like 'file' sob ( CommonSobResult, "ADD" , "row" , "New files : " & qrsingleValue & EOL ) sob ( CommonSobResult, "ADD" , "row" , EOL ) sob ( CommonSobResult, "ADD" , "row" , "Additions not finalised until Save/SavedAs is triggered." & EOL ) toggleOKColour ( sobid) ELSE sob ( CommonSobResult, "SET" , "TITLE" , "No open database" ) toggleWarnColour ( sobid) END IF cancelShow(0) END Function ' ------------------------------------------------------------------ FUNCTION CbImportMasterToCSV ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if dim CsvFile sob ( CommonSobResult, "empty" ) dim proceed = 1 IF ( ! isDbOpen ) then dbOpen("memory") SetFileFilter ( "CSV Files(*.csv)\0*.cs*\0All Files (*.*)\0*.*\0\0" ) CsvFile = getFile IF ( right( csvFile,4) == ".csv" ) then ELSE csvFile = csvFile & ".csv" END if IF ( isFile (CsvFile) ) then CSVread ( csvFile , , "master") END IF IF ( isDBtable ( , "master") ) then sob ( CommonSobResult, "add" , "row" ,"Master table read from CSV" & EOL ) CsvFile = replace ( CsvFile , ".csv", "TT.csv" ) IF ( isFile (CsvFile) ) then CSVread ( csvFile , , "TagTable") IF ( isDBtable ( , "TagTable") ) then sob ( CommonSobResult, "add" , "row", "TAG table read from CSV" & EOL ) ELSE sob ( CommonSobResult, "add" , "row", "FAILURE: Could not read TAG table from CSV" & EOL ) END IF END IF OnOpenPrepareMasterTagTable (CommonSobResult) ELSE sob ( CommonSobResult, "add" , "row" ,"FAILURE: Could not read Master table rom CSV" & EOL ) END IF ELSE sob ( CommonSobResult, "add" , "row" , "FAILURE: CSV read. A database is already open !" & EOL ) END if cancelShow(0) toggleWarnColour ( sobid) END function ' ------------------------------------------------------------------ FUNCTION CbExportSubListToCSV ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) cancelShow(0) toggleWarnColour ( sobid) EXIT function END if dim CsvFile sob ( CommonSobResult, "empty" ) IF ( isTable ( "temp" , "SubList") ) then SetFileFilter ( "CSV Files(*.csv)\0*.cs*\0All Files (*.*)\0*.*\0\0" ) CsvFile = getFile IF ( right( csvFile,4) == ".csv" ) then ELSE csvFile = csvFile & ".csv" END if TRY qrWriteToCSV (CsvFile ,"select * from SubList") NoErrorReport ("CSV written: " &csvFile ) CATCH ErrorReport ("CSV write failed: " &csvFile ) END try ELSE sob ( CommonSobResult, "SET" , "TITLE" , "No open database" ) END IF cancelShow(0) toggleWarnColour ( sobid) END function FUNCTION CbExportMasterToCSV ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) cancelShow(0) toggleWarnColour ( sobid) EXIT function END if dim CsvFile sob ( CommonSobResult, "empty" ) IF ( isTable ( , "master") ) then SetFileFilter ( "CSV Files(*.csv)\0*.cs*\0All Files (*.*)\0*.*\0\0" ) CsvFile = getFile IF ( right( csvFile,4) == ".csv" ) then ELSE csvFile = csvFile & ".csv" END if qrWriteToCSV (CsvFile ,"select * from master") IF ( isTable ( , "TagTable") ) then CsvFile = replace ( CsvFile , ".csv", "TT.csv" ) qrWriteToCSV (CsvFile ,"select * from TagTable") END IF ELSE sob ( CommonSobResult, "SET" , "TITLE" , "No open database" ) END IF cancelShow(0) toggleWarnColour ( sobid) END function ' ------------------------------------------------------------------ FUNCTION CbExportMasterToExcel ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if dim ExlFile sob ( CommonSobResult, "empty" ) dim InitialExcelState IF ( isTable ( , "master") ) then SetFileFilter ( "EXCEL Files(*.xlsx)\0*.xl*\0All Files (*.*)\0*.*\0\0" ) ExlFile = getFile cancelClear () cancelText( "Export a database to Excel" , 0 ) cancelText( "Creating link to Excel" , 1 ) cancelText( " " , 3 ) cancelShow(1) InitialExcelState = xlActive IF ( ! xlRunning ) then xlopen () ELSE dim myWb = exl.Workbooks.Add END IF cancelText( "Exporting MASTER table, should be less than 60 seconds" , 1 ) exl.ActiveSheet.name ="Master" qrWriteToXL ( "select * from master") cancelText( "Formating Master table, should be less than 60 seconds" , 1 ) xlTableFormat () exl.range(exl.cells(1,1),exl.cells(1,QRcols)).autofilter cancelText( "Exporting Tag table, should be less than 60 seconds" , 1 ) exl.worksheets.add().name = "TagTable" qrWriteToXL ( "select * from TagTable") cancelText( "Formating Tag table, should be less that 60 seconds" , 1 ) xlTableFormat () exl.range(exl.cells(1,1),exl.cells(1,QRcols)).autofilter ' ----------------------------------------------------------- cancelText( "Creating Admin sheet" , 1 ) exl.worksheets.add().name = "Admin" exl.cells(1,1).value = "Content:" exl.Cells(2,1).value = "Source:" exl.Cells(3,1).value = "Generated:" exl.cells(1,2).value = "Master table, and Tag table" exl.Cells(2,2).value = sob ( statusDBFile, "GET" ,"TITLE" ) exl.Cells(3,2).value = "'" & date & " " & time exl.Cells(9,1).value = "Generated using " & appName & " " & AppVersion exl.Cells(10,1).value = "Freeware, no installation, single file, < 5 Mbyte" exl.Cells(11,1).value = "www.PlodWare.com" exl.Cells(9,1).font.size = 8 exl.Cells(10,1).font.size = 8 exl.Cells(11,1).font.size = 8 exl.cells.columns.autofit exl.cells.VerticalAlignment = -4108 ' ----------------------------------------------------------- cancelText( "Saving workbook to file" ,1 ) exl.Application.DisplayAlerts = 0 exl.ActiveWorkBook.saveas ( ExlFile , 50 ) ' 50 = xlExcel12 (Excel Binary Workbook in 2007-2013 with or without macro's, xlsb) ' 51 = xlOpenXMLWorkbook (without macro's in 2007-2013, xlsx) ' 52 = xlOpenXMLWorkbookMacroEnabled (with or without macro's in 2007-2013, xlsm) ' 56 = xlExcel8 (97-2003 format in Excel 2007-2013, xls) ' ActiveWorkBook.close exl.Application.DisplayAlerts = 1 exl.ActiveWorkbook.Close IF ( ! InitialExcelState ) then exl.quit set exl = nothing END IF sob ( CommonSobResult, "ADD" , "ROW" , "Database saved to: " & EOL & ExlFile ) ELSE sob ( CommonSobResult, "SET" , "TITLE" , "No open database" ) END IF cancelShow(0) toggleWarnColour ( sobid) END function ' ------------------------------------------------------------------ FUNCTION CbImportMasterToExcel ( sobid ) IF ( ! isDBOpen ) then ErrorReport ("No database selected" ) EXIT function END if dim ExlFile , proceed sob ( CommonSobResult, "empty" ) dim InitialExcelState IF ( ! isDbOpen ) then proceed = 1 END IF IF ( proceed ) then proceed = dbValidityCheck () END IF IF ( proceed) then SetFileFilter ( "EXCEL Files(*.xlsx)\0*.xl*\0All Files (*.*)\0*.*\0\0" ) ExlFile = getFile cancelClear () cancelText( "Creating a database from Excel" , 0 ) cancelText( "Creating link to Excel" , 1 ) cancelText( " " , 3 ) cancelShow(1) dbOpen ("memory") dim xlState = isXLrunning () cancelText( "Importing MASTER table, should be less than 60 seconds" , 1 ) proceed = xlExportToDB ( ExlFile , "master", "master" , , ,1 ) IF ( proceed ) then cancelText( "Importing TagTable , should be less than 60 seconds" , 1 ) proceed = xlExportToDB ( ExlFile , "TagTable", "TagTable" ) END if IF ( ! xlState ) then exl.quit set exl = nothing END IF IF ( proceed ) then OnOpenPrepareMasterTagTable (CommonSobResult) sob ( CommonSobResult, "ADD" , "ROW" , "Database created from: " & EOL & ExlFile ) ELSE sob ( CommonSobResult, "ADD" , "ROW" , "FAILED: Tying to create Database from: " & EOL & ExlFile ) dbClose () END IF ELSE sob ( CommonSobResult, "SET" , "TITLE" , "No open database" ) END IF cancelShow(0) toggleWarnColour ( sobid) END function ' ------------------------------------------------------------------ FUNCTION createCommonPanel ( sobid ) dim localWindow = sob ( sobid , "add", "CONTAINER", "COLUMN.W") sob ( -1 , "ADD" , "SPACE" , 0 , 10) sob ( -1 , "SET" , "RGB" , -1 ) CommonSobTitle = sob ( -1 , "ADD" , "LABEL", "Scan new folder and enter in database" ) sob ( -1 , "SET" , "RGB" , bgBoxCol ) sob ( -1 , "ADD" , "SPACE" , 0 , 10) sob ( -1 , "SET" , "RGB" , -1 ) CommonSobInfo =sob ( -1 , "ADD" , "EDIT.ROWS" , " " , 13 ) sob ( -1 , "EMPTY" ) sob ( -1 , "SET" , "RW" , 0 ) sob ( -1 , "SET" , "RGB" , bgROeditCol ) sob ( -1 , "ADD" , "SPACE" , 0 , 10) sob ( -1 , "SET" , "RGB" , -1 ) sob ( localWindow , "add", "CONTAINER", "row.h") CommonSobBtn = sob ( -1 , "ADD" , "BUTTON" , "PUSH", "Scan" ) sob ( -1 , "ADD" , "SPACE" , 0 , 40) sob ( -1 , "SET" , "RGB" , -1 ) CommonSobBtnB = sob ( -1 , "ADD" , "BUTTON" , "PUSH", "Open Export folder" ) sob ( -1 , "ADD" , "SPACE" , 0 , 40) sob ( -1 , "SET" , "RGB" , -1 ) sob ( CommonSobBtnB , "ON" , "PRESS", "CbOpenExportFolder") CommonSobCombo = sob(-1,"add","COMBO" , "dropdown" , " " ) sob ( localWindow , "ADD" , "SPACE" , 0 , 10) sob ( -1 , "SET" , "RGB" , -1 ) CommonSobResult = sob ( localWindow , "ADD" , "EDIT.ROWS" , " " , 13 ) sob ( -1 , "EMPTY" ) sob ( -1 , "SET" , "RW" , 0 ) sob ( -1 , "SET" , "RGB" , bgROeditCol ) ' trick to have an invisible button, so that we have a asynchronous triggerable event reDrawRegionsID = sob ( localWindow , "add" , "BUTTON" , "PUSH" ," " ) sob ( -1 , "set" , "show" , 0 ) sob ( -1 , "ON", "press", "cbReDrawRegions" ) sob ( localWindow , "add" , "SPACE" , 0 , 1) sob ( -1 , "SET" , "RGB" , -1 ) END FUNCTION createCommonPanel ( CommonOvl) '--------------------------- ' FINISHED '--------------------------- FUNCTION CbBackUpDatabase ( sobid ) IF ( ! isDbOpen ) then ErrorReport("no database open") cancelShow(0) toggleWarnColour ( sobid) EXIT function END if drop table if exists temp.srcMaster create temp table srcMaster as select "" as dst , * from master CbBackUpDatabaseInner ( sobid ) END Function FUNCTION CbBackUpSubList ( sobid ) IF ( ! isDbOpen ) then ErrorReport("no database open") cancelShow(0) toggleWarnColour ( sobid) EXIT function END if drop table if exists temp.srcMaster create temp table srcMaster as select "" as dst , * from sublist insert into srcMaster select "" as dst , rowid, * from master where type like 'folder' update srcMaster set children = 0 update srcMaster set key = path || name || '\' > update srcMaster >> set >> children = frmtbl.cnt >> from >> ( select count(*) as cnt, path from srcMaster where type like 'file' group by path ) as frmTbl >> Where >> srcMaster .type like 'folder' >> and ( srcMaster .key = frmTbl.path ) sql ( freestring ) delete from srcMaster where type like 'folder' and children = 0 CbBackUpDatabaseInner ( sobid ) END Function FUNCTION CbBackUpDatabaseInner ( sobid ) dim proceed = 1 dim dstFolder , dstLength , srcPath , srcPathLast, srcLength , fileCount sob ( CommonSobResult, "empty" ) proceed = isDbOpen IF ( proceed ) then dstFolder = GetPath IF ( dstFolder == "" ) then proceed = 0 ELSE IF ( ! isFile ( dstFolder ) ) then sob ( CommonSobResult, "ADD", "ROW" , "Destination does not exist, it will be created" & EOL ) IF ( ! MKDIR ( dstFolder ) ) then sob ( CommonSobResult, "ADD", "ROW" , "Could not create destination folder " & EOL & dstFolder & EOL ) proceed = 0 END if END if END IF ELSE sob ( CommonSobResult, "SET" , "TITLE" , "No open database" ) END IF IF ( proceed ) then ' prepare the DST file list dstFolder = dstFolder & "\\" dstLength = length( dstFolder) END IF IF ( proceed ) then sob ( CommonSobResult, "add" , "row" , "Destination folder base: " & dstFolder & EOL ) dim fso = createobject ("Scripting.FileSystemObject") IF ( ! fso ) THEN report (" could not create FSO") proceed = 0 END IF END if IF ( proceed ) then cancelClear () cancelText( "Back-up database " , 0 ) cancelText( "Establish content of back-up area" , 1 ) cancelText( " " , 3 ) cancelShow(1) ' prepare srcMaster to have ' src file name path||name ' dst path dst getrootFolders (CommonSobResult ,CommonSobCombo ) drop table if exists temp.dstMaster drop table if exists temp.dstMasterRoot ' set srcMaster to have DST path for each file dim index = 0 WITHQUERY ( "select path from shortest " ) index = index + 1 sob ( CommonSobResult, "add" , "row" , "Src folder base: " & WQtext(1) & EOL ) srcLength = length ( wqtext(1)) set srcPath = split ( wqtext(1) , "\\" ) ' arrange that srcMaster . dst ' holds the destination PATH for its ROW srcPathLast = dstFolder & right( "00000" & index ,4) & "-" & srcPath ( uBound(srcPath) ) & "\\" sob ( CommonSobResult, "add" , "row" , "Dst for this Src: " & srcPathLast & EOL ) IF ( isFILE ( srcPathLast) ) then ELSE mkdirPath ( srcPathLast ) IF ( ISFILe ( srcPathLast) ) then ELSE message = "Problem at : " & srcPathLast END IF END if > update srcMaster >> SET >> dst = $dst || subStr ( path , $len) >> WHERE >> path like $key || '%' freestring = replace ( freestring , "$dst" , sqlString(srcPathLast) ) freestring = replace ( freestring , "$len" , "" & (srcLength+1)) freestring = replace ( freestring , "$key" , sqlString (wqtext(1))) sql ( freestring ) sob ( CommonSobResult, "add" , "row" , "DST: " & srcPathLast& EOL ) ' BE WARNED: dbFileList internally tries to DROP its target TBL ' and then create a new one ' BUT: inside a WITHQUERY / End loop table DROP does NOT work (database is locked) ' so, I delete the rows of the table so that in the next loop, the table is empty dbFileList ( "temp" , "dstMasterRoot" , srcPathLast ,-1) create temp table if not exists dstMaster as select * from temp.dstMasterRoot limit 0 insert into temp.dstMaster select * from temp.dstMasterRoot delete from temp.dstMasterRoot END WithQuery END if IF ( proceed ) then ' work out which files have to be deleted from DST because they no longer exist in the SRC cancelText( "Deleting files in back-up, not in database" , 1 ) update srcMaster set key = dst || name || size || write where type like 'file' drop table if exists bupMaster create table bupMaster as select * from temp.srcmaster update bupMaster set path = dst alter table bupMaster drop column dst update dstMaster set key = path || name || size || write where type like 'file' '--------------------------- ' remove any dst FILES which are no longer in src SELECT count (*) from dstMaster where not key in ( select key from srcMaster ) and type like 'file' fileCount = qrSingleValue sob ( CommonSobResult, "add" , "row" , "Nr files in DST not in SRC: " & qrSingleValue & EOL ) cancelText( "Files to delete: " & FileCount , 2 ) ' delete the file from the file system WITHQUERY ( " select (path||name ) from dstMaster where not key in ( select key from srcMaster ) and type like 'file' ") deleteFileNow ( wqtext(1) ) report = "Deleting FILE from DST where file nolonger in SRC, or SRC file has changed: " & wqtext(1) cancelToggle () fileCount-- IF ( fileCount BAND 0x0f ) then ELSE cancelText( "Files to delete: " & FileCount , 2 ) report ("Files to delete: " & FileCount ) END IF END withquery ' delete the file entry in dst delete from dstMaster where not key in ( select key from srcMaster ) and type like 'file' '--------------------------- ' remove from the dst any FOLDERS which are no longer in src cancelText( "Deleting folders in back-up, not in database" , 1 ) update srcMaster set key = dst || name || '\' where type like 'folder' update dstMaster set key = path || name || '\' where type like 'folder' SELECT count (*) from dstMaster where not key in ( select key from srcMaster ) and type like 'folder' fileCount = qrSingleValue sob ( CommonSobResult, "add" , "row" , "Nr folders in DST not in SRC: " & qrSingleValue & EOL ) cancelText( "Folders to delete: " & FileCount , 2 ) ' we use the Scripting.FileSystemObject from the operating system to do the delete folder! ' delete the FOLDER from the file system WITHQUERY ( " select path , name from dstMaster where not key in ( select key from srcMaster ) and type like 'folder' order by level desc ") report ( "delete folder " & wqtext(1) & wqtext(2)) fso.DeleteFolder ( wqtext(1) & wqtext(2) ) cancelToggle () fileCount-- IF ( fileCount BAND 0x0f ) then ELSE cancelText( "Folders to delete: " & FileCount , 2 ) report ("Folders to delete: " & FileCount ) END IF END withquery ' delete the FOLDER entry in dst delete from dstMaster where not key in ( select key from srcMaster ) and type like 'FOLDER' '--------------------------- END if dim dstPoint IF ( proceed ) then cancelText( "Create the DST folder structure" , 1 ) ' create the DST folder structure WITHQUERY ( " select dst , dst || name || '\\' from srcMaster where not key in ( select key from dstMaster ) and type like 'folder' group by (dst||name) order by level ") IF ( isFILE ( wqText(2)) ) then ELSE mkdirPath ( wqText(2) ) IF ( ISFILe ( wqText(2)) ) then ELSE message = "Problem at : " & wqText(2) END IF END if END withquery END if IF ( proceed ) then cancelText( "Copy files" , 1 ) ' do not need to copy files with key in src & dst SELECT count (*) from srcMaster where key in ( select key from dstMaster ) and type like 'file' sob ( CommonSobResult, "add" , "row" , "Nr files in SRC that have NOT changed: " & qrSingleValue & EOL ) delete from srcMaster where key in ( select key from dstMaster ) SELECT count (*) from srcMaster where type like 'file' fileCount = qrSingleValue cancelText( "Files to go: " & FileCount , 2 ) ' copy the SRC files to DST WITHQUERY ( " select path || name , dst from srcMaster where not key in ( select key from dstMaster ) and type like 'file' ") TRY fso.copyFile ( wqtext(1) ,wqtext(2) ) CATCH IF ( isFile (wqtext(1) ) then report ( "copy file failure: " & EOL & wqtext(1) & EOL & wqtext(2) & EOL ) END IF END try cancelToggle () fileCount-- IF ( fileCount BAND 0x0f ) then ELSE cancelText( "Files to go: " & FileCount , 2 ) END IF END withquery END if vacuum dim saveDBas= dstFolder & "MetaTagger " & date & " " & replace ( left(time,5 ) , ":", "-") & ".db3" DBSaveCopy ( saveDBas , 1 ) > ATTACH DATABASE freestring = freestring & " " & sqlstring (saveDBas) >> as NEW sql ( freestring ) drop table new.master alter table new.bupmaster rename to master vacuum detach database new cancelShow(0) toggleWarnColour ( sobid) END function FUNCTION createFrontPage ( sobid ) dim localWindow = sob ( HelpFixedOvl , "add", "CONTAINER", "COLUMN.W") sob ( -1 , "ADD" , "SPACE" , 0 , 10) sob ( -1 , "SET" , "RGB" , -1 ) sob ( -1 , "ADD" , "LABEL", appName ) sob ( -1 , "SET" , "RGB" , bgBoxCol ) sob ( -1 , "ADD" , "SPACE" , 0 , 10) sob ( -1 , "SET" , "RGB" , -1 ) HelpFixedHTMLSOB =sob ( -1 , "ADD" , "web" ) sob ( HelpFixedHTMLSOB, "SET", "SILENT" , 1) sob ( -1 , "EMPTY" ) END FUNCTION ' redirect the PWS default status reports to MetaTagger status area redirectStatusReportingTo ( StatusTxt) createFrontPage ( HelpPageSob ) sink () db_arg_open ( APP$Path & app$Name & ".db3") UpdateUsedFileList() WelcomeMessage(1) autoStartChk ( ) sob ( appWindow,"SET","SHOW",1)