root/src/apps/deskbar/PreferencesWindow.cpp
/*
 * Copyright 2009-2011 Haiku, Inc.
 * All Rights Reserved. Distributed under the terms of the MIT License.
 *
 * Authors:
 *              John Scipione, jscipione@gmail.com
 *              Jonas Sundström, jonas@kirilla.com
 */


#include "PreferencesWindow.h"

#include <Box.h>
#include <Button.h>
#include <Catalog.h>
#include <CheckBox.h>
#include <ControlLook.h>
#include <File.h>
#include <FormattingConventions.h>
#include <GroupLayout.h>
#include <LayoutBuilder.h>
#include <ListView.h>
#include <Locale.h>
#include <OpenWithTracker.h>
#include <Path.h>
#include <RadioButton.h>
#include <Roster.h>
#include <Screen.h>
#include <SeparatorView.h>
#include <Slider.h>
#include <SpaceLayoutItem.h>
#include <Spinner.h>
#include <View.h>

#include <DeskbarPrivate.h>

#include "BarApp.h"
#include "DeskbarUtils.h"
#include "ScreenCornerSelector.h"
#include "StatusView.h"


static const char* kSettingsFileName = "prefs_window_settings";


#undef B_TRANSLATION_CONTEXT
#define B_TRANSLATION_CONTEXT "PreferencesWindow"


