Selaa lähdekoodia

Login dialog added

Miklos Espak 12 vuotta sitten
vanhempi
commit
890b8ba34b

+ 11 - 0
Libs/XNAT/Widgets/CMakeLists.txt

@@ -7,19 +7,30 @@ project(CTKXNATWidgets)
 set(KIT_export_directive "CTK_XNAT_WIDGETS_EXPORT")
 
 set(KIT_SRCS
+  ctkXnatLoginDialog.cpp
+  ctkXnatLoginProfile.cpp
   ctkXnatProjectListModel.cpp
+  ctkXnatSettings.cpp
   )
 
 # Files which should be processed by Qts moc
 set(KIT_MOC_SRCS
+  ctkXnatLoginDialog.h
   ctkXnatProjectListModel.h
 )
 
 
+# UI forms
+set(KIT_UI_FORMS
+  ctkXnatLoginDialog.ui
+)
+
 # Resources
 set(KIT_resources
 )
 
+set(QT_USE_QTSCRIPT 1)
+
 # Target libraries - See CMake/ctkFunctionGetTargetLibraries.cmake
 # The following macro will read the target libraries from the file 'target_libraries.cmake'
 set(KIT_target_libraries)

+ 393 - 0
Libs/XNAT/Widgets/ctkXnatLoginDialog.cpp

