diff --git a/README b/README index 5419bdd..f4f8860 100644 --- a/README +++ b/README @@ -1,34 +1,5 @@ -Filer v1.0 Beta 3 +Filer is an automatic file organizer. It takes the files it's opened with or that are dropped on it and moves, renames, copies or does all sorts of other things with them according to rules created by the user. -Filer is an automatic filer organizer. It can move, rename, copy, and much, much more. You could even make it bark. ;-) For extensive help and tips, click on the Help button from with the program. +Filer is accompanied by AutoFiler. Instead of working on a set of files provided by the user, it can be started (automatically with Haiku) to monitor certain folders and deal with new files appearing there according to the user-defined rules. -About this version - -Some more bugs were found in the second beta release and a suggested feature was added. Changes since Beta 2: - -1) Fixed a bug which caused all Type tests to fail - -2) Removed a crash after removing a rule - -3) Added the AutoFiler, a helper program which watches folders and runs the Filer on them whenever a new file appears in one of them. - -Please e-mail me if you find any bugs. - ---DarkWyrm -darkwyrm@gmail.com - - -Usage - -Double-click on the Filer to open the list of organization rules. To make it organize files, just drag and drop one or more files onto its icon, right click on a file and choose it from the Open With menu, or give it a list of file names from the Terminal. If a rule doesn't work right, try running it from the Terminal to see what it's doing as it processes your files. - - -Features - -- Choose from a file's name, size, location, type, or attributes as a source for conditions for organization - -- A wide variety of possible actions for files which match - -- Use substitution patterns in actions for more flexible renaming and organizing - -- Wildly extensible through the use of Terminal commands +For more information, see the accompanying documentation and rule making reference in the documentation folder. diff --git a/documentation/Rule-Making Reference.html b/documentation/Rule-Making Reference.html index 5e22b1b..601d062 100644 --- a/documentation/Rule-Making Reference.html +++ b/documentation/Rule-Making Reference.html @@ -8,6 +8,7 @@ * Distributed under the terms of the MIT License. * * Authors: + * DarkWyrm * Humdinger --> @@ -98,6 +99,25 @@ ul { padding-left: 14px; } + +/* Rounded corner boxes */ +/* Common declarations */ +.box-info { + -webkit-border-radius: 10px; + -khtml-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + border-style: dotted; + border-width: thin; + border-color: #dcdcdc; + padding: 10px 15px 10px 80px; + margin-bottom: 15px; + margin-top: 15px; + min-height: 42px; +} +.box-info { + background: #e4ffde url(images/alert_info_32.png) 15px 15px no-repeat; +} @@ -110,12 +130,15 @@


-

You will probably find most parts of the Filer pretty self-explanatory, please refer to Filer's User Documentation for more information.

-

To really get power out of it, you'll want to read the information below, though. There are also a number of useful examples to get you going.

+

You will probably find most parts of the Filer pretty self-explanatory, please refer to Filer's User Documentation for more information.
+To really get power out of it, you'll want to read the information below, though. There are also a number of useful examples to get you going.

+

index Rule Conditions

You will need at least one condition for the rule to test for. It can be the type of file, something about its name, how big it is, or some other attribute. These other attributes can be things like someone's nickname kept in a Person file or the e-mail address in the To: field of an e-mail. Note that these can appear on just about any kind of file, but generally will only be found on the kind of file you expect it to be on. A rule will only match if all the conditions you set are met.

+
If a rule doesn't work right, try running Filer from the Terminal to see what it's doing as it processes your files.
+

index Rule Actions

@@ -128,6 +151,7 @@

Delete itOnly if you're sure of yourself and hate a cluttered Trash can. Terminal command…For experts. Run a command just as if you typed it into a Terminal. Substitutions (see below) are performed before the command is executed. This can make the Filer automatically do all sorts of things it couldn't do otherwise. If you move or rename the file this way, you'll need to do everything else with more Terminal command actions or a shell script. +

index Substitutions

@@ -144,6 +168,7 @@

%TIME%Current time using 24-hour time. %ATTR:xxxx%An extended attribute of the file. The technical name for the attribute is put between the colon and the second %. At this point, unfortunately, the case-sensitive, technical name of the attribute must be used. For example, an e-mail address attribute is META:email. This can be found in the FileTypes preferences application by choosing the type of file it is normally found on and double-clicking on it in the "Extra attributes" box. In the window that appears, it will be in the box marked "Internal name". +

index Common Attributes

diff --git a/documentation/User Documentation.html b/documentation/User Documentation.html index f59fb99..9b8cf9a 100644 --- a/documentation/User Documentation.html +++ b/documentation/User Documentation.html @@ -101,6 +101,25 @@ ul { padding-left: 14px; } + +/* Rounded corner boxes */ +/* Common declarations */ +.box-info { + -webkit-border-radius: 10px; + -khtml-border-radius: 10px; + -moz-border-radius: 10px; + border-radius: 10px; + border-style: dotted; + border-width: thin; + border-color: #dcdcdc; + padding: 10px 15px 10px 80px; + margin-bottom: 15px; + margin-top: 15px; + min-height: 42px; +} +.box-info { + background: #e4ffde url(images/alert_info_32.png) 15px 15px no-repeat; +} @@ -113,7 +132,7 @@


-

Filer is an automatic file organizer. It takes the files it's opened with or that are dropped on its icon and moves, renames, copies or does all sorts of other things with them according to rules created by the user.

+

Filer is an automatic file organizer. It takes the files it's opened with or that are dropped on it and moves, renames, copies or does all sorts of other things with them according to rules created by the user.

Filer is accompanied by AutoFiler. Instead of working on a set of files provided by the user, it can be started (automatically with Haiku) to monitor certain folders and deal with new files appearing there according to the user-defined rules.

@@ -137,7 +156,10 @@

Filer's dropzone

As described above under 4.), you can drag&drop files onto the Dropzone to have them processed according to your set of rules. If you'd like to embed this Dropzone into the Desktop, click the button Replicate dropzone….
-It opens a window to resize the dropzone to fit your needs. When that's done, drag the little Replicant handle in the bottom-right corner onto your Desktop. Make sure to have Show replicants in the Deskbar menu activated. Once on the Desktop, right-click the Replicant's handle for a menu to remove it again.

+It opens a window to resize the dropzone to fit your needs. When that's done, drag the little Replicant handle in the bottom-right corner onto your Desktop. Make sure to have Show replicants in the Deskbar menu activated. Once on the Desktop, right-click the Replicant's handle for a menu to remove it again.
+A right-click into the shaded part of the replicated Dropzone offers a menu as a shortcut to Open Filer….

+
+If you drag files from another partition onto a replicated Dropzone, you'll end up with copies of those files on your Desktop. This is because notifications of dropped files are not only processed by the Replicant, but also by the Tracker sitting below.

index @@ -181,11 +203,13 @@

  • Initial release.
  • -

    1.1.0 - 03-05-2016

    +

    1.1.0 - 05-05-2016

      -
    • A new GUI to integrate Filer and AutoFiler settings.
    • +
    • A new GUI that integrates Filer and AutoFiler settings.
    • +
    • Some usability improvements.
    • +
    • Apply changed settings, rules and monitored folders live.
    • Add a button to manually start/stop AutoFiler.
    • -
    • Adding a dropzone and make it replicatable. +
    • Add a dropzone and make it replicatable.
    • Add user documentation.
    diff --git a/documentation/images/alert_info_32.png b/documentation/images/alert_info_32.png new file mode 100644 index 0000000..05b4fe8 Binary files /dev/null and b/documentation/images/alert_info_32.png differ diff --git a/sources/AutoFilerTab.cpp b/sources/AutoFilerTab.cpp index dfa2c43..577e1b3 100644 --- a/sources/AutoFilerTab.cpp +++ b/sources/AutoFilerTab.cpp @@ -41,7 +41,7 @@ AutoFilerTab::AutoFilerTab() B_DIRECTORY_NODE, false, NULL, fRefFilter); BMessage panelMsg(MSG_FOLDER_CHOSEN); fFilePanel->SetMessage(&panelMsg); - + gRefLock.Lock(); for (int32 i = 0; i < gRefStructList.CountItems(); i++) { @@ -49,7 +49,7 @@ AutoFilerTab::AutoFilerTab() fFolderList->AddItem(new BStringItem(BPath(&refholder->ref).Path())); } gRefLock.Unlock(); - + fFolderList->MakeFocus(); if (fFolderList->CountItems() > 0) fFolderList->Select(0L); @@ -143,12 +143,13 @@ AutoFilerTab::AttachedToWindow() fFolderList->SetTarget(this); fFilePanel->SetTarget(this); + BMessenger messenger(this); if (fFolderList->CountItems() > 0) { - BMessenger messenger(this); BMessage msg(MSG_FOLDER_SELECTED); messenger.SendMessage(&msg); } BMessage msg(MSG_UPDATE_LABEL); + messenger.SendMessage(&msg); fRunner = new BMessageRunner(this, &msg, 0.5 * 6000000); // x * seconds BView::AttachedToWindow(); @@ -160,8 +161,6 @@ AutoFilerTab::DetachedFromWindow() { if (!fDirtySettings) return; - - SaveFolders(); // save autorun value bool autorun = (fAutorunBox->Value() == B_CONTROL_ON); @@ -182,6 +181,22 @@ AutoFilerTab::DetachedFromWindow() void AutoFilerTab::MessageReceived(BMessage* msg) { + if (msg->WasDropped()) { + BMessenger msgr(this); + entry_ref tempRef; + int32 i = 0; + while (msg->FindRef("refs", i, &tempRef) == B_OK) + { + BEntry entry(&tempRef); + if (entry.Exists()) { + BMessage newMsg(MSG_FOLDER_CHOSEN); + newMsg.AddRef("refs", &tempRef); + msgr.SendMessage(&newMsg); + } else + printf("Couldn't find file %s\n",tempRef.name); + i++; + } + } switch (msg->what) { case MSG_AUTOFILER_AUTORUN: @@ -228,6 +243,9 @@ AutoFilerTab::MessageReceived(BMessage* msg) BStringItem* item = (BStringItem*)fFolderList->RemoveItem(selection); delete item; + int32 count = fFolderList->CountItems(); + fFolderList->Select((selection > count - 1) ? count - 1 : selection); + gRefLock.Lock(); RefStorage* refholder = (RefStorage*)gRefStructList.RemoveItem(selection); delete refholder; @@ -250,11 +268,23 @@ AutoFilerTab::MessageReceived(BMessage* msg) int32 index; if (msg->FindInt32("index", &index) != B_OK) index = -1; - + entry_ref ref; if (msg->FindRef("refs", &ref) != B_OK) break; - + + BEntry entry(&ref); + BPath path; + entry.GetPath(&path); + if (!entry.IsDirectory()) { + path.GetParent(&path); + get_ref_for_path(path.Path(), &ref); + } + + BString newpath(path.Path()); + if (!IsFolderUnique(newpath)) + return; + BStringItem* item = (BStringItem*)fFolderList->ItemAt(index); if (item) { gRefLock.Lock(); @@ -282,6 +312,22 @@ AutoFilerTab::MessageReceived(BMessage* msg) } +bool +AutoFilerTab::IsFolderUnique(BString newpath) +{ + if (fFolderList->IsEmpty()) + return true; + + bool unique = true; + for (int i = 0; i < fFolderList->CountItems(); i++) { + BStringItem* sItem = (BStringItem*)fFolderList->ItemAt(i); + if (newpath.Compare(sItem->Text()) == 0) + unique = false; + } + return unique; +} + + void AutoFilerTab::ToggleAutoFiler() { @@ -310,7 +356,8 @@ AutoFilerTab::UpdateAutoFilerLabel() void AutoFilerTab::UpdateAutoFilerFolders() { - fDirtySettings = true; + SaveFolders(); + // if AutoFiler is running, tell it to refresh its folders if (be_roster->IsRunning(kAutoFilerSignature)) { BMessage msg(MSG_REFRESH_FOLDERS); diff --git a/sources/AutoFilerTab.h b/sources/AutoFilerTab.h index 7ba2fbb..0385710 100644 --- a/sources/AutoFilerTab.h +++ b/sources/AutoFilerTab.h @@ -37,6 +37,7 @@ class AutoFilerTab : public BView void EnableAutorun(); void DisableAutorun(); + bool IsFolderUnique(BString newpath); void ToggleAutoFiler(); void UpdateAutoFilerLabel(); diff --git a/sources/DropZoneTab.cpp b/sources/DropZoneTab.cpp index 379248f..0a758e4 100644 --- a/sources/DropZoneTab.cpp +++ b/sources/DropZoneTab.cpp @@ -153,6 +153,7 @@ DropZone::MessageReceived(BMessage* msg) if (msg->WasDropped()) { msg->what = B_REFS_RECEIVED; be_roster->Launch(kFilerSignature, msg); + return; } switch (msg->what) { diff --git a/sources/RefStorage.cpp b/sources/RefStorage.cpp index 86a36df..44d80b7 100644 --- a/sources/RefStorage.cpp +++ b/sources/RefStorage.cpp @@ -68,7 +68,6 @@ LoadFolders() if (refholder) gRefStructList.AddItem(refholder); } - return status; } diff --git a/sources/RuleEditWindow.cpp b/sources/RuleEditWindow.cpp index de53fc6..5764478 100644 --- a/sources/RuleEditWindow.cpp +++ b/sources/RuleEditWindow.cpp @@ -22,11 +22,12 @@ #include "TestView.h" -RuleEditWindow::RuleEditWindow(BRect& rect, FilerRule* rule) +RuleEditWindow::RuleEditWindow(BRect& rect, FilerRule* rule, BHandler* caller) : BWindow(rect, "Edit rule", B_TITLED_WINDOW, B_ASYNCHRONOUS_CONTROLS | B_NOT_RESIZABLE | B_CLOSE_ON_ESCAPE), - fOriginalID(-1) + fOriginalID(-1), + fCaller(caller) { if (rule) fOriginalID = rule->GetID(); @@ -340,10 +341,5 @@ RuleEditWindow::SendRuleMessage() msg.AddPointer("item", rule); msg.AddInt64("id", fOriginalID); - for (int32 i = 0; i < be_app->CountWindows(); i++) - { - BWindow* win = be_app->WindowAt(i); - if (strcmp(win->Title(), "Filer settings") == 0) - win->PostMessage(&msg); - } + fCaller->Looper()->PostMessage(&msg, fCaller); } diff --git a/sources/RuleEditWindow.h b/sources/RuleEditWindow.h index ea35abc..a33ef64 100644 --- a/sources/RuleEditWindow.h +++ b/sources/RuleEditWindow.h @@ -20,7 +20,7 @@ class TestView; class RuleEditWindow : public BWindow { public: - RuleEditWindow(BRect& rect, FilerRule* rule); + RuleEditWindow(BRect& rect, FilerRule* rule, BHandler* caller); ~RuleEditWindow(); void MessageReceived(BMessage* msg); @@ -50,6 +50,7 @@ class RuleEditWindow : public BWindow BButton* fHelp; int64 fOriginalID; + BHandler* fCaller; BList fTestList; BList fActionList; diff --git a/sources/RuleRunner.cpp b/sources/RuleRunner.cpp index 3f4f516..881dc4e 100644 --- a/sources/RuleRunner.cpp +++ b/sources/RuleRunner.cpp @@ -1053,7 +1053,7 @@ SaveRules(BObjectList* ruleList) // While we could use other means of obtaining table names, this table is also // used for maintaining the order of the rules, which must be preserved DBCommand(db,"create table RuleList (ruleid int primary key, name varchar);", - "PrefsWindow::SaveRules"); + "RuleTab::SaveRules"); BString command; @@ -1077,11 +1077,11 @@ SaveRules(BObjectList* ruleList) << "(entrytype varchar, testtype varchar, testmode varchar, testvalue varchar, attrtype varchar, attrtypename varchar, attrpublicname varchar);"; - DBCommand(db, command.String(), "PrefsWindow::SaveRules"); + DBCommand(db, command.String(), "RuleTab::SaveRules"); command = "insert into RuleList values("; command << i << ",'" << tablename << "');"; - DBCommand(db, command.String(), "PrefsWindow::SaveRules"); + DBCommand(db, command.String(), "RuleTab::SaveRules"); for (int32 j = 0; j < rule->CountTests(); j++) { @@ -1108,7 +1108,7 @@ SaveRules(BObjectList* ruleList) << "', '" << EscapeIllegalCharacters(attrName.String()) << "');"; - DBCommand(db, command.String(), "PrefsWindow::SaveRules:save test"); + DBCommand(db, command.String(), "RuleTab::SaveRules:save test"); } for (int32 j = 0; j < rule->CountActions(); j++) @@ -1127,7 +1127,7 @@ SaveRules(BObjectList* ruleList) << "', '" << "', '" << EscapeIllegalCharacters(value.String()) << "', '', '', '');"; - DBCommand(db, command.String(), "PrefsWindow::SaveRules:save action"); + DBCommand(db, command.String(), "RuleTab::SaveRules:save action"); } } db.close(); @@ -1156,7 +1156,7 @@ LoadRules(BObjectList* ruleList) CppSQLite3Query query; query = DBQuery(db,"select name from RuleList order by ruleid;", - "PrefsWindow::LoadRules"); + "RuleTab::LoadRules"); BString command; while (!query.eof()) @@ -1185,7 +1185,7 @@ LoadRules(BObjectList* ruleList) // Now comes the fun(?) part: loading the tests and actions. Joy. :/ command = "select * from "; command << rulename << " where entrytype = 'test';"; - query = DBQuery(db, command.String(), "PrefsWindow::LoadRules"); + query = DBQuery(db, command.String(), "RuleTab::LoadRules"); while (!query.eof()) { @@ -1216,7 +1216,7 @@ LoadRules(BObjectList* ruleList) command = "select * from "; command << rulename << " where entrytype = 'action';"; - query = DBQuery(db, command.String(), "PrefsWindow::LoadRules"); + query = DBQuery(db, command.String(), "RuleTab::LoadRules"); while (!query.eof()) { diff --git a/sources/RuleTab.cpp b/sources/RuleTab.cpp index ffa4b2f..de92e69 100644 --- a/sources/RuleTab.cpp +++ b/sources/RuleTab.cpp @@ -166,15 +166,6 @@ RuleTab::AttachedToWindow() } -void -RuleTab::DetachedFromWindow() -{ - if (fChanges) - SaveRules(fRuleList); - MakeEmpty(); -} - - void RuleTab::MessageReceived(BMessage* message) { @@ -190,7 +181,7 @@ RuleTab::MessageReceived(BMessage* message) frame.bottom = frame.top + 300; frame.OffsetBy(60, 30); - RuleEditWindow* rulewin = new RuleEditWindow(frame, NULL); + RuleEditWindow* rulewin = new RuleEditWindow(frame, NULL, this); rulewin->Show(); break; } @@ -205,29 +196,35 @@ RuleTab::MessageReceived(BMessage* message) FilerRule* rule = fRuleList->ItemAt( fRuleItemList->CurrentSelection()); - RuleEditWindow* rulewin = new RuleEditWindow(frame, rule); + RuleEditWindow* rulewin = new RuleEditWindow(frame, rule, this); rulewin->Show(); break; } case MSG_ADD_RULE: { - fChanges = true; FilerRule* item; if (message->FindPointer("item", (void**)&item) == B_OK) AddRule(item); + + SaveRules(fRuleList); break; } case MSG_REMOVE_RULE: { - fChanges = true; - if (fRuleItemList->CurrentSelection() >= 0) - RemoveRule((RuleItem*)fRuleItemList->ItemAt( - fRuleItemList->CurrentSelection())); + int32 selection = fRuleItemList->CurrentSelection(); + if (selection < 0) + break; + + RemoveRule((RuleItem*)fRuleItemList->ItemAt(selection)); + + int32 count = fRuleItemList->CountItems(); + fRuleItemList->Select((selection > count - 1) ? count - 1 : selection); + + SaveRules(fRuleList); break; } case MSG_UPDATE_RULE: { - fChanges = true; FilerRule* rule; if (message->FindPointer("item", (void**)&rule) == B_OK) { @@ -248,6 +245,8 @@ RuleTab::MessageReceived(BMessage* message) } delete rule; } + + SaveRules(fRuleList); break; } case MSG_REVERT: @@ -276,24 +275,26 @@ RuleTab::MessageReceived(BMessage* message) } case MSG_MOVE_RULE_UP: { - fChanges = true; int32 selection = fRuleItemList->CurrentSelection(); if (selection < 1) break; fRuleItemList->SwapItems(selection, selection - 1); fRuleList->SwapItems(selection, selection - 1); + + SaveRules(fRuleList); break; } case MSG_MOVE_RULE_DOWN: { - fChanges = true; int32 selection = fRuleItemList->CurrentSelection(); if (selection > fRuleItemList->CountItems() - 1) break; fRuleItemList->SwapItems(selection, selection + 1); fRuleList->SwapItems(selection, selection + 1); + + SaveRules(fRuleList); break; } default: diff --git a/sources/RuleTab.h b/sources/RuleTab.h index 0a877e5..bd9512a 100644 --- a/sources/RuleTab.h +++ b/sources/RuleTab.h @@ -25,7 +25,6 @@ class RuleTab : public BView ~RuleTab(); virtual void AttachedToWindow(); - virtual void DetachedFromWindow(); void MessageReceived(BMessage* message); private: diff --git a/sources/main.cpp b/sources/main.cpp index 51d72a2..fe2e6f8 100644 --- a/sources/main.cpp +++ b/sources/main.cpp @@ -93,7 +93,6 @@ App::RefsReceived(BMessage* msg) printf("No files given could be processed. Exiting.\n"); fQuitRequested = true; } - ProcessFiles(); }