PreferencesWindow::PreferencesWindow(BRect frame)
        :
        BWindow(frame, "", B_TITLED_WINDOW,
                B_NOT_RESIZABLE | B_AUTO_UPDATE_SIZE_LIMITS | B_NOT_ZOOMABLE)
{
        BString title(B_TRANSLATE("%appname% preferences"));
        title.ReplaceFirst("%appname%", B_TRANSLATE_SYSTEM_NAME("Deskbar"));
        SetTitle(title);

        // Initial settings (used by revert button)
        fSettings = *static_cast<TBarApp*>(be_app)->Settings();

        // Menu controls
        fMenuRecentDocuments = new BCheckBox(B_TRANSLATE("Recent documents:"),
                new BMessage(kUpdateRecentCounts));
        fMenuRecentApplications = new BCheckBox(B_TRANSLATE("Recent applications:"),
                new BMessage(kUpdateRecentCounts));
        fMenuRecentFolders = new BCheckBox(B_TRANSLATE("Recent folders:"),
                new BMessage(kUpdateRecentCounts));

        fMenuRecentDocumentCount = new BSpinner("recent documents", NULL,
                new BMessage(kUpdateRecentCounts));
        fMenuRecentApplicationCount = new BSpinner("recent applications", NULL,
                new BMessage(kUpdateRecentCounts));
        fMenuRecentFolderCount = new BSpinner("recent folders", NULL,
                new BMessage(kUpdateRecentCounts));

        // Applications controls
        fAppsSort = new BCheckBox(B_TRANSLATE("Sort applications by name"),
                new BMessage(kSortRunningApps));
        fAppsSortTrackerFirst = new BCheckBox(B_TRANSLATE("Tracker always first"),
                new BMessage(kTrackerFirst));
        fAppsShowExpanders = new BCheckBox(B_TRANSLATE("Show application expander"),
                new BMessage(kSuperExpando));
        fAppsExpandNew = new BCheckBox(B_TRANSLATE("Expand new applications"),
                new BMessage(kExpandNewTeams));
        fAppsHideLabels = new BCheckBox(B_TRANSLATE("Hide application names"),
                new BMessage(kHideLabels));
        fAppsIconSizeSlider = new BSlider("icon_size", B_TRANSLATE("Icon size"),
                new BMessage(kResizeTeamIcons), kMinimumIconSize / kIconSizeInterval,
                kMaximumIconSize / kIconSizeInterval, B_HORIZONTAL);
        fAppsIconSizeSlider->SetHashMarks(B_HASH_MARKS_BOTTOM);
        fAppsIconSizeSlider->SetHashMarkCount((kMaximumIconSize - kMinimumIconSize)
                / kIconSizeInterval + 1);
        fAppsIconSizeSlider->SetLimitLabels(B_TRANSLATE("Small"), B_TRANSLATE("Large"));
        fAppsIconSizeSlider->SetModificationMessage(new BMessage(kResizeTeamIcons));

        // Window controls
        fWindowLocation = new ScreenCornerSelector(BRect(0, 0, 120, 80), B_TRANSLATE("Location"),
                new BMessage(kMsgSetLocation), B_FOLLOW_NONE);

        if (fSettings.vertical && fSettings.left && fSettings.top)
                fWindowLocation->SetValue(B_DESKBAR_LEFT_TOP);
        if (fSettings.vertical && fSettings.left && !fSettings.top)
                fWindowLocation->SetValue(B_DESKBAR_LEFT_BOTTOM);
        if (fSettings.vertical && !fSettings.left && fSettings.top)
                fWindowLocation->SetValue(B_DESKBAR_RIGHT_TOP);
        if (fSettings.vertical && !fSettings.left && !fSettings.top)
                fWindowLocation->SetValue(B_DESKBAR_RIGHT_BOTTOM);
        if (!fSettings.vertical && fSettings.top)
                fWindowLocation->SetValue(B_DESKBAR_TOP);
        if (!fSettings.vertical && !fSettings.top)
                fWindowLocation->SetValue(B_DESKBAR_BOTTOM);

        fWindowAlwaysOnTop = new BCheckBox(B_TRANSLATE("Always on top"),
                new BMessage(kAlwaysTop));
        fWindowAutoRaise = new BCheckBox(B_TRANSLATE("Auto-raise"),
                new BMessage(kAutoRaise));
        fWindowAutoHide = new BCheckBox(B_TRANSLATE("Auto-hide"),
                new BMessage(kAutoHide));

        // Menu settings
        fMenuRecentDocuments->SetValue(fSettings.recentDocsEnabled);
        fMenuRecentDocumentCount->SetEnabled(fSettings.recentDocsEnabled);
        fMenuRecentDocumentCount->SetRange(0, 50);
        fMenuRecentDocumentCount->SetValue(fSettings.recentDocsCount);

        fMenuRecentApplications->SetValue(fSettings.recentAppsEnabled);
        fMenuRecentApplicationCount->SetEnabled(fSettings.recentAppsEnabled);
        fMenuRecentApplicationCount->SetRange(0, 50);
        fMenuRecentApplicationCount->SetValue(fSettings.recentAppsCount);

        fMenuRecentFolders->SetValue(fSettings.recentFoldersEnabled);
        fMenuRecentFolderCount->SetEnabled(fSettings.recentFoldersEnabled);
        fMenuRecentFolderCount->SetRange(0, 50);
        fMenuRecentFolderCount->SetValue(fSettings.recentFoldersCount);

        // Applications settings
        fAppsSort->SetValue(fSettings.sortRunningApps);
        fAppsSortTrackerFirst->SetValue(fSettings.trackerAlwaysFirst);
        fAppsShowExpanders->SetValue(fSettings.superExpando);
        fAppsExpandNew->SetValue(fSettings.expandNewTeams);
        fAppsHideLabels->SetValue(fSettings.hideLabels);
        fAppsIconSizeSlider->SetValue(fSettings.iconSize / kIconSizeInterval);

        // Window settings
        fWindowAlwaysOnTop->SetValue(fSettings.alwaysOnTop);
        fWindowAutoRaise->SetValue(fSettings.autoRaise);
        fWindowAutoHide->SetValue(fSettings.autoHide);

        _EnableDisableDependentItems();

        // Targets
        fAppsSort->SetTarget(be_app);
        fAppsSortTrackerFirst->SetTarget(be_app);
        fAppsShowExpanders->SetTarget(be_app);
        fAppsExpandNew->SetTarget(be_app);
        fAppsHideLabels->SetTarget(be_app);
        fAppsIconSizeSlider->SetTarget(be_app);

        fWindowLocation->SetTarget(be_app);
        fWindowAlwaysOnTop->SetTarget(be_app);
        fWindowAutoRaise->SetTarget(be_app);
        fWindowAutoHide->SetTarget(be_app);

        const float spacing = be_control_look->DefaultItemSpacing() * 2.3;

        // Applications
        BBox* appsSettingsBox = new BBox("applications");
        appsSettingsBox->SetLabel(B_TRANSLATE("Applications"));
        appsSettingsBox->AddChild(BLayoutBuilder::Group<>()
                .AddGroup(B_VERTICAL, 0)
                        .Add(fAppsSort)
                        .Add(fAppsSortTrackerFirst)
                        .Add(fAppsShowExpanders)
                        .AddGroup(B_HORIZONTAL, 0)
                                .Add(BSpaceLayoutItem::CreateHorizontalStrut(spacing))
                                .Add(fAppsExpandNew)
                                .End()
                        .Add(fAppsHideLabels)
                        .AddGlue()
                        .Add(BSpaceLayoutItem::CreateVerticalStrut(B_USE_SMALL_SPACING))
                        .Add(fAppsIconSizeSlider)
                        .SetInsets(B_USE_DEFAULT_SPACING)
                        .End()
                .View());

        // Menu
        BBox* menuBox = new BBox("menu");
        menuBox->SetLabel(B_TRANSLATE("Menu"));
        menuBox->AddChild(BLayoutBuilder::Group<>()
                .AddGroup(B_VERTICAL, 0)
                        .AddGroup(B_HORIZONTAL, 0)
                                .AddGroup(B_VERTICAL, 0)
                                        .Add(fMenuRecentDocuments)
                                        .Add(fMenuRecentFolders)
                                        .Add(fMenuRecentApplications)
                                        .End()
                                .AddGroup(B_VERTICAL, 0)
                                        .Add(fMenuRecentDocumentCount)
                                        .Add(fMenuRecentFolderCount)
                                        .Add(fMenuRecentApplicationCount)
                                        .End()
                                .End()
                                .Add(BSpaceLayoutItem::CreateVerticalStrut(
                                        B_USE_SMALL_SPACING))
                                .Add(new BButton(B_TRANSLATE("Edit in Tracker"
                                        B_UTF8_ELLIPSIS), new BMessage(kEditInTracker)))
                        .AddGlue()
                        .SetInsets(B_USE_DEFAULT_SPACING)
                        .End()
                .View());

        // Window
        BBox* windowSettingsBox = new BBox("window");
        windowSettingsBox->SetLabel(B_TRANSLATE("Window"));
        windowSettingsBox->AddChild(BLayoutBuilder::Group<>()
                .SetExplicitMaxSize(BSize(B_SIZE_UNLIMITED, B_SIZE_UNSET))
                .AddGroup(B_HORIZONTAL, 0)
                        .Add(fWindowLocation)
                        .AddGroup(B_VERTICAL, 0)
                                .AddGlue()
                                .Add(fWindowAlwaysOnTop)
                                .Add(fWindowAutoRaise)
                                .Add(fWindowAutoHide)
                                .AddGlue()
                        .End()
                        .SetInsets(B_USE_DEFAULT_SPACING)
                .End()
        .View());

        // Action Buttons
        fDefaultsButton = new BButton(B_TRANSLATE("Defaults"),
                new BMessage(kDefaults));
        fRevertButton = new BButton(B_TRANSLATE("Revert"),
                new BMessage(kRevert));

        // Layout
        BLayoutBuilder::Group<>(this)
                .AddGroup(B_VERTICAL, B_USE_DEFAULT_SPACING)
                        .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
                                .Add(appsSettingsBox)
                                .AddGroup(B_VERTICAL, B_USE_SMALL_SPACING)
                                        .Add(menuBox)
                                        .Add(windowSettingsBox)
                                .End()
                        .End()
                        .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING)
                                .Add(fDefaultsButton)
                                .Add(fRevertButton)
                                .AddGlue()
                                .End()
                        .SetInsets(B_USE_WINDOW_SPACING)
                        .End();

        BMessage windowSettings;
        BPoint where;
        if (_LoadSettings(&windowSettings) == B_OK
                && windowSettings.FindPoint("window_position", &where) == B_OK
                && BScreen(this).Frame().Contains(where)) {
                MoveTo(where);
        } else
                CenterOnScreen();
}