@@ -0,0 +1,393 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) University College London,
+    Centre for Medical Image Computing
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+#include "ctkXnatLoginDialog.h"
+
+#include <QListView>
+#include <QMap>
+#include <QMessageBox>
+#include <QStringListModel>
+#include <QTimer>
+
+#include <ctkXnatConnection.h>
+#include <ctkXnatException.h>
+#include "ctkXnatLoginProfile.h"
+#include "ctkXnatSettings.h"
+
+class ctkXnatLoginDialogPrivate
+{
+public:
+  ctkXnatLoginDialogPrivate(ctkXnatConnectionFactory& f)
+  : factory(f)
+  {
+  }
+
+  ctkXnatSettings* settings;
+
+  ctkXnatConnectionFactory& factory;
+  ctkXnatConnection* connection;
+
+  QMap<QString, ctkXnatLoginProfile*> profiles;
+
+  QStringListModel model;
+  QStringList profileNames;
+
+  bool dirty;
+};
+
+ctkXnatLoginDialog::ctkXnatLoginDialog(ctkXnatConnectionFactory& f, QWidget* parent, Qt::WindowFlags flags)
+: QDialog(parent, flags)
+, ui(0)
+, d_ptr(new ctkXnatLoginDialogPrivate(f))
+{
+  Q_D(ctkXnatLoginDialog);
+
+  // initialize data members
+  d->settings = 0;
+  d->connection = 0;
+  d->dirty = false;
+
+  if (!ui)
+    {
+    // Create UI
+    ui = new Ui::ctkXnatLoginDialog();
+    ui->setupUi(this);
+
+    QItemSelectionModel* oldSelectionModel = ui->lstProfiles->selectionModel();
+    ui->lstProfiles->setModel(&d->model);
+    delete oldSelectionModel;
+    ui->lstProfiles->setSelectionMode(QAbstractItemView::SingleSelection);
+    ui->lstProfiles->setSelectionRectVisible(false);
+    ui->lstProfiles->setEditTriggers(QAbstractItemView::NoEditTriggers);
+    ui->btnSave->setEnabled(false);
+
+    // Create connections after setting defaults, so you don't trigger stuff when setting defaults.
+    createConnections();
+    }
+}
+
+ctkXnatLoginDialog::~ctkXnatLoginDialog()
+{
+  Q_D(ctkXnatLoginDialog);
+
+  foreach (ctkXnatLoginProfile* profile, d->profiles)
+    {
+    delete profile;
+    }
+
+  if (ui)
+    {
+    delete ui;
+    }
+}
+
+void ctkXnatLoginDialog::createConnections()
+{
+  connect(ui->edtProfileName, SIGNAL(textChanged(const QString&)), this, SLOT(onFieldChanged()));
+  connect(ui->edtServerUri, SIGNAL(textChanged(const QString&)), this, SLOT(onFieldChanged()));
+  connect(ui->edtUserName, SIGNAL(textChanged(const QString&)), this, SLOT(onFieldChanged()));
+  // Password change is not listened to.
+//  connect(ui->edtPassword, SIGNAL(textChanged(const QString&)), this, SLOT(onFieldChanged()));
+  connect(ui->cbxDefaultProfile, SIGNAL(toggled(bool)), this, SLOT(onFieldChanged()));
+  connect(ui->lstProfiles->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)),
+      this, SLOT(onCurrentProfileChanged(const QModelIndex&)));
+}
+
+ctkXnatSettings* ctkXnatLoginDialog::settings() const
+{
+  Q_D(const ctkXnatLoginDialog);
+
+  return d->settings;
+}
+
+void ctkXnatLoginDialog::setSettings(ctkXnatSettings* settings)
+{
+  Q_D(ctkXnatLoginDialog);
+  d->settings = settings;
+  d->profiles = d->settings->getLoginProfiles();
+
+  d->profileNames = d->profiles.keys();
+  d->profileNames.sort();
+  d->model.setStringList(d->profileNames);
+  
+  ctkXnatLoginProfile* defaultProfile = d->settings->getDefaultLoginProfile();
+
+  if (defaultProfile)
+    {
+    int profileNumber = d->profileNames.indexOf(defaultProfile->name());
+    QModelIndex index = d->model.index(profileNumber);
+    if (index.isValid())
+      {
+      ui->lstProfiles->setCurrentIndex(index);
+      }
+    ui->edtPassword->setFocus();
+    }
+}
+
+ctkXnatConnection* ctkXnatLoginDialog::getConnection()
+{
+  Q_D(ctkXnatLoginDialog);
+  return d->connection;
+}
+
+void ctkXnatLoginDialog::accept()
+{
+  Q_D(ctkXnatLoginDialog);
+
+  QString url = ui->edtServerUri->text();
+  if ( url.isEmpty() )
+    {
+    QMessageBox::warning(this, tr("Missing XNAT server URI"), tr("Please enter XNAT server URI."));
+    ui->edtServerUri->selectAll();
+    ui->edtServerUri->setFocus();
+    return;
+    }
+
+  QString userName = ui->edtUserName->text();
+  if ( userName.isEmpty() )
+    {
+    QMessageBox::warning(this, tr("Missing user name"), tr("Please enter user name."));
+    ui->edtUserName->selectAll();
+    ui->edtUserName->setFocus();
+    return;
+    }
+
+  if (d->dirty)
+    {
+    const QString& profileName = ui->edtProfileName->text();
+    if (askToSaveProfile(profileName))
+      {
+      saveProfile(profileName);
+      }
+    }
+
+  QString password = ui->edtPassword->text();
+
+  // create XNAT connection
+  try
+    {
+    d->connection = d->factory.makeConnection(url.toAscii().constData(), userName.toAscii().constData(),
+                                        password.toAscii().constData());
+    }
+  catch (ctkXnatException& e)
+    {
+    QMessageBox::warning(this, tr("Invalid Login Error"), tr(e.what()));
+    ui->edtServerUri->selectAll();
+    ui->edtServerUri->setFocus();
+    return;
+    }
+
+  QDialog::accept();
+}
+
+void ctkXnatLoginDialog::onCurrentProfileChanged(const QModelIndex& currentIndex)
+{
+  Q_D(ctkXnatLoginDialog);
+
+  if (!currentIndex.isValid())
+    {
+    loadProfile();
+    return;
+    }
+
+  int originalIndexRow = currentIndex.row();
+  QString newProfileName = d->profileNames[currentIndex.row()];
+  ctkXnatLoginProfile* profile = d->profiles[newProfileName];
+
+  bool newProfileSaved = false;
+  if (d->dirty)
+    {
+    QString profileName = ui->edtProfileName->text();
+    if (askToSaveProfile(profileName))
+      {
+      saveProfile(profileName);
+      newProfileSaved = true;
+      }
+    }
+
+  loadProfile(*profile);
+
+  d->dirty = false;
+  ui->btnSave->setEnabled(false);
+  ui->btnDelete->setEnabled(true);
+
+  // Ugly hack. If the current index has changed because of saving the edited element
+  // then we have to select it again, but a bit later. If we select it right here then
+  // both the original index and the new index are selected. (Even if single selection
+  // is set.)
+  if (newProfileSaved && originalIndexRow != currentIndex.row())
+    {
+    QTimer::singleShot(0, this, SLOT(resetLstProfilesCurrentIndex()));
+    }
+}
+
+void ctkXnatLoginDialog::resetLstProfilesCurrentIndex()
+{
+  // Yes, this is really needed. See the comment above.
+  ui->lstProfiles->setCurrentIndex(ui->lstProfiles->currentIndex());
+}
+
+bool ctkXnatLoginDialog::askToSaveProfile(const QString& profileName)
+{
+  QString question = QString(
+      "You have not saved the changes of the %1 profile.\n"
+      "Do you want to save them now?").arg(profileName);
+  QMessageBox::StandardButton answer = QMessageBox::question(this, "", question, QMessageBox::Yes | QMessageBox::No,
+                                QMessageBox::Yes);
+
+  return answer == QMessageBox::Yes;
+}
+
+void ctkXnatLoginDialog::saveProfile(const QString& profileName)
+{
+  Q_D(ctkXnatLoginDialog);
+  
+  ctkXnatLoginProfile* profile = d->profiles[profileName];
+  bool oldProfileWasDefault = profile && profile->isDefault();
+  if (!profile)
+    {
+    profile = new ctkXnatLoginProfile();
+    d->profiles[profileName] = profile;
+    int profileNumber = d->profileNames.size();
+    
+    // Insertion into the profile name list and the listView (ascending order)
+    int idx = 0;
+    while (idx < profileNumber && QString::localeAwareCompare(profileName, d->profileNames[idx]) > 0)
+      {
+      ++idx;
+      }
+    d->profileNames.insert(idx, profileName);
+    d->model.insertRow(idx);
+    d->model.setData(d->model.index(idx), profileName);
+    }
+  
+  storeProfile(*profile);
+  
+  // If the profile is to be default then remove the default flag from the other profiles.
+  // This code assumes that the newly created profiles are not default.
+  if (profile->isDefault() && !oldProfileWasDefault)
+    {
+    foreach (ctkXnatLoginProfile* otherProfile, d->profiles.values())
+      {
+      const QString& otherProfileName = otherProfile->name();
+      if (otherProfileName != profileName && otherProfile->isDefault())
+        {
+        otherProfile->setDefault(false);
+        d->settings->setLoginProfile(otherProfileName, otherProfile);
+        }
+      }
+    }
+  
+  d->settings->setLoginProfile(profileName, profile);
+  d->dirty = false;
+  ui->btnSave->setEnabled(false);
+}
+
+void ctkXnatLoginDialog::on_btnSave_clicked()
+{
+  Q_D(ctkXnatLoginDialog);
+  QString editedProfileName = ui->edtProfileName->text();
+
+  bool selectSavedProfile = true;
+  
+  QModelIndex currentIndex = ui->lstProfiles->currentIndex();
+  if (currentIndex.isValid())
+    {
+    QString selectedProfileName = d->profileNames[currentIndex.row()];
+    if (editedProfileName == selectedProfileName)
+      {
+      selectSavedProfile = false;
+      }
+    }
+
+  saveProfile(editedProfileName);
+
+  if (selectSavedProfile)
+    {
+    int editedProfileNumber = d->profileNames.indexOf(editedProfileName);
+    QModelIndex editedProfileIndex = d->model.index(editedProfileNumber, 0);
+    ui->lstProfiles->setCurrentIndex(editedProfileIndex);
+    }
+}
+
+void ctkXnatLoginDialog::blockSignalsOfFields(bool value)
+{
+  ui->edtProfileName->blockSignals(value);
+  ui->edtServerUri->blockSignals(value);
+  ui->edtUserName->blockSignals(value);
+  ui->edtPassword->blockSignals(value);
+  ui->cbxDefaultProfile->blockSignals(value);
+}
+
+void ctkXnatLoginDialog::loadProfile(const ctkXnatLoginProfile& profile)
+{
+  blockSignalsOfFields(true);
+
+  ui->edtProfileName->setText(profile.name());
+  ui->edtServerUri->setText(profile.serverUri());
+  ui->edtUserName->setText(profile.userName());
+  ui->edtPassword->setText(profile.password());
+  ui->cbxDefaultProfile->setChecked(profile.isDefault());
+
+  blockSignalsOfFields(false);
+}
+
+void ctkXnatLoginDialog::storeProfile(ctkXnatLoginProfile& profile)
+{
+  profile.setName(ui->edtProfileName->text());
+  profile.setServerUri(ui->edtServerUri->text());
+  profile.setUserName(ui->edtUserName->text());
+  profile.setPassword(ui->edtPassword->text());
+  profile.setDefault(ui->cbxDefaultProfile->isChecked());
+}
+
+void ctkXnatLoginDialog::on_btnDelete_clicked()
+{
+  Q_D(ctkXnatLoginDialog);
+
+  QString profileName = ui->edtProfileName->text();
+
+  int idx = d->profileNames.indexOf(profileName);
+  d->model.removeRow(idx);
+  d->profileNames.removeAt(idx);
+  delete d->profiles.take(profileName);
+
+  if (d->profiles.empty())
+    {
+    ui->btnDelete->setEnabled(false);
+    ui->edtProfileName->setFocus();
+    }
+  
+  d->settings->removeLoginProfile(profileName);
+}
+
+void ctkXnatLoginDialog::on_edtProfileName_textChanged(const QString& text)
+{
+  ui->lstProfiles->clearSelection();
+  ui->btnDelete->setEnabled(false);
+}
+
+void ctkXnatLoginDialog::onFieldChanged()
+{
+  Q_D(ctkXnatLoginDialog);
+  d->dirty = true;
+  ui->btnSave->setEnabled(true);
+}

