diff --git a/src/group_gui.cpp b/src/group_gui.cpp --- a/src/group_gui.cpp +++ b/src/group_gui.cpp @@ -30,6 +30,8 @@ #include "table/sprites.h" +static const int LEVEL_WIDTH = 10; ///< Indenting width of a sub-group in pixels + typedef GUIList GUIGroupList; static const NWidgetPart _nested_group_widgets[] = { @@ -105,34 +107,26 @@ private: }; VehicleID vehicle_sel; ///< Selected vehicle + GroupID group_sel; ///< Selected group (for drag/drop) GroupID group_rename; ///< Group being renamed, INVALID_GROUP if none GroupID group_over; ///< Group over which a vehicle is dragged, INVALID_GROUP if none GUIGroupList groups; ///< List of groups uint tiny_step_height; ///< Step height for the group list Scrollbar *group_sb; + SmallVector indents; ///< Indentation levels + Dimension column_size[VGC_END]; ///< Size of the columns in the group list. - /** - * (Re)Build the group list. - * - * @param owner The owner of the window - */ - void BuildGroupList(Owner owner) + void AddParents(GUIGroupList *source, GroupID parent, int indent) { - if (!this->groups.NeedRebuild()) return; - - this->groups.Clear(); - - const Group *g; - FOR_ALL_GROUPS(g) { - if (g->owner == owner && g->vehicle_type == this->vli.vtype) { - *this->groups.Append() = g; + for (const Group **g = source->Begin(); g != source->End(); g++) { + if ((*g)->parent == parent) { + *this->groups.Append() = *g; + *this->indents.Append() = indent; + AddParents(source, (*g)->index, indent + 1); } } - - this->groups.Compact(); - this->groups.RebuildDone(); } /** Sort the groups by their name */ @@ -159,6 +153,36 @@ private: } /** + * (Re)Build the group list. + * + * @param owner The owner of the window + */ + void BuildGroupList(Owner owner) + { + if (!this->groups.NeedRebuild()) return; + + this->groups.Clear(); + this->indents.Clear(); + + GUIGroupList list; + + const Group *g; + FOR_ALL_GROUPS(g) { + if (g->owner == owner && g->vehicle_type == this->vli.vtype) { + *list.Append() = g; + } + } + + list.ForceResort(); + list.Sort(&GroupNameSorter); + + AddParents(&list, INVALID_GROUP, 0); + + this->groups.Compact(); + this->groups.RebuildDone(); + } + + /** * Compute tiny_step_height and column_size * @return Total width required for the group list. */ @@ -204,9 +228,10 @@ private: * @param left Left of the row. * @param right Right of the row. * @param g_id Group to list. + * @param indent Indentation level. * @param protection Whether autoreplace protection is set. */ - void DrawGroupInfo(int y, int left, int right, GroupID g_id, bool protection = false) const + void DrawGroupInfo(int y, int left, int right, GroupID g_id, int indent = 0, bool protection = false) const { /* Highlight the group if a vehicle is dragged over it */ if (g_id == this->group_over) { @@ -231,7 +256,7 @@ private: str = STR_GROUP_NAME; } int x = rtl ? right - WD_FRAMERECT_RIGHT - 8 - this->column_size[VGC_NAME].width + 1 : left + WD_FRAMERECT_LEFT + 8; - DrawString(x, x + this->column_size[VGC_NAME].width - 1, y + (this->tiny_step_height - this->column_size[VGC_NAME].height) / 2, str, colour); + DrawString(x + indent * LEVEL_WIDTH, x + this->column_size[VGC_NAME].width - 1, y + (this->tiny_step_height - this->column_size[VGC_NAME].height) / 2, str, colour); /* draw autoreplace protection */ x = rtl ? x - 8 - this->column_size[VGC_PROTECT].width : x + 8 + this->column_size[VGC_NAME].width; @@ -295,6 +320,7 @@ public: this->vli.index = ALL_GROUP; this->vehicle_sel = INVALID_VEHICLE; + this->group_sel = INVALID_GROUP; this->group_rename = INVALID_GROUP; this->group_over = INVALID_GROUP; @@ -308,7 +334,6 @@ public: this->groups.ForceRebuild(); this->groups.NeedResort(); this->BuildGroupList(vli.company); - this->groups.Sort(&GroupNameSorter); this->GetWidget(WID_GL_CAPTION)->widget_data = STR_VEHICLE_LIST_TRAIN_CAPTION + this->vli.vtype; this->GetWidget(WID_GL_LIST_VEHICLE)->tool_tip = STR_VEHICLE_LIST_TRAIN_LIST_TOOLTIP + this->vli.vtype; @@ -442,7 +467,6 @@ public: this->SortVehicleList(); this->BuildGroupList(this->owner); - this->groups.Sort(&GroupNameSorter); this->group_sb->SetCount(this->groups.Length()); this->vscroll->SetCount(this->vehicles.Length()); @@ -508,7 +532,7 @@ public: assert(g->owner == this->owner); - DrawGroupInfo(y1, r.left, r.right, g->index, g->replace_protection); + DrawGroupInfo(y1, r.left, r.right, g->index, this->indents[i], g->replace_protection); y1 += this->tiny_step_height; } @@ -523,6 +547,19 @@ public: break; case WID_GL_LIST_VEHICLE: + if (this->vli.index != ALL_GROUP) { + /* Mark vehicles which are in sub-groups */ + int y = r.top; + uint max = min(this->vscroll->GetPosition() + this->vscroll->GetCapacity(), this->vehicles.Length()); + for (uint i = this->vscroll->GetPosition(); i < max; ++i) { + const Vehicle *v = this->vehicles[i]; + if (v->group_id != this->vli.index) { + GfxFillRect(r.left + 1, y + 1, r.right - 1, y + this->resize.step_height - 2, _colour_gradient[COLOUR_GREY][3], FILLRECT_CHECKER); + } + y += this->resize.step_height; + } + } + this->DrawVehicleListItems(this->vehicle_sel, this->resize.step_height, r); break; } @@ -560,7 +597,9 @@ public: uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); if (id_g >= this->groups.Length()) return; - this->vli.index = this->groups[id_g]->index; + this->group_sel = this->vli.index = this->groups[id_g]->index; + + SetObjectToPlaceWnd(SPR_CURSOR_MOUSE, PAL_NONE, HT_DRAG, this); this->vehicles.ForceRebuild(); this->SetDirty(); @@ -620,14 +659,46 @@ public: case WID_GL_REPLACE_PROTECTION: { const Group *g = Group::GetIfValid(this->vli.index); if (g != NULL) { - DoCommandP(0, this->vli.index, !g->replace_protection, CMD_SET_GROUP_REPLACE_PROTECTION); + DoCommandP(0, this->vli.index, !g->replace_protection | (_ctrl_pressed << 1), CMD_SET_GROUP_REPLACE_PROTECTION); } break; } } } - virtual void OnDragDrop(Point pt, int widget) + void OnDragDrop_Group(Point pt, int widget) + { + const Group *g = Group::Get(this->group_sel); + + switch (widget) { + case WID_GL_ALL_VEHICLES: // All vehicles + case WID_GL_DEFAULT_VEHICLES: // Ungroupd vehicles + if (g->parent != INVALID_GROUP) { + DoCommandP(0, this->group_sel | (1 << 16), INVALID_GROUP, CMD_ALTER_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_SET_PARENT)); + } + + this->group_sel = INVALID_GROUP; + this->group_over = INVALID_GROUP; + this->SetDirty(); + break; + + case WID_GL_LIST_GROUP: { // Matrix group + uint id_g = this->group_sb->GetScrolledRowFromWidget(pt.y, this, WID_GL_LIST_GROUP, 0, this->tiny_step_height); + GroupID new_g = id_g >= this->groups.Length() ? INVALID_GROUP : this->groups[id_g]->index; + + if (this->group_sel != new_g && g->parent != new_g) { + DoCommandP(0, this->group_sel | (1 << 16), new_g, CMD_ALTER_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_SET_PARENT)); + } + + this->group_sel = INVALID_GROUP; + this->group_over = INVALID_GROUP; + this->SetDirty(); + break; + } + } + } + + void OnDragDrop_Vehicle(Point pt, int widget) { switch (widget) { case WID_GL_DEFAULT_VEHICLES: // Ungrouped vehicles @@ -668,12 +739,19 @@ public: break; } } + } + + virtual void OnDragDrop(Point pt, int widget) + { + if (this->vehicle_sel != INVALID_VEHICLE) OnDragDrop_Vehicle(pt, widget); + if (this->group_sel != INVALID_GROUP) OnDragDrop_Group(pt, widget); + _cursor.vehchain = false; } virtual void OnQueryTextFinished(char *str) { - if (str != NULL) DoCommandP(0, this->group_rename, 0, CMD_RENAME_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_RENAME), NULL, str); + if (str != NULL) DoCommandP(0, this->group_rename, 0, CMD_ALTER_GROUP | CMD_MSG(STR_ERROR_GROUP_CAN_T_RENAME), NULL, str); this->group_rename = INVALID_GROUP; } @@ -742,7 +820,7 @@ public: virtual void OnMouseDrag(Point pt, int widget) { - if (this->vehicle_sel == INVALID_VEHICLE) return; + if (this->vehicle_sel == INVALID_VEHICLE && this->group_sel == INVALID_GROUP) return; /* A vehicle is dragged over... */ GroupID new_group_over = INVALID_GROUP; @@ -759,7 +837,11 @@ public: } /* Do not highlight when dragging over the current group */ - if (Vehicle::Get(vehicle_sel)->group_id == new_group_over) new_group_over = INVALID_GROUP; + if (this->vehicle_sel != INVALID_VEHICLE) { + if (Vehicle::Get(vehicle_sel)->group_id == new_group_over) new_group_over = INVALID_GROUP; + } else if (this->group_sel != INVALID_GROUP) { + if (this->group_sel == new_group_over || Group::Get(this->group_sel)->parent == new_group_over) new_group_over = INVALID_GROUP; + } /* Mark widgets as dirty if the group changed. */ if (new_group_over != this->group_over) {