summaryrefslogtreecommitdiff
path: root/src/modinfodialogtab.h
blob: 3f98314a7c4750f2848d8a23d3d77ffc2533a04f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
#ifndef MODINFODIALOGTAB_H
#define MODINFODIALOGTAB_H

#include "modinfo.h"
#include <QObject>

namespace MOShared { class FilesOrigin; }
namespace Ui { class ModInfoDialog; }

class Settings;
class OrganizerCore;

// base class for all tabs in the mod info dialog
//
// when the dialog is opened or when next/previous is clicked, the sequence is:
// setMod(), clear(), feedFile() an update()
//
// when the dialog is closed, canClose() is called on all tabs
//
// when a tab is selected for the first time for the current mod,
// firstActivation() is called; this is used by NexusTab to refresh stuff
//
// when the dialog is first shown, restoreState() is called on all tabs and
// saveState() is called when the dialog is closed
//
// there isn't a good framework for keyboard shortcuts because only the delete
// key is used for now, which calls deletedRequested() on all tabs until one
// returns true
//
// each tab override canHandleSeparators() and canHandleUnmanaged() to return
// true if they can handle separators or unmanaged mods; if these return false
// (which they do by default), the tabs will be removed from the widget entirely
//
// when tabs modify the origin and call emitOriginModified() (such as the
// conflicts tabs), all tabs that return true in usesOriginFiles() will go
// through the full update sequence as above
//
// tabs can call emitModOpen() to request showing a different mod
//
// hasDataChanged() should be called when a tab goes from having data to being
// empty or vice versa; this will update the tab text color
//
class ModInfoDialogTab : public QObject
{
  Q_OBJECT;

public:
  ModInfoDialogTab(const ModInfoDialogTab&) = delete;
  ModInfoDialogTab& operator=(const ModInfoDialogTab&) = delete;
  ModInfoDialogTab(ModInfoDialogTab&&) = default;
  ModInfoDialogTab& operator=(ModInfoDialogTab&&) = default;
  virtual ~ModInfoDialogTab() = default;

  // called by ModInfoDialog every time this tab is selected; this will call
  // firstActivation() the first time it's called, until resetFirstActivation()
  // is called
  //
  void activated();

  // called by ModInfoDialog when the selected mod has changed, to make sure
  // activated() will call firstActivation() next time
  //
  void resetFirstActivation();


  // called when the selected mod changed, `mod` can never be empty, but
  // `origin` can (if the mod is not active, for example)
  //
  // derived classes can override this to connect to events on the mod for
  // examples (see NexusTab), but must call the base class implementation
  //
  virtual void setMod(ModInfo::Ptr mod, MOShared::FilesOrigin* origin);

  // this tab should clear its user interface; clear() will always be called
  // before feedFile() and update()
  //
  virtual void clear() = 0;

  // the dialog will go through each file in the mod and call feedFile()
  // with it on all tabs; if a tab handles the file, it should return true to
  // prevent other tabs from displaying it
  //
  // this prevents individual tabs from having to go through the filesystem
  // independently, which would kill performance, but it cannot be the only way
  // to update tabs because some of them don't actually use files (like
  // NotesTab) or they use the internal structures such as `FilesOrigin` (like
  // ConflictsTab)
  //
  // once all the files have been fed to tabs, update() will be called; tabs
  // that need to do additional work after being fed files, or tabs that are
  // unable to use feedFile() at all can use update() to do work
  //
  virtual bool feedFile(const QString& rootPath, const QString& filename);

  // called after all the files on the filesystem have been sent through
  // feedFile()
  //
  // this can be used to do a final processing of the files handled by
  // feedFile() (ImagesTab will set up the scrollbars, for example) or something
  // completely different (CategoriesTab will fill up the lists)
  //
  virtual void update();

  // called when a tab is visible for the first time for the current mod, can
  // be used to do expensive work that's not worth doing until the tab is
  // actually selected by the user
  //
  virtual void firstActivation();

  //
  virtual bool canClose();
  virtual void saveState(Settings& s);
  virtual void restoreState(const Settings& s);

  virtual bool deleteRequested();

  virtual bool canHandleSeparators() const;
  virtual bool canHandleUnmanaged() const;
  virtual bool usesOriginFiles() const;

  ModInfo::Ptr mod() const;
  MOShared::FilesOrigin* origin() const;

  int tabID() const;
  bool hasData() const;

signals:
  void originModified(int originID);
  void modOpen(QString name);
  void hasDataChanged();
  void wantsFocus();

protected:
  Ui::ModInfoDialog* ui;

  ModInfoDialogTab(
    OrganizerCore& oc, PluginContainer& plugin,
    QWidget* parent, Ui::ModInfoDialog* ui, int id);

  OrganizerCore& core();
  PluginContainer& plugin();

  QWidget* parentWidget();

  void emitOriginModified();
  void emitModOpen(QString name);
  void setHasData(bool b);

  void setFocus();

  // this needs to be a template because saveState() and restoreState() are
  // not in QWidget, but they're in various widgets
  //
  template <class Widget>
  void saveWidgetState(QSettings& s, Widget* w)
  {
    s.setValue(settingName(w), w->saveState());
  }

  template <class Widget>
  void restoreWidgetState(const QSettings& s, Widget* w)
  {
    if (s.contains(settingName(w))) {
      w->restoreState(s.value(settingName(w)).toByteArray());
    }
  }

private:
  OrganizerCore& m_core;
  PluginContainer& m_plugin;
  QWidget* m_parent;
  ModInfo::Ptr m_mod;
  MOShared::FilesOrigin* m_origin;
  int m_tabID;
  bool m_hasData;
  bool m_firstActivation;


  QString settingName(QWidget* w)
  {
    return "geometry/modinfodialog_" + w->objectName();
  }
};


class NotesTab : public ModInfoDialogTab
{
public:
  NotesTab(
    OrganizerCore& oc, PluginContainer& plugin,
    QWidget* parent, Ui::ModInfoDialog* ui, int index);

  void clear() override;
  void update() override;
  bool canHandleSeparators() const override;
  bool usesOriginFiles() const override;

private:
  void onComments();
  void onNotes();
  void checkHasData();
};

#endif // MODINFODIALOGTAB_H