+ 81 - 0
Libs/XNAT/Widgets/ctkXnatLoginDialog.h

@@ -0,0 +1,81 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) University College London,
+    Centre for Medical Image Computing
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+#ifndef ctkXnatLoginDialog_h
+#define ctkXnatLoginDialog_h
+
+#include <QDialog>
+
+#include "ctkXNATWidgetsExport.h"
+
+#include "ui_ctkXnatLoginDialog.h"
+
+#include <ctkXnatConnectionFactory.h>
+#include "ctkXnatLoginProfile.h"
+
+class ctkXnatConnection;
+class ctkXnatLoginDialogPrivate;
+class ctkXnatSettings;
+
+class CTK_XNAT_WIDGETS_EXPORT ctkXnatLoginDialog : public QDialog
+{
+  Q_OBJECT
+
+public:
+  explicit ctkXnatLoginDialog(ctkXnatConnectionFactory& f, QWidget* parent = 0, Qt::WindowFlags flags = 0);
+  virtual ~ctkXnatLoginDialog();
+
+  ctkXnatSettings* settings() const;
+  void setSettings(ctkXnatSettings* settings);
+
+  ctkXnatConnection* getConnection();
+
+  virtual void accept();
+
+private slots:
+
+  void on_btnSave_clicked();
+  void on_btnDelete_clicked();
+  void on_edtProfileName_textChanged(const QString& text);
+  void onFieldChanged();
+  void onCurrentProfileChanged(const QModelIndex& current);
+  void resetLstProfilesCurrentIndex();
+
+private:
+  void createConnections();
+  void blockSignalsOfFields(bool value);
+
+  void saveProfile(const QString& profileName);
+  bool askToSaveProfile(const QString& profileName);
+  void loadProfile(const ctkXnatLoginProfile& profile = ctkXnatLoginProfile());
+  void storeProfile(ctkXnatLoginProfile& profile);
+
+  /// \brief All the controls for the main view part.
+  Ui::ctkXnatLoginDialog* ui;
+
+  /// \brief d pointer of the pimpl pattern
+  QScopedPointer<ctkXnatLoginDialogPrivate> d_ptr;
+
+  Q_DECLARE_PRIVATE(ctkXnatLoginDialog);
+  Q_DISABLE_COPY(ctkXnatLoginDialog);
+};
+
+#endif