PreferencesWindow::~PreferencesWindow()
{
        _UpdateRecentCounts();
}


void
PreferencesWindow::MessageReceived(BMessage* message)
{
        switch (message->what) {
                case kEditInTracker:
                        OpenWithTracker(B_USER_DESKBAR_DIRECTORY);
                        break;

                case kUpdatePreferences:
                        _EnableDisableDependentItems();
                        _UpdateButtons();
                        break;

                case kUpdateRecentCounts:
                        _UpdateRecentCounts();
                        _UpdateButtons();
                        break;

                case kStateChanged:
                        _EnableDisableDependentItems();
                        break;

                case kRevert:
                        _UpdatePreferences(&fSettings);
                        break;

                case kDefaults:
                        _UpdatePreferences(
                                static_cast<TBarApp*>(be_app)->DefaultSettings());
                        break;

                default:
                        BWindow::MessageReceived(message);
                        break;
        }
}


bool
PreferencesWindow::QuitRequested()
{
        BMessage windowSettings;
        windowSettings.AddPoint("window_position", Frame().LeftTop());
        _SaveSettings(&windowSettings);

        be_app->PostMessage(kConfigQuit);

        return false;
}


void
PreferencesWindow::Show()
{
        if (IsHidden())
                SetWorkspaces(B_CURRENT_WORKSPACE);

        _UpdateButtons();

        BWindow::Show();
}


