[Companion] Restore model drag/drop/compare, many UI improvements to main and sub-windows. (#4769)
* [Companion] Refactor MdiChild & TreeModel, restore drag/drop, and more:
* Add full drag-and-drop functions to the models list, including model copy, move, and insert actions (also between windows);
* Add general radio settings copy/paste across file windows;
* Full MIME support with distinct types for various data (eg. models vs. general settings), also add new type for metadata (header);
* Copied/cut models can now be inserted in any spot (similar to drop-insert);
* Deleted/cut models can now be removed entirely (including the empty slot) for all radio types (any models after the removed one move up, like for Horus) (may need to make this an option);
* Empty model slots can now also be removed (any models after the removed slot move up) (but total # of slots is still maintained);
* Default model can be deleted (new default is picked);
* Completely revamp the actions system (copy/paste/edit/etc) to make it fully functional:
* Actions exist in the window, not just context menu, therefore all keyboard shortcuts actually work, w/out any duplicated code;
* Provides access from MainWindow in a comprehensive manner: all actions are available and properly updated based on selections in current editor window, w/out awkward event monitoring;
* More actions available from context menu, and somewhat reorganized;
* Also show all actions on mini toolbars right in the MDI window (possibly make optional later);
* Updated English localization file to account for plurals usage in new action item names (other languages need to be updated);
* Keyboard navigation in the model list now works/practical;
* MDI window uses all available height by default;
* Newly added models/categories are selected and highlighted; Category names are immediately editable; Model editor is/can be launched if wizard is not (need to make option);
* MainWindow updates:
* Edit toolbar and menu now showing all available edit actions, with proper context;
* File menu properly updates with all "general" radio actions: edit/copy/paste settings and simulate radio;
* Added Windows menu showing list of open windows (eg. to switch when full screen) and tabbed window option (need to make persistent setting);
* Immediate update of icon size preference;
* Do not delete toolbars when re-translating UI (they don't lose assigned position);
* [Companion] Restore model compare functionality by using new MIME data exchange system; Add ability to compare any number of models at a time; Multi-model printer now shows custom hardware settings for each dropped model (even from different radios).
* [Companion] Add missing monoblue & monowhite theme sdsync icons.
* [Companion] Model wizard starts with default model name populated.
* [Companion] Properly re-translate UI instead of recreating all the actions/etc.; Tell Mac specifically where to put our actions (seems to make it happier too).
* [Companion] Further improve handling of Window menu items/detection of sub-window changes.
* [Companion] Add file-close action.
* [Companion] Add user options and save preferences:
* Option to use wizard, open editor, or do nothing on new model creation;
* Option to remove empty model slots when deleting/moving models (in non-Horus radios);
* Options to hide toolbars in document windows (r-click on TBs);
* Save/restore size of document windows, and maximized/tabbed state;
* Save/restore size/position of model compare window;
* Add shortcuts for model compare, FW download, log viewer;
* [Companion] Disable being able to "delete" empty model slots (more awkward than helpful).
@@ -67,7 +67,8 @@ void AppPreferencesDialog::writeValues()
|
||||
g.autoCheckFw(ui->autoCheckFirmware->isChecked());
|
||||
g.showSplash(ui->showSplash->isChecked());
|
||||
g.simuSW(ui->simuSW->isChecked());
|
||||
g.useWizard(ui->modelWizard_CB->isChecked());
|
||||
g.removeModelSlots(ui->opt_removeBlankSlots->isChecked());
|
||||
g.newModelAction(ui->opt_newMdl_useWizard->isChecked() ? 1 : ui->opt_newMdl_useEditor->isChecked() ? 2 : 0);
|
||||
g.historySize(ui->historySize->value());
|
||||
g.backLight(ui->backLightColor->currentIndex());
|
||||
g.profile[g.id()].volumeGain(round(ui->volumeGain->value() * 10.0));
|
||||
@@ -149,7 +150,10 @@ void AppPreferencesDialog::initSettings()
|
||||
}
|
||||
|
||||
ui->simuSW->setChecked(g.simuSW());
|
||||
ui->modelWizard_CB->setChecked(g.useWizard());
|
||||
ui->opt_removeBlankSlots->setChecked(g.removeModelSlots());
|
||||
ui->opt_newMdl_useNone->setChecked(g.newModelAction() == 0);
|
||||
ui->opt_newMdl_useWizard->setChecked(g.newModelAction() == 1);
|
||||
ui->opt_newMdl_useEditor->setChecked(g.newModelAction() == 2);
|
||||
ui->libraryPath->setText(g.libDir());
|
||||
ui->ge_lineedit->setText(g.gePath());
|
||||
|
||||
|
||||
@@ -662,6 +662,13 @@ Mode 4:
|
||||
<string>Application Settings</string>
|
||||
</attribute>
|
||||
<layout class="QGridLayout" name="gridLayout_3">
|
||||
<item row="14" column="0">
|
||||
<widget class="QLabel" name="label_17">
|
||||
<property name="text">
|
||||
<string>Automatic Backup Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="10" column="1" colspan="3">
|
||||
<widget class="QCheckBox" name="autoCheckCompanion">
|
||||
<property name="text">
|
||||
@@ -682,103 +689,6 @@ Mode 4:
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1" colspan="3">
|
||||
<widget class="QCheckBox" name="backupEnable">
|
||||
<property name="text">
|
||||
<string>Enable automatic backup before writing firmware</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="3">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="1">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="11" column="1" colspan="3">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1" colspan="3">
|
||||
<widget class="QCheckBox" name="autoCheckFirmware">
|
||||
<property name="text">
|
||||
<string>Automatic check for OpenTX firmware updates</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Splash Screen Library</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Files to keep</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="1" column="1" colspan="3">
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="ge_label">
|
||||
<property name="text">
|
||||
<string>Google Earth Executable</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>User Splash Screens</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="1">
|
||||
<widget class="QSpinBox" name="historySize">
|
||||
<property name="sizePolicy">
|
||||
@@ -794,45 +704,37 @@ Mode 4:
|
||||
</size>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>15</number>
|
||||
<number>50</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>10</number>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="label_17">
|
||||
<item row="6" column="1">
|
||||
<widget class="QCheckBox" name="useFirmwareNightlyBuilds">
|
||||
<property name="text">
|
||||
<string>Automatic Backup Folder</string>
|
||||
<string>Use OpenTX firmware nightly builds</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="1" colspan="3">
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="5" column="1" colspan="3">
|
||||
<widget class="QCheckBox" name="modelWizard_CB">
|
||||
<property name="text">
|
||||
<string>Use model wizard when creating a new model</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="1">
|
||||
<widget class="QLineEdit" name="libraryPath">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
<item row="14" column="1">
|
||||
<widget class="QLineEdit" name="backupPath">
|
||||
<property name="minimumSize">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="15" column="1">
|
||||
<item row="17" column="1">
|
||||
<widget class="QComboBox" name="splashincludeCB">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Expanding" vsizetype="Fixed">
|
||||
@@ -852,23 +754,134 @@ Mode 4:
|
||||
</item>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="1">
|
||||
<widget class="QLineEdit" name="backupPath">
|
||||
<property name="minimumSize">
|
||||
<item row="1" column="1" colspan="3">
|
||||
<widget class="Line" name="line_4">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QLabel" name="label_5">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Files to keep</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="3" column="1" colspan="3">
|
||||
<widget class="Line" name="line">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="13" column="1" colspan="3">
|
||||
<widget class="Line" name="line_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="8" column="1" colspan="3">
|
||||
<widget class="QCheckBox" name="autoCheckFirmware">
|
||||
<property name="text">
|
||||
<string>Automatic check for OpenTX firmware updates</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="14" column="2" colspan="2">
|
||||
<widget class="QPushButton" name="backupPathButton">
|
||||
<property name="text">
|
||||
<string>Select Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="17" column="0">
|
||||
<widget class="QLabel" name="label_10">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Splash Screen Library</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="0" column="0">
|
||||
<widget class="QLabel" name="ge_label">
|
||||
<property name="text">
|
||||
<string>Google Earth Executable</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="20" column="1">
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>350</width>
|
||||
<height>0</height>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
<item row="15" column="1" colspan="3">
|
||||
<widget class="QCheckBox" name="backupEnable">
|
||||
<property name="text">
|
||||
<string>Enable automatic backup before writing firmware</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="1">
|
||||
<widget class="QLineEdit" name="libraryPath">
|
||||
<property name="enabled">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="readOnly">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="12" column="2" colspan="2">
|
||||
<widget class="QPushButton" name="backupPathButton">
|
||||
<item row="16" column="1" colspan="3">
|
||||
<widget class="Line" name="line_5">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Horizontal</enum>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="18" column="0">
|
||||
<widget class="QLabel" name="label_9">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Select Folder</string>
|
||||
<string>User Splash Screens</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="useCompanionNightlyBuilds">
|
||||
<property name="text">
|
||||
<string>Use Companion nightly builds</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -892,30 +905,67 @@ Mode 4:
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="16" column="2" colspan="2">
|
||||
<item row="18" column="2" colspan="2">
|
||||
<widget class="QPushButton" name="libraryPathButton">
|
||||
<property name="text">
|
||||
<string>Select Folder</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="6" column="1">
|
||||
<widget class="QCheckBox" name="useFirmwareNightlyBuilds">
|
||||
<property name="text">
|
||||
<string>Use OpenTX firmware nightly builds</string>
|
||||
<item row="12" column="0">
|
||||
<widget class="QLabel" name="label_8">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Preferred" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
<property name="text">
|
||||
<string>Action on New Model</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="9" column="1">
|
||||
<widget class="QCheckBox" name="useCompanionNightlyBuilds">
|
||||
<property name="text">
|
||||
<string>Use Companion nightly builds</string>
|
||||
<item row="12" column="1" colspan="2">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="opt_newMdl_useWizard">
|
||||
<property name="text">
|
||||
<string>Use model wizard</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">btnGrp_newModelActions</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="opt_newMdl_useEditor">
|
||||
<property name="text">
|
||||
<string>Open model editor</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">btnGrp_newModelActions</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="opt_newMdl_useNone">
|
||||
<property name="text">
|
||||
<string>Just create the model</string>
|
||||
</property>
|
||||
<attribute name="buttonGroup">
|
||||
<string notr="true">btnGrp_newModelActions</string>
|
||||
</attribute>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="11" column="1" colspan="2">
|
||||
<widget class="QCheckBox" name="opt_removeBlankSlots">
|
||||
<property name="toolTip">
|
||||
<string><html><head/><body><p>This option maintains the behaviour from older OpenTx versions where empty model slots are preserved when a model is deleted or moved. </p><p>When this option is de-selected, the other models may be re-arranged to fill the gap left by the removed model.</p></body></html></string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>false</bool>
|
||||
<property name="text">
|
||||
<string>Remove empty model slots when deleting models (only applies for radios w/out categories)</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
@@ -1137,7 +1187,6 @@ Mode 4:
|
||||
<tabstop>ge_pathButton</tabstop>
|
||||
<tabstop>historySize</tabstop>
|
||||
<tabstop>showSplash</tabstop>
|
||||
<tabstop>modelWizard_CB</tabstop>
|
||||
<tabstop>autoCheckFirmware</tabstop>
|
||||
<tabstop>autoCheckCompanion</tabstop>
|
||||
<tabstop>backupPath</tabstop>
|
||||
@@ -1192,4 +1241,7 @@ Mode 4:
|
||||
</hints>
|
||||
</connection>
|
||||
</connections>
|
||||
<buttongroups>
|
||||
<buttongroup name="btnGrp_newModelActions"/>
|
||||
</buttongroups>
|
||||
</ui>
|
||||
|
||||
@@ -620,6 +620,7 @@
|
||||
<file>themes/monoblue/16/contributors.png</file>
|
||||
<file>themes/monoblue/16/stop.png</file>
|
||||
<file>themes/monoblue/16/write_eeprom.png</file>
|
||||
<file>themes/monoblue/16/sdsync.png</file>
|
||||
<file>themes/monoblue/24/paintbrush.png</file>
|
||||
<file>themes/monoblue/24/open.png</file>
|
||||
<file>themes/monoblue/24/edit.png</file>
|
||||
@@ -663,6 +664,7 @@
|
||||
<file>themes/monoblue/24/contributors.png</file>
|
||||
<file>themes/monoblue/24/stop.png</file>
|
||||
<file>themes/monoblue/24/write_eeprom.png</file>
|
||||
<file>themes/monoblue/24/sdsync.png</file>
|
||||
<file>themes/monoblue/32/paintbrush.png</file>
|
||||
<file>themes/monoblue/32/open.png</file>
|
||||
<file>themes/monoblue/32/edit.png</file>
|
||||
@@ -706,6 +708,7 @@
|
||||
<file>themes/monoblue/32/contributors.png</file>
|
||||
<file>themes/monoblue/32/stop.png</file>
|
||||
<file>themes/monoblue/32/write_eeprom.png</file>
|
||||
<file>themes/monoblue/32/sdsync.png</file>
|
||||
<file>themes/monoblue/48/paintbrush.png</file>
|
||||
<file>themes/monoblue/48/open.png</file>
|
||||
<file>themes/monoblue/48/edit.png</file>
|
||||
@@ -749,6 +752,7 @@
|
||||
<file>themes/monoblue/48/contributors.png</file>
|
||||
<file>themes/monoblue/48/stop.png</file>
|
||||
<file>themes/monoblue/48/write_eeprom.png</file>
|
||||
<file>themes/monoblue/48/sdsync.png</file>
|
||||
<file>themes/monowhite/16/paintbrush.png</file>
|
||||
<file>themes/monowhite/16/open.png</file>
|
||||
<file>themes/monowhite/16/edit.png</file>
|
||||
@@ -792,6 +796,7 @@
|
||||
<file>themes/monowhite/16/contributors.png</file>
|
||||
<file>themes/monowhite/16/stop.png</file>
|
||||
<file>themes/monowhite/16/write_eeprom.png</file>
|
||||
<file>themes/monowhite/16/sdsync.png</file>
|
||||
<file>themes/monowhite/24/paintbrush.png</file>
|
||||
<file>themes/monowhite/24/open.png</file>
|
||||
<file>themes/monowhite/24/edit.png</file>
|
||||
@@ -835,6 +840,7 @@
|
||||
<file>themes/monowhite/24/contributors.png</file>
|
||||
<file>themes/monowhite/24/stop.png</file>
|
||||
<file>themes/monowhite/24/write_eeprom.png</file>
|
||||
<file>themes/monowhite/24/sdsync.png</file>
|
||||
<file>themes/monowhite/32/paintbrush.png</file>
|
||||
<file>themes/monowhite/32/open.png</file>
|
||||
<file>themes/monowhite/32/edit.png</file>
|
||||
@@ -878,6 +884,7 @@
|
||||
<file>themes/monowhite/32/contributors.png</file>
|
||||
<file>themes/monowhite/32/stop.png</file>
|
||||
<file>themes/monowhite/32/write_eeprom.png</file>
|
||||
<file>themes/monowhite/32/sdsync.png</file>
|
||||
<file>themes/monowhite/48/paintbrush.png</file>
|
||||
<file>themes/monowhite/48/open.png</file>
|
||||
<file>themes/monowhite/48/edit.png</file>
|
||||
@@ -921,6 +928,7 @@
|
||||
<file>themes/monowhite/48/contributors.png</file>
|
||||
<file>themes/monowhite/48/stop.png</file>
|
||||
<file>themes/monowhite/48/write_eeprom.png</file>
|
||||
<file>themes/monowhite/48/sdsync.png</file>
|
||||
<file>themes/yerico/16/paintbrush.png</file>
|
||||
<file>themes/yerico/16/open.png</file>
|
||||
<file>themes/yerico/16/edit.png</file>
|
||||
|
||||
@@ -20,42 +20,46 @@
|
||||
|
||||
#include "comparedialog.h"
|
||||
#include "ui_comparedialog.h"
|
||||
#include "appdata.h"
|
||||
#include "helpers.h"
|
||||
#include "modelslist.h"
|
||||
#include <QPrinter>
|
||||
#include <QPrintDialog>
|
||||
|
||||
class DragDropHeader {
|
||||
public:
|
||||
DragDropHeader():
|
||||
general_settings(false),
|
||||
models_count(0)
|
||||
{
|
||||
}
|
||||
bool general_settings;
|
||||
uint8_t models_count;
|
||||
uint8_t models[CPN_MAX_MODELS];
|
||||
};
|
||||
//class DragDropHeader {
|
||||
// public:
|
||||
// DragDropHeader():
|
||||
// general_settings(false),
|
||||
// models_count(0)
|
||||
// {
|
||||
// }
|
||||
// bool general_settings;
|
||||
// uint8_t models_count;
|
||||
// uint8_t models[CPN_MAX_MODELS];
|
||||
//};
|
||||
|
||||
CompareDialog::CompareDialog(QWidget * parent, Firmware * firmware):
|
||||
QDialog(parent, Qt::WindowTitleHint | Qt::WindowSystemMenuHint),
|
||||
multimodelprinter(firmware),
|
||||
model1Valid(false),
|
||||
model2Valid(false),
|
||||
QDialog(parent, Qt::Window),
|
||||
multimodelprinter(new MultiModelPrinter(firmware)),
|
||||
ui(new Ui::CompareDialog)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
|
||||
setWindowIcon(CompanionIcon("compare.png"));
|
||||
setAcceptDrops(true);
|
||||
if (!g.compareWinGeo().isEmpty()) {
|
||||
restoreGeometry(g.compareWinGeo());
|
||||
}
|
||||
}
|
||||
|
||||
//setDragDropOverwriteMode(true);
|
||||
//setDropIndicatorShown(true);
|
||||
// TODO scroll to top ... ui->textEdit->scrollToAnchor("1");
|
||||
CompareDialog::~CompareDialog()
|
||||
{
|
||||
delete multimodelprinter;
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void CompareDialog::dragMoveEvent(QDragMoveEvent * event)
|
||||
{
|
||||
if (event->mimeData()->hasFormat("application/x-companion")) {
|
||||
if (event->mimeData()->hasFormat("application/x-companion-modeldata")) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
else {
|
||||
@@ -65,8 +69,7 @@ void CompareDialog::dragMoveEvent(QDragMoveEvent * event)
|
||||
|
||||
void CompareDialog::dragEnterEvent(QDragEnterEvent * event)
|
||||
{
|
||||
// accept just text/uri-list mime format
|
||||
if (event->mimeData()->hasFormat("application/x-companion")) {
|
||||
if (event->mimeData()->hasFormat("application/x-companion-modeldata")) {
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
else {
|
||||
@@ -79,55 +82,85 @@ void CompareDialog::dragLeaveEvent(QDragLeaveEvent * event)
|
||||
event->accept();
|
||||
}
|
||||
|
||||
bool CompareDialog::handleDroppedModel(const QMimeData * mimeData, ModelData & model, QLabel * label)
|
||||
bool CompareDialog::handleMimeData(const QMimeData * mimeData)
|
||||
{
|
||||
if (mimeData->hasFormat("application/x-companion")) {
|
||||
QByteArray gmData = mimeData->data("application/x-companion");
|
||||
DragDropHeader * header = (DragDropHeader *)gmData.data();
|
||||
if (!header->general_settings) {
|
||||
char * gData = gmData.data() + sizeof(DragDropHeader);
|
||||
if (gData[0] == 'M') {
|
||||
ModelData * modeltemp = (ModelData *)(gData + 1);
|
||||
if (modeltemp->used) {
|
||||
memcpy(&model, modeltemp, sizeof(ModelData));
|
||||
QString name(model.name);
|
||||
if (!name.isEmpty())
|
||||
label->setText(name);
|
||||
else
|
||||
label->setText(tr("No name"));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
QVector<ModelData> mList;
|
||||
GeneralSettings gs;
|
||||
if (!TreeModel::decodeMimeData(mimeData, &mList, &gs) || mList.isEmpty())
|
||||
return false;
|
||||
for (int i=0; i < mList.size(); ++i) {
|
||||
GMData data;
|
||||
data.model = mList[i];
|
||||
data.gs = gs;
|
||||
modelsList.append(data);
|
||||
}
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CompareDialog::closeEvent(QCloseEvent * event)
|
||||
{
|
||||
g.compareWinGeo(saveGeometry());
|
||||
}
|
||||
|
||||
void CompareDialog::dropEvent(QDropEvent *event)
|
||||
{
|
||||
QLabel * child = qobject_cast<QLabel*>(childAt(event->pos()));
|
||||
if (!child) return;
|
||||
if (child->objectName().contains("label_1")) {
|
||||
model1Valid = handleDroppedModel(event->mimeData(), model1, ui->label_1);
|
||||
}
|
||||
else if (child->objectName().contains("label_2")) {
|
||||
model2Valid = handleDroppedModel(event->mimeData(), model2, ui->label_2);
|
||||
}
|
||||
event->accept();
|
||||
if (model1Valid && model2Valid) {
|
||||
multimodelprinter.setModel(0, model1);
|
||||
multimodelprinter.setModel(1, model2);
|
||||
ui->textEdit->setHtml(multimodelprinter.print(ui->textEdit->document()));
|
||||
if (handleMimeData(event->mimeData())) {
|
||||
event->accept();
|
||||
compare();
|
||||
}
|
||||
}
|
||||
|
||||
void CompareDialog::closeEvent(QCloseEvent *event)
|
||||
void CompareDialog::compare()
|
||||
{
|
||||
QLayoutItem *child;
|
||||
while ((child = ui->layout_modelNames->takeAt(0))) {
|
||||
if (child->widget())
|
||||
delete child->widget();
|
||||
delete child;
|
||||
}
|
||||
|
||||
multimodelprinter->clearModels();
|
||||
ui->textEdit->clear();
|
||||
|
||||
for (int i=0; i < modelsList.size(); ++i) {
|
||||
multimodelprinter->setModel(i, &modelsList[i].model, &modelsList[i].gs);
|
||||
QString name(modelsList.at(i).model.name);
|
||||
if (name.isEmpty())
|
||||
name = tr("Unnamed Model %1").arg(i+1);
|
||||
|
||||
QWidget * hdr = new QWidget(this);
|
||||
hdr->setLayout(new QHBoxLayout());
|
||||
hdr->layout()->setContentsMargins(0, 0, 0, 0);
|
||||
hdr->layout()->setSpacing(2);
|
||||
QToolButton * btn = new QToolButton(hdr);
|
||||
btn->setIcon(CompanionIcon("clear.png"));
|
||||
btn->setProperty("index", i);
|
||||
btn->setFixedSize(18, 18);
|
||||
btn->setToolTip(tr("Click to remove this model."));
|
||||
hdr->layout()->addWidget(btn);
|
||||
QLabel * lbl = new QLabel(name, this);
|
||||
lbl->setStyleSheet("font-weight: bold;");
|
||||
hdr->layout()->addWidget(lbl);
|
||||
connect(btn, &QToolButton::clicked, this, &CompareDialog::removeModelBtnClicked);
|
||||
|
||||
ui->layout_modelNames->addWidget(hdr);
|
||||
}
|
||||
if (modelsList.size())
|
||||
ui->textEdit->setHtml(multimodelprinter->print(ui->textEdit->document()));
|
||||
}
|
||||
|
||||
CompareDialog::~CompareDialog()
|
||||
void CompareDialog::removeModel(int idx)
|
||||
{
|
||||
delete ui;
|
||||
if (idx < modelsList.size()) {
|
||||
modelsList.remove(idx);
|
||||
compare();
|
||||
}
|
||||
}
|
||||
|
||||
void CompareDialog::removeModelBtnClicked()
|
||||
{
|
||||
if (sender() && sender()->property("index").isValid())
|
||||
removeModel(sender()->property("index").toInt());
|
||||
}
|
||||
|
||||
void CompareDialog::on_printButton_clicked()
|
||||
|
||||
@@ -35,27 +35,32 @@ class CompareDialog : public QDialog
|
||||
public:
|
||||
CompareDialog(QWidget *parent, Firmware * firmware);
|
||||
~CompareDialog();
|
||||
|
||||
|
||||
protected:
|
||||
MultiModelPrinter multimodelprinter;
|
||||
ModelData model1;
|
||||
ModelData model2;
|
||||
bool model1Valid;
|
||||
bool model2Valid;
|
||||
struct GMData {
|
||||
ModelData model;
|
||||
GeneralSettings gs;
|
||||
};
|
||||
|
||||
MultiModelPrinter * multimodelprinter;
|
||||
QVector<GMData> modelsList;
|
||||
QMap<int, GMData> modelsMap;
|
||||
Ui::CompareDialog * ui;
|
||||
|
||||
|
||||
protected slots:
|
||||
void removeModelBtnClicked();
|
||||
void on_printButton_clicked();
|
||||
void on_printFileButton_clicked();
|
||||
|
||||
|
||||
protected:
|
||||
void closeEvent(QCloseEvent * event);
|
||||
bool handleDroppedModel(const QMimeData * mimeData, ModelData & model, QLabel * label);
|
||||
void dropEvent(QDropEvent *event);
|
||||
void dragMoveEvent(QDragMoveEvent *event);
|
||||
void dragEnterEvent(QDragEnterEvent *event);
|
||||
void dragLeaveEvent(QDragLeaveEvent *event);
|
||||
void printDiff();
|
||||
virtual void closeEvent(QCloseEvent * event);
|
||||
virtual void dragMoveEvent(QDragMoveEvent *event);
|
||||
virtual void dragEnterEvent(QDragEnterEvent *event);
|
||||
virtual void dragLeaveEvent(QDragLeaveEvent *event);
|
||||
virtual void dropEvent(QDropEvent *event);
|
||||
bool handleMimeData(const QMimeData * mimeData);
|
||||
void compare();
|
||||
void removeModel(int idx);
|
||||
};
|
||||
|
||||
#endif // _COMPAREDIALOG_H_
|
||||
|
||||
@@ -29,63 +29,22 @@
|
||||
<string>Compare Models</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout">
|
||||
<item row="0" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label_1">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color:green;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Drag first model here</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLabel" name="label_2">
|
||||
<property name="font">
|
||||
<font>
|
||||
<weight>75</weight>
|
||||
<bold>true</bold>
|
||||
</font>
|
||||
</property>
|
||||
<property name="acceptDrops">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<property name="styleSheet">
|
||||
<string notr="true">color:red;</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Drag second model here</string>
|
||||
</property>
|
||||
<property name="alignment">
|
||||
<set>Qt::AlignCenter</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<item row="2" column="0">
|
||||
<widget class="QTextEdit" name="textEdit">
|
||||
<property name="readOnly">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<item row="3" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>To compare models, drag and drop them anywhere in this window.</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="horizontalSpacer">
|
||||
<property name="orientation">
|
||||
@@ -122,11 +81,28 @@
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout" name="layout_modelNames">
|
||||
<property name="spacing">
|
||||
<number>6</number>
|
||||
</property>
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="companion.qrc"/>
|
||||
</resources>
|
||||
<resources/>
|
||||
<connections>
|
||||
<connection>
|
||||
<sender>closeButton</sender>
|
||||
|
||||
@@ -1502,6 +1502,7 @@ RadioData::RadioData()
|
||||
void ModelData::clear()
|
||||
{
|
||||
memset(this, 0, sizeof(ModelData));
|
||||
modelIndex = -1; // an invalid index, this is managed by the TreeView data model
|
||||
moduleData[0].channelsCount = 8;
|
||||
moduleData[1].channelsStart = 0;
|
||||
moduleData[1].channelsCount = 8;
|
||||
|
||||
@@ -27,8 +27,6 @@
|
||||
#include "eeprominterface.h"
|
||||
|
||||
#define SPLASH_TIME 5
|
||||
#define MAX_RECENT 15
|
||||
#define MAX_PROFILES 15
|
||||
|
||||
class MdiChild;
|
||||
QT_BEGIN_NAMESPACE
|
||||
@@ -52,9 +50,10 @@ class MainWindow : public QMainWindow
|
||||
|
||||
public:
|
||||
MainWindow();
|
||||
~MainWindow();
|
||||
|
||||
signals:
|
||||
void FirmwareChanged();
|
||||
void firmwareChanged();
|
||||
|
||||
protected:
|
||||
QString getCompanionUpdateBaseUrl();
|
||||
@@ -70,7 +69,7 @@ class MainWindow : public QMainWindow
|
||||
|
||||
private slots:
|
||||
void openDocURL();
|
||||
void retranslateUi();
|
||||
void retranslateUi(bool showMsg = false);
|
||||
|
||||
void setLanguage(const QString & langString);
|
||||
void onLanguageChanged(QAction * act);
|
||||
@@ -78,6 +77,12 @@ class MainWindow : public QMainWindow
|
||||
void onThemeChanged(QAction * act);
|
||||
void setIconThemeSize(int index);
|
||||
void onIconSizeChanged(QAction * act);
|
||||
void setTabbedWindows(bool on);
|
||||
void onChangeWindowAction(QAction * act);
|
||||
void updateWindowActions();
|
||||
void updateWindowActionTitle(const QMdiSubWindow * win, QAction * act = NULL);
|
||||
void onSubwindowTitleChanged();
|
||||
void onSubwindowModified();
|
||||
|
||||
void checkForUpdates();
|
||||
void checkForFirmwareUpdate();
|
||||
@@ -94,13 +99,11 @@ class MainWindow : public QMainWindow
|
||||
void openFile();
|
||||
void save();
|
||||
void saveAs();
|
||||
void cut();
|
||||
void closeFile();
|
||||
void openRecentFile();
|
||||
void loadProfileId(const unsigned pid);
|
||||
void loadProfile();
|
||||
void logFile();
|
||||
void copy();
|
||||
void paste();
|
||||
void writeEeprom();
|
||||
void readEeprom();
|
||||
void writeFlash(QString fileToFlash="");
|
||||
@@ -110,7 +113,6 @@ class MainWindow : public QMainWindow
|
||||
void burnConfig();
|
||||
void burnList();
|
||||
void burnFuses();
|
||||
void simulate();
|
||||
void contributors();
|
||||
void sdsync();
|
||||
void changelog();
|
||||
@@ -118,8 +120,6 @@ class MainWindow : public QMainWindow
|
||||
void customizeSplash();
|
||||
void about();
|
||||
void compare();
|
||||
void print();
|
||||
void loadBackup();
|
||||
void appPrefs();
|
||||
void fwPrefs();
|
||||
void updateMenus();
|
||||
@@ -127,7 +127,6 @@ class MainWindow : public QMainWindow
|
||||
void copyProfile();
|
||||
void deleteProfile(const int pid);
|
||||
void deleteCurrentProfile();
|
||||
void setActiveSubWindow(QWidget *window);
|
||||
void autoClose();
|
||||
|
||||
void closeUpdatesWaitDialog();
|
||||
@@ -135,20 +134,17 @@ class MainWindow : public QMainWindow
|
||||
void openFile(const QString & fileName, bool updateLastUsedDir = false);
|
||||
|
||||
private:
|
||||
QAction * addAct(const QString &, const QString &, const QString &, enum QKeySequence::StandardKey, const char *, QObject *slotObj=NULL);
|
||||
QAction * addAct(const QString &, const QString &, const QString &, const QKeySequence &, const char *, QObject *slotObj=NULL);
|
||||
QAction * addAct(const QString &, const QString &, const QString &, const char *);
|
||||
QAction * addActToGroup(QActionGroup * aGroup, const QString & sName, const QString & lName,
|
||||
const char * propName = 0, const QVariant & propValue = QVariant(), const QVariant & dfltValue = QVariant());
|
||||
QAction * addAct(const QString & icon, const char * slot = NULL, const QKeySequence & shortcut = 0, QObject * slotObj = NULL, const char * signal = NULL);
|
||||
QAction * addActToGroup(QActionGroup * aGroup, const QString & sName, const QString & lName, const char * propName = 0,
|
||||
const QVariant & propValue = QVariant(), const QVariant & dfltValue = QVariant(), const QKeySequence & shortcut = 0);
|
||||
void trAct(QAction * act, const QString & text, const QString & descript);
|
||||
|
||||
QMenu * createLanguageMenu(QWidget * parent = Q_NULLPTR);
|
||||
QMenu * createRecentFileMenu();
|
||||
QMenu * createProfilesMenu();
|
||||
|
||||
void createActions();
|
||||
void createMenus();
|
||||
void createToolBars();
|
||||
void createStatusBar();
|
||||
void showReadyStatus();
|
||||
void updateRecentFileActions();
|
||||
void updateProfilesActions();
|
||||
|
||||
@@ -172,13 +168,21 @@ class MainWindow : public QMainWindow
|
||||
|
||||
QNetworkAccessManager *networkManager;
|
||||
|
||||
QVector<QAction *> actionsList;
|
||||
QVector<QAction *> recentFileActs;
|
||||
QVector<QAction *> profileActs;
|
||||
QList<QAction *> fileWindowActions;
|
||||
|
||||
QMenu *fileMenu;
|
||||
QMenu *editMenu;
|
||||
QMenu *settingsMenu;
|
||||
QMenu *burnMenu;
|
||||
QMenu *helpMenu;
|
||||
QMenu *windowMenu;
|
||||
QMenu *iconThemeSizeMenu;
|
||||
QMenu *themeMenu;
|
||||
QMenu *recentFilesMenu;
|
||||
QMenu *profilesMenu;
|
||||
QActionGroup * windowsListActions;
|
||||
QToolBar *fileToolBar;
|
||||
QToolBar *editToolBar;
|
||||
QToolBar *burnToolBar;
|
||||
@@ -187,6 +191,8 @@ class MainWindow : public QMainWindow
|
||||
QAction *openAct;
|
||||
QAction *saveAct;
|
||||
QAction *saveAsAct;
|
||||
QAction *closeAct;
|
||||
QAction *recentFilesAct;
|
||||
QAction *exitAct;
|
||||
QAction *appPrefsAct;
|
||||
QAction *fwPrefsAct;
|
||||
@@ -197,9 +203,6 @@ class MainWindow : public QMainWindow
|
||||
QAction *fwchangelogAct;
|
||||
QAction *compareAct;
|
||||
QAction *editSplashAct;
|
||||
QAction *cutAct;
|
||||
QAction *copyAct;
|
||||
QAction *pasteAct;
|
||||
QAction *writeEepromAct;
|
||||
QAction *readEepromAct;
|
||||
QAction *burnConfigAct;
|
||||
@@ -207,20 +210,19 @@ class MainWindow : public QMainWindow
|
||||
QAction *burnFusesAct;
|
||||
QAction *writeFlashAct;
|
||||
QAction *readFlashAct;
|
||||
QAction *writeBackupToRadioAct;
|
||||
QAction *readBackupToFileAct;
|
||||
QAction *simulateAct;
|
||||
QAction *separatorAct;
|
||||
QAction *writeBUToRadioAct;
|
||||
QAction *readBUToFileAct;
|
||||
QAction *aboutAct;
|
||||
QAction *printAct;
|
||||
QAction *loadbackupAct;
|
||||
QAction *logsAct;
|
||||
QAction *recentFileActs[MAX_RECENT];
|
||||
QAction *profileActs[MAX_PROFILES];
|
||||
QAction *profilesMenuAct;
|
||||
QAction *createProfileAct;
|
||||
QAction *copyProfileAct;
|
||||
QAction *deleteProfileAct;
|
||||
QAction *openDocURLAct;
|
||||
QAction *actTabbedWindows;
|
||||
QAction *actTileWindows;
|
||||
QAction *actCascadeWindows;
|
||||
QAction *actCloseAllWindows;
|
||||
};
|
||||
|
||||
#endif // _MAINWINDOW_H_
|
||||
|
||||
@@ -23,9 +23,14 @@
|
||||
|
||||
#include "eeprominterface.h"
|
||||
#include "modelslist.h"
|
||||
#include <QtGui>
|
||||
|
||||
class MainWindow;
|
||||
#include <QActionGroup>
|
||||
#include <QtGui>
|
||||
#include <QMessageBox>
|
||||
#include <QProxyStyle>
|
||||
#include <QWidget>
|
||||
|
||||
class QToolBar;
|
||||
|
||||
namespace Ui {
|
||||
class MdiChild;
|
||||
@@ -33,93 +38,198 @@ class MdiChild;
|
||||
|
||||
class MdiChild : public QWidget
|
||||
{
|
||||
// friend class ModelsListWidget;
|
||||
|
||||
Q_OBJECT
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
MdiChild(MainWindow * parent);
|
||||
enum Actions {
|
||||
ACT_GEN_EDT, // radio general settings
|
||||
ACT_GEN_CPY,
|
||||
ACT_GEN_PST,
|
||||
ACT_GEN_SIM,
|
||||
ACT_ITM_EDT, // edit model/rename category
|
||||
ACT_ITM_DEL, // delete model or cat
|
||||
ACT_CAT_ADD, // category actions...
|
||||
//ACT_CAT_EDT, // not sure these are needed...
|
||||
//ACT_CAT_DEL, // the ACT_ITM_* actions do the same thing
|
||||
ACT_CAT_SEP, // convenience separator shown/hidden with category actions
|
||||
ACT_MDL_ADD, // model actions...
|
||||
ACT_MDL_CPY,
|
||||
ACT_MDL_CUT,
|
||||
ACT_MDL_PST,
|
||||
ACT_MDL_DUP,
|
||||
ACT_MDL_INS,
|
||||
ACT_MDL_MOV,
|
||||
ACT_MDL_RTR, // ResToRe backup
|
||||
ACT_MDL_WIZ,
|
||||
ACT_MDL_DFT, // set as DeFaulT
|
||||
ACT_MDL_PRT, // print
|
||||
ACT_MDL_SIM,
|
||||
ACT_ENUM_END
|
||||
};
|
||||
|
||||
MdiChild(QWidget *parent = Q_NULLPTR, QWidget * parentWin = Q_NULLPTR, Qt::WindowFlags f = Qt::WindowFlags());
|
||||
~MdiChild();
|
||||
|
||||
QString currentFile() const;
|
||||
QString userFriendlyCurrentFile() const;
|
||||
QVector<int> getSelectedCategories() const;
|
||||
QVector<int> getSelectedModels() const;
|
||||
QList<QAction *> getGeneralActions();
|
||||
QList<QAction *> getEditActions(bool incCatNew = true);
|
||||
QList<QAction *> getModelActions();
|
||||
//QList<QAction *> getCategoryActions();
|
||||
QAction * getAction(const Actions type);
|
||||
|
||||
public slots:
|
||||
void newFile();
|
||||
bool loadFile(const QString & fileName, bool resetCurrentFile=true);
|
||||
bool save();
|
||||
bool saveAs(bool isNew=false);
|
||||
bool saveFile(const QString & fileName, bool setCurrent=true);
|
||||
bool hasModelSelected();
|
||||
bool hasPasteData() const;
|
||||
QString userFriendlyCurrentFile() const;
|
||||
QString currentFile() const { return curFile; }
|
||||
int getCurrentModel() const;
|
||||
int getCurrentCategory() const;
|
||||
void refresh(bool expand=false);
|
||||
void keyPressEvent(QKeyEvent * event);
|
||||
|
||||
signals:
|
||||
void copyAvailable(bool val);
|
||||
|
||||
protected:
|
||||
void convertStorage(Board::Type from, Board::Type to);
|
||||
void forceNewFilename(const QString & suffix, const QString & ext);
|
||||
void closeEvent(QCloseEvent * event);
|
||||
|
||||
protected slots:
|
||||
void documentWasModified();
|
||||
void on_simulateButton_clicked();
|
||||
void onModelMoveToCategory();
|
||||
void on_radioSettings_clicked();
|
||||
void setDefault();
|
||||
void writeEeprom();
|
||||
void print(int model=-1, const QString & filename="");
|
||||
void onFirmwareChanged();
|
||||
|
||||
public slots:
|
||||
signals:
|
||||
void modified();
|
||||
void newStatusMessage(const QString & msg, const int duration);
|
||||
|
||||
protected:
|
||||
virtual void changeEvent(QEvent * event);
|
||||
virtual void closeEvent(QCloseEvent * event);
|
||||
virtual void resizeEvent(QResizeEvent * event);
|
||||
virtual QSize sizeHint() const;
|
||||
|
||||
private slots:
|
||||
void setupNavigation();
|
||||
void updateNavigation();
|
||||
void updateTitle();
|
||||
void setModified();
|
||||
void retranslateUi();
|
||||
void showModelsListContextMenu(const QPoint & pos);
|
||||
void checkAndInitModel(int row);
|
||||
void generalEdit();
|
||||
void categoryAdd();
|
||||
void categoryRename();
|
||||
void categoryDelete();
|
||||
void modelAdd();
|
||||
void modelEdit();
|
||||
void modelDuplicate();
|
||||
void modelChangeCategory(int toCategoryId);
|
||||
void wizardEdit();
|
||||
void openModelEditWindow();
|
||||
bool loadBackup();
|
||||
void confirmDelete();
|
||||
void deleteSelectedModels();
|
||||
void showContextMenu(const QPoint & pos);
|
||||
void adjustToolbarLayout();
|
||||
|
||||
void initModelsList();
|
||||
void refresh();
|
||||
void onItemActivated(const QModelIndex index);
|
||||
void onItemSelected(const QModelIndex &);
|
||||
void onCurrentItemChanged(const QModelIndex &, const QModelIndex &);
|
||||
void onDataChanged(const QModelIndex & index);
|
||||
|
||||
void cut();
|
||||
void generalEdit();
|
||||
void copyGeneralSettings();
|
||||
void pasteGeneralSettings();
|
||||
|
||||
void copy();
|
||||
void cut();
|
||||
void paste();
|
||||
void writeEeprom();
|
||||
void insert();
|
||||
void edit();
|
||||
void confirmDelete();
|
||||
void categoryAdd();
|
||||
void modelAdd();
|
||||
void modelEdit();
|
||||
void wizardEdit();
|
||||
void modelDuplicate();
|
||||
void onModelMoveToCategory();
|
||||
|
||||
void openModelWizard(int row = -1);
|
||||
void openModelEditWindow(int row = -1);
|
||||
|
||||
void setDefault();
|
||||
void modelSimulate();
|
||||
void radioSimulate();
|
||||
void print(int model=-1, const QString & filename="");
|
||||
void setModified();
|
||||
void updateTitle();
|
||||
void onItemSelected(QModelIndex);
|
||||
bool isModel(QModelIndex);
|
||||
bool isCategory(QModelIndex);
|
||||
bool loadBackup();
|
||||
|
||||
void pasteModelData(const QMimeData * mimeData, const QModelIndex row, bool insert = false, bool move = false);
|
||||
void pasteGeneralData(const QMimeData * mimeData);
|
||||
|
||||
private:
|
||||
QAction *addAct(Actions actId, const QString & icon, const char * slot = 0, const QKeySequence & shortcut = 0, QObject * slotObj = NULL);
|
||||
|
||||
QModelIndex getCurrentIndex() const;
|
||||
int getCurrentModel() const;
|
||||
int countSelectedModels() const;
|
||||
bool hasSelectedModel();
|
||||
bool setSelectedModel(const int modelIndex);
|
||||
int getCurrentCategory() const;
|
||||
int countSelectedCats() const;
|
||||
bool hasSelectedCat();
|
||||
|
||||
bool deleteCategory(int categoryIndex = -1, QString * error = NULL);
|
||||
void deleteSelectedCats();
|
||||
|
||||
void checkAndInitModel(int row);
|
||||
void findNewDefaultModel(const unsigned startAt = 0);
|
||||
bool insertModelRows(int atModelIdx, int count);
|
||||
int modelAppend(const ModelData model);
|
||||
int newModel(int modelIndex = -1, int categoryIndex = -1);
|
||||
unsigned deleteModels(const QVector<int> modelIndices);
|
||||
bool deleteModel(const int modelIndex);
|
||||
void deleteSelectedModels();
|
||||
void moveModelsToCategory(const QVector<int> models, const int toCategoryId);
|
||||
void moveSelectedModelsToCat(const int toCategoryId);
|
||||
|
||||
void clearCutList();
|
||||
void removeModelFromCutList(const int modelIndex);
|
||||
bool hasClipboardData(const quint8 type = 0) const;
|
||||
|
||||
bool maybeSave();
|
||||
void setCurrentFile(const QString & fileName);
|
||||
void doCopy(QByteArray * gmData);
|
||||
void doPaste(QByteArray * gmData, QModelIndex row);
|
||||
void initModelsList();
|
||||
void forceNewFilename(const QString & suffix, const QString & ext);
|
||||
void convertStorage(Board::Type from, Board::Type to);
|
||||
void showWarning(const QString & msg);
|
||||
int askQuestion(const QString & msg, int button0 = QMessageBox::Yes, int button1 = QMessageBox::No | QMessageBox::Default, int button2 = 0);
|
||||
|
||||
MainWindow * parent;
|
||||
Ui::MdiChild * ui;
|
||||
TreeModel * modelsListModel;
|
||||
QWidget * parentWindow;
|
||||
|
||||
QString curFile;
|
||||
QVector<int> cutModels;
|
||||
QVector<QAction *> action;
|
||||
QToolBar * radioToolbar;
|
||||
QToolBar * categoriesToolbar;
|
||||
QToolBar * modelsToolbar;
|
||||
|
||||
Firmware * firmware;
|
||||
RadioData radioData;
|
||||
|
||||
int lastSelectedModel;
|
||||
bool isUntitled;
|
||||
bool fileChanged;
|
||||
bool showCatToolbar;
|
||||
const quint16 stateDataVersion;
|
||||
};
|
||||
|
||||
// This will draw the drop indicator across all columns of a model View (vs. in just one column), and lets us make the indicator more obvious.
|
||||
class ItemViewProxyStyle: public QProxyStyle
|
||||
{
|
||||
public:
|
||||
ItemViewProxyStyle(QStyle * style = 0) : QProxyStyle(style) {}
|
||||
|
||||
void drawPrimitive(PrimitiveElement element, const QStyleOption * option, QPainter * painter, const QWidget * widget = 0) const
|
||||
{
|
||||
if (element == QStyle::PE_IndicatorItemViewItemDrop && !option->rect.isNull()) {
|
||||
painter->save();
|
||||
// set a wider stroke
|
||||
QPen pen(painter->pen());
|
||||
pen.setWidthF(2.5f);
|
||||
painter->setPen(pen);
|
||||
// adjust the rectangle size of the indicator to encompass the whole row
|
||||
QStyleOption opt(*option);
|
||||
opt.rect.setLeft(0);
|
||||
if (widget)
|
||||
opt.rect.setRight(widget->width());
|
||||
// call default handler and exit
|
||||
QProxyStyle::drawPrimitive(element, &opt, painter, widget);
|
||||
painter->restore();
|
||||
}
|
||||
else {
|
||||
QProxyStyle::drawPrimitive(element, option, painter, widget);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif // _MDICHILD_H_
|
||||
|
||||
@@ -6,35 +6,37 @@
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>468</width>
|
||||
<height>414</height>
|
||||
<width>545</width>
|
||||
<height>575</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<layout class="QVBoxLayout" name="verticalLayout" stretch="0,0,1">
|
||||
<property name="spacing">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="radioSettings">
|
||||
<property name="text">
|
||||
<string>Radio settings</string>
|
||||
<layout class="QHBoxLayout" name="topToolbarLayout">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</widget>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QTreeView" name="modelsList"/>
|
||||
<layout class="QHBoxLayout" name="botToolbarLayout">
|
||||
<property name="spacing">
|
||||
<number>2</number>
|
||||
</property>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="simulateButton">
|
||||
<property name="text">
|
||||
<string>Simulate Tx</string>
|
||||
<widget class="QTreeView" name="modelsList">
|
||||
<property name="uniformRowHeights">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="companion.qrc"/>
|
||||
</resources>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
@@ -24,16 +24,17 @@ TreeItem::TreeItem(const QVector<QVariant> & itemData):
|
||||
itemData(itemData),
|
||||
parentItem(NULL),
|
||||
categoryIndex(-1),
|
||||
modelIndex(-1)
|
||||
modelIndex(-1),
|
||||
flags(0)
|
||||
{
|
||||
}
|
||||
|
||||
TreeItem::TreeItem(TreeItem * parent, int categoryIndex, int modelIndex):
|
||||
itemData(parent->columnCount()),
|
||||
parentItem(parent),
|
||||
categoryIndex(categoryIndex),
|
||||
modelIndex(modelIndex)
|
||||
TreeItem(QVector<QVariant>(parent->columnCount()))
|
||||
{
|
||||
setParent(parent);
|
||||
setCategoryIndex(categoryIndex);
|
||||
setModelIndex(modelIndex);
|
||||
}
|
||||
|
||||
TreeItem::~TreeItem()
|
||||
@@ -69,16 +70,16 @@ QVariant TreeItem::data(int column) const
|
||||
return itemData.value(column);
|
||||
}
|
||||
|
||||
TreeItem * TreeItem::appendChild(int categoryIndex, int modelIndex)
|
||||
TreeItem *TreeItem::insertChild(const int row, int categoryIndex, int modelIndex)
|
||||
{
|
||||
TreeItem * item = new TreeItem(this, categoryIndex, modelIndex);
|
||||
childItems.insert(childItems.size(), item);
|
||||
childItems.insert(row, item);
|
||||
return item;
|
||||
}
|
||||
|
||||
TreeItem * TreeItem::parent()
|
||||
TreeItem * TreeItem::appendChild(int categoryIndex, int modelIndex)
|
||||
{
|
||||
return parentItem;
|
||||
return insertChild(childItems.size(), categoryIndex, modelIndex);
|
||||
}
|
||||
|
||||
bool TreeItem::removeChildren(int position, int count)
|
||||
@@ -92,6 +93,14 @@ bool TreeItem::removeChildren(int position, int count)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TreeItem::insertChildren(int row, int count)
|
||||
{
|
||||
for (int i=0; i < count; ++i) {
|
||||
insertChild(row + i, -1, -1);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TreeItem::setData(int column, const QVariant & value)
|
||||
{
|
||||
if (column < 0 || column >= itemData.size())
|
||||
@@ -101,6 +110,29 @@ bool TreeItem::setData(int column, const QVariant & value)
|
||||
return true;
|
||||
}
|
||||
|
||||
void TreeItem::setFlag(const quint16 & flag, const bool on)
|
||||
{
|
||||
if (on)
|
||||
flags |= flag;
|
||||
else
|
||||
flags &= ~flag;
|
||||
}
|
||||
|
||||
bool TreeItem::isCategory() const
|
||||
{
|
||||
return (modelIndex < 0 && categoryIndex > -1);
|
||||
}
|
||||
|
||||
bool TreeItem::isModel() const
|
||||
{
|
||||
return (modelIndex > -1);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* TreeModel
|
||||
*/
|
||||
|
||||
TreeModel::TreeModel(RadioData * radioData, QObject * parent):
|
||||
QAbstractItemModel(parent),
|
||||
radioData(radioData),
|
||||
@@ -108,14 +140,20 @@ TreeModel::TreeModel(RadioData * radioData, QObject * parent):
|
||||
{
|
||||
Board::Type board = getCurrentBoard();
|
||||
QVector<QVariant> labels;
|
||||
if (!IS_HORUS(board))
|
||||
if (!getCurrentFirmware()->getCapability(Capability::HasModelCategories))
|
||||
labels << tr("Index");
|
||||
labels << tr("Name");
|
||||
if (!(IS_HORUS(board) || IS_SKY9X(board))) {
|
||||
labels << tr("Size");
|
||||
}
|
||||
rootItem = new TreeItem(labels);
|
||||
// uniqueId and version for drag/drop operations (see encodeHeaderData())
|
||||
mimeHeaderData.instanceId = QUuid::createUuid();
|
||||
mimeHeaderData.dataVersion = 1;
|
||||
|
||||
refresh();
|
||||
//connect(this, &QAbstractItemModel::rowsAboutToBeRemoved, this, &TreeModel::onRowsAboutToBeRemoved);
|
||||
connect(this, &QAbstractItemModel::rowsRemoved, this, &TreeModel::onRowsRemoved);
|
||||
}
|
||||
|
||||
TreeModel::~TreeModel()
|
||||
@@ -133,39 +171,38 @@ QVariant TreeModel::data(const QModelIndex & index, int role) const
|
||||
if (!index.isValid())
|
||||
return QVariant();
|
||||
|
||||
if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::FontRole) {
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
TreeItem * item = getItem(index);
|
||||
|
||||
if (role == Qt::FontRole) {
|
||||
if (item->getModelIndex() == (int)radioData->generalSettings.currModelIndex) {
|
||||
QFont font;
|
||||
font.setBold(true);
|
||||
return font;
|
||||
}
|
||||
if (role == Qt::DisplayRole || role == Qt::EditRole) {
|
||||
return item->data(index.column());
|
||||
}
|
||||
return item->data(index.column());
|
||||
|
||||
if (role == Qt::FontRole && item->getModelIndex() == (int)radioData->generalSettings.currModelIndex) {
|
||||
QFont font;
|
||||
font.setBold(true);
|
||||
return font;
|
||||
}
|
||||
|
||||
if (role == Qt::ForegroundRole && (item->getFlags() & TreeItem::MarkedForCut)) {
|
||||
return QPalette().brush(QPalette::Disabled, QPalette::Text);
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
Qt::ItemFlags TreeModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return 0;
|
||||
Qt::ItemFlags f = QAbstractItemModel::flags(index);
|
||||
|
||||
return Qt::ItemIsEditable | QAbstractItemModel::flags(index);
|
||||
}
|
||||
|
||||
TreeItem * TreeModel::getItem(const QModelIndex & index) const
|
||||
{
|
||||
if (index.isValid()) {
|
||||
TreeItem * item = static_cast<TreeItem *>(index.internalPointer());
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
if (getItem(index)->isCategory())
|
||||
f |= Qt::ItemIsEditable;
|
||||
else
|
||||
f |= Qt::ItemIsDragEnabled; // TODO drag/drop categories
|
||||
}
|
||||
return rootItem;
|
||||
f |= Qt::ItemIsDropEnabled;
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
QVariant TreeModel::headerData(int section, Qt::Orientation orientation, int role) const
|
||||
@@ -206,8 +243,10 @@ QModelIndex TreeModel::parent(const QModelIndex & index) const
|
||||
bool TreeModel::removeRows(int position, int rows, const QModelIndex & parent)
|
||||
{
|
||||
TreeItem * parentItem = getItem(parent);
|
||||
bool success = true;
|
||||
if (!parentItem)
|
||||
return false;
|
||||
|
||||
bool success = true;
|
||||
if (position >= 0 && rows > 0) {
|
||||
beginRemoveRows(parent, position, position + rows - 1);
|
||||
success = parentItem->removeChildren(position, rows);
|
||||
@@ -217,6 +256,23 @@ bool TreeModel::removeRows(int position, int rows, const QModelIndex & parent)
|
||||
return success;
|
||||
}
|
||||
|
||||
/* unused but possibly useful in future
|
||||
bool TreeModel::insertRows(int row, int count, const QModelIndex & parent)
|
||||
{
|
||||
TreeItem * parentItem = getItem(parent);
|
||||
if (!parentItem)
|
||||
return false;
|
||||
|
||||
bool success = true;
|
||||
if (row >= 0 && count > 0) {
|
||||
beginInsertRows(parent, row, row + count - 1);
|
||||
success = parentItem->insertChildren(row, count);
|
||||
endInsertRows();
|
||||
}
|
||||
|
||||
return success;
|
||||
} */
|
||||
|
||||
int TreeModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
TreeItem * parentItem = getItem(parent);
|
||||
@@ -241,21 +297,361 @@ bool TreeModel::setData(const QModelIndex & index, const QVariant & value, int r
|
||||
return result;
|
||||
}
|
||||
|
||||
QStringList TreeModel::mimeTypes() const
|
||||
{
|
||||
QStringList types;
|
||||
types << "application/x-companion-modeldata";
|
||||
types << "application/x-companion-generaldata";
|
||||
//type << "application/x-companion-radiodata-header"; // supported but not advertised, must be in conjunction with one of the above
|
||||
return types;
|
||||
}
|
||||
|
||||
Qt::DropActions TreeModel::supportedDropActions() const
|
||||
{
|
||||
return Qt::CopyAction | Qt::MoveAction;
|
||||
}
|
||||
|
||||
Qt::DropActions TreeModel::supportedDragActions() const
|
||||
{
|
||||
return Qt::CopyAction | Qt::MoveAction;
|
||||
}
|
||||
|
||||
// This method encodes all the data on default drag operation, including general radio settings. This is useful for eg. Compare dialog/model printer.
|
||||
QMimeData * TreeModel::mimeData(const QModelIndexList & indexes) const
|
||||
{
|
||||
QMimeData * mimeData = new QMimeData();
|
||||
getModelsMimeData(indexes, mimeData);
|
||||
getGeneralMimeData(mimeData);
|
||||
getHeaderMimeData(mimeData);
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
bool TreeModel::canDropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) const
|
||||
{
|
||||
Q_UNUSED(action);
|
||||
//qDebug() << action << row << column << parent.row();
|
||||
|
||||
// we do not accept dropped general settings right now (user must copy/paste those)
|
||||
if (hasHeaderMimeData(data) && hasModelsMimeData(data) && (row > -1 || parent.isValid()))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TreeModel::dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent)
|
||||
{
|
||||
if (!canDropMimeData(data, action, row, column, parent))
|
||||
return false;
|
||||
|
||||
if (action == Qt::IgnoreAction)
|
||||
return true;
|
||||
|
||||
QModelIndex idx;
|
||||
bool isInsert = false;
|
||||
if (row > -1) {
|
||||
// dropped between rows (insert)
|
||||
isInsert = true;
|
||||
idx = index(row, column, parent);
|
||||
}
|
||||
else if (parent.isValid()) {
|
||||
// was dropped on a row (overwrite)
|
||||
idx = parent;
|
||||
}
|
||||
else {
|
||||
// dropped who knows where, (shouldn't be here though due check in canDropMimeData())
|
||||
return false;
|
||||
}
|
||||
//qDebug() << action << row << column << parent.row() << idx << idx.row() << hasOwnMimeData(data);
|
||||
|
||||
// Force drops from other file windows to be copy actions because we don't want to delete our models.
|
||||
if (action == Qt::MoveAction && !hasOwnMimeData(data))
|
||||
action = Qt::CopyAction;
|
||||
|
||||
// canDropMimeData() only accepts models
|
||||
emit modelsDropped(data, idx, isInsert, (action == Qt::MoveAction));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
QMimeData *TreeModel::getModelsMimeData(const QModelIndexList & indexes, QMimeData * mimeData) const
|
||||
{
|
||||
if (!mimeData)
|
||||
mimeData = new QMimeData();
|
||||
QByteArray mData;
|
||||
encodeModelsData(indexes, &mData);
|
||||
mimeData->setData("application/x-companion-modeldata", mData);
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
QMimeData *TreeModel::getGeneralMimeData(QMimeData * mimeData) const
|
||||
{
|
||||
if (!mimeData)
|
||||
mimeData = new QMimeData();
|
||||
QByteArray mData;
|
||||
encodeGeneralData(&mData);
|
||||
mimeData->setData("application/x-companion-generaldata", mData);
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
QMimeData *TreeModel::getHeaderMimeData(QMimeData * mimeData) const
|
||||
{
|
||||
if (!mimeData)
|
||||
mimeData = new QMimeData();
|
||||
QByteArray mData;
|
||||
encodeHeaderData(&mData);
|
||||
mimeData->setData("application/x-companion-radiodata-header", mData);
|
||||
return mimeData;
|
||||
}
|
||||
|
||||
QUuid TreeModel::getMimeDataSourceId(const QMimeData * mimeData) const
|
||||
{
|
||||
MimeHeaderData header;
|
||||
decodeHeaderData(mimeData, &header);
|
||||
return header.instanceId;
|
||||
}
|
||||
|
||||
bool TreeModel::hasSupportedMimeData(const QMimeData * mimeData) const
|
||||
{
|
||||
foreach (const QString & mtype, mimeTypes()) {
|
||||
if (mimeData->hasFormat(mtype))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TreeModel::hasModelsMimeData(const QMimeData * mimeData) const
|
||||
{
|
||||
return mimeData->hasFormat("application/x-companion-modeldata");
|
||||
}
|
||||
|
||||
bool TreeModel::hasGenralMimeData(const QMimeData * mimeData) const
|
||||
{
|
||||
return mimeData->hasFormat("application/x-companion-generaldata");
|
||||
}
|
||||
|
||||
bool TreeModel::hasHeaderMimeData(const QMimeData * mimeData) const
|
||||
{
|
||||
return mimeData->hasFormat("application/x-companion-radiodata-header");
|
||||
}
|
||||
|
||||
// returns true if mime data origin was this data model (vs. from another file window)
|
||||
bool TreeModel::hasOwnMimeData(const QMimeData * mimeData) const
|
||||
{
|
||||
return (getMimeDataSourceId(mimeData) == mimeHeaderData.instanceId);
|
||||
}
|
||||
|
||||
void TreeModel::encodeModelsData(const QModelIndexList & indexes, QByteArray * data) const
|
||||
{
|
||||
foreach (const QModelIndex &index, indexes) {
|
||||
if (index.isValid() && index.column() == 0) {
|
||||
if (!getItem(index)->isCategory()) { // TODO: encode categoreis also
|
||||
data->append('M');
|
||||
data->append((char *)&radioData->models[getModelIndex(index)], sizeof(ModelData));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TreeModel::encodeGeneralData(QByteArray * data) const
|
||||
{
|
||||
data->append('G');
|
||||
data->append((char *)&radioData->generalSettings, sizeof(GeneralSettings));
|
||||
}
|
||||
|
||||
void TreeModel::encodeHeaderData(QByteArray * data) const
|
||||
{
|
||||
// We use a unique ID representing this TreeModel instance (a unique file).
|
||||
// This can be used eg. to detect cross-file drop operations.
|
||||
QDataStream stream(data, QIODevice::WriteOnly);
|
||||
stream << mimeHeaderData.dataVersion;
|
||||
stream << mimeHeaderData.instanceId;
|
||||
}
|
||||
|
||||
// static
|
||||
bool TreeModel::decodeHeaderData(const QMimeData * mimeData, MimeHeaderData * header)
|
||||
{
|
||||
if (header && mimeData->hasFormat("application/x-companion-radiodata-header")) {
|
||||
QByteArray data = mimeData->data("application/x-companion-radiodata-header");
|
||||
QDataStream stream(&data, QIODevice::ReadOnly);
|
||||
stream >> header->dataVersion >> header->instanceId;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// static
|
||||
bool TreeModel::decodeMimeData(const QMimeData * mimeData, QVector<ModelData> * models, GeneralSettings * gs, bool * hasGenSet)
|
||||
{
|
||||
bool ret = false;
|
||||
char * gData;
|
||||
|
||||
if (hasGenSet)
|
||||
*hasGenSet = false;
|
||||
|
||||
if (models && mimeData->hasFormat("application/x-companion-modeldata")) {
|
||||
QByteArray mdlData = mimeData->data("application/x-companion-modeldata");
|
||||
gData = mdlData.data();
|
||||
int size = 0;
|
||||
while (size < mdlData.size()) {
|
||||
char c = *gData++;
|
||||
if (c != 'M')
|
||||
break;
|
||||
ModelData model(*((ModelData *)gData));
|
||||
models->append(model);
|
||||
gData += sizeof(ModelData);
|
||||
size += sizeof(ModelData) + 1;
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
// General settings
|
||||
if (gs && mimeData->hasFormat("application/x-companion-generaldata")) {
|
||||
QByteArray genData = mimeData->data("application/x-companion-generaldata");
|
||||
gData = genData.data();
|
||||
char c = *gData++;
|
||||
if (c == 'G') {
|
||||
*gs = *((GeneralSettings *)gData);
|
||||
ret = true;
|
||||
if (hasGenSet)
|
||||
*hasGenSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
// static
|
||||
int TreeModel::countModelsInMimeData(const QMimeData * mimeData)
|
||||
{
|
||||
int ret = 0;
|
||||
if (mimeData->hasFormat("application/x-companion-modeldata")) {
|
||||
QByteArray mdlData = mimeData->data("application/x-companion-modeldata");
|
||||
ret = mdlData.size() / (sizeof(ModelData) + 1);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
TreeItem * TreeModel::getItem(const QModelIndex & index) const
|
||||
{
|
||||
if (index.isValid()) {
|
||||
TreeItem * item = static_cast<TreeItem *>(index.internalPointer());
|
||||
if (item) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return rootItem;
|
||||
}
|
||||
|
||||
// recursive
|
||||
QModelIndex TreeModel::getIndexForModel(const int modelIndex, QModelIndex parent)
|
||||
{
|
||||
for (int i=0; i < rowCount(parent); ++i) {
|
||||
QModelIndex idx = index(i, 0, parent);
|
||||
if (hasChildren(idx) && (idx = getIndexForModel(modelIndex, idx)).isValid())
|
||||
return idx;
|
||||
if (getItem(idx)->getModelIndex() == modelIndex)
|
||||
return idx;
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
QModelIndex TreeModel::getIndexForCategory(const int categoryIndex)
|
||||
{
|
||||
for (int i=0; i < rowCount(); ++i) {
|
||||
if (getItem(index(i, 0))->getCategoryIndex() == categoryIndex)
|
||||
return index(i, 0);
|
||||
}
|
||||
return QModelIndex();
|
||||
}
|
||||
|
||||
int TreeModel::getAvailableEEpromSize()
|
||||
{
|
||||
return availableEEpromSize;
|
||||
}
|
||||
|
||||
int TreeModel::getModelIndex(const QModelIndex & index) const
|
||||
{
|
||||
return getItem(index)->getModelIndex();
|
||||
}
|
||||
|
||||
int TreeModel::getCategoryIndex(const QModelIndex & index) const
|
||||
{
|
||||
return getItem(index)->getCategoryIndex();
|
||||
}
|
||||
|
||||
int TreeModel::rowNumber(const QModelIndex & index) const
|
||||
{
|
||||
return getItem(index)->childNumber();
|
||||
}
|
||||
|
||||
bool TreeModel::isCategoryType(const QModelIndex & index) const
|
||||
{
|
||||
return index.isValid() && getItem(index)->isCategory();
|
||||
}
|
||||
|
||||
bool TreeModel::isModelType(const QModelIndex & index) const
|
||||
{
|
||||
return index.isValid() && getItem(index)->isModel();
|
||||
}
|
||||
|
||||
void TreeModel::markItemForCut(const QModelIndex & index, bool on)
|
||||
{
|
||||
if (index.isValid() && index.column() == 0)
|
||||
getItem(index)->setFlag(TreeItem::MarkedForCut, on);
|
||||
}
|
||||
|
||||
void TreeModel::markItemsForCut(const QModelIndexList & indexes, bool on)
|
||||
{
|
||||
foreach (const QModelIndex &index, indexes)
|
||||
markItemForCut(index, on);
|
||||
}
|
||||
|
||||
// onRowsAboutToBeRemoved could be a way to deal with models being drag-drop moved to another window/file.
|
||||
// TreeModel detects these as removals and runs removeRows(), which deletes the Model indexes
|
||||
// but not the actual models from the RadioData::models array.
|
||||
// BUT this also runs when moving rows within our own tree, and if there is an error during the move,
|
||||
// or the user cancels the operation, removeRows() is still called automatically somewhere inside QAbstractItemModel().
|
||||
// If a solution could be found to this problem then we could enable DnD-moving models between file windows.
|
||||
/*
|
||||
void TreeModel::onRowsAboutToBeRemoved(const QModelIndex & parent, int first, int last)
|
||||
{
|
||||
qDebug() << parent << first << last;
|
||||
QVector<int> modelIndices;
|
||||
for (int i=first; i <= last; ++i) {
|
||||
modelIndices << getItem(index(i, 0, parent))->getModelIndex();
|
||||
}
|
||||
if (modelIndices.size())
|
||||
emit modelsRemoved(modelIndices);
|
||||
}
|
||||
*/
|
||||
|
||||
void TreeModel::onRowsRemoved(const QModelIndex & parent, int first, int last)
|
||||
{
|
||||
// This is a workaround to deal with models being DnD moved to another window/file or if user cancels a DnD move within our own.
|
||||
// TreeModel detects these as removals and runs removeRows(), which deletes the Model indexes but not our actual models. See notes above.
|
||||
//qDebug() << parent << first << last;
|
||||
emit refreshRequested(); // request refresh from View because it may have it's own ideas
|
||||
}
|
||||
|
||||
void TreeModel::refresh()
|
||||
{
|
||||
EEPROMInterface * eepromInterface = getCurrentEEpromInterface();
|
||||
Board::Type board = eepromInterface->getBoard();
|
||||
TreeItem * defaultCategoryItem = NULL;
|
||||
bool hasCategories = getCurrentFirmware()->getCapability(Capability::HasModelCategories);
|
||||
bool hasEepromSizeData = (rootItem->columnCount() > 2);
|
||||
|
||||
if (!IS_SKY9X(board) && !IS_HORUS(board)) {
|
||||
if (hasEepromSizeData) {
|
||||
availableEEpromSize = Boards::getEEpromSize(board) - 64; // let's consider fat
|
||||
availableEEpromSize -= 16 * ((eepromInterface->getSize(radioData->generalSettings) + 14) / 15);
|
||||
}
|
||||
|
||||
this->blockSignals(true); // make sure onRowsRemoved is not triggered
|
||||
removeRows(0, rowCount());
|
||||
this->blockSignals(false);
|
||||
|
||||
TreeItem * defaultCategoryItem = NULL;
|
||||
|
||||
if (IS_HORUS(board)) {
|
||||
if (hasCategories) {
|
||||
for (unsigned i = 0; i < radioData->categories.size(); i++) {
|
||||
TreeItem * current = rootItem->appendChild(i, -1);
|
||||
current->setData(0, QString(radioData->categories[i].name));
|
||||
@@ -266,7 +662,10 @@ void TreeModel::refresh()
|
||||
ModelData & model = radioData->models[i];
|
||||
int currentColumn = 0;
|
||||
TreeItem * current = NULL;
|
||||
if (IS_HORUS(board)) {
|
||||
|
||||
model.modelIndex = i;
|
||||
|
||||
if (hasCategories) {
|
||||
if (!model.isEmpty()) {
|
||||
TreeItem * categoryItem;
|
||||
// TODO category should be set to -1 if not Horus
|
||||
@@ -297,7 +696,7 @@ void TreeModel::refresh()
|
||||
else
|
||||
modelName = QString().sprintf("Model%02d", i+1);
|
||||
current->setData(currentColumn++, modelName);
|
||||
if (!IS_SKY9X(board) && !IS_HORUS(board)) {
|
||||
if (hasEepromSizeData) {
|
||||
int size = eepromInterface->getSize(model);
|
||||
current->setData(currentColumn++, QString().sprintf("%5d", size));
|
||||
size = 16 * ((size + 14) / 15);
|
||||
@@ -310,190 +709,7 @@ void TreeModel::refresh()
|
||||
}
|
||||
}
|
||||
|
||||
if (!IS_SKY9X(board) && !IS_HORUS(board)) {
|
||||
if (hasEepromSizeData) {
|
||||
availableEEpromSize = (availableEEpromSize / 16) * 15;
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
ModelsListWidget::ModelsListWidget(QWidget * parent):
|
||||
QTreeView(parent),
|
||||
radioData(NULL)
|
||||
{
|
||||
setColumnWidth(0, 50);
|
||||
setColumnWidth(2, 100);
|
||||
|
||||
active_highlight_color = palette().color(QPalette::Active, QPalette::Highlight);
|
||||
}
|
||||
|
||||
void ModelsListWidget::mousePressEvent(QMouseEvent *event)
|
||||
{
|
||||
if (event->button() == Qt::LeftButton)
|
||||
dragStartPosition = event->pos();
|
||||
|
||||
QTreeView::mousePressEvent(event);
|
||||
}
|
||||
|
||||
void ModelsListWidget::mouseMoveEvent(QMouseEvent *event)
|
||||
{
|
||||
if (!(event->buttons() & Qt::LeftButton))
|
||||
return;
|
||||
|
||||
if ((event->pos() - dragStartPosition).manhattanLength() < QApplication::startDragDistance())
|
||||
return;
|
||||
|
||||
QDrag * drag = new QDrag(this);
|
||||
|
||||
QByteArray gmData;
|
||||
doCopy(&gmData);
|
||||
|
||||
QMimeData * mimeData = new QMimeData;
|
||||
mimeData->setData("application/x-companion", gmData);
|
||||
|
||||
drag->setMimeData(mimeData);
|
||||
|
||||
//Qt::DropAction dropAction = drag->exec(Qt::CopyAction | Qt::MoveAction, Qt::CopyAction);
|
||||
|
||||
//if(dropAction==Qt::MoveAction)
|
||||
|
||||
// QTreeWidget::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void ModelsListWidget::saveSelection()
|
||||
{
|
||||
/*currentSelection.current_item = currentItem();
|
||||
for (int i=0; i<getCurrentFirmware()->getCapability(Models)+1; ++i) {
|
||||
currentSelection.selected[i] = selectionModel()->isSelected(model()->index(i, 0));
|
||||
}*/
|
||||
}
|
||||
|
||||
void ModelsListWidget::restoreSelection()
|
||||
{
|
||||
/*setCurrentItem(currentSelection.current_item);
|
||||
for (int i=0; i<getCurrentFirmware()->getCapability(Models)+1; ++i) {
|
||||
selectionModel()->select(model()->index(i, 0), currentSelection.selected[i] ? QItemSelectionModel::Select : QItemSelectionModel::Deselect);
|
||||
}*/
|
||||
}
|
||||
|
||||
void ModelsListWidget::dragEnterEvent(QDragEnterEvent *event)
|
||||
{
|
||||
if (event->mimeData()->hasFormat("application/x-companion")) {
|
||||
event->acceptProposedAction();
|
||||
saveSelection();
|
||||
}
|
||||
}
|
||||
|
||||
void ModelsListWidget::dragLeaveEvent(QDragLeaveEvent *)
|
||||
{
|
||||
restoreSelection();
|
||||
}
|
||||
|
||||
void ModelsListWidget::dragMoveEvent(QDragMoveEvent *event)
|
||||
{
|
||||
int row = indexAt(event->pos()).row();
|
||||
const QMimeData * mimeData = event->mimeData();
|
||||
if (mimeData->hasFormat("application/x-companion")) {
|
||||
QByteArray gmData = mimeData->data("application/x-companion");
|
||||
event->acceptProposedAction();
|
||||
clearSelection();
|
||||
DragDropHeader * header = (DragDropHeader *)gmData.data();
|
||||
if (row >= 0) {
|
||||
if (header->general_settings)
|
||||
selectionModel()->select(model()->index(0, 0), QItemSelectionModel::Select);
|
||||
for (int i=row, end=std::min(getCurrentFirmware()->getCapability(Models)+1, row+header->models_count); i<end; i++)
|
||||
selectionModel()->select(model()->index(i, 0), QItemSelectionModel::Select);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelsListWidget::dropEvent(QDropEvent *event)
|
||||
{
|
||||
int row = this->indexAt(event->pos()).row();
|
||||
if (row < 0)
|
||||
return;
|
||||
|
||||
// QMessageBox::warning(this, tr("Companion"), tr("Index :%1").arg(row));
|
||||
const QMimeData * mimeData = event->mimeData();
|
||||
|
||||
if (mimeData->hasFormat("application/x-companion")) {
|
||||
QByteArray gmData = mimeData->data("application/x-companion");
|
||||
if (event->source() && event->dropAction() == Qt::MoveAction)
|
||||
((ModelsListWidget*)event->source())->doCut(&gmData);
|
||||
doPaste(&gmData, row);
|
||||
clearSelection();
|
||||
// setCurrentItem(topLevelItem(row));
|
||||
DragDropHeader * header = (DragDropHeader *)gmData.data();
|
||||
if (header->general_settings)
|
||||
selectionModel()->select(model()->index(0, 0), QItemSelectionModel::Select);
|
||||
for (int i=row, end=std::min(getCurrentFirmware()->getCapability(Models)+1, row+header->models_count); i<end; i++)
|
||||
selectionModel()->select(model()->index(i, 0), QItemSelectionModel::Select);
|
||||
}
|
||||
event->acceptProposedAction();
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
void ModelsListWidget::focusInEvent ( QFocusEvent * event )
|
||||
{
|
||||
QTreeView::focusInEvent(event);
|
||||
QPalette palette = this->palette();
|
||||
palette.setColor(QPalette::Active, QPalette::Highlight, active_highlight_color);
|
||||
palette.setColor(QPalette::Inactive, QPalette::Highlight, active_highlight_color);
|
||||
setPalette(palette);
|
||||
}
|
||||
|
||||
void ModelsListWidget::focusOutEvent ( QFocusEvent * event )
|
||||
{
|
||||
QTreeView::focusOutEvent(event);
|
||||
QPalette palette = this->palette();
|
||||
palette.setColor(QPalette::Active, QPalette::Highlight, palette.color(QPalette::Active, QPalette::Midlight));
|
||||
palette.setColor(QPalette::Inactive, QPalette::Highlight, palette.color(QPalette::Active, QPalette::Midlight));
|
||||
setPalette(palette);
|
||||
}
|
||||
#endif
|
||||
|
||||
void ModelsListWidget::doCut(QByteArray * gmData)
|
||||
{
|
||||
bool modified = false;
|
||||
DragDropHeader * header = (DragDropHeader *)gmData->data();
|
||||
for (int i=0; i<header->models_count; i++) {
|
||||
if (radioData->generalSettings.currModelIndex != (unsigned int)header->models[i]-1) {
|
||||
radioData->models[header->models[i]-1].clear();
|
||||
modified=true;
|
||||
}
|
||||
}
|
||||
if (modified) {
|
||||
((MdiChild *)parent())->setModified();
|
||||
}
|
||||
}
|
||||
|
||||
void ModelsListWidget::duplicate()
|
||||
{
|
||||
int i = this->currentRow();
|
||||
if (i && i<getCurrentFirmware()->getCapability(Models)) {
|
||||
ModelData * model = &radioData->models[i-1];
|
||||
while (i<getCurrentFirmware()->getCapability(Models)) {
|
||||
if (radioData->models[i].isEmpty()) {
|
||||
radioData->models[i] = *model;
|
||||
strcpy(radioData->models[i].filename, qPrintable(radioData->getNextModelFilename()));
|
||||
((MdiChild *)parent())->setModified();
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (i==getCurrentFirmware()->getCapability(Models)) {
|
||||
QMessageBox::warning(this, "Companion", tr("No free slot available, cannot duplicate"), QMessageBox::Ok);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ModelsListWidget::onCurrentItemChanged(QTreeWidgetItem * current, QTreeWidgetItem *)
|
||||
{
|
||||
/*int index = indexOfTopLevelItem(current);
|
||||
if (!isVisible())
|
||||
((MdiChild*)parent())->viableModelSelected(false);
|
||||
else if (index<1)
|
||||
((MdiChild*)parent())->viableModelSelected(false);
|
||||
else
|
||||
((MdiChild*)parent())->viableModelSelected(!radioData->models[currentRow()-1].isEmpty()); */
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -22,17 +22,15 @@
|
||||
#define _MODELSLIST_H_
|
||||
|
||||
#include "eeprominterface.h"
|
||||
#include <QtWidgets>
|
||||
|
||||
struct CurrentSelection
|
||||
{
|
||||
QTreeWidgetItem * current_item;
|
||||
bool selected[CPN_MAX_MODELS+1];
|
||||
};
|
||||
#include <QAbstractItemModel>
|
||||
#include <QMimeData>
|
||||
#include <QUuid>
|
||||
|
||||
class TreeItem
|
||||
{
|
||||
public:
|
||||
enum TreeItemFlags { MarkedForCut = 0x01 };
|
||||
|
||||
explicit TreeItem(const QVector<QVariant> & itemData);
|
||||
explicit TreeItem(TreeItem * parent, int categoryIndex, int modelIndex);
|
||||
~TreeItem();
|
||||
@@ -42,14 +40,26 @@ class TreeItem
|
||||
int columnCount() const;
|
||||
QVariant data(int column) const;
|
||||
TreeItem * appendChild(int categoryIndex, int modelIndex);
|
||||
TreeItem * parent();
|
||||
TreeItem * insertChild(const int row, int categoryIndex, int modelIndex);
|
||||
bool removeChildren(int position, int count);
|
||||
bool insertChildren(int row, int count);
|
||||
|
||||
int childNumber() const;
|
||||
bool setData(int column, const QVariant &value);
|
||||
|
||||
TreeItem * parent() { return parentItem; }
|
||||
void setParent(TreeItem * p) { parentItem = p; }
|
||||
int getModelIndex() const { return modelIndex; }
|
||||
void setModelIndex(int value) { modelIndex = value; }
|
||||
int getCategoryIndex() const { return categoryIndex; }
|
||||
void setCategoryIndex(int value) { categoryIndex = value; }
|
||||
|
||||
quint16 getFlags() const { return flags; }
|
||||
void setFlags(const quint16 & value) { flags = value; }
|
||||
void setFlag(const quint16 & flag, const bool on = true);
|
||||
|
||||
bool isCategory() const;
|
||||
bool isModel() const;
|
||||
|
||||
private:
|
||||
QList<TreeItem*> childItems;
|
||||
@@ -57,6 +67,7 @@ class TreeItem
|
||||
TreeItem * parentItem;
|
||||
int categoryIndex;
|
||||
int modelIndex;
|
||||
quint16 flags;
|
||||
};
|
||||
|
||||
|
||||
@@ -65,47 +76,81 @@ class TreeModel : public QAbstractItemModel
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
struct MimeHeaderData {
|
||||
QUuid instanceId;
|
||||
quint16 dataVersion;
|
||||
};
|
||||
|
||||
TreeModel(RadioData * radioData, QObject *parent = 0);
|
||||
virtual ~TreeModel();
|
||||
|
||||
QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE;
|
||||
QVariant headerData(int section, Qt::Orientation orientation,
|
||||
int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
|
||||
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
|
||||
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
|
||||
virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) Q_DECL_OVERRIDE;
|
||||
|
||||
QModelIndex index(int row, int column,
|
||||
const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
|
||||
virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
virtual QModelIndex parent(const QModelIndex &index) const Q_DECL_OVERRIDE;
|
||||
virtual Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
|
||||
|
||||
int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
virtual int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
|
||||
virtual bool removeRows(int position, int rows, const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE;
|
||||
//virtual bool insertRows(int row, int count, const QModelIndex & parent) Q_DECL_OVERRIDE;
|
||||
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE;
|
||||
bool setData(const QModelIndex &index, const QVariant &value,
|
||||
int role = Qt::EditRole) Q_DECL_OVERRIDE;
|
||||
virtual QStringList mimeTypes() const Q_DECL_OVERRIDE;
|
||||
virtual Qt::DropActions supportedDropActions() const Q_DECL_OVERRIDE;
|
||||
virtual Qt::DropActions supportedDragActions() const Q_DECL_OVERRIDE;
|
||||
virtual QMimeData * mimeData(const QModelIndexList & indexes) const Q_DECL_OVERRIDE;
|
||||
virtual bool canDropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) const Q_DECL_OVERRIDE;
|
||||
virtual bool dropMimeData(const QMimeData * data, Qt::DropAction action, int row, int column, const QModelIndex & parent) Q_DECL_OVERRIDE;
|
||||
|
||||
bool removeRows(int position, int rows,
|
||||
const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE;
|
||||
void encodeModelsData(const QModelIndexList & indexes, QByteArray * data) const;
|
||||
void encodeGeneralData(QByteArray * data) const;
|
||||
void encodeHeaderData(QByteArray * data) const;
|
||||
QMimeData * getModelsMimeData(const QModelIndexList & indexes, QMimeData * mimeData = NULL) const;
|
||||
QMimeData * getGeneralMimeData(QMimeData * mimeData = NULL) const;
|
||||
QMimeData * getHeaderMimeData(QMimeData * mimeData = NULL) const;
|
||||
QUuid getMimeDataSourceId(const QMimeData * mimeData) const;
|
||||
bool hasSupportedMimeData(const QMimeData * mimeData) const;
|
||||
bool hasModelsMimeData(const QMimeData * mimeData) const;
|
||||
bool hasGenralMimeData(const QMimeData * mimeData) const;
|
||||
bool hasHeaderMimeData(const QMimeData * mimeData) const;
|
||||
bool hasOwnMimeData(const QMimeData * mimeData) const;
|
||||
|
||||
static bool decodeHeaderData(const QMimeData * mimeData, MimeHeaderData * header);
|
||||
static bool decodeMimeData(const QMimeData * mimeData, QVector<ModelData> * models = NULL, GeneralSettings * gs = NULL, bool * hasGenSet = NULL);
|
||||
static int countModelsInMimeData(const QMimeData * mimeData);
|
||||
|
||||
QModelIndex getIndexForModel(const int modelIndex, QModelIndex parent = QModelIndex());
|
||||
QModelIndex getIndexForCategory(const int categoryIndex);
|
||||
int getAvailableEEpromSize();
|
||||
int getModelIndex(const QModelIndex & index) const;
|
||||
int getCategoryIndex(const QModelIndex & index) const;
|
||||
int rowNumber(const QModelIndex & index = QModelIndex()) const;
|
||||
bool isCategoryType(const QModelIndex & index) const;
|
||||
bool isModelType(const QModelIndex & index) const;
|
||||
|
||||
public slots:
|
||||
void markItemForCut(const QModelIndex & index, bool on = true);
|
||||
void markItemsForCut(const QModelIndexList & indexes, bool on = true);
|
||||
void refresh();
|
||||
|
||||
int getAvailableEEpromSize() { return availableEEpromSize; }
|
||||
signals:
|
||||
void modelsDropped(const QMimeData * mimeData, const QModelIndex toRowIdx, const bool insert, const bool move);
|
||||
void modelsRemoved(const QVector<int> modelIndices);
|
||||
void refreshRequested();
|
||||
|
||||
int getModelIndex(const QModelIndex & index) const {
|
||||
return getItem(index)->getModelIndex();
|
||||
}
|
||||
private slots:
|
||||
//void onRowsAboutToBeRemoved(const QModelIndex & parent, int first, int last);
|
||||
void onRowsRemoved(const QModelIndex & parent, int first, int last);
|
||||
|
||||
int getCategoryIndex(const QModelIndex & index) const {
|
||||
return getItem(index)->getCategoryIndex();
|
||||
}
|
||||
|
||||
int rowNumber(const QModelIndex & index = QModelIndex()) const {
|
||||
return getItem(index)->childNumber();
|
||||
}
|
||||
private:
|
||||
TreeItem * getItem(const QModelIndex & index) const;
|
||||
|
||||
TreeItem * rootItem;
|
||||
RadioData * radioData;
|
||||
int availableEEpromSize;
|
||||
MimeHeaderData mimeHeaderData;
|
||||
};
|
||||
|
||||
#endif // _MODELSLIST_H_
|
||||
|
||||
@@ -121,13 +121,10 @@ MultiModelPrinter::MultiModelPrinter(Firmware * firmware):
|
||||
|
||||
MultiModelPrinter::~MultiModelPrinter()
|
||||
{
|
||||
for(int i=0; i < modelPrinterMap.size(); i++) {
|
||||
if (modelPrinterMap.value(i).second)
|
||||
delete modelPrinterMap.value(i).second;
|
||||
}
|
||||
clearModels();
|
||||
}
|
||||
|
||||
void MultiModelPrinter::setModel(int idx, const ModelData & model, const GeneralSettings & generalSettings)
|
||||
void MultiModelPrinter::setModel(int idx, const ModelData * model, const GeneralSettings * generalSettings)
|
||||
{
|
||||
if (modelPrinterMap.contains(idx) && modelPrinterMap.value(idx).second) {
|
||||
// free existing model printer
|
||||
@@ -135,13 +132,22 @@ void MultiModelPrinter::setModel(int idx, const ModelData & model, const General
|
||||
modelPrinterMap[idx].second = NULL;
|
||||
}
|
||||
|
||||
QPair<const ModelData *, ModelPrinter *> pair(&model, new ModelPrinter(firmware, generalSettings, model));
|
||||
QPair<const ModelData *, ModelPrinter *> pair(model, new ModelPrinter(firmware, *generalSettings, *model));
|
||||
modelPrinterMap.insert(idx, pair); // QMap.insert will replace any existing key
|
||||
}
|
||||
|
||||
void MultiModelPrinter::setModel(int idx, const ModelData & model)
|
||||
void MultiModelPrinter::setModel(int idx, const ModelData * model)
|
||||
{
|
||||
setModel(idx, model, defaultSettings);
|
||||
setModel(idx, model, &defaultSettings);
|
||||
}
|
||||
|
||||
void MultiModelPrinter::clearModels()
|
||||
{
|
||||
for(int i=0; i < modelPrinterMap.size(); i++) {
|
||||
if (modelPrinterMap.value(i).second)
|
||||
delete modelPrinterMap.value(i).second;
|
||||
}
|
||||
modelPrinterMap.clear();
|
||||
}
|
||||
|
||||
QString MultiModelPrinter::print(QTextDocument * document)
|
||||
|
||||
@@ -34,8 +34,9 @@ class MultiModelPrinter: public QObject
|
||||
MultiModelPrinter(Firmware * firmware);
|
||||
virtual ~MultiModelPrinter();
|
||||
|
||||
void setModel(int idx, const ModelData & model, const GeneralSettings & generalSettings);
|
||||
void setModel(int idx, const ModelData & model);
|
||||
void setModel(int idx, const ModelData * model, const GeneralSettings * generalSettings);
|
||||
void setModel(int idx, const ModelData * model);
|
||||
void clearModels();
|
||||
QString print(QTextDocument * document);
|
||||
|
||||
protected:
|
||||
|
||||
@@ -36,7 +36,7 @@ PrintDialog::PrintDialog(QWidget *parent, Firmware * firmware, GeneralSettings &
|
||||
ui->setupUi(this);
|
||||
setWindowIcon(CompanionIcon("print.png"));
|
||||
setWindowTitle(model.name);
|
||||
multimodelprinter.setModel(0, model, generalSettings);
|
||||
multimodelprinter.setModel(0, &model, &generalSettings);
|
||||
ui->textEdit->setHtml(multimodelprinter.print(ui->textEdit->document()));
|
||||
if (!printfilename.isEmpty()) {
|
||||
printToFile();
|
||||
|
||||
@@ -1021,6 +1021,8 @@ class ModelData {
|
||||
int category;
|
||||
char name[15+1];
|
||||
char filename[16+1];
|
||||
int modelIndex; // Companion only, temporary index position managed by data model.
|
||||
|
||||
TimerData timers[CPN_MAX_TIMERS];
|
||||
bool noGlobalFunctions;
|
||||
bool thrTrim; // Enable Throttle Trim
|
||||
|
||||
@@ -434,6 +434,9 @@ QStringList AppData::simuDbgFilters() { return _simuDbgFilters; }
|
||||
QByteArray AppData::mainWinGeo() { return _mainWinGeo; }
|
||||
QByteArray AppData::mainWinState() { return _mainWinState; }
|
||||
QByteArray AppData::modelEditGeo() { return _modelEditGeo; }
|
||||
QByteArray AppData::mdiWinGeo() { return _mdiWinGeo; }
|
||||
QByteArray AppData::mdiWinState() { return _mdiWinState; }
|
||||
QByteArray AppData::compareWinGeo(){ return _compareWinGeo; }
|
||||
|
||||
QString AppData::armMcu() { return _armMcu; }
|
||||
QString AppData::avrArguments() { return _avrArguments; }
|
||||
@@ -464,8 +467,10 @@ bool AppData::snapToClpbrd() { return _snapToClpbrd; }
|
||||
bool AppData::autoCheckApp() { return _autoCheckApp; }
|
||||
bool AppData::autoCheckFw() { return _autoCheckFw; }
|
||||
bool AppData::simuSW() { return _simuSW; }
|
||||
bool AppData::useWizard() { return _useWizard; }
|
||||
bool AppData::tabbedMdi() { return _tabbedMdi; }
|
||||
bool AppData::removeModelSlots() { return _remvModelSlots; }
|
||||
|
||||
int AppData::newModelAction() { return _newModelAction; }
|
||||
int AppData::backLight() { return _backLight; }
|
||||
int AppData::embedSplashes() { return _embedSplashes; }
|
||||
int AppData::fwServerFails() { return _fwServerFails; }
|
||||
@@ -486,6 +491,9 @@ void AppData::simuDbgFilters (const QStringList x) { store(x, _simuDbgFilters,
|
||||
void AppData::mainWinGeo (const QByteArray x) { store(x, _mainWinGeo, "mainWindowGeometry" );}
|
||||
void AppData::mainWinState (const QByteArray x) { store(x, _mainWinState, "mainWindowState" );}
|
||||
void AppData::modelEditGeo (const QByteArray x) { store(x, _modelEditGeo, "modelEditGeometry" );}
|
||||
void AppData::mdiWinGeo (const QByteArray x) { store(x, _mdiWinGeo, "mdiWinGeo" );}
|
||||
void AppData::mdiWinState (const QByteArray x) { store(x, _mdiWinState, "mdiWinState" );}
|
||||
void AppData::compareWinGeo (const QByteArray x) { store(x, _compareWinGeo, "compareWinGeo" );}
|
||||
|
||||
void AppData::armMcu (const QString x) { store(x, _armMcu, "arm_mcu" );}
|
||||
void AppData::avrArguments (const QString x) { store(x, _avrArguments, "avr_arguments" );}
|
||||
@@ -516,8 +524,10 @@ void AppData::snapToClpbrd (const bool x) { store(x, _snapToClpbrd,
|
||||
void AppData::autoCheckApp (const bool x) { store(x, _autoCheckApp, "startup_check_companion" );}
|
||||
void AppData::autoCheckFw (const bool x) { store(x, _autoCheckFw, "startup_check_fw" );}
|
||||
void AppData::simuSW (const bool x) { store(x, _simuSW, "simuSW" );}
|
||||
void AppData::useWizard (const bool x) { store(x, _useWizard, "useWizard" );}
|
||||
void AppData::tabbedMdi (const bool x) { store(x, _tabbedMdi, "tabbedMdi" );}
|
||||
void AppData::removeModelSlots(const bool x) { store(x, _remvModelSlots, "removeModelSlots" );}
|
||||
|
||||
void AppData::newModelAction (const int x) { store(x, _newModelAction, "newModelAction" );}
|
||||
void AppData::backLight (const int x) { store(x, _backLight, "backLight" );}
|
||||
void AppData::embedSplashes (const int x) { store(x, _embedSplashes, "embedded_splashes" );}
|
||||
void AppData::fwServerFails (const int x) { store(x, _fwServerFails, "fwserver" );}
|
||||
@@ -569,6 +579,9 @@ void AppData::init()
|
||||
getset( _mainWinGeo, "mainWindowGeometry" ,"" );
|
||||
getset( _mainWinState, "mainWindowState" ,"" );
|
||||
getset( _modelEditGeo, "modelEditGeometry" ,"" );
|
||||
getset( _mdiWinGeo, "mdiWinGeo" ,"" );
|
||||
getset( _mdiWinState, "mdiWinState" ,"" );
|
||||
getset( _compareWinGeo, "compareWinGeo" ,"" );
|
||||
|
||||
getset( _armMcu, "arm_mcu" ,"at91sam3s4-9x" );
|
||||
getset( _avrArguments, "avr_arguments" ,"" );
|
||||
@@ -607,8 +620,10 @@ void AppData::init()
|
||||
getset( _autoCheckApp, "startup_check_companion" ,true );
|
||||
getset( _autoCheckFw, "startup_check_fw" ,true );
|
||||
getset( _simuSW, "simuSW" ,false );
|
||||
getset( _useWizard, "useWizard" ,true );
|
||||
getset( _tabbedMdi, "tabbedMdi" ,false );
|
||||
getset( _remvModelSlots, "removeModelSlots" ,true );
|
||||
|
||||
getset( _newModelAction, "newModelAction" ,1 );
|
||||
getset( _backLight, "backLight" ,0 );
|
||||
getset( _embedSplashes, "embedded_splashes" ,0 );
|
||||
getset( _fwServerFails, "fwserver" ,0 );
|
||||
@@ -636,7 +651,7 @@ QMap<int, QString> AppData::getActiveProfiles()
|
||||
|
||||
bool AppData::importSettings(QSettings & toSettings)
|
||||
{
|
||||
QSettings * fromSettings;
|
||||
QSettings * fromSettings = NULL;
|
||||
|
||||
QSettings settings21("OpenTX", "Companion 2.1");
|
||||
QSettings settings20("OpenTX", "Companion 2.0");
|
||||
@@ -651,8 +666,6 @@ bool AppData::importSettings(QSettings & toSettings)
|
||||
fromSettings = &settings16;
|
||||
else if (settings9x.contains("default_mode"))
|
||||
fromSettings = &settings9x;
|
||||
else
|
||||
return false;
|
||||
|
||||
// do not copy these settings
|
||||
QStringList excludeKeys = QStringList() << "compilation-server";
|
||||
@@ -664,17 +677,19 @@ bool AppData::importSettings(QSettings & toSettings)
|
||||
#endif
|
||||
|
||||
// import settings
|
||||
foreach (const QString & key, fromSettings->allKeys()) {
|
||||
if (fromSettings->value(key).isValid() && !excludeKeys.contains(key)) {
|
||||
toSettings.setValue(key, fromSettings->value(key));
|
||||
if (fromSettings) {
|
||||
foreach (const QString & key, fromSettings->allKeys()) {
|
||||
if (fromSettings->value(key).isValid() && !excludeKeys.contains(key)) {
|
||||
toSettings.setValue(key, fromSettings->value(key));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Additional adjustments for companion9x settings
|
||||
if (fromSettings->applicationName() == "companion9x") {
|
||||
if (fromSettings && fromSettings->applicationName() == "companion9x") {
|
||||
// Store old values in new locations
|
||||
autoCheckApp(toSettings.value("startup_check_companion9x", true).toBool());
|
||||
useWizard(toSettings.value("wizardEnable", true).toBool());
|
||||
toSettings.setValue("useWizard", toSettings.value("wizardEnable", true));
|
||||
|
||||
// Convert and store the firmware type
|
||||
QString fwType = toSettings.value("firmware", "").toString();
|
||||
@@ -717,5 +732,14 @@ bool AppData::importSettings(QSettings & toSettings)
|
||||
id( 0 );
|
||||
}
|
||||
|
||||
return true;
|
||||
// convert old settings to new
|
||||
|
||||
if (toSettings.contains("useWizard")) {
|
||||
if (!toSettings.contains("newModelAction")) {
|
||||
newModelAction(toSettings.value("useWizard").toBool() ? 1 : 2);
|
||||
}
|
||||
toSettings.remove("useWizard");
|
||||
}
|
||||
|
||||
return fromSettings ? true : false;
|
||||
}
|
||||
|
||||
@@ -255,6 +255,9 @@ class AppData: protected CompStoreObj
|
||||
QByteArray _mainWinGeo;
|
||||
QByteArray _mainWinState;
|
||||
QByteArray _modelEditGeo;
|
||||
QByteArray _mdiWinGeo;
|
||||
QByteArray _mdiWinState;
|
||||
QByteArray _compareWinGeo;
|
||||
|
||||
QString _armMcu;
|
||||
QString _avrArguments;
|
||||
@@ -285,8 +288,10 @@ class AppData: protected CompStoreObj
|
||||
bool _autoCheckApp;
|
||||
bool _autoCheckFw;
|
||||
bool _simuSW;
|
||||
bool _useWizard;
|
||||
bool _tabbedMdi;
|
||||
bool _remvModelSlots;
|
||||
|
||||
int _newModelAction; // 0=no action; 1=model wizard; 2=model edit
|
||||
int _backLight;
|
||||
int _embedSplashes;
|
||||
int _fwServerFails;
|
||||
@@ -309,6 +314,9 @@ class AppData: protected CompStoreObj
|
||||
QByteArray mainWinGeo();
|
||||
QByteArray mainWinState();
|
||||
QByteArray modelEditGeo();
|
||||
QByteArray mdiWinGeo();
|
||||
QByteArray mdiWinState();
|
||||
QByteArray compareWinGeo();
|
||||
|
||||
QString armMcu();
|
||||
QString avrArguments();
|
||||
@@ -340,8 +348,10 @@ class AppData: protected CompStoreObj
|
||||
bool autoCheckApp();
|
||||
bool autoCheckFw();
|
||||
bool simuSW();
|
||||
bool useWizard();
|
||||
bool tabbedMdi();
|
||||
bool removeModelSlots();
|
||||
|
||||
int newModelAction();
|
||||
int backLight();
|
||||
int embedSplashes();
|
||||
int fwServerFails();
|
||||
@@ -362,6 +372,9 @@ class AppData: protected CompStoreObj
|
||||
void mainWinGeo (const QByteArray);
|
||||
void mainWinState (const QByteArray);
|
||||
void modelEditGeo (const QByteArray);
|
||||
void mdiWinGeo (const QByteArray);
|
||||
void mdiWinState (const QByteArray);
|
||||
void compareWinGeo (const QByteArray);
|
||||
|
||||
void armMcu (const QString);
|
||||
void avrArguments (const QString);
|
||||
@@ -393,8 +406,10 @@ class AppData: protected CompStoreObj
|
||||
void autoCheckApp (const bool);
|
||||
void autoCheckFw (const bool);
|
||||
void simuSW (const bool);
|
||||
void useWizard (const bool);
|
||||
void tabbedMdi (const bool);
|
||||
void removeModelSlots(const bool);
|
||||
|
||||
void newModelAction (const int);
|
||||
void backLight (const int);
|
||||
void embedSplashes (const int);
|
||||
void fwServerFails (const int);
|
||||
|
||||
|
After Width: | Height: | Size: 427 B |
|
After Width: | Height: | Size: 572 B |
|
After Width: | Height: | Size: 714 B |
|
After Width: | Height: | Size: 809 B |
|
After Width: | Height: | Size: 729 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
@@ -44,7 +44,7 @@ WizMix::WizMix(const GeneralSettings & settings, unsigned int modelId, const Mod
|
||||
originalModelData(modelData),
|
||||
vehicle(NOVEHICLE)
|
||||
{
|
||||
strcpy(name, " ");
|
||||
strncpy(name, originalModelData.name, WIZ_MODEL_NAME_LENGTH);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -295,6 +295,11 @@ ModelSelectionPage::ModelSelectionPage(WizardDialog *dlg, QString image, QString
|
||||
l->addWidget(helicopterRB);
|
||||
}
|
||||
|
||||
void ModelSelectionPage::initializePage()
|
||||
{
|
||||
nameLineEdit->setText(wizDlg->mix.name);
|
||||
}
|
||||
|
||||
bool ModelSelectionPage::validatePage()
|
||||
{
|
||||
//Filter and insert model name in mix data
|
||||
|
||||
@@ -73,6 +73,7 @@ private:
|
||||
QRadioButton *planeRB;
|
||||
QRadioButton *multirotorRB;
|
||||
QRadioButton *helicopterRB;
|
||||
void initializePage();
|
||||
bool validatePage();
|
||||
int nextId() const;
|
||||
};
|
||||
|
||||