+ 230 - 0
Libs/XNAT/Widgets/ctkXnatLoginDialog.ui

@@ -0,0 +1,230 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ctkXnatLoginDialog</class>
+ <widget class="QDialog" name="ctkXnatLoginDialog">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>571</width>
+    <height>295</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Login Profiles</string>
+  </property>
+  <layout class="QHBoxLayout" name="horizontalLayout_2" stretch="1,3">
+   <item>
+    <widget class="QWidget" name="wgtProfiles" native="true">
+     <layout class="QVBoxLayout" name="verticalLayout">
+      <property name="margin">
+       <number>0</number>
+      </property>
+      <item>
+       <widget class="QLabel" name="lblProfiles">
+        <property name="text">
+         <string>Profiles:</string>
+        </property>
+        <property name="buddy">
+         <cstring>lstProfiles</cstring>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QListView" name="lstProfiles">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Preferred" vsizetype="Expanding">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+       </widget>
+      </item>
+      <item>
+       <widget class="QWidget" name="widget" native="true">
+        <layout class="QHBoxLayout" name="horizontalLayout_3">
+         <property name="margin">
+          <number>0</number>
+         </property>
+         <item>
+          <widget class="QPushButton" name="btnSave">
+           <property name="text">
+            <string>Save</string>
+           </property>
+          </widget>
+         </item>
+         <item>
+          <widget class="QPushButton" name="btnDelete">
+           <property name="text">
+            <string>Delete</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
+    <widget class="QWidget" name="widget_2" native="true">
+     <property name="sizePolicy">
+      <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+       <horstretch>0</horstretch>
+       <verstretch>0</verstretch>
+      </sizepolicy>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_2">
+      <property name="margin">
+       <number>0</number>
+      </property>
+      <item>
+       <widget class="QWidget" name="wgtFields" native="true">
+        <property name="sizePolicy">
+         <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+          <horstretch>0</horstretch>
+          <verstretch>0</verstretch>
+         </sizepolicy>
+        </property>
+        <layout class="QFormLayout" name="formLayout">
+         <property name="fieldGrowthPolicy">
+          <enum>QFormLayout::AllNonFixedFieldsGrow</enum>
+         </property>
+         <property name="margin">
+          <number>0</number>
+         </property>
+         <item row="0" column="0" colspan="2">
+          <widget class="QLabel" name="label">
+           <property name="text">
+            <string>Login information:</string>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="0">
+          <widget class="QLabel" name="lblProfileName">
+           <property name="text">
+            <string>Profile name:</string>
+           </property>
+           <property name="buddy">
+            <cstring>edtProfileName</cstring>
+           </property>
+          </widget>
+         </item>
+         <item row="1" column="1">
+          <widget class="QLineEdit" name="edtProfileName"/>
+         </item>
+         <item row="2" column="0">
+          <widget class="QLabel" name="lblServerUri">
+           <property name="text">
+            <string>Server URI:</string>
+           </property>
+           <property name="buddy">
+            <cstring>edtServerUri</cstring>
+           </property>
+          </widget>
+         </item>
+         <item row="2" column="1">
+          <widget class="QLineEdit" name="edtServerUri"/>
+         </item>
+         <item row="3" column="0">
+          <widget class="QLabel" name="lblUserName">
+           <property name="text">
+            <string>User name:</string>
+           </property>
+           <property name="buddy">
+            <cstring>edtUserName</cstring>
+           </property>
+          </widget>
+         </item>
+         <item row="3" column="1">
+          <widget class="QLineEdit" name="edtUserName"/>
+         </item>
+         <item row="4" column="0">
+          <widget class="QLabel" name="lblPassword">
+           <property name="text">
+            <string>Password:</string>
+           </property>
+           <property name="buddy">
+            <cstring>edtPassword</cstring>
+           </property>
+          </widget>
+         </item>
+         <item row="4" column="1">
+          <widget class="QLineEdit" name="edtPassword">
+           <property name="echoMode">
+            <enum>QLineEdit::Password</enum>
+           </property>
+          </widget>
+         </item>
+         <item row="5" column="1">
+          <widget class="QCheckBox" name="cbxDefaultProfile">
+           <property name="text">
+            <string>Use as default</string>
+           </property>
+          </widget>
+         </item>
+        </layout>
+       </widget>
+      </item>
+      <item>
+       <widget class="QDialogButtonBox" name="bbxDialog">
+        <property name="orientation">
+         <enum>Qt::Horizontal</enum>
+        </property>
+        <property name="standardButtons">
+         <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+        </property>
+       </widget>
+      </item>
+     </layout>
+    </widget>
+   </item>
+  </layout>
+ </widget>
+ <tabstops>
+  <tabstop>edtProfileName</tabstop>
+  <tabstop>edtServerUri</tabstop>
+  <tabstop>edtUserName</tabstop>
+  <tabstop>edtPassword</tabstop>
+  <tabstop>cbxDefaultProfile</tabstop>
+  <tabstop>btnSave</tabstop>
+  <tabstop>btnDelete</tabstop>
+  <tabstop>lstProfiles</tabstop>
+  <tabstop>bbxDialog</tabstop>
+ </tabstops>
+ <resources/>
+ <connections>
+  <connection>
+   <sender>bbxDialog</sender>
+   <signal>accepted()</signal>
+   <receiver>ctkXnatLoginDialog</receiver>
+   <slot>accept()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>248</x>
+     <y>254</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>157</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+  <connection>
+   <sender>bbxDialog</sender>
+   <signal>rejected()</signal>
+   <receiver>ctkXnatLoginDialog</receiver>
+   <slot>reject()</slot>
+   <hints>
+    <hint type="sourcelabel">
+     <x>316</x>
+     <y>260</y>
+    </hint>
+    <hint type="destinationlabel">
+     <x>286</x>
+     <y>274</y>
+    </hint>
+   </hints>
+  </connection>
+ </connections>
+</ui>