//      #pragma mark - private methods


void
PreferencesWindow::_EnableDisableDependentItems()
{
        TBarApp* barApp = static_cast<TBarApp*>(be_app);
        if (barApp->BarView()->Vertical()
                && barApp->BarView()->ExpandoState()) {
                fAppsShowExpanders->SetEnabled(true);
                fAppsExpandNew->SetEnabled(fAppsShowExpanders->Value());
        } else {
                fAppsShowExpanders->SetEnabled(false);
                fAppsExpandNew->SetEnabled(false);
        }

        fMenuRecentDocumentCount->SetEnabled(
                fMenuRecentDocuments->Value() != B_CONTROL_OFF);
        fMenuRecentFolderCount->SetEnabled(
                fMenuRecentFolders->Value() != B_CONTROL_OFF);
        fMenuRecentApplicationCount->SetEnabled(
                fMenuRecentApplications->Value() != B_CONTROL_OFF);

        fWindowAutoRaise->SetEnabled(
                fWindowAlwaysOnTop->Value() == B_CONTROL_OFF);
}


bool
PreferencesWindow::_IsDefaultable()
{
        desk_settings* settings = static_cast<TBarApp*>(be_app)->Settings();
        desk_settings* defaults = static_cast<TBarApp*>(be_app)->DefaultSettings();

        return defaults->sortRunningApps != settings->sortRunningApps
                || defaults->trackerAlwaysFirst != settings->trackerAlwaysFirst
                || defaults->superExpando != settings->superExpando
                || defaults->expandNewTeams != settings->expandNewTeams
                || defaults->hideLabels != settings->hideLabels
                || defaults->iconSize != settings->iconSize
                || defaults->recentAppsEnabled != settings->recentAppsEnabled
                || defaults->recentDocsEnabled != settings->recentDocsEnabled
                || defaults->recentFoldersEnabled
                        != settings->recentFoldersEnabled
                || defaults->recentAppsCount != settings->recentAppsCount
                || defaults->recentDocsCount != settings->recentDocsCount
                || defaults->recentFoldersCount != settings->recentFoldersCount
                || defaults->alwaysOnTop != settings->alwaysOnTop
                || defaults->autoRaise != settings->autoRaise
                || defaults->autoHide != settings->autoHide;
}


bool
PreferencesWindow::_IsRevertable()
{
        desk_settings* settings = static_cast<TBarApp*>(be_app)->Settings();

        return fSettings.sortRunningApps != settings->sortRunningApps
                || fSettings.trackerAlwaysFirst != settings->trackerAlwaysFirst
                || fSettings.superExpando != settings->superExpando
                || fSettings.expandNewTeams != settings->expandNewTeams
                || fSettings.hideLabels != settings->hideLabels
                || fSettings.iconSize != settings->iconSize
                || fSettings.recentAppsEnabled != settings->recentAppsEnabled
                || fSettings.recentDocsEnabled != settings->recentDocsEnabled
                || fSettings.recentFoldersEnabled
                        != settings->recentFoldersEnabled
                || fSettings.recentAppsCount != settings->recentAppsCount
                || fSettings.recentDocsCount != settings->recentDocsCount
                || fSettings.recentFoldersCount != settings->recentFoldersCount
                || fSettings.alwaysOnTop != settings->alwaysOnTop
                || fSettings.autoRaise != settings->autoRaise
                || fSettings.autoHide != settings->autoHide;
}


status_t
PreferencesWindow::_InitSettingsFile(BFile* file, bool write)
{
        BPath prefsPath;
        status_t status = GetDeskbarSettingsDirectory(prefsPath);
        if (status != B_OK)
                return status;

        status = prefsPath.Append(kSettingsFileName);
        if (status != B_OK)
                return status;

        if (write) {
                status = file->SetTo(prefsPath.Path(),
                        B_CREATE_FILE | B_ERASE_FILE | B_WRITE_ONLY);
        } else
                status = file->SetTo(prefsPath.Path(), B_READ_ONLY);

        return status;
}


status_t
PreferencesWindow::_LoadSettings(BMessage* settings)
{
        BFile prefsFile;
        status_t status = _InitSettingsFile(&prefsFile, false);
        if (status != B_OK)
                return status;

        return settings->Unflatten(&prefsFile);
}