+ 81 - 0
Libs/XNAT/Widgets/ctkXnatLoginProfile.cpp

@@ -0,0 +1,81 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) University College London,
+    Centre for Medical Image Computing
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+#include "ctkXnatLoginProfile.h"
+
+ctkXnatLoginProfile::ctkXnatLoginProfile()
+{
+  m_default = false;
+}
+
+ctkXnatLoginProfile::~ctkXnatLoginProfile()
+{
+}
+
+QString ctkXnatLoginProfile::name() const
+{
+  return m_name;
+}
+
+void ctkXnatLoginProfile::setName(const QString& name)
+{
+  m_name = name;
+}
+
+QString ctkXnatLoginProfile::serverUri() const
+{
+  return m_serverUri;
+}
+
+void ctkXnatLoginProfile::setServerUri(const QString& serverUri)
+{
+  m_serverUri = serverUri;
+}
+
+QString ctkXnatLoginProfile::userName() const
+{
+  return m_userName;
+}
+
+void ctkXnatLoginProfile::setUserName(const QString& userName)
+{
+  m_userName = userName;
+}
+
+QString ctkXnatLoginProfile::password() const
+{
+  return m_password;
+}
+
+void ctkXnatLoginProfile::setPassword(const QString& password)
+{
+  m_password = password;
+}
+
+bool ctkXnatLoginProfile::isDefault() const
+{
+  return m_default;
+}
+
+void ctkXnatLoginProfile::setDefault(const bool& default_)
+{
+  m_default = default_;
+}

+ 58 - 0
Libs/XNAT/Widgets/ctkXnatLoginProfile.h

@@ -0,0 +1,58 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) University College London,
+    Centre for Medical Image Computing
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+#ifndef ctkXnatLoginProfile_h
+#define ctkXnatLoginProfile_h
+
+#include "ctkXNATWidgetsExport.h"
+
+#include <QString>
+
+class CTK_XNAT_WIDGETS_EXPORT ctkXnatLoginProfile
+{
+public:
+  explicit ctkXnatLoginProfile();
+  virtual ~ctkXnatLoginProfile();
+
+  QString name() const;
+  void setName(const QString& profileName);
+
+  QString serverUri() const;
+  void setServerUri(const QString& serverUri);
+
+  QString userName() const;
+  void setUserName(const QString& userName);
+
+  QString password() const;
+  void setPassword(const QString& password);
+
+  bool isDefault() const;
+  void setDefault(const bool& default_);
+
+private:
+  QString m_name;
+  QString m_serverUri;
+  QString m_userName;
+  QString m_password;
+  bool m_default;
+};
+
+#endif

+ 63 - 0
Libs/XNAT/Widgets/ctkXnatSettings.cpp

@@ -0,0 +1,63 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) University College London,
+    Centre for Medical Image Computing
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+#include "ctkXnatSettings.h"
+
+#include <QDir>
+#include <QFileInfo>
+#include <QUuid>
+
+#include "ctkXnatLoginProfile.h"
+
+ctkXnatSettings::ctkXnatSettings()
+{
+}
+
+ctkXnatSettings::~ctkXnatSettings()
+{
+}
+
+QString ctkXnatSettings::getWorkSubdirectory() const
+{
+  // set work directory name
+  QDir workDir;
+  QString workDirName = getDefaultWorkDirectory();
+  if ( !workDirName.isEmpty() )
+    {
+    workDir = QDir(workDirName);
+    }
+
+  // generate random name for subdirectory
+  QString subdir = QUuid::createUuid().toString();
+
+  // create subdirectory in work directory
+  bool subdirCreated = workDir.mkdir(subdir);
+
+  // check whether subdirectory was created
+  if ( !subdirCreated )
+    {
+    // display error message
+    return QString();
+    }
+
+  // return full path of subdirectory
+  return QFileInfo(workDir, subdir).absoluteFilePath();
+}