status_t
PreferencesWindow::_SaveSettings(BMessage* settings)
{
        BFile prefsFile;
        status_t status = _InitSettingsFile(&prefsFile, true);
        if (status != B_OK)
                return status;

        return settings->Flatten(&prefsFile);
}


void
PreferencesWindow::_UpdateButtons()
{
        fDefaultsButton->SetEnabled(_IsDefaultable());
        fRevertButton->SetEnabled(_IsRevertable());
}


void
PreferencesWindow::_UpdatePreferences(desk_settings* settings)
{
        desk_settings* current = static_cast<TBarApp*>(be_app)->Settings();
        bool updateRecentCounts = false;

        if (current->sortRunningApps != settings->sortRunningApps) {
                fAppsSort->SetValue(settings->sortRunningApps);
                fAppsSort->Invoke();
        }
        if (current->trackerAlwaysFirst != settings->trackerAlwaysFirst) {
                fAppsSortTrackerFirst->SetValue(settings->trackerAlwaysFirst);
                fAppsSortTrackerFirst->Invoke();
        }
        if (current->superExpando != settings->superExpando) {
                fAppsShowExpanders->SetValue(settings->superExpando);
                fAppsShowExpanders->Invoke();
        }
        if (current->expandNewTeams != settings->expandNewTeams) {
                fAppsExpandNew->SetValue(settings->expandNewTeams);
                fAppsExpandNew->Invoke();
        }
        if (current->hideLabels != settings->hideLabels) {
                fAppsHideLabels->SetValue(settings->hideLabels);
                fAppsHideLabels->Invoke();
        }
        if (current->iconSize != settings->iconSize) {
                fAppsIconSizeSlider->SetValue(settings->iconSize / kIconSizeInterval);
                fAppsIconSizeSlider->Invoke();
        }
        if (current->recentDocsEnabled != settings->recentDocsEnabled) {
                fMenuRecentDocuments->SetValue(settings->recentDocsEnabled
                        ? B_CONTROL_ON : B_CONTROL_OFF);
                updateRecentCounts = true;
        }
        if (current->recentFoldersEnabled != settings->recentFoldersEnabled) {
                fMenuRecentFolders->SetValue(settings->recentFoldersEnabled
                        ? B_CONTROL_ON : B_CONTROL_OFF);
                updateRecentCounts = true;
        }
        if (current->recentAppsEnabled != settings->recentAppsEnabled) {
                fMenuRecentApplications->SetValue(settings->recentAppsEnabled
                        ? B_CONTROL_ON : B_CONTROL_OFF);
                updateRecentCounts = true;
        }
        if (current->recentDocsCount != settings->recentDocsCount) {
                fMenuRecentDocumentCount->SetValue(settings->recentDocsCount);
                updateRecentCounts = true;
        }
        if (current->recentFoldersCount != settings->recentFoldersCount) {
                fMenuRecentFolderCount->SetValue(settings->recentFoldersCount);
                updateRecentCounts = true;
        }
        if (current->recentAppsCount != settings->recentAppsCount) {
                fMenuRecentApplicationCount->SetValue(settings->recentAppsCount);
                updateRecentCounts = true;
        }
        if (current->alwaysOnTop != settings->alwaysOnTop) {
                fWindowAlwaysOnTop->SetValue(settings->alwaysOnTop);
                fWindowAlwaysOnTop->Invoke();
        }
        if (current->autoRaise != settings->autoRaise) {
                fWindowAutoRaise->SetValue(settings->autoRaise);
                fWindowAutoRaise->Invoke();
        }
        if (current->autoHide != settings->autoHide) {
                fWindowAutoHide->SetValue(settings->autoHide);
                fWindowAutoHide->Invoke();
        }

        if (updateRecentCounts)
                _UpdateRecentCounts();
}


void
PreferencesWindow::_UpdateRecentCounts()
{
        BMessage message(kUpdateRecentCounts);

        message.AddInt32("documents", fMenuRecentDocumentCount->Value());
        message.AddInt32("applications", fMenuRecentApplicationCount->Value());
        message.AddInt32("folders", fMenuRecentFolderCount->Value());

        message.AddBool("documentsEnabled", fMenuRecentDocuments->Value());
        message.AddBool("applicationsEnabled", fMenuRecentApplications->Value());
        message.AddBool("foldersEnabled", fMenuRecentFolders->Value());

        be_app->PostMessage(&message);

        _EnableDisableDependentItems();
}