+ 58 - 0
Libs/XNAT/Widgets/ctkXnatSettings.h

@@ -0,0 +1,58 @@
+/*=============================================================================
+
+  Library: CTK
+
+  Copyright (c) University College London,
+    Centre for Medical Image Computing
+
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+=============================================================================*/
+
+#ifndef ctkXnatSettings_h
+#define ctkXnatSettings_h
+
+#include <QMap>
+#include <QString>
+
+#include "ctkXNATWidgetsExport.h"
+
+class ctkXnatLoginProfile;
+
+class CTK_XNAT_WIDGETS_EXPORT ctkXnatSettings
+{
+public:
+  virtual QString getDefaultDirectory() const = 0;
+  virtual void setDefaultDirectory(const QString& dir) = 0;
+
+  virtual QString getDefaultWorkDirectory() const = 0;
+  virtual void setDefaultWorkDirectory(const QString& workDir) = 0;
+
+  virtual QString getWorkSubdirectory() const;
+
+  virtual QMap<QString, ctkXnatLoginProfile*> getLoginProfiles() const = 0;
+  virtual void setLoginProfiles(QMap<QString, ctkXnatLoginProfile*> loginProfiles) = 0;
+
+  virtual ctkXnatLoginProfile* getLoginProfile(QString profileName) const = 0;
+  virtual void setLoginProfile(QString profileName, ctkXnatLoginProfile*) = 0;
+
+  virtual void removeLoginProfile(QString profileName) = 0;
+
+  virtual ctkXnatLoginProfile* getDefaultLoginProfile() const = 0;
+
+protected:
+  explicit ctkXnatSettings();
+  virtual ~ctkXnatSettings();
+};
+
